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.
This commit is contained in:
Noam Postavsky 2019-04-24 17:38:23 -04:00
parent 6a738b581f
commit ffce236268
2 changed files with 94 additions and 39 deletions

View File

@ -608,6 +608,28 @@ int foo()
;; Assuming 2 space indent. ;; Assuming 2 space indent.
(should (string= "def xxx\n xxx\nend" (buffer-string))))) (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 () (ert-deftest snippet-with-multiline-mirrors-issue-665 ()
"In issue 665, a multi-line mirror is attempted." "In issue 665, a multi-line mirror is attempted."

View File

@ -3785,6 +3785,9 @@ BEG, END and LENGTH like overlay modification hooks."
(= beg (yas--field-start field)) ; Insertion at field start? (= beg (yas--field-start field)) ; Insertion at field start?
(not (yas--field-modified-p field)))) (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) (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.
@ -3819,12 +3822,29 @@ field start. This hook does nothing if an undo is in progress."
(cl-assert (memq pfield (yas--snippet-fields psnippet))) (cl-assert (memq pfield (yas--snippet-fields psnippet)))
(yas--advance-end-maybe pfield (overlay-end overlay)) (yas--advance-end-maybe pfield (overlay-end overlay))
(setq pfield (yas--snippet-previous-active-field psnippet))))) (setq pfield (yas--snippet-previous-active-field psnippet)))))
(save-excursion ;; Update fields now, but delay auto indentation until
(yas--field-update-display field)) ;; post-command. We don't want to run indentation on
(yas--update-mirrors snippet))) ;; 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!") (lwarn '(yasnippet zombie) :warning "Killing zombie snippet!")
(delete-overlay overlay))))) (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 () (defun yas--auto-fill ()
(let* ((orig-point (point)) (let* ((orig-point (point))
(end (progn (forward-paragraph) (point))) (end (progn (forward-paragraph) (point)))
@ -4800,46 +4820,58 @@ When multiple expressions are found, only the last one counts."
(parent 1) (parent 1)
(t 0)))))) (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) (defun yas--update-mirrors (snippet)
"Update all the mirrors of SNIPPET." "Update all the mirrors of SNIPPET."
(yas--save-restriction-and-widen (yas--save-restriction-and-widen
(save-excursion (save-excursion
(cl-loop (let ((f-ms (yas--snippet-field-mirrors snippet)))
for (field . mirror) (cl-loop
in (cl-sort for (field . mirror) in f-ms
;; Make a list of (FIELD . MIRROR). ;; Before updating a mirror with a parent-field, maybe advance
(cl-mapcan (lambda (field) ;; its start (#290).
(mapcar (lambda (mirror) do (let ((parent-field (yas--mirror-parent-field mirror)))
(cons field mirror)) (when parent-field
(yas--field-mirrors field))) (yas--advance-start-maybe mirror (yas--fom-start parent-field))))
(yas--snippet-fields snippet)) ;; Update this mirror.
;; Then sort this list so that entries with mirrors with do (yas--mirror-update-display mirror field)
;; parent fields appear before. This was important for ;; `yas--place-overlays' is needed since the active field and
;; fixing #290, and also handles the case where a mirror in ;; protected overlays might have been changed because of insertions
;; a field causes another mirror to need reupdating. ;; in `yas--mirror-update-display'.
#'> :key (lambda (fm) (yas--calculate-mirror-depth (cdr fm)))) do (let ((active-field (yas--snippet-active-field snippet)))
;; Before updating a mirror with a parent-field, maybe advance (when active-field (yas--place-overlays snippet active-field))))
;; its start (#290). ;; Delay indenting until we're done all mirrors. We must do
do (let ((parent-field (yas--mirror-parent-field mirror))) ;; this to avoid losing whitespace between fields that are
(when parent-field ;; still empty (i.e., they will be non-empty after updating).
(yas--advance-start-maybe mirror (yas--fom-start parent-field)))) (yas--indent-mirrors-of-snippet snippet f-ms)))))
;; 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)))))))
(defun yas--mirror-update-display (mirror field) (defun yas--mirror-update-display (mirror field)
"Update MIRROR according to FIELD (and mirror transform)." "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). ;; Don't pop up more than once in a session (still log though).
(defvar warning-suppress-types) ; `warnings' is autoloaded by `lwarn'. (defvar warning-suppress-types) ; `warnings' is autoloaded by `lwarn'.
(add-to-list 'warning-suppress-types '(yasnippet auto-fill bug))) (add-to-list 'warning-suppress-types '(yasnippet auto-fill bug)))
(yas--do-todo-field-updates)
(condition-case err (condition-case err
(progn (yas--finish-moving-snippets) (progn (yas--finish-moving-snippets)
(cond ((eq 'undo this-command) (cond ((eq 'undo this-command)