Decide field clearing based on command's effect

In [1] we started deciding clearing based on a command's
delete-selection property, but it turns out that some commands perform
insertion, and optionally use the region's contents.  So these commands
should have a nil delete-selection property, but they still ought to
clear a snippet field when used.

To achieve this, we now check if the command has inserted text in the
post-change hook of the field overlay.

* yasnippet.el (yas--skip-and-clear): Add optional FROM paramter.  Only
clear non-empty fields.
(yas--skip-and-clear-field-p): Check the change start and length
instead of current command delete-selection property.
(yas--on-field-overlay-modification): Perform field clearing on the
post-change call.

[1]: acf2cdd "Decide field clearing commands based on delsel"
This commit is contained in:
Noam Postavsky 2016-04-23 16:36:10 -04:00
parent 4f2aa1526a
commit 567ada01e6

View File

@ -3348,15 +3348,17 @@ Otherwise deletes a character normally by calling `delete-char'."
(t (t
(call-interactively 'delete-char))))) (call-interactively 'delete-char)))))
(defun yas--skip-and-clear (field) (defun yas--skip-and-clear (field &optional from)
"Deletes the region of FIELD and sets it's modified state to t." "Deletes the region of FIELD and sets it's modified state to t.
If given, FROM indicates position to start at instead of FIELD's beginning."
;; Just before skipping-and-clearing the field, mark its children ;; Just before skipping-and-clearing the field, mark its children
;; fields as modified, too. If the children have mirrors-in-fields ;; fields as modified, too. If the children have mirrors-in-fields
;; this prevents them from updating erroneously (we're skipping and ;; this prevents them from updating erroneously (we're skipping and
;; deleting!). ;; deleting!).
;; ;;
(yas--mark-this-and-children-modified field) (yas--mark-this-and-children-modified field)
(delete-region (yas--field-start field) (yas--field-end field))) (unless (= (yas--field-start field) (yas--field-end field))
(delete-region (or from (yas--field-start field)) (yas--field-end field))))
(defun yas--mark-this-and-children-modified (field) (defun yas--mark-this-and-children-modified (field)
(setf (yas--field-modified-p field) t) (setf (yas--field-modified-p field) t)
@ -3390,40 +3392,33 @@ Move the overlay, or create it if it does not exit."
(overlay-put yas--active-field-overlay 'insert-behind-hooks (overlay-put yas--active-field-overlay 'insert-behind-hooks
'(yas--on-field-overlay-modification)))) '(yas--on-field-overlay-modification))))
(defun yas--skip-and-clear-field-p (field _beg _end &optional _length) (defun yas--skip-and-clear-field-p (field beg _end length)
"Tell if newly modified FIELD should be cleared and skipped. "Tell if newly modified FIELD should be cleared and skipped.
BEG, END and LENGTH like overlay modification hooks." BEG, END and LENGTH like overlay modification hooks."
(and (not (yas--field-modified-p field)) (and (= length 0) ; A 0 pre-change length indicates insertion.
(= (point) (yas--field-start field)) (= beg (yas--field-start field)) ; Insertion at field start?
(require 'delsel) (not (yas--field-modified-p field))))
;; `yank' sets `this-command' to t during execution.
(let* ((command (if (commandp this-command) this-command
this-original-command))
(clearp (if (symbolp command) (get command 'delete-selection))))
(when (and (not (memq clearp '(yank supersede kill)))
(functionp clearp))
(setq clearp (funcall clearp)))
clearp)))
(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 yas--inhibit-overlay-hooks (unless (or (not after?)
yas--inhibit-overlay-hooks
(not (overlayp yas--active-field-overlay)) ; Avoid Emacs bug #21824. (not (overlayp yas--active-field-overlay)) ; Avoid Emacs bug #21824.
(yas--undo-in-progress)) (yas--undo-in-progress))
(let* ((field (overlay-get overlay 'yas--field)) (let* ((inhibit-modification-hooks t)
(field (overlay-get overlay 'yas--field))
(snippet (overlay-get yas--active-field-overlay 'yas--snippet))) (snippet (overlay-get yas--active-field-overlay 'yas--snippet)))
(cond (after? (when (yas--skip-and-clear-field-p field beg end length)
(yas--advance-end-maybe field (overlay-end overlay)) ;; We delete text starting from the END of insertion.
(save-excursion (yas--skip-and-clear field end))
(yas--field-update-display field)) (setf (yas--field-modified-p field) t)
(yas--update-mirrors snippet)) (yas--advance-end-maybe field (overlay-end overlay))
(field (save-excursion
(when (yas--skip-and-clear-field-p field beg end) (yas--field-update-display field))
(yas--skip-and-clear field)) (yas--update-mirrors snippet))))
(setf (yas--field-modified-p field) t))))))
;;; Apropos protection overlays: ;;; Apropos protection overlays:
;; ;;