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

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

[elpa] master c432e78 31/42: Fix field adjustment on deletion


From: Noam Postavsky
Subject: [elpa] master c432e78 31/42: Fix field adjustment on deletion
Date: Sun, 22 Dec 2019 17:38:02 -0500 (EST)

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

    Fix field adjustment on deletion
    
    For deletion, we need to check the bounds before the deletion happens,
    otherwise the overlay may already be moved to wrong place.
    * yasnippet.el (yas--before-change-modified-snippets): New variable.
    (yas--merge-and-drop-dups): New function.
    (yas--gather-active-snippets): New function.
    (yas--on-field-overlay-modification): Use it.
    
    * yasnippet.el
---
 yasnippet-tests.el | 21 ++++++++++++
 yasnippet.el       | 94 +++++++++++++++++++++++++++++++++++++-----------------
 2 files changed, 86 insertions(+), 29 deletions(-)

diff --git a/yasnippet-tests.el b/yasnippet-tests.el
index db9c177..7791db8 100644
--- a/yasnippet-tests.el
+++ b/yasnippet-tests.el
@@ -1122,6 +1122,27 @@ hello ${1:$(when (stringp yas-text) (funcall func 
yas-text))} foo${1:$$(concat \
      (ert-simulate-command '(yas-next-field-or-maybe-expand))
      (should (string= (buffer-string) "<-<-abcdef\n")))))
 
+(ert-deftest nested-snippet-expansion-5-nested-delete ()
+  "See Github #996."
+  (let ((yas-triggers-in-field t))
+    (yas-with-snippet-dirs
+     '((".emacs.d/snippets"
+        ("text-mode"
+         ("sel" . "${1:ch}")
+         ("ch" . "<-${1:ch}"))))
+     (yas-reload-all)
+     (text-mode)
+     (yas-minor-mode +1)
+     (insert "sel")
+     (ert-simulate-command '(yas-expand))
+     (ert-simulate-command '(forward-word 1))
+     (ert-simulate-command '(yas-expand))
+     (ert-simulate-command '(forward-word 1))
+     ;; The (cl-assert (memq pfield (yas--snippet-fields psnippet)))
+     ;; in `yas--on-field-overlay-modification' failed here.
+     (ert-simulate-command '(delete-backward-char 1))
+     (should (string= (buffer-string) "<-c\n")))))
+
 
 ;;; Loading
 ;;;
diff --git a/yasnippet.el b/yasnippet.el
index 2ec192a..27df486 100644
--- a/yasnippet.el
+++ b/yasnippet.el
@@ -3788,13 +3788,45 @@ BEG, END and LENGTH like overlay modification hooks."
 (defvar yas--todo-snippet-indent nil nil)
 (make-variable-buffer-local 'yas--todo-snippet-indent)
 
+(defvar yas--before-change-modified-snippets nil)
+
+(defun yas--merge-and-drop-dups (list1 list2 cmp key)
+  ;; `delete-consecutive-dups' + `cl-merge'.
+  (funcall (if (fboundp 'delete-consecutive-dups)
+               #'delete-consecutive-dups ; 24.4
+             #'delete-dups)
+           (cl-merge 'list list1 list2 cmp :key key)))
+
+(defun yas--gather-active-snippets (overlay beg end then-delete)
+  ;; Add active snippets in BEG..END into an OVERLAY keyed entry of
+  ;; `yas--before-change-modified-snippets'.  Return accumulated list.
+  ;; If THEN-DELETE is non-nil, delete the entry.
+  (let ((new (yas-active-snippets beg end))
+        (old (assq overlay yas--before-change-modified-snippets)))
+    (prog1 (cond ((and new old)
+                  (setf (cdr old)
+                        (yas--merge-and-drop-dups
+                         (cdr old) new
+                         ;; Sort like `yas-active-snippets'.
+                         #'>= #'yas--snippet-id)))
+                 (new (unless then-delete
+                        ;; Don't add new entry if we're about to
+                        ;; remove it anyway.
+                        (push (cons overlay new)
+                              yas--before-change-modified-snippets))
+                      new)
+                 (old (cdr old))
+                 (t nil))
+      (when then-delete
+        (cl-callf2 delq old yas--before-change-modified-snippets)))))
+
+
 (defun yas--on-field-overlay-modification (overlay after? beg end &optional 
length)
   "Clears the field and updates mirrors, conditionally.
 
 Only clears the field if it hasn't been modified and point is at
 field start.  This hook does nothing if an undo is in progress."
-  (unless (or (not after?)
-              yas--inhibit-overlay-hooks
+  (unless (or yas--inhibit-overlay-hooks
               (not (overlayp yas--active-field-overlay)) ; Avoid Emacs bug 
#21824.
               ;; If a single change hits multiple overlays of the same
               ;; snippet, then we delete the snippet the first time,
@@ -3807,33 +3839,37 @@ field start.  This hook does nothing if an undo is in 
progress."
            (field (overlay-get overlay 'yas--field))
            (snippet (overlay-get yas--active-field-overlay 'yas--snippet)))
       (if (yas--snippet-live-p snippet)
-          (save-match-data
-            (yas--letenv (yas--snippet-expand-env snippet)
-              (when (yas--skip-and-clear-field-p field beg end length)
-                ;; We delete text starting from the END of insertion.
-                (yas--skip-and-clear field end))
-              (setf (yas--field-modified-p field) t)
-              ;; Adjust any pending active fields in case of stacked
-              ;; expansion.
-              (let ((pfield field)
-                    (psnippets (yas-active-snippets beg end)))
-                (while (and pfield psnippets)
-                  (let ((psnippet (pop psnippets)))
-                    (cl-assert (memq pfield (yas--snippet-fields psnippet)))
-                    (yas--advance-end-maybe pfield (overlay-end overlay))
-                    (setq pfield (yas--snippet-previous-active-field 
psnippet)))))
-              ;; 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))))
+          (if after?
+              (save-match-data
+                (yas--letenv (yas--snippet-expand-env snippet)
+                  (when (yas--skip-and-clear-field-p field beg end length)
+                    ;; We delete text starting from the END of insertion.
+                    (yas--skip-and-clear field end))
+                  (setf (yas--field-modified-p field) t)
+                  ;; Adjust any pending active fields in case of stacked
+                  ;; expansion.
+                  (let ((pfield field)
+                        (psnippets (yas--gather-active-snippets
+                                    overlay beg end t)))
+                    (while (and pfield psnippets)
+                      (let ((psnippet (pop psnippets)))
+                        (cl-assert (memq pfield (yas--snippet-fields 
psnippet)))
+                        (yas--advance-end-maybe pfield (overlay-end overlay))
+                        (setq pfield (yas--snippet-previous-active-field 
psnippet)))))
+                  ;; 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))))
+            ;; Remember active snippets to use for after the change.
+            (yas--gather-active-snippets overlay beg end nil))
         (lwarn '(yasnippet zombie) :warning "Killing zombie snippet!")
         (delete-overlay overlay)))))
 



reply via email to

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