[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master e878afb 12/63: Update handling of markers during indentati
From: |
Noam Postavsky |
Subject: |
[elpa] master e878afb 12/63: Update handling of markers during indentation |
Date: |
Mon, 17 Jul 2017 22:54:12 -0400 (EDT) |
branch: master
commit e878afb8832ecf05d654d99cd7ecb3406f7a425e
Author: Noam Postavsky <address@hidden>
Commit: Noam Postavsky <address@hidden>
Update handling of markers during indentation
* yasnippet.el (yas--snapshot-marker-location): New function, save a
regexp and whitespace count determining a marker's location in a line.
(yas--restore-marker-location): New function, restores a marker's
location based on info from `yas--snapshot-marker-location'.
(yas--indent-region): Use them to fix marker locations after indentation.
* yasnippet-tests.el (indent-org-property, indent-cc-mode):
(indent-snippet-mode): New tests.
---
yasnippet-tests.el | 62 ++++++++++++++++++++++++++++++++++++++++
yasnippet.el | 84 +++++++++++++++++++++++++++++++++++++++---------------
2 files changed, 123 insertions(+), 23 deletions(-)
diff --git a/yasnippet-tests.el b/yasnippet-tests.el
index db2fda6..c50aa9d 100644
--- a/yasnippet-tests.el
+++ b/yasnippet-tests.el
@@ -243,6 +243,68 @@ $1 ------------------------")
XXXXX ---------------- XXXXX ----
XXXXX ------------------------"))))
+(ert-deftest indent-org-property ()
+ "Handling of `org-mode' property indentation, see `org-property-format'."
+ ;; This is an interesting case because `org-indent-line' calls
+ ;; `replace-match' for properties.
+ (with-temp-buffer
+ (org-mode)
+ (yas-minor-mode +1)
+ (yas-expand-snippet "* Test ${1:test}\n:PROPERTIES:\n:ID: $1-after\n:END:")
+ (yas-mock-insert "foo bar")
+ (ert-simulate-command '(yas-next-field))
+ (goto-char (point-min))
+ (let ((expected (with-temp-buffer
+ (insert (format (concat "* Test foo bar\n"
+ " " org-property-format "\n"
+ " " org-property-format "\n"
+ " " org-property-format)
+ ":PROPERTIES:" ""
+ ":ID:" "foo bar-after"
+ ":END:" ""))
+ (delete-trailing-whitespace)
+ (buffer-string))))
+ ;; Some org-mode versions leave trailing whitespace, some don't.
+ (delete-trailing-whitespace)
+ (should (equal expected (buffer-string))))))
+
+(ert-deftest indent-cc-mode ()
+ "Handling of cc-mode's indentation."
+ ;; This is an interesting case because cc-mode deletes all the
+ ;; indentation before recreating it.
+ (with-temp-buffer
+ (c++-mode)
+ (yas-minor-mode +1)
+ (yas-expand-snippet "\
+int foo()
+{
+ if ($1) {
+ delete $1;
+ $1 = 0;
+ }
+}")
+ (yas-mock-insert "var")
+ (should (string= "\
+int foo()
+{
+ if (var) {
+ delete var;
+ var = 0;
+ }
+}" (buffer-string)))))
+
+(ert-deftest indent-snippet-mode ()
+ "Handling of snippet-mode indentation."
+ ;; This is an interesting case because newlines match [[:space:]] in
+ ;; snippet-mode.
+ (with-temp-buffer
+ (snippet-mode)
+ (yas-minor-mode +1)
+ (yas-expand-snippet "# -*- mode: snippet -*-\n# name: $1\n# key: $1\n#
--\n")
+ (yas-mock-insert "foo")
+ (should (string= "# -*- mode: snippet -*-\n# name: foo\n# key: foo\n# --\n"
+ (buffer-string)))))
+
(ert-deftest indent-mirrors-on-update ()
"Check that mirrors are always kept indented."
(with-temp-buffer
diff --git a/yasnippet.el b/yasnippet.el
index fa70339..e0333bb 100644
--- a/yasnippet.el
+++ b/yasnippet.el
@@ -3917,40 +3917,78 @@ Meant to be called in a narrowed buffer, does various
passes"
(goto-char parse-start)
(yas--indent snippet)))
+;; HACK: Some implementations of `indent-line-function' (called via
+;; `indent-according-to-mode') delete text before they insert (like
+;; cc-mode), some make complicated regexp replacements (looking at
+;; you, org-mode). To find place where the marker "should" go after
+;; indentation, we create a regexp based on what the line looks like
+;; before, putting a capture group where the marker is. The regexp
+;; matches any whitespace with [[:space:]]* to allow for the
+;; indentation changing whitespace. Additionally, we try to preserve
+;; the amount of whitespace *following* the marker, because
+;; indentation generally affects whitespace at the beginning, not the
+;; end.
+;;
+;; This is all best-effort heuristic stuff, but it should cover 99% of
+;; use-cases.
+
+(defun yas--snapshot-marker-location (marker)
+ "Returns info for restoring MARKER's location after indent.
+The returned value is a list of the form (REGEXP MARKER WS-COUNT)."
+ (when (and (<= (line-beginning-position) marker)
+ (<= marker (line-end-position)))
+ (let ((before
+ (split-string (buffer-substring-no-properties
+ (line-beginning-position) marker) "[[:space:]]+" t))
+ (after
+ (split-string (buffer-substring-no-properties
+ marker (line-end-position)) "[[:space:]]+" t)))
+ (list (concat "[[:space:]]*"
+ (mapconcat (lambda (s)
+ (if (eq s marker) "\\(\\)"
+ (regexp-quote s)))
+ (nconc before (list marker) after)
+ "[[:space:]]*"))
+ marker
+ (progn (goto-char marker)
+ (skip-syntax-forward " " (line-end-position))
+ (- (point) marker))))))
+
+(defun yas--restore-marker-location (re-marker)
+ "Restores marker based on info from `yas--snapshot-marker-location'."
+ (let ((regexp (nth 0 re-marker))
+ (marker (nth 1 re-marker))
+ (ws-count (nth 2 re-marker)))
+ (beginning-of-line)
+ (save-restriction
+ ;; Narrowing is the only way to limit `looking-at'.
+ (narrow-to-region (point) (line-end-position))
+ (if (not (looking-at regexp))
+ (lwarn '(yasnippet re-marker) :warning
+ "Couldn't find: %S" regexp)
+ (goto-char (match-beginning 1))
+ (skip-syntax-forward " ")
+ (skip-syntax-backward " " (- (point) ws-count))
+ (set-marker marker (point))))))
+
(defun yas--indent-region (from to snippet)
"Indent the lines between FROM and TO with `indent-according-to-mode'.
The SNIPPET's markers are preserved."
- ;;; Apropos indenting problems....
- ;;
- ;; `indent-according-to-mode' uses whatever `indent-line-function'
- ;; is available. Some implementations of these functions delete text
- ;; before they insert. If there happens to be a marker just after
- ;; the text being deleted, the insertion actually happens after the
- ;; marker, which misplaces it.
- ;;
- ;; This would also happen if we had used overlays with the
- ;; `front-advance' property set to nil.
- ;;
- ;; This is why I have these `trouble-markers', they are the ones at
- ;; the first non-whitespace char at the line. After indentation
- ;; takes place we should be at the correct to restore them. All
- ;; other non-trouble-markers should have been *pushed* and don't
- ;; need special attention.
(let* ((snippet-markers (yas--collect-snippet-markers snippet))
(to (set-marker (make-marker) to)))
(save-excursion
(goto-char from)
(save-restriction
(widen)
- ;; Indent each non-empty line.
(cl-loop if (/= (line-beginning-position) (line-end-position)) do
- (back-to-indentation)
- (let ((trouble-markers ; The markers at (point).
- (cl-remove (point) snippet-markers :test #'/=)))
+ ;; Indent each non-empty line.
+ (let ((remarkers
+ (delq nil (mapcar #'yas--snapshot-marker-location
+ snippet-markers))))
(unwind-protect
- (indent-according-to-mode)
- (dolist (marker trouble-markers)
- (set-marker marker (point)))))
+ (progn (back-to-indentation)
+ (indent-according-to-mode))
+ (mapc #'yas--restore-marker-location remarkers)))
while (and (zerop (forward-line 1))
(< (point) to)))))))
- [elpa] master 2ca6321 05/63: Promote yas--snippets-at-point to a public API, (continued)
- [elpa] master 2ca6321 05/63: Promote yas--snippets-at-point to a public API, Noam Postavsky, 2017/07/17
- [elpa] master 9abf842 19/63: Enable snippet-mode automatically, Noam Postavsky, 2017/07/17
- [elpa] master 48cd716 14/63: Use more compact format for snippet menus, Noam Postavsky, 2017/07/17
- [elpa] master b62cf52 23/63: Remove lambda list building hack, Noam Postavsky, 2017/07/17
- [elpa] master 7b3c29d 13/63: * doc/faq.org: Typos and grammar., Noam Postavsky, 2017/07/17
- [elpa] master 14819c9 28/63: Make yas-buffer-local-condition into a defcustom, Noam Postavsky, 2017/07/17
- [elpa] master f3d0e03 25/63: * yasnippet-tests.el (snippet-exit-hooks): New test., Noam Postavsky, 2017/07/17
- [elpa] master 4ee3835 08/63: Use expand-env for all snippet evaluations, Noam Postavsky, 2017/07/17
- [elpa] master 203df22 22/63: Use debug-on-error to simplify error handling, Noam Postavsky, 2017/07/17
- [elpa] master 9c9547a 21/63: Don't catch and rethrow yas-{-}exception, Noam Postavsky, 2017/07/17
- [elpa] master e878afb 12/63: Update handling of markers during indentation,
Noam Postavsky <=
- [elpa] master 6c4fbb2 24/63: Fix snippet local exit hook, Noam Postavsky, 2017/07/17
- [elpa] master d0c6fec 16/63: Refactor snippet marker manipulating functions, Noam Postavsky, 2017/07/17
- [elpa] master e74f00e 18/63: Fix whitespace lossage between mirrors, Noam Postavsky, 2017/07/17
- [elpa] master 4f37afd 27/63: Let snippets expand in strings/comments by default, Noam Postavsky, 2017/07/17
- [elpa] master cddb826 29/63: Merge: let snippets expand in strings/comments by default, Noam Postavsky, 2017/07/17
- [elpa] master 462f566 44/63: Don't leave unreadable objects in the undo list, Noam Postavsky, 2017/07/17
- [elpa] master 3949421 41/63: Fix problems with auto-fill-mode interaction, Noam Postavsky, 2017/07/17
- [elpa] master 0d9afb2 31/63: Fix mirror transformation error with expand-env, Noam Postavsky, 2017/07/17
- [elpa] master b4d2f9c 37/63: Update snippets submodule, Noam Postavsky, 2017/07/17
- [elpa] master c5fddf8 34/63: Restore syntax-propertize-function before indenting snippet, Noam Postavsky, 2017/07/17