mirror of
https://github.com/joaotavora/yasnippet.git
synced 2025-10-13 21:13:04 +00:00
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
This commit is contained in:
parent
ffce236268
commit
c432e78ffd
@ -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))
|
(ert-simulate-command '(yas-next-field-or-maybe-expand))
|
||||||
(should (string= (buffer-string) "<-<-abcdef\n")))))
|
(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
|
;;; Loading
|
||||||
;;;
|
;;;
|
||||||
|
94
yasnippet.el
94
yasnippet.el
@ -3788,13 +3788,45 @@ BEG, END and LENGTH like overlay modification hooks."
|
|||||||
(defvar yas--todo-snippet-indent nil nil)
|
(defvar yas--todo-snippet-indent nil nil)
|
||||||
(make-variable-buffer-local 'yas--todo-snippet-indent)
|
(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)
|
(defun yas--on-field-overlay-modification (overlay after? beg end &optional length)
|
||||||
"Clears the field and updates mirrors, conditionally.
|
"Clears the field and updates mirrors, conditionally.
|
||||||
|
|
||||||
Only clears the field if it hasn't been modified and point is at
|
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."
|
field start. This hook does nothing if an undo is in progress."
|
||||||
(unless (or (not after?)
|
(unless (or yas--inhibit-overlay-hooks
|
||||||
yas--inhibit-overlay-hooks
|
|
||||||
(not (overlayp yas--active-field-overlay)) ; Avoid Emacs bug #21824.
|
(not (overlayp yas--active-field-overlay)) ; Avoid Emacs bug #21824.
|
||||||
;; If a single change hits multiple overlays of the same
|
;; If a single change hits multiple overlays of the same
|
||||||
;; snippet, then we delete the snippet the first time,
|
;; 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))
|
(field (overlay-get overlay 'yas--field))
|
||||||
(snippet (overlay-get yas--active-field-overlay 'yas--snippet)))
|
(snippet (overlay-get yas--active-field-overlay 'yas--snippet)))
|
||||||
(if (yas--snippet-live-p snippet)
|
(if (yas--snippet-live-p snippet)
|
||||||
(save-match-data
|
(if after?
|
||||||
(yas--letenv (yas--snippet-expand-env snippet)
|
(save-match-data
|
||||||
(when (yas--skip-and-clear-field-p field beg end length)
|
(yas--letenv (yas--snippet-expand-env snippet)
|
||||||
;; We delete text starting from the END of insertion.
|
(when (yas--skip-and-clear-field-p field beg end length)
|
||||||
(yas--skip-and-clear field end))
|
;; We delete text starting from the END of insertion.
|
||||||
(setf (yas--field-modified-p field) t)
|
(yas--skip-and-clear field end))
|
||||||
;; Adjust any pending active fields in case of stacked
|
(setf (yas--field-modified-p field) t)
|
||||||
;; expansion.
|
;; Adjust any pending active fields in case of stacked
|
||||||
(let ((pfield field)
|
;; expansion.
|
||||||
(psnippets (yas-active-snippets beg end)))
|
(let ((pfield field)
|
||||||
(while (and pfield psnippets)
|
(psnippets (yas--gather-active-snippets
|
||||||
(let ((psnippet (pop psnippets)))
|
overlay beg end t)))
|
||||||
(cl-assert (memq pfield (yas--snippet-fields psnippet)))
|
(while (and pfield psnippets)
|
||||||
(yas--advance-end-maybe pfield (overlay-end overlay))
|
(let ((psnippet (pop psnippets)))
|
||||||
(setq pfield (yas--snippet-previous-active-field psnippet)))))
|
(cl-assert (memq pfield (yas--snippet-fields psnippet)))
|
||||||
;; Update fields now, but delay auto indentation until
|
(yas--advance-end-maybe pfield (overlay-end overlay))
|
||||||
;; post-command. We don't want to run indentation on
|
(setq pfield (yas--snippet-previous-active-field psnippet)))))
|
||||||
;; the intermediate state where field text might be
|
;; Update fields now, but delay auto indentation until
|
||||||
;; removed (and hence the field could be deleted along
|
;; post-command. We don't want to run indentation on
|
||||||
;; with leading indentation).
|
;; the intermediate state where field text might be
|
||||||
(let ((yas-indent-line nil))
|
;; removed (and hence the field could be deleted along
|
||||||
(save-excursion
|
;; with leading indentation).
|
||||||
(yas--field-update-display field))
|
(let ((yas-indent-line nil))
|
||||||
(yas--update-mirrors snippet))
|
(save-excursion
|
||||||
(unless (or (not (eq yas-indent-line 'auto))
|
(yas--field-update-display field))
|
||||||
(memq snippet yas--todo-snippet-indent))
|
(yas--update-mirrors snippet))
|
||||||
(push snippet yas--todo-snippet-indent))))
|
(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!")
|
(lwarn '(yasnippet zombie) :warning "Killing zombie snippet!")
|
||||||
(delete-overlay overlay)))))
|
(delete-overlay overlay)))))
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user