fix: properly handle snippet and mirror markers with `snippet--open-object'

* also snippet--close-object is apparently not needed

* don't call the mirror transforms when field-text is null, just
  return empty string. also zero-length fields considered to have a
  null field-text, not the empty string.

* correctly place markers when inserting objects
This commit is contained in:
Joao Tavora 2013-10-19 15:31:24 +01:00
parent d43f42186d
commit 76bc40fd03

View File

@ -215,8 +215,10 @@ I would need these somewhere in the let* form
(defun snippet--transform-lambda (transform-form) (defun snippet--transform-lambda (transform-form)
`(lambda (field-text) `(lambda (field-text)
(if (null field-text)
""
,(or transform-form ,(or transform-form
'field-text))) 'field-text))))
(defun define--snippet-body (body) (defun define--snippet-body (body)
"Does the actual work for `define-snippet'" "Does the actual work for `define-snippet'"
@ -231,16 +233,24 @@ I would need these somewhere in the let* form
,@(cl-loop ,@(cl-loop
for (sym form) in sym-tuples for (sym form) in sym-tuples
collect (pcase form append (pcase form
(`(field ,_ ,text) (`(field ,_ . ,rest)
`(snippet--insert-field ,sym ,(if (stringp text) `((snippet--insert-object ,sym)
text))) ,(when (stringp (car rest))
`(snippet--with-current-object ,sym
(insert ,(car rest))))))
(`(mirror . ,_) (`(mirror . ,_)
`(snippet--insert-mirror ,sym)) `((snippet--insert-object ,sym)))
((pred stringp) ((pred stringp)
`(insert ,form)) `((insert ,form)))
((pred functionp) ((pred functionp)
`(insert (funcall ,form))))) `((insert (funcall ,form))))))
,@(cl-loop
for (sym form) in sym-tuples
append (pcase form
(`(field . ,_)
`((mapc #'snippet--update-mirror
(snippet--field-mirrors ,sym))))))
(setq snippet--field-overlay (setq snippet--field-overlay
(make-overlay (point) (point) nil nil t)) (make-overlay (point) (point) nil nil t))
@ -389,6 +399,7 @@ can be:
(let ((map (make-sparse-keymap))) (let ((map (make-sparse-keymap)))
(define-key map (kbd "<tab>") 'snippet-next-field) (define-key map (kbd "<tab>") 'snippet-next-field)
(define-key map (kbd "S-<tab>") 'snippet-prev-field) (define-key map (kbd "S-<tab>") 'snippet-prev-field)
(define-key map (kbd "<backtab>") 'snippet-prev-field)
map) map)
"The active keymap while a snippet expansion is in progress.") "The active keymap while a snippet expansion is in progress.")
@ -397,12 +408,14 @@ can be:
(defun snippet--object-next-field (object) (defun snippet--object-next-field (object)
(loop for next = (snippet--object-next object) (loop for next = (snippet--object-next object)
then (snippet--object-next next) then (snippet--object-next next)
while next
when (snippet--field-p next) when (snippet--field-p next)
return next)) return next))
(defun snippet--object-prev-field (object) (defun snippet--object-prev-field (object)
(loop for prev = (snippet--object-prev object) (loop for prev = (snippet--object-prev object)
then (snippet--object-prev prev) then (snippet--object-prev prev)
while prev
when (snippet--field-p prev) when (snippet--field-p prev)
return prev)) return prev))
@ -430,43 +443,44 @@ can be:
""))) "")))
(defun snippet--make-marker () (defun snippet--make-marker ()
(let ((marker (make-marker))) (point-marker))
(set-marker-insertion-type marker t)
(set-marker marker (point))))
(defun snippet--open-markers (object) (defun snippet--object-empty-p (object)
(= (snippet--object-start object)
(snippet--object-end object)))
(defun snippet--objects-adjacent-p (prev next)
(eq (snippet--object-end prev)
(snippet--object-start next)))
(defun snippet--open-object (object)
(set-marker-insertion-type (snippet--object-start object) nil) (set-marker-insertion-type (snippet--object-start object) nil)
(set-marker-insertion-type (snippet--object-end object) t)) (cl-loop for o = object then prev
for prev = (snippet--object-prev o)
while (and prev
(snippet--objects-adjacent-p prev o)
(snippet--object-empty-p prev))
do (set-marker-insertion-type (snippet--object-start prev) nil))
(defun snippet--close-markers (object) (set-marker-insertion-type (snippet--object-end object) t)
(let ((start (snippet--object-start object)) (cl-loop for o = object then next
(end (snippet--object-end object))) for next = (snippet--object-next o)
(cond ((= start end) while (and next
(set-marker-insertion-type start t) (snippet--objects-adjacent-p o next)
(set-marker-insertion-type end t)) (snippet--object-empty-p next))
(t do (set-marker-insertion-type (snippet--object-end next) t)))
(set-marker-insertion-type start t)
(set-marker-insertion-type end nil)))))
(defun snippet--call-with-current-object (object fn) (defun snippet--call-with-current-object (object fn)
(unwind-protect (snippet--open-object object)
(progn
(snippet--open-markers object)
(funcall fn)) (funcall fn))
(snippet--close-markers object)))
(defmacro snippet--with-current-object (object &rest body) (defmacro snippet--with-current-object (object &rest body)
(declare (indent defun) (debug t)) (declare (indent defun) (debug t))
`(snippet--call-with-current-object ,object #'(lambda () ,@body))) `(snippet--call-with-current-object ,object #'(lambda () ,@body)))
(defun snippet--insert-object (object)
(defun snippet--insert-field (field text) (set-marker (snippet--object-start object) (point))
(when text (set-marker (snippet--object-end object) (point)))
(snippet--with-current-object field
(insert text))))
(defun snippet--insert-mirror (mirror)
(snippet--update-mirror mirror))
(defun snippet--update-mirror (mirror) (defun snippet--update-mirror (mirror)
(snippet--with-current-object mirror (snippet--with-current-object mirror
@ -484,24 +498,31 @@ can be:
(snippet--object-end field)) (snippet--object-end field))
(overlay-put snippet--field-overlay 'snippet--field field)) (overlay-put snippet--field-overlay 'snippet--field field))
(defun snippet--field-overlay-changed (overlay after? _beg _end &optional _length) (defun snippet--field-overlay-changed (overlay after? _beg _end
&optional _length)
;; there's a slight (apparently innocuous) bug here: if the overlay has
;; zero-length, both `insert-in-front' and `insert-behind' modification hooks
;; are called
;;
(let* ((field (overlay-get overlay 'snippet--field)) (let* ((field (overlay-get overlay 'snippet--field))
(inhibit-modification-hooks t)) (inhibit-modification-hooks t))
(cond (after? (cond (after?
(snippet--close-markers field)
(mapc #'snippet--update-mirror (snippet--field-mirrors field)) (mapc #'snippet--update-mirror (snippet--field-mirrors field))
(move-overlay overlay (move-overlay overlay
(snippet--object-start field) (snippet--object-start field)
(snippet--object-end field))) (snippet--object-end field)))
(t (t
(snippet--open-markers field))))) (snippet--open-object field)))))
(defun snippet--field-text (field) (defun snippet--field-text (field)
(buffer-substring-no-properties (snippet--object-start field) (let ((start (snippet--object-start field))
(snippet--object-end field))) (end (snippet--object-end field)))
(and (/= start end)
(buffer-substring-no-properties start end))))
(defvar snippet--debug nil) (defvar snippet--debug nil)
;; (setq snippet--debug t) ;; (setq snippet--debug t)
;; (setq snippet--debug nil)
(defun snippet--post-command-hook () (defun snippet--post-command-hook ()
(cond ((and snippet--field-overlay (cond ((and snippet--field-overlay