emacs-elpa-diffs
[Top][All Lists]
Advanced

[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)))))))
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]