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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master ffce236 30/42: Do auto indentation only in post command ho


From: Noam Postavsky
Subject: [elpa] master ffce236 30/42: Do auto indentation only in post command hook
Date: Sun, 22 Dec 2019 17:38:01 -0500 (EST)

branch: master
commit ffce2362681d0bdaf096ed950e84e5f38a1149b4
Author: Noam Postavsky <address@hidden>
Commit: Noam Postavsky <address@hidden>

    Do auto indentation only in post command hook
    
    * yasnippet.el (yas--todo-snippet-indent): New variable.
    (yas--on-field-overlay-modification): Save snippet to it, and don't
    indent after mirror update.
    (yas--do-todo-field-updates): New function.
    (yas--post-command-handler): Call it.
    (yas--snippet-field-mirrors, yas--indent-mirrors-of-snippet): New
    functions, split out from...
    (yas--snippet-field-mirrors): ...here.
    * yasnippet-tests.el (yas-test-delete-and-insert-command)
    (indent-mirrors-on-complex-update): New test and helper function.
---
 yasnippet-tests.el |  22 +++++++++++
 yasnippet.el       | 111 ++++++++++++++++++++++++++++++++++-------------------
 2 files changed, 94 insertions(+), 39 deletions(-)

diff --git a/yasnippet-tests.el b/yasnippet-tests.el
index c555a9e..db9c177 100644
--- a/yasnippet-tests.el
+++ b/yasnippet-tests.el
@@ -608,6 +608,28 @@ int foo()
     ;; Assuming 2 space indent.
     (should (string= "def xxx\n  xxx\nend" (buffer-string)))))
 
+(defun yas-test-delete-and-insert-command (beg end new)
+  "Simulate a completion command (similar to company-mode)."
+  (interactive "r\ns")
+  ;; Simulate a completion command (like what company-mode does)
+  ;; which deletes the "xxx" and then replaces it with something
+  ;; else.
+  (delete-region beg end)
+  (insert new))
+
+(ert-deftest indent-mirrors-on-complex-update ()
+  "Don't get messed up by command that deletes and then inserts."
+  (with-temp-buffer
+    (ruby-mode)
+    (yas-minor-mode 1)
+    (yas-expand-snippet "def foo\n  ${1:slice} = append($1)\nend")
+    (yas-mock-insert "xxx")
+    (ert-simulate-command `(yas-test-delete-and-insert-command
+                            ,(- (point) 3) ,(point) ,"yyy"))
+    ;; Assuming 2 space indent.
+    (should (string= "def foo\n  yyy = append(yyy)\nend" (buffer-string)))))
+
+
 
 (ert-deftest snippet-with-multiline-mirrors-issue-665 ()
   "In issue 665, a multi-line mirror is attempted."
diff --git a/yasnippet.el b/yasnippet.el
index dcf593e..2ec192a 100644
--- a/yasnippet.el
+++ b/yasnippet.el
@@ -3785,6 +3785,9 @@ BEG, END and LENGTH like overlay modification hooks."
        (= beg (yas--field-start field)) ; Insertion at field start?
        (not (yas--field-modified-p field))))
 
+(defvar yas--todo-snippet-indent nil nil)
+(make-variable-buffer-local 'yas--todo-snippet-indent)
+
 (defun yas--on-field-overlay-modification (overlay after? beg end &optional 
length)
   "Clears the field and updates mirrors, conditionally.
 
@@ -3819,12 +3822,29 @@ field start.  This hook does nothing if an undo is in 
progress."
                     (cl-assert (memq pfield (yas--snippet-fields psnippet)))
                     (yas--advance-end-maybe pfield (overlay-end overlay))
                     (setq pfield (yas--snippet-previous-active-field 
psnippet)))))
-              (save-excursion
-                (yas--field-update-display field))
-              (yas--update-mirrors snippet)))
+              ;; Update fields now, but delay auto indentation until
+              ;; post-command.  We don't want to run indentation on
+              ;; the intermediate state where field text might be
+              ;; removed (and hence the field could be deleted along
+              ;; with leading indentation).
+              (let ((yas-indent-line nil))
+                (save-excursion
+                  (yas--field-update-display field))
+                (yas--update-mirrors snippet))
+              (unless (or (not (eq yas-indent-line 'auto))
+                          (memq snippet yas--todo-snippet-indent))
+                (push snippet yas--todo-snippet-indent))))
         (lwarn '(yasnippet zombie) :warning "Killing zombie snippet!")
         (delete-overlay overlay)))))
 
+(defun yas--do-todo-field-updates ()
+  (when yas--todo-snippet-indent
+    (save-excursion
+      (cl-loop for snippet in yas--todo-snippet-indent
+               do (yas--indent-mirrors-of-snippet
+                   snippet (yas--snippet-field-mirrors snippet)))
+      (setq yas--todo-snippet-indent nil))))
+
 (defun yas--auto-fill ()
   (let* ((orig-point (point))
          (end (progn (forward-paragraph) (point)))
@@ -4800,46 +4820,58 @@ When multiple expressions are found, only the last one 
counts."
                     (parent 1)
                     (t 0))))))
 
+(defun yas--snippet-field-mirrors (snippet)
+  ;; Make a list of (FIELD . MIRROR).
+  (cl-sort
+   (cl-mapcan (lambda (field)
+                (mapcar (lambda (mirror)
+                          (cons field mirror))
+                        (yas--field-mirrors field)))
+              (yas--snippet-fields snippet))
+   ;; Then sort this list so that entries with mirrors with
+   ;; parent fields appear before.  This was important for
+   ;; fixing #290, and also handles the case where a mirror in
+   ;; a field causes another mirror to need reupdating.
+   #'> :key (lambda (fm) (yas--calculate-mirror-depth (cdr fm)))))
+
+(defun yas--indent-mirrors-of-snippet (snippet &optional f-ms)
+  ;; Indent mirrors of SNIPPET.  F-MS is the return value of
+  ;; (yas--snippet-field-mirrors SNIPPET).
+  (when (eq yas-indent-line 'auto)
+    (let ((yas--inhibit-overlay-hooks t))
+      (cl-loop for (beg . end) in
+               (cl-sort (mapcar (lambda (f-m)
+                                  (let ((mirror (cdr f-m)))
+                                    (cons (yas--mirror-start mirror)
+                                          (yas--mirror-end mirror))))
+                                (or f-ms
+                                    (yas--snippet-field-mirrors snippet)))
+                        #'< :key #'car)
+               do (yas--indent-region beg end snippet)))))
+
 (defun yas--update-mirrors (snippet)
   "Update all the mirrors of SNIPPET."
   (yas--save-restriction-and-widen
     (save-excursion
-      (cl-loop
-       for (field . mirror)
-       in (cl-sort
-           ;; Make a list of (FIELD . MIRROR).
-           (cl-mapcan (lambda (field)
-                        (mapcar (lambda (mirror)
-                                  (cons field mirror))
-                                (yas--field-mirrors field)))
-                      (yas--snippet-fields snippet))
-           ;; Then sort this list so that entries with mirrors with
-           ;; parent fields appear before.  This was important for
-           ;; fixing #290, and also handles the case where a mirror in
-           ;; a field causes another mirror to need reupdating.
-           #'> :key (lambda (fm) (yas--calculate-mirror-depth (cdr fm))))
-       ;; Before updating a mirror with a parent-field, maybe advance
-       ;; its start (#290).
-       do (let ((parent-field (yas--mirror-parent-field mirror)))
-            (when parent-field
-              (yas--advance-start-maybe mirror (yas--fom-start parent-field))))
-       ;; Update this mirror.
-       do (yas--mirror-update-display mirror field)
-       ;; Delay indenting until we're done all mirrors.  We must do
-       ;; this to avoid losing whitespace between fields that are
-       ;; still empty (i.e., they will be non-empty after updating).
-       when (eq yas-indent-line 'auto)
-       collect (cons (yas--mirror-start mirror) (yas--mirror-end mirror))
-       into indent-regions
-       ;; `yas--place-overlays' is needed since the active field and
-       ;; protected overlays might have been changed because of insertions
-       ;; in `yas--mirror-update-display'.
-       do (let ((active-field (yas--snippet-active-field snippet)))
-            (when active-field (yas--place-overlays snippet active-field)))
-       finally do
-       (let ((yas--inhibit-overlay-hooks t))
-         (cl-loop for (beg . end) in (cl-sort indent-regions #'< :key #'car)
-                  do (yas--indent-region beg end snippet)))))))
+      (let ((f-ms (yas--snippet-field-mirrors snippet)))
+        (cl-loop
+         for (field . mirror) in f-ms
+         ;; Before updating a mirror with a parent-field, maybe advance
+         ;; its start (#290).
+         do (let ((parent-field (yas--mirror-parent-field mirror)))
+              (when parent-field
+                (yas--advance-start-maybe mirror (yas--fom-start 
parent-field))))
+         ;; Update this mirror.
+         do (yas--mirror-update-display mirror field)
+         ;; `yas--place-overlays' is needed since the active field and
+         ;; protected overlays might have been changed because of insertions
+         ;; in `yas--mirror-update-display'.
+         do (let ((active-field (yas--snippet-active-field snippet)))
+              (when active-field (yas--place-overlays snippet active-field))))
+        ;; Delay indenting until we're done all mirrors.  We must do
+        ;; this to avoid losing whitespace between fields that are
+        ;; still empty (i.e., they will be non-empty after updating).
+        (yas--indent-mirrors-of-snippet snippet f-ms)))))
 
 (defun yas--mirror-update-display (mirror field)
   "Update MIRROR according to FIELD (and mirror transform)."
@@ -4897,6 +4929,7 @@ When multiple expressions are found, only the last one 
counts."
     ;; Don't pop up more than once in a session (still log though).
     (defvar warning-suppress-types) ; `warnings' is autoloaded by `lwarn'.
     (add-to-list 'warning-suppress-types '(yasnippet auto-fill bug)))
+  (yas--do-todo-field-updates)
   (condition-case err
       (progn (yas--finish-moving-snippets)
              (cond ((eq 'undo this-command)



reply via email to

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