From 7a0ee29afa797b8e3a8511d20887e04bb0ff1404 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 8 Sep 2008 08:40:01 +0000 Subject: [PATCH 01/75] Trying nested placeholders From cc0211316ed0ea2c0586332ade527c1203145e0c Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 8 Sep 2008 08:42:14 +0000 Subject: [PATCH 02/75] Checking in still largely failing attempt on nested placeholders,,, --- yasnippet.el | 253 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 195 insertions(+), 58 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index e3d51e4..461545a 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -91,14 +91,31 @@ mode will be listed under the menu \"yasnippet\".") (defvar yas/trigger-symbol " =>" "The text that will be used in menu to represent the trigger.") -(defface yas/field-highlight-face - '((((class color) (background light)) (:background "DarkSeaGreen2")) - (t (:background "DimGrey"))) - "The face used to highlight a field of snippet.") -(defface yas/mirror-highlight-face - '((((class color) (background light)) (:background "LightYellow2")) - (t (:background "gray22"))) - "The face used to highlight mirror fields of a snippet.") + +(defun yas/define-multiple-faces (prefix background-color-pairs &optional doc) + "TODO: describe this rebuscated function" + (mapcar #'(lambda (color-pair) + (let* ((depth (position color-pair background-color-pairs))) + (when depth + (eval `(defface ,(intern (format "%s-%d" prefix depth)) + '((((class color) (background light)) (:background ,(first color-pair))) + (t (:background ,(second color-pair)))) + ,(when doc + (format "%s %d." doc depth))))))) + background-color-pairs)) + +;; Define multiple faces up to nested field (and mirror) depth 4 +(eval-when-compile + (yas/define-multiple-faces "yas/field-highlight-face" `(("DarkSeaGreen1" "DimGrey") + ("DarkSeaGreen3" "SlateGrey") + ("DarkOliveGreen2" "LightSlateGrey") + ("DarkOliveGreen4" "Gray")) + "The face used to highlight a field of a snippet with depth ") + (yas/define-multiple-faces "yas/mirror-highlight-face" `(("LightYellow1" "gray22") + ("LightYellow3" "grey32") + ("khaki2" "grey42") + ("khaki4" "grey52")) + "The face used to highlight mirror fields of a snippet with depth ")) (defvar yas/window-system-popup-function #'yas/dropdown-list-popup-for-template "When there's multiple candidate for a snippet key. This function @@ -318,12 +335,13 @@ TODO: describe the rest of the fields" (prev nil) snippet) (defstruct (yas/field - (:constructor yas/make-field (overlay number value transform))) + (:constructor yas/make-field (overlay number value transform parent-field))) "A field in a snippet." overlay number transform - value) + value + parent-field) (defstruct (yas/snippet-table (:constructor yas/make-snippet-table ())) "A table to store snippets for a perticular mode." (hash (make-hash-table :test 'equal)) @@ -336,7 +354,10 @@ TODO: describe the rest of the fields" (not (null (overlay-start (yas/snippet-overlay snippet)))))) (defun yas/snippet-add-field (snippet field) - "Add FIELD to SNIPPET." + "Add FIELD to the correct group of SNIPPET. + +If no group is found, create one using `yas/make-group'. Return +FIELD." (let ((group (find field (yas/snippet-groups snippet) :test @@ -348,7 +369,8 @@ TODO: describe the rest of the fields" (if group (yas/group-add-field group field) (push (yas/make-group field snippet) - (yas/snippet-groups snippet))))) + (yas/snippet-groups snippet))) + field)) (defun yas/group-value (group) "Get the default value of the field group." @@ -620,21 +642,33 @@ redo-ed." start)) (insert key))) -(defun yas/replace-fields-with-value (fields text) - "In all of the fields of the snippet group GROUP fields, delete -whatever value (string) existed and insert TEXT instead. +(defun yas/replace-fields-with-value (fields &optional rep) +;; TODO: revise need for this rebuscatedeness +;; "For all FIELDS, delete characters outside the field's value +;; in field's overlay region. -The string to insert is calculated according to -`yas/calculate-field-value', which might insert different text -for each field." +;; This default behaviour ensures other overlays covered by the same +;; region are not innapropriately displaced. + +;; With optional parameter REP, replace the field with delete whatever value (string) +;; existed and insert the field's text instead instead. + +;; In both cases, to enable producing different replacements for +;; each field, the replacement is calculated according to +;; `yas/calculate-field-value', which is passed the field itself, +;; and, as the second paramenter ,the value of `yas/field-value' or +;; REP if it is non-nil" (dolist (field fields) (let* ((overlay (yas/field-overlay field)) (start (overlay-start overlay)) (end (overlay-end overlay)) - (length (- end start))) - (goto-char start) - (insert (yas/calculate-field-value field text)) - (delete-char length)))) + (length (- end start)) + (text (yas/calculate-field-value field (or rep + (yas/field-value field))))) + (when text + (goto-char start) + (insert text) + (delete-char length))))) (defun yas/expand-snippet (start end template) "Expand snippet at current point. Text between START and END @@ -688,27 +722,7 @@ will be deleted before inserting template." ;; `yas/registered-snippets' var. Create fields. (let ((snippet (yas/register-snippet (yas/make-snippet)))) (goto-char (point-min)) - (while (re-search-forward yas/field-regexp nil t) - (let ((number (or (match-string-no-properties 1) - (match-string-no-properties 2))) - (transform nil) - (value (match-string-no-properties 3))) - (when (eq (elt value 0) ?\$) - (setq transform (substring value 1)) - (setq value nil)) - (if (and number - (string= "0" number)) - (progn - (replace-match "") - (setf (yas/snippet-exit-marker snippet) - (copy-marker (point) t))) - (yas/snippet-add-field - snippet - (yas/make-field - (make-overlay (match-beginning 0) (match-end 0)) - (and number (string-to-number number)) - value - transform))))) + (yas/field-parse-create snippet) ;; Step 6: Sort and link each field group (setf (yas/snippet-groups snippet) @@ -741,10 +755,14 @@ will be deleted before inserting template." (setf (yas/snippet-overlay snippet) overlay) (setf (yas/snippet-end-marker snippet) (overlay-end overlay))) - ;; Step 8: Replace fields with default values + ;; Step 8: Replace mirror field values with primary group's + ;; value (dolist (group (yas/snippet-groups snippet)) - (yas/replace-fields-with-value (yas/group-fields group) - (yas/group-value group))) + (yas/replace-fields-with-value + (remove-if #'(lambda (field) + (eq (yas/group-primary-field group) field)) + (yas/group-fields group)) + (yas/group-value group))) ;; Step 9: restore all escape characters (yas/replace-all yas/escape-dollar "$") @@ -760,12 +778,13 @@ will be deleted before inserting template." (overlay-put overlay 'yas/modified? nil) (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) - (overlay-put overlay 'face 'yas/field-highlight-face) + (overlay-put overlay + 'face (intern (format "yas/field-highlight-face-%d" + (overlay-get overlay 'priority)))) (dolist (field (yas/group-fields group)) (unless (equal overlay (yas/field-overlay field)) (overlay-put (yas/field-overlay field) - 'face - 'yas/mirror-highlight-face))))) + 'face (intern (format "yas/mirror-highlight-face-%d" (overlay-get overlay 'priority)))))))) ;; Step 11: move to end and make sure exit-marker exist (goto-char (point-max)) @@ -806,6 +825,114 @@ will be deleted before inserting template." (replace-match "") (indent-according-to-mode))))))) +(defun yas/field-parse-create (snippet &optional parent-field) + "Parse a recently inserted snippet template, creating all +necessary fields. + +Allows nested placeholder in the style of Textmate." + ;; 5. Search from current point for yas/field-regexp + ;; + ;; a) That is, look for "$<`number'>" or + ;; "${<`number'>:<`value'>}". A few special cases. + ;; + ;; b) When `value' starts with $, assume the rest is a lisp + ;; expression returning string. assign that to `transform' + ;; + ;; A transformation is signalled when `value' starts with the + ;; character "$" as the first value after the ":". The rest of + ;; `value' is not allowed to have any other nested snippet + ;; definitions. Don't know if Textmate allows this... + ;; + ;; c) If `number' is 0 (zero) the string found is deleted and + ;; that special place is the snippet's exit marker... + ;; + ;; d) Otherwise a placeholder field for `number' is added to the + ;; snippet with `value' and `transform'. + ;; + ;; e) Then, still, buffer is temporarily narrowed down to `value' + ;; and `yas/field-parse-create' is called again recursively + ;; with the recently created field as `parent-field'. That + ;; might actually add more fields. + ;; + ;; f) In any case, point is moved to just after the closing bracket + ;; after `value' and the search starts again from a). + ;; + ;; + (while (re-search-forward yas/field-regexp nil t) + (let* ((number (or (match-string-no-properties 1) + (match-string-no-properties 2))) + (transform nil) + (bracket-end (set-marker (make-marker) + (yas/field-bracket-end))) + (value-start (set-marker (make-marker) (match-beginning 3))) + (value-end (set-marker (make-marker) + (or (and (marker-position bracket-end) + (1- bracket-end)) + (match-end 3)))) + (value (when (and (marker-position value-start) + (marker-position value-end)) + (buffer-substring-no-properties value-start value-end))) + brand-new-field) + ;; b) look for a transformation + (when (eq (elt value 0) ?\$) + (setq transform (substring value 1)) + (setq value nil)) + (if (and number + (string= "0" number)) + ;; c) set exit marker and forget + (progn + (replace-match "") + (setf (yas/snippet-exit-marker snippet) + (copy-marker (point) t))) + ;; d) add field + (setq brand-new-field + (yas/snippet-add-field + snippet + (yas/make-field + (make-overlay (match-beginning 0) (or (marker-position bracket-end) + (match-end 0))) + (and number (string-to-number number)) + value + transform + parent-field))) + ;; e) set correct overlay priority + (overlay-put (yas/field-overlay brand-new-field) 'priority + (if parent-field + (1+ (overlay-get (yas/field-overlay parent-field) + 'priority)) + 0)) + (when value + ;; f) delete useless regions, move to correct spot for more + ;; search... + (when (marker-position bracket-end) + (delete-region value-end bracket-end)) + (delete-region (match-beginning 0) value-start) + ;; g) investigate nested placeholders + (save-excursion + (save-restriction + (narrow-to-region value-start value-end) + (goto-char (point-min)) + (yas/field-parse-create snippet brand-new-field)))))))) + +(defun yas/field-bracket-end () + "Calculates position of the field's closing bracket if any. + +Assumes a regexp search for `yas/field-regexp' matched +recently. Return Nil if no field value subexpression was found, +or throws error if the snippet has malformed nested +placeholders." + (let ((bracket-or-number-start (1+ (match-beginning 0))) + bracket-end) + (when (eq ?\{ (char-after bracket-or-number-start)) + (setq bracket-end (condition-case oops + (scan-sexps bracket-or-number-start 1) + ;; TODO: Later should throw another error with + ;; information about failed syntax! + (error + (message "Invalid snippet template!"))))) + bracket-end)) + + (defun yas/current-snippet-overlay (&optional point) "Get the most proper overlay which is belongs to a snippet." (let ((point (or point (point))) @@ -1356,17 +1483,23 @@ current buffer." "Cleanup SNIPPET, but leave point as it is. This renders the snippet as ordinary text" (let* ((overlay (yas/snippet-overlay snippet)) - (yas/snippet-beg (overlay-start overlay)) - (yas/snippet-end (overlay-end overlay))) + yas/snippet-beg yas/snippet-end) ;; save the end of the moribund snippet in case we need to undo ;; its original expansion. This is used by `yas/undo-expand-snippet' (when (and overlay (overlay-buffer overlay)) + (setq yas/snippet-beg (overlay-start overlay)) + (setq yas/snippet-end (overlay-end overlay)) (setf (yas/snippet-end-marker snippet) yas/snippet-end) (delete-overlay overlay)) (dolist (group (yas/snippet-groups snippet)) (dolist (field (yas/group-fields group)) (delete-overlay (yas/field-overlay field)))) + ;; XXX: `yas/after-exit-snippet-hook' should be run with + ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not + ;; be the case if the main overlay had somehow already + ;; disappeared, which sometimes happens when the snippet's messed + ;; up... (run-hooks 'yas/after-exit-snippet-hook)) (yas/unregister-snippet snippet) (setq buffer-undo-list (yas/snippet-saved-buffer-undo-list snippet))) @@ -1446,9 +1579,10 @@ is pushed into this variable and it then replaces should not trigger any undo-recording action") (defun yas/field-undo-before-hook () - "Saves the field-level undo history, `buffer-undo-list' into a global -`yas/field-undo-history' variable just before a command is -performed. It will come in handy in case the command is to be undone" + "Saves the field-level undo history, `buffer-undo-list' into a +global `yas/field-undo-history' variable just before a command is +performed. That variable will come in handy in case the command +is to be undone" (setq yas/field-undo-history buffer-undo-list)) (defun yas/field-undo-after-hook () @@ -1482,7 +1616,7 @@ be a part of that list while registered snippets last." ;; there first. Have no clue why sometimes one is and one ;; isn't. ;; - (unless (null (car yas/field-undo-history)) + (unless (null (car-safe yas/field-undo-history)) (push nil yas/field-undo-history)) (push `(apply yas/field-undo-group-text-change ,group @@ -1517,7 +1651,9 @@ be a part of that list while registered snippets last." (with-output-to-temp-buffer "*YASnippet trace*" (princ "Interesting YASnippet vars: \n\n") (princ (format "Register hash-table: %s\n\n" yas/registered-snippets)) - (cond ((eq (hash-table-count yas/registered-snippets) 0) + (cond ((not yas/registered-snippets) + (princ " No snippet hash table!")) + ((eq (hash-table-count yas/registered-snippets) 0) (princ " No registered snippets\n")) (t (maphash #'(lambda (key snippet) @@ -1526,7 +1662,8 @@ be a part of that list while registered snippets last." (yas/snippet-id snippet) (length (yas/snippet-groups snippet)))) (dolist (group (yas/snippet-groups snippet)) - (princ (format "\t group with %s fields. Primary field is value is \"%s\"\n" + (princ (format "\t group $%s with %s fields. Primary field is value is \"%s\"\n" + (yas/group-number group) (length (yas/group-fields group)) (yas/field-value (yas/group-primary-field group)))))) yas/registered-snippets))) @@ -1542,7 +1679,7 @@ be a part of that list while registered snippets last." (let ((undo-list buffer-undo-list)) (dotimes (i 10) (when undo-list - (princ (format "%s: %s\n" i (car undo-list))) + (princ (format "%s: %s\n" i (car-safe undo-list))) (setq undo-list (cdr undo-list))))))) From 4a2db923bbc9e05a46c44dabe9b2143941e50625 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 8 Sep 2008 15:29:20 +0000 Subject: [PATCH 03/75] Nested placeholders, more or less working, but had to get rid of the other workaround (which was a bit ugly anyway). Problem is that I have to find another workaround. --- yasnippet.el | 329 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 213 insertions(+), 116 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 461545a..9b15b55 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -62,9 +62,9 @@ major modes.") current column if this variable is non-`nil'.") (make-variable-buffer-local 'yas/indent-line) -(defvar yas/trigger-key (kbd "TAB") +(defvar yas/trigger-key (kbd "") "The key to bind as a trigger of snippet.") -(defvar yas/next-field-key (kbd "TAB") +(defvar yas/next-field-key (kbd "") "The key to navigate to next field.") (defvar yas/keymap (make-sparse-keymap) @@ -245,11 +245,18 @@ to expand. (list 'yas/overlay-modification-hook) "The list of hooks to the overlay modification event.") (defvar yas/overlay-insert-in-front-hooks - (list 'yas/overlay-insert-in-front-hook) - "The list of hooks of the overlay inserted in front event.") -(defvar yas/keymap-overlay-modification-hooks - (list 'yas/overlay-maybe-insert-behind-hook) - "The list of hooks of the big keymap overlay modification event.") + (list 'yas/overlay-insert-in-front-hook) + "The list of hooks of the overlay inserted in front event.") +(defvar yas/overlay-insert-behind-hooks + (list 'yas/overlay-insert-behind-hook) + "The list of hooks of the overlay inserted behind event.") + + +(setq yas/keymap-overlay-modification-hooks nil) + +;; (defvar yas/keymap-overlay-modification-hooks +;; (list 'yas/overlay-maybe-insert-behind-hook) +;; "The list of hooks of the big keymap overlay modification event.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; YASnippet minor mode @@ -341,7 +348,9 @@ TODO: describe the rest of the fields" number transform value - parent-field) + parent-field + subfields + group) (defstruct (yas/snippet-table (:constructor yas/make-snippet-table ())) "A table to store snippets for a perticular mode." (hash (make-hash-table :test 'equal)) @@ -368,9 +377,11 @@ FIELD." (yas/group-number group))))))) (if group (yas/group-add-field group field) - (push (yas/make-group field snippet) - (yas/snippet-groups snippet))) - field)) + (setq group (yas/make-group field snippet)) + (push group (yas/snippet-groups snippet))) + + (setf (yas/field-group field) group)) + field) (defun yas/group-value (group) "Get the default value of the field group." @@ -547,8 +558,8 @@ the template of a snippet in the current snippet-table." start end))) -(defun yas/synchronize-fields (field-group) - "Update all fields' text according to the primary field." +(defun yas/synchronize-fields (field-group &optional dont-recurse-down) + "Update all mirror fields' text according to the primary field." (when (yas/snippet-valid? (yas/group-snippet field-group)) (save-excursion (let* ((inhibit-modification-hooks t) @@ -558,7 +569,16 @@ the template of a snippet in the current snippet-table." (yas/replace-fields-with-value (remove-if #'(lambda (field) (equal field primary)) (yas/group-fields field-group)) - text))))) + text) + ;; Call recursively for subfields + (unless dont-recurse-down + (dolist (subfield (yas/field-subfields primary)) + (yas/synchronize-fields (yas/field-group subfield)))) + ;; Call recursively for parent field + (when (yas/field-parent-field primary) + (yas/synchronize-fields (yas/field-group (yas/field-parent-field primary)) + 'dont-recurse)))))) + (defun yas/current-field-text (field) (let ((primary-overlay (yas/field-overlay field))) (when primary-overlay @@ -567,7 +587,18 @@ the template of a snippet in the current snippet-table." (defun yas/overlay-modification-hook (overlay after? beg end &optional length) - "Modification hook for snippet field overlay." + "Synchronizes all fields for the group of the current field overlay + +Used to ensure mirror fields in the same group contain the same value +of the primary field." + (message (format "Running mod hook for %s of %s." + (cond ((overlay-get overlay 'yas/snippet-reference) + (format "big overlay of snippet %s," (yas/snippet-id (overlay-get overlay 'yas/snippet-reference)))) + ((overlay-get overlay 'yas/group) + (format "field overlay of group $%s," (yas/group-number (overlay-get overlay 'yas/group)))) + (t + "STH UNKNOWN")) + overlay)) (when (and after? (not undo-in-progress)) (yas/synchronize-fields (overlay-get overlay 'yas/group)))) @@ -584,43 +615,55 @@ the template of a snippet in the current snippet-table." (delete-char (- (overlay-end overlay) end))))) (yas/synchronize-fields field-group)))) -(defun yas/overlay-maybe-insert-behind-hook (overlay after? beg end &optional length) - "Insert behind hook sometimes doesn't get called. I don't know why. -So I add modification hook in the big overlay and try to detect `insert-behind' -event manually." - (when after? - (cond ((and (= beg end) - (> length 0) - (= (overlay-start overlay) - (overlay-end overlay))) - (yas/exit-snippet (overlay-get overlay 'yas/snippet-reference))) - ((and (= length 0) - (> end beg) - (null (yas/current-snippet-overlay beg)) - (not (bobp))) - (let ((field-overlay (yas/current-snippet-overlay (1- beg)))) - (if field-overlay - (when (= beg (overlay-end field-overlay)) - (move-overlay field-overlay - (overlay-start field-overlay) - end) - (yas/synchronize-fields (overlay-get field-overlay 'yas/group))) - (let ((snippet (yas/snippet-of-current-keymap)) - (done nil)) - (if snippet - (do* ((groups (yas/snippet-groups snippet) (cdr groups)) - (group (car groups) (car groups))) - ((or (null groups) - done)) - (setq field-overlay (yas/field-overlay - (yas/group-primary-field group))) - (when (and (= (overlay-start field-overlay) - (overlay-end field-overlay)) - (= beg - (overlay-start field-overlay))) - (move-overlay field-overlay beg end) - (yas/synchronize-fields group) - (setq done t))))))))))) +(defun yas/overlay-insert-behind-hook (overlay after? beg end &optional length) + "Hook for snippet overlay when text is inserted just behind a snippet field." + (let ((current-field-overlay (yas/current-field-overlay beg))) + (when (and after? + (or (null current-field-overlay) ; not inside another field + (< (overlay-get current-field-overlay 'priority) + (overlay-get overlay 'priority)))) + (move-overlay overlay + (overlay-start overlay) + end) + (yas/synchronize-fields (overlay-get overlay 'yas/group))))) + +;; (defun yas/overlay-maybe-insert-behind-hook (overlay after? beg end &optional length) +;; "Insert behind hook sometimes doesn't get called. I don't know why. +;; So I add modification hook in the big overlay and try to detect `insert-behind' +;; event manually." +;; (when after? +;; (cond ((and (= beg end) +;; (> length 0) +;; (= (overlay-start overlay) +;; (overlay-end overlay))) +;; (yas/exit-snippet (overlay-get overlay 'yas/snippet-reference))) +;; ((and (= length 0) +;; (> end beg) +;; (null (yas/current-field-overlay beg)) +;; (not (bobp))) +;; (let ((field-overlay (yas/current-field-overlay (1- beg)))) +;; (if field-overlay +;; (when (= beg (overlay-end field-overlay)) +;; (move-overlay field-overlay +;; (overlay-start field-overlay) +;; end) +;; (yas/synchronize-fields (overlay-get field-overlay 'yas/group))) +;; (let ((snippet (yas/snippet-of-current-keymap)) +;; (done nil)) +;; (if snippet +;; (do* ((groups (yas/snippet-groups snippet) (cdr groups)) +;; (group (car groups) (car groups))) +;; ((or (null groups) +;; done)) +;; (setq field-overlay (yas/field-overlay +;; (yas/group-primary-field group))) +;; (when (and (= (overlay-start field-overlay) +;; (overlay-end field-overlay)) +;; (= beg +;; (overlay-start field-overlay))) +;; (move-overlay field-overlay beg end) +;; (yas/synchronize-fields group) +;; (setq done t))))))))))) (defun yas/remove-recent-undo-from-history () (let ((undo (car buffer-undo-list))) @@ -744,13 +787,17 @@ will be deleted before inserting template." nil nil t))) - (overlay-put overlay - 'modification-hooks - yas/keymap-overlay-modification-hooks) - (overlay-put overlay - 'insert-behind-hooks - yas/keymap-overlay-modification-hooks) + ;; XXX: DEBUG: Got rid of this workaround. Hope I can find + ;; some other one. + ;; + ;; (overlay-put overlay + ;; 'modification-hooks + ;; yas/keymap-overlay-modification-hooks) + ;; (overlay-put overlay + ;; 'insert-behind-hooks + ;; yas/keymap-overlay-modification-hooks) (overlay-put overlay 'keymap yas/keymap) + (overlay-put overlay 'priority 10) ;; FIXME: hardcoded value here! (overlay-put overlay 'yas/snippet-reference snippet) (setf (yas/snippet-overlay snippet) overlay) (setf (yas/snippet-end-marker snippet) (overlay-end overlay))) @@ -778,6 +825,7 @@ will be deleted before inserting template." (overlay-put overlay 'yas/modified? nil) (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) + (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) (overlay-put overlay 'face (intern (format "yas/field-highlight-face-%d" (overlay-get overlay 'priority)))) @@ -849,13 +897,21 @@ Allows nested placeholder in the style of Textmate." ;; d) Otherwise a placeholder field for `number' is added to the ;; snippet with `value' and `transform'. ;; - ;; e) Then, still, buffer is temporarily narrowed down to `value' - ;; and `yas/field-parse-create' is called again recursively - ;; with the recently created field as `parent-field'. That - ;; might actually add more fields. + ;; e) Correct overlay priority is set to increment by one the + ;; priority of `parent-field' if that is passed, effectively + ;; describing the current recursion level. ;; - ;; f) In any case, point is moved to just after the closing bracket - ;; after `value' and the search starts again from a). + ;; f) The enclosing "${<`number'>:" and closing bracket regions are + ;; delete. + ;; + ;; g) Then, still, buffer is temporarily narrowed down to `value' + ;; and `yas/field-parse-create' is called again recursively with + ;; the recently created field as `parent-field'. That might + ;; actually add more fields. + ;; + ;; h) Update `value' of the newly created field to adjust for some + ;; possible pruning that happened in the subcalls to + ;; `yas/field-parse-create' ;; ;; (while (re-search-forward yas/field-regexp nil t) @@ -884,7 +940,8 @@ Allows nested placeholder in the style of Textmate." (replace-match "") (setf (yas/snippet-exit-marker snippet) (copy-marker (point) t))) - ;; d) add field + ;; d) add a brand new field, linking it to the possible parent + ;; field and adding it to the parent field's subfield list. (setq brand-new-field (yas/snippet-add-field snippet @@ -895,6 +952,9 @@ Allows nested placeholder in the style of Textmate." value transform parent-field))) + (when parent-field + (setf (yas/field-subfields parent-field) + (push brand-new-field (yas/field-subfields parent-field)))) ;; e) set correct overlay priority (overlay-put (yas/field-overlay brand-new-field) 'priority (if parent-field @@ -912,7 +972,10 @@ Allows nested placeholder in the style of Textmate." (save-restriction (narrow-to-region value-start value-end) (goto-char (point-min)) - (yas/field-parse-create snippet brand-new-field)))))))) + (yas/field-parse-create snippet brand-new-field))) + ;; h) + (setf (yas/field-value brand-new-field) + (buffer-substring-no-properties value-start value-end))))))) (defun yas/field-bracket-end () "Calculates position of the field's closing bracket if any. @@ -933,24 +996,30 @@ placeholders." bracket-end)) -(defun yas/current-snippet-overlay (&optional point) - "Get the most proper overlay which is belongs to a snippet." - (let ((point (or point (point))) - (snippet-overlay nil)) - (dolist (overlay (overlays-at point)) - ;; appending and removing-duplicates fixes a bug when overlays - ;; are not recognized because point is really at the end - (when (overlay-get overlay 'yas/snippet) - (if (null snippet-overlay) - (setq snippet-overlay overlay) - (when (> (yas/snippet-id (overlay-get overlay 'yas/snippet)) - (yas/snippet-id (overlay-get snippet-overlay 'yas/snippet))) - (setq snippet-overlay overlay))))) - snippet-overlay)) +(defun yas/current-field-overlay (&optional point) + "Return the most ." + (let ((point (or point (point)))) + (car (sort (delete-if-not #'(lambda (overlay) + (overlay-get overlay 'yas/snippet)) + (overlays-at point)) + #'(lambda (overlay1 overlay2) + (let ((id-1 (yas/snippet-id (overlay-get overlay1 'yas/snippet))) + (id-2 (yas/snippet-id (overlay-get overlay2 'yas/snippet))) + (prio-1 (overlay-get overlay1 'priority)) + (prio-2 (overlay-get overlay2 'priority))) + (cond ((> id-1 id-2) + t) + ((< id-1 id-2) + nil) + ((> prio-1 prio-2) + t) + (t + nil)))))))) (defun yas/snippet-of-current-keymap (&optional point) - "Get the snippet holding the snippet keymap under POINT." - (let ((point (or point (point))) + "Return the most recently inserted snippet holding covering +POINT." + (let ((point (or point (point))) (keymap-snippet nil) (snippet nil)) (dolist (overlay (overlays-at point)) @@ -964,21 +1033,30 @@ placeholders." keymap-snippet)) (defun yas/current-overlay-for-navigation () - "Get current overlay for navigation. Might be overlay at current or previous point." - (let ((overlay1 (yas/current-snippet-overlay)) - (overlay2 (if (bobp) - nil - (yas/current-snippet-overlay (- (point) 1))))) - (if (null overlay1) - overlay2 - (if (or (null overlay2) - (eq (overlay-get overlay1 'yas/snippet) - (overlay-get overlay2 'yas/snippet))) - overlay1 - (if (> (yas/snippet-id (overlay-get overlay2 'yas/snippet)) - (yas/snippet-id (overlay-get overlay1 'yas/snippet))) - overlay2 - overlay1))))) + "Get current overlay for navigation. + + +XXX: FIXME: investigate why: Might be overlay at current or previous point." + (yas/current-field-overlay)) + + +;;XXX: DEBUG removed + + + ;; (let ((overlay1 (yas/current-field-overlay)) + ;; (overlay2 (if (bobp) + ;; nil + ;; (yas/current-field-overlay (- (point) 1))))) + ;; (if (null overlay1) + ;; overlay2 + ;; (if (or (null overlay2) + ;; (eq (overlay-get overlay1 'yas/snippet) + ;; (overlay-get overlay2 'yas/snippet))) + ;; overlay1 + ;; (if (> (yas/snippet-id (overlay-get overlay2 'yas/snippet)) + ;; (yas/snippet-id (overlay-get overlay1 'yas/snippet))) + ;; overlay2 + ;; overlay1))))) (defun yas/navigate-group (group next?) "Go to next of previous field group. Exit snippet if none." @@ -1592,8 +1670,8 @@ information is added to `buffer-undo-list' This function is added to the `post-command-hook' and should be a part of that list while registered snippets last." - (let* ((overlay (or (yas/current-snippet-overlay) - (yas/current-snippet-overlay (1- (point))))) + (let* ((overlay (or (yas/current-field-overlay) + (yas/current-field-overlay (1- (point))))) (group (when overlay (overlay-get overlay 'yas/group)))) (when group @@ -1657,31 +1735,50 @@ be a part of that list while registered snippets last." (princ " No registered snippets\n")) (t (maphash #'(lambda (key snippet) - (princ (format "\t key %s for snippet %s with %s groups\n" + (princ (format "\t key %s for snippet %s" key - (yas/snippet-id snippet) - (length (yas/snippet-groups snippet)))) + (yas/snippet-id snippet))) + + + (princ (format "\t Big priority %s overlay %s\n\n" + (overlay-get (yas/snippet-overlay snippet) 'priority) + (yas/snippet-overlay snippet))) + + + (dolist (group (yas/snippet-groups snippet)) - (princ (format "\t group $%s with %s fields. Primary field is value is \"%s\"\n" + (princ (format "\t group $%s with %s fields.\n" (yas/group-number group) - (length (yas/group-fields group)) - (yas/field-value (yas/group-primary-field group)))))) + (length (yas/group-fields group)))) + (dolist (field (yas/group-fields group)) + (let ((overlay (yas/field-overlay field))) + (princ (format "\t %s field. Saved (%s) . " + (if (eq field (yas/group-primary-field group)) + "Primary" "Mirror") + (yas/field-value (yas/group-primary-field group)))) + (if (and (overlayp overlay) + (overlay-buffer overlay)) + (princ (format "Priority %d overlay (%d:%d:%s)\n" + (overlay-get overlay 'priority) + (overlay-start overlay) + (overlay-end overlay) + (buffer-substring (overlay-start overlay) (overlay-end overlay)))) + (princ "NO OVERLAY\n")))))) yas/registered-snippets))) (princ (format "\nPost command hook: %s\n" post-command-hook)) (princ (format "\nPre command hook: %s\n" pre-command-hook)) - (princ (format "\nUndo is %s. Undolist has %s elements. First 10 elements follow:\n" - (if (eq buffer-undo-list t) - "DISABLED" - "ENABLED") - (length buffer-undo-list))) - (let ((undo-list buffer-undo-list)) - (dotimes (i 10) - (when undo-list - (princ (format "%s: %s\n" i (car-safe undo-list))) - (setq undo-list (cdr undo-list))))))) - + ;; (princ (format "\nUndo is %s." + ;; (if (eq buffer-undo-list t) + ;; "DISABLED" + ;; "ENABLED"))) + ;; (unless (eq buffer-undo-list t) + ;; (princ (format "Undolist has %s elements. First 3 elements follow:\n" (length buffer-undo-list))) + ;; (let ((first-ten (subseq buffer-undo-list 0 2))) + ;; (dolist (undo-elem first-ten) + ;; (princ (format "%s: %s\n" (position undo-elem first-ten) undo-elem))))) +)) (provide 'yasnippet) From e899cb4f833f6993fb5aee7b6fc2ba50383c814f Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 8 Sep 2008 17:06:16 +0000 Subject: [PATCH 04/75] Big bugs but it's around! Field level undo obviously needs a massive change.... --- yasnippet.el | 161 +++++++++++++++++++-------------------------------- 1 file changed, 59 insertions(+), 102 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 9b15b55..70ac7c2 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -340,7 +340,9 @@ TODO: describe the rest of the fields" (fields (list primary-field)) (next nil) (prev nil) - snippet) + snippet + deleted + modified) (defstruct (yas/field (:constructor yas/make-field (overlay number value transform parent-field))) "A field in a snippet." @@ -604,21 +606,33 @@ of the primary field." (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length) "Hook for snippet overlay when text is inserted in front of a snippet field." - (when after? - (let ((field-group (overlay-get overlay 'yas/group)) - (inhibit-modification-hooks t)) - (when (not (overlay-get overlay 'yas/modified?)) - (overlay-put overlay 'yas/modified? t) - (when (> (overlay-end overlay) end) - (save-excursion - (goto-char end) - (delete-char (- (overlay-end overlay) end))))) - (yas/synchronize-fields field-group)))) + (let ((group (overlay-get overlay 'yas/group))) + (when (and after? + group + (not (yas/group-deleted group))) + (let ((inhibit-modification-hooks t)) + ;; If the group hasn't ever been modified, delete it + ;; completely. + (when (not (yas/group-modified group)) + (setf (yas/group-modified group) t) + (when (> (overlay-end overlay) end) + (save-excursion + (goto-char end) + (delete-char (- (overlay-end overlay) end)))) + ;; Mark subgroups as `yas/group-deleted', so insert-in-front + ;; and behind hooks won't be run by them. + (mapcar #'(lambda (group) + (setf (yas/group-deleted group) t)) + (mapcar #'yas/field-group (yas/field-subfields (yas/group-primary-field group))))) + ;; in any case, synchronize mirror fields + (yas/synchronize-fields group))))) (defun yas/overlay-insert-behind-hook (overlay after? beg end &optional length) "Hook for snippet overlay when text is inserted just behind a snippet field." - (let ((current-field-overlay (yas/current-field-overlay beg))) + (let ((current-field-overlay (yas/current-field-overlay beg)) + (group (overlay-get overlay 'yas/group))) (when (and after? + (not (yas/group-deleted group)) (or (null current-field-overlay) ; not inside another field (< (overlay-get current-field-overlay 'priority) (overlay-get overlay 'priority)))) @@ -787,8 +801,9 @@ will be deleted before inserting template." nil nil t))) - ;; XXX: DEBUG: Got rid of this workaround. Hope I can find - ;; some other one. + ;; XXX: DEBUG: Got rid of this workaround and used old + ;; `yas/overlay-insert-behind-hook' . Hope I can find some + ;; other one. ;; ;; (overlay-put overlay ;; 'modification-hooks @@ -822,7 +837,6 @@ will be deleted before inserting template." (yas/group-primary-field group)))) (overlay-put overlay 'yas/snippet snippet) (overlay-put overlay 'yas/group group) - (overlay-put overlay 'yas/modified? nil) (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) @@ -995,7 +1009,6 @@ placeholders." (message "Invalid snippet template!"))))) bracket-end)) - (defun yas/current-field-overlay (&optional point) "Return the most ." (let ((point (or point (point)))) @@ -1032,43 +1045,6 @@ POINT." (setq keymap-snippet snippet))))) keymap-snippet)) -(defun yas/current-overlay-for-navigation () - "Get current overlay for navigation. - - -XXX: FIXME: investigate why: Might be overlay at current or previous point." - (yas/current-field-overlay)) - - -;;XXX: DEBUG removed - - - ;; (let ((overlay1 (yas/current-field-overlay)) - ;; (overlay2 (if (bobp) - ;; nil - ;; (yas/current-field-overlay (- (point) 1))))) - ;; (if (null overlay1) - ;; overlay2 - ;; (if (or (null overlay2) - ;; (eq (overlay-get overlay1 'yas/snippet) - ;; (overlay-get overlay2 'yas/snippet))) - ;; overlay1 - ;; (if (> (yas/snippet-id (overlay-get overlay2 'yas/snippet)) - ;; (yas/snippet-id (overlay-get overlay1 'yas/snippet))) - ;; overlay2 - ;; overlay1))))) - -(defun yas/navigate-group (group next?) - "Go to next of previous field group. Exit snippet if none." - (let ((target (if next? - (yas/group-next group) - (yas/group-prev group)))) - (if target - (goto-char (overlay-start - (yas/field-overlay - (yas/group-primary-field target)))) - (yas/exit-snippet (yas/group-snippet group))))) - (defun yas/parse-template (&optional file-name) "Parse the template in the current buffer. If the buffer contains a line of \"# --\" then the contents @@ -1430,7 +1406,7 @@ when the condition evaluated to non-nil." (if template (progn (yas/expand-snippet start end template) 'expanded) ; expanded successfully - 'interruptted)) ; interrupted by user + 'interrupted)) ; interrupted by user (if (eq yas/fallback-behavior 'return-nil) nil ; return nil (let* ((yas/minor-mode nil) @@ -1438,59 +1414,40 @@ when the condition evaluated to non-nil." (when (commandp command) (call-interactively command)))))))))) -(defun yas/next-field-group () +(defun yas/current-field-overlay-for-navigation () + ;; FIXME: has big bug + (or (yas/current-field-overlay (1- (point))) + (yas/current-field-overlay))) + +(defun yas/next-field-group (&optional arg) "Navigate to next field group. If there's none, exit the snippet." (interactive) - (let ((overlay (yas/current-overlay-for-navigation))) - (if overlay - (yas/navigate-group (overlay-get overlay 'yas/group) t) - (let ((snippet (yas/snippet-of-current-keymap)) - (done nil)) - (if snippet - (do* ((groups (yas/snippet-groups snippet) (cdr groups)) - (group (car groups) (car groups))) - ((or (null groups) - done) - (unless done - (let* ((overlay (yas/snippet-overlay snippet)) - (keymap (overlay-get overlay 'keymap)) - (command nil)) - (overlay-put overlay 'keymap nil) - (overlay-put overlay 'yas/snippet-reference nil) - (setq command (key-binding yas/next-field-key)) - (when (commandp command) - (call-interactively command)) - (overlay-put overlay 'keymap keymap) - (overlay-put overlay 'yas/snippet-reference snippet)))) - (when (= (point) - (overlay-start - (yas/field-overlay - (yas/group-primary-field group)))) - (setq done t) - (yas/navigate-group group t)))))))) + (let* ((arg (or arg + 1)) + (overlay (yas/current-field-overlay-for-navigation)) + (number (and overlay + (+ arg + (yas/group-number (overlay-get overlay 'yas/group))))) + (snippet (yas/snippet-of-current-keymap)) + (target-group (and number + snippet + (find-if #'(lambda (group) + (= number (yas/group-number group))) + (yas/snippet-groups snippet))))) + (unless (< number 1) + (if target-group + (goto-char (overlay-start + (yas/field-overlay + (yas/group-primary-field target-group)))) + (when snippet + (yas/exit-snippet snippet)))))) (defun yas/prev-field-group () "Navigate to prev field group. If there's none, exit the snippet." (interactive) - (let ((overlay (yas/current-overlay-for-navigation))) - (if overlay - (yas/navigate-group (overlay-get overlay 'yas/group) nil) - (let ((snippet (yas/snippet-of-current-keymap)) - (done nil)) - (if snippet - (do* ((groups (yas/snippet-groups snippet) (cdr groups)) - (group (car groups) (car groups))) - ((or (null groups) - done) - (unless done (message "Not in a snippet field."))) - (when (= (point) - (overlay-start - (yas/field-overlay - (yas/group-primary-field group)))) - (setq done t) - (yas/navigate-group group nil))) - (message "Not in a snippet field.")))))) - + + (yas/next-field-group -1)) + (defun yas/exit-snippet (snippet) "Goto exit-marker of SNIPPET and cleanup the snippe. Cleaning up the snippet does not delete it!" @@ -1604,7 +1561,7 @@ registered snippets last." (<= (point) (overlay-end primary-overlay))))) (yas/snippet-groups snippet))) (yas/cleanup-snippet snippet)) - ( ;; + (;; ;; Snippet at point, and point inside a snippet field, ;; everything is normal ;; From 058b7333af81717d17c3af4a972af4eeb51f4435 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Tue, 9 Sep 2008 14:07:54 +0000 Subject: [PATCH 05/75] * Many bugs, gotta look for them later, now have to make my cv... --- yasnippet.el | 339 +++++++-------------------------------------------- 1 file changed, 44 insertions(+), 295 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 70ac7c2..6572a6f 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -332,6 +332,7 @@ TODO: describe the rest of the fields" (id (yas/snippet-next-id) :read-only t) (overlay nil) (saved-buffer-undo-list nil) + (active-group nil) (end-marker nil)) (defstruct (yas/group (:constructor yas/make-group (primary-field snippet))) @@ -341,8 +342,8 @@ TODO: describe the rest of the fields" (next nil) (prev nil) snippet - deleted - modified) + (deleted nil) + (modified nil)) (defstruct (yas/field (:constructor yas/make-field (overlay number value transform parent-field))) "A field in a snippet." @@ -870,13 +871,14 @@ will be deleted before inserting template." (setq buffer-undo-list original-undo-list) ;; Step 15: place the cursor at a proper place - (let ((groups (yas/snippet-groups snippet)) - (exit-marker (yas/snippet-exit-marker snippet))) + (let* ((groups (yas/snippet-groups snippet)) + (exit-marker (yas/snippet-exit-marker snippet)) + (first-group (setf (yas/snippet-active-group snippet) (car groups)))) (if groups (goto-char (overlay-start (yas/field-overlay (yas/group-primary-field - (car groups))))) + first-group)))) ;; no need to call exit-snippet, since no overlay created. (yas/exit-snippet snippet))) @@ -975,21 +977,24 @@ Allows nested placeholder in the style of Textmate." (1+ (overlay-get (yas/field-overlay parent-field) 'priority)) 0)) + ;; f) delete useless regions, move to correct spot for more + ;; search... + (delete-region (match-beginning 0) (or (marker-position value-start) + (point))) (when value - ;; f) delete useless regions, move to correct spot for more - ;; search... - (when (marker-position bracket-end) - (delete-region value-end bracket-end)) - (delete-region (match-beginning 0) value-start) - ;; g) investigate nested placeholders - (save-excursion - (save-restriction - (narrow-to-region value-start value-end) - (goto-char (point-min)) - (yas/field-parse-create snippet brand-new-field))) - ;; h) + (when (marker-position bracket-end) + (delete-region value-end bracket-end)) + + ;; g) investigate nested placeholders + (save-excursion + (save-restriction + (narrow-to-region value-start value-end) + (goto-char (point-min)) + (yas/field-parse-create snippet brand-new-field))) + ;; h) (setf (yas/field-value brand-new-field) - (buffer-substring-no-properties value-start value-end))))))) + (buffer-substring-no-properties value-start value-end)) + ))))) (defun yas/field-bracket-end () "Calculates position of the field's closing bracket if any. @@ -1414,33 +1419,37 @@ when the condition evaluated to non-nil." (when (commandp command) (call-interactively command)))))))))) -(defun yas/current-field-overlay-for-navigation () - ;; FIXME: has big bug - (or (yas/current-field-overlay (1- (point))) - (yas/current-field-overlay))) +(defun yas/current-group-for-navigation (&optional snippet) + (or (and snippet + (yas/snippet-active-group snippet)) + (overlay-get (or (yas/current-field-overlay (1- (point))) + (yas/current-field-overlay)) 'yas/group))) (defun yas/next-field-group (&optional arg) "Navigate to next field group. If there's none, exit the snippet." (interactive) (let* ((arg (or arg 1)) - (overlay (yas/current-field-overlay-for-navigation)) - (number (and overlay - (+ arg - (yas/group-number (overlay-get overlay 'yas/group))))) (snippet (yas/snippet-of-current-keymap)) + (number (and snippet + (+ arg + (yas/group-number (yas/current-group-for-navigation snippet))))) (target-group (and number - snippet + (> number 0) (find-if #'(lambda (group) - (= number (yas/group-number group))) + (and (not (yas/group-deleted group)) + (= number (yas/group-number group)))) (yas/snippet-groups snippet))))) - (unless (< number 1) - (if target-group - (goto-char (overlay-start - (yas/field-overlay - (yas/group-primary-field target-group)))) - (when snippet - (yas/exit-snippet snippet)))))) + (cond ((and number + (> number (length (remove-if #'yas/group-deleted (yas/snippet-groups snippet))))) + (yas/exit-snippet snippet)) + (target-group + (goto-char (overlay-start + (yas/field-overlay + (yas/group-primary-field target-group)))) + (setf (yas/snippet-active-group snippet) target-group)) + (t + nil)))) (defun yas/prev-field-group () "Navigate to prev field group. If there's none, exit the snippet." @@ -1753,263 +1762,3 @@ handle the end-of-buffer error fired in it by calling (condition-case err ad-do-it (error (message (error-message-string err))))) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Contents of dropdown-list.el -;; -;; dropdown-list.el is used by yasnippet to select multiple -;; candidate snippets. -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; dropdown-list.el --- Drop-down menu interface -;; -;; Filename: dropdown-list.el -;; Description: Drop-down menu interface -;; Author: Jaeyoun Chung [jay.chung@gmail.com] -;; Maintainer: -;; Copyright (C) 2008 Jaeyoun Chung -;; Created: Sun Mar 16 11:20:45 2008 (Pacific Daylight Time) -;; Version: -;; Last-Updated: Sun Mar 16 12:19:49 2008 (Pacific Daylight Time) -;; By: dradams -;; Update #: 43 -;; URL: http://www.emacswiki.org/cgi-bin/wiki/dropdown-list.el -;; Keywords: convenience menu -;; Compatibility: GNU Emacs 21.x, GNU Emacs 22.x -;; -;; Features that might be required by this library: -;; -;; `cl'. -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; Commentary: -;; -;; According to Jaeyoun Chung, "overlay code stolen from company-mode.el." -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; Change log: -;; -;; 2008/03/16 dadams -;; Clean-up - e.g. use char-to-string for control chars removed by email posting. -;; Moved example usage code (define-key*, command-selector) inside the library. -;; Require cl.el at byte-compile time. -;; Added GPL statement. -;; 2008/01/06 Jaeyoun Chung -;; Posted to gnu-emacs-sources@gnu.org at 9:10 p.m. -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License as -;; published by the Free Software Foundation; either version 3, or -;; (at your option) any later version. -;; -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -;; General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with this program; see the file COPYING. If not, write to -;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth -;; Floor, Boston, MA 02110-1301, USA. -;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;;; Code: - -(eval-when-compile (require 'cl)) ;; decf, fourth, incf, loop, mapcar* - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defface dropdown-list-face - '((t :inherit default :background "lightyellow" :foreground "black")) - "*Bla." :group 'dropdown-list) - -(defface dropdown-list-selection-face - '((t :inherit dropdown-list-face :background "purple")) - "*Bla." :group 'dropdown-list) - -(defvar dropdown-list-overlays nil) - -(defun dropdown-list-hide () - (while dropdown-list-overlays - (delete-overlay (pop dropdown-list-overlays)))) - -(defun dropdown-list-put-overlay (beg end &optional prop value prop2 value2) - (let ((ov (make-overlay beg end))) - (overlay-put ov 'window t) - (when prop - (overlay-put ov prop value) - (when prop2 (overlay-put ov prop2 value2))) - ov)) - -(defun dropdown-list-line (start replacement &optional no-insert) - ;; start might be in the middle of a tab, which means we need to hide the - ;; tab and add spaces - (let ((end (+ start (length replacement))) - beg-point end-point - before-string after-string) - (goto-char (point-at-eol)) - (if (< (current-column) start) - (progn (setq before-string (make-string (- start (current-column)) ? )) - (setq beg-point (point))) - (goto-char (point-at-bol)) ;; Emacs bug, move-to-column is wrong otherwise - (move-to-column start) - (setq beg-point (point)) - (when (> (current-column) start) - (goto-char (1- (point))) - (setq beg-point (point)) - (setq before-string (make-string (- start (current-column)) ? )))) - (move-to-column end) - (setq end-point (point)) - (let ((end-offset (- (current-column) end))) - (when (> end-offset 0) (setq after-string (make-string end-offset ?b)))) - (when no-insert - ;; prevent inheriting of faces - (setq before-string (when before-string (propertize before-string 'face 'default))) - (setq after-string (when after-string (propertize after-string 'face 'default)))) - (let ((string (concat before-string replacement after-string))) - (if no-insert - string - (push (dropdown-list-put-overlay beg-point end-point 'invisible t - 'after-string string) - dropdown-list-overlays))))) - -(defun dropdown-list-start-column (display-width) - (let ((column (mod (current-column) (window-width))) - (width (window-width))) - (cond ((<= (+ column display-width) width) column) - ((> column display-width) (- column display-width)) - ((>= width display-width) (- width display-width)) - (t nil)))) - -(defun dropdown-list-move-to-start-line (candidate-count) - (decf candidate-count) - (let ((above-line-count (save-excursion (- (vertical-motion (- candidate-count))))) - (below-line-count (save-excursion (vertical-motion candidate-count)))) - (cond ((= below-line-count candidate-count) - t) - ((= above-line-count candidate-count) - (vertical-motion (- candidate-count)) - t) - ((>= (+ below-line-count above-line-count) candidate-count) - (vertical-motion (- (- candidate-count below-line-count))) - t) - (t nil)))) - -(defun dropdown-list-at-point (candidates &optional selidx) - (dropdown-list-hide) - (let* ((lengths (mapcar #'length candidates)) - (max-length (apply #'max lengths)) - (start (dropdown-list-start-column (+ max-length 3))) - (i -1) - (candidates (mapcar* (lambda (candidate length) - (let ((diff (- max-length length))) - (propertize - (concat (if (> diff 0) - (concat candidate (make-string diff ? )) - (substring candidate 0 max-length)) - (format "%3d" (+ 2 i))) - 'face (if (eql (incf i) selidx) - 'dropdown-list-selection-face - 'dropdown-list-face)))) - candidates - lengths))) - (save-excursion - (and start - (dropdown-list-move-to-start-line (length candidates)) - (loop initially (vertical-motion 0) - for candidate in candidates - do (dropdown-list-line (+ (current-column) start) candidate) - while (/= (vertical-motion 1) 0) - finally return t))))) - -(defun dropdown-list (candidates) - (let ((selection) - (temp-buffer)) - (save-window-excursion - (unwind-protect - (let ((candidate-count (length candidates)) - done key selidx) - (while (not done) - (unless (dropdown-list-at-point candidates selidx) - (switch-to-buffer (setq temp-buffer (get-buffer-create "*selection*")) - 'norecord) - (delete-other-windows) - (delete-region (point-min) (point-max)) - (insert (make-string (length candidates) ?\n)) - (goto-char (point-min)) - (dropdown-list-at-point candidates selidx)) - (setq key (read-key-sequence "")) - (cond ((and (stringp key) - (>= (aref key 0) ?1) - (<= (aref key 0) (+ ?0 (min 9 candidate-count)))) - (setq selection (- (aref key 0) ?1) - done t)) - ((member key `(,(char-to-string ?\C-p) [up])) - (setq selidx (mod (+ candidate-count (1- (or selidx 0))) - candidate-count))) - ((member key `(,(char-to-string ?\C-n) [down])) - (setq selidx (mod (1+ (or selidx -1)) candidate-count))) - ((member key `(,(char-to-string ?\f)))) - ((member key `(,(char-to-string ?\r) [return])) - (setq selection selidx - done t)) - (t (setq done t))))) - (dropdown-list-hide) - (and temp-buffer (kill-buffer temp-buffer))) - ;; (when selection - ;; (message "your selection => %d: %s" selection (nth selection candidates)) - ;; (sit-for 1)) - selection))) - -(defun define-key* (keymap key command) - "Add COMMAND to the multiple-command binding of KEY in KEYMAP. -Use multiple times to bind different COMMANDs to the same KEY." - (define-key keymap key (combine-command command (lookup-key keymap key)))) - -(defun combine-command (command defs) - "$$$$$ FIXME - no doc string" - (cond ((null defs) command) - ((and (listp defs) - (eq 'lambda (car defs)) - (= (length defs) 4) - (listp (fourth defs)) - (eq 'command-selector (car (fourth defs)))) - (unless (member `',command (cdr (fourth defs))) - (setcdr (fourth defs) (nconc (cdr (fourth defs)) `(',command)))) - defs) - (t - `(lambda () (interactive) (command-selector ',defs ',command))))) - -(defvar command-selector-last-command nil "$$$$$ FIXME - no doc string") - -(defun command-selector (&rest candidates) - "$$$$$ FIXME - no doc string" - (if (and (eq last-command this-command) command-selector-last-command) - (call-interactively command-selector-last-command) - (let* ((candidate-strings - (mapcar (lambda (candidate) - (format "%s" (if (symbolp candidate) - candidate - (let ((s (format "%s" candidate))) - (if (>= (length s) 7) - (concat (substring s 0 7) "...") - s))))) - candidates)) - (selection (dropdown-list candidate-strings))) - (when selection - (let ((cmd (nth selection candidates))) - (call-interactively cmd) - (setq command-selector-last-command cmd)))))) - -;;;;;;;;;;;;;;;;;;;; - -(provide 'dropdown-list) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; dropdown-list.el ends here From 05dc1f1474889d49c8d19c4e5e704c90a94eb5ed Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Tue, 9 Sep 2008 17:25:03 +0000 Subject: [PATCH 06/75] * About to simplify this considerably. Deleted previous section on field-level undo, only a stub for better times to come! --- yasnippet.el | 120 ++++++--------------------------------------------- 1 file changed, 13 insertions(+), 107 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 6572a6f..cf4ebf7 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -331,7 +331,6 @@ TODO: describe the rest of the fields" (exit-marker nil) (id (yas/snippet-next-id) :read-only t) (overlay nil) - (saved-buffer-undo-list nil) (active-group nil) (end-marker nil)) @@ -722,11 +721,13 @@ redo-ed." (end (overlay-end overlay)) (length (- end start)) (text (yas/calculate-field-value field (or rep - (yas/field-value field))))) + (yas/field-value field)))) + (inhibit-modification-hooks t)) (when text (goto-char start) (insert text) - (delete-char length))))) + (delete-char length) + (move-overlay overlay (overlay-start overlay) (point)))))) (defun yas/expand-snippet (start end template) "Expand snippet at current point. Text between START and END @@ -866,8 +867,7 @@ will be deleted before inserting template." (widen) (delete-char length) - ;; Step 14: Restore undo information, and also save it for future use. - (setf (yas/snippet-saved-buffer-undo-list snippet) original-undo-list) + ;; Step 14: Restore undo information (setq buffer-undo-list original-undo-list) ;; Step 15: place the cursor at a proper place @@ -1581,112 +1581,18 @@ registered snippets last." ;; ;; XXX: Commentary on this section by joaot. ;; -;; "Field-level undo" means undoing for bits of snippet fields that have -;; already been filled out. Because this is kind of experimental, I -;; have called it "field-undo", to distinguish it from regular undo -;; like the one used by `yas/undo-expand-snippet' to undo the original -;; snippet expansion. -;; -;; Field level undo allows no redos. Also, field level undo undoes any -;; change, even if it is only one character long. This might be -;; implemented in the future. -;; -;; Field level undo cooperates with normal undo and seems transparet -;; to the `undo' command. The basic idea is the same as with snippet -;; registration/unregistration. The undo history is saved in -;; `yas/field-undo-original-history' before each command and rewritten -;; if appropriate at the end. -;; -;; This is done by registering `yas/field-undo-before-hook' and -;; `yas/field-undo-after-hook' in the `pre-command-hook' and -;; `post-command-hook', respectively. -;; -;; Also, the `value' slot of the primary field of each group is used -;; to keep track of the most recently inserted text of that snippet -;; field. This could be seen as a hack, but that slot wasn't being -;; used anyway and its new meaning is actually quite reasonable. -;; -;; Another detail is that undo informatino shoulnd't be recorded for -;; some commands, most notably `undo' itself. Therefore, a variable -;; `yas/field-undo-forbidden-commands' has been introduced, to be -;; tested agains `this-command'. -;; - -(defvar yas/field-undo-history nil - "Saves the value of `buffer-undo-list' when undo information is -to be recorded by `yas/field-undo-after-hook'. A new piece of undo -is pushed into this variable and it then replaces -`buffer-undo-list' if appropriate.") - -(defvar yas/field-undo-forbidden-commands '(undo aquamacs-undo redo aquamacs-redo) - "A list of commands executed while a snippet is active that -should not trigger any undo-recording action") - +;; ... (defun yas/field-undo-before-hook () - "Saves the field-level undo history, `buffer-undo-list' into a -global `yas/field-undo-history' variable just before a command is -performed. That variable will come in handy in case the command -is to be undone" - (setq yas/field-undo-history buffer-undo-list)) + "..." + ) (defun yas/field-undo-after-hook () - "Compares the value (a string) of the currently active snippet -group with a previously saved one. If these are different, undo -information is added to `buffer-undo-list' + "..." + ) -This function is added to the `post-command-hook' and should -be a part of that list while registered snippets last." - (let* ((overlay (or (yas/current-field-overlay) - (yas/current-field-overlay (1- (point))))) - (group (when overlay - (overlay-get overlay 'yas/group)))) - (when group - (let ((new-text (yas/current-field-text (yas/group-primary-field group))) - (old-text (yas/field-value (yas/group-primary-field group)))) - ;; - ;; Unless extended undo forbids `this-command', or the old and - ;; new field strings are the same, rewrite the undo history - ;; with a call to `yas/field-undo-group-text-change' - ;; instead of whatever was placed there by the currently - ;; finishing `this-command' command. This call receives the id - ;; of the currently active snippet, the group to be undone and - ;; the old text. - ;; - (unless (or (memq this-command yas/field-undo-forbidden-commands) - (string= new-text - old-text)) - ;; - ;; Push a separator onto the history list, if one wasn't - ;; there first. Have no clue why sometimes one is and one - ;; isn't. - ;; - (unless (null (car-safe yas/field-undo-history)) - (push nil yas/field-undo-history)) - (push `(apply yas/field-undo-group-text-change - ,group - ,old-text) - yas/field-undo-history) - (setq buffer-undo-list yas/field-undo-history)) - ;; - ;; Then, in any case, save the new text into the value slot of - ;; the primary this is because some "forbidden" commands might - ;; really have changed the field value, most notably `undo' - ;; itself! This was a hard bug to track down! - ;; - (setf (yas/field-value (yas/group-primary-field group)) new-text))))) - -(defun yas/field-undo-group-text-change (group old-text) - "Undoes one step of field-level undo history, in the snippet - field group GROUP, replacing its text with OLD-TEXT, but - respecting any transforms." - (yas/remove-recent-undo-from-history) - (let ((inhibit-modification-hooks t) ; otherwise an additional - ; `yas/replace-fields-with-value' - ; is called - (buffer-undo-list t)) - (yas/replace-fields-with-value - (yas/group-fields group) - old-text))) +(defun yas/field-restore-overlay-position (snippet) + "..." + ) ;; Debug functions. Use (or change) at will whenever needed. From d5695a34cb20389609ab4875ea3c754c9c7aa628 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Wed, 10 Sep 2008 17:00:23 +0000 Subject: [PATCH 07/75] * Almost removed the field-overlay stuff, preparing for using simple markers. --- yasnippet.el | 239 +++++++++++++++++---------------------------------- 1 file changed, 77 insertions(+), 162 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index cf4ebf7..c3cadc4 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -3,7 +3,7 @@ ;; Copyright 2008 pluskid ;; ;; Author: pluskid -;; Version: 0.5.6 +;; Version: 0.5.6 XXX: Change this ;; X-URL: http://code.google.com/p/yasnippet/ ;; This file is free software; you can redistribute it and/or modify @@ -91,31 +91,10 @@ mode will be listed under the menu \"yasnippet\".") (defvar yas/trigger-symbol " =>" "The text that will be used in menu to represent the trigger.") - -(defun yas/define-multiple-faces (prefix background-color-pairs &optional doc) - "TODO: describe this rebuscated function" - (mapcar #'(lambda (color-pair) - (let* ((depth (position color-pair background-color-pairs))) - (when depth - (eval `(defface ,(intern (format "%s-%d" prefix depth)) - '((((class color) (background light)) (:background ,(first color-pair))) - (t (:background ,(second color-pair)))) - ,(when doc - (format "%s %d." doc depth))))))) - background-color-pairs)) - -;; Define multiple faces up to nested field (and mirror) depth 4 -(eval-when-compile - (yas/define-multiple-faces "yas/field-highlight-face" `(("DarkSeaGreen1" "DimGrey") - ("DarkSeaGreen3" "SlateGrey") - ("DarkOliveGreen2" "LightSlateGrey") - ("DarkOliveGreen4" "Gray")) - "The face used to highlight a field of a snippet with depth ") - (yas/define-multiple-faces "yas/mirror-highlight-face" `(("LightYellow1" "gray22") - ("LightYellow3" "grey32") - ("khaki2" "grey42") - ("khaki4" "grey52")) - "The face used to highlight mirror fields of a snippet with depth ")) +(defface yas/field-highlight-face + '((((class color) (background light)) (:background "DarkSeaGreen1")) + (t (:background "DimGrey"))) + "The face used to highlight the currently active field of a snippet") (defvar yas/window-system-popup-function #'yas/dropdown-list-popup-for-template "When there's multiple candidate for a snippet key. This function @@ -330,7 +309,7 @@ TODO: describe the rest of the fields" (groups nil) (exit-marker nil) (id (yas/snippet-next-id) :read-only t) - (overlay nil) + (control-overlay nil) (active-group nil) (end-marker nil)) @@ -344,9 +323,10 @@ TODO: describe the rest of the fields" (deleted nil) (modified nil)) (defstruct (yas/field - (:constructor yas/make-field (overlay number value transform parent-field))) + (:constructor yas/make-field (start-marker end-marker number value transform parent-field))) "A field in a snippet." - overlay + start + end number transform value @@ -361,8 +341,8 @@ TODO: describe the rest of the fields" (defun yas/snippet-valid? (snippet) "See if snippet is valid (ie. still alive)." (and (not (null snippet)) - (not (null (yas/snippet-overlay snippet))) - (not (null (overlay-start (yas/snippet-overlay snippet)))))) + (not (null (yas/snippet-control-overlay snippet))) + (not (null (overlay-start (yas/snippet-control-overlay snippet)))))) (defun yas/snippet-add-field (snippet field) "Add FIELD to the correct group of SNIPPET. @@ -406,7 +386,7 @@ as the primary field of the group." (defun yas/snippet-field-compare (field1 field2) "Compare two fields. The field with a number is sorted first. If they both have a number, compare through the number. If neither -have, compare through the start point of the overlay." +have, compare through the field's start point" (let ((n1 (yas/field-number field1)) (n2 (yas/field-number field2))) (if n1 @@ -415,8 +395,8 @@ have, compare through the start point of the overlay." t) (if n2 nil - (< (overlay-start (yas/field-overlay field1)) - (overlay-start (yas/field-overlay field2))))))) + (< (yas/field-start field1) + (yas/field-start field2)))))) (defun yas/template-condition-predicate (condition) (condition-case err @@ -582,10 +562,8 @@ the template of a snippet in the current snippet-table." 'dont-recurse)))))) (defun yas/current-field-text (field) - (let ((primary-overlay (yas/field-overlay field))) - (when primary-overlay - (buffer-substring-no-properties (overlay-start primary-overlay) - (overlay-end primary-overlay))))) + (buffer-substring-no-properties (yas/field-start field) + (yas/field-end field))) (defun yas/overlay-modification-hook (overlay after? beg end &optional length) @@ -641,44 +619,6 @@ of the primary field." end) (yas/synchronize-fields (overlay-get overlay 'yas/group))))) -;; (defun yas/overlay-maybe-insert-behind-hook (overlay after? beg end &optional length) -;; "Insert behind hook sometimes doesn't get called. I don't know why. -;; So I add modification hook in the big overlay and try to detect `insert-behind' -;; event manually." -;; (when after? -;; (cond ((and (= beg end) -;; (> length 0) -;; (= (overlay-start overlay) -;; (overlay-end overlay))) -;; (yas/exit-snippet (overlay-get overlay 'yas/snippet-reference))) -;; ((and (= length 0) -;; (> end beg) -;; (null (yas/current-field-overlay beg)) -;; (not (bobp))) -;; (let ((field-overlay (yas/current-field-overlay (1- beg)))) -;; (if field-overlay -;; (when (= beg (overlay-end field-overlay)) -;; (move-overlay field-overlay -;; (overlay-start field-overlay) -;; end) -;; (yas/synchronize-fields (overlay-get field-overlay 'yas/group))) -;; (let ((snippet (yas/snippet-of-current-keymap)) -;; (done nil)) -;; (if snippet -;; (do* ((groups (yas/snippet-groups snippet) (cdr groups)) -;; (group (car groups) (car groups))) -;; ((or (null groups) -;; done)) -;; (setq field-overlay (yas/field-overlay -;; (yas/group-primary-field group))) -;; (when (and (= (overlay-start field-overlay) -;; (overlay-end field-overlay)) -;; (= beg -;; (overlay-start field-overlay))) -;; (move-overlay field-overlay beg end) -;; (yas/synchronize-fields group) -;; (setq done t))))))))))) - (defun yas/remove-recent-undo-from-history () (let ((undo (car buffer-undo-list))) (while (null undo) @@ -700,25 +640,10 @@ redo-ed." (insert key))) (defun yas/replace-fields-with-value (fields &optional rep) -;; TODO: revise need for this rebuscatedeness -;; "For all FIELDS, delete characters outside the field's value -;; in field's overlay region. - -;; This default behaviour ensures other overlays covered by the same -;; region are not innapropriately displaced. - -;; With optional parameter REP, replace the field with delete whatever value (string) -;; existed and insert the field's text instead instead. - -;; In both cases, to enable producing different replacements for -;; each field, the replacement is calculated according to -;; `yas/calculate-field-value', which is passed the field itself, -;; and, as the second paramenter ,the value of `yas/field-value' or -;; REP if it is non-nil" +"TODO: revise need for this rebuscatedeness." (dolist (field fields) - (let* ((overlay (yas/field-overlay field)) - (start (overlay-start overlay)) - (end (overlay-end overlay)) + (let* ((start (yas/field-start field)) + (end (yas/field-end field)) (length (- end start)) (text (yas/calculate-field-value field (or rep (yas/field-value field)))) @@ -727,7 +652,7 @@ redo-ed." (goto-char start) (insert text) (delete-char length) - (move-overlay overlay (overlay-start overlay) (point)))))) + (move-marker (yas/field-end field) (point)))))) (defun yas/expand-snippet (start end template) "Expand snippet at current point. Text between START and END @@ -803,20 +728,9 @@ will be deleted before inserting template." nil nil t))) - ;; XXX: DEBUG: Got rid of this workaround and used old - ;; `yas/overlay-insert-behind-hook' . Hope I can find some - ;; other one. - ;; - ;; (overlay-put overlay - ;; 'modification-hooks - ;; yas/keymap-overlay-modification-hooks) - ;; (overlay-put overlay - ;; 'insert-behind-hooks - ;; yas/keymap-overlay-modification-hooks) (overlay-put overlay 'keymap yas/keymap) - (overlay-put overlay 'priority 10) ;; FIXME: hardcoded value here! (overlay-put overlay 'yas/snippet-reference snippet) - (setf (yas/snippet-overlay snippet) overlay) + (setf (yas/snippet-control-overlay snippet) overlay) (setf (yas/snippet-end-marker snippet) (overlay-end overlay))) ;; Step 8: Replace mirror field values with primary group's @@ -833,23 +747,6 @@ will be deleted before inserting template." (yas/replace-all yas/escape-backquote "`") (yas/replace-all yas/escape-backslash "\\") - ;; Step 10: Set up properties of overlays - (dolist (group (yas/snippet-groups snippet)) - (let ((overlay (yas/field-overlay - (yas/group-primary-field group)))) - (overlay-put overlay 'yas/snippet snippet) - (overlay-put overlay 'yas/group group) - (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) - (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) - (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) - (overlay-put overlay - 'face (intern (format "yas/field-highlight-face-%d" - (overlay-get overlay 'priority)))) - (dolist (field (yas/group-fields group)) - (unless (equal overlay (yas/field-overlay field)) - (overlay-put (yas/field-overlay field) - 'face (intern (format "yas/mirror-highlight-face-%d" (overlay-get overlay 'priority)))))))) - ;; Step 11: move to end and make sure exit-marker exist (goto-char (point-max)) (unless (yas/snippet-exit-marker snippet) @@ -871,20 +768,29 @@ will be deleted before inserting template." (setq buffer-undo-list original-undo-list) ;; Step 15: place the cursor at a proper place - (let* ((groups (yas/snippet-groups snippet)) - (exit-marker (yas/snippet-exit-marker snippet)) - (first-group (setf (yas/snippet-active-group snippet) (car groups)))) - (if groups - (goto-char (overlay-start - (yas/field-overlay - (yas/group-primary-field - first-group)))) - ;; no need to call exit-snippet, since no overlay created. - (yas/exit-snippet snippet))) + (let ((first-group (car (yas/snippet-groups snippet))) + (first-field (and first-group + (yas/group-primary-field first-group))) + overlay) + (cond (first-field + (setf (yas/snippet-active-group snippet) first-group) + (goto-char (yas/field-start first-field)) + ;; Step 10: Set up properties of the wandering active field + ;; overlay. + (setq overlay (make-overlay (yas/field-start first-field) (yas/field-end first-field))) + (overlay-put overlay 'yas/group group) + (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) + (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) + (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) + (overlay-put overlay 'face 'yas/field-highlight-face) + (setf (yas/snippet-active-field-overlay snippet) overlay)) + (t + ;; no need to call exit-snippet, since no overlay created. + (yas/exit-snippet snippet)))) ;; Step 16: Do necessary indenting (save-excursion - (goto-char (overlay-start (yas/snippet-overlay snippet))) + (goto-char (overlay-start (yas/snippet-control-overlay snippet))) (while (re-search-forward "$>" nil t) (replace-match "") (indent-according-to-mode))))))) @@ -913,10 +819,6 @@ Allows nested placeholder in the style of Textmate." ;; d) Otherwise a placeholder field for `number' is added to the ;; snippet with `value' and `transform'. ;; - ;; e) Correct overlay priority is set to increment by one the - ;; priority of `parent-field' if that is passed, effectively - ;; describing the current recursion level. - ;; ;; f) The enclosing "${<`number'>:" and closing bracket regions are ;; delete. ;; @@ -962,8 +864,9 @@ Allows nested placeholder in the style of Textmate." (yas/snippet-add-field snippet (yas/make-field - (make-overlay (match-beginning 0) (or (marker-position bracket-end) - (match-end 0))) + (set-marker (make-marker) (match-beginning 0)) + (set-marker (make-marker) (or (marker-position bracket-end) + (match-end 0))) (and number (string-to-number number)) value transform @@ -971,12 +874,7 @@ Allows nested placeholder in the style of Textmate." (when parent-field (setf (yas/field-subfields parent-field) (push brand-new-field (yas/field-subfields parent-field)))) - ;; e) set correct overlay priority - (overlay-put (yas/field-overlay brand-new-field) 'priority - (if parent-field - (1+ (overlay-get (yas/field-overlay parent-field) - 'priority)) - 0)) + ;; f) delete useless regions, move to correct spot for more ;; search... (delete-region (match-beginning 0) (or (marker-position value-start) @@ -993,8 +891,7 @@ Allows nested placeholder in the style of Textmate." (yas/field-parse-create snippet brand-new-field))) ;; h) (setf (yas/field-value brand-new-field) - (buffer-substring-no-properties value-start value-end)) - ))))) + (buffer-substring-no-properties value-start value-end))))))) (defun yas/field-bracket-end () "Calculates position of the field's closing bracket if any. @@ -1444,13 +1341,17 @@ when the condition evaluated to non-nil." (> number (length (remove-if #'yas/group-deleted (yas/snippet-groups snippet))))) (yas/exit-snippet snippet)) (target-group - (goto-char (overlay-start - (yas/field-overlay - (yas/group-primary-field target-group)))) - (setf (yas/snippet-active-group snippet) target-group)) + (yas/move-to-group snippet target-group)) (t nil)))) +(defun yas/move-to-group (snippet group) + (let ((field (yas/group-primary-field target-group))) + (goto-char (yas/field-start field)) + (setf (yas/snippet-active-group snippet) target-group) + (move-overlay (yas/snippet-active-field-overlay snippet) (yas/field-start field) + (yas/field-end field)))) + (defun yas/prev-field-group () "Navigate to prev field group. If there's none, exit the snippet." (interactive) @@ -1526,19 +1427,33 @@ current buffer." (defun yas/cleanup-snippet (snippet) "Cleanup SNIPPET, but leave point as it is. This renders the snippet as ordinary text" - (let* ((overlay (yas/snippet-overlay snippet)) + (let* ((control-overlay (yas/snippet-control-overlay snippet)) + (field-overlay (yas/snippet-active-field-overlay snippet)) yas/snippet-beg yas/snippet-end) ;; save the end of the moribund snippet in case we need to undo ;; its original expansion. This is used by `yas/undo-expand-snippet' - (when (and overlay - (overlay-buffer overlay)) - (setq yas/snippet-beg (overlay-start overlay)) - (setq yas/snippet-end (overlay-end overlay)) + (when (and control-overlay + (overlay-buffer control-overlay)) + (setq yas/snippet-beg (overlay-start control-overlay)) + (setq yas/snippet-end (overlay-end control-overlay)) (setf (yas/snippet-end-marker snippet) yas/snippet-end) - (delete-overlay overlay)) + (delete-overlay control-overlay)) + ;; Delete the currently active field overlay if any + (when (and field-overlay + (overlay-buffer field-overlay)) + (delete-overlay field-overlay)) + ;; + + + (dolist (group (yas/snippet-groups snippet)) (dolist (field (yas/group-fields group)) - (delete-overlay (yas/field-overlay field)))) + (let ((start-marker (yas/field-start field)) + (end-marker (yas/field-end field))) + (setf (yas/field-start field) (marker-position start-marker)) + (setf (yas/field-end field) (marker-position end-marker)) + (set-marker start-marker nil) + (set-marker end-marker nil)))) ;; XXX: `yas/after-exit-snippet-hook' should be run with ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not ;; be the case if the main overlay had somehow already @@ -1613,8 +1528,8 @@ registered snippets last." (princ (format "\t Big priority %s overlay %s\n\n" - (overlay-get (yas/snippet-overlay snippet) 'priority) - (yas/snippet-overlay snippet))) + (overlay-get (yas/snippet-control-overlay snippet) 'priority) + (yas/snippet-control-overlay snippet))) From 38ce2aac825486ffee1d85c4b034a7330b267381 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Fri, 12 Sep 2008 13:19:34 +0000 Subject: [PATCH 08/75] * Nice! Getting there with this scheme, undo and everything! --- yasnippet.el | 366 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 220 insertions(+), 146 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index c3cadc4..acc3f04 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -292,24 +292,13 @@ set to t." (defstruct (yas/snippet (:constructor yas/make-snippet ())) "A snippet. -Description of some fields: - -`yas/snippet-saved-buffer-undo-list' saves the value of -`buffer-undo-list' just after the snippet has been expanded. This -is to be restored when the snippet is cleaned up. Thus the -snippet expansion can still be undone after -`yas/cleanup-snippet', even if field-level undo steps were -recorded. - -`yas/snippet-end-marker' saves the actual end position of the -snippets main overlay, at the time the snippet was cleaned -up. Thus `yas/undo-expand-snippet' can clean it up properly. - -TODO: describe the rest of the fields" +..." (groups nil) (exit-marker nil) (id (yas/snippet-next-id) :read-only t) (control-overlay nil) + (active-field-overlay nil) + field-undo-saved-boundaries (active-group nil) (end-marker nil)) @@ -323,7 +312,7 @@ TODO: describe the rest of the fields" (deleted nil) (modified nil)) (defstruct (yas/field - (:constructor yas/make-field (start-marker end-marker number value transform parent-field))) + (:constructor yas/make-field (start end number value transform parent-field))) "A field in a snippet." start end @@ -579,45 +568,52 @@ of the primary field." (t "STH UNKNOWN")) overlay)) - (when (and after? (not undo-in-progress)) + (when after? + ;; (and after? (not undo-in-progress)) (yas/synchronize-fields (overlay-get overlay 'yas/group)))) (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length) "Hook for snippet overlay when text is inserted in front of a snippet field." (let ((group (overlay-get overlay 'yas/group))) (when (and after? - group - (not (yas/group-deleted group))) + group) (let ((inhibit-modification-hooks t)) - ;; If the group hasn't ever been modified, delete it + ;; + ;; If the group hasn't ever been modified, delete its contents ;; completely. + ;; (when (not (yas/group-modified group)) (setf (yas/group-modified group) t) (when (> (overlay-end overlay) end) (save-excursion (goto-char end) (delete-char (- (overlay-end overlay) end)))) - ;; Mark subgroups as `yas/group-deleted', so insert-in-front - ;; and behind hooks won't be run by them. + ;; + ;; Mark subgroups as `yas/group-deleted', so we're no longer + ;; able to move them. XXX:UNDO:TODO: This action has to be undoable! + ;; (mapcar #'(lambda (group) (setf (yas/group-deleted group) t)) (mapcar #'yas/field-group (yas/field-subfields (yas/group-primary-field group))))) ;; in any case, synchronize mirror fields (yas/synchronize-fields group))))) +(defun yas/move-overlay-and-field (overlay field start end) + (move-overlay overlay + start + end) + (move-marker (yas/field-start field) start) + (move-marker (yas/field-end field) end)) + (defun yas/overlay-insert-behind-hook (overlay after? beg end &optional length) - "Hook for snippet overlay when text is inserted just behind a snippet field." - (let ((current-field-overlay (yas/current-field-overlay beg)) - (group (overlay-get overlay 'yas/group))) + "Hook for snippet overlay when text is inserted just behind the currently active field overlay." + (let* ((group (overlay-get overlay 'yas/group)) + (field (and group + (yas/group-primary-field group)))) (when (and after? - (not (yas/group-deleted group)) - (or (null current-field-overlay) ; not inside another field - (< (overlay-get current-field-overlay 'priority) - (overlay-get overlay 'priority)))) - (move-overlay overlay - (overlay-start overlay) - end) - (yas/synchronize-fields (overlay-get overlay 'yas/group))))) + field) + (yas/move-overlay-and-field overlay field (overlay-start overlay) end) + (yas/synchronize-fields group)))) (defun yas/remove-recent-undo-from-history () (let ((undo (car buffer-undo-list))) @@ -669,7 +665,7 @@ will be deleted before inserting template." (save-restriction (narrow-to-region start start) - (setq buffer-undo-list t) ;; disable undo for a short while + ;; (setq buffer-undo-list t) ;; disable undo for a short while (insert template) ;; Step 1: do necessary indent @@ -752,38 +748,30 @@ will be deleted before inserting template." (unless (yas/snippet-exit-marker snippet) (setf (yas/snippet-exit-marker snippet) (copy-marker (point) t))) - ;; Step 12: Construct undo information - (unless (eq original-undo-list t) - (add-to-list 'original-undo-list - `(apply yas/undo-expand-snippet - ,(point-min) - ,key - ,snippet))) + ;; ;; Step 12: Construct undo information + ;; (unless (eq original-undo-list t) + ;; (add-to-list 'original-undo-list + ;; `(apply yas/undo-expand-snippet + ;; ,(point-min) + ;; ,key + ;; ,snippet))) ;; Step 13: remove the trigger key (widen) (delete-char length) - ;; Step 14: Restore undo information - (setq buffer-undo-list original-undo-list) + ;; ;; Step 14: Restore undo information + ;; (setq buffer-undo-list original-undo-list) ;; Step 15: place the cursor at a proper place - (let ((first-group (car (yas/snippet-groups snippet))) - (first-field (and first-group - (yas/group-primary-field first-group))) + (let* ((first-group (car (yas/snippet-groups snippet))) + (first-field (and first-group + (yas/group-primary-field first-group))) overlay) (cond (first-field - (setf (yas/snippet-active-group snippet) first-group) - (goto-char (yas/field-start first-field)) - ;; Step 10: Set up properties of the wandering active field - ;; overlay. - (setq overlay (make-overlay (yas/field-start first-field) (yas/field-end first-field))) - (overlay-put overlay 'yas/group group) - (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) - (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) - (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) - (overlay-put overlay 'face 'yas/field-highlight-face) - (setf (yas/snippet-active-field-overlay snippet) overlay)) + ;; Step 10: Move to the new group, setting up + ;; properties of the wandering active field overlay. + (yas/move-to-group snippet first-group)) (t ;; no need to call exit-snippet, since no overlay created. (yas/exit-snippet snippet)))) @@ -911,26 +899,6 @@ placeholders." (message "Invalid snippet template!"))))) bracket-end)) -(defun yas/current-field-overlay (&optional point) - "Return the most ." - (let ((point (or point (point)))) - (car (sort (delete-if-not #'(lambda (overlay) - (overlay-get overlay 'yas/snippet)) - (overlays-at point)) - #'(lambda (overlay1 overlay2) - (let ((id-1 (yas/snippet-id (overlay-get overlay1 'yas/snippet))) - (id-2 (yas/snippet-id (overlay-get overlay2 'yas/snippet))) - (prio-1 (overlay-get overlay1 'priority)) - (prio-2 (overlay-get overlay2 'priority))) - (cond ((> id-1 id-2) - t) - ((< id-1 id-2) - nil) - ((> prio-1 prio-2) - t) - (t - nil)))))))) - (defun yas/snippet-of-current-keymap (&optional point) "Return the most recently inserted snippet holding covering POINT." @@ -947,6 +915,9 @@ POINT." (setq keymap-snippet snippet))))) keymap-snippet)) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Template-related and snippet loading functions + (defun yas/parse-template (&optional file-name) "Parse the template in the current buffer. If the buffer contains a line of \"# --\" then the contents @@ -1317,10 +1288,8 @@ when the condition evaluated to non-nil." (call-interactively command)))))))))) (defun yas/current-group-for-navigation (&optional snippet) - (or (and snippet - (yas/snippet-active-group snippet)) - (overlay-get (or (yas/current-field-overlay (1- (point))) - (yas/current-field-overlay)) 'yas/group))) + (and snippet + (yas/snippet-active-group snippet))) (defun yas/next-field-group (&optional arg) "Navigate to next field group. If there's none, exit the snippet." @@ -1346,20 +1315,33 @@ when the condition evaluated to non-nil." nil)))) (defun yas/move-to-group (snippet group) - (let ((field (yas/group-primary-field target-group))) + "Update SNIPPET to move to group GROUP." + (let ((field (yas/group-primary-field group)) + (overlay (yas/snippet-active-field-overlay snippet))) (goto-char (yas/field-start field)) - (setf (yas/snippet-active-group snippet) target-group) - (move-overlay (yas/snippet-active-field-overlay snippet) (yas/field-start field) - (yas/field-end field)))) + (setf (yas/snippet-active-group snippet) group) + (cond ((and overlay + (overlay-buffer overlay)) + (move-overlay overlay (yas/field-start field) + (yas/field-end field))) + (t + (setq overlay (make-overlay (yas/field-start first-field) (yas/field-end first-field))) + (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) + (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) + (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) + (overlay-put overlay 'face 'yas/field-highlight-face) + (setf (yas/snippet-active-field-overlay snippet) overlay))) + (overlay-put overlay 'yas/group group) + (overlay-put overlay 'yas/field field))) + (defun yas/prev-field-group () "Navigate to prev field group. If there's none, exit the snippet." (interactive) - (yas/next-field-group -1)) (defun yas/exit-snippet (snippet) - "Goto exit-marker of SNIPPET and cleanup the snippe. Cleaning + "Goto exit-marker of SNIPPET and cleanup the snippet. Cleaning up the snippet does not delete it!" (interactive) (goto-char (yas/snippet-exit-marker snippet)) @@ -1403,6 +1385,8 @@ registered snippet exists in the current buffer. Return snippet" (add-hook 'pre-command-hook 'yas/field-undo-before-hook 'append 'local) (add-hook 'post-command-hook 'yas/check-cleanup-snippet 'append 'local) (add-hook 'post-command-hook 'yas/field-undo-after-hook 'append 'local) + ;; DEBUG + (add-hook 'post-command-hook 'yas/debug-some-vars 'append 'local) snippet) (defun yas/unregister-snippet (snippet) @@ -1415,14 +1399,22 @@ current buffer." (hash-table-count yas/registered-snippets)) (remove-hook 'pre-command-hook 'yas/field-undo-before-hook 'local) (remove-hook 'post-command-hook 'yas/field-undo-after-hook 'local) - (remove-hook 'post-command-hook 'yas/check-cleanup-snippet 'local))) + (remove-hook 'post-command-hook 'yas/check-cleanup-snippet 'local) + ;; DEBUG + (remove-hook 'post-command-hook 'yas/debug-some-vars ' 'local) + + )) (defun yas/exterminate-snippets () "Remove all locally registered snippets and remove `yas/check-cleanup-snippet' from the `post-command-hook'" (interactive) - (maphash #'(lambda (key snippet) (yas/cleanup-snippet snippet)) - yas/registered-snippets)) + (maphash #'(lambda (key snippet) + (when (yas/snippet-p snippet) (yas/cleanup-snippet snippet))) + yas/registered-snippets) + (unless (eq 0 (hash-table-count yas/registered-snippets)) + (setq yas/registered-snippets (make-hash-table :test 'eq)) + (message "Warning: yas/snippet hash-table not fully clean. Forcing NIL."))) (defun yas/cleanup-snippet (snippet) "Cleanup SNIPPET, but leave point as it is. This renders the @@ -1430,60 +1422,67 @@ snippet as ordinary text" (let* ((control-overlay (yas/snippet-control-overlay snippet)) (field-overlay (yas/snippet-active-field-overlay snippet)) yas/snippet-beg yas/snippet-end) - ;; save the end of the moribund snippet in case we need to undo + ;; + ;; Save the end of the moribund snippet in case we need to undo ;; its original expansion. This is used by `yas/undo-expand-snippet' + ;; (when (and control-overlay (overlay-buffer control-overlay)) (setq yas/snippet-beg (overlay-start control-overlay)) (setq yas/snippet-end (overlay-end control-overlay)) (setf (yas/snippet-end-marker snippet) yas/snippet-end) (delete-overlay control-overlay)) + ;; ;; Delete the currently active field overlay if any + ;; (when (and field-overlay (overlay-buffer field-overlay)) (delete-overlay field-overlay)) + ;; + ;; Iterate every group, and in it, every field. ;; - - - (dolist (group (yas/snippet-groups snippet)) (dolist (field (yas/group-fields group)) (let ((start-marker (yas/field-start field)) (end-marker (yas/field-end field))) - (setf (yas/field-start field) (marker-position start-marker)) - (setf (yas/field-end field) (marker-position end-marker)) - (set-marker start-marker nil) - (set-marker end-marker nil)))) + ;; + ;; convert markers into points, before losing the reference. + ;; + (when (markerp start-marker) + (setf (yas/field-start field) (marker-position start-marker)) + (set-marker start-marker nil)) + (when (markerp end-marker) + (setf (yas/field-end field) (marker-position end-marker)) + (set-marker end-marker nil))))) + ;; ;; XXX: `yas/after-exit-snippet-hook' should be run with ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not ;; be the case if the main overlay had somehow already ;; disappeared, which sometimes happens when the snippet's messed ;; up... + ;; (run-hooks 'yas/after-exit-snippet-hook)) - (yas/unregister-snippet snippet) - (setq buffer-undo-list (yas/snippet-saved-buffer-undo-list snippet))) + (yas/unregister-snippet snippet)) (defun yas/check-cleanup-snippet () - "Checks if point exited any of the fields of the snippet, if so -clean it up. + "Checks if point exited the currently active field of the +snippet, if so cleans up the whole snippet up. This function is part of `post-command-hook' while registered snippets last." - (let ((snippet (yas/snippet-of-current-keymap))) + (let* ((snippet (yas/snippet-of-current-keymap)) + (field-overlay (and snippet + (yas/snippet-active-field-overlay snippet)))) (cond ( ;; ;; No snippet at point, cleanup *all* snippets ;; (null snippet) (yas/exterminate-snippets)) - ( ;; - ;; A snippet exits at point, but point is out of any - ;; primary snippet field. - (and snippet - (notany #'(lambda (group) - (let ((primary-overlay (yas/field-overlay (yas/group-primary-field group)))) - (and (>= (point) (overlay-start primary-overlay)) - (<= (point) (overlay-end primary-overlay))))) - (yas/snippet-groups snippet))) + ( ;; A snippet exits at point, but point left the currently + ;; active field overlay + (and field-overlay + (or (> (point) (overlay-end field-overlay)) + (< (point) (overlay-start field-overlay)))) (yas/cleanup-snippet snippet)) (;; ;; Snippet at point, and point inside a snippet field, @@ -1497,17 +1496,83 @@ registered snippets last." ;; XXX: Commentary on this section by joaot. ;; ;; ... + (defun yas/field-undo-before-hook () "..." - ) + (let* ((snippet (yas/snippet-of-current-keymap)) + (field-overlay (and snippet + (yas/snippet-active-field-overlay snippet)))) + (when (and field-overlay + (overlay-buffer field-overlay)) + (setf (yas/snippet-field-undo-saved-boundaries snippet) + (cons (overlay-start field-overlay) + (overlay-end field-overlay)))))) (defun yas/field-undo-after-hook () "..." - ) + (let* ((snippet (yas/snippet-of-current-keymap)) + (saved-boundaries (and snippet + (yas/snippet-field-undo-saved-boundaries snippet)))) + (unless (null saved-boundaries) + (yas/push-undo-action-maybe (list 'yas/field-undo-restore-boundaries + (car saved-boundaries) + (cdr saved-boundaries)))) + (unless (null snippet) + (yas/push-undo-action-maybe (list 'yas/restore-active-group nil))))) + -(defun yas/field-restore-overlay-position (snippet) + +(defun yas/restore-active-group (snippet) "..." - ) + (message "Would be restoring the active group, but how????")) + + +(defun yas/push-undo-action-maybe (apply-args) + "..." + (let ((undo-list buffer-undo-list) + (target-separator nil) + done) + (unless (eq t buffer-undo-list) + ;; + ;; Discard possibly existing/missing start separator + ;; + (when (null (car undo-list)) + (setq undo-list (cdr undo-list))) + ;; + ;; Find the target separator keeping `undo-list' as a reference to + ;; the list starting before that. + ;; + (while (not done) + (cond ((eq (first apply-args) + (condition-case opps + (second (car undo-list)) + (error nil))) + (setq done 'return)) + ((null (cadr undo-list)) + (setq done 'try-insert)) + (t + (setq undo-list (cdr undo-list))))) + (unless (eq done 'return) + ;; + ;; Push a the apply-args action there + ;; + (setq target-separator (cdr undo-list)) + (setf (cdr undo-list) + (cons (cons 'apply + apply-args) + target-separator)))))) + + +(defun yas/field-undo-restore-boundaries (start end) + "..." + (let* ((snippet (yas/snippet-of-current-keymap)) + (field-overlay (and snippet + (yas/snippet-active-field-overlay snippet))) + (group (and snippet + (yas/snippet-active-group snippet))) + (field (and group + (yas/group-primary-field group)))) + (yas/move-overlay-and-field field-overlay field start end))) ;; Debug functions. Use (or change) at will whenever needed. @@ -1527,45 +1592,54 @@ registered snippets last." (yas/snippet-id snippet))) - (princ (format "\t Big priority %s overlay %s\n\n" - (overlay-get (yas/snippet-control-overlay snippet) 'priority) + (princ (format "\t Big overlay %s\n" (yas/snippet-control-overlay snippet))) + (if (yas/snippet-active-field-overlay snippet) + (princ (format "\t Field overlay %s\n " + (yas/snippet-active-field-overlay snippet))) + (princ "No active field overlay!!\m")) - - (dolist (group (yas/snippet-groups snippet)) - (princ (format "\t group $%s with %s fields.\n" - (yas/group-number group) - (length (yas/group-fields group)))) + + (dolist (group (yas/snippet-groups snippet)) + (princ (format "\t Group $%s with %s fields is %s and %s" + (yas/group-number group) + (length (yas/group-fields group)) + (if (yas/group-deleted group) + "DELETED" + "alive") + (if (eq group (yas/snippet-active-group snippet)) + "ACTIVE!\n" + "NOT ACTIVE!\n"))) (dolist (field (yas/group-fields group)) - (let ((overlay (yas/field-overlay field))) - (princ (format "\t %s field. Saved (%s) . " - (if (eq field (yas/group-primary-field group)) - "Primary" "Mirror") - (yas/field-value (yas/group-primary-field group)))) - (if (and (overlayp overlay) - (overlay-buffer overlay)) - (princ (format "Priority %d overlay (%d:%d:%s)\n" - (overlay-get overlay 'priority) - (overlay-start overlay) - (overlay-end overlay) - (buffer-substring (overlay-start overlay) (overlay-end overlay)))) - (princ "NO OVERLAY\n")))))) - yas/registered-snippets))) + (princ (format "\t\t* %s field. Current value (%s) .\n" + (if (eq field (yas/group-primary-field group)) + "Primary" "Mirror") + (yas/current-field-text field))) + (princ (format "\t\t From %s to %s\n" + (yas/field-start field) + (yas/field-end field))) + ))) yas/registered-snippets))) (princ (format "\nPost command hook: %s\n" post-command-hook)) (princ (format "\nPre command hook: %s\n" pre-command-hook)) - ;; (princ (format "\nUndo is %s." - ;; (if (eq buffer-undo-list t) - ;; "DISABLED" - ;; "ENABLED"))) - ;; (unless (eq buffer-undo-list t) - ;; (princ (format "Undolist has %s elements. First 3 elements follow:\n" (length buffer-undo-list))) - ;; (let ((first-ten (subseq buffer-undo-list 0 2))) - ;; (dolist (undo-elem first-ten) - ;; (princ (format "%s: %s\n" (position undo-elem first-ten) undo-elem))))) -)) + (princ (format "\nUndo is %s." + (if (eq buffer-undo-list t) + "DISABLED" + "ENABLED"))) + (unless (eq buffer-undo-list t) + (princ (format "Undolist has %s elements. First 10 elements follow:\n" (length buffer-undo-list))) + (let ((first-ten (subseq buffer-undo-list 0 19))) + (dolist (undo-elem first-ten) + (princ (format "%s: %s\n" (position undo-elem first-ten) undo-elem))))))) + +(defun yas/exterminate-package () + (interactive) + (yas/minor-mode -1) + (mapatoms #'(lambda (atom) + (when (string-match "yas/" (symbol-name atom)) + (unintern atom))))) (provide 'yasnippet) From d9cb83dc93edce95ca4c5142f1d5cc0264fafd46 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Fri, 12 Sep 2008 13:35:57 +0000 Subject: [PATCH 09/75] * I like. Very nice. New undo scheme is powerful for completely emulating Textmate behaviour I think. --- yasnippet.el | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index acc3f04..52f9638 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -298,7 +298,7 @@ set to t." (id (yas/snippet-next-id) :read-only t) (control-overlay nil) (active-field-overlay nil) - field-undo-saved-boundaries + undo-saved-boundaries (active-group nil) (end-marker nil)) @@ -1382,9 +1382,9 @@ up the snippet does not delete it!" `post-command-hook' that should exist while at least one registered snippet exists in the current buffer. Return snippet" (puthash (yas/snippet-id snippet) snippet yas/registered-snippets) - (add-hook 'pre-command-hook 'yas/field-undo-before-hook 'append 'local) + (add-hook 'pre-command-hook 'yas/undo-before-hook 'append 'local) (add-hook 'post-command-hook 'yas/check-cleanup-snippet 'append 'local) - (add-hook 'post-command-hook 'yas/field-undo-after-hook 'append 'local) + (add-hook 'post-command-hook 'yas/undo-after-hook 'append 'local) ;; DEBUG (add-hook 'post-command-hook 'yas/debug-some-vars 'append 'local) snippet) @@ -1397,8 +1397,8 @@ current buffer." (remhash (yas/snippet-id snippet) yas/registered-snippets) (when (eq 0 (hash-table-count yas/registered-snippets)) - (remove-hook 'pre-command-hook 'yas/field-undo-before-hook 'local) - (remove-hook 'post-command-hook 'yas/field-undo-after-hook 'local) + (remove-hook 'pre-command-hook 'yas/undo-before-hook 'local) + (remove-hook 'post-command-hook 'yas/undo-after-hook 'local) (remove-hook 'post-command-hook 'yas/check-cleanup-snippet 'local) ;; DEBUG (remove-hook 'post-command-hook 'yas/debug-some-vars ' 'local) @@ -1497,34 +1497,41 @@ registered snippets last." ;; ;; ... -(defun yas/field-undo-before-hook () +(defun yas/undo-before-hook () "..." (let* ((snippet (yas/snippet-of-current-keymap)) (field-overlay (and snippet (yas/snippet-active-field-overlay snippet)))) (when (and field-overlay (overlay-buffer field-overlay)) - (setf (yas/snippet-field-undo-saved-boundaries snippet) + (setf (yas/snippet-undo-saved-boundaries snippet) (cons (overlay-start field-overlay) (overlay-end field-overlay)))))) -(defun yas/field-undo-after-hook () +(defun yas/undo-after-hook () "..." (let* ((snippet (yas/snippet-of-current-keymap)) (saved-boundaries (and snippet - (yas/snippet-field-undo-saved-boundaries snippet)))) - (unless (null saved-boundaries) - (yas/push-undo-action-maybe (list 'yas/field-undo-restore-boundaries - (car saved-boundaries) - (cdr saved-boundaries)))) + (yas/snippet-undo-saved-boundaries snippet)))) (unless (null snippet) - (yas/push-undo-action-maybe (list 'yas/restore-active-group nil))))) + (yas/push-undo-action-maybe (list 'yas/undo-restore-active-group nil))) + (unless (null saved-boundaries) + (yas/push-undo-action-maybe (list 'yas/undo-restore-boundaries + (car saved-boundaries) + (cdr saved-boundaries)))))) -(defun yas/restore-active-group (snippet) +(defun yas/undo-restore-active-group (&optional point) "..." - (message "Would be restoring the active group, but how????")) + (let* ((point (or point + (point))) + (snippet (yas/snippet-of-current-keymap point))) + (message "Would restoring group point %s and %s" + point + (if snippet + (format "snippet id %d" (yas/snippet-id snippet)) + "NO SNIPPET!!!")))) (defun yas/push-undo-action-maybe (apply-args) @@ -1563,7 +1570,7 @@ registered snippets last." target-separator)))))) -(defun yas/field-undo-restore-boundaries (start end) +(defun yas/undo-restore-boundaries (start end) "..." (let* ((snippet (yas/snippet-of-current-keymap)) (field-overlay (and snippet From 5a3b16121947db6d795f12dc86f5ca3b8cf8e3f3 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Fri, 12 Sep 2008 18:34:52 +0000 Subject: [PATCH 10/75] * Much closer to getting this all working, undo, redo and nested stuff. Works most of the time but: 1. Consider getting rid of the `deleted' state of a group and using some other criteria to discover if user can move to a group. `deleted' is only for subgroups anyway, i think. 2. Consider adding information about restoring boundaries in the yas/group itself. This way, a) upon `yas/snippet-cleanup', start and end marker could simply be set to nil and deleted. b) `yas/undo-restore-active-group' could recurse down subgroups restoring them as well. (maybe without specifically moving to them, but OK. 3. Don't forget to correct `yas/update-mirrors' to correctly use the transformations in the mirror fields. --- yasnippet.el | 130 +++++++++++++++++++++++++-------------------------- 1 file changed, 63 insertions(+), 67 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 52f9638..dc2fb3c 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -298,7 +298,7 @@ set to t." (id (yas/snippet-next-id) :read-only t) (control-overlay nil) (active-field-overlay nil) - undo-saved-boundaries + undo-saved-info (active-group nil) (end-marker nil)) @@ -529,7 +529,7 @@ the template of a snippet in the current snippet-table." start end))) -(defun yas/synchronize-fields (field-group &optional dont-recurse-down) +(defun yas/update-mirrors (field-group &optional dont-recurse-down) "Update all mirror fields' text according to the primary field." (when (yas/snippet-valid? (yas/group-snippet field-group)) (save-excursion @@ -544,37 +544,36 @@ the template of a snippet in the current snippet-table." ;; Call recursively for subfields (unless dont-recurse-down (dolist (subfield (yas/field-subfields primary)) - (yas/synchronize-fields (yas/field-group subfield)))) + (yas/update-mirrors (yas/field-group subfield)))) ;; Call recursively for parent field (when (yas/field-parent-field primary) - (yas/synchronize-fields (yas/field-group (yas/field-parent-field primary)) + (yas/update-mirrors (yas/field-group (yas/field-parent-field primary)) 'dont-recurse)))))) (defun yas/current-field-text (field) (buffer-substring-no-properties (yas/field-start field) (yas/field-end field))) +(defun yas/current-active-group (&optional snippet point) + "..." + (let ((snippet (or snippet + (yas/snippet-of-current-keymap (or point + (point)))))) + (and snippet + (yas/snippet-active-group snippet)))) (defun yas/overlay-modification-hook (overlay after? beg end &optional length) "Synchronizes all fields for the group of the current field overlay Used to ensure mirror fields in the same group contain the same value of the primary field." - (message (format "Running mod hook for %s of %s." - (cond ((overlay-get overlay 'yas/snippet-reference) - (format "big overlay of snippet %s," (yas/snippet-id (overlay-get overlay 'yas/snippet-reference)))) - ((overlay-get overlay 'yas/group) - (format "field overlay of group $%s," (yas/group-number (overlay-get overlay 'yas/group)))) - (t - "STH UNKNOWN")) - overlay)) (when after? ;; (and after? (not undo-in-progress)) - (yas/synchronize-fields (overlay-get overlay 'yas/group)))) + (yas/update-mirrors (yas/current-active-group)))) (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length) "Hook for snippet overlay when text is inserted in front of a snippet field." - (let ((group (overlay-get overlay 'yas/group))) + (let ((group (yas/current-active-group))) (when (and after? group) (let ((inhibit-modification-hooks t)) @@ -590,13 +589,15 @@ of the primary field." (delete-char (- (overlay-end overlay) end)))) ;; ;; Mark subgroups as `yas/group-deleted', so we're no longer - ;; able to move them. XXX:UNDO:TODO: This action has to be undoable! + ;; able to move them. This action is undoable as long as + ;; `yas/undo-before-hook' exists in the `pre-command-hook' + ;; in the proper place. ;; (mapcar #'(lambda (group) (setf (yas/group-deleted group) t)) (mapcar #'yas/field-group (yas/field-subfields (yas/group-primary-field group))))) ;; in any case, synchronize mirror fields - (yas/synchronize-fields group))))) + (yas/update-mirrors group))))) (defun yas/move-overlay-and-field (overlay field start end) (move-overlay overlay @@ -607,13 +608,13 @@ of the primary field." (defun yas/overlay-insert-behind-hook (overlay after? beg end &optional length) "Hook for snippet overlay when text is inserted just behind the currently active field overlay." - (let* ((group (overlay-get overlay 'yas/group)) + (let* ((group (yas/current-active-group)) (field (and group (yas/group-primary-field group)))) (when (and after? field) (yas/move-overlay-and-field overlay field (overlay-start overlay) end) - (yas/synchronize-fields group)))) + (yas/update-mirrors group)))) (defun yas/remove-recent-undo-from-history () (let ((undo (car buffer-undo-list))) @@ -1320,6 +1321,7 @@ when the condition evaluated to non-nil." (overlay (yas/snippet-active-field-overlay snippet))) (goto-char (yas/field-start field)) (setf (yas/snippet-active-group snippet) group) + (setf (yas/group-deleted group) nil) (cond ((and overlay (overlay-buffer overlay)) (move-overlay overlay (yas/field-start field) @@ -1330,9 +1332,7 @@ when the condition evaluated to non-nil." (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) (overlay-put overlay 'face 'yas/field-highlight-face) - (setf (yas/snippet-active-field-overlay snippet) overlay))) - (overlay-put overlay 'yas/group group) - (overlay-put overlay 'yas/field field))) + (setf (yas/snippet-active-field-overlay snippet) overlay))))) (defun yas/prev-field-group () @@ -1383,8 +1383,8 @@ up the snippet does not delete it!" registered snippet exists in the current buffer. Return snippet" (puthash (yas/snippet-id snippet) snippet yas/registered-snippets) (add-hook 'pre-command-hook 'yas/undo-before-hook 'append 'local) - (add-hook 'post-command-hook 'yas/check-cleanup-snippet 'append 'local) (add-hook 'post-command-hook 'yas/undo-after-hook 'append 'local) + (add-hook 'post-command-hook 'yas/check-cleanup-snippet 'append 'local) ;; DEBUG (add-hook 'post-command-hook 'yas/debug-some-vars 'append 'local) snippet) @@ -1397,13 +1397,11 @@ current buffer." (remhash (yas/snippet-id snippet) yas/registered-snippets) (when (eq 0 (hash-table-count yas/registered-snippets)) - (remove-hook 'pre-command-hook 'yas/undo-before-hook 'local) - (remove-hook 'post-command-hook 'yas/undo-after-hook 'local) + (remove-hook 'pre-command-hook 'yas/undo-before-hook 'local) + (remove-hook 'post-command-hook 'yas/undo-after-hook 'local) (remove-hook 'post-command-hook 'yas/check-cleanup-snippet 'local) ;; DEBUG - (remove-hook 'post-command-hook 'yas/debug-some-vars ' 'local) - - )) + (remove-hook 'post-command-hook 'yas/debug-some-vars 'local))) (defun yas/exterminate-snippets () "Remove all locally registered snippets and remove @@ -1471,18 +1469,18 @@ snippet, if so cleans up the whole snippet up. This function is part of `post-command-hook' while registered snippets last." (let* ((snippet (yas/snippet-of-current-keymap)) - (field-overlay (and snippet - (yas/snippet-active-field-overlay snippet)))) - (cond ( ;; + (group (and snippet + (yas/snippet-active-group snippet)))) + (cond (;; ;; No snippet at point, cleanup *all* snippets ;; (null snippet) (yas/exterminate-snippets)) - ( ;; A snippet exits at point, but point left the currently + (;; A snippet exits at point, but point left the currently ;; active field overlay - (and field-overlay - (or (> (point) (overlay-end field-overlay)) - (< (point) (overlay-start field-overlay)))) + (or (not group) + (and group + (not (yas/point-in-field-p (yas/group-primary-field group))))) (yas/cleanup-snippet snippet)) (;; ;; Snippet at point, and point inside a snippet field, @@ -1504,35 +1502,44 @@ registered snippets last." (yas/snippet-active-field-overlay snippet)))) (when (and field-overlay (overlay-buffer field-overlay)) - (setf (yas/snippet-undo-saved-boundaries snippet) - (cons (overlay-start field-overlay) - (overlay-end field-overlay)))))) + (setf (yas/snippet-undo-saved-info snippet) + (list + ;; + ;; Save boundaries of current field + ;; + (cons (overlay-start field-overlay) + (overlay-end field-overlay)) + ;; + ;; Save a reference to current group + ;; + (yas/snippet-active-group snippet)))))) (defun yas/undo-after-hook () "..." (let* ((snippet (yas/snippet-of-current-keymap)) - (saved-boundaries (and snippet - (yas/snippet-undo-saved-boundaries snippet)))) - (unless (null snippet) - (yas/push-undo-action-maybe (list 'yas/undo-restore-active-group nil))) - (unless (null saved-boundaries) - (yas/push-undo-action-maybe (list 'yas/undo-restore-boundaries - (car saved-boundaries) - (cdr saved-boundaries)))))) + (saved-info (and snippet + (yas/snippet-undo-saved-info snippet)))) + (unless (null saved-info) + (yas/push-undo-action-maybe (list 'yas/undo-restore-active-group + (second saved-info) + (car (first saved-info)) + (cdr (first saved-info))))))) - - -(defun yas/undo-restore-active-group (&optional point) +(defun yas/undo-restore-active-group (group start end) "..." - (let* ((point (or point - (point))) - (snippet (yas/snippet-of-current-keymap point))) - (message "Would restoring group point %s and %s" - point - (if snippet - (format "snippet id %d" (yas/snippet-id snippet)) - "NO SNIPPET!!!")))) + (let* ((snippet (yas/snippet-of-current-keymap)) + (field-overlay (yas/snippet-active-field-overlay snippet)) + (field (yas/group-primary-field group))) + (yas/move-to-group snippet group) + (yas/move-overlay-and-field field-overlay field start end) + (yas/update-mirrors group))) +(defun yas/point-in-field-p (field &optional point) + "..." + (let ((point (or point + (point)))) + (and (>= point (yas/field-start field)) + (<= point (yas/field-end field))))) (defun yas/push-undo-action-maybe (apply-args) "..." @@ -1570,17 +1577,6 @@ registered snippets last." target-separator)))))) -(defun yas/undo-restore-boundaries (start end) - "..." - (let* ((snippet (yas/snippet-of-current-keymap)) - (field-overlay (and snippet - (yas/snippet-active-field-overlay snippet))) - (group (and snippet - (yas/snippet-active-group snippet))) - (field (and group - (yas/group-primary-field group)))) - (yas/move-overlay-and-field field-overlay field start end))) - ;; Debug functions. Use (or change) at will whenever needed. (defun yas/debug-some-vars () From aa75b00b557a6632a1230f972529bd11bd7ada0a Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 15 Sep 2008 08:58:48 +0000 Subject: [PATCH 11/75] * Only the full snippet undo/redo missing. Fields are quite OK, I think. * Also the mirror and primary transforms would be nice. --- yasnippet.el | 458 +++++++++++++++++++++++++++------------------------ 1 file changed, 244 insertions(+), 214 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index dc2fb3c..8be3881 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -3,7 +3,7 @@ ;; Copyright 2008 pluskid ;; ;; Author: pluskid -;; Version: 0.5.6 XXX: Change this +;; Version: 0.5.6 XXX: Change this ;; X-URL: http://code.google.com/p/yasnippet/ ;; This file is free software; you can redistribute it and/or modify @@ -309,7 +309,6 @@ set to t." (next nil) (prev nil) snippet - (deleted nil) (modified nil)) (defstruct (yas/field (:constructor yas/make-field (start end number value transform parent-field))) @@ -535,32 +534,36 @@ the template of a snippet in the current snippet-table." (save-excursion (let* ((inhibit-modification-hooks t) (primary (yas/group-primary-field field-group)) - (text (yas/current-field-text primary))) + (text (yas/current-field-text primary)) + (buffer-undo-list t)) ;; For all fields except the primary, replace their text (yas/replace-fields-with-value (remove-if #'(lambda (field) (equal field primary)) (yas/group-fields field-group)) text) - ;; Call recursively for subfields - (unless dont-recurse-down - (dolist (subfield (yas/field-subfields primary)) - (yas/update-mirrors (yas/field-group subfield)))) - ;; Call recursively for parent field - (when (yas/field-parent-field primary) - (yas/update-mirrors (yas/field-group (yas/field-parent-field primary)) - 'dont-recurse)))))) + ;; Call recursively for subfields + (unless dont-recurse-down + (dolist (subfield (yas/field-subfields primary)) + (yas/update-mirrors (yas/field-group subfield)))) + ;; Call recursively for parent field + (when (yas/field-parent-field primary) + (yas/update-mirrors (yas/field-group (yas/field-parent-field primary)) + 'dont-recurse)))))) (defun yas/current-field-text (field) (buffer-substring-no-properties (yas/field-start field) - (yas/field-end field))) + (yas/field-end field))) (defun yas/current-active-group (&optional snippet point) - "..." + "... + +XXX: TODO: Remove if possible and replace inline. +" (let ((snippet (or snippet - (yas/snippet-of-current-keymap (or point - (point)))))) + (yas/snippet-of-current-keymap (or point + (point)))))) (and snippet - (yas/snippet-active-group snippet)))) + (yas/snippet-active-group snippet)))) (defun yas/overlay-modification-hook (overlay after? beg end &optional length) "Synchronizes all fields for the group of the current field overlay @@ -575,44 +578,45 @@ of the primary field." "Hook for snippet overlay when text is inserted in front of a snippet field." (let ((group (yas/current-active-group))) (when (and after? - group) + group) (let ((inhibit-modification-hooks t)) - ;; - ;; If the group hasn't ever been modified, delete its contents - ;; completely. - ;; - (when (not (yas/group-modified group)) - (setf (yas/group-modified group) t) - (when (> (overlay-end overlay) end) - (save-excursion - (goto-char end) - (delete-char (- (overlay-end overlay) end)))) - ;; - ;; Mark subgroups as `yas/group-deleted', so we're no longer - ;; able to move them. This action is undoable as long as - ;; `yas/undo-before-hook' exists in the `pre-command-hook' - ;; in the proper place. - ;; - (mapcar #'(lambda (group) - (setf (yas/group-deleted group) t)) - (mapcar #'yas/field-group (yas/field-subfields (yas/group-primary-field group))))) - ;; in any case, synchronize mirror fields - (yas/update-mirrors group))))) + ;; + ;; If the group hasn't ever been modified, delete its contents + ;; completely. + ;; + (when (not (yas/group-modified group)) + (setf (yas/group-modified group) t) + (when (> (overlay-end overlay) end) + (save-excursion + (goto-char end) + (delete-char (- (overlay-end overlay) end)))) + ;; ;; +;; ;; Mark subgroups as `yas/group-deleted', so we're no longer +;; ;; able to move them. This action is undoable as long as +;; ;; `yas/undo-before-hook' exists in the `pre-command-hook' +;; ;; in the proper place. +;; ;; +;; (mapcar #'(lambda (group) +;; (setf (yas/group-deleted group) t)) +;; (mapcar #'yas/field-group (yas/field-subfields (yas/group-primary-field group)))) +) + ;; in any case, synchronize mirror fields + (yas/update-mirrors group))))) (defun yas/move-overlay-and-field (overlay field start end) (move-overlay overlay - start - end) + start + end) (move-marker (yas/field-start field) start) (move-marker (yas/field-end field) end)) (defun yas/overlay-insert-behind-hook (overlay after? beg end &optional length) "Hook for snippet overlay when text is inserted just behind the currently active field overlay." (let* ((group (yas/current-active-group)) - (field (and group - (yas/group-primary-field group)))) + (field (and group + (yas/group-primary-field group)))) (when (and after? - field) + field) (yas/move-overlay-and-field overlay field (overlay-start overlay) end) (yas/update-mirrors group)))) @@ -644,12 +648,12 @@ redo-ed." (length (- end start)) (text (yas/calculate-field-value field (or rep (yas/field-value field)))) - (inhibit-modification-hooks t)) + (inhibit-modification-hooks t)) (when text (goto-char start) (insert text) (delete-char length) - (move-marker (yas/field-end field) (point)))))) + (move-marker (yas/field-end field) (point)))))) (defun yas/expand-snippet (start end template) "Expand snippet at current point. Text between START and END @@ -750,12 +754,12 @@ will be deleted before inserting template." (setf (yas/snippet-exit-marker snippet) (copy-marker (point) t))) ;; ;; Step 12: Construct undo information - ;; (unless (eq original-undo-list t) - ;; (add-to-list 'original-undo-list - ;; `(apply yas/undo-expand-snippet - ;; ,(point-min) - ;; ,key - ;; ,snippet))) + ;; (unless (eq original-undo-list t) + ;; (add-to-list 'original-undo-list + ;; `(apply yas/undo-expand-snippet + ;; ,(point-min) + ;; ,key + ;; ,snippet))) ;; Step 13: remove the trigger key (widen) @@ -766,16 +770,16 @@ will be deleted before inserting template." ;; Step 15: place the cursor at a proper place (let* ((first-group (car (yas/snippet-groups snippet))) - (first-field (and first-group - (yas/group-primary-field first-group))) - overlay) - (cond (first-field - ;; Step 10: Move to the new group, setting up - ;; properties of the wandering active field overlay. - (yas/move-to-group snippet first-group)) - (t - ;; no need to call exit-snippet, since no overlay created. - (yas/exit-snippet snippet)))) + (first-field (and first-group + (yas/group-primary-field first-group))) + overlay) + (cond (first-field + ;; Step 10: Move to the new group, setting up + ;; properties of the wandering active field overlay. + (yas/move-to-group snippet first-group)) + (t + ;; no need to call exit-snippet, since no overlay created. + (yas/exit-snippet snippet)))) ;; Step 16: Do necessary indenting (save-excursion @@ -853,34 +857,34 @@ Allows nested placeholder in the style of Textmate." (yas/snippet-add-field snippet (yas/make-field - (set-marker (make-marker) (match-beginning 0)) - (set-marker (make-marker) (or (marker-position bracket-end) - (match-end 0))) + (set-marker (make-marker) (match-beginning 0)) + (set-marker (make-marker) (or (marker-position bracket-end) + (match-end 0))) (and number (string-to-number number)) value transform parent-field))) - (when parent-field - (setf (yas/field-subfields parent-field) - (push brand-new-field (yas/field-subfields parent-field)))) - - ;; f) delete useless regions, move to correct spot for more - ;; search... - (delete-region (match-beginning 0) (or (marker-position value-start) - (point))) + (when parent-field + (setf (yas/field-subfields parent-field) + (push brand-new-field (yas/field-subfields parent-field)))) + + ;; f) delete useless regions, move to correct spot for more + ;; search... + (delete-region (match-beginning 0) (or (marker-position value-start) + (point))) (when value - (when (marker-position bracket-end) - (delete-region value-end bracket-end)) - - ;; g) investigate nested placeholders - (save-excursion - (save-restriction - (narrow-to-region value-start value-end) - (goto-char (point-min)) - (yas/field-parse-create snippet brand-new-field))) - ;; h) - (setf (yas/field-value brand-new-field) - (buffer-substring-no-properties value-start value-end))))))) + (when (marker-position bracket-end) + (delete-region value-end bracket-end)) + + ;; g) investigate nested placeholders + (save-excursion + (save-restriction + (narrow-to-region value-start value-end) + (goto-char (point-min)) + (yas/field-parse-create snippet brand-new-field))) + ;; h) + (setf (yas/field-value brand-new-field) + (buffer-substring-no-properties value-start value-end))))))) (defun yas/field-bracket-end () "Calculates position of the field's closing bracket if any. @@ -903,7 +907,7 @@ placeholders." (defun yas/snippet-of-current-keymap (&optional point) "Return the most recently inserted snippet holding covering POINT." - (let ((point (or point (point))) + (let ((point (or point (point))) (keymap-snippet nil) (snippet nil)) (dolist (overlay (overlays-at point)) @@ -1290,56 +1294,62 @@ when the condition evaluated to non-nil." (defun yas/current-group-for-navigation (&optional snippet) (and snippet - (yas/snippet-active-group snippet))) + (yas/snippet-active-group snippet))) + +(defun yas/group-probably-deleted-p (group) + (let ((primary-field (yas/group-primary-field group))) + (and (zerop (- (yas/field-start primary-field) (yas/field-end primary-field))) + (yas/field-parent-field primary-field)))) (defun yas/next-field-group (&optional arg) "Navigate to next field group. If there's none, exit the snippet." (interactive) (let* ((arg (or arg - 1)) - (snippet (yas/snippet-of-current-keymap)) - (number (and snippet - (+ arg - (yas/group-number (yas/current-group-for-navigation snippet))))) - (target-group (and number - (> number 0) - (find-if #'(lambda (group) - (and (not (yas/group-deleted group)) - (= number (yas/group-number group)))) - (yas/snippet-groups snippet))))) + 1)) + (snippet (yas/snippet-of-current-keymap)) + (number (and snippet + (+ arg + (yas/group-number (yas/current-group-for-navigation snippet))))) + (live-groups (remove-if #'yas/group-probably-deleted-p (yas/snippet-groups snippet))) + (target-group (and number + (> number 0) + (find-if #'(lambda (group) + (= number (yas/group-number group))) + live-groups)))) (cond ((and number - (> number (length (remove-if #'yas/group-deleted (yas/snippet-groups snippet))))) - (yas/exit-snippet snippet)) - (target-group - (yas/move-to-group snippet target-group)) - (t - nil)))) + (> number (length live-groups))) + (yas/exit-snippet snippet)) + (target-group + (yas/move-to-group snippet target-group)) + (t + nil)))) -(defun yas/move-to-group (snippet group) +(defun yas/move-to-group (snippet group &optional dontmove) "Update SNIPPET to move to group GROUP." (let ((field (yas/group-primary-field group)) - (overlay (yas/snippet-active-field-overlay snippet))) - (goto-char (yas/field-start field)) + (overlay (yas/snippet-active-field-overlay snippet))) + (unless dontmove + (goto-char (yas/field-start field))) (setf (yas/snippet-active-group snippet) group) - (setf (yas/group-deleted group) nil) +;; (setf (yas/group-deleted group) nil) (cond ((and overlay - (overlay-buffer overlay)) - (move-overlay overlay (yas/field-start field) - (yas/field-end field))) - (t - (setq overlay (make-overlay (yas/field-start first-field) (yas/field-end first-field))) - (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) - (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) - (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) - (overlay-put overlay 'face 'yas/field-highlight-face) - (setf (yas/snippet-active-field-overlay snippet) overlay))))) + (overlay-buffer overlay)) + (move-overlay overlay (yas/field-start field) + (yas/field-end field))) + (t + (setq overlay (make-overlay (yas/field-start first-field) (yas/field-end first-field))) + (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) + (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) + (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) + (overlay-put overlay 'face 'yas/field-highlight-face) + (setf (yas/snippet-active-field-overlay snippet) overlay))))) (defun yas/prev-field-group () "Navigate to prev field group. If there's none, exit the snippet." (interactive) (yas/next-field-group -1)) - + (defun yas/exit-snippet (snippet) "Goto exit-marker of SNIPPET and cleanup the snippet. Cleaning up the snippet does not delete it!" @@ -1408,7 +1418,7 @@ current buffer." `yas/check-cleanup-snippet' from the `post-command-hook'" (interactive) (maphash #'(lambda (key snippet) - (when (yas/snippet-p snippet) (yas/cleanup-snippet snippet))) + (when (yas/snippet-p snippet) (yas/cleanup-snippet snippet))) yas/registered-snippets) (unless (eq 0 (hash-table-count yas/registered-snippets)) (setq yas/registered-snippets (make-hash-table :test 'eq)) @@ -1418,12 +1428,12 @@ current buffer." "Cleanup SNIPPET, but leave point as it is. This renders the snippet as ordinary text" (let* ((control-overlay (yas/snippet-control-overlay snippet)) - (field-overlay (yas/snippet-active-field-overlay snippet)) + (field-overlay (yas/snippet-active-field-overlay snippet)) yas/snippet-beg yas/snippet-end) ;; ;; Save the end of the moribund snippet in case we need to undo ;; its original expansion. This is used by `yas/undo-expand-snippet' - ;; + ;; (when (and control-overlay (overlay-buffer control-overlay)) (setq yas/snippet-beg (overlay-start control-overlay)) @@ -1432,33 +1442,33 @@ snippet as ordinary text" (delete-overlay control-overlay)) ;; ;; Delete the currently active field overlay if any - ;; + ;; (when (and field-overlay - (overlay-buffer field-overlay)) + (overlay-buffer field-overlay)) (delete-overlay field-overlay)) ;; ;; Iterate every group, and in it, every field. - ;; + ;; (dolist (group (yas/snippet-groups snippet)) (dolist (field (yas/group-fields group)) - (let ((start-marker (yas/field-start field)) - (end-marker (yas/field-end field))) - ;; - ;; convert markers into points, before losing the reference. - ;; - (when (markerp start-marker) - (setf (yas/field-start field) (marker-position start-marker)) - (set-marker start-marker nil)) - (when (markerp end-marker) - (setf (yas/field-end field) (marker-position end-marker)) - (set-marker end-marker nil))))) + (let ((start-marker (yas/field-start field)) + (end-marker (yas/field-end field))) + ;; + ;; convert markers into points, before losing the reference. + ;; + (when (markerp start-marker) + (setf (yas/field-start field) (marker-position start-marker)) + (set-marker start-marker nil)) + (when (markerp end-marker) + (setf (yas/field-end field) (marker-position end-marker)) + (set-marker end-marker nil))))) ;; ;; XXX: `yas/after-exit-snippet-hook' should be run with ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not ;; be the case if the main overlay had somehow already ;; disappeared, which sometimes happens when the snippet's messed ;; up... - ;; + ;; (run-hooks 'yas/after-exit-snippet-hook)) (yas/unregister-snippet snippet)) @@ -1469,8 +1479,8 @@ snippet, if so cleans up the whole snippet up. This function is part of `post-command-hook' while registered snippets last." (let* ((snippet (yas/snippet-of-current-keymap)) - (group (and snippet - (yas/snippet-active-group snippet)))) + (group (and snippet + (yas/snippet-active-group snippet)))) (cond (;; ;; No snippet at point, cleanup *all* snippets ;; @@ -1479,8 +1489,8 @@ registered snippets last." (;; A snippet exits at point, but point left the currently ;; active field overlay (or (not group) - (and group - (not (yas/point-in-field-p (yas/group-primary-field group))))) + (and group + (not (yas/point-in-field-p (yas/group-primary-field group))))) (yas/cleanup-snippet snippet)) (;; ;; Snippet at point, and point inside a snippet field, @@ -1498,83 +1508,102 @@ registered snippets last." (defun yas/undo-before-hook () "..." (let* ((snippet (yas/snippet-of-current-keymap)) - (field-overlay (and snippet - (yas/snippet-active-field-overlay snippet)))) + (field-overlay (and snippet + (yas/snippet-active-field-overlay snippet)))) (when (and field-overlay - (overlay-buffer field-overlay)) + (overlay-buffer field-overlay)) (setf (yas/snippet-undo-saved-info snippet) - (list - ;; - ;; Save boundaries of current field - ;; - (cons (overlay-start field-overlay) - (overlay-end field-overlay)) - ;; - ;; Save a reference to current group - ;; - (yas/snippet-active-group snippet)))))) + (list + ;; + ;; Save boundaries of current field + ;; + (cons (overlay-start field-overlay) + (overlay-end field-overlay)) + ;; + ;; Save a reference to current group + ;; + (yas/snippet-active-group snippet)))))) (defun yas/undo-after-hook () "..." (let* ((snippet (yas/snippet-of-current-keymap)) - (saved-info (and snippet - (yas/snippet-undo-saved-info snippet)))) + (saved-info (and snippet + (yas/snippet-undo-saved-info snippet)))) (unless (null saved-info) (yas/push-undo-action-maybe (list 'yas/undo-restore-active-group - (second saved-info) - (car (first saved-info)) - (cdr (first saved-info))))))) - + (second saved-info) + (car (first saved-info)) + (cdr (first saved-info))))))) + (defun yas/undo-restore-active-group (group start end) "..." (let* ((snippet (yas/snippet-of-current-keymap)) - (field-overlay (yas/snippet-active-field-overlay snippet)) - (field (yas/group-primary-field group))) - (yas/move-to-group snippet group) + (field-overlay (yas/snippet-active-field-overlay snippet)) + (field (yas/group-primary-field group)) + (inhibit-modification-hooks t)) + (yas/move-to-group snippet group 'dontmove) (yas/move-overlay-and-field field-overlay field start end) (yas/update-mirrors group))) (defun yas/point-in-field-p (field &optional point) "..." (let ((point (or point - (point)))) + (point)))) (and (>= point (yas/field-start field)) - (<= point (yas/field-end field))))) + (<= point (yas/field-end field))))) (defun yas/push-undo-action-maybe (apply-args) "..." (let ((undo-list buffer-undo-list) - (target-separator nil) - done) - (unless (eq t buffer-undo-list) + (target-separator nil) + done) + (unless (eq t buffer-undo-list) ;; ;; Discard possibly existing/missing start separator ;; (when (null (car undo-list)) - (setq undo-list (cdr undo-list))) + (setq undo-list (cdr undo-list))) ;; ;; Find the target separator keeping `undo-list' as a reference to ;; the list starting before that. ;; (while (not done) - (cond ((eq (first apply-args) - (condition-case opps - (second (car undo-list)) - (error nil))) - (setq done 'return)) - ((null (cadr undo-list)) - (setq done 'try-insert)) - (t - (setq undo-list (cdr undo-list))))) + (cond ((eq (first apply-args) + (condition-case opps + (second (car undo-list)) + (error nil))) + (setq done 'return)) + ((null (cadr undo-list)) + (setq done 'try-insert)) + (t + (setq undo-list (cdr undo-list))))) (unless (eq done 'return) - ;; - ;; Push a the apply-args action there - ;; - (setq target-separator (cdr undo-list)) - (setf (cdr undo-list) - (cons (cons 'apply - apply-args) - target-separator)))))) + ;; + ;; Push a the apply-args action there + ;; + (setq target-separator (cdr undo-list)) + (setf (cdr undo-list) + (cons (cons 'apply + apply-args) + target-separator)))))) + +(defun yas/sanitize-undo-redo () + (let ((undo-list buffer-undo-list) + done) + (unless (eq t buffer-undo-list) + ;; + ;; Discard possibly existing/missing start separator + ;; + (when (null (car undo-list)) + (setq undo-list (cdr undo-list))) + (delete-if #'(lambda (elem) + (when (and (consp elem) + (integerp (cdr elem)) + (> (cdr elem) (point-max))) + (prog1 t + (message "Deleting %s in the undo-list (greater than point-max=%s)!!!" elem (point-max))))) + undo-list + :end (position nil undo-list))))) ;; Debug functions. Use (or change) at will whenever needed. @@ -1590,52 +1619,53 @@ registered snippets last." (princ " No registered snippets\n")) (t (maphash #'(lambda (key snippet) - (princ (format "\t key %s for snippet %s" + (princ (format "\t key %s for snippet %s" key (yas/snippet-id snippet))) - (princ (format "\t Big overlay %s\n" - (yas/snippet-control-overlay snippet))) + (princ (format "\t Big overlay %s\n" + (yas/snippet-control-overlay snippet))) - (if (yas/snippet-active-field-overlay snippet) - (princ (format "\t Field overlay %s\n " - (yas/snippet-active-field-overlay snippet))) - (princ "No active field overlay!!\m")) + (if (yas/snippet-active-field-overlay snippet) + (princ (format "\t Field overlay %s\n " + (yas/snippet-active-field-overlay snippet))) + (princ "No active field overlay!!\m")) - (dolist (group (yas/snippet-groups snippet)) - (princ (format "\t Group $%s with %s fields is %s and %s" - (yas/group-number group) + (dolist (group (yas/snippet-groups snippet)) + (princ (format "\t Group $%s with %s fields is %s and %s" + (yas/group-number group) (length (yas/group-fields group)) - (if (yas/group-deleted group) - "DELETED" - "alive") - (if (eq group (yas/snippet-active-group snippet)) - "ACTIVE!\n" - "NOT ACTIVE!\n"))) - (dolist (field (yas/group-fields group)) - (princ (format "\t\t* %s field. Current value (%s) .\n" - (if (eq field (yas/group-primary-field group)) - "Primary" "Mirror") - (yas/current-field-text field))) - (princ (format "\t\t From %s to %s\n" - (yas/field-start field) - (yas/field-end field))) - ))) yas/registered-snippets))) + (if (yas/group-probably-deleted-p group) + "DELETED" + "alive") + (if (eq group (yas/snippet-active-group snippet)) + "ACTIVE!\n" + "NOT ACTIVE!\n"))) + (dolist (field (yas/group-fields group)) + (princ (format "\t\t* %s field. Current value (%s) .\n" + (if (eq field (yas/group-primary-field group)) + "Primary" "Mirror") + (yas/current-field-text field))) + (princ (format "\t\t From %s to %s\n" + (yas/field-start field) + (yas/field-end field))) + ))) yas/registered-snippets))) (princ (format "\nPost command hook: %s\n" post-command-hook)) (princ (format "\nPre command hook: %s\n" pre-command-hook)) - (princ (format "\nUndo is %s." + (princ (format "\nUndo is %s and point-max is %s.\n" (if (eq buffer-undo-list t) "DISABLED" - "ENABLED"))) + "ENABLED") + (point-max))) (unless (eq buffer-undo-list t) (princ (format "Undolist has %s elements. First 10 elements follow:\n" (length buffer-undo-list))) (let ((first-ten (subseq buffer-undo-list 0 19))) - (dolist (undo-elem first-ten) - (princ (format "%s: %s\n" (position undo-elem first-ten) undo-elem))))))) + (dolist (undo-elem first-ten) + (princ (format "%s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 50)))))))) (defun yas/exterminate-package () (interactive) From 9aa2cc64863266a0c6fee4234bffce7be5e46ef8 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Wed, 17 Sep 2008 16:36:30 +0000 Subject: [PATCH 12/75] * Cleaning up some code and preparing for complete snippet undo/redo, with nested snippets --- yasnippet.el | 99 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 42 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 8be3881..33b7390 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -298,9 +298,9 @@ set to t." (id (yas/snippet-next-id) :read-only t) (control-overlay nil) (active-field-overlay nil) - undo-saved-info (active-group nil) - (end-marker nil)) + (end-marker nil) + saved-active-group) (defstruct (yas/group (:constructor yas/make-group (primary-field snippet))) "A group contains a list of field with the same number." @@ -309,7 +309,8 @@ set to t." (next nil) (prev nil) snippet - (modified nil)) + (modified nil) + saved-boundaries) (defstruct (yas/field (:constructor yas/make-field (start end number value transform parent-field))) "A field in a snippet." @@ -593,7 +594,7 @@ of the primary field." ;; ;; ;; ;; Mark subgroups as `yas/group-deleted', so we're no longer ;; ;; able to move them. This action is undoable as long as -;; ;; `yas/undo-before-hook' exists in the `pre-command-hook' +;; ;; `yas/save-active-group-boundaries' exists in the `pre-command-hook' ;; ;; in the proper place. ;; ;; ;; (mapcar #'(lambda (group) @@ -905,8 +906,7 @@ placeholders." bracket-end)) (defun yas/snippet-of-current-keymap (&optional point) - "Return the most recently inserted snippet holding covering -POINT." + "Return the most recently inserted snippet covering POINT." (let ((point (or point (point))) (keymap-snippet nil) (snippet nil)) @@ -1392,8 +1392,8 @@ up the snippet does not delete it!" `post-command-hook' that should exist while at least one registered snippet exists in the current buffer. Return snippet" (puthash (yas/snippet-id snippet) snippet yas/registered-snippets) - (add-hook 'pre-command-hook 'yas/undo-before-hook 'append 'local) - (add-hook 'post-command-hook 'yas/undo-after-hook 'append 'local) + (add-hook 'pre-command-hook 'yas/save-active-group-boundaries 'append 'local) + (add-hook 'post-command-hook 'yas/save-active-group-boundaries-after 'append 'local) (add-hook 'post-command-hook 'yas/check-cleanup-snippet 'append 'local) ;; DEBUG (add-hook 'post-command-hook 'yas/debug-some-vars 'append 'local) @@ -1407,8 +1407,8 @@ current buffer." (remhash (yas/snippet-id snippet) yas/registered-snippets) (when (eq 0 (hash-table-count yas/registered-snippets)) - (remove-hook 'pre-command-hook 'yas/undo-before-hook 'local) - (remove-hook 'post-command-hook 'yas/undo-after-hook 'local) + (remove-hook 'pre-command-hook 'yas/save-active-group-boundaries 'local) + (remove-hook 'post-command-hook 'yas/save-active-group-boundaries-after 'local) (remove-hook 'post-command-hook 'yas/check-cleanup-snippet 'local) ;; DEBUG (remove-hook 'post-command-hook 'yas/debug-some-vars 'local))) @@ -1505,43 +1505,58 @@ registered snippets last." ;; ;; ... -(defun yas/undo-before-hook () - "..." - (let* ((snippet (yas/snippet-of-current-keymap)) - (field-overlay (and snippet - (yas/snippet-active-field-overlay snippet)))) - (when (and field-overlay - (overlay-buffer field-overlay)) - (setf (yas/snippet-undo-saved-info snippet) - (list - ;; - ;; Save boundaries of current field - ;; - (cons (overlay-start field-overlay) - (overlay-end field-overlay)) - ;; - ;; Save a reference to current group - ;; - (yas/snippet-active-group snippet)))))) +(defun yas/save-active-group-boundaries () + "While snippet is active, save the active group and the active group's boundaries. -(defun yas/undo-after-hook () - "..." - (let* ((snippet (yas/snippet-of-current-keymap)) - (saved-info (and snippet - (yas/snippet-undo-saved-info snippet)))) - (unless (null saved-info) - (yas/push-undo-action-maybe (list 'yas/undo-restore-active-group - (second saved-info) - (car (first saved-info)) - (cdr (first saved-info))))))) +This is stored in the `yas/group' itself. -(defun yas/undo-restore-active-group (group start end) +Intended to be placed in `pre-command-hook'." + (let* ((snippet (yas/snippet-of-current-keymap)) + (group (yas/snippet-active-group snippet)) + (field-overlay (yas/snippet-active-field-overlay snippet))) + ;; + ;; Save a reference to current group + ;; + (setf (yas/snippet-saved-active-group snippet) + group) + ;; + ;; Save boundaries of current field + ;; + (setf (yas/group-saved-boundaries group) + (cons (overlay-start field-overlay) + (overlay-end field-overlay))))) + +(defun yas/save-active-group-boundaries-after () + "..." + (let* ((snippet (yas/snippet-of-current-keymap)) + (saved-group (yas/snippet-saved-active-group snippet)) + (saved-boundaries (yas/group-saved-boundaries saved-group))) + ;; + ;; Push an action to restore the active group + ;; + (yas/push-undo-action-maybe (list 'yas/restore-active-group + saved-group)) + ;; + ;; Push an action after that to restore the active group's + ;; boundaries + ;; + (yas/push-undo-action-maybe (list 'yas/restore-active-group-boundaries + (car saved-boundaries) + (cdr saved-boundaries))))) + +(defun yas/restore-active-group (group) "..." (let* ((snippet (yas/snippet-of-current-keymap)) - (field-overlay (yas/snippet-active-field-overlay snippet)) - (field (yas/group-primary-field group)) (inhibit-modification-hooks t)) - (yas/move-to-group snippet group 'dontmove) + (yas/move-to-group snippet group 'dontmove))) + +(defun yas/restore-active-group-boundaries (group) + ",,," + (let* ((snippet (yas/snippet-of-current-keymap)) + (group (yas/snippet-active-group snippet)) + (field-overlay (yas/snippet-active-field-overlay snippet)) + (field (yas/group-primary-field group)) + (inhibit-modification-hooks t)) (yas/move-overlay-and-field field-overlay field start end) (yas/update-mirrors group))) From 94e6ee55032b8af5214be27e7f6c64bd00076853 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Thu, 18 Sep 2008 08:25:34 +0000 Subject: [PATCH 13/75] * Simplified passing of info between pre and post-command-hook --- yasnippet.el | 67 +++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 33b7390..1f63a14 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -299,8 +299,7 @@ set to t." (control-overlay nil) (active-field-overlay nil) (active-group nil) - (end-marker nil) - saved-active-group) + (end-marker nil)) (defstruct (yas/group (:constructor yas/make-group (primary-field snippet))) "A group contains a list of field with the same number." @@ -309,8 +308,8 @@ set to t." (next nil) (prev nil) snippet - (modified nil) - saved-boundaries) + (modified nil)) + (defstruct (yas/field (:constructor yas/make-field (start end number value transform parent-field))) "A field in a snippet." @@ -1393,7 +1392,7 @@ up the snippet does not delete it!" registered snippet exists in the current buffer. Return snippet" (puthash (yas/snippet-id snippet) snippet yas/registered-snippets) (add-hook 'pre-command-hook 'yas/save-active-group-boundaries 'append 'local) - (add-hook 'post-command-hook 'yas/save-active-group-boundaries-after 'append 'local) + (add-hook 'post-command-hook 'yas/correct-undo-list 'append 'local) (add-hook 'post-command-hook 'yas/check-cleanup-snippet 'append 'local) ;; DEBUG (add-hook 'post-command-hook 'yas/debug-some-vars 'append 'local) @@ -1408,7 +1407,7 @@ current buffer." (when (eq 0 (hash-table-count yas/registered-snippets)) (remove-hook 'pre-command-hook 'yas/save-active-group-boundaries 'local) - (remove-hook 'post-command-hook 'yas/save-active-group-boundaries-after 'local) + (remove-hook 'post-command-hook 'yas/correct-undo-list 'local) (remove-hook 'post-command-hook 'yas/check-cleanup-snippet 'local) ;; DEBUG (remove-hook 'post-command-hook 'yas/debug-some-vars 'local))) @@ -1505,6 +1504,8 @@ registered snippets last." ;; ;; ... +(defvar yas/pending-undo-actions nil) + (defun yas/save-active-group-boundaries () "While snippet is active, save the active group and the active group's boundaries. @@ -1512,51 +1513,40 @@ This is stored in the `yas/group' itself. Intended to be placed in `pre-command-hook'." (let* ((snippet (yas/snippet-of-current-keymap)) - (group (yas/snippet-active-group snippet)) - (field-overlay (yas/snippet-active-field-overlay snippet))) + (group (yas/snippet-active-group snippet)) + (field-overlay (yas/snippet-active-field-overlay snippet)) + undo-actions) ;; ;; Save a reference to current group ;; - (setf (yas/snippet-saved-active-group snippet) - group) + (push (list 'yas/restore-active-group + group + snippet) + undo-actions) ;; ;; Save boundaries of current field ;; - (setf (yas/group-saved-boundaries group) - (cons (overlay-start field-overlay) - (overlay-end field-overlay))))) + (push (list 'yas/restore-active-group-boundaries + group + snippet + (overlay-start field-overlay) + (overlay-end field-overlay)) + undo-actions))) -(defun yas/save-active-group-boundaries-after () - "..." - (let* ((snippet (yas/snippet-of-current-keymap)) - (saved-group (yas/snippet-saved-active-group snippet)) - (saved-boundaries (yas/group-saved-boundaries saved-group))) - ;; - ;; Push an action to restore the active group - ;; - (yas/push-undo-action-maybe (list 'yas/restore-active-group - saved-group)) - ;; - ;; Push an action after that to restore the active group's - ;; boundaries - ;; - (yas/push-undo-action-maybe (list 'yas/restore-active-group-boundaries - (car saved-boundaries) - (cdr saved-boundaries))))) -(defun yas/restore-active-group (group) + +(defun yas/restore-active-group (group snippet) "..." - (let* ((snippet (yas/snippet-of-current-keymap)) - (inhibit-modification-hooks t)) + (let ((inhibit-modification-hooks t)) (yas/move-to-group snippet group 'dontmove))) -(defun yas/restore-active-group-boundaries (group) +(defun yas/restore-active-group-boundaries (group snippet start end) ",,," (let* ((snippet (yas/snippet-of-current-keymap)) - (group (yas/snippet-active-group snippet)) - (field-overlay (yas/snippet-active-field-overlay snippet)) + (group (yas/snippet-active-group snippet)) + (field-overlay (yas/snippet-active-field-overlay snippet)) (field (yas/group-primary-field group)) - (inhibit-modification-hooks t)) + (inhibit-modification-hooks t)) (yas/move-overlay-and-field field-overlay field start end) (yas/update-mirrors group))) @@ -1567,6 +1557,9 @@ Intended to be placed in `pre-command-hook'." (and (>= point (yas/field-start field)) (<= point (yas/field-end field))))) +(defun yas/correct-undo-list () + (mapcar #'yas/push-undo-action-maybe yas/pending-undo-actions)) + (defun yas/push-undo-action-maybe (apply-args) "..." (let ((undo-list buffer-undo-list) From d117ee38572624a62ff77b80800d389d3d77c168 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Thu, 18 Sep 2008 15:08:20 +0000 Subject: [PATCH 14/75] * Nice try reviving snippets. --- yasnippet.el | 219 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 133 insertions(+), 86 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 1f63a14..4f90346 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -298,8 +298,7 @@ set to t." (id (yas/snippet-next-id) :read-only t) (control-overlay nil) (active-field-overlay nil) - (active-group nil) - (end-marker nil)) + (active-group nil)) (defstruct (yas/group (:constructor yas/make-group (primary-field snippet))) "A group contains a list of field with the same number." @@ -565,13 +564,23 @@ XXX: TODO: Remove if possible and replace inline. (and snippet (yas/snippet-active-group snippet)))) +(defun yas/make-control-overlay (start end) + "..." + (let ((overlay (make-overlay start + end + nil + nil + t))) + (overlay-put overlay 'keymap yas/keymap) + (overlay-put overlay 'yas/snippet-reference snippet) + overlay)) + (defun yas/overlay-modification-hook (overlay after? beg end &optional length) "Synchronizes all fields for the group of the current field overlay Used to ensure mirror fields in the same group contain the same value of the primary field." - (when after? - ;; (and after? (not undo-in-progress)) + (when (and after? (not undo-in-progress)) (yas/update-mirrors (yas/current-active-group)))) (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length) @@ -604,11 +613,29 @@ of the primary field." (yas/update-mirrors group))))) (defun yas/move-overlay-and-field (overlay field start end) - (move-overlay overlay - start - end) - (move-marker (yas/field-start field) start) - (move-marker (yas/field-end field) end)) + ;; + ;; Move the overlay to the correct spot, creating one if necessary. + ;; + (cond ((and overlay + (overlay-buffer overlay)) + (move-overlay overlay start end)) + (t + (setq overlay (make-overlay start end)) + (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) + (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) + (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) + (overlay-put overlay 'face 'yas/field-highlight-face))) + ;; + ;; Move the markers to the correct spot, correcting them if they're + ;; no longer markers + ;; + (if (markerp (yas/field-start field)) + (move-marker (yas/field-start field) start) + (setf (yas/field-start field) (set-marker (make-marker) start))) + (if (markerp (yas/field-end field)) + (move-marker (yas/field-end field) end) + (setf (yas/field-end field) (set-marker (make-marker) end))) + overlay) (defun yas/overlay-insert-behind-hook (overlay after? beg end &optional length) "Hook for snippet overlay when text is inserted just behind the currently active field overlay." @@ -620,26 +647,6 @@ of the primary field." (yas/move-overlay-and-field overlay field (overlay-start overlay) end) (yas/update-mirrors group)))) -(defun yas/remove-recent-undo-from-history () - (let ((undo (car buffer-undo-list))) - (while (null undo) - (setq buffer-undo-list (cdr buffer-undo-list)) - (setq undo (car buffer-undo-list))) - ;; Remove this undo operation record - (setq buffer-undo-list (cdr buffer-undo-list)))) - -(defun yas/undo-expand-snippet (start key snippet) - "Undo a snippet expansion. Delete the overlays. This undo can't be -redo-ed." - (yas/remove-recent-undo-from-history) - (let ((inhibit-modification-hooks t) - (buffer-undo-list t)) - (yas/exit-snippet snippet) - (goto-char start) - (delete-char (- (yas/snippet-end-marker snippet) - start)) - (insert key))) - (defun yas/replace-fields-with-value (fields &optional rep) "TODO: revise need for this rebuscatedeness." (dolist (field fields) @@ -722,17 +729,9 @@ will be deleted before inserting template." (when prev (setf (yas/group-next prev) group)) (setq prev group))) - + ;; Step 7: Create keymap overlay for snippet - (let ((overlay (make-overlay (point-min) - (point-max) - nil - nil - t))) - (overlay-put overlay 'keymap yas/keymap) - (overlay-put overlay 'yas/snippet-reference snippet) - (setf (yas/snippet-control-overlay snippet) overlay) - (setf (yas/snippet-end-marker snippet) (overlay-end overlay))) + (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay (point-min) (point-max))) ;; Step 8: Replace mirror field values with primary group's ;; value @@ -1330,19 +1329,10 @@ when the condition evaluated to non-nil." (unless dontmove (goto-char (yas/field-start field))) (setf (yas/snippet-active-group snippet) group) -;; (setf (yas/group-deleted group) nil) - (cond ((and overlay - (overlay-buffer overlay)) - (move-overlay overlay (yas/field-start field) - (yas/field-end field))) - (t - (setq overlay (make-overlay (yas/field-start first-field) (yas/field-end first-field))) - (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) - (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) - (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) - (overlay-put overlay 'face 'yas/field-highlight-face) - (setf (yas/snippet-active-field-overlay snippet) overlay))))) - + (setf (yas/snippet-active-field-overlay snippet) + (yas/move-overlay-and-field overlay field + (yas/field-start field) + (yas/field-end field))))) (defun yas/prev-field-group () "Navigate to prev field group. If there's none, exit the snippet." @@ -1385,17 +1375,34 @@ up the snippet does not delete it!" (eval-when-compile (make-variable-buffer-local 'yas/registered-snippets)) +(defun yas/add-remove-many-hooks (hook-var fn-list &optional remove) + (mapcar (if remove + #'(lambda (fn) (remove-hook hook-var fn 'local)) + #'(lambda (fn) (add-hook hook-var fn 'append 'local))) + fn-list)) + (defun yas/register-snippet (snippet) "Register SNIPPET in the `yas/registered-snippets' table. Add a `yas/check-cleanup-snippet' function to the buffer-local `post-command-hook' that should exist while at least one registered snippet exists in the current buffer. Return snippet" + ;; + ;; register the snippet + ;; (puthash (yas/snippet-id snippet) snippet yas/registered-snippets) - (add-hook 'pre-command-hook 'yas/save-active-group-boundaries 'append 'local) - (add-hook 'post-command-hook 'yas/correct-undo-list 'append 'local) - (add-hook 'post-command-hook 'yas/check-cleanup-snippet 'append 'local) - ;; DEBUG - (add-hook 'post-command-hook 'yas/debug-some-vars 'append 'local) + ;; + ;; setup the `pre-command-hook' + ;; + (yas/add-remove-many-hooks 'pre-command-hook + (list 'yas/clear-pending-undo-actions + 'yas/save-active-group-boundaries)) + ;; + ;; setup the `post-command-hook' + ;; + (yas/add-remove-many-hooks 'post-command-hook + (list 'yas/check-cleanup-snippet + 'yas/correct-undo-list + 'yas/debug-some-vars)) snippet) (defun yas/unregister-snippet (snippet) @@ -1406,11 +1413,15 @@ current buffer." (remhash (yas/snippet-id snippet) yas/registered-snippets) (when (eq 0 (hash-table-count yas/registered-snippets)) - (remove-hook 'pre-command-hook 'yas/save-active-group-boundaries 'local) - (remove-hook 'post-command-hook 'yas/correct-undo-list 'local) - (remove-hook 'post-command-hook 'yas/check-cleanup-snippet 'local) - ;; DEBUG - (remove-hook 'post-command-hook 'yas/debug-some-vars 'local))) + (yas/add-remove-many-hooks 'pre-command-hook + (list 'yas/clear-pending-undo-actions + 'yas/save-active-group-boundaries) + 'remove) + (yas/add-remove-many-hooks 'post-command-hook + (list 'yas/correct-undo-list + 'yas/check-cleanup-snippet + 'yas/debug-some-vars) + 'remove))) (defun yas/exterminate-snippets () "Remove all locally registered snippets and remove @@ -1428,7 +1439,9 @@ current buffer." snippet as ordinary text" (let* ((control-overlay (yas/snippet-control-overlay snippet)) (field-overlay (yas/snippet-active-field-overlay snippet)) - yas/snippet-beg yas/snippet-end) + yas/snippet-beg + yas/snippet-end + saved-groups-and-boundaries) ;; ;; Save the end of the moribund snippet in case we need to undo ;; its original expansion. This is used by `yas/undo-expand-snippet' @@ -1437,7 +1450,6 @@ snippet as ordinary text" (overlay-buffer control-overlay)) (setq yas/snippet-beg (overlay-start control-overlay)) (setq yas/snippet-end (overlay-end control-overlay)) - (setf (yas/snippet-end-marker snippet) yas/snippet-end) (delete-overlay control-overlay)) ;; ;; Delete the currently active field overlay if any @@ -1462,6 +1474,16 @@ snippet as ordinary text" (setf (yas/field-end field) (marker-position end-marker)) (set-marker end-marker nil))))) ;; + ;; forget all other pending undo actions and push a undo/redo + ;; action for snippet revival + ;; + (setq yas/pending-undo-actions nil) + (yas/push-undo-action-maybe (list 'yas/revive-snippet + snippet + yas/snippet-beg + yas/snippet-end + (yas/snippet-active-group snippet))) + ;; ;; XXX: `yas/after-exit-snippet-hook' should be run with ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not ;; be the case if the main overlay had somehow already @@ -1471,6 +1493,7 @@ snippet as ordinary text" (run-hooks 'yas/after-exit-snippet-hook)) (yas/unregister-snippet snippet)) + (defun yas/check-cleanup-snippet () "Checks if point exited the currently active field of the snippet, if so cleans up the whole snippet up. @@ -1506,45 +1529,69 @@ registered snippets last." (defvar yas/pending-undo-actions nil) +(defun yas/clear-pending-undo-actions () + (setq yas/pending-undo-actions nil)) + (defun yas/save-active-group-boundaries () - "While snippet is active, save the active group and the active group's boundaries. + "While snippet is active, save the active group and the active +group's boundaries. -This is stored in the `yas/group' itself. +Creates undo actions in `yas/pending-undo-actions' that will +eventually be pushed into the `buffer-undo-list' variable. This +function is intended to be placed in `pre-command-hook'. -Intended to be placed in `pre-command-hook'." +The actual pushing of actions into the `buffer-undo-list' is +performed in `yas/correct-undo-list', which is placed in the +`post-command-hook'." (let* ((snippet (yas/snippet-of-current-keymap)) (group (yas/snippet-active-group snippet)) - (field-overlay (yas/snippet-active-field-overlay snippet)) - undo-actions) + (field-overlay (yas/snippet-active-field-overlay snippet))) + ;; + ;; Save boundaries of current field + ;; + (push (list 'yas/restore-group-boundaries + group + snippet + (overlay-start field-overlay) + (overlay-end field-overlay)) + yas/pending-undo-actions) ;; ;; Save a reference to current group ;; (push (list 'yas/restore-active-group group snippet) - undo-actions) - ;; - ;; Save boundaries of current field - ;; - (push (list 'yas/restore-active-group-boundaries - group - snippet - (overlay-start field-overlay) - (overlay-end field-overlay)) - undo-actions))) - + yas/pending-undo-actions))) +(defun yas/revive-snippet (snippet snippet-start snippet-end active-group) + ;; + ;; Revive the control overlay + ;; + (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay snippet-start snippet-end)) + ;; + ;; Revive each group + ;; + (dolist (group (yas/snippet-groups snippet)) + (yas/restore-group-boundaries group snippet + (yas/field-start (yas/group-primary-field group)) + (yas/field-end (yas/group-primary-field group)))) + ;; + ;; Move to the previously active group + ;; + (yas/restore-active-group active-group snippet) + ;; + ;; Reregister this snippet + ;; + (yas/register-snippet snipept)) (defun yas/restore-active-group (group snippet) "..." (let ((inhibit-modification-hooks t)) (yas/move-to-group snippet group 'dontmove))) -(defun yas/restore-active-group-boundaries (group snippet start end) +(defun yas/restore-group-boundaries (group snippet start end) ",,," - (let* ((snippet (yas/snippet-of-current-keymap)) - (group (yas/snippet-active-group snippet)) - (field-overlay (yas/snippet-active-field-overlay snippet)) + (let* ((field-overlay (yas/snippet-active-field-overlay snippet)) (field (yas/group-primary-field group)) (inhibit-modification-hooks t)) (yas/move-overlay-and-field field-overlay field start end) @@ -1609,11 +1656,11 @@ Intended to be placed in `pre-command-hook'." (integerp (cdr elem)) (> (cdr elem) (point-max))) (prog1 t - (message "Deleting %s in the undo-list (greater than point-max=%s)!!!" elem (point-max))))) + (message "Deleting %s in the undo-list (greater than point-max=%s)!!!" + elem (point-max))))) undo-list :end (position nil undo-list))))) - ;; Debug functions. Use (or change) at will whenever needed. (defun yas/debug-some-vars () From 9fa6f3533ee7321e3d70bc0b5a793cb08407a80f Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Thu, 18 Sep 2008 21:43:28 +0000 Subject: [PATCH 15/75] * Getting there, getting there, `yas/push-undo-action-maybe' has to be modified to adapt to new arg list and use `pushnew' * A `yas/cleanup-snippet' action has to be pushed on `yas/expand' * expanding a new snippet with a snippet already active must render the previous one disabled. This shouldn't be a field, it should be found out on the fly. disabled means its big overlay is coloured some other color. * transformations have to be accounted for * code has to be cleaned up and thoroughly commented. --- yasnippet.el | 177 ++++++++++++++++++++++++++------------------------- 1 file changed, 91 insertions(+), 86 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 4f90346..62709db 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -567,10 +567,10 @@ XXX: TODO: Remove if possible and replace inline. (defun yas/make-control-overlay (start end) "..." (let ((overlay (make-overlay start - end - nil - nil - t))) + end + nil + nil + t))) (overlay-put overlay 'keymap yas/keymap) (overlay-put overlay 'yas/snippet-reference snippet) overlay)) @@ -617,14 +617,14 @@ of the primary field." ;; Move the overlay to the correct spot, creating one if necessary. ;; (cond ((and overlay - (overlay-buffer overlay)) - (move-overlay overlay start end)) - (t - (setq overlay (make-overlay start end)) - (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) - (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) - (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) - (overlay-put overlay 'face 'yas/field-highlight-face))) + (overlay-buffer overlay)) + (move-overlay overlay start end)) + (t + (setq overlay (make-overlay start end)) + (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) + (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) + (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) + (overlay-put overlay 'face 'yas/field-highlight-face))) ;; ;; Move the markers to the correct spot, correcting them if they're ;; no longer markers @@ -729,9 +729,9 @@ will be deleted before inserting template." (when prev (setf (yas/group-next prev) group)) (setq prev group))) - + ;; Step 7: Create keymap overlay for snippet - (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay (point-min) (point-max))) + (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay (point-min) (point-max))) ;; Step 8: Replace mirror field values with primary group's ;; value @@ -1330,9 +1330,9 @@ when the condition evaluated to non-nil." (goto-char (yas/field-start field))) (setf (yas/snippet-active-group snippet) group) (setf (yas/snippet-active-field-overlay snippet) - (yas/move-overlay-and-field overlay field - (yas/field-start field) - (yas/field-end field))))) + (yas/move-overlay-and-field overlay field + (yas/field-start field) + (yas/field-end field))))) (defun yas/prev-field-group () "Navigate to prev field group. If there's none, exit the snippet." @@ -1377,9 +1377,9 @@ up the snippet does not delete it!" (defun yas/add-remove-many-hooks (hook-var fn-list &optional remove) (mapcar (if remove - #'(lambda (fn) (remove-hook hook-var fn 'local)) - #'(lambda (fn) (add-hook hook-var fn 'append 'local))) - fn-list)) + #'(lambda (fn) (remove-hook hook-var fn 'local)) + #'(lambda (fn) (add-hook hook-var fn 'append 'local))) + fn-list)) (defun yas/register-snippet (snippet) "Register SNIPPET in the `yas/registered-snippets' table. Add a @@ -1388,21 +1388,21 @@ up the snippet does not delete it!" registered snippet exists in the current buffer. Return snippet" ;; ;; register the snippet - ;; + ;; (puthash (yas/snippet-id snippet) snippet yas/registered-snippets) ;; ;; setup the `pre-command-hook' ;; (yas/add-remove-many-hooks 'pre-command-hook - (list 'yas/clear-pending-undo-actions - 'yas/save-active-group-boundaries)) + (list 'yas/clear-pending-undo-actions + 'yas/save-active-group-boundaries)) ;; ;; setup the `post-command-hook' ;; (yas/add-remove-many-hooks 'post-command-hook - (list 'yas/check-cleanup-snippet - 'yas/correct-undo-list - 'yas/debug-some-vars)) + (list 'yas/check-cleanup-snippet + 'yas/correct-undo-list + 'yas/debug-some-vars)) snippet) (defun yas/unregister-snippet (snippet) @@ -1414,14 +1414,14 @@ current buffer." (when (eq 0 (hash-table-count yas/registered-snippets)) (yas/add-remove-many-hooks 'pre-command-hook - (list 'yas/clear-pending-undo-actions - 'yas/save-active-group-boundaries) - 'remove) + (list 'yas/clear-pending-undo-actions + 'yas/save-active-group-boundaries) + 'remove) (yas/add-remove-many-hooks 'post-command-hook - (list 'yas/correct-undo-list - 'yas/check-cleanup-snippet - 'yas/debug-some-vars) - 'remove))) + (list 'yas/correct-undo-list + 'yas/check-cleanup-snippet + 'yas/debug-some-vars) + 'remove))) (defun yas/exterminate-snippets () "Remove all locally registered snippets and remove @@ -1440,8 +1440,8 @@ snippet as ordinary text" (let* ((control-overlay (yas/snippet-control-overlay snippet)) (field-overlay (yas/snippet-active-field-overlay snippet)) yas/snippet-beg - yas/snippet-end - saved-groups-and-boundaries) + yas/snippet-end + saved-groups-and-boundaries) ;; ;; Save the end of the moribund snippet in case we need to undo ;; its original expansion. This is used by `yas/undo-expand-snippet' @@ -1477,12 +1477,12 @@ snippet as ordinary text" ;; forget all other pending undo actions and push a undo/redo ;; action for snippet revival ;; - (setq yas/pending-undo-actions nil) - (yas/push-undo-action-maybe (list 'yas/revive-snippet - snippet - yas/snippet-beg - yas/snippet-end - (yas/snippet-active-group snippet))) + (setq yas/pending-undo-actions (list + (list 'above-all + `(yas/revive-snippet ,snippet + ,yas/snippet-beg + ,yas/snippet-end + ,(yas/snippet-active-group snippet))))) ;; ;; XXX: `yas/after-exit-snippet-hook' should be run with ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not @@ -1549,18 +1549,20 @@ performed in `yas/correct-undo-list', which is placed in the ;; ;; Save boundaries of current field ;; - (push (list 'yas/restore-group-boundaries - group - snippet - (overlay-start field-overlay) - (overlay-end field-overlay)) + (push (list 'after-first-action + (list 'yas/restore-group-boundaries + group + snippet + (overlay-start field-overlay) + (overlay-end field-overlay))) yas/pending-undo-actions) ;; ;; Save a reference to current group ;; - (push (list 'yas/restore-active-group - group - snippet) + (push (list 'after-first-action + (list 'yas/restore-active-group + group + snippet)) yas/pending-undo-actions))) (defun yas/revive-snippet (snippet snippet-start snippet-end active-group) @@ -1569,20 +1571,20 @@ performed in `yas/correct-undo-list', which is placed in the ;; (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay snippet-start snippet-end)) ;; - ;; Revive each group + ;; Revive each group ;; (dolist (group (yas/snippet-groups snippet)) (yas/restore-group-boundaries group snippet - (yas/field-start (yas/group-primary-field group)) - (yas/field-end (yas/group-primary-field group)))) + (yas/field-start (yas/group-primary-field group)) + (yas/field-end (yas/group-primary-field group)))) ;; ;; Move to the previously active group - ;; + ;; (yas/restore-active-group active-group snippet) ;; - ;; Reregister this snippet + ;; Reregister this snippet ;; - (yas/register-snippet snipept)) + (yas/register-snippet snippet)) (defun yas/restore-active-group (group snippet) "..." @@ -1605,42 +1607,45 @@ performed in `yas/correct-undo-list', which is placed in the (<= point (yas/field-end field))))) (defun yas/correct-undo-list () - (mapcar #'yas/push-undo-action-maybe yas/pending-undo-actions)) + (mapcar #'(lambda (args) + (apply #'yas/push-undo-action-maybe args)) + yas/pending-undo-actions)) -(defun yas/push-undo-action-maybe (apply-args) +(defun yas/push-undo-action-maybe (how apply-args &optional jump-first-separator) "..." (let ((undo-list buffer-undo-list) (target-separator nil) done) (unless (eq t buffer-undo-list) - ;; - ;; Discard possibly existing/missing start separator - ;; - (when (null (car undo-list)) - (setq undo-list (cdr undo-list))) - ;; - ;; Find the target separator keeping `undo-list' as a reference to - ;; the list starting before that. - ;; - (while (not done) - (cond ((eq (first apply-args) - (condition-case opps - (second (car undo-list)) - (error nil))) - (setq done 'return)) - ((null (cadr undo-list)) - (setq done 'try-insert)) - (t - (setq undo-list (cdr undo-list))))) - (unless (eq done 'return) - ;; - ;; Push a the apply-args action there - ;; - (setq target-separator (cdr undo-list)) - (setf (cdr undo-list) - (cons (cons 'apply - apply-args) - target-separator)))))) + (cond ((eq how 'after-first-action) + ;; + ;; Discard possibly existing/missing start separator + ;; + (when (null (car undo-list)) + (setq undo-list (cdr undo-list))) + ;; + ;; Find the target separator keeping `undo-list' as a reference to + ;; the list starting before that. + ;; + (while (not done) + (cond ((eq (first apply-args) + (condition-case opps + (second (car undo-list)) + (error nil))) + (setq done 'return)) + ((null (cadr undo-list)) + (setq done 'try-insert)) + (t + (setq undo-list (cdr undo-list))))) + (unless (eq done 'return) + ;; + ;; Push a the apply-args action there + ;; + (setq target-separator (cdr undo-list)) + (setf (cdr undo-list) + (cons (cons 'apply + apply-args) + target-separator)))))))) (defun yas/sanitize-undo-redo () (let ((undo-list buffer-undo-list) @@ -1657,7 +1662,7 @@ performed in `yas/correct-undo-list', which is placed in the (> (cdr elem) (point-max))) (prog1 t (message "Deleting %s in the undo-list (greater than point-max=%s)!!!" - elem (point-max))))) + elem (point-max))))) undo-list :end (position nil undo-list))))) From 50e7af74fad215b221a5f3a7142782977c6bca8b Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Fri, 19 Sep 2008 16:40:50 +0000 Subject: [PATCH 16/75] * Fuckin' more complicated than I thought. Think I'll have to sanitize the `buffer-undo-list' once in a while --- yasnippet.el | 221 +++++++++++++++++++++++++++------------------------ 1 file changed, 119 insertions(+), 102 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 62709db..562a779 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -752,13 +752,14 @@ will be deleted before inserting template." (unless (yas/snippet-exit-marker snippet) (setf (yas/snippet-exit-marker snippet) (copy-marker (point) t))) - ;; ;; Step 12: Construct undo information - ;; (unless (eq original-undo-list t) - ;; (add-to-list 'original-undo-list - ;; `(apply yas/undo-expand-snippet - ;; ,(point-min) - ;; ,key - ;; ,snippet))) + ;; Step 12: Construct undo information + (setq yas/pending-undo-actions (list + (list 'before-first-action + `(apply yas/cleanup-snippet + ,snippet + after-first-action) + 'jump-first-separator))) + ;; Step 13: remove the trigger key (widen) @@ -1375,6 +1376,14 @@ up the snippet does not delete it!" (eval-when-compile (make-variable-buffer-local 'yas/registered-snippets)) +(defvar yas/temporary-pre-command-hooks (list 'yas/clear-pending-undo-actions + 'yas/save-active-group-boundaries)) + +(defvar yas/temporary-post-command-hooks (list 'yas/check-cleanup-snippet + 'yas/push-pending-undo-actions + 'yas/debug-some-vars)) + + (defun yas/add-remove-many-hooks (hook-var fn-list &optional remove) (mapcar (if remove #'(lambda (fn) (remove-hook hook-var fn 'local)) @@ -1391,18 +1400,10 @@ registered snippet exists in the current buffer. Return snippet" ;; (puthash (yas/snippet-id snippet) snippet yas/registered-snippets) ;; - ;; setup the `pre-command-hook' + ;; setup the `pre-command-hook' and `post-command-hook' ;; - (yas/add-remove-many-hooks 'pre-command-hook - (list 'yas/clear-pending-undo-actions - 'yas/save-active-group-boundaries)) - ;; - ;; setup the `post-command-hook' - ;; - (yas/add-remove-many-hooks 'post-command-hook - (list 'yas/check-cleanup-snippet - 'yas/correct-undo-list - 'yas/debug-some-vars)) + (yas/add-remove-many-hooks 'pre-command-hook yas/temporary-pre-command-hooks) + (yas/add-remove-many-hooks 'post-command-hook yas/temporary-post-command-hooks) snippet) (defun yas/unregister-snippet (snippet) @@ -1410,23 +1411,30 @@ registered snippet exists in the current buffer. Return snippet" table. Remove `yas/check-cleanup-snippet' from the buffer-local `post-command-hook' if no more snippets registered in the current buffer." + ;; + ;; + ;; (remhash (yas/snippet-id snippet) yas/registered-snippets) + ;; + ;; + ;; (when (eq 0 (hash-table-count yas/registered-snippets)) - (yas/add-remove-many-hooks 'pre-command-hook - (list 'yas/clear-pending-undo-actions - 'yas/save-active-group-boundaries) - 'remove) - (yas/add-remove-many-hooks 'post-command-hook - (list 'yas/correct-undo-list - 'yas/check-cleanup-snippet - 'yas/debug-some-vars) - 'remove))) + (yas/add-remove-many-hooks 'pre-command-hook yas/temporary-pre-command-hooks 'remove) + (yas/add-remove-many-hooks 'post-command-hook yas/temporary-post-command-hooks 'remove) + (when yas/pending-undo-actions + (add-hook 'post-command-hook 'yas/push-pending-undo-actions-once 'append 'local)))) + (defun yas/exterminate-snippets () "Remove all locally registered snippets and remove `yas/check-cleanup-snippet' from the `post-command-hook'" (interactive) + (setq yas/pending-undo-actions nil) + (setq buffer-undo-list nil) + (yas/cleanup-all-snippets)) + +(defun yas/cleanup-all-snippets () (maphash #'(lambda (key snippet) (when (yas/snippet-p snippet) (yas/cleanup-snippet snippet))) yas/registered-snippets) @@ -1434,7 +1442,7 @@ current buffer." (setq yas/registered-snippets (make-hash-table :test 'eq)) (message "Warning: yas/snippet hash-table not fully clean. Forcing NIL."))) -(defun yas/cleanup-snippet (snippet) +(defun yas/cleanup-snippet (snippet &optional undo-action-method) "Cleanup SNIPPET, but leave point as it is. This renders the snippet as ordinary text" (let* ((control-overlay (yas/snippet-control-overlay snippet)) @@ -1477,12 +1485,15 @@ snippet as ordinary text" ;; forget all other pending undo actions and push a undo/redo ;; action for snippet revival ;; - (setq yas/pending-undo-actions (list - (list 'above-all - `(yas/revive-snippet ,snippet - ,yas/snippet-beg - ,yas/snippet-end - ,(yas/snippet-active-group snippet))))) + (unless (eq 'this-command 'yas/exterminate-snippets) + (setq yas/pending-undo-actions (list + (list (or undo-action-method 'before-first-action) + `(apply yas/revive-snippet + ,snippet + ,yas/snippet-beg + ,yas/snippet-end + ,(yas/snippet-active-group snippet)) + 'jump-first-separator)))) ;; ;; XXX: `yas/after-exit-snippet-hook' should be run with ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not @@ -1507,7 +1518,7 @@ registered snippets last." ;; No snippet at point, cleanup *all* snippets ;; (null snippet) - (yas/exterminate-snippets)) + (yas/cleanup-all-snippets)) (;; A snippet exits at point, but point left the currently ;; active field overlay (or (not group) @@ -1521,13 +1532,13 @@ registered snippets last." t nil)))) -;; Field-level undo functionality ;; -;; XXX: Commentary on this section by joaot. +;; Undo functionality ;; -;; ... (defvar yas/pending-undo-actions nil) +(eval-when-compile + (make-variable-buffer-local 'yas/pending-undo-actions)) (defun yas/clear-pending-undo-actions () (setq yas/pending-undo-actions nil)) @@ -1550,41 +1561,45 @@ performed in `yas/correct-undo-list', which is placed in the ;; Save boundaries of current field ;; (push (list 'after-first-action - (list 'yas/restore-group-boundaries - group - snippet - (overlay-start field-overlay) - (overlay-end field-overlay))) + `(apply yas/restore-group-boundaries + ,group + ,snippet + ,(overlay-start field-overlay) + ,(overlay-end field-overlay))) yas/pending-undo-actions) ;; ;; Save a reference to current group ;; (push (list 'after-first-action - (list 'yas/restore-active-group - group - snippet)) + `(apply yas/restore-active-group ,group ,snippet)) yas/pending-undo-actions))) (defun yas/revive-snippet (snippet snippet-start snippet-end active-group) - ;; - ;; Revive the control overlay - ;; - (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay snippet-start snippet-end)) - ;; - ;; Revive each group - ;; - (dolist (group (yas/snippet-groups snippet)) - (yas/restore-group-boundaries group snippet - (yas/field-start (yas/group-primary-field group)) - (yas/field-end (yas/group-primary-field group)))) - ;; - ;; Move to the previously active group - ;; - (yas/restore-active-group active-group snippet) - ;; - ;; Reregister this snippet - ;; - (yas/register-snippet snippet)) + (let ((inhibit-modification-hooks t) + (buffer-undo-list t)) + ;; + ;; Revive the control overlay + ;; + (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay snippet-start snippet-end)) + ;; + ;; Revive each group + ;; + (dolist (group (yas/snippet-groups snippet)) + (yas/restore-group-boundaries group snippet + (yas/field-start (yas/group-primary-field group)) + (yas/field-end (yas/group-primary-field group)))) + ;; + ;; Move to the previously active group + ;; + (yas/move-to-group snippet active-group) + ;; + ;; Reregister this snippet + ;; + (yas/register-snippet snippet) + ;; + ;; Erase any pending undo actions. + ;; + (setq yas/pending-undo-actions nil))) (defun yas/restore-active-group (group snippet) "..." @@ -1596,7 +1611,7 @@ performed in `yas/correct-undo-list', which is placed in the (let* ((field-overlay (yas/snippet-active-field-overlay snippet)) (field (yas/group-primary-field group)) (inhibit-modification-hooks t)) - (yas/move-overlay-and-field field-overlay field start end) + (setf (yas/snippet-active-field-overlay snippet) (yas/move-overlay-and-field field-overlay field start end)) (yas/update-mirrors group))) (defun yas/point-in-field-p (field &optional point) @@ -1606,46 +1621,48 @@ performed in `yas/correct-undo-list', which is placed in the (and (>= point (yas/field-start field)) (<= point (yas/field-end field))))) -(defun yas/correct-undo-list () +(defun yas/push-pending-undo-actions () (mapcar #'(lambda (args) (apply #'yas/push-undo-action-maybe args)) yas/pending-undo-actions)) -(defun yas/push-undo-action-maybe (how apply-args &optional jump-first-separator) +(defun yas/push-pending-undo-actions-once () + (yas/push-pending-undo-actions) + (remove-hook 'post-command-hook 'yas/push-pending-undo-actions-once 'local)) + +(defun yas/push-undo-action-maybe (how entry &optional jump-first-separator) "..." - (let ((undo-list buffer-undo-list) - (target-separator nil) - done) - (unless (eq t buffer-undo-list) - (cond ((eq how 'after-first-action) - ;; - ;; Discard possibly existing/missing start separator - ;; - (when (null (car undo-list)) - (setq undo-list (cdr undo-list))) - ;; - ;; Find the target separator keeping `undo-list' as a reference to - ;; the list starting before that. - ;; - (while (not done) - (cond ((eq (first apply-args) - (condition-case opps - (second (car undo-list)) - (error nil))) - (setq done 'return)) - ((null (cadr undo-list)) - (setq done 'try-insert)) - (t - (setq undo-list (cdr undo-list))))) - (unless (eq done 'return) - ;; - ;; Push a the apply-args action there - ;; - (setq target-separator (cdr undo-list)) - (setf (cdr undo-list) - (cons (cons 'apply - apply-args) - target-separator)))))))) + (unless (eq t buffer-undo-list) + (cond (;; + ;; + ;; + (eq 'after-first-action how) + (let ((undo-list buffer-undo-list) + done) + (when (and jump-first-separator + (null (car undo-list))) + (setq undo-list (cdr undo-list))) + (while (not done) + (cond ((condition-case oops + (and (eq 'apply (first entry)) + (eq (second entry) + (second (car undo-list)))) + (error nil)) + (setq done 'return)) + ((null (cadr undo-list)) + (setq done 'try-insert)) + (t + (setq undo-list (cdr undo-list))))) + (unless (eq done 'return) + (push entry (cdr undo-list))))) + (;; + ;; + ;; + (eq 'before-first-action how) + (if (and jump-first-separator + (null (car buffer-undo-list))) + (push entry (cdr buffer-undo-list)) + (push entry buffer-undo-list)))))) (defun yas/sanitize-undo-redo () (let ((undo-list buffer-undo-list) @@ -1725,7 +1742,7 @@ performed in `yas/correct-undo-list', which is placed in the (princ (format "Undolist has %s elements. First 10 elements follow:\n" (length buffer-undo-list))) (let ((first-ten (subseq buffer-undo-list 0 19))) (dolist (undo-elem first-ten) - (princ (format "%s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 50)))))))) + (princ (format "%2s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 70)))))))) (defun yas/exterminate-package () (interactive) From 795c08dddcb7bae0b5c60b37d0fa6e9ef56bc013 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Fri, 23 Jan 2009 14:38:15 +0000 Subject: [PATCH 17/75] --- snippets/text-mode/html-mode/dov | 5 ++ yasnippet.el | 79 +++++++++++++++++++++++--------- 2 files changed, 62 insertions(+), 22 deletions(-) create mode 100644 snippets/text-mode/html-mode/dov diff --git a/snippets/text-mode/html-mode/dov b/snippets/text-mode/html-mode/dov new file mode 100644 index 0000000..e2cfd03 --- /dev/null +++ b/snippets/text-mode/html-mode/dov @@ -0,0 +1,5 @@ +#name : ... +# -- + + $0 + diff --git a/yasnippet.el b/yasnippet.el index 562a779..d6144cb 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -1552,7 +1552,7 @@ eventually be pushed into the `buffer-undo-list' variable. This function is intended to be placed in `pre-command-hook'. The actual pushing of actions into the `buffer-undo-list' is -performed in `yas/correct-undo-list', which is placed in the +performed in `yas/push-pending-undo-actions', which is placed in the `post-command-hook'." (let* ((snippet (yas/snippet-of-current-keymap)) (group (yas/snippet-active-group snippet)) @@ -1626,6 +1626,9 @@ performed in `yas/correct-undo-list', which is placed in the (apply #'yas/push-undo-action-maybe args)) yas/pending-undo-actions)) +;; +;; TODO: get rid of this "once" thing +;; (defun yas/push-pending-undo-actions-once () (yas/push-pending-undo-actions) (remove-hook 'post-command-hook 'yas/push-pending-undo-actions-once 'local)) @@ -1711,27 +1714,29 @@ performed in `yas/correct-undo-list', which is placed in the (dolist (group (yas/snippet-groups snippet)) - (princ (format "\t Group $%s with %s fields is %s and %s" - (yas/group-number group) - (length (yas/group-fields group)) - (if (yas/group-probably-deleted-p group) - "DELETED" - "alive") - (if (eq group (yas/snippet-active-group snippet)) - "ACTIVE!\n" - "NOT ACTIVE!\n"))) - (dolist (field (yas/group-fields group)) - (princ (format "\t\t* %s field. Current value (%s) .\n" - (if (eq field (yas/group-primary-field group)) - "Primary" "Mirror") - (yas/current-field-text field))) - (princ (format "\t\t From %s to %s\n" - (yas/field-start field) - (yas/field-end field))) - ))) yas/registered-snippets))) + ;; (princ (format "\t Group $%s with %s fields is %s and %s" +;; (yas/group-number group) +;; (length (yas/group-fields group)) +;; (if (yas/group-probably-deleted-p group) +;; "DELETED" +;; "alive") +;; (if (eq group (yas/snippet-active-group snippet)) +;; "LIVE!\n" +;; "SLEEPY!\n"))) +;; (dolist (field (yas/group-fields group)) +;; (princ (format "\t\t* %s field. Current value (%s) .\n" +;; (if (eq field (yas/group-primary-field group)) +;; "Primary" "Mirror") +;; (yas/current-field-text field))) +;; (princ (format "\t\t From %s to %s\n" +;; (yas/field-start field) +;; (yas/field-end field))) +;; ) +)) yas/registered-snippets))) + + (princ (format "\nPRE- command hook: %s\n" pre-command-hook)) + (princ (format "\nPOST- command hook: %s\n" post-command-hook)) - (princ (format "\nPost command hook: %s\n" post-command-hook)) - (princ (format "\nPre command hook: %s\n" pre-command-hook)) (princ (format "\nUndo is %s and point-max is %s.\n" (if (eq buffer-undo-list t) @@ -1742,7 +1747,37 @@ performed in `yas/correct-undo-list', which is placed in the (princ (format "Undolist has %s elements. First 10 elements follow:\n" (length buffer-undo-list))) (let ((first-ten (subseq buffer-undo-list 0 19))) (dolist (undo-elem first-ten) - (princ (format "%2s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 70)))))))) + (princ (format "%2s: %s\n" + (position undo-elem first-ten) + (cond ((null undo-elem) + "--- (separator) ---") + (t + (truncate-string-to-width (format "%s" undo-elem) 70)))))))))) + + +(defun yas/debug-pprint-group (group) + (cond ((yas/group-p group) + (format "[%sgrp $%s with %s flds %s]" + (if (eq group (yas/snippet-active-group (yas/group-snippet field-group))) + "LIVE " + "") + (yas/group-number group) + (length (yas/group-fields group)) + (mapconcat #'yas/debug-pprint-field (yas/group-fields group) " ")) + (t + "(not a group!")))) + +(defun yas/debug-pprint-field (field) + (cond ((yas/field-p field) + (format "[%s: (%s) %s->%s]" + (if (eq field (yas/group-primary-field (yas/field-group field))) + "primary" + "mirror") + (yas/current-field-text field) + (yas/field-start field) + (yas/field-end field))))) + + (defun yas/exterminate-package () (interactive) From 14735e1ba01081f2808b5bc2a3885eabc285633c Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Wed, 1 Jul 2009 16:58:39 +0000 Subject: [PATCH 18/75] Trashed prior implementation of nested snippets and now going full steam on a much better one (I hope...) --- yasnippet.el | 998 +++++++++++++++------------------------------------ 1 file changed, 288 insertions(+), 710 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index d6144cb..1741132 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -69,11 +69,11 @@ current column if this variable is non-`nil'.") (defvar yas/keymap (make-sparse-keymap) "The keymap of snippet.") -(define-key yas/keymap yas/next-field-key 'yas/next-field-group) -(define-key yas/keymap (kbd "S-TAB") 'yas/prev-field-group) -(define-key yas/keymap (kbd "") 'yas/prev-field-group) -(define-key yas/keymap (kbd "") 'yas/prev-field-group) -(define-key yas/keymap (kbd "") 'yas/prev-field-group) +(define-key yas/keymap yas/next-field-key 'yas/next-field) +(define-key yas/keymap (kbd "S-TAB") 'yas/prev-field) +(define-key yas/keymap (kbd "") 'yas/prev-field) +(define-key yas/keymap (kbd "") 'yas/prev-field) +(define-key yas/keymap (kbd "") 'yas/prev-field) (defvar yas/show-all-modes-in-menu nil "Currently yasnippet only all \"real modes\" to menubar. For @@ -96,6 +96,11 @@ mode will be listed under the menu \"yasnippet\".") (t (:background "DimGrey"))) "The face used to highlight the currently active field of a snippet") +(defface yas/field-debug-face + '((((class color) (background light)) (:background "tomato")) + (t (:background "tomato"))) + "The face used for debugging") + (defvar yas/window-system-popup-function #'yas/dropdown-list-popup-for-template "When there's multiple candidate for a snippet key. This function is called to let user select one of them. `yas/text-popup-function' @@ -209,9 +214,12 @@ to expand. (defconst yas/escape-backquote (concat "YASESCAPE" "BACKQUOTE" "PROTECTGUARD")) +;; (defconst yas/field-regexp +;; (concat "$\\([0-9]+\\)" "\\|" +;; "${\\(?:\\([0-9]+\\):\\)?\\([^}]*\\)}")) (defconst yas/field-regexp (concat "$\\([0-9]+\\)" "\\|" - "${\\(?:\\([0-9]+\\):\\)?\\([^}]*\\)}")) + "${\\(?:\\([0-9]+\\):\\)?\\(.*\\)}")) (defvar yas/snippet-id-seed 0 "Contains the next id for a snippet.") @@ -220,23 +228,6 @@ to expand. (incf yas/snippet-id-seed) id)) -(defvar yas/overlay-modification-hooks - (list 'yas/overlay-modification-hook) - "The list of hooks to the overlay modification event.") -(defvar yas/overlay-insert-in-front-hooks - (list 'yas/overlay-insert-in-front-hook) - "The list of hooks of the overlay inserted in front event.") -(defvar yas/overlay-insert-behind-hooks - (list 'yas/overlay-insert-behind-hook) - "The list of hooks of the overlay inserted behind event.") - - -(setq yas/keymap-overlay-modification-hooks nil) - -;; (defvar yas/keymap-overlay-modification-hooks -;; (list 'yas/overlay-maybe-insert-behind-hook) -;; "The list of hooks of the big keymap overlay modification event.") - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; YASnippet minor mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -289,86 +280,49 @@ set to t." content name condition) + (defstruct (yas/snippet (:constructor yas/make-snippet ())) "A snippet. ..." - (groups nil) - (exit-marker nil) + (fields '()) + (exit nil) (id (yas/snippet-next-id) :read-only t) (control-overlay nil) (active-field-overlay nil) - (active-group nil)) + (active-field nil)) -(defstruct (yas/group (:constructor yas/make-group (primary-field snippet))) - "A group contains a list of field with the same number." - primary-field - (fields (list primary-field)) +(defstruct (yas/field (:constructor yas/make-field (number overlay-pair parent-field))) + "A field." + number + overlay-pair + parent-field + (mirrors '()) (next nil) (prev nil) - snippet + (transform nil) (modified nil)) -(defstruct (yas/field - (:constructor yas/make-field (start end number value transform parent-field))) - "A field in a snippet." - start - end - number - transform - value - parent-field - subfields - group) +(defstruct (yas/mirror (:constructor yas/make-mirror (overlay))) + "A mirror." + overlay + (transform nil)) + +(defun yas/field-start (field) (overlay-start (car (yas/field-overlay-pair field)))) +(defun yas/field-end (field) (overlay-end (cdr (yas/field-overlay-pair field)))) + +(defun yas/mirror-start (mirror) (overlay-start (yas/mirror-overlay mirror))) +(defun yas/mirror-end (mirror) (overlay-end (yas/mirror-overlay mirror))) + (defstruct (yas/snippet-table (:constructor yas/make-snippet-table ())) "A table to store snippets for a perticular mode." (hash (make-hash-table :test 'equal)) (parent nil)) -(defun yas/snippet-valid? (snippet) - "See if snippet is valid (ie. still alive)." - (and (not (null snippet)) - (not (null (yas/snippet-control-overlay snippet))) - (not (null (overlay-start (yas/snippet-control-overlay snippet)))))) - -(defun yas/snippet-add-field (snippet field) - "Add FIELD to the correct group of SNIPPET. - -If no group is found, create one using `yas/make-group'. Return -FIELD." - (let ((group (find field - (yas/snippet-groups snippet) - :test - '(lambda (field group) - (and (not (null (yas/field-number field))) - (not (null (yas/group-number group))) - (= (yas/field-number field) - (yas/group-number group))))))) - (if group - (yas/group-add-field group field) - (setq group (yas/make-group field snippet)) - (push group (yas/snippet-groups snippet))) - - (setf (yas/field-group field) group)) - field) - -(defun yas/group-value (group) - "Get the default value of the field group." - (or (yas/field-value - (yas/group-primary-field group)) - "")) -(defun yas/group-number (group) - "Get the number of the field GROUP." - (yas/field-number - (yas/group-primary-field group))) -(defun yas/group-add-field (group field) - "Add a FIELD to the field GROUP. If the value of the primary -field is nil and that of the field is not nil, the field is set -as the primary field of the group." - (push field (yas/group-fields group)) - (when (and (null (yas/field-value (yas/group-primary-field group))) - (yas/field-value field)) - (setf (yas/group-primary-field group) field))) +(defun yas/snippet-find-field (snippet number) + (find-if #'(lambda (field) + (eq number (yas/field-number field))) + (yas/snippet-fields snippet))) (defun yas/snippet-field-compare (field1 field2) "Compare two fields. The field with a number is sorted first. @@ -469,15 +423,19 @@ a list of modes like this to help the judgement." (format "%s" (eval (read string)))))) (error (format "(error in elisp evaluation: %s)" (error-message-string err))))) -(defun yas/calculate-field-value (field value) + +(defun yas/apply-transform (field-or-mirror field) "Calculate the value of the field. If there's a transform for this field, apply it. Otherwise, the value is returned unmodified." - (let ((text value) - (transform (yas/field-transform field))) + (let ((text (yas/field-text-for-display field)) + (transform (if (yas/mirror-p field-or-mirror) + (yas/mirror-transform field-or-mirror) + (yas/field-transform field-or-mirror)))) (if transform (yas/eval-string transform) text))) + (defsubst yas/replace-all (from to) "Replace all occurance from FROM to TO." (goto-char (point-min)) @@ -527,42 +485,28 @@ the template of a snippet in the current snippet-table." start end))) -(defun yas/update-mirrors (field-group &optional dont-recurse-down) - "Update all mirror fields' text according to the primary field." - (when (yas/snippet-valid? (yas/group-snippet field-group)) - (save-excursion - (let* ((inhibit-modification-hooks t) - (primary (yas/group-primary-field field-group)) - (text (yas/current-field-text primary)) - (buffer-undo-list t)) - ;; For all fields except the primary, replace their text - (yas/replace-fields-with-value (remove-if #'(lambda (field) - (equal field primary)) - (yas/group-fields field-group)) - text) - ;; Call recursively for subfields - (unless dont-recurse-down - (dolist (subfield (yas/field-subfields primary)) - (yas/update-mirrors (yas/field-group subfield)))) - ;; Call recursively for parent field - (when (yas/field-parent-field primary) - (yas/update-mirrors (yas/field-group (yas/field-parent-field primary)) - 'dont-recurse)))))) +(defun yas/field-text-for-display (field &optional field-number) + "Return the propertized display text for field FIELD. " + (let ((text (yas/current-field-text field))) + (when text + (while (and (string-match yas/field-regexp text) + (match-beginning 3)) + (setq text + (concat + (substring text 0 (match-beginning 0)) + (if (and field-number + (match-beginning 2) + (= field-number + (string-to-number (substring text (match-beginning 2))))) + (propertize (substring text (match-beginning 3) (match-end 3)) 'face 'yas/field-highlight-face) + (substring text (match-beginning 3) (match-end 3))) + (substring text (match-end 0))))) + text))) (defun yas/current-field-text (field) (buffer-substring-no-properties (yas/field-start field) (yas/field-end field))) -(defun yas/current-active-group (&optional snippet point) - "... - -XXX: TODO: Remove if possible and replace inline. -" - (let ((snippet (or snippet - (yas/snippet-of-current-keymap (or point - (point)))))) - (and snippet - (yas/snippet-active-group snippet)))) (defun yas/make-control-overlay (start end) "..." @@ -575,112 +519,46 @@ XXX: TODO: Remove if possible and replace inline. (overlay-put overlay 'yas/snippet-reference snippet) overlay)) -(defun yas/overlay-modification-hook (overlay after? beg end &optional length) - "Synchronizes all fields for the group of the current field overlay +(defun yas/on-field-overlay-modification (overlay after? beg end &optional length) + "To be written" + (when (and after? + yas/registered-snippets) + (maphash #'(lambda (key snippet) + (dolist (field (yas/snippet-fields snippet)) + (dolist (mirror (yas/field-mirrors field)) + (yas/mirror-update-display mirror field)))) + yas/registered-snippets))) -Used to ensure mirror fields in the same group contain the same value -of the primary field." - (when (and after? (not undo-in-progress)) - (yas/update-mirrors (yas/current-active-group)))) +(defun yas/on-hidden-overlay-modification (overlay after? beg end &optional length) + (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length) - "Hook for snippet overlay when text is inserted in front of a snippet field." - (let ((group (yas/current-active-group))) - (when (and after? - group) - (let ((inhibit-modification-hooks t)) - ;; - ;; If the group hasn't ever been modified, delete its contents - ;; completely. - ;; - (when (not (yas/group-modified group)) - (setf (yas/group-modified group) t) - (when (> (overlay-end overlay) end) - (save-excursion - (goto-char end) - (delete-char (- (overlay-end overlay) end)))) - ;; ;; -;; ;; Mark subgroups as `yas/group-deleted', so we're no longer -;; ;; able to move them. This action is undoable as long as -;; ;; `yas/save-active-group-boundaries' exists in the `pre-command-hook' -;; ;; in the proper place. -;; ;; -;; (mapcar #'(lambda (group) -;; (setf (yas/group-deleted group) t)) -;; (mapcar #'yas/field-group (yas/field-subfields (yas/group-primary-field group)))) -) - ;; in any case, synchronize mirror fields - (yas/update-mirrors group))))) - -(defun yas/move-overlay-and-field (overlay field start end) - ;; - ;; Move the overlay to the correct spot, creating one if necessary. - ;; - (cond ((and overlay - (overlay-buffer overlay)) - (move-overlay overlay start end)) - (t - (setq overlay (make-overlay start end)) - (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks) - (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks) - (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks) - (overlay-put overlay 'face 'yas/field-highlight-face))) - ;; - ;; Move the markers to the correct spot, correcting them if they're - ;; no longer markers - ;; - (if (markerp (yas/field-start field)) - (move-marker (yas/field-start field) start) - (setf (yas/field-start field) (set-marker (make-marker) start))) - (if (markerp (yas/field-end field)) - (move-marker (yas/field-end field) end) - (setf (yas/field-end field) (set-marker (make-marker) end))) - overlay) + "To be written" + ) (defun yas/overlay-insert-behind-hook (overlay after? beg end &optional length) - "Hook for snippet overlay when text is inserted just behind the currently active field overlay." - (let* ((group (yas/current-active-group)) - (field (and group - (yas/group-primary-field group)))) - (when (and after? - field) - (yas/move-overlay-and-field overlay field (overlay-start overlay) end) - (yas/update-mirrors group)))) - -(defun yas/replace-fields-with-value (fields &optional rep) -"TODO: revise need for this rebuscatedeness." - (dolist (field fields) - (let* ((start (yas/field-start field)) - (end (yas/field-end field)) - (length (- end start)) - (text (yas/calculate-field-value field (or rep - (yas/field-value field)))) - (inhibit-modification-hooks t)) - (when text - (goto-char start) - (insert text) - (delete-char length) - (move-marker (yas/field-end field) (point)))))) + "To be written" + ) (defun yas/expand-snippet (start end template) "Expand snippet at current point. Text between START and END will be deleted before inserting template." (run-hooks 'yas/before-expand-snippet-hook) - (goto-char start) - (let ((key (buffer-substring-no-properties start end)) - (original-undo-list buffer-undo-list) ;; save previous undo information - (inhibit-modification-hooks t) - (length (- end start)) - (column (current-column))) + (let* ((key (buffer-substring-no-properties start end)) + (original-undo-list buffer-undo-list) ;; save previous undo information + (buffer-undo-list t) + (inhibit-modification-hooks t) + (length (- end start)) + (column (current-column))) (save-restriction (narrow-to-region start start) ;; (setq buffer-undo-list t) ;; disable undo for a short while (insert template) - ;; Step 1: do necessary indent + ;; Step XX: do necessary indent (when yas/indent-line (let* ((indent (if indent-tabs-mode (concat (make-string (/ column tab-width) ?\t) @@ -691,11 +569,11 @@ will be deleted before inserting template." (= (current-column) 0)) (insert indent)))) - ;; Step 2: protect backslash and backquote + ;; Step XX: protect backslash and backquote (yas/replace-all "\\\\" yas/escape-backslash) (yas/replace-all "\\`" yas/escape-backquote) - ;; Step 3: evaluate all backquotes + ;; Step XX: evaluate all backquotes (goto-char (point-min)) (while (re-search-forward "`\\([^`]*\\)`" nil t) ;; go back so that (current-column) in elisp code evaluation @@ -704,84 +582,77 @@ will be deleted before inserting template." (replace-match (yas/eval-string (match-string-no-properties 1)) t t)) - ;; Step 4: protect all escapes, including backslash and backquot + ;; Step XX: protect all escapes, including backslash and backquot ;; which may be produced in Step 3 (yas/replace-all "\\\\" yas/escape-backslash) (yas/replace-all "\\`" yas/escape-backquote) (yas/replace-all "\\$" yas/escape-dollar) - ;; Step 5: Create and register a brand new snippet in the local + ;; Step XX: Create and register a brand new snippet in the local ;; `yas/registered-snippets' var. Create fields. (let ((snippet (yas/register-snippet (yas/make-snippet)))) (goto-char (point-min)) (yas/field-parse-create snippet) - ;; Step 6: Sort and link each field group - (setf (yas/snippet-groups snippet) - (sort (yas/snippet-groups snippet) - '(lambda (group1 group2) - (yas/snippet-field-compare - (yas/group-primary-field group1) - (yas/group-primary-field group2))))) + ;; Step XX: Sort and link each field + (setf (yas/snippet-fields snippet) + (sort (yas/snippet-fields snippet) + '(lambda (field1 field2) + (yas/snippet-field-compare field1 field2)))) + (let ((prev nil)) - (dolist (group (yas/snippet-groups snippet)) - (setf (yas/group-prev group) prev) + (dolist (field (yas/snippet-fields snippet)) + (setf (yas/field-prev field) prev) (when prev - (setf (yas/group-next prev) group)) - (setq prev group))) + (setf (yas/field-next prev) field)) + (setq prev field))) - ;; Step 7: Create keymap overlay for snippet + ;; Step XX: Hide (or highlight for debugging) all hidden overlays + (let ((prop-list)) + (push (if (member 'yas/debug-some-vars post-command-hook) + (cons 'face 'yas/field-debug-face) + (cons 'invisible t)) + prop-list) + (push (cons 'evaporate t) prop-list) + (dolist (prop prop-list) + (dolist (field (yas/snippet-fields snippet)) + (overlay-put (car (yas/field-overlay-pair field)) (car prop) (cdr prop)) + (overlay-put (cdr (yas/field-overlay-pair field)) (car prop) (cdr prop)) + (dolist (mirror (yas/field-mirrors field)) + (overlay-put (yas/mirror-overlay mirror) (car prop) (cdr prop)) + (yas/mirror-update-display mirror field))) + (when (overlayp (yas/snippet-exit snippet)) + (overlay-put (yas/snippet-exit snippet) (car prop) (cdr prop))))) + + ;; Step XX: Create keymap overlay for snippet (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay (point-min) (point-max))) - ;; Step 8: Replace mirror field values with primary group's - ;; value - (dolist (group (yas/snippet-groups snippet)) - (yas/replace-fields-with-value - (remove-if #'(lambda (field) - (eq (yas/group-primary-field group) field)) - (yas/group-fields group)) - (yas/group-value group))) - - ;; Step 9: restore all escape characters + ;; Step XX: restore all escape characters (yas/replace-all yas/escape-dollar "$") (yas/replace-all yas/escape-backquote "`") (yas/replace-all yas/escape-backslash "\\") - ;; Step 11: move to end and make sure exit-marker exist + ;; Step XX: move to end and make sure exit-marker exist (goto-char (point-max)) - (unless (yas/snippet-exit-marker snippet) - (setf (yas/snippet-exit-marker snippet) (copy-marker (point) t))) + (unless (yas/snippet-exit snippet) + (setf (yas/snippet-exit snippet) (copy-marker (point) t))) - ;; Step 12: Construct undo information - (setq yas/pending-undo-actions (list - (list 'before-first-action - `(apply yas/cleanup-snippet - ,snippet - after-first-action) - 'jump-first-separator))) - - - ;; Step 13: remove the trigger key + ;; Step XX: remove the trigger key (widen) (delete-char length) - ;; ;; Step 14: Restore undo information - ;; (setq buffer-undo-list original-undo-list) - - ;; Step 15: place the cursor at a proper place - (let* ((first-group (car (yas/snippet-groups snippet))) - (first-field (and first-group - (yas/group-primary-field first-group))) + ;; Step XX: place the cursor at a proper place + (let* ((first-field (car (yas/snippet-fields snippet))) overlay) (cond (first-field - ;; Step 10: Move to the new group, setting up + ;; Step XX: Move to the new field, setting up ;; properties of the wandering active field overlay. - (yas/move-to-group snippet first-group)) + (yas/move-to-field snippet first-field)) (t ;; no need to call exit-snippet, since no overlay created. (yas/exit-snippet snippet)))) - ;; Step 16: Do necessary indenting + ;; Step XX: Do necessary indenting (save-excursion (goto-char (overlay-start (yas/snippet-control-overlay snippet))) (while (re-search-forward "$>" nil t) @@ -793,117 +664,36 @@ will be deleted before inserting template." necessary fields. Allows nested placeholder in the style of Textmate." - ;; 5. Search from current point for yas/field-regexp - ;; - ;; a) That is, look for "$<`number'>" or - ;; "${<`number'>:<`value'>}". A few special cases. - ;; - ;; b) When `value' starts with $, assume the rest is a lisp - ;; expression returning string. assign that to `transform' - ;; - ;; A transformation is signalled when `value' starts with the - ;; character "$" as the first value after the ":". The rest of - ;; `value' is not allowed to have any other nested snippet - ;; definitions. Don't know if Textmate allows this... - ;; - ;; c) If `number' is 0 (zero) the string found is deleted and - ;; that special place is the snippet's exit marker... - ;; - ;; d) Otherwise a placeholder field for `number' is added to the - ;; snippet with `value' and `transform'. - ;; - ;; f) The enclosing "${<`number'>:" and closing bracket regions are - ;; delete. - ;; - ;; g) Then, still, buffer is temporarily narrowed down to `value' - ;; and `yas/field-parse-create' is called again recursively with - ;; the recently created field as `parent-field'. That might - ;; actually add more fields. - ;; - ;; h) Update `value' of the newly created field to adjust for some - ;; possible pruning that happened in the subcalls to - ;; `yas/field-parse-create' - ;; - ;; (while (re-search-forward yas/field-regexp nil t) - (let* ((number (or (match-string-no-properties 1) - (match-string-no-properties 2))) - (transform nil) - (bracket-end (set-marker (make-marker) - (yas/field-bracket-end))) - (value-start (set-marker (make-marker) (match-beginning 3))) - (value-end (set-marker (make-marker) - (or (and (marker-position bracket-end) - (1- bracket-end)) - (match-end 3)))) - (value (when (and (marker-position value-start) - (marker-position value-end)) - (buffer-substring-no-properties value-start value-end))) - brand-new-field) - ;; b) look for a transformation - (when (eq (elt value 0) ?\$) - (setq transform (substring value 1)) - (setq value nil)) - (if (and number - (string= "0" number)) - ;; c) set exit marker and forget - (progn - (replace-match "") - (setf (yas/snippet-exit-marker snippet) - (copy-marker (point) t))) - ;; d) add a brand new field, linking it to the possible parent - ;; field and adding it to the parent field's subfield list. - (setq brand-new-field - (yas/snippet-add-field - snippet - (yas/make-field - (set-marker (make-marker) (match-beginning 0)) - (set-marker (make-marker) (or (marker-position bracket-end) - (match-end 0))) - (and number (string-to-number number)) - value - transform - parent-field))) - (when parent-field - (setf (yas/field-subfields parent-field) - (push brand-new-field (yas/field-subfields parent-field)))) - - ;; f) delete useless regions, move to correct spot for more - ;; search... - (delete-region (match-beginning 0) (or (marker-position value-start) - (point))) - (when value - (when (marker-position bracket-end) - (delete-region value-end bracket-end)) - - ;; g) investigate nested placeholders - (save-excursion - (save-restriction - (narrow-to-region value-start value-end) - (goto-char (point-min)) - (yas/field-parse-create snippet brand-new-field))) - ;; h) - (setf (yas/field-value brand-new-field) - (buffer-substring-no-properties value-start value-end))))))) - -(defun yas/field-bracket-end () - "Calculates position of the field's closing bracket if any. - -Assumes a regexp search for `yas/field-regexp' matched -recently. Return Nil if no field value subexpression was found, -or throws error if the snippet has malformed nested -placeholders." - (let ((bracket-or-number-start (1+ (match-beginning 0))) - bracket-end) - (when (eq ?\{ (char-after bracket-or-number-start)) - (setq bracket-end (condition-case oops - (scan-sexps bracket-or-number-start 1) - ;; TODO: Later should throw another error with - ;; information about failed syntax! - (error - (message "Invalid snippet template!"))))) - bracket-end)) + (let ((number (or (match-string-no-properties 1) + (match-string-no-properties 2)))) + (cond ((and number + (string= "0" number)) + (setf (yas/snippet-exit snippet) + (make-overlay (match-beginning 0) (match-end 0)))) + ((match-beginning 3) + (let ((brand-new-field (yas/make-field (and number (string-to-number number)) + (cons (make-overlay (match-beginning 0) + (match-beginning 3)) + (make-overlay (match-end 3) + (match-end 0))) + parent-field))) + (push brand-new-field (yas/snippet-fields snippet)) + (save-excursion + (save-restriction + (narrow-to-region (match-beginning 3) (match-end 3)) + (goto-char (point-min)) + (yas/field-parse-create snippet brand-new-field))))) + (t + (let ((field (yas/snippet-find-field snippet (and number (string-to-number number))))) + (when field + (push (yas/make-mirror (make-overlay (match-beginning 0) + (match-end 0))) + (yas/field-mirrors field))))))))) +(defun yas/mirror-update-display (mirror field) + (overlay-put (yas/mirror-overlay mirror) 'after-string (yas/apply-transform mirror field))) + (defun yas/snippet-of-current-keymap (&optional point) "Return the most recently inserted snippet covering POINT." (let ((point (or point (point))) @@ -1291,110 +1081,81 @@ when the condition evaluated to non-nil." (when (commandp command) (call-interactively command)))))))))) -(defun yas/current-group-for-navigation (&optional snippet) - (and snippet - (yas/snippet-active-group snippet))) +(defun yas/field-probably-deleted-p (field) + "Guess if FIELD was deleted because of his parent-field" + (and (zerop (- (yas/field-start field) (yas/field-end field))) + (yas/field-parent-field field))) -(defun yas/group-probably-deleted-p (group) - (let ((primary-field (yas/group-primary-field group))) - (and (zerop (- (yas/field-start primary-field) (yas/field-end primary-field))) - (yas/field-parent-field primary-field)))) - -(defun yas/next-field-group (&optional arg) - "Navigate to next field group. If there's none, exit the snippet." +(defun yas/next-field (&optional arg) + "Navigate to next field. If there's none, exit the snippet." (interactive) (let* ((arg (or arg 1)) (snippet (yas/snippet-of-current-keymap)) (number (and snippet (+ arg - (yas/group-number (yas/current-group-for-navigation snippet))))) - (live-groups (remove-if #'yas/group-probably-deleted-p (yas/snippet-groups snippet))) - (target-group (and number - (> number 0) - (find-if #'(lambda (group) - (= number (yas/group-number group))) - live-groups)))) + (yas/field-number (yas/snippet-active-field snippet))))) + (live-fields (remove-if #'yas/field-probably-deleted-p (yas/snippet-fields snippet))) + (target-field (yas/snippet-find-field snippet number))) (cond ((and number - (> number (length live-groups))) + (> number (length live-fields))) (yas/exit-snippet snippet)) - (target-group - (yas/move-to-group snippet target-group)) + (target-field + (yas/move-to-field snippet target-field)) (t nil)))) -(defun yas/move-to-group (snippet group &optional dontmove) - "Update SNIPPET to move to group GROUP." - (let ((field (yas/group-primary-field group)) - (overlay (yas/snippet-active-field-overlay snippet))) - (unless dontmove - (goto-char (yas/field-start field))) - (setf (yas/snippet-active-group snippet) group) - (setf (yas/snippet-active-field-overlay snippet) - (yas/move-overlay-and-field overlay field - (yas/field-start field) - (yas/field-end field))))) +(defun yas/move-to-field (snippet field) + "Update SNIPPET to move to field FIELD." + (goto-char (overlay-end (car (yas/field-overlay-pair field)))) + (setf (yas/snippet-active-field snippet) field) + (let ((overlay (yas/snippet-active-field-overlay snippet))) + (if overlay + (move-overlay overlay + (overlay-end (car (yas/field-overlay-pair field))) + (overlay-start (cdr (yas/field-overlay-pair field)))) + (setf (yas/snippet-active-field-overlay snippet) + (make-overlay (overlay-end (car (yas/field-overlay-pair field))) + (overlay-start (cdr (yas/field-overlay-pair field))))) + (setq overlay (yas/snippet-active-field-overlay snippet)) + (overlay-put overlay 'face 'yas/field-highlight-face) + (overlay-put overlay 'modification-hooks '(yas/on-field-overlay-modification)) + (overlay-put overlay 'insert-in-front-hooks '(yas/on-field-overlay-modification)) + (overlay-put overlay 'insert-behind-hooks '(yas/on-field-overlay-modification))) + + (overlay-put overlay 'yas/field field))) -(defun yas/prev-field-group () - "Navigate to prev field group. If there's none, exit the snippet." +(defun yas/prev-field () + "Navigate to prev field. If there's none, exit the snippet." (interactive) - (yas/next-field-group -1)) + (yas/next-field -1)) (defun yas/exit-snippet (snippet) - "Goto exit-marker of SNIPPET and cleanup the snippet. Cleaning + "Goto exit-marker of SNIPPET and commit the snippet. Cleaning up the snippet does not delete it!" (interactive) - (goto-char (yas/snippet-exit-marker snippet)) - (yas/cleanup-snippet snippet)) + (let ((exit-marker (set-marker (make-marker) (if (markerp (yas/snippet-exit snippet)) + (yas/snippet-exit snippet) + (overlay-start (yas/snippet-exit snippet)))))) + (yas/commit-snippet snippet) + (goto-char exit-marker) + (set-marker exit-marker nil))) ;; Snippet register and unregister routines. ;; -;; XXX: Commentary on this section by joaot. -;; -;; These routines, along with minor modifications upwards, allow some -;; management of currently active snippets. -;; -;; The idea is to temporarily set `post-command-hook' while locally -;; "registered" snippets last. After each command, -;; `yas/check-cleanup-snippet' is run, checking for some condition and -;; possibly unregistering the snippet. When no more snippets are -;; registered, the `post-command-hook' is cleared up. -;; -;; They were introduced to fix bug 28 -;; "http://code.google.com/p/yasnippet/issues/detail?id=28". Whenever -;; point exits a snippet or a snippet field, *all* snippets are -;; destroyed. -;; -;; Also, this scheme have been reused to fix bug 33 -;; "http://code.google.com/p/yasnippet/issues/detail?id=33", which -;; deals with undoing changes when part of the snippet's field have -;; been filled out already. See commentary on "Field-level undo" below -;; - (defvar yas/registered-snippets nil "A hash table holding all active snippets") (eval-when-compile (make-variable-buffer-local 'yas/registered-snippets)) -(defvar yas/temporary-pre-command-hooks (list 'yas/clear-pending-undo-actions - 'yas/save-active-group-boundaries)) - -(defvar yas/temporary-post-command-hooks (list 'yas/check-cleanup-snippet - 'yas/push-pending-undo-actions - 'yas/debug-some-vars)) - - -(defun yas/add-remove-many-hooks (hook-var fn-list &optional remove) - (mapcar (if remove - #'(lambda (fn) (remove-hook hook-var fn 'local)) - #'(lambda (fn) (add-hook hook-var fn 'append 'local))) - fn-list)) - (defun yas/register-snippet (snippet) - "Register SNIPPET in the `yas/registered-snippets' table. Add a -`yas/check-cleanup-snippet' function to the buffer-local -`post-command-hook' that should exist while at least one -registered snippet exists in the current buffer. Return snippet" + "Register SNIPPET in the `yas/registered-snippets' table. Add +a `yas/pre-command-handler' function to the buffer-local +`pre-command-hook' and `yas/post-command-handler' to the +`post-command-hook'. This should exist while registered snippets +exists in the current buffer. Return snippet" + (unless yas/registered-snippets + (setq yas/registered-snippets (make-hash-table :test 'eq))) ;; ;; register the snippet ;; @@ -1402,15 +1163,14 @@ registered snippet exists in the current buffer. Return snippet" ;; ;; setup the `pre-command-hook' and `post-command-hook' ;; - (yas/add-remove-many-hooks 'pre-command-hook yas/temporary-pre-command-hooks) - (yas/add-remove-many-hooks 'post-command-hook yas/temporary-post-command-hooks) + (add-hook 'pre-command-hook 'yas/pre-command-handler) + (add-hook 'post-command-hook 'yas/post-command-handler) snippet) (defun yas/unregister-snippet (snippet) - "Unregister snippet from the `yas/registered-snippets' -table. Remove `yas/check-cleanup-snippet' from the buffer-local -`post-command-hook' if no more snippets registered in the -current buffer." + "Unregister snippet from the `yas/registered-snippets' table. +Remove the handlers registered in `yas/register-snippet' if no +more snippets registered in the current buffer." ;; ;; ;; @@ -1420,36 +1180,27 @@ current buffer." ;; (when (eq 0 (hash-table-count yas/registered-snippets)) - (yas/add-remove-many-hooks 'pre-command-hook yas/temporary-pre-command-hooks 'remove) - (yas/add-remove-many-hooks 'post-command-hook yas/temporary-post-command-hooks 'remove) - (when yas/pending-undo-actions - (add-hook 'post-command-hook 'yas/push-pending-undo-actions-once 'append 'local)))) + (remove-hook 'pre-command-hook 'yas/pre-command-handler) + (remove-hook 'post-command-hook 'yas/post-command-handler))) (defun yas/exterminate-snippets () "Remove all locally registered snippets and remove `yas/check-cleanup-snippet' from the `post-command-hook'" (interactive) - (setq yas/pending-undo-actions nil) - (setq buffer-undo-list nil) - (yas/cleanup-all-snippets)) + (when yas/registered-snippets + (maphash #'(lambda (key value) (yas/commit-snippet value)) yas/registered-snippets))) -(defun yas/cleanup-all-snippets () - (maphash #'(lambda (key snippet) - (when (yas/snippet-p snippet) (yas/cleanup-snippet snippet))) - yas/registered-snippets) - (unless (eq 0 (hash-table-count yas/registered-snippets)) - (setq yas/registered-snippets (make-hash-table :test 'eq)) - (message "Warning: yas/snippet hash-table not fully clean. Forcing NIL."))) +(defun yas/delete-overlay-region (overlay) + (delete-region (overlay-start overlay) (overlay-end overlay))) -(defun yas/cleanup-snippet (snippet &optional undo-action-method) - "Cleanup SNIPPET, but leave point as it is. This renders the +(defun yas/commit-snippet (snippet) + "Commit SNIPPET, but leave point as it is. This renders the snippet as ordinary text" (let* ((control-overlay (yas/snippet-control-overlay snippet)) - (field-overlay (yas/snippet-active-field-overlay snippet)) + (active-field-overlay (yas/snippet-active-field-overlay snippet)) yas/snippet-beg - yas/snippet-end - saved-groups-and-boundaries) + yas/snippet-end) ;; ;; Save the end of the moribund snippet in case we need to undo ;; its original expansion. This is used by `yas/undo-expand-snippet' @@ -1459,41 +1210,25 @@ snippet as ordinary text" (setq yas/snippet-beg (overlay-start control-overlay)) (setq yas/snippet-end (overlay-end control-overlay)) (delete-overlay control-overlay)) + + (when active-field-overlay + (delete-overlay active-field-overlay)) + + ;; Delete all the text under the overlays ;; - ;; Delete the currently active field overlay if any + (dolist (field (yas/snippet-fields snippet)) + (dolist (mirror (yas/field-mirrors field)) + (goto-char (overlay-start (yas/mirror-overlay mirror))) + (yas/delete-overlay-region (yas/mirror-overlay mirror)) + (insert (yas/apply-transform mirror field))) + (yas/delete-overlay-region (car (yas/field-overlay-pair field))) + (yas/delete-overlay-region (cdr (yas/field-overlay-pair field)))) + (if (overlayp (yas/snippet-exit snippet)) + (yas/delete-overlay-region (yas/snippet-exit snippet)) + (set-marker (yas/snippet-exit snippet) nil)) + + ;; TODO: Maybe action for snippet revival ;; - (when (and field-overlay - (overlay-buffer field-overlay)) - (delete-overlay field-overlay)) - ;; - ;; Iterate every group, and in it, every field. - ;; - (dolist (group (yas/snippet-groups snippet)) - (dolist (field (yas/group-fields group)) - (let ((start-marker (yas/field-start field)) - (end-marker (yas/field-end field))) - ;; - ;; convert markers into points, before losing the reference. - ;; - (when (markerp start-marker) - (setf (yas/field-start field) (marker-position start-marker)) - (set-marker start-marker nil)) - (when (markerp end-marker) - (setf (yas/field-end field) (marker-position end-marker)) - (set-marker end-marker nil))))) - ;; - ;; forget all other pending undo actions and push a undo/redo - ;; action for snippet revival - ;; - (unless (eq 'this-command 'yas/exterminate-snippets) - (setq yas/pending-undo-actions (list - (list (or undo-action-method 'before-first-action) - `(apply yas/revive-snippet - ,snippet - ,yas/snippet-beg - ,yas/snippet-end - ,(yas/snippet-active-group snippet)) - 'jump-first-separator)))) ;; ;; XXX: `yas/after-exit-snippet-hook' should be run with ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not @@ -1505,25 +1240,26 @@ snippet as ordinary text" (yas/unregister-snippet snippet)) -(defun yas/check-cleanup-snippet () +(defun yas/check-commit-snippet () "Checks if point exited the currently active field of the snippet, if so cleans up the whole snippet up. This function is part of `post-command-hook' while registered snippets last." (let* ((snippet (yas/snippet-of-current-keymap)) - (group (and snippet - (yas/snippet-active-group snippet)))) + (field (and snippet + (yas/snippet-active-field snippet)))) (cond (;; ;; No snippet at point, cleanup *all* snippets ;; (null snippet) - (yas/cleanup-all-snippets)) + ;; (yas/cleanup-all-snippets) +) (;; A snippet exits at point, but point left the currently ;; active field overlay - (or (not group) - (and group - (not (yas/point-in-field-p (yas/group-primary-field group))))) + (or (not field) + (and field + (not (yas/point-in-field-p (yas/field-primary-field field))))) (yas/cleanup-snippet snippet)) (;; ;; Snippet at point, and point inside a snippet field, @@ -1532,88 +1268,6 @@ registered snippets last." t nil)))) -;; -;; Undo functionality -;; - -(defvar yas/pending-undo-actions nil) -(eval-when-compile - (make-variable-buffer-local 'yas/pending-undo-actions)) - -(defun yas/clear-pending-undo-actions () - (setq yas/pending-undo-actions nil)) - -(defun yas/save-active-group-boundaries () - "While snippet is active, save the active group and the active -group's boundaries. - -Creates undo actions in `yas/pending-undo-actions' that will -eventually be pushed into the `buffer-undo-list' variable. This -function is intended to be placed in `pre-command-hook'. - -The actual pushing of actions into the `buffer-undo-list' is -performed in `yas/push-pending-undo-actions', which is placed in the -`post-command-hook'." - (let* ((snippet (yas/snippet-of-current-keymap)) - (group (yas/snippet-active-group snippet)) - (field-overlay (yas/snippet-active-field-overlay snippet))) - ;; - ;; Save boundaries of current field - ;; - (push (list 'after-first-action - `(apply yas/restore-group-boundaries - ,group - ,snippet - ,(overlay-start field-overlay) - ,(overlay-end field-overlay))) - yas/pending-undo-actions) - ;; - ;; Save a reference to current group - ;; - (push (list 'after-first-action - `(apply yas/restore-active-group ,group ,snippet)) - yas/pending-undo-actions))) - -(defun yas/revive-snippet (snippet snippet-start snippet-end active-group) - (let ((inhibit-modification-hooks t) - (buffer-undo-list t)) - ;; - ;; Revive the control overlay - ;; - (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay snippet-start snippet-end)) - ;; - ;; Revive each group - ;; - (dolist (group (yas/snippet-groups snippet)) - (yas/restore-group-boundaries group snippet - (yas/field-start (yas/group-primary-field group)) - (yas/field-end (yas/group-primary-field group)))) - ;; - ;; Move to the previously active group - ;; - (yas/move-to-group snippet active-group) - ;; - ;; Reregister this snippet - ;; - (yas/register-snippet snippet) - ;; - ;; Erase any pending undo actions. - ;; - (setq yas/pending-undo-actions nil))) - -(defun yas/restore-active-group (group snippet) - "..." - (let ((inhibit-modification-hooks t)) - (yas/move-to-group snippet group 'dontmove))) - -(defun yas/restore-group-boundaries (group snippet start end) - ",,," - (let* ((field-overlay (yas/snippet-active-field-overlay snippet)) - (field (yas/group-primary-field group)) - (inhibit-modification-hooks t)) - (setf (yas/snippet-active-field-overlay snippet) (yas/move-overlay-and-field field-overlay field start end)) - (yas/update-mirrors group))) - (defun yas/point-in-field-p (field &optional point) "..." (let ((point (or point @@ -1621,73 +1275,19 @@ performed in `yas/push-pending-undo-actions', which is placed in the (and (>= point (yas/field-start field)) (<= point (yas/field-end field))))) -(defun yas/push-pending-undo-actions () - (mapcar #'(lambda (args) - (apply #'yas/push-undo-action-maybe args)) - yas/pending-undo-actions)) - ;; -;; TODO: get rid of this "once" thing +;; Pre and post command handlers ;; -(defun yas/push-pending-undo-actions-once () - (yas/push-pending-undo-actions) - (remove-hook 'post-command-hook 'yas/push-pending-undo-actions-once 'local)) -(defun yas/push-undo-action-maybe (how entry &optional jump-first-separator) - "..." - (unless (eq t buffer-undo-list) - (cond (;; - ;; - ;; - (eq 'after-first-action how) - (let ((undo-list buffer-undo-list) - done) - (when (and jump-first-separator - (null (car undo-list))) - (setq undo-list (cdr undo-list))) - (while (not done) - (cond ((condition-case oops - (and (eq 'apply (first entry)) - (eq (second entry) - (second (car undo-list)))) - (error nil)) - (setq done 'return)) - ((null (cadr undo-list)) - (setq done 'try-insert)) - (t - (setq undo-list (cdr undo-list))))) - (unless (eq done 'return) - (push entry (cdr undo-list))))) - (;; - ;; - ;; - (eq 'before-first-action how) - (if (and jump-first-separator - (null (car buffer-undo-list))) - (push entry (cdr buffer-undo-list)) - (push entry buffer-undo-list)))))) +(defun yas/pre-command-handler () + ) -(defun yas/sanitize-undo-redo () - (let ((undo-list buffer-undo-list) - done) - (unless (eq t buffer-undo-list) - ;; - ;; Discard possibly existing/missing start separator - ;; - (when (null (car undo-list)) - (setq undo-list (cdr undo-list))) - (delete-if #'(lambda (elem) - (when (and (consp elem) - (integerp (cdr elem)) - (> (cdr elem) (point-max))) - (prog1 t - (message "Deleting %s in the undo-list (greater than point-max=%s)!!!" - elem (point-max))))) - undo-list - :end (position nil undo-list))))) +(defun yas/post-command-handler () + ) ;; Debug functions. Use (or change) at will whenever needed. + (defun yas/debug-some-vars () (interactive) (with-output-to-temp-buffer "*YASnippet trace*" @@ -1699,44 +1299,36 @@ performed in `yas/push-pending-undo-actions', which is placed in the (princ " No registered snippets\n")) (t (maphash #'(lambda (key snippet) - (princ (format "\t key %s for snippet %s" + (princ (format "\t key %s for snippet %s\n" key (yas/snippet-id snippet))) - (princ (format "\t Big overlay %s\n" + (princ (format "\t Control overlay %s\n" (yas/snippet-control-overlay snippet))) - (if (yas/snippet-active-field-overlay snippet) - (princ (format "\t Field overlay %s\n " - (yas/snippet-active-field-overlay snippet))) - (princ "No active field overlay!!\m")) + (dolist (field (yas/snippet-fields snippet)) + (princ (format "\t field %s with %s mirrors is %s and %s" + (yas/field-number field) + (length (yas/field-mirrors field)) + (if (yas/field-probably-deleted-p field) + "DELETED" + "alive") + (if (eq field (yas/snippet-active-field snippet)) + "ACTIVE!\n" + "NOT ACTIVE!\n"))) + (princ (format "\t\t Covering: %s\n" (yas/current-field-text field))) + (princ (format "\t\t Displays: %s\n" (yas/field-text-for-display field))) + ;; (dolist (mirror (yas/field-mirrors field)) + ;; (princ (format "\t\t Mirror displays: \n" + ;; (if (eq field (yas/field-primary-field field)) + ;; "Primary" "Mirror")))) +)) + yas/registered-snippets))) - (dolist (group (yas/snippet-groups snippet)) - ;; (princ (format "\t Group $%s with %s fields is %s and %s" -;; (yas/group-number group) -;; (length (yas/group-fields group)) -;; (if (yas/group-probably-deleted-p group) -;; "DELETED" -;; "alive") -;; (if (eq group (yas/snippet-active-group snippet)) -;; "LIVE!\n" -;; "SLEEPY!\n"))) -;; (dolist (field (yas/group-fields group)) -;; (princ (format "\t\t* %s field. Current value (%s) .\n" -;; (if (eq field (yas/group-primary-field group)) -;; "Primary" "Mirror") -;; (yas/current-field-text field))) -;; (princ (format "\t\t From %s to %s\n" -;; (yas/field-start field) -;; (yas/field-end field))) -;; ) -)) yas/registered-snippets))) - - (princ (format "\nPRE- command hook: %s\n" pre-command-hook)) - (princ (format "\nPOST- command hook: %s\n" post-command-hook)) - + (princ (format "\nPost command hook: %s\n" post-command-hook)) + (princ (format "\nPre command hook: %s\n" pre-command-hook)) (princ (format "\nUndo is %s and point-max is %s.\n" (if (eq buffer-undo-list t) @@ -1747,38 +1339,9 @@ performed in `yas/push-pending-undo-actions', which is placed in the (princ (format "Undolist has %s elements. First 10 elements follow:\n" (length buffer-undo-list))) (let ((first-ten (subseq buffer-undo-list 0 19))) (dolist (undo-elem first-ten) - (princ (format "%2s: %s\n" - (position undo-elem first-ten) - (cond ((null undo-elem) - "--- (separator) ---") - (t - (truncate-string-to-width (format "%s" undo-elem) 70)))))))))) + (princ (format "%2s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 70)))))))) -(defun yas/debug-pprint-group (group) - (cond ((yas/group-p group) - (format "[%sgrp $%s with %s flds %s]" - (if (eq group (yas/snippet-active-group (yas/group-snippet field-group))) - "LIVE " - "") - (yas/group-number group) - (length (yas/group-fields group)) - (mapconcat #'yas/debug-pprint-field (yas/group-fields group) " ")) - (t - "(not a group!")))) - -(defun yas/debug-pprint-field (field) - (cond ((yas/field-p field) - (format "[%s: (%s) %s->%s]" - (if (eq field (yas/group-primary-field (yas/field-group field))) - "primary" - "mirror") - (yas/current-field-text field) - (yas/field-start field) - (yas/field-end field))))) - - - (defun yas/exterminate-package () (interactive) (yas/minor-mode -1) @@ -1786,6 +1349,21 @@ performed in `yas/push-pending-undo-actions', which is placed in the (when (string-match "yas/" (symbol-name atom)) (unintern atom))))) +(defun yas/debug-test (&optional verbose) + (interactive "P") + (yas/load-directory "~/Source/yasnippet/snippets/") + ;;(kill-buffer (get-buffer "*YAS TEST*")) + (set-buffer (switch-to-buffer "*YAS TEST*")) + (yas/exterminate-snippets) + (erase-buffer) + (setq buffer-undo-list nil) + (insert "dov") + (html-mode) + (when verbose + (add-hook (make-local-variable 'post-command-hook) 'yas/debug-some-vars)) + (yas/expand)) + + (provide 'yasnippet) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; From 42f83457806c501a3e4584dcb456f31293658e32 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Wed, 1 Jul 2009 16:58:46 +0000 Subject: [PATCH 19/75] Trashed prior implementation of nested snippets and now going full steam on a much better one (I hope...) --- snippets/text-mode/html-mode/dov | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/snippets/text-mode/html-mode/dov b/snippets/text-mode/html-mode/dov index e2cfd03..e444aee 100644 --- a/snippets/text-mode/html-mode/dov +++ b/snippets/text-mode/html-mode/dov @@ -1,5 +1,8 @@ #name : ... # -- - + $0 + + actually some other shit and $3 + From 25c4e884ae9fea7a1d1d7369c364ebd5203d1091 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Wed, 1 Jul 2009 22:15:00 +0000 Subject: [PATCH 20/75] * lookin' good, next step: protecting the hidden overlays from modification somehow... --- yasnippet.el | 145 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 91 insertions(+), 54 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 1741132..2df9907 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -187,7 +187,7 @@ to expand. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Internal variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar yas/version "0.5.6") +(defvar yas/version "0.5.6-nested-placeholders") (defvar yas/snippet-tables (make-hash-table) "A hash table of snippet tables corresponding to each major-mode.") @@ -214,12 +214,17 @@ to expand. (defconst yas/escape-backquote (concat "YASESCAPE" "BACKQUOTE" "PROTECTGUARD")) -;; (defconst yas/field-regexp -;; (concat "$\\([0-9]+\\)" "\\|" -;; "${\\(?:\\([0-9]+\\):\\)?\\([^}]*\\)}")) (defconst yas/field-regexp - (concat "$\\([0-9]+\\)" "\\|" - "${\\(?:\\([0-9]+\\):\\)?\\(.*\\)}")) + "${\\([0-9]+:\\)?\\([^}]*\\)}" + "A regexp to *almost* recognize a field") + +(defconst yas/transform-mirror-regexp + "${\\(?:\\([0-9]+\\):\\)?$\\([^}]*\\)" + "A regexp to *almost* recognize a mirror with a transform") + +(defconst yas/simple-mirror-regexp + "$\\([0-9]+\\)" + "A regexp to recognize a simple mirror") (defvar yas/snippet-id-seed 0 "Contains the next id for a snippet.") @@ -303,7 +308,7 @@ set to t." (transform nil) (modified nil)) -(defstruct (yas/mirror (:constructor yas/make-mirror (overlay))) +(defstruct (yas/mirror (:constructor yas/make-mirror (overlay transform))) "A mirror." overlay (transform nil)) @@ -427,7 +432,12 @@ a list of modes like this to help the judgement." (defun yas/apply-transform (field-or-mirror field) "Calculate the value of the field. If there's a transform for this field, apply it. Otherwise, the value is returned -unmodified." +unmodified. + +TODO: I really dont think field transforms are easily done, but oh +well + +" (let ((text (yas/field-text-for-display field)) (transform (if (yas/mirror-p field-or-mirror) (yas/mirror-transform field-or-mirror) @@ -485,23 +495,22 @@ the template of a snippet in the current snippet-table." start end))) -(defun yas/field-text-for-display (field &optional field-number) +(defun yas/field-text-for-display (field) "Return the propertized display text for field FIELD. " + (let ((text (yas/current-field-text field))) (when text - (while (and (string-match yas/field-regexp text) - (match-beginning 3)) - (setq text - (concat - (substring text 0 (match-beginning 0)) - (if (and field-number - (match-beginning 2) - (= field-number - (string-to-number (substring text (match-beginning 2))))) - (propertize (substring text (match-beginning 3) (match-end 3)) 'face 'yas/field-highlight-face) - (substring text (match-beginning 3) (match-end 3))) - (substring text (match-end 0))))) - text))) + (while (string-match yas/field-regexp text) + (let ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1))) + (setq text + (concat + (substring text + 0 + (match-beginning 0)) + (substring text + (match-beginning 2) + (1- (length text)))))))) + text)) (defun yas/current-field-text (field) (buffer-substring-no-properties (yas/field-start field) @@ -529,8 +538,6 @@ the template of a snippet in the current snippet-table." (yas/mirror-update-display mirror field)))) yas/registered-snippets))) -(defun yas/on-hidden-overlay-modification (overlay after? beg end &optional length) - (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length) "To be written" @@ -592,7 +599,7 @@ will be deleted before inserting template." ;; `yas/registered-snippets' var. Create fields. (let ((snippet (yas/register-snippet (yas/make-snippet)))) (goto-char (point-min)) - (yas/field-parse-create snippet) + (yas/snippet-parse-create snippet) ;; Step XX: Sort and link each field (setf (yas/snippet-fields snippet) @@ -614,6 +621,7 @@ will be deleted before inserting template." (cons 'invisible t)) prop-list) (push (cons 'evaporate t) prop-list) + (push (cons 'read-only t) prop-list) ;; what i really wanted is read-only (dolist (prop prop-list) (dolist (field (yas/snippet-fields snippet)) (overlay-put (car (yas/field-overlay-pair field)) (car prop) (cdr prop)) @@ -659,37 +667,66 @@ will be deleted before inserting template." (replace-match "") (indent-according-to-mode))))))) -(defun yas/field-parse-create (snippet &optional parent-field) +(defun yas/snippet-parse-create (snippet) "Parse a recently inserted snippet template, creating all necessary fields. Allows nested placeholder in the style of Textmate." - (while (re-search-forward yas/field-regexp nil t) - (let ((number (or (match-string-no-properties 1) - (match-string-no-properties 2)))) - (cond ((and number - (string= "0" number)) - (setf (yas/snippet-exit snippet) - (make-overlay (match-beginning 0) (match-end 0)))) - ((match-beginning 3) - (let ((brand-new-field (yas/make-field (and number (string-to-number number)) - (cons (make-overlay (match-beginning 0) - (match-beginning 3)) - (make-overlay (match-end 3) - (match-end 0))) - parent-field))) - (push brand-new-field (yas/snippet-fields snippet)) - (save-excursion - (save-restriction - (narrow-to-region (match-beginning 3) (match-end 3)) - (goto-char (point-min)) - (yas/field-parse-create snippet brand-new-field))))) - (t - (let ((field (yas/snippet-find-field snippet (and number (string-to-number number))))) - (when field - (push (yas/make-mirror (make-overlay (match-beginning 0) - (match-end 0))) - (yas/field-mirrors field))))))))) + (let ((parse-start (point))) + (yas/field-parse-create snippet) + (goto-char parse-start) + (yas/transform-mirror-parse-create snippet) + (goto-char parse-start) + (yas/simple-mirror-parse-create snippet))) + +(defun yas/field-parse-create (snippet &optional parent-field) + (while (re-search-forward yas/field-regexp nil t) + (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) + (number (string-to-number (match-string-no-properties 1))) + (brand-new-field (and real-match-end-0 + (save-match-data (not (string-match "$(" (match-string-no-properties 2)))) + number + (not (zerop number)) + (yas/make-field number + (cons (make-overlay (match-beginning 0) + (match-beginning 2)) + (make-overlay (1- real-match-end-0) + real-match-end-0)) + parent-field)))) + (when brand-new-field + (push brand-new-field (yas/snippet-fields snippet)) + (save-excursion + (save-restriction + (narrow-to-region (match-beginning 2) (1- real-match-end-0)) + (goto-char (point-min)) + (yas/field-parse-create snippet brand-new-field))))))) + +(defun yas/transform-mirror-parse-create (snippet) + (while (re-search-forward yas/transform-mirror-regexp nil t) + (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) + (number (string-to-number (match-string-no-properties 1))) + (field (and number + (not (zerop number)) + (yas/snippet-find-field snippet number)))) + (when (and real-match-end-0 field) + (push (yas/make-mirror (make-overlay (match-beginning 0) + real-match-end-0) + (buffer-substring-no-properties (match-beginning 2) + (1- real-match-end-0))) + (yas/field-mirrors field)))))) + +(defun yas/simple-mirror-parse-create (snippet) + (while (re-search-forward yas/simple-mirror-regexp nil t) + (let ((number (string-to-number (match-string-no-properties 1)))) + (if (zerop number) + (setf (yas/snippet-exit snippet) + (make-overlay (match-beginning 0) (match-end 0))) + (let ((field (yas/snippet-find-field snippet number))) + (when field + (push (yas/make-mirror (make-overlay (match-beginning 0) + (match-end 0)) + nil) + (yas/field-mirrors field)))))))) (defun yas/mirror-update-display (mirror field) (overlay-put (yas/mirror-overlay mirror) 'after-string (yas/apply-transform mirror field))) @@ -1357,8 +1394,8 @@ registered snippets last." (yas/exterminate-snippets) (erase-buffer) (setq buffer-undo-list nil) - (insert "dov") - (html-mode) + (insert "prop") + (objc-mode) (when verbose (add-hook (make-local-variable 'post-command-hook) 'yas/debug-some-vars)) (yas/expand)) From 333d48d7316cdf1713097685f80d8a23ca81b723 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Thu, 2 Jul 2009 09:59:12 +0000 Subject: [PATCH 21/75] Going OK, next step is to handler the feared undo/redo! --- yasnippet.el | 61 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 2df9907..01f4dbf 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -495,12 +495,14 @@ the template of a snippet in the current snippet-table." start end))) +;; "${\\(?:\\([0-9]+\\):\\)?$\\([^}]*\\)" + (defun yas/field-text-for-display (field) "Return the propertized display text for field FIELD. " (let ((text (yas/current-field-text field))) (when text - (while (string-match yas/field-regexp text) + (while (string-match "${\\([0-9]+:\\)?\\(.*\\)}.*" text) (let ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1))) (setq text (concat @@ -509,7 +511,10 @@ the template of a snippet in the current snippet-table." (match-beginning 0)) (substring text (match-beginning 2) - (1- (length text)))))))) + (match-end 2)) + (substring text + (1+ (match-end 2)) + (match-end 0))))))) text)) (defun yas/current-field-text (field) @@ -538,6 +543,13 @@ the template of a snippet in the current snippet-table." (yas/mirror-update-display mirror field)))) yas/registered-snippets))) +(defun yas/on-hidden-overlay-modification (overlay after? beg end &optional length) + (unless (or after? + (null (overlay-buffer overlay))) + (save-excursion + (yas/exit-snippet (overlay-get overlay 'yas/snippet))) + (call-interactively this-command) + (signal 'shit '("Aborted my friend")))) (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length) "To be written" @@ -621,7 +633,8 @@ will be deleted before inserting template." (cons 'invisible t)) prop-list) (push (cons 'evaporate t) prop-list) - (push (cons 'read-only t) prop-list) ;; what i really wanted is read-only + (push (cons 'yas/snippet snippet) prop-list) + (push (cons 'modification-hooks '(yas/on-hidden-overlay-modification)) prop-list) ;; what i really wanted is 'read-only (dolist (prop prop-list) (dolist (field (yas/snippet-fields snippet)) (overlay-put (car (yas/field-overlay-pair field)) (car prop) (cdr prop)) @@ -1151,6 +1164,7 @@ when the condition evaluated to non-nil." (move-overlay overlay (overlay-end (car (yas/field-overlay-pair field))) (overlay-start (cdr (yas/field-overlay-pair field)))) + ;; create a new overlay (setf (yas/snippet-active-field-overlay snippet) (make-overlay (overlay-end (car (yas/field-overlay-pair field))) (overlay-start (cdr (yas/field-overlay-pair field))))) @@ -1159,7 +1173,6 @@ when the condition evaluated to non-nil." (overlay-put overlay 'modification-hooks '(yas/on-field-overlay-modification)) (overlay-put overlay 'insert-in-front-hooks '(yas/on-field-overlay-modification)) (overlay-put overlay 'insert-behind-hooks '(yas/on-field-overlay-modification))) - (overlay-put overlay 'yas/field field))) (defun yas/prev-field () @@ -1248,21 +1261,33 @@ snippet as ordinary text" (setq yas/snippet-end (overlay-end control-overlay)) (delete-overlay control-overlay)) - (when active-field-overlay - (delete-overlay active-field-overlay)) + (let ((inhibit-modification-hooks t)) + (when active-field-overlay + (delete-overlay active-field-overlay)) - ;; Delete all the text under the overlays - ;; - (dolist (field (yas/snippet-fields snippet)) - (dolist (mirror (yas/field-mirrors field)) - (goto-char (overlay-start (yas/mirror-overlay mirror))) - (yas/delete-overlay-region (yas/mirror-overlay mirror)) - (insert (yas/apply-transform mirror field))) - (yas/delete-overlay-region (car (yas/field-overlay-pair field))) - (yas/delete-overlay-region (cdr (yas/field-overlay-pair field)))) - (if (overlayp (yas/snippet-exit snippet)) - (yas/delete-overlay-region (yas/snippet-exit snippet)) - (set-marker (yas/snippet-exit snippet) nil)) + ;; Delete all the text under the overlays + ;; + (dolist (field (yas/snippet-fields snippet)) + (dolist (mirror (yas/field-mirrors field)) + (let ((mirror-overlay (yas/mirror-overlay mirror))) + (when (and mirror-overlay + (overlay-buffer mirror-overlay)) + (goto-char (overlay-start mirror-overlay)) + (yas/delete-overlay-region mirror-overlay) + (insert (yas/apply-transform mirror field))))) + (let* ((overlay-pair (yas/field-overlay-pair field)) + (before (car overlay-pair)) + (after (cdr overlay-pair))) + (dolist (ov (list before after)) + (when (and ov + (overlay-buffer ov)) + (yas/delete-overlay-region ov))))) + ;; Take care of the exit marker + ;; + (if (and (overlayp (yas/snippet-exit snippet)) + (overlay-buffer (yas/snippet-exit snippet))) + (yas/delete-overlay-region (yas/snippet-exit snippet)) + (set-marker (yas/snippet-exit snippet) nil))) ;; TODO: Maybe action for snippet revival ;; From f790e77e7c0ffda87289d3f15cf4fdc22299ff8a Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Thu, 2 Jul 2009 12:51:08 +0000 Subject: [PATCH 22/75] rudimentary undo, very buggy, but this is the way to go --- yasnippet.el | 234 ++++++++++++++++++++++++--------------------------- 1 file changed, 109 insertions(+), 125 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 01f4dbf..123fe30 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -96,6 +96,11 @@ mode will be listed under the menu \"yasnippet\".") (t (:background "DimGrey"))) "The face used to highlight the currently active field of a snippet") +(defface yas/mirror-highlight-face + '((((class color) (background light)) (:background "Dodgerblue")) + (t (:background "DimGrey"))) + "The face used to highlight a mirror of a snippet") + (defface yas/field-debug-face '((((class color) (background light)) (:background "tomato")) (t (:background "tomato"))) @@ -286,6 +291,9 @@ set to t." name condition) +(defvar yas/active-field-overlay nil + "Overlays the currently active field") + (defstruct (yas/snippet (:constructor yas/make-snippet ())) "A snippet. @@ -294,7 +302,6 @@ set to t." (exit nil) (id (yas/snippet-next-id) :read-only t) (control-overlay nil) - (active-field-overlay nil) (active-field nil)) (defstruct (yas/field (:constructor yas/make-field (number overlay-pair parent-field))) @@ -503,18 +510,17 @@ the template of a snippet in the current snippet-table." (let ((text (yas/current-field-text field))) (when text (while (string-match "${\\([0-9]+:\\)?\\(.*\\)}.*" text) - (let ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1))) - (setq text - (concat - (substring text - 0 - (match-beginning 0)) - (substring text - (match-beginning 2) - (match-end 2)) - (substring text - (1+ (match-end 2)) - (match-end 0))))))) + (setq text + (concat + (substring text + 0 + (match-beginning 0)) + (substring text + (match-beginning 2) + (match-end 2)) + (substring text + (1+ (match-end 2)) + (match-end 0)))))) text)) (defun yas/current-field-text (field) @@ -566,9 +572,6 @@ will be deleted before inserting template." (goto-char start) (let* ((key (buffer-substring-no-properties start end)) - (original-undo-list buffer-undo-list) ;; save previous undo information - (buffer-undo-list t) - (inhibit-modification-hooks t) (length (- end start)) (column (current-column))) (save-restriction @@ -588,97 +591,77 @@ will be deleted before inserting template." (= (current-column) 0)) (insert indent)))) - ;; Step XX: protect backslash and backquote - (yas/replace-all "\\\\" yas/escape-backslash) - (yas/replace-all "\\`" yas/escape-backquote) + (let ((template-beg (point-min)) + (template-end (point-max))) + (widen) + (goto-char template-end) + (delete-char length) + (let ((snippet (yas/snippet-create template-beg template-end))) + (save-excursion + ;; Do more indenting + (goto-char (overlay-start (yas/snippet-control-overlay snippet))) + (while (re-search-forward "$>" nil t) + (replace-match "") + (indent-according-to-mode)))))))) - ;; Step XX: evaluate all backquotes +(defun yas/snippet-create (begin end) + (narrow-to-region begin end) + ;; Create and register a brand new snippet in the local + ;; `yas/registered-snippets' var. Create fields. + (let ((snippet (yas/register-snippet (yas/make-snippet)))) (goto-char (point-min)) - (while (re-search-forward "`\\([^`]*\\)`" nil t) - ;; go back so that (current-column) in elisp code evaluation - ;; will calculate to a meaningful value - (goto-char (match-beginning 0)) - (replace-match (yas/eval-string (match-string-no-properties 1)) - t t)) + (yas/snippet-parse-create snippet) - ;; Step XX: protect all escapes, including backslash and backquot - ;; which may be produced in Step 3 - (yas/replace-all "\\\\" yas/escape-backslash) - (yas/replace-all "\\`" yas/escape-backquote) - (yas/replace-all "\\$" yas/escape-dollar) - - ;; Step XX: Create and register a brand new snippet in the local - ;; `yas/registered-snippets' var. Create fields. - (let ((snippet (yas/register-snippet (yas/make-snippet)))) - (goto-char (point-min)) - (yas/snippet-parse-create snippet) - - ;; Step XX: Sort and link each field - (setf (yas/snippet-fields snippet) - (sort (yas/snippet-fields snippet) - '(lambda (field1 field2) - (yas/snippet-field-compare field1 field2)))) + ;; Sort and link each field + (setf (yas/snippet-fields snippet) + (sort (yas/snippet-fields snippet) + '(lambda (field1 field2) + (yas/snippet-field-compare field1 field2)))) - (let ((prev nil)) - (dolist (field (yas/snippet-fields snippet)) - (setf (yas/field-prev field) prev) - (when prev - (setf (yas/field-next prev) field)) - (setq prev field))) + (let ((prev nil)) + (dolist (field (yas/snippet-fields snippet)) + (setf (yas/field-prev field) prev) + (when prev + (setf (yas/field-next prev) field)) + (setq prev field))) - ;; Step XX: Hide (or highlight for debugging) all hidden overlays - (let ((prop-list)) - (push (if (member 'yas/debug-some-vars post-command-hook) - (cons 'face 'yas/field-debug-face) - (cons 'invisible t)) - prop-list) - (push (cons 'evaporate t) prop-list) - (push (cons 'yas/snippet snippet) prop-list) - (push (cons 'modification-hooks '(yas/on-hidden-overlay-modification)) prop-list) ;; what i really wanted is 'read-only - (dolist (prop prop-list) - (dolist (field (yas/snippet-fields snippet)) - (overlay-put (car (yas/field-overlay-pair field)) (car prop) (cdr prop)) - (overlay-put (cdr (yas/field-overlay-pair field)) (car prop) (cdr prop)) - (dolist (mirror (yas/field-mirrors field)) - (overlay-put (yas/mirror-overlay mirror) (car prop) (cdr prop)) - (yas/mirror-update-display mirror field))) - (when (overlayp (yas/snippet-exit snippet)) - (overlay-put (yas/snippet-exit snippet) (car prop) (cdr prop))))) + ;; Hide (or highlight for debugging) all hidden overlays + (let ((prop-list)) + (push (if (member 'yas/debug-some-vars post-command-hook) + (cons 'face 'yas/field-debug-face) + (cons 'invisible t)) + prop-list) + (push (cons 'evaporate t) prop-list) + (push (cons 'yas/snippet snippet) prop-list) + (push (cons 'modification-hooks '(yas/on-hidden-overlay-modification)) prop-list) ;; what i really wanted is 'read-only + (dolist (prop prop-list) + (dolist (field (yas/snippet-fields snippet)) + (overlay-put (car (yas/field-overlay-pair field)) (car prop) (cdr prop)) + (overlay-put (cdr (yas/field-overlay-pair field)) (car prop) (cdr prop)) + (dolist (mirror (yas/field-mirrors field)) + (overlay-put (yas/mirror-overlay mirror) (car prop) (cdr prop)) + (yas/mirror-update-display mirror field))) + (when (overlayp (yas/snippet-exit snippet)) + (overlay-put (yas/snippet-exit snippet) (car prop) (cdr prop))))) - ;; Step XX: Create keymap overlay for snippet - (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay (point-min) (point-max))) - - ;; Step XX: restore all escape characters - (yas/replace-all yas/escape-dollar "$") - (yas/replace-all yas/escape-backquote "`") - (yas/replace-all yas/escape-backslash "\\") - - ;; Step XX: move to end and make sure exit-marker exist - (goto-char (point-max)) - (unless (yas/snippet-exit snippet) - (setf (yas/snippet-exit snippet) (copy-marker (point) t))) - - ;; Step XX: remove the trigger key - (widen) - (delete-char length) - - ;; Step XX: place the cursor at a proper place - (let* ((first-field (car (yas/snippet-fields snippet))) - overlay) - (cond (first-field - ;; Step XX: Move to the new field, setting up - ;; properties of the wandering active field overlay. - (yas/move-to-field snippet first-field)) - (t - ;; no need to call exit-snippet, since no overlay created. - (yas/exit-snippet snippet)))) - - ;; Step XX: Do necessary indenting - (save-excursion - (goto-char (overlay-start (yas/snippet-control-overlay snippet))) - (while (re-search-forward "$>" nil t) - (replace-match "") - (indent-according-to-mode))))))) + ;; Create keymap overlay for snippet + (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay (point-min) (point-max))) + ;; Step XX: move to end and make sure exit-marker exist + (goto-char (point-max)) + (unless (yas/snippet-exit snippet) + (setf (yas/snippet-exit snippet) (copy-marker (point) t))) + ;; Step XX: place the cursor at a proper place + (let* ((first-field (car (yas/snippet-fields snippet))) + overlay) + (cond (first-field + ;; Step XX: Move to the new field, setting up + ;; properties of the wandering active field overlay. + (yas/move-to-field snippet first-field)) + (t + ;; no need to call exit-snippet, since no overlay created. + (yas/exit-snippet snippet)))) + (widen) + snippet)) (defun yas/snippet-parse-create (snippet) "Parse a recently inserted snippet template, creating all @@ -742,7 +725,8 @@ Allows nested placeholder in the style of Textmate." (yas/field-mirrors field)))))))) (defun yas/mirror-update-display (mirror field) - (overlay-put (yas/mirror-overlay mirror) 'after-string (yas/apply-transform mirror field))) + (overlay-put (yas/mirror-overlay mirror) 'after-string + (propertize (yas/apply-transform mirror field) 'face 'yas/mirror-highlight-face))) (defun yas/snippet-of-current-keymap (&optional point) "Return the most recently inserted snippet covering POINT." @@ -1159,21 +1143,20 @@ when the condition evaluated to non-nil." "Update SNIPPET to move to field FIELD." (goto-char (overlay-end (car (yas/field-overlay-pair field)))) (setf (yas/snippet-active-field snippet) field) - (let ((overlay (yas/snippet-active-field-overlay snippet))) - (if overlay - (move-overlay overlay - (overlay-end (car (yas/field-overlay-pair field))) - (overlay-start (cdr (yas/field-overlay-pair field)))) + (if (and yas/active-field-overlay + (overlay-buffer yas/active-field-overlay)) + (move-overlay yas/active-field-overlay + (overlay-end (car (yas/field-overlay-pair field))) + (overlay-start (cdr (yas/field-overlay-pair field)))) ;; create a new overlay - (setf (yas/snippet-active-field-overlay snippet) - (make-overlay (overlay-end (car (yas/field-overlay-pair field))) - (overlay-start (cdr (yas/field-overlay-pair field))))) - (setq overlay (yas/snippet-active-field-overlay snippet)) - (overlay-put overlay 'face 'yas/field-highlight-face) - (overlay-put overlay 'modification-hooks '(yas/on-field-overlay-modification)) - (overlay-put overlay 'insert-in-front-hooks '(yas/on-field-overlay-modification)) - (overlay-put overlay 'insert-behind-hooks '(yas/on-field-overlay-modification))) - (overlay-put overlay 'yas/field field))) + (setq yas/active-field-overlay + (make-overlay (overlay-end (car (yas/field-overlay-pair field))) + (overlay-start (cdr (yas/field-overlay-pair field))))) + (overlay-put yas/active-field-overlay 'face 'yas/field-highlight-face) + (overlay-put yas/active-field-overlay 'modification-hooks '(yas/on-field-overlay-modification)) + (overlay-put yas/active-field-overlay 'insert-in-front-hooks '(yas/on-field-overlay-modification)) + (overlay-put yas/active-field-overlay 'insert-behind-hooks '(yas/on-field-overlay-modification))) + (overlay-put yas/active-field-overlay 'yas/field field)) (defun yas/prev-field () "Navigate to prev field. If there's none, exit the snippet." @@ -1248,7 +1231,6 @@ more snippets registered in the current buffer." "Commit SNIPPET, but leave point as it is. This renders the snippet as ordinary text" (let* ((control-overlay (yas/snippet-control-overlay snippet)) - (active-field-overlay (yas/snippet-active-field-overlay snippet)) yas/snippet-beg yas/snippet-end) ;; @@ -1261,12 +1243,17 @@ snippet as ordinary text" (setq yas/snippet-end (overlay-end control-overlay)) (delete-overlay control-overlay)) - (let ((inhibit-modification-hooks t)) - (when active-field-overlay - (delete-overlay active-field-overlay)) + ;; TODO: Maybe action for snippet revival + ;; + (push `(apply yas/snippet-create ,yas/snippet-beg ,yas/snippet-end) + buffer-undo-list) + ;; Trash those overlays! + ;; + (let ((inhibit-modification-hooks t)) + (when yas/active-field-overlay + (delete-overlay yas/active-field-overlay)) ;; Delete all the text under the overlays - ;; (dolist (field (yas/snippet-fields snippet)) (dolist (mirror (yas/field-mirrors field)) (let ((mirror-overlay (yas/mirror-overlay mirror))) @@ -1289,9 +1276,6 @@ snippet as ordinary text" (yas/delete-overlay-region (yas/snippet-exit snippet)) (set-marker (yas/snippet-exit snippet) nil))) - ;; TODO: Maybe action for snippet revival - ;; - ;; ;; XXX: `yas/after-exit-snippet-hook' should be run with ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not ;; be the case if the main overlay had somehow already @@ -1419,7 +1403,7 @@ registered snippets last." (yas/exterminate-snippets) (erase-buffer) (setq buffer-undo-list nil) - (insert "prop") + (insert "prip") (objc-mode) (when verbose (add-hook (make-local-variable 'post-command-hook) 'yas/debug-some-vars)) From 3f9b630d951f10f33ad0e7800c9d3f004c911fae Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Thu, 2 Jul 2009 21:56:23 +0000 Subject: [PATCH 23/75] * Much better way of calculating a fields visible value, but some subtle bugs. * TODO: exit snippet if the yas/active-field-overlay is exited * TODO: kill all field text on first keystroke * TODO: handle border cases with empty/near empty active field overlays * TODO: handle escapes * TODO: handle nested snippets --- yasnippet.el | 85 ++++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 46 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 123fe30..f792d37 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -502,25 +502,21 @@ the template of a snippet in the current snippet-table." start end))) -;; "${\\(?:\\([0-9]+\\):\\)?$\\([^}]*\\)" - (defun yas/field-text-for-display (field) "Return the propertized display text for field FIELD. " - - (let ((text (yas/current-field-text field))) - (when text - (while (string-match "${\\([0-9]+:\\)?\\(.*\\)}.*" text) - (setq text - (concat - (substring text - 0 - (match-beginning 0)) - (substring text - (match-beginning 2) - (match-end 2)) - (substring text - (1+ (match-end 2)) - (match-end 0)))))) + (let ((hidden-overlays (remove-if-not #'(lambda (ov) + (overlay-get ov 'yas/hidden)) + (overlays-in (yas/field-start field) (yas/field-end field)))) + (text)) + (when hidden-overlays + (reduce #'(lambda (ov1 ov2) + (setq text (concat text + (buffer-substring (overlay-end ov1) (overlay-start ov2)) + (overlay-get ov1 'after-string))) + ov2) + (sort hidden-overlays + #'(lambda (ov1 ov2) + (> (overlay-start ov2) (overlay-start ov1)))))) text)) (defun yas/current-field-text (field) @@ -544,18 +540,17 @@ the template of a snippet in the current snippet-table." (when (and after? yas/registered-snippets) (maphash #'(lambda (key snippet) - (dolist (field (yas/snippet-fields snippet)) - (dolist (mirror (yas/field-mirrors field)) - (yas/mirror-update-display mirror field)))) + (yas/update-mirrors snippet)) yas/registered-snippets))) +(add-to-list 'debug-ignored-errors "^Exit the snippet first$") (defun yas/on-hidden-overlay-modification (overlay after? beg end &optional length) (unless (or after? (null (overlay-buffer overlay))) - (save-excursion - (yas/exit-snippet (overlay-get overlay 'yas/snippet))) - (call-interactively this-command) - (signal 'shit '("Aborted my friend")))) + ;; (save-excursion + ;; (yas/exit-snippet (overlay-get overlay 'yas/snippet))) + ;; (call-interactively this-command) + (error "Exit the snippet first"))) (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length) "To be written" @@ -632,6 +627,7 @@ will be deleted before inserting template." (cons 'invisible t)) prop-list) (push (cons 'evaporate t) prop-list) + (push (cons 'yas/hidden t) prop-list) (push (cons 'yas/snippet snippet) prop-list) (push (cons 'modification-hooks '(yas/on-hidden-overlay-modification)) prop-list) ;; what i really wanted is 'read-only (dolist (prop prop-list) @@ -639,11 +635,13 @@ will be deleted before inserting template." (overlay-put (car (yas/field-overlay-pair field)) (car prop) (cdr prop)) (overlay-put (cdr (yas/field-overlay-pair field)) (car prop) (cdr prop)) (dolist (mirror (yas/field-mirrors field)) - (overlay-put (yas/mirror-overlay mirror) (car prop) (cdr prop)) - (yas/mirror-update-display mirror field))) + (overlay-put (yas/mirror-overlay mirror) (car prop) (cdr prop)))) (when (overlayp (yas/snippet-exit snippet)) (overlay-put (yas/snippet-exit snippet) (car prop) (cdr prop))))) + ;; Update the mirrors + (yas/update-mirrors snippet) + ;; Create keymap overlay for snippet (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay (point-min) (point-max))) ;; Step XX: move to end and make sure exit-marker exist @@ -719,29 +717,20 @@ Allows nested placeholder in the style of Textmate." (make-overlay (match-beginning 0) (match-end 0))) (let ((field (yas/snippet-find-field snippet number))) (when field - (push (yas/make-mirror (make-overlay (match-beginning 0) - (match-end 0)) - nil) - (yas/field-mirrors field)))))))) + (let ((ov (make-overlay (match-beginning 0) + (match-end 0)))) + (overlay-put ov 'yas/mirrorp t) + (push (yas/make-mirror ov nil) + (yas/field-mirrors field))))))))) + +(defun yas/update-mirrors (snippet) + (dolist (field (yas/snippet-fields snippet)) + (dolist (mirror (yas/field-mirrors field)) + (yas/mirror-update-display mirror field)))) (defun yas/mirror-update-display (mirror field) (overlay-put (yas/mirror-overlay mirror) 'after-string (propertize (yas/apply-transform mirror field) 'face 'yas/mirror-highlight-face))) - -(defun yas/snippet-of-current-keymap (&optional point) - "Return the most recently inserted snippet covering POINT." - (let ((point (or point (point))) - (keymap-snippet nil) - (snippet nil)) - (dolist (overlay (overlays-at point)) - (setq snippet (overlay-get overlay 'yas/snippet-reference)) - (when snippet - (if (null keymap-snippet) - (setq keymap-snippet snippet) - (when (> (yas/snippet-id snippet) - (yas/snippet-id keymap-snippet)) - (setq keymap-snippet snippet))))) - keymap-snippet)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Template-related and snippet loading functions @@ -1403,7 +1392,11 @@ registered snippets last." (yas/exterminate-snippets) (erase-buffer) (setq buffer-undo-list nil) - (insert "prip") + (let ((abbrev)) + (if (require 'ido nil t) + (setq abbrev (ido-completing-read "Snippet abbrev: " '("crazy" "prop"))) + (setq abbrev "crazy")) + (insert abbrev)) (objc-mode) (when verbose (add-hook (make-local-variable 'post-command-hook) 'yas/debug-some-vars)) From 09323108935b04a9ec3bda5ea95c4ef84408b9d0 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Fri, 3 Jul 2009 15:04:49 +0000 Subject: [PATCH 24/75] snippet revival working relatively OK, but need to work on undoing an expansion --- yasnippet.el | 267 +++++++++++++++++++++++++++++---------------------- 1 file changed, 150 insertions(+), 117 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index f792d37..a396b20 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -294,6 +294,8 @@ set to t." (defvar yas/active-field-overlay nil "Overlays the currently active field") +(make-variable-buffer-local 'yas/active-field-overlay) + (defstruct (yas/snippet (:constructor yas/make-snippet ())) "A snippet. @@ -301,8 +303,7 @@ set to t." (fields '()) (exit nil) (id (yas/snippet-next-id) :read-only t) - (control-overlay nil) - (active-field nil)) + (control-overlay nil)) (defstruct (yas/field (:constructor yas/make-field (number overlay-pair parent-field))) "A field." @@ -313,7 +314,7 @@ set to t." (next nil) (prev nil) (transform nil) - (modified nil)) + (modified-p nil)) (defstruct (yas/mirror (:constructor yas/make-mirror (overlay transform))) "A mirror." @@ -502,11 +503,18 @@ the template of a snippet in the current snippet-table." start end))) +(defun yas/hidden-overlays-in (beg end) + "A sorted list of hidden yas overlays overlapping the region + between BEG and END" + (sort (remove-if-not #'(lambda (ov) + (overlay-get ov 'yas/hidden)) + (overlays-in beg end)) + #'(lambda (ov1 ov2) + (> (overlay-start ov2) (overlay-start ov1))))) + (defun yas/field-text-for-display (field) "Return the propertized display text for field FIELD. " - (let ((hidden-overlays (remove-if-not #'(lambda (ov) - (overlay-get ov 'yas/hidden)) - (overlays-in (yas/field-start field) (yas/field-end field)))) + (let ((hidden-overlays (yas/hidden-overlays-in (yas/field-start field) (yas/field-end field))) (text)) (when hidden-overlays (reduce #'(lambda (ov1 ov2) @@ -514,9 +522,7 @@ the template of a snippet in the current snippet-table." (buffer-substring (overlay-end ov1) (overlay-start ov2)) (overlay-get ov1 'after-string))) ov2) - (sort hidden-overlays - #'(lambda (ov1 ov2) - (> (overlay-start ov2) (overlay-start ov1)))))) + hidden-overlays)) text)) (defun yas/current-field-text (field) @@ -532,25 +538,39 @@ the template of a snippet in the current snippet-table." nil t))) (overlay-put overlay 'keymap yas/keymap) - (overlay-put overlay 'yas/snippet-reference snippet) + (overlay-put overlay 'yas/snippet snippet) overlay)) (defun yas/on-field-overlay-modification (overlay after? beg end &optional length) "To be written" - (when (and after? - yas/registered-snippets) - (maphash #'(lambda (key snippet) - (yas/update-mirrors snippet)) - yas/registered-snippets))) + (unless undo-in-progress + (cond ((and after? + yas/registered-snippets) + (maphash #'(lambda (key snippet) + (yas/update-mirrors snippet)) + yas/registered-snippets)) + ((not after?) + (let ((field (overlay-get overlay 'yas/field))) + (unless (yas/field-modified-p field) + (let ((inhibit-modification-hooks t)) + (reduce #'(lambda (ov1 ov2) + (delete-region (overlay-end ov1) (overlay-start ov2)) + ov2) + (yas/hidden-overlays-in (yas/field-start field) (yas/field-end field)))) + (setf (yas/field-modified-p field) t)))) + (t + nil)))) (add-to-list 'debug-ignored-errors "^Exit the snippet first$") (defun yas/on-hidden-overlay-modification (overlay after? beg end &optional length) - (unless (or after? - (null (overlay-buffer overlay))) - ;; (save-excursion - ;; (yas/exit-snippet (overlay-get overlay 'yas/snippet))) - ;; (call-interactively this-command) - (error "Exit the snippet first"))) + (unless undo-in-progress + (unless (or after? + (null (overlay-buffer overlay))) + ;; (save-excursion + ;; (yas/exit-snippet (overlay-get overlay 'yas/snippet))) + ;; (call-interactively this-command) + (goto-char beg) + (error "Exit the snippet first")))) (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length) "To be written" @@ -572,7 +592,6 @@ will be deleted before inserting template." (save-restriction (narrow-to-region start start) - ;; (setq buffer-undo-list t) ;; disable undo for a short while (insert template) ;; Step XX: do necessary indent @@ -644,19 +663,19 @@ will be deleted before inserting template." ;; Create keymap overlay for snippet (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay (point-min) (point-max))) - ;; Step XX: move to end and make sure exit-marker exist + + ;; Move to end (goto-char (point-max)) - (unless (yas/snippet-exit snippet) - (setf (yas/snippet-exit snippet) (copy-marker (point) t))) - ;; Step XX: place the cursor at a proper place + + ;; Place the cursor at a proper place (let* ((first-field (car (yas/snippet-fields snippet))) overlay) (cond (first-field - ;; Step XX: Move to the new field, setting up - ;; properties of the wandering active field overlay. + ;; Move to the new field, setting up properties of the + ;; wandering active field overlay. (yas/move-to-field snippet first-field)) (t - ;; no need to call exit-snippet, since no overlay created. + ;; No fields, quite a simple snippet I suppose (yas/exit-snippet snippet)))) (widen) snippet)) @@ -683,9 +702,9 @@ Allows nested placeholder in the style of Textmate." (not (zerop number)) (yas/make-field number (cons (make-overlay (match-beginning 0) - (match-beginning 2)) + (match-beginning 2) nil t nil) (make-overlay (1- real-match-end-0) - real-match-end-0)) + real-match-end-0 nil t nil)) parent-field)))) (when brand-new-field (push brand-new-field (yas/snippet-fields snippet)) @@ -704,7 +723,7 @@ Allows nested placeholder in the style of Textmate." (yas/snippet-find-field snippet number)))) (when (and real-match-end-0 field) (push (yas/make-mirror (make-overlay (match-beginning 0) - real-match-end-0) + real-match-end-0 nil t nil) (buffer-substring-no-properties (match-beginning 2) (1- real-match-end-0))) (yas/field-mirrors field)))))) @@ -714,11 +733,11 @@ Allows nested placeholder in the style of Textmate." (let ((number (string-to-number (match-string-no-properties 1)))) (if (zerop number) (setf (yas/snippet-exit snippet) - (make-overlay (match-beginning 0) (match-end 0))) + (make-overlay (match-beginning 0) (match-end 0) nil t nil)) (let ((field (yas/snippet-find-field snippet number))) (when field (let ((ov (make-overlay (match-beginning 0) - (match-end 0)))) + (match-end 0) nil t nil))) (overlay-put ov 'yas/mirrorp t) (push (yas/make-mirror ov nil) (yas/field-mirrors field))))))))) @@ -1109,15 +1128,22 @@ when the condition evaluated to non-nil." (and (zerop (- (yas/field-start field) (yas/field-end field))) (yas/field-parent-field field))) +(defun yas/snippet-of-current-keymap () + (first (remove nil (mapcar #'(lambda (ov) + (overlay-get ov 'yas/snippet)) + (overlays-at (point)))))) + + (defun yas/next-field (&optional arg) "Navigate to next field. If there's none, exit the snippet." (interactive) (let* ((arg (or arg 1)) (snippet (yas/snippet-of-current-keymap)) + (active-field (overlay-get yas/active-field-overlay 'yas/field)) (number (and snippet (+ arg - (yas/field-number (yas/snippet-active-field snippet))))) + (yas/field-number active-field)))) (live-fields (remove-if #'yas/field-probably-deleted-p (yas/snippet-fields snippet))) (target-field (yas/snippet-find-field snippet number))) (cond ((and number @@ -1131,7 +1157,6 @@ when the condition evaluated to non-nil." (defun yas/move-to-field (snippet field) "Update SNIPPET to move to field FIELD." (goto-char (overlay-end (car (yas/field-overlay-pair field)))) - (setf (yas/snippet-active-field snippet) field) (if (and yas/active-field-overlay (overlay-buffer yas/active-field-overlay)) (move-overlay yas/active-field-overlay @@ -1140,7 +1165,8 @@ when the condition evaluated to non-nil." ;; create a new overlay (setq yas/active-field-overlay (make-overlay (overlay-end (car (yas/field-overlay-pair field))) - (overlay-start (cdr (yas/field-overlay-pair field))))) + (overlay-start (cdr (yas/field-overlay-pair field))) + nil nil t)) (overlay-put yas/active-field-overlay 'face 'yas/field-highlight-face) (overlay-put yas/active-field-overlay 'modification-hooks '(yas/on-field-overlay-modification)) (overlay-put yas/active-field-overlay 'insert-in-front-hooks '(yas/on-field-overlay-modification)) @@ -1156,12 +1182,7 @@ when the condition evaluated to non-nil." "Goto exit-marker of SNIPPET and commit the snippet. Cleaning up the snippet does not delete it!" (interactive) - (let ((exit-marker (set-marker (make-marker) (if (markerp (yas/snippet-exit snippet)) - (yas/snippet-exit snippet) - (overlay-start (yas/snippet-exit snippet)))))) - (yas/commit-snippet snippet) - (goto-char exit-marker) - (set-marker exit-marker nil))) + (goto-char (yas/commit-snippet snippet))) ;; Snippet register and unregister routines. ;; @@ -1185,8 +1206,8 @@ exists in the current buffer. Return snippet" ;; ;; setup the `pre-command-hook' and `post-command-hook' ;; - (add-hook 'pre-command-hook 'yas/pre-command-handler) - (add-hook 'post-command-hook 'yas/post-command-handler) + (add-hook 'pre-command-hook 'yas/pre-command-handler 'local) + (add-hook 'post-command-hook 'yas/post-command-handler 'local) snippet) (defun yas/unregister-snippet (snippet) @@ -1202,8 +1223,8 @@ more snippets registered in the current buffer." ;; (when (eq 0 (hash-table-count yas/registered-snippets)) - (remove-hook 'pre-command-hook 'yas/pre-command-handler) - (remove-hook 'post-command-hook 'yas/post-command-handler))) + (remove-hook 'pre-command-hook 'yas/pre-command-handler 'local) + (remove-hook 'post-command-hook 'yas/post-command-handler 'local))) (defun yas/exterminate-snippets () @@ -1218,21 +1239,26 @@ more snippets registered in the current buffer." (defun yas/commit-snippet (snippet) "Commit SNIPPET, but leave point as it is. This renders the -snippet as ordinary text" - (let* ((control-overlay (yas/snippet-control-overlay snippet)) +snippet as ordinary text. + +Return a buffer position where the point should be placed if +exiting the snippet." + (let ((control-overlay (yas/snippet-control-overlay snippet)) yas/snippet-beg - yas/snippet-end) + yas/snippet-end + exit (point)) ;; - ;; Save the end of the moribund snippet in case we need to undo - ;; its original expansion. This is used by `yas/undo-expand-snippet' + ;; Save the end of the moribund snippet in case we need to revive it + ;; its original expansion. ;; (when (and control-overlay (overlay-buffer control-overlay)) (setq yas/snippet-beg (overlay-start control-overlay)) (setq yas/snippet-end (overlay-end control-overlay)) - (delete-overlay control-overlay)) + (delete-overlay control-overlay) + (narrow-to-region yas/snippet-beg yas/snippet-end)) - ;; TODO: Maybe action for snippet revival + ;; Push an action for snippet revival ;; (push `(apply yas/snippet-create ,yas/snippet-beg ,yas/snippet-end) buffer-undo-list) @@ -1260,10 +1286,12 @@ snippet as ordinary text" (yas/delete-overlay-region ov))))) ;; Take care of the exit marker ;; - (if (and (overlayp (yas/snippet-exit snippet)) - (overlay-buffer (yas/snippet-exit snippet))) - (yas/delete-overlay-region (yas/snippet-exit snippet)) - (set-marker (yas/snippet-exit snippet) nil))) + (cond ((and (yas/snippet-exit snippet) + (overlay-buffer (yas/snippet-exit snippet))) + (setq exit (overlay-start (yas/snippet-exit snippet))) + (yas/delete-overlay-region (yas/snippet-exit snippet))) + (t + (setq exit (point-max))))) ;; XXX: `yas/after-exit-snippet-hook' should be run with ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not @@ -1271,9 +1299,10 @@ snippet as ordinary text" ;; disappeared, which sometimes happens when the snippet's messed ;; up... ;; - (run-hooks 'yas/after-exit-snippet-hook)) - (yas/unregister-snippet snippet)) - + (run-hooks 'yas/after-exit-snippet-hook) + (yas/unregister-snippet snippet) + (widen) + exit)) (defun yas/check-commit-snippet () "Checks if point exited the currently active field of the @@ -1281,27 +1310,28 @@ snippet, if so cleans up the whole snippet up. This function is part of `post-command-hook' while registered snippets last." - (let* ((snippet (yas/snippet-of-current-keymap)) - (field (and snippet - (yas/snippet-active-field snippet)))) - (cond (;; - ;; No snippet at point, cleanup *all* snippets - ;; - (null snippet) - ;; (yas/cleanup-all-snippets) -) - (;; A snippet exits at point, but point left the currently - ;; active field overlay - (or (not field) - (and field - (not (yas/point-in-field-p (yas/field-primary-field field))))) - (yas/cleanup-snippet snippet)) - (;; - ;; Snippet at point, and point inside a snippet field, - ;; everything is normal - ;; - t - nil)))) + (unless undo-in-progress + (let* ((snippet (yas/snippet-of-current-keymap)) + (field (and yas/active-field-overlay + (overlay-buffer yas/active-field-overlay) + (overlay-get yas/active-field-overlay 'yas/field)))) + (cond ((null snippet) + ;; + ;; No snippet at point, cleanup *all* snippets + ;; + (yas/exterminate-snippets)) + ((or (not field) + (and field + (not (yas/point-in-field-p field)))) + ;; A snippet exitss at point, but point left the currently + ;; active field overlay + (save-excursion (yas/commit-snippet snippet))) + ( ;; + ;; Snippet at point, and point inside a snippet field, + ;; everything is normal + ;; + t + nil))))) (defun yas/point-in-field-p (field &optional point) "..." @@ -1313,12 +1343,15 @@ registered snippets last." ;; ;; Pre and post command handlers ;; - (defun yas/pre-command-handler () ) (defun yas/post-command-handler () - ) + ;; (when (eq this-command 'yas/next-field) + ;; (when (null (car buffer-undo-list)) + ;; (setq buffer-undo-list (cdr buffer-undo-list)))) + ;; (yas/check-commit-snippet) +) ;; Debug functions. Use (or change) at will whenever needed. @@ -1328,39 +1361,39 @@ registered snippets last." (with-output-to-temp-buffer "*YASnippet trace*" (princ "Interesting YASnippet vars: \n\n") (princ (format "Register hash-table: %s\n\n" yas/registered-snippets)) - (cond ((not yas/registered-snippets) - (princ " No snippet hash table!")) - ((eq (hash-table-count yas/registered-snippets) 0) - (princ " No registered snippets\n")) - (t - (maphash #'(lambda (key snippet) - (princ (format "\t key %s for snippet %s\n" - key - (yas/snippet-id snippet))) + ;; (cond ((not yas/registered-snippets) +;; (princ " No snippet hash table!")) +;; ((eq (hash-table-count yas/registered-snippets) 0) +;; (princ " No registered snippets\n")) +;; (t +;; (maphash #'(lambda (key snippet) +;; (princ (format "\t key %s for snippet %s\n" +;; key +;; (yas/snippet-id snippet))) - (princ (format "\t Control overlay %s\n" - (yas/snippet-control-overlay snippet))) +;; (princ (format "\t Control overlay %s\n" +;; (yas/snippet-control-overlay snippet))) - (dolist (field (yas/snippet-fields snippet)) - (princ (format "\t field %s with %s mirrors is %s and %s" - (yas/field-number field) - (length (yas/field-mirrors field)) - (if (yas/field-probably-deleted-p field) - "DELETED" - "alive") - (if (eq field (yas/snippet-active-field snippet)) - "ACTIVE!\n" - "NOT ACTIVE!\n"))) - (princ (format "\t\t Covering: %s\n" (yas/current-field-text field))) - (princ (format "\t\t Displays: %s\n" (yas/field-text-for-display field))) - ;; (dolist (mirror (yas/field-mirrors field)) - ;; (princ (format "\t\t Mirror displays: \n" - ;; (if (eq field (yas/field-primary-field field)) - ;; "Primary" "Mirror")))) -)) - yas/registered-snippets))) +;; (dolist (field (yas/snippet-fields snippet)) +;; (princ (format "\t field %s with %s mirrors is %s and %s" +;; (yas/field-number field) +;; (length (yas/field-mirrors field)) +;; (if (yas/field-probably-deleted-p field) +;; "DELETED" +;; "alive") +;; (if (eq field (overlay-get yas/active-field-overlay 'yas/field)) +;; "ACTIVE!\n" +;; "NOT ACTIVE!\n"))) +;; (princ (format "\t\t Covering: %s\n" (yas/current-field-text field))) +;; ;; (princ (format "\t\t Displays: %s\n" (yas/field-text-for-display field))) +;; ;; (dolist (mirror (yas/field-mirrors field)) +;; ;; (princ (format "\t\t Mirror displays: \n" +;; ;; (if (eq field (yas/field-primary-field field)) +;; ;; "Primary" "Mirror")))) +;; )) +;; yas/registered-snippets))) (princ (format "\nPost command hook: %s\n" post-command-hook)) (princ (format "\nPre command hook: %s\n" pre-command-hook)) @@ -1384,7 +1417,7 @@ registered snippets last." (when (string-match "yas/" (symbol-name atom)) (unintern atom))))) -(defun yas/debug-test (&optional verbose) +(defun yas/debug-test (&optional quiet) (interactive "P") (yas/load-directory "~/Source/yasnippet/snippets/") ;;(kill-buffer (get-buffer "*YAS TEST*")) @@ -1394,11 +1427,11 @@ registered snippets last." (setq buffer-undo-list nil) (let ((abbrev)) (if (require 'ido nil t) - (setq abbrev (ido-completing-read "Snippet abbrev: " '("crazy" "prop"))) - (setq abbrev "crazy")) + (setq abbrev (ido-completing-read "Snippet abbrev: " '("crazy" "prip" "prop"))) + (setq abbrev "prop")) (insert abbrev)) (objc-mode) - (when verbose + (unless quiet (add-hook (make-local-variable 'post-command-hook) 'yas/debug-some-vars)) (yas/expand)) From 97608378d3639febdb4fd66a635b3aac36b869f0 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Sat, 4 Jul 2009 20:12:49 +0000 Subject: [PATCH 25/75] * got rid of yas/registered-snippets * undo/redo much smoother * hooks last while yas/minor-mode is t (this may be changed) --- yasnippet.el | 214 +++++++++++++++++---------------------------------- 1 file changed, 69 insertions(+), 145 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index a396b20..0e955be 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -263,8 +263,12 @@ You can customize the key through `yas/trigger-key'." " yas" :group 'editing (define-key yas/minor-mode-map yas/trigger-key 'yas/expand) - - (set 'yas/registered-snippets (make-hash-table :test 'eq))) + (if yas/minor-mode + (progn + (add-hook 'post-command-hook 'yas/post-command-handler nil t) + (add-hook 'pre-command-hook 'yas/pre-command-handler t t)) + (remove-hook 'post-command-hook 'yas/post-command-handler) + (remove-hook 'pre-command-hook 'yas/pre-command-handler))) (defun yas/minor-mode-auto-on () "Turn on YASnippet minor mode unless `yas/dont-activate' is @@ -529,41 +533,41 @@ the template of a snippet in the current snippet-table." (buffer-substring-no-properties (yas/field-start field) (yas/field-end field))) +(defun yas/undo-in-progress () + (or undo-in-progress + (eq this-command 'undo))) (defun yas/make-control-overlay (start end) "..." (let ((overlay (make-overlay start end nil - nil - t))) + t + nil))) (overlay-put overlay 'keymap yas/keymap) (overlay-put overlay 'yas/snippet snippet) + (overlay-put overlay 'evaporate t) overlay)) (defun yas/on-field-overlay-modification (overlay after? beg end &optional length) "To be written" - (unless undo-in-progress - (cond ((and after? - yas/registered-snippets) - (maphash #'(lambda (key snippet) - (yas/update-mirrors snippet)) - yas/registered-snippets)) - ((not after?) - (let ((field (overlay-get overlay 'yas/field))) - (unless (yas/field-modified-p field) - (let ((inhibit-modification-hooks t)) - (reduce #'(lambda (ov1 ov2) - (delete-region (overlay-end ov1) (overlay-start ov2)) - ov2) - (yas/hidden-overlays-in (yas/field-start field) (yas/field-end field)))) - (setf (yas/field-modified-p field) t)))) - (t - nil)))) + (cond (after? + (mapcar #'yas/update-mirrors (yas/snippets-at-point))) + ((not (or after? (yas/undo-in-progress))) + (let ((field (overlay-get overlay 'yas/field))) + (unless (yas/field-modified-p field) + (let ((inhibit-modification-hooks t)) + (reduce #'(lambda (ov1 ov2) + (delete-region (overlay-end ov1) (overlay-start ov2)) + ov2) + (yas/hidden-overlays-in (yas/field-start field) (yas/field-end field)))) + (setf (yas/field-modified-p field) t)))) + (t + nil))) (add-to-list 'debug-ignored-errors "^Exit the snippet first$") (defun yas/on-hidden-overlay-modification (overlay after? beg end &optional length) - (unless undo-in-progress + (unless (yas/undo-in-progress) (unless (or after? (null (overlay-buffer overlay))) ;; (save-excursion @@ -611,18 +615,24 @@ will be deleted before inserting template." (goto-char template-end) (delete-char length) (let ((snippet (yas/snippet-create template-beg template-end))) + ;; Do more indenting (save-excursion - ;; Do more indenting (goto-char (overlay-start (yas/snippet-control-overlay snippet))) (while (re-search-forward "$>" nil t) (replace-match "") - (indent-according-to-mode)))))))) + (indent-according-to-mode))) + ;; Push an undo action + (push `(apply yas/take-care-of-redo ,template-beg ,template-end) + buffer-undo-list)))))) + +(defun yas/take-care-of-redo (beg end) + (message "taking care of undo between %s and %s" beg end) + (push `(apply yas/snippet-create ,beg ,end) + buffer-undo-list)) (defun yas/snippet-create (begin end) (narrow-to-region begin end) - ;; Create and register a brand new snippet in the local - ;; `yas/registered-snippets' var. Create fields. - (let ((snippet (yas/register-snippet (yas/make-snippet)))) + (let ((snippet (yas/make-snippet))) (goto-char (point-min)) (yas/snippet-parse-create snippet) @@ -1128,10 +1138,10 @@ when the condition evaluated to non-nil." (and (zerop (- (yas/field-start field) (yas/field-end field))) (yas/field-parent-field field))) -(defun yas/snippet-of-current-keymap () - (first (remove nil (mapcar #'(lambda (ov) - (overlay-get ov 'yas/snippet)) - (overlays-at (point)))))) +(defun yas/snippets-at-point () + (remove nil (mapcar #'(lambda (ov) + (overlay-get ov 'yas/snippet)) + (overlays-at (point))))) (defun yas/next-field (&optional arg) @@ -1139,7 +1149,7 @@ when the condition evaluated to non-nil." (interactive) (let* ((arg (or arg 1)) - (snippet (yas/snippet-of-current-keymap)) + (snippet (first (yas/snippets-at-point))) (active-field (overlay-get yas/active-field-overlay 'yas/field)) (number (and snippet (+ arg @@ -1162,7 +1172,8 @@ when the condition evaluated to non-nil." (move-overlay yas/active-field-overlay (overlay-end (car (yas/field-overlay-pair field))) (overlay-start (cdr (yas/field-overlay-pair field)))) - ;; create a new overlay + ;; create a new overlay, this is the only yas overlay that + ;; shouldn't evaporate (setq yas/active-field-overlay (make-overlay (overlay-end (car (yas/field-overlay-pair field))) (overlay-start (cdr (yas/field-overlay-pair field))) @@ -1184,55 +1195,12 @@ up the snippet does not delete it!" (interactive) (goto-char (yas/commit-snippet snippet))) -;; Snippet register and unregister routines. -;; -(defvar yas/registered-snippets nil - "A hash table holding all active snippets") -(eval-when-compile - (make-variable-buffer-local 'yas/registered-snippets)) - -(defun yas/register-snippet (snippet) - "Register SNIPPET in the `yas/registered-snippets' table. Add -a `yas/pre-command-handler' function to the buffer-local -`pre-command-hook' and `yas/post-command-handler' to the -`post-command-hook'. This should exist while registered snippets -exists in the current buffer. Return snippet" - (unless yas/registered-snippets - (setq yas/registered-snippets (make-hash-table :test 'eq))) - ;; - ;; register the snippet - ;; - (puthash (yas/snippet-id snippet) snippet yas/registered-snippets) - ;; - ;; setup the `pre-command-hook' and `post-command-hook' - ;; - (add-hook 'pre-command-hook 'yas/pre-command-handler 'local) - (add-hook 'post-command-hook 'yas/post-command-handler 'local) - snippet) - -(defun yas/unregister-snippet (snippet) - "Unregister snippet from the `yas/registered-snippets' table. -Remove the handlers registered in `yas/register-snippet' if no -more snippets registered in the current buffer." - ;; - ;; - ;; - (remhash (yas/snippet-id snippet) yas/registered-snippets) - ;; - ;; - ;; - (when (eq 0 - (hash-table-count yas/registered-snippets)) - (remove-hook 'pre-command-hook 'yas/pre-command-handler 'local) - (remove-hook 'post-command-hook 'yas/post-command-handler 'local))) - - (defun yas/exterminate-snippets () - "Remove all locally registered snippets and remove - `yas/check-cleanup-snippet' from the `post-command-hook'" + "Remove all snippets in buffer" (interactive) - (when yas/registered-snippets - (maphash #'(lambda (key value) (yas/commit-snippet value)) yas/registered-snippets))) + (mapcar #'yas/commit-snippet (remove nil (mapcar #'(lambda (ov) + (overlay-get ov 'yas/snippet)) + (overlays-in (point-min) (point-max)))))) (defun yas/delete-overlay-region (overlay) (delete-region (overlay-start overlay) (overlay-end overlay))) @@ -1300,29 +1268,23 @@ exiting the snippet." ;; up... ;; (run-hooks 'yas/after-exit-snippet-hook) - (yas/unregister-snippet snippet) (widen) exit)) (defun yas/check-commit-snippet () "Checks if point exited the currently active field of the -snippet, if so cleans up the whole snippet up. - -This function is part of `post-command-hook' while -registered snippets last." - (unless undo-in-progress - (let* ((snippet (yas/snippet-of-current-keymap)) - (field (and yas/active-field-overlay - (overlay-buffer yas/active-field-overlay) - (overlay-get yas/active-field-overlay 'yas/field)))) +snippet, if so cleans up the whole snippet up." + (unless (yas/undo-in-progress) + (let* ((snippet (first (yas/snippets-at-point)))) (cond ((null snippet) ;; ;; No snippet at point, cleanup *all* snippets ;; (yas/exterminate-snippets)) - ((or (not field) - (and field - (not (yas/point-in-field-p field)))) + ((let ((beg (overlay-start yas/active-field-overlay)) + (end (overlay-end yas/active-field-overlay))) + (or (> (point) end) + (< (point) beg))) ;; A snippet exitss at point, but point left the currently ;; active field overlay (save-excursion (yas/commit-snippet snippet))) @@ -1333,13 +1295,6 @@ registered snippets last." t nil))))) -(defun yas/point-in-field-p (field &optional point) - "..." - (let ((point (or point - (point)))) - (and (>= point (yas/field-start field)) - (<= point (yas/field-end field))))) - ;; ;; Pre and post command handlers ;; @@ -1347,53 +1302,21 @@ registered snippets last." ) (defun yas/post-command-handler () - ;; (when (eq this-command 'yas/next-field) - ;; (when (null (car buffer-undo-list)) - ;; (setq buffer-undo-list (cdr buffer-undo-list)))) - ;; (yas/check-commit-snippet) -) + (yas/check-commit-snippet)) ;; Debug functions. Use (or change) at will whenever needed. +(defun yas/toggle-hidden-overlays () + (interactive) + (mapcar #'(lambda (ov) + (when (overlay-get ov 'yas/hidden) + (overlay-put ov 'invisible (not (overlay-get ov 'invisible))))) + (overlays-in (point-min) (point-max)))) (defun yas/debug-some-vars () (interactive) (with-output-to-temp-buffer "*YASnippet trace*" (princ "Interesting YASnippet vars: \n\n") - (princ (format "Register hash-table: %s\n\n" yas/registered-snippets)) - ;; (cond ((not yas/registered-snippets) -;; (princ " No snippet hash table!")) -;; ((eq (hash-table-count yas/registered-snippets) 0) -;; (princ " No registered snippets\n")) -;; (t -;; (maphash #'(lambda (key snippet) -;; (princ (format "\t key %s for snippet %s\n" -;; key -;; (yas/snippet-id snippet))) - - -;; (princ (format "\t Control overlay %s\n" -;; (yas/snippet-control-overlay snippet))) - - -;; (dolist (field (yas/snippet-fields snippet)) -;; (princ (format "\t field %s with %s mirrors is %s and %s" -;; (yas/field-number field) -;; (length (yas/field-mirrors field)) -;; (if (yas/field-probably-deleted-p field) -;; "DELETED" -;; "alive") -;; (if (eq field (overlay-get yas/active-field-overlay 'yas/field)) -;; "ACTIVE!\n" -;; "NOT ACTIVE!\n"))) -;; (princ (format "\t\t Covering: %s\n" (yas/current-field-text field))) -;; ;; (princ (format "\t\t Displays: %s\n" (yas/field-text-for-display field))) -;; ;; (dolist (mirror (yas/field-mirrors field)) -;; ;; (princ (format "\t\t Mirror displays: \n" -;; ;; (if (eq field (yas/field-primary-field field)) -;; ;; "Primary" "Mirror")))) -;; )) -;; yas/registered-snippets))) (princ (format "\nPost command hook: %s\n" post-command-hook)) (princ (format "\nPre command hook: %s\n" pre-command-hook)) @@ -1425,15 +1348,16 @@ registered snippets last." (yas/exterminate-snippets) (erase-buffer) (setq buffer-undo-list nil) + (html-mode) (let ((abbrev)) - (if (require 'ido nil t) - (setq abbrev (ido-completing-read "Snippet abbrev: " '("crazy" "prip" "prop"))) - (setq abbrev "prop")) + ;; (if (require 'ido nil t) + ;; (setq abbrev (ido-completing-read "Snippet abbrev: " '("crazy" "prip" "prop"))) + ;; (setq abbrev "prop")) + (setq abbrev "bosta") (insert abbrev)) - (objc-mode) (unless quiet - (add-hook (make-local-variable 'post-command-hook) 'yas/debug-some-vars)) - (yas/expand)) + (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local)) + ) (provide 'yasnippet) From 514243f5b94a1d42347810e0fc57f6167fc3323c Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Sun, 5 Jul 2009 20:20:59 +0000 Subject: [PATCH 26/75] * Changed the approach yet again, this is it, this should work... --- yasnippet.el | 290 ++++++++++++++++++++------------------------------- 1 file changed, 113 insertions(+), 177 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 0e955be..7fbeae6 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -307,12 +307,13 @@ set to t." (fields '()) (exit nil) (id (yas/snippet-next-id) :read-only t) - (control-overlay nil)) + (control-overlay nil) + active-field) -(defstruct (yas/field (:constructor yas/make-field (number overlay-pair parent-field))) +(defstruct (yas/field (:constructor yas/make-field (number start end parent-field))) "A field." number - overlay-pair + start end parent-field (mirrors '()) (next nil) @@ -320,17 +321,11 @@ set to t." (transform nil) (modified-p nil)) -(defstruct (yas/mirror (:constructor yas/make-mirror (overlay transform))) +(defstruct (yas/mirror (:constructor yas/make-mirror (start end transform))) "A mirror." - overlay + start end (transform nil)) -(defun yas/field-start (field) (overlay-start (car (yas/field-overlay-pair field)))) -(defun yas/field-end (field) (overlay-end (cdr (yas/field-overlay-pair field)))) - -(defun yas/mirror-start (mirror) (overlay-start (yas/mirror-overlay mirror))) -(defun yas/mirror-end (mirror) (overlay-end (yas/mirror-overlay mirror))) - (defstruct (yas/snippet-table (:constructor yas/make-snippet-table ())) "A table to store snippets for a perticular mode." (hash (make-hash-table :test 'equal)) @@ -507,31 +502,9 @@ the template of a snippet in the current snippet-table." start end))) -(defun yas/hidden-overlays-in (beg end) - "A sorted list of hidden yas overlays overlapping the region - between BEG and END" - (sort (remove-if-not #'(lambda (ov) - (overlay-get ov 'yas/hidden)) - (overlays-in beg end)) - #'(lambda (ov1 ov2) - (> (overlay-start ov2) (overlay-start ov1))))) - (defun yas/field-text-for-display (field) "Return the propertized display text for field FIELD. " - (let ((hidden-overlays (yas/hidden-overlays-in (yas/field-start field) (yas/field-end field))) - (text)) - (when hidden-overlays - (reduce #'(lambda (ov1 ov2) - (setq text (concat text - (buffer-substring (overlay-end ov1) (overlay-start ov2)) - (overlay-get ov1 'after-string))) - ov2) - hidden-overlays)) - text)) - -(defun yas/current-field-text (field) - (buffer-substring-no-properties (yas/field-start field) - (yas/field-end field))) + (buffer-substring (yas/field-start field) (yas/field-end field))) (defun yas/undo-in-progress () (or undo-in-progress @@ -551,31 +524,21 @@ the template of a snippet in the current snippet-table." (defun yas/on-field-overlay-modification (overlay after? beg end &optional length) "To be written" - (cond (after? + (cond ((and after? + (not (yas/undo-in-progress))) (mapcar #'yas/update-mirrors (yas/snippets-at-point))) - ((not (or after? (yas/undo-in-progress))) - (let ((field (overlay-get overlay 'yas/field))) - (unless (yas/field-modified-p field) - (let ((inhibit-modification-hooks t)) - (reduce #'(lambda (ov1 ov2) - (delete-region (overlay-end ov1) (overlay-start ov2)) - ov2) - (yas/hidden-overlays-in (yas/field-start field) (yas/field-end field)))) - (setf (yas/field-modified-p field) t)))) + ;; ((not (or after? (yas/undo-in-progress))) +;; (let ((field (overlay-get overlay 'yas/field))) +;; (unless (yas/field-modified-p field) +;; (let ((inhibit-modification-hooks t)) +;; (reduce #'(lambda (ov1 ov2) +;; (delete-region (overlay-end ov1) (overlay-start ov2)) +;; ov2) +;; (yas/hidden-overlays-in (yas/field-start field) (yas/field-end field)))) +;; (setf (yas/field-modified-p field) t)))) (t nil))) -(add-to-list 'debug-ignored-errors "^Exit the snippet first$") -(defun yas/on-hidden-overlay-modification (overlay after? beg end &optional length) - (unless (yas/undo-in-progress) - (unless (or after? - (null (overlay-buffer overlay))) - ;; (save-excursion - ;; (yas/exit-snippet (overlay-get overlay 'yas/snippet))) - ;; (call-interactively this-command) - (goto-char beg) - (error "Exit the snippet first")))) - (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length) "To be written" ) @@ -592,103 +555,70 @@ will be deleted before inserting template." (let* ((key (buffer-substring-no-properties start end)) (length (- end start)) - (column (current-column))) + (column (current-column)) + snippet) + (delete-char length) (save-restriction - (narrow-to-region start start) + (let ((buffer-undo-list t)) + (narrow-to-region start start) + (insert template) + (setq snippet (yas/snippet-create (point-min) (point-max)))) + (push (cons (point-min) (point-max)) buffer-undo-list) + ;; Push an undo action + (push `(apply yas/take-care-of-redo ,(point-min) ,(point-max) ,snippet) + buffer-undo-list)))) - (insert template) +(defun yas/take-care-of-redo (beg end snippet) + (push `(apply yas/snippet-revive ,beg ,end ,snippet) + buffer-undo-list)) - ;; Step XX: do necessary indent - (when yas/indent-line - (let* ((indent (if indent-tabs-mode - (concat (make-string (/ column tab-width) ?\t) - (make-string (% column tab-width) ?\ )) - (make-string column ?\ )))) - (goto-char (point-min)) - (while (and (zerop (forward-line)) - (= (current-column) 0)) - (insert indent)))) - - (let ((template-beg (point-min)) - (template-end (point-max))) - (widen) - (goto-char template-end) - (delete-char length) - (let ((snippet (yas/snippet-create template-beg template-end))) - ;; Do more indenting - (save-excursion - (goto-char (overlay-start (yas/snippet-control-overlay snippet))) - (while (re-search-forward "$>" nil t) - (replace-match "") - (indent-according-to-mode))) - ;; Push an undo action - (push `(apply yas/take-care-of-redo ,template-beg ,template-end) - buffer-undo-list)))))) - -(defun yas/take-care-of-redo (beg end) - (message "taking care of undo between %s and %s" beg end) - (push `(apply yas/snippet-create ,beg ,end) +(defun yas/snippet-revive (beg end snippet) + (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay beg end)) + (overlay-put (yas/snippet-control-overlay snippet) 'yas/snippet snippet) + (yas/move-to-field snippet (car (yas/snippet-fields snippet))) + (push `(apply yas/take-care-of-redo ,beg ,end ,snippet) buffer-undo-list)) (defun yas/snippet-create (begin end) - (narrow-to-region begin end) (let ((snippet (yas/make-snippet))) - (goto-char (point-min)) - (yas/snippet-parse-create snippet) + (goto-char begin) + (yas/snippet-parse-create snippet) - ;; Sort and link each field - (setf (yas/snippet-fields snippet) - (sort (yas/snippet-fields snippet) - '(lambda (field1 field2) - (yas/snippet-field-compare field1 field2)))) - - (let ((prev nil)) - (dolist (field (yas/snippet-fields snippet)) - (setf (yas/field-prev field) prev) - (when prev - (setf (yas/field-next prev) field)) - (setq prev field))) + ;; Sort and link each field + (yas/snippet-sort-link-fields snippet) + + ;; Update the mirrors for the first time + (yas/update-mirrors snippet) - ;; Hide (or highlight for debugging) all hidden overlays - (let ((prop-list)) - (push (if (member 'yas/debug-some-vars post-command-hook) - (cons 'face 'yas/field-debug-face) - (cons 'invisible t)) - prop-list) - (push (cons 'evaporate t) prop-list) - (push (cons 'yas/hidden t) prop-list) - (push (cons 'yas/snippet snippet) prop-list) - (push (cons 'modification-hooks '(yas/on-hidden-overlay-modification)) prop-list) ;; what i really wanted is 'read-only - (dolist (prop prop-list) - (dolist (field (yas/snippet-fields snippet)) - (overlay-put (car (yas/field-overlay-pair field)) (car prop) (cdr prop)) - (overlay-put (cdr (yas/field-overlay-pair field)) (car prop) (cdr prop)) - (dolist (mirror (yas/field-mirrors field)) - (overlay-put (yas/mirror-overlay mirror) (car prop) (cdr prop)))) - (when (overlayp (yas/snippet-exit snippet)) - (overlay-put (yas/snippet-exit snippet) (car prop) (cdr prop))))) + ;; Create keymap overlay for snippet + (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay (point-min) (point-max))) - ;; Update the mirrors - (yas/update-mirrors snippet) + ;; Move to end + (goto-char (point-max)) - ;; Create keymap overlay for snippet - (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay (point-min) (point-max))) + ;; Place the cursor at a proper place + (let* ((first-field (car (yas/snippet-fields snippet))) + overlay) + (cond (first-field + ;; Move to the new field, setting up properties of the + ;; wandering active field overlay. + (yas/move-to-field snippet first-field)) + (t + ;; No fields, quite a simple snippet I suppose + (yas/exit-snippet snippet)))) + snippet)) - ;; Move to end - (goto-char (point-max)) - - ;; Place the cursor at a proper place - (let* ((first-field (car (yas/snippet-fields snippet))) - overlay) - (cond (first-field - ;; Move to the new field, setting up properties of the - ;; wandering active field overlay. - (yas/move-to-field snippet first-field)) - (t - ;; No fields, quite a simple snippet I suppose - (yas/exit-snippet snippet)))) - (widen) - snippet)) +(defun yas/snippet-sort-link-fields (snippet) + (setf (yas/snippet-fields snippet) + (sort (yas/snippet-fields snippet) + '(lambda (field1 field2) + (yas/snippet-field-compare field1 field2)))) + (let ((prev nil)) + (dolist (field (yas/snippet-fields snippet)) + (setf (yas/field-prev field) prev) + (when prev + (setf (yas/field-next prev) field)) + (setq prev field)))) (defun yas/snippet-parse-create (snippet) "Parse a recently inserted snippet template, creating all @@ -711,16 +641,16 @@ Allows nested placeholder in the style of Textmate." number (not (zerop number)) (yas/make-field number - (cons (make-overlay (match-beginning 0) - (match-beginning 2) nil t nil) - (make-overlay (1- real-match-end-0) - real-match-end-0 nil t nil)) + (set-marker (make-marker) (match-beginning 2)) + (set-marker (make-marker) (1- real-match-end-0)) parent-field)))) (when brand-new-field + (delete-region (1- real-match-end-0) real-match-end-0) + (delete-region (match-beginning 0) (match-beginning 2)) (push brand-new-field (yas/snippet-fields snippet)) (save-excursion (save-restriction - (narrow-to-region (match-beginning 2) (1- real-match-end-0)) + (narrow-to-region (yas/field-start brand-new-field) (yas/field-end brand-new-field)) (goto-char (point-min)) (yas/field-parse-create snippet brand-new-field))))))) @@ -732,34 +662,40 @@ Allows nested placeholder in the style of Textmate." (not (zerop number)) (yas/snippet-find-field snippet number)))) (when (and real-match-end-0 field) - (push (yas/make-mirror (make-overlay (match-beginning 0) - real-match-end-0 nil t nil) + (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) + (set-marker (make-marker) (match-beginning 0)) (buffer-substring-no-properties (match-beginning 2) (1- real-match-end-0))) - (yas/field-mirrors field)))))) + (yas/field-mirrors field)) + (delete-region (match-beginning 0) real-match-end-0))))) (defun yas/simple-mirror-parse-create (snippet) (while (re-search-forward yas/simple-mirror-regexp nil t) (let ((number (string-to-number (match-string-no-properties 1)))) - (if (zerop number) - (setf (yas/snippet-exit snippet) - (make-overlay (match-beginning 0) (match-end 0) nil t nil)) - (let ((field (yas/snippet-find-field snippet number))) - (when field - (let ((ov (make-overlay (match-beginning 0) - (match-end 0) nil t nil))) - (overlay-put ov 'yas/mirrorp t) - (push (yas/make-mirror ov nil) - (yas/field-mirrors field))))))))) + (cond ((zerop number) + (setf (yas/snippet-exit snippet) + (set-marker (make-marker) (match-beginning 0))) + (delete-region (match-beginning 0) (match-end 0))) + (t + (let ((field (yas/snippet-find-field snippet number))) + (when field + (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) + (set-marker (make-marker) (match-beginning 0)) + nil) + (yas/field-mirrors field)) + (delete-region (match-beginning 0) (match-end 0))))))))) (defun yas/update-mirrors (snippet) + (save-excursion (dolist (field (yas/snippet-fields snippet)) (dolist (mirror (yas/field-mirrors field)) - (yas/mirror-update-display mirror field)))) + (yas/mirror-update-display mirror field))))) (defun yas/mirror-update-display (mirror field) - (overlay-put (yas/mirror-overlay mirror) 'after-string - (propertize (yas/apply-transform mirror field) 'face 'yas/mirror-highlight-face))) + (goto-char (yas/mirror-start mirror)) + (delete-region (yas/mirror-start mirror) (yas/mirror-end mirror)) + (insert (yas/apply-transform mirror field)) + (set-marker (yas/mirror-end mirror) (point))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Template-related and snippet loading functions @@ -1166,19 +1102,19 @@ when the condition evaluated to non-nil." (defun yas/move-to-field (snippet field) "Update SNIPPET to move to field FIELD." - (goto-char (overlay-end (car (yas/field-overlay-pair field)))) + (goto-char (yas/field-start field)) + (setf (yas/snippet-active-field snippet) field) (if (and yas/active-field-overlay (overlay-buffer yas/active-field-overlay)) (move-overlay yas/active-field-overlay - (overlay-end (car (yas/field-overlay-pair field))) - (overlay-start (cdr (yas/field-overlay-pair field)))) - ;; create a new overlay, this is the only yas overlay that - ;; shouldn't evaporate + (yas/field-start field) + (yas/field-end field)) (setq yas/active-field-overlay - (make-overlay (overlay-end (car (yas/field-overlay-pair field))) - (overlay-start (cdr (yas/field-overlay-pair field))) + (make-overlay (yas/field-start field) + (yas/field-end field) nil nil t)) (overlay-put yas/active-field-overlay 'face 'yas/field-highlight-face) + (overlay-put yas/active-field-overlay 'evaporate t) (overlay-put yas/active-field-overlay 'modification-hooks '(yas/on-field-overlay-modification)) (overlay-put yas/active-field-overlay 'insert-in-front-hooks '(yas/on-field-overlay-modification)) (overlay-put yas/active-field-overlay 'insert-behind-hooks '(yas/on-field-overlay-modification))) @@ -1228,7 +1164,7 @@ exiting the snippet." ;; Push an action for snippet revival ;; - (push `(apply yas/snippet-create ,yas/snippet-beg ,yas/snippet-end) + (push `(apply yas/snippet-revive ,yas/snippet-beg ,yas/snippet-end ,snippet) buffer-undo-list) ;; Trash those overlays! @@ -1302,17 +1238,17 @@ snippet, if so cleans up the whole snippet up." ) (defun yas/post-command-handler () - (yas/check-commit-snippet)) + (cond ((eq 'undo this-command) + (let ((snippet (car (yas/snippets-at-point)))) + (when snippet + (yas/move-to-field snippet (or (yas/snippet-active-field snippet) + (car (yas/snippet-fields snippet))))))) + (t + ;; (yas/check-commit-snippet) + ))) ;; Debug functions. Use (or change) at will whenever needed. -(defun yas/toggle-hidden-overlays () - (interactive) - (mapcar #'(lambda (ov) - (when (overlay-get ov 'yas/hidden) - (overlay-put ov 'invisible (not (overlay-get ov 'invisible))))) - (overlays-in (point-min) (point-max)))) - (defun yas/debug-some-vars () (interactive) (with-output-to-temp-buffer "*YASnippet trace*" From f31a071457d0bf98547dfa34550051d397c450ad Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Sun, 5 Jul 2009 22:17:22 +0000 Subject: [PATCH 27/75] * a little bit of trouble with protection after C-d or deletions, but nothing too serious... --- yasnippet.el | 184 ++++++++++++++++++++++++++++----------------------- 1 file changed, 100 insertions(+), 84 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 7fbeae6..547aebf 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -66,10 +66,13 @@ current column if this variable is non-`nil'.") "The key to bind as a trigger of snippet.") (defvar yas/next-field-key (kbd "") "The key to navigate to next field.") +(defvar yas/clear-field-key (kbd "C-d") + "The key to clear the currently active field.") (defvar yas/keymap (make-sparse-keymap) "The keymap of snippet.") (define-key yas/keymap yas/next-field-key 'yas/next-field) +(define-key yas/keymap yas/clear-field-key 'yas/clear-field) (define-key yas/keymap (kbd "S-TAB") 'yas/prev-field) (define-key yas/keymap (kbd "") 'yas/prev-field) (define-key yas/keymap (kbd "") 'yas/prev-field) @@ -298,7 +301,11 @@ set to t." (defvar yas/active-field-overlay nil "Overlays the currently active field") +(defvar yas/field-protection-overlays nil + "Two overlays protect the current active field ") + (make-variable-buffer-local 'yas/active-field-overlay) +(make-variable-buffer-local 'yas/field-protection-overlays) (defstruct (yas/snippet (:constructor yas/make-snippet ())) "A snippet. @@ -522,30 +529,36 @@ the template of a snippet in the current snippet-table." (overlay-put overlay 'evaporate t) overlay)) +(defun yas/clear-field (&optional field) + (interactive) + (let ((field (or field + (and yas/active-field-overlay + (overlay-buffer yas/active-field-overlay) + (overlay-get yas/active-field-overlay 'yas/field))))) + (delete-region (yas/field-start field) (yas/field-end field)))) + (defun yas/on-field-overlay-modification (overlay after? beg end &optional length) "To be written" (cond ((and after? (not (yas/undo-in-progress))) (mapcar #'yas/update-mirrors (yas/snippets-at-point))) - ;; ((not (or after? (yas/undo-in-progress))) -;; (let ((field (overlay-get overlay 'yas/field))) -;; (unless (yas/field-modified-p field) -;; (let ((inhibit-modification-hooks t)) -;; (reduce #'(lambda (ov1 ov2) -;; (delete-region (overlay-end ov1) (overlay-start ov2)) -;; ov2) -;; (yas/hidden-overlays-in (yas/field-start field) (yas/field-end field)))) -;; (setf (yas/field-modified-p field) t)))) (t - nil))) + (let ((field (overlay-get yas/active-field-overlay 'yas/field))) + (when (and field + (not (or after? (yas/undo-in-progress))) + (not (yas/field-modified-p field))) + (setf (yas/field-modified-p field) t) + (yas/clear-field field)))))) -(defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length) +(defun yas/on-protection-overlay-modification (overlay after? beg end &optional length) "To be written" - ) - -(defun yas/overlay-insert-behind-hook (overlay after? beg end &optional length) - "To be written" - ) + (cond ((not (or after? + (yas/undo-in-progress))) + (let ((snippet (car (yas/snippets-at-point)))) + (when snippet + (yas/commit-snippet snippet) + (call-interactively this-command) + (error "Snippet exited")))))) (defun yas/expand-snippet (start end template) "Expand snippet at current point. Text between START and END @@ -569,13 +582,19 @@ will be deleted before inserting template." buffer-undo-list)))) (defun yas/take-care-of-redo (beg end snippet) + (let ((inhibit-modification-hooks t)) + (when yas/active-field-overlay + (delete-overlay yas/active-field-overlay)) + (when yas/field-protection-overlays + (mapcar #'delete-overlay yas/field-protection-overlays))) (push `(apply yas/snippet-revive ,beg ,end ,snippet) buffer-undo-list)) (defun yas/snippet-revive (beg end snippet) (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay beg end)) (overlay-put (yas/snippet-control-overlay snippet) 'yas/snippet snippet) - (yas/move-to-field snippet (car (yas/snippet-fields snippet))) + (yas/move-to-field snippet (or (yas/snippet-active-field snippet) + (car (yas/snippet-fields snippet)))) (push `(apply yas/take-care-of-redo ,beg ,end ,snippet) buffer-undo-list)) @@ -1100,10 +1119,7 @@ when the condition evaluated to non-nil." (t nil)))) -(defun yas/move-to-field (snippet field) - "Update SNIPPET to move to field FIELD." - (goto-char (yas/field-start field)) - (setf (yas/snippet-active-field snippet) field) +(defun yas/make-move-active-field-overlay (snippet field) (if (and yas/active-field-overlay (overlay-buffer yas/active-field-overlay)) (move-overlay yas/active-field-overlay @@ -1114,10 +1130,36 @@ when the condition evaluated to non-nil." (yas/field-end field) nil nil t)) (overlay-put yas/active-field-overlay 'face 'yas/field-highlight-face) - (overlay-put yas/active-field-overlay 'evaporate t) + ;;(overlay-put yas/active-field-overlay 'evaporate t) (overlay-put yas/active-field-overlay 'modification-hooks '(yas/on-field-overlay-modification)) (overlay-put yas/active-field-overlay 'insert-in-front-hooks '(yas/on-field-overlay-modification)) - (overlay-put yas/active-field-overlay 'insert-behind-hooks '(yas/on-field-overlay-modification))) + (overlay-put yas/active-field-overlay 'insert-behind-hooks '(yas/on-field-overlay-modification)))) + +(defun yas/make-move-field-protection-overlays (snippet field) + (cond ((and yas/field-protection-overlays + (every #'overlay-buffer yas/field-protection-overlays)) + (move-overlay (first yas/field-protection-overlays) (1- (yas/field-start field)) (yas/field-start field)) + (move-overlay (second yas/field-protection-overlays) (yas/field-end field) (1+ (yas/field-end field)))) + (t + (setq yas/field-protection-overlays + (list (make-overlay (1- (yas/field-start field)) (yas/field-start field) nil t nil) + (make-overlay (yas/field-end field) (1+ (yas/field-end field)) nil t nil))) + (dolist (ov yas/field-protection-overlays) + (overlay-put ov 'face 'yas/field-debug-face) + ;; (overlay-put ov 'evaporate t) + (overlay-put ov 'modification-hooks '(yas/on-protection-overlay-modification)) + (overlay-put ov 'insert-in-front-hooks '(yas/on-protection-overlay-modification)) + (overlay-put ov 'insert-behind-hooks '(yas/on-protection-overlay-modification)))))) + + +(defun yas/move-to-field (snippet field) + "Update SNIPPET to move to field FIELD. + +Also create some protection overlays" + (goto-char (yas/field-start field)) + (setf (yas/snippet-active-field snippet) field) + (yas/make-move-active-field-overlay snippet field) + (yas/make-move-field-protection-overlays snippet field) (overlay-put yas/active-field-overlay 'yas/field field)) (defun yas/prev-field () @@ -1129,7 +1171,9 @@ when the condition evaluated to non-nil." "Goto exit-marker of SNIPPET and commit the snippet. Cleaning up the snippet does not delete it!" (interactive) - (goto-char (yas/commit-snippet snippet))) + (goto-char (if (yas/snippet-exit snippet) + (yas/snippet-exit snippet) + (overlay-end (yas/snippet-control-overlay snippet))))) (defun yas/exterminate-snippets () "Remove all snippets in buffer" @@ -1149,8 +1193,7 @@ Return a buffer position where the point should be placed if exiting the snippet." (let ((control-overlay (yas/snippet-control-overlay snippet)) yas/snippet-beg - yas/snippet-end - exit (point)) + yas/snippet-end) ;; ;; Save the end of the moribund snippet in case we need to revive it ;; its original expansion. @@ -1159,43 +1202,18 @@ exiting the snippet." (overlay-buffer control-overlay)) (setq yas/snippet-beg (overlay-start control-overlay)) (setq yas/snippet-end (overlay-end control-overlay)) - (delete-overlay control-overlay) - (narrow-to-region yas/snippet-beg yas/snippet-end)) + (delete-overlay control-overlay)) + + (let ((inhibit-modification-hooks t)) + (when yas/active-field-overlay + (delete-overlay yas/active-field-overlay)) + (when yas/field-protection-overlays + (mapcar #'delete-overlay yas/field-protection-overlays))) ;; Push an action for snippet revival ;; (push `(apply yas/snippet-revive ,yas/snippet-beg ,yas/snippet-end ,snippet) buffer-undo-list) - - ;; Trash those overlays! - ;; - (let ((inhibit-modification-hooks t)) - (when yas/active-field-overlay - (delete-overlay yas/active-field-overlay)) - ;; Delete all the text under the overlays - (dolist (field (yas/snippet-fields snippet)) - (dolist (mirror (yas/field-mirrors field)) - (let ((mirror-overlay (yas/mirror-overlay mirror))) - (when (and mirror-overlay - (overlay-buffer mirror-overlay)) - (goto-char (overlay-start mirror-overlay)) - (yas/delete-overlay-region mirror-overlay) - (insert (yas/apply-transform mirror field))))) - (let* ((overlay-pair (yas/field-overlay-pair field)) - (before (car overlay-pair)) - (after (cdr overlay-pair))) - (dolist (ov (list before after)) - (when (and ov - (overlay-buffer ov)) - (yas/delete-overlay-region ov))))) - ;; Take care of the exit marker - ;; - (cond ((and (yas/snippet-exit snippet) - (overlay-buffer (yas/snippet-exit snippet))) - (setq exit (overlay-start (yas/snippet-exit snippet))) - (yas/delete-overlay-region (yas/snippet-exit snippet))) - (t - (setq exit (point-max))))) ;; XXX: `yas/after-exit-snippet-hook' should be run with ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not @@ -1203,33 +1221,32 @@ exiting the snippet." ;; disappeared, which sometimes happens when the snippet's messed ;; up... ;; - (run-hooks 'yas/after-exit-snippet-hook) - (widen) - exit)) + (run-hooks 'yas/after-exit-snippet-hook))) (defun yas/check-commit-snippet () "Checks if point exited the currently active field of the snippet, if so cleans up the whole snippet up." - (unless (yas/undo-in-progress) - (let* ((snippet (first (yas/snippets-at-point)))) - (cond ((null snippet) + (let* ((snippet (first (yas/snippets-at-point)))) + (cond ((null snippet) + ;; + ;; No snippet at point, cleanup *all* snippets + ;; + (yas/exterminate-snippets)) + ((let ((beg (overlay-start yas/active-field-overlay)) + (end (overlay-end yas/active-field-overlay))) + (or (not end) + (not beg) + (> (point) end) + (< (point) beg))) + ;; A snippet exitss at point, but point left the currently + ;; active field overlay + (yas/commit-snippet snippet)) + ( ;; + ;; Snippet at point, and point inside a snippet field, + ;; everything is normal ;; - ;; No snippet at point, cleanup *all* snippets - ;; - (yas/exterminate-snippets)) - ((let ((beg (overlay-start yas/active-field-overlay)) - (end (overlay-end yas/active-field-overlay))) - (or (> (point) end) - (< (point) beg))) - ;; A snippet exitss at point, but point left the currently - ;; active field overlay - (save-excursion (yas/commit-snippet snippet))) - ( ;; - ;; Snippet at point, and point inside a snippet field, - ;; everything is normal - ;; - t - nil))))) + t + nil)))) ;; ;; Pre and post command handlers @@ -1243,9 +1260,8 @@ snippet, if so cleans up the whole snippet up." (when snippet (yas/move-to-field snippet (or (yas/snippet-active-field snippet) (car (yas/snippet-fields snippet))))))) - (t - ;; (yas/check-commit-snippet) - ))) + ((not (yas/undo-in-progress)) + (yas/check-commit-snippet)))) ;; Debug functions. Use (or change) at will whenever needed. @@ -1263,7 +1279,7 @@ snippet, if so cleans up the whole snippet up." "ENABLED") (point-max))) (unless (eq buffer-undo-list t) - (princ (format "Undolist has %s elements. First 10 elements follow:\n" (length buffer-undo-list))) + (princ (format "Undpolist has %s elements. First 10 elements follow:\n" (length buffer-undo-list))) (let ((first-ten (subseq buffer-undo-list 0 19))) (dolist (undo-elem first-ten) (princ (format "%2s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 70)))))))) From 354580b8f79529eed8a007100c886a4b29e7697f Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Sun, 5 Jul 2009 22:28:21 +0000 Subject: [PATCH 28/75] * field protection problem solved, I guess * on to the transformations, escapes and indent --- yasnippet.el | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 547aebf..6c30009 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -74,6 +74,9 @@ current column if this variable is non-`nil'.") (define-key yas/keymap yas/next-field-key 'yas/next-field) (define-key yas/keymap yas/clear-field-key 'yas/clear-field) (define-key yas/keymap (kbd "S-TAB") 'yas/prev-field) +(define-key yas/keymap (kbd "") 'yas/prev-field) +(define-key yas/keymap (kbd "DEL") 'yas/prev-field) +(define-key yas/keymap (kbd "") 'yas/prev-field) (define-key yas/keymap (kbd "") 'yas/prev-field) (define-key yas/keymap (kbd "") 'yas/prev-field) (define-key yas/keymap (kbd "") 'yas/prev-field) @@ -535,7 +538,8 @@ the template of a snippet in the current snippet-table." (and yas/active-field-overlay (overlay-buffer yas/active-field-overlay) (overlay-get yas/active-field-overlay 'yas/field))))) - (delete-region (yas/field-start field) (yas/field-end field)))) + (let ((inhibit-modification-hooks t)) + (delete-region (yas/field-start field) (yas/field-end field))))) (defun yas/on-field-overlay-modification (overlay after? beg end &optional length) "To be written" @@ -1147,9 +1151,7 @@ when the condition evaluated to non-nil." (dolist (ov yas/field-protection-overlays) (overlay-put ov 'face 'yas/field-debug-face) ;; (overlay-put ov 'evaporate t) - (overlay-put ov 'modification-hooks '(yas/on-protection-overlay-modification)) - (overlay-put ov 'insert-in-front-hooks '(yas/on-protection-overlay-modification)) - (overlay-put ov 'insert-behind-hooks '(yas/on-protection-overlay-modification)))))) + (overlay-put ov 'modification-hooks '(yas/on-protection-overlay-modification)))))) (defun yas/move-to-field (snippet field) From b56563de6059b6074a71bc5a7b6142d985ff5daf Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 6 Jul 2009 10:08:13 +0000 Subject: [PATCH 29/75] * stacked expansion is tricky, but can be done * yas/minor-mode should be needed to expand snippets, this means hooks last while snippets last * will probably *not* fix this bug: ${1:nested{2:thing}} when "thing" is changed "nested"'s end marker doens't move --- yasnippet.el | 113 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 32 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 6c30009..156a3ef 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -72,10 +72,8 @@ current column if this variable is non-`nil'.") (defvar yas/keymap (make-sparse-keymap) "The keymap of snippet.") (define-key yas/keymap yas/next-field-key 'yas/next-field) -(define-key yas/keymap yas/clear-field-key 'yas/clear-field) +(define-key yas/keymap yas/clear-field-key 'yas/clear-field-or-delete-char) (define-key yas/keymap (kbd "S-TAB") 'yas/prev-field) -(define-key yas/keymap (kbd "") 'yas/prev-field) -(define-key yas/keymap (kbd "DEL") 'yas/prev-field) (define-key yas/keymap (kbd "") 'yas/prev-field) (define-key yas/keymap (kbd "") 'yas/prev-field) (define-key yas/keymap (kbd "") 'yas/prev-field) @@ -526,33 +524,47 @@ the template of a snippet in the current snippet-table." end nil t - nil))) + t))) (overlay-put overlay 'keymap yas/keymap) (overlay-put overlay 'yas/snippet snippet) (overlay-put overlay 'evaporate t) overlay)) -(defun yas/clear-field (&optional field) +(defun yas/clear-field-or-delete-char (&optional field) (interactive) (let ((field (or field (and yas/active-field-overlay (overlay-buffer yas/active-field-overlay) (overlay-get yas/active-field-overlay 'yas/field))))) - (let ((inhibit-modification-hooks t)) - (delete-region (yas/field-start field) (yas/field-end field))))) + (cond ((and field + (not (yas/field-modified-p field))) + (yas/clear-field field)) + (t + (call-interactively 'delete-char))))) + +(defun yas/clear-field (field) + (setf (yas/field-modified-p field) t) + (delete-region (yas/field-start field) (yas/field-end field))) (defun yas/on-field-overlay-modification (overlay after? beg end &optional length) - "To be written" - (cond ((and after? - (not (yas/undo-in-progress))) - (mapcar #'yas/update-mirrors (yas/snippets-at-point))) - (t - (let ((field (overlay-get yas/active-field-overlay 'yas/field))) - (when (and field - (not (or after? (yas/undo-in-progress))) - (not (yas/field-modified-p field))) - (setf (yas/field-modified-p field) t) - (yas/clear-field field)))))) + "Clears the field and updates mirrors, conditionally. + +Only clears the field if it hasn't been modified and it point it +at field start. This hook doesn't do anything if an undo is in +progress." + (unless (yas/undo-in-progress) + (cond (after? + (mapcar #'yas/update-mirrors (yas/snippets-at-point))) + (t + (let ((field (overlay-get yas/active-field-overlay 'yas/field))) + (when (and field + (not after?) + (not (yas/field-modified-p field)) + (eq (point) (if (markerp (yas/field-start field)) + (marker-position (yas/field-start field)) + (yas/field-start field)))) + (yas/clear-field field)) + (setf (yas/field-modified-p field) t)))))) (defun yas/on-protection-overlay-modification (overlay after? beg end &optional length) "To be written" @@ -573,6 +585,7 @@ will be deleted before inserting template." (let* ((key (buffer-substring-no-properties start end)) (length (- end start)) (column (current-column)) + (inhibit-modification-hooks t) snippet) (delete-char length) (save-restriction @@ -583,22 +596,21 @@ will be deleted before inserting template." (push (cons (point-min) (point-max)) buffer-undo-list) ;; Push an undo action (push `(apply yas/take-care-of-redo ,(point-min) ,(point-max) ,snippet) - buffer-undo-list)))) + buffer-undo-list)) + + + ;; if this is a stacked expansion update the other snippets at point + (mapcar #'yas/update-mirrors (rest (yas/snippets-at-point))))) (defun yas/take-care-of-redo (beg end snippet) - (let ((inhibit-modification-hooks t)) - (when yas/active-field-overlay - (delete-overlay yas/active-field-overlay)) - (when yas/field-protection-overlays - (mapcar #'delete-overlay yas/field-protection-overlays))) - (push `(apply yas/snippet-revive ,beg ,end ,snippet) - buffer-undo-list)) + (yas/commit-snippet snippet)) (defun yas/snippet-revive (beg end snippet) (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay beg end)) (overlay-put (yas/snippet-control-overlay snippet) 'yas/snippet snippet) (yas/move-to-field snippet (or (yas/snippet-active-field snippet) (car (yas/snippet-fields snippet)))) + (yas/points-to-markers snippet) (push `(apply yas/take-care-of-redo ,beg ,end ,snippet) buffer-undo-list)) @@ -1098,10 +1110,12 @@ when the condition evaluated to non-nil." (yas/field-parent-field field))) (defun yas/snippets-at-point () - (remove nil (mapcar #'(lambda (ov) - (overlay-get ov 'yas/snippet)) - (overlays-at (point))))) - + (sort + (remove nil (mapcar #'(lambda (ov) + (overlay-get ov 'yas/snippet)) + (overlays-at (point)))) + #'(lambda (s1 s2) + (>= (yas/snippet-id s2) (yas/snippet-id s1))))) (defun yas/next-field (&optional arg) "Navigate to next field. If there's none, exit the snippet." @@ -1187,7 +1201,39 @@ up the snippet does not delete it!" (defun yas/delete-overlay-region (overlay) (delete-region (overlay-start overlay) (overlay-end overlay))) -(defun yas/commit-snippet (snippet) +(defun yas/markers-to-points (snippet) + "Convert all markers in SNIPPET to simple integer buffer positions." + (dolist (field (yas/snippet-fields snippet)) + (let ((start (marker-position (yas/field-start field))) + (end (marker-position (yas/field-end field)))) + (set-marker (yas/field-start field) nil) + (set-marker (yas/field-end field) nil) + (setf (yas/field-start field) start) + (setf (yas/field-end field) end)) + (dolist (mirror (yas/field-mirrors field)) + (let ((start (marker-position (yas/mirror-start mirror))) + (end (marker-position (yas/mirror-end mirror)))) + (set-marker (yas/mirror-start mirror) nil) + (set-marker (yas/mirror-end mirror) nil) + (setf (yas/mirror-start mirror) start) + (setf (yas/mirror-end mirror) end)))) + (when (yas/snippet-exit snippet) + (let ((exit (marker-position (yas/snippet-exit snippet)))) + (set-marker (yas/snippet-exit snippet) nil) + (setf (yas/snippet-exit snippet) exit)))) + +(defun yas/points-to-markers (snippet) + "Convert all simple integer buffer positions in SNIPPET to markers" + (dolist (field (yas/snippet-fields snippet)) + (setf (yas/field-start field) (set-marker (make-marker) (yas/field-start field))) + (setf (yas/field-end field) (set-marker (make-marker) (yas/field-end field))) + (dolist (mirror (yas/field-mirrors field)) + (setf (yas/mirror-start mirror) (set-marker (make-marker) (yas/mirror-start mirror))) + (setf (yas/mirror-end mirror) (set-marker (make-marker) (yas/mirror-end mirror))))) + (when (yas/snippet-exit snippet) + (setf (yas/snippet-exit snippet) (set-marker (make-marker) (yas/snippet-exit snippet))))) + +(defun yas/commit-snippet (snippet &optional no-hooks) "Commit SNIPPET, but leave point as it is. This renders the snippet as ordinary text. @@ -1212,6 +1258,8 @@ exiting the snippet." (when yas/field-protection-overlays (mapcar #'delete-overlay yas/field-protection-overlays))) + (yas/markers-to-points snippet) + ;; Push an action for snippet revival ;; (push `(apply yas/snippet-revive ,yas/snippet-beg ,yas/snippet-end ,snippet) @@ -1223,7 +1271,7 @@ exiting the snippet." ;; disappeared, which sometimes happens when the snippet's messed ;; up... ;; - (run-hooks 'yas/after-exit-snippet-hook))) + (unless no-hooks (run-hooks 'yas/after-exit-snippet-hook)))) (defun yas/check-commit-snippet () "Checks if point exited the currently active field of the @@ -1303,6 +1351,7 @@ snippet, if so cleans up the whole snippet up." (erase-buffer) (setq buffer-undo-list nil) (html-mode) + (yas/minor-mode) (let ((abbrev)) ;; (if (require 'ido nil t) ;; (setq abbrev (ido-completing-read "Snippet abbrev: " '("crazy" "prip" "prop"))) From 6354579eeddd3d58c5f8eb2ca049d3af83de0d0a Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 6 Jul 2009 14:53:01 +0000 Subject: [PATCH 30/75] * stacked edition not quite perfect yet, otherwise everything looking good --- yasnippet.el | 695 ++++++++++++++++++++++++++------------------------- 1 file changed, 355 insertions(+), 340 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 156a3ef..5c94f9d 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -288,7 +288,6 @@ set to t." (interactive) (yas/minor-mode -1)) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Internal Structs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -299,66 +298,11 @@ set to t." name condition) -(defvar yas/active-field-overlay nil - "Overlays the currently active field") - -(defvar yas/field-protection-overlays nil - "Two overlays protect the current active field ") - -(make-variable-buffer-local 'yas/active-field-overlay) -(make-variable-buffer-local 'yas/field-protection-overlays) - -(defstruct (yas/snippet (:constructor yas/make-snippet ())) - "A snippet. - -..." - (fields '()) - (exit nil) - (id (yas/snippet-next-id) :read-only t) - (control-overlay nil) - active-field) - -(defstruct (yas/field (:constructor yas/make-field (number start end parent-field))) - "A field." - number - start end - parent-field - (mirrors '()) - (next nil) - (prev nil) - (transform nil) - (modified-p nil)) - -(defstruct (yas/mirror (:constructor yas/make-mirror (start end transform))) - "A mirror." - start end - (transform nil)) - (defstruct (yas/snippet-table (:constructor yas/make-snippet-table ())) "A table to store snippets for a perticular mode." (hash (make-hash-table :test 'equal)) (parent nil)) -(defun yas/snippet-find-field (snippet number) - (find-if #'(lambda (field) - (eq number (yas/field-number field))) - (yas/snippet-fields snippet))) - -(defun yas/snippet-field-compare (field1 field2) - "Compare two fields. The field with a number is sorted first. -If they both have a number, compare through the number. If neither -have, compare through the field's start point" - (let ((n1 (yas/field-number field1)) - (n2 (yas/field-number field2))) - (if n1 - (if n2 - (< n1 n2) - t) - (if n2 - nil - (< (yas/field-start field1) - (yas/field-start field2)))))) - (defun yas/template-condition-predicate (condition) (condition-case err (save-excursion @@ -444,29 +388,6 @@ a list of modes like this to help the judgement." (error (format "(error in elisp evaluation: %s)" (error-message-string err))))) -(defun yas/apply-transform (field-or-mirror field) - "Calculate the value of the field. If there's a transform -for this field, apply it. Otherwise, the value is returned -unmodified. - -TODO: I really dont think field transforms are easily done, but oh -well - -" - (let ((text (yas/field-text-for-display field)) - (transform (if (yas/mirror-p field-or-mirror) - (yas/mirror-transform field-or-mirror) - (yas/field-transform field-or-mirror)))) - (if transform - (yas/eval-string transform) - text))) - -(defsubst yas/replace-all (from to) - "Replace all occurance from FROM to TO." - (goto-char (point-min)) - (while (search-forward from nil t) - (replace-match to t t))) - (defun yas/snippet-table (mode) "Get the snippet table corresponding to MODE." (let ((table (gethash mode yas/snippet-tables))) @@ -474,6 +395,7 @@ well (setq table (yas/make-snippet-table)) (puthash mode table yas/snippet-tables)) table)) + (defsubst yas/current-snippet-table () "Get the snippet table for current major-mode." (yas/snippet-table major-mode)) @@ -510,228 +432,6 @@ the template of a snippet in the current snippet-table." start end))) -(defun yas/field-text-for-display (field) - "Return the propertized display text for field FIELD. " - (buffer-substring (yas/field-start field) (yas/field-end field))) - -(defun yas/undo-in-progress () - (or undo-in-progress - (eq this-command 'undo))) - -(defun yas/make-control-overlay (start end) - "..." - (let ((overlay (make-overlay start - end - nil - t - t))) - (overlay-put overlay 'keymap yas/keymap) - (overlay-put overlay 'yas/snippet snippet) - (overlay-put overlay 'evaporate t) - overlay)) - -(defun yas/clear-field-or-delete-char (&optional field) - (interactive) - (let ((field (or field - (and yas/active-field-overlay - (overlay-buffer yas/active-field-overlay) - (overlay-get yas/active-field-overlay 'yas/field))))) - (cond ((and field - (not (yas/field-modified-p field))) - (yas/clear-field field)) - (t - (call-interactively 'delete-char))))) - -(defun yas/clear-field (field) - (setf (yas/field-modified-p field) t) - (delete-region (yas/field-start field) (yas/field-end field))) - -(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 it point it -at field start. This hook doesn't do anything if an undo is in -progress." - (unless (yas/undo-in-progress) - (cond (after? - (mapcar #'yas/update-mirrors (yas/snippets-at-point))) - (t - (let ((field (overlay-get yas/active-field-overlay 'yas/field))) - (when (and field - (not after?) - (not (yas/field-modified-p field)) - (eq (point) (if (markerp (yas/field-start field)) - (marker-position (yas/field-start field)) - (yas/field-start field)))) - (yas/clear-field field)) - (setf (yas/field-modified-p field) t)))))) - -(defun yas/on-protection-overlay-modification (overlay after? beg end &optional length) - "To be written" - (cond ((not (or after? - (yas/undo-in-progress))) - (let ((snippet (car (yas/snippets-at-point)))) - (when snippet - (yas/commit-snippet snippet) - (call-interactively this-command) - (error "Snippet exited")))))) - -(defun yas/expand-snippet (start end template) - "Expand snippet at current point. Text between START and END -will be deleted before inserting template." - (run-hooks 'yas/before-expand-snippet-hook) - (goto-char start) - - (let* ((key (buffer-substring-no-properties start end)) - (length (- end start)) - (column (current-column)) - (inhibit-modification-hooks t) - snippet) - (delete-char length) - (save-restriction - (let ((buffer-undo-list t)) - (narrow-to-region start start) - (insert template) - (setq snippet (yas/snippet-create (point-min) (point-max)))) - (push (cons (point-min) (point-max)) buffer-undo-list) - ;; Push an undo action - (push `(apply yas/take-care-of-redo ,(point-min) ,(point-max) ,snippet) - buffer-undo-list)) - - - ;; if this is a stacked expansion update the other snippets at point - (mapcar #'yas/update-mirrors (rest (yas/snippets-at-point))))) - -(defun yas/take-care-of-redo (beg end snippet) - (yas/commit-snippet snippet)) - -(defun yas/snippet-revive (beg end snippet) - (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay beg end)) - (overlay-put (yas/snippet-control-overlay snippet) 'yas/snippet snippet) - (yas/move-to-field snippet (or (yas/snippet-active-field snippet) - (car (yas/snippet-fields snippet)))) - (yas/points-to-markers snippet) - (push `(apply yas/take-care-of-redo ,beg ,end ,snippet) - buffer-undo-list)) - -(defun yas/snippet-create (begin end) - (let ((snippet (yas/make-snippet))) - (goto-char begin) - (yas/snippet-parse-create snippet) - - ;; Sort and link each field - (yas/snippet-sort-link-fields snippet) - - ;; Update the mirrors for the first time - (yas/update-mirrors snippet) - - ;; Create keymap overlay for snippet - (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay (point-min) (point-max))) - - ;; Move to end - (goto-char (point-max)) - - ;; Place the cursor at a proper place - (let* ((first-field (car (yas/snippet-fields snippet))) - overlay) - (cond (first-field - ;; Move to the new field, setting up properties of the - ;; wandering active field overlay. - (yas/move-to-field snippet first-field)) - (t - ;; No fields, quite a simple snippet I suppose - (yas/exit-snippet snippet)))) - snippet)) - -(defun yas/snippet-sort-link-fields (snippet) - (setf (yas/snippet-fields snippet) - (sort (yas/snippet-fields snippet) - '(lambda (field1 field2) - (yas/snippet-field-compare field1 field2)))) - (let ((prev nil)) - (dolist (field (yas/snippet-fields snippet)) - (setf (yas/field-prev field) prev) - (when prev - (setf (yas/field-next prev) field)) - (setq prev field)))) - -(defun yas/snippet-parse-create (snippet) - "Parse a recently inserted snippet template, creating all -necessary fields. - -Allows nested placeholder in the style of Textmate." - (let ((parse-start (point))) - (yas/field-parse-create snippet) - (goto-char parse-start) - (yas/transform-mirror-parse-create snippet) - (goto-char parse-start) - (yas/simple-mirror-parse-create snippet))) - -(defun yas/field-parse-create (snippet &optional parent-field) - (while (re-search-forward yas/field-regexp nil t) - (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) - (number (string-to-number (match-string-no-properties 1))) - (brand-new-field (and real-match-end-0 - (save-match-data (not (string-match "$(" (match-string-no-properties 2)))) - number - (not (zerop number)) - (yas/make-field number - (set-marker (make-marker) (match-beginning 2)) - (set-marker (make-marker) (1- real-match-end-0)) - parent-field)))) - (when brand-new-field - (delete-region (1- real-match-end-0) real-match-end-0) - (delete-region (match-beginning 0) (match-beginning 2)) - (push brand-new-field (yas/snippet-fields snippet)) - (save-excursion - (save-restriction - (narrow-to-region (yas/field-start brand-new-field) (yas/field-end brand-new-field)) - (goto-char (point-min)) - (yas/field-parse-create snippet brand-new-field))))))) - -(defun yas/transform-mirror-parse-create (snippet) - (while (re-search-forward yas/transform-mirror-regexp nil t) - (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) - (number (string-to-number (match-string-no-properties 1))) - (field (and number - (not (zerop number)) - (yas/snippet-find-field snippet number)))) - (when (and real-match-end-0 field) - (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) - (set-marker (make-marker) (match-beginning 0)) - (buffer-substring-no-properties (match-beginning 2) - (1- real-match-end-0))) - (yas/field-mirrors field)) - (delete-region (match-beginning 0) real-match-end-0))))) - -(defun yas/simple-mirror-parse-create (snippet) - (while (re-search-forward yas/simple-mirror-regexp nil t) - (let ((number (string-to-number (match-string-no-properties 1)))) - (cond ((zerop number) - (setf (yas/snippet-exit snippet) - (set-marker (make-marker) (match-beginning 0))) - (delete-region (match-beginning 0) (match-end 0))) - (t - (let ((field (yas/snippet-find-field snippet number))) - (when field - (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) - (set-marker (make-marker) (match-beginning 0)) - nil) - (yas/field-mirrors field)) - (delete-region (match-beginning 0) (match-end 0))))))))) - -(defun yas/update-mirrors (snippet) - (save-excursion - (dolist (field (yas/snippet-fields snippet)) - (dolist (mirror (yas/field-mirrors field)) - (yas/mirror-update-display mirror field))))) - -(defun yas/mirror-update-display (mirror field) - (goto-char (yas/mirror-start mirror)) - (delete-region (yas/mirror-start mirror) (yas/mirror-end mirror)) - (insert (yas/apply-transform mirror field)) - (set-marker (yas/mirror-end mirror) (point))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Template-related and snippet loading functions @@ -822,9 +522,11 @@ t is returned simply." "Show a popup menu listing templates to let the user select one." (car (x-popup-menu (yas/point-to-coord) (yas/fake-keymap-for-popup templates)))) + (defun yas/text-popup-for-template (templates) "Can't display popup menu in text mode. Just select the first one." (yas/template-content (cdar templates))) + (defun yas/dropdown-list-popup-for-template (templates) "Use dropdown-list.el to popup for templates. Better than the default \"select first\" behavior of `yas/text-popup-for-template'. @@ -946,13 +648,14 @@ all the parameters: (save-buffer)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; User level functions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; User level functions + (defun yas/about () (interactive) (message (concat "yasnippet (version " yas/version ") -- pluskid "))) + (defun yas/reload-all () "Reload all snippets." (interactive) @@ -1061,7 +764,6 @@ when the condition evaluated to non-nil." (yas/define-snippets mode (list (list key template name condition)))) - (defun yas/hippie-try-expand (first-time?) "Integrate with hippie expand. Just put this function in `hippie-expand-try-functions-list'." @@ -1104,18 +806,103 @@ when the condition evaluated to non-nil." (when (commandp command) (call-interactively command)))))))))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Snippet expansion and field managment + +(defvar yas/active-field-overlay nil + "Overlays the currently active field") + +(defvar yas/field-protection-overlays nil + "Two overlays protect the current active field ") + +(make-variable-buffer-local 'yas/active-field-overlay) +(make-variable-buffer-local 'yas/field-protection-overlays) + +(defstruct (yas/snippet (:constructor yas/make-snippet ())) + "A snippet. + +..." + (fields '()) + (exit nil) + (id (yas/snippet-next-id) :read-only t) + (control-overlay nil) + active-field) + +(defstruct (yas/field (:constructor yas/make-field (number start end parent-field))) + "A field." + number + start end + parent-field + (mirrors '()) + (next nil) + (prev nil) + (transform nil) + (modified-p nil)) + +(defstruct (yas/mirror (:constructor yas/make-mirror (start end transform))) + "A mirror." + start end + (transform nil)) + +(defun yas/apply-transform (field-or-mirror field) + "Calculate the value of the field. If there's a transform +for this field, apply it. Otherwise, the value is returned +unmodified. + +TODO: I really dont think field transforms are easily done, but oh +well + +" + (let ((text (yas/field-text-for-display field)) + (transform (if (yas/mirror-p field-or-mirror) + (yas/mirror-transform field-or-mirror) + (yas/field-transform field-or-mirror)))) + (if transform + (yas/eval-string transform) + text))) + +(defsubst yas/replace-all (from to) + "Replace all occurance from FROM to TO." + (goto-char (point-min)) + (while (search-forward from nil t) + (replace-match to t t))) + +(defun yas/snippet-find-field (snippet number) + (find-if #'(lambda (field) + (eq number (yas/field-number field))) + (yas/snippet-fields snippet))) + +(defun yas/snippet-field-compare (field1 field2) + "Compare two fields. The field with a number is sorted first. +If they both have a number, compare through the number. If neither +have, compare through the field's start point" + (let ((n1 (yas/field-number field1)) + (n2 (yas/field-number field2))) + (if n1 + (if n2 + (< n1 n2) + t) + (if n2 + nil + (< (yas/field-start field1) + (yas/field-start field2)))))) + (defun yas/field-probably-deleted-p (field) "Guess if FIELD was deleted because of his parent-field" (and (zerop (- (yas/field-start field) (yas/field-end field))) (yas/field-parent-field field))) -(defun yas/snippets-at-point () +(defun yas/snippets-at-point (&optional all-snippets) + "Return a sorted list of snippets at point, most recently +inserted first." (sort (remove nil (mapcar #'(lambda (ov) (overlay-get ov 'yas/snippet)) - (overlays-at (point)))) + (if all-snippets + (overlays-in (point-min) (point-max)) + (overlays-at (point))))) #'(lambda (s1 s2) - (>= (yas/snippet-id s2) (yas/snippet-id s1))))) + (<= (yas/snippet-id s2) (yas/snippet-id s1))))) (defun yas/next-field (&optional arg) "Navigate to next field. If there's none, exit the snippet." @@ -1167,7 +954,6 @@ when the condition evaluated to non-nil." ;; (overlay-put ov 'evaporate t) (overlay-put ov 'modification-hooks '(yas/on-protection-overlay-modification)))))) - (defun yas/move-to-field (snippet field) "Update SNIPPET to move to field FIELD. @@ -1191,13 +977,6 @@ up the snippet does not delete it!" (yas/snippet-exit snippet) (overlay-end (yas/snippet-control-overlay snippet))))) -(defun yas/exterminate-snippets () - "Remove all snippets in buffer" - (interactive) - (mapcar #'yas/commit-snippet (remove nil (mapcar #'(lambda (ov) - (overlay-get ov 'yas/snippet)) - (overlays-in (point-min) (point-max)))))) - (defun yas/delete-overlay-region (overlay) (delete-region (overlay-start overlay) (overlay-end overlay))) @@ -1258,7 +1037,7 @@ exiting the snippet." (when yas/field-protection-overlays (mapcar #'delete-overlay yas/field-protection-overlays))) - (yas/markers-to-points snippet) + ;; (if yas/allow-buggy-redo (yas/points-to-markers snippet)) ;; Push an action for snippet revival ;; @@ -1276,31 +1055,25 @@ exiting the snippet." (defun yas/check-commit-snippet () "Checks if point exited the currently active field of the snippet, if so cleans up the whole snippet up." - (let* ((snippet (first (yas/snippets-at-point)))) - (cond ((null snippet) - ;; - ;; No snippet at point, cleanup *all* snippets - ;; - (yas/exterminate-snippets)) - ((let ((beg (overlay-start yas/active-field-overlay)) - (end (overlay-end yas/active-field-overlay))) - (or (not end) - (not beg) - (> (point) end) - (< (point) beg))) - ;; A snippet exitss at point, but point left the currently - ;; active field overlay - (yas/commit-snippet snippet)) - ( ;; - ;; Snippet at point, and point inside a snippet field, - ;; everything is normal - ;; - t - nil)))) + (let* ((snippets (yas/snippets-at-point 'all-snippets))) + (dolist (snippet snippets) + ;; TODO: handle nested field exceptions, smaller, more nested + ;; find should come up earlier as `containing-field's + (let ((containing-field (find-if #'yas/field-contains-point-p (reverse (yas/snippet-fields snippet))))) + (cond ((not containing-field) + (yas/commit-snippet snippet)) + ((and containing-field + (or (not yas/active-field-overlay) + (not (overlay-buffer yas/active-field-overlay)))) + (save-excursion + (yas/move-to-field snippet containing-field))) + (t + nil)))))) + +(defun yas/field-contains-point-p (field) + (and (>= (point) (yas/field-start field)) + (< (point) (yas/field-end field)))) -;; -;; Pre and post command handlers -;; (defun yas/pre-command-handler () ) @@ -1313,6 +1086,248 @@ snippet, if so cleans up the whole snippet up." ((not (yas/undo-in-progress)) (yas/check-commit-snippet)))) +(defun yas/field-text-for-display (field) + "Return the propertized display text for field FIELD. " + (buffer-substring (yas/field-start field) (yas/field-end field))) + +(defun yas/undo-in-progress () + (or undo-in-progress + (eq this-command 'undo))) + +(defun yas/make-control-overlay (start end) + "..." + (let ((overlay (make-overlay start + end + nil + t + t))) + (overlay-put overlay 'keymap yas/keymap) + (overlay-put overlay 'yas/snippet snippet) + (overlay-put overlay 'evaporate t) + overlay)) + +(defun yas/clear-field-or-delete-char (&optional field) + (interactive) + (let ((field (or field + (and yas/active-field-overlay + (overlay-buffer yas/active-field-overlay) + (overlay-get yas/active-field-overlay 'yas/field))))) + (cond ((and field + (not (yas/field-modified-p field))) + (yas/clear-field field)) + (t + (call-interactively 'delete-char))))) + +(defun yas/clear-field (field) + "Deletes the region of FIELD and sets it modified state to t" + (setf (yas/field-modified-p field) t) + (delete-region (yas/field-start field) (yas/field-end field))) + +(defun yas/advance-field-and-parents-maybe (field end) + "Advance FIELDs end-marker to END and recurse for parent fields + +This is needed since markers don't \"rear-advance\" like overlays" + (when (< (yas/field-end field) end) + (set-marker (yas/field-end field) end) + (when (yas/field-parent-field field) + (yas/advance-field-and-parents-maybe (yas/field-parent-field field) end)))) + +(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 it point it +at field start. This hook doesn't do anything if an undo is in +progress." + (unless (yas/undo-in-progress) + (let ((field (overlay-get yas/active-field-overlay 'yas/field))) + (cond (after? + (yas/advance-field-and-parents-maybe field (overlay-end overlay)) + (mapcar #'yas/update-mirrors (yas/snippets-at-point))) + (field + (when (and (not after?) + (not (yas/field-modified-p field)) + (eq (point) (if (markerp (yas/field-start field)) + (marker-position (yas/field-start field)) + (yas/field-start field)))) + (yas/clear-field field)) + (setf (yas/field-modified-p field) t)))))) + +(defun yas/on-protection-overlay-modification (overlay after? beg end &optional length) + "To be written" + (cond ((not (or after? + (yas/undo-in-progress))) + (let ((snippet (car (yas/snippets-at-point)))) + (when snippet + (yas/commit-snippet snippet) + (call-interactively this-command) + (error "Snippet exited")))))) + +(defun yas/expand-snippet (start end template) + "Expand snippet at current point. Text between START and END +will be deleted before inserting template." + (run-hooks 'yas/before-expand-snippet-hook) + (goto-char start) + + (let* ((key (buffer-substring-no-properties start end)) + (length (- end start)) + (column (current-column)) + (inhibit-modification-hooks t) + snippet) + ;; Narrow the region down to the template, shoosh the + ;; buffer-undo-list, then come out as if all that happened was a + ;; normal, undo-recorded, insertion. + ;; + (save-restriction + (let ((buffer-undo-list t) + (template-start (+ start length))) + (narrow-to-region template-start template-start) + (insert template) + (setq snippet (yas/snippet-create (point-min) (point-max)))) + (push (cons (point-min) (point-max)) buffer-undo-list)) + ;; Delete the trigger key + ;; + (goto-char start) + (delete-char length) + ;; Move to the first of fields, or exit the snippet to its exit + ;; point + ;; + (let ((first-field (car (yas/snippet-fields snippet)))) + (cond (first-field + (yas/move-to-field snippet first-field)) + (t + (yas/exit-snippet snippet)))) + ;; Push an undo action + (let ((start (overlay-start (yas/snippet-control-overlay snippet))) + (end (overlay-end (yas/snippet-control-overlay snippet)))) + (push `(apply yas/take-care-of-redo ,start ,end ,snippet) + buffer-undo-list)) + + ;; if this is a stacked expansion update the other snippets at point + (mapcar #'yas/update-mirrors (rest (yas/snippets-at-point))))) + +(defun yas/take-care-of-redo (beg end snippet) + (yas/commit-snippet snippet)) + +(defun yas/snippet-revive (beg end snippet) + (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay beg end)) + (overlay-put (yas/snippet-control-overlay snippet) 'yas/snippet snippet) + (yas/move-to-field snippet (or (yas/snippet-active-field snippet) + (car (yas/snippet-fields snippet)))) + ;; (if yas/allow-buggy-redo (yas/points-to-markers snippet)) + (push `(apply yas/take-care-of-redo ,beg ,end ,snippet) + buffer-undo-list)) + +(defun yas/snippet-create (begin end) + (let ((snippet (yas/make-snippet))) + (goto-char begin) + (yas/snippet-parse-create snippet) + + ;; Sort and link each field + (yas/snippet-sort-link-fields snippet) + + ;; Update the mirrors for the first time + (yas/update-mirrors snippet) + + ;; Create keymap overlay for snippet + (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay (point-min) (point-max))) + + ;; Move to end + (goto-char (point-max)) + + + snippet)) + +(defun yas/snippet-sort-link-fields (snippet) + (setf (yas/snippet-fields snippet) + (sort (yas/snippet-fields snippet) + '(lambda (field1 field2) + (yas/snippet-field-compare field1 field2)))) + (let ((prev nil)) + (dolist (field (yas/snippet-fields snippet)) + (setf (yas/field-prev field) prev) + (when prev + (setf (yas/field-next prev) field)) + (setq prev field)))) + +(defun yas/snippet-parse-create (snippet) + "Parse a recently inserted snippet template, creating all +necessary fields. + +Allows nested placeholder in the style of Textmate." + (let ((parse-start (point))) + (yas/field-parse-create snippet) + (goto-char parse-start) + (yas/transform-mirror-parse-create snippet) + (goto-char parse-start) + (yas/simple-mirror-parse-create snippet))) + +(defun yas/field-parse-create (snippet &optional parent-field) + (while (re-search-forward yas/field-regexp nil t) + (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) + (number (string-to-number (match-string-no-properties 1))) + (brand-new-field (and real-match-end-0 + (save-match-data (not (string-match "$(" (match-string-no-properties 2)))) + number + (not (zerop number)) + (yas/make-field number + (set-marker (make-marker) (match-beginning 2)) + (set-marker (make-marker) (1- real-match-end-0)) + parent-field)))) + (when brand-new-field + (delete-region (1- real-match-end-0) real-match-end-0) + (delete-region (match-beginning 0) (match-beginning 2)) + (push brand-new-field (yas/snippet-fields snippet)) + (save-excursion + (save-restriction + (narrow-to-region (yas/field-start brand-new-field) (yas/field-end brand-new-field)) + (goto-char (point-min)) + (yas/field-parse-create snippet brand-new-field))))))) + +(defun yas/transform-mirror-parse-create (snippet) + (while (re-search-forward yas/transform-mirror-regexp nil t) + (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) + (number (string-to-number (match-string-no-properties 1))) + (field (and number + (not (zerop number)) + (yas/snippet-find-field snippet number)))) + (when (and real-match-end-0 field) + (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) + (set-marker (make-marker) (match-beginning 0)) + (buffer-substring-no-properties (match-beginning 2) + (1- real-match-end-0))) + (yas/field-mirrors field)) + (delete-region (match-beginning 0) real-match-end-0))))) + +(defun yas/simple-mirror-parse-create (snippet) + (while (re-search-forward yas/simple-mirror-regexp nil t) + (let ((number (string-to-number (match-string-no-properties 1)))) + (cond ((zerop number) + (setf (yas/snippet-exit snippet) + (set-marker (make-marker) (match-beginning 0))) + (delete-region (match-beginning 0) (match-end 0))) + (t + (let ((field (yas/snippet-find-field snippet number))) + (when field + (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) + (set-marker (make-marker) (match-beginning 0)) + nil) + (yas/field-mirrors field)) + (delete-region (match-beginning 0) (match-end 0))))))))) + +(defun yas/update-mirrors (snippet) + (save-excursion + (dolist (field (yas/snippet-fields snippet)) + (dolist (mirror (yas/field-mirrors field)) + (yas/mirror-update-display mirror field))))) + +(defun yas/mirror-update-display (mirror field) + (goto-char (yas/mirror-start mirror)) + (delete-region (yas/mirror-start mirror) (yas/mirror-end mirror)) + (insert (yas/apply-transform mirror field)) + (set-marker (yas/mirror-end mirror) (point))) + + + ;; Debug functions. Use (or change) at will whenever needed. (defun yas/debug-some-vars () From 5f5e23c829d93756fca20565ed141128bd83dc41 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 6 Jul 2009 21:38:41 +0000 Subject: [PATCH 31/75] a minor change, having trouble debugging on mac --- snippets/text-mode/html-mode/dov | 3 +++ snippets/text-mode/objc-mode/prop | 3 ++- yasnippet.el | 13 ++++++------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/snippets/text-mode/html-mode/dov b/snippets/text-mode/html-mode/dov index e444aee..e8341ea 100644 --- a/snippets/text-mode/html-mode/dov +++ b/snippets/text-mode/html-mode/dov @@ -1,5 +1,8 @@ #name : ... # -- +a mirror up here $3 + + $0 diff --git a/snippets/text-mode/objc-mode/prop b/snippets/text-mode/objc-mode/prop index 4d585db..f17ae13 100644 --- a/snippets/text-mode/objc-mode/prop +++ b/snippets/text-mode/objc-mode/prop @@ -1,8 +1,9 @@ #name : foo { ... } ; setFoo { ... } # -- -- (${1:id})${2:foo} +- ${1:id} ${2:foo and its ${3:nested} shit} { return $2; + // dont forget we have $3 } - (void)set${2:$(capitalize text)}:($1)aValue diff --git a/yasnippet.el b/yasnippet.el index 5c94f9d..f0668ff 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -1059,20 +1059,20 @@ snippet, if so cleans up the whole snippet up." (dolist (snippet snippets) ;; TODO: handle nested field exceptions, smaller, more nested ;; find should come up earlier as `containing-field's - (let ((containing-field (find-if #'yas/field-contains-point-p (reverse (yas/snippet-fields snippet))))) - (cond ((not containing-field) + (let ((active-field (yas/snippet-active-field snippet))) + (cond ((not (and active-field (yas/field-contains-point-p active-field))) (yas/commit-snippet snippet)) - ((and containing-field + ((and active-field (or (not yas/active-field-overlay) (not (overlay-buffer yas/active-field-overlay)))) (save-excursion - (yas/move-to-field snippet containing-field))) + (yas/move-to-field snippet active-field))) (t nil)))))) (defun yas/field-contains-point-p (field) (and (>= (point) (yas/field-start field)) - (< (point) (yas/field-end field)))) + (<= (point) (yas/field-end field)))) (defun yas/pre-command-handler () ) @@ -1327,8 +1327,8 @@ Allows nested placeholder in the style of Textmate." (set-marker (yas/mirror-end mirror) (point))) - ;; Debug functions. Use (or change) at will whenever needed. +;; (defun yas/debug-some-vars () (interactive) @@ -1362,7 +1362,6 @@ Allows nested placeholder in the style of Textmate." (yas/load-directory "~/Source/yasnippet/snippets/") ;;(kill-buffer (get-buffer "*YAS TEST*")) (set-buffer (switch-to-buffer "*YAS TEST*")) - (yas/exterminate-snippets) (erase-buffer) (setq buffer-undo-list nil) (html-mode) From e0308fa495a1a73d9485104325227397db158966 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Tue, 7 Jul 2009 16:04:03 +0000 Subject: [PATCH 32/75] * stacked expansion tricky tricky, but is working and undo/redo seems reasonablt OK * will try to let the modification hooks do the previous-field advancing but I really don't know if its possible * also remember to fix the "navigate to deleted field on undo" bug --- yasnippet.el | 64 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index f0668ff..3e752d2 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -826,7 +826,8 @@ when the condition evaluated to non-nil." (exit nil) (id (yas/snippet-next-id) :read-only t) (control-overlay nil) - active-field) + active-field + previous-active-field) (defstruct (yas/field (:constructor yas/make-field (number start end parent-field))) "A field." @@ -1037,10 +1038,17 @@ exiting the snippet." (when yas/field-protection-overlays (mapcar #'delete-overlay yas/field-protection-overlays))) - ;; (if yas/allow-buggy-redo (yas/points-to-markers snippet)) + ;; For stacked expansion: if the original expansion took place + ;; from a field, make sure we advance it here at least to + ;; `yas/snippet-end'... + ;; + (let ((previous-field (yas/snippet-previous-active-field snippet))) + (when previous-field + (yas/advance-field-and-parents-maybe previous-field yas/snippet-end))) ;; Push an action for snippet revival ;; + ;; (if yas/allow-buggy-redo (yas/points-to-markers snippet)) (push `(apply yas/snippet-revive ,yas/snippet-beg ,yas/snippet-end ,snippet) buffer-undo-list) @@ -1057,16 +1065,19 @@ exiting the snippet." snippet, if so cleans up the whole snippet up." (let* ((snippets (yas/snippets-at-point 'all-snippets))) (dolist (snippet snippets) - ;; TODO: handle nested field exceptions, smaller, more nested - ;; find should come up earlier as `containing-field's (let ((active-field (yas/snippet-active-field snippet))) (cond ((not (and active-field (yas/field-contains-point-p active-field))) (yas/commit-snippet snippet)) ((and active-field (or (not yas/active-field-overlay) (not (overlay-buffer yas/active-field-overlay)))) + ;; + ;; this case is mainly for recent snippet exits that + ;; place us back int the field of another snippet + ;; (save-excursion - (yas/move-to-field snippet active-field))) + (yas/move-to-field snippet active-field) + (yas/update-mirrors snippet))) (t nil)))))) @@ -1142,7 +1153,7 @@ progress." (let ((field (overlay-get yas/active-field-overlay 'yas/field))) (cond (after? (yas/advance-field-and-parents-maybe field (overlay-end overlay)) - (mapcar #'yas/update-mirrors (yas/snippets-at-point))) + (yas/update-mirrors (car (yas/snippets-at-point)))) (field (when (and (not after?) (not (yas/field-modified-p field)) @@ -1168,26 +1179,32 @@ will be deleted before inserting template." (run-hooks 'yas/before-expand-snippet-hook) (goto-char start) - (let* ((key (buffer-substring-no-properties start end)) - (length (- end start)) - (column (current-column)) - (inhibit-modification-hooks t) - snippet) + (let ((key (buffer-substring-no-properties start end)) + (inhibit-modification-hooks t) + (column (current-column)) + snippet) ;; Narrow the region down to the template, shoosh the - ;; buffer-undo-list, then come out as if all that happened was a - ;; normal, undo-recorded, insertion. + ;; buffer-undo-list and any modification hooks, then come out as + ;; if all that happened was a normal, undo-recorded, insertion. ;; (save-restriction (let ((buffer-undo-list t) - (template-start (+ start length))) + (template-start end)) (narrow-to-region template-start template-start) (insert template) - (setq snippet (yas/snippet-create (point-min) (point-max)))) - (push (cons (point-min) (point-max)) buffer-undo-list)) - ;; Delete the trigger key + (setq snippet (yas/snippet-create (point-min) (point-max))))) + ;; Delete the trigger key, this should trigger modification hooks ;; - (goto-char start) - (delete-char length) + (delete-region start end) + ;; This checks for stacked expansion + ;; + (let ((existing-field (and yas/active-field-overlay + (overlay-buffer yas/active-field-overlay) + (overlay-get yas/active-field-overlay 'yas/field)))) + (when existing-field + (setf (yas/snippet-previous-active-field snippet) existing-field) + (yas/advance-field-and-parents-maybe existing-field (overlay-end yas/active-field-overlay)))) + ;; Move to the first of fields, or exit the snippet to its exit ;; point ;; @@ -1199,11 +1216,9 @@ will be deleted before inserting template." ;; Push an undo action (let ((start (overlay-start (yas/snippet-control-overlay snippet))) (end (overlay-end (yas/snippet-control-overlay snippet)))) + (push (cons start end) buffer-undo-list) (push `(apply yas/take-care-of-redo ,start ,end ,snippet) - buffer-undo-list)) - - ;; if this is a stacked expansion update the other snippets at point - (mapcar #'yas/update-mirrors (rest (yas/snippets-at-point))))) + buffer-undo-list)))) (defun yas/take-care-of-redo (beg end snippet) (yas/commit-snippet snippet)) @@ -1318,7 +1333,8 @@ Allows nested placeholder in the style of Textmate." (save-excursion (dolist (field (yas/snippet-fields snippet)) (dolist (mirror (yas/field-mirrors field)) - (yas/mirror-update-display mirror field))))) + (let ((inhibit-modification-hooks t)) + (yas/mirror-update-display mirror field)))))) (defun yas/mirror-update-display (mirror field) (goto-char (yas/mirror-start mirror)) From 7ce15312a7552e15122683a09fae5ff854e85d38 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Tue, 7 Jul 2009 17:15:32 +0000 Subject: [PATCH 33/75] Commenting... --- yasnippet.el | 237 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 164 insertions(+), 73 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 3e752d2..be03e15 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -827,6 +827,8 @@ when the condition evaluated to non-nil." (id (yas/snippet-next-id) :read-only t) (control-overlay nil) active-field + ;; stacked expansion: this slot saves the active field where the + ;; child expansion took place previous-active-field) (defstruct (yas/field (:constructor yas/make-field (number start end parent-field))) @@ -925,36 +927,6 @@ inserted first." (t nil)))) -(defun yas/make-move-active-field-overlay (snippet field) - (if (and yas/active-field-overlay - (overlay-buffer yas/active-field-overlay)) - (move-overlay yas/active-field-overlay - (yas/field-start field) - (yas/field-end field)) - (setq yas/active-field-overlay - (make-overlay (yas/field-start field) - (yas/field-end field) - nil nil t)) - (overlay-put yas/active-field-overlay 'face 'yas/field-highlight-face) - ;;(overlay-put yas/active-field-overlay 'evaporate t) - (overlay-put yas/active-field-overlay 'modification-hooks '(yas/on-field-overlay-modification)) - (overlay-put yas/active-field-overlay 'insert-in-front-hooks '(yas/on-field-overlay-modification)) - (overlay-put yas/active-field-overlay 'insert-behind-hooks '(yas/on-field-overlay-modification)))) - -(defun yas/make-move-field-protection-overlays (snippet field) - (cond ((and yas/field-protection-overlays - (every #'overlay-buffer yas/field-protection-overlays)) - (move-overlay (first yas/field-protection-overlays) (1- (yas/field-start field)) (yas/field-start field)) - (move-overlay (second yas/field-protection-overlays) (yas/field-end field) (1+ (yas/field-end field)))) - (t - (setq yas/field-protection-overlays - (list (make-overlay (1- (yas/field-start field)) (yas/field-start field) nil t nil) - (make-overlay (yas/field-end field) (1+ (yas/field-end field)) nil t nil))) - (dolist (ov yas/field-protection-overlays) - (overlay-put ov 'face 'yas/field-debug-face) - ;; (overlay-put ov 'evaporate t) - (overlay-put ov 'modification-hooks '(yas/on-protection-overlay-modification)))))) - (defun yas/move-to-field (snippet field) "Update SNIPPET to move to field FIELD. @@ -981,37 +953,44 @@ up the snippet does not delete it!" (defun yas/delete-overlay-region (overlay) (delete-region (overlay-start overlay) (overlay-end overlay))) -(defun yas/markers-to-points (snippet) - "Convert all markers in SNIPPET to simple integer buffer positions." - (dolist (field (yas/snippet-fields snippet)) - (let ((start (marker-position (yas/field-start field))) - (end (marker-position (yas/field-end field)))) - (set-marker (yas/field-start field) nil) - (set-marker (yas/field-end field) nil) - (setf (yas/field-start field) start) - (setf (yas/field-end field) end)) - (dolist (mirror (yas/field-mirrors field)) - (let ((start (marker-position (yas/mirror-start mirror))) - (end (marker-position (yas/mirror-end mirror)))) - (set-marker (yas/mirror-start mirror) nil) - (set-marker (yas/mirror-end mirror) nil) - (setf (yas/mirror-start mirror) start) - (setf (yas/mirror-end mirror) end)))) - (when (yas/snippet-exit snippet) - (let ((exit (marker-position (yas/snippet-exit snippet)))) - (set-marker (yas/snippet-exit snippet) nil) - (setf (yas/snippet-exit snippet) exit)))) +;; Markers to points: This can be useful for performance reasons, so +;; that an excessive number of live markers arent kept aroung in the +;; `buffer-undo-list'. However in `markers-to-points', the set-to-nil +;; markers can't simply be discarded and replaced with fresh ones in +;; `points-to-markers'. The original set-to-nil marker has to be +;; reused. +;; +;; (defun yas/markers-to-points (snippet) +;; "Convert all markers in SNIPPET to simple integer buffer positions." +;; (dolist (field (yas/snippet-fields snippet)) +;; (let ((start (marker-position (yas/field-start field))) +;; (end (marker-position (yas/field-end field)))) +;; (set-marker (yas/field-start field) nil) +;; (set-marker (yas/field-end field) nil) +;; (setf (yas/field-start field) start) +;; (setf (yas/field-end field) end)) +;; (dolist (mirror (yas/field-mirrors field)) +;; (let ((start (marker-position (yas/mirror-start mirror))) +;; (end (marker-position (yas/mirror-end mirror)))) +;; (set-marker (yas/mirror-start mirror) nil) +;; (set-marker (yas/mirror-end mirror) nil) +;; (setf (yas/mirror-start mirror) start) +;; (setf (yas/mirror-end mirror) end)))) +;; (when (yas/snippet-exit snippet) +;; (let ((exit (marker-position (yas/snippet-exit snippet)))) +;; (set-marker (yas/snippet-exit snippet) nil) +;; (setf (yas/snippet-exit snippet) exit)))) -(defun yas/points-to-markers (snippet) - "Convert all simple integer buffer positions in SNIPPET to markers" - (dolist (field (yas/snippet-fields snippet)) - (setf (yas/field-start field) (set-marker (make-marker) (yas/field-start field))) - (setf (yas/field-end field) (set-marker (make-marker) (yas/field-end field))) - (dolist (mirror (yas/field-mirrors field)) - (setf (yas/mirror-start mirror) (set-marker (make-marker) (yas/mirror-start mirror))) - (setf (yas/mirror-end mirror) (set-marker (make-marker) (yas/mirror-end mirror))))) - (when (yas/snippet-exit snippet) - (setf (yas/snippet-exit snippet) (set-marker (make-marker) (yas/snippet-exit snippet))))) +;; (defun yas/points-to-markers (snippet) +;; "Convert all simple integer buffer positions in SNIPPET to markers" +;; (dolist (field (yas/snippet-fields snippet)) +;; (setf (yas/field-start field) (set-marker (make-marker) (yas/field-start field))) +;; (setf (yas/field-end field) (set-marker (make-marker) (yas/field-end field))) +;; (dolist (mirror (yas/field-mirrors field)) +;; (setf (yas/mirror-start mirror) (set-marker (make-marker) (yas/mirror-start mirror))) +;; (setf (yas/mirror-end mirror) (set-marker (make-marker) (yas/mirror-end mirror))))) +;; (when (yas/snippet-exit snippet) +;; (setf (yas/snippet-exit snippet) (set-marker (make-marker) (yas/snippet-exit snippet))))) (defun yas/commit-snippet (snippet &optional no-hooks) "Commit SNIPPET, but leave point as it is. This renders the @@ -1038,8 +1017,8 @@ exiting the snippet." (when yas/field-protection-overlays (mapcar #'delete-overlay yas/field-protection-overlays))) - ;; For stacked expansion: if the original expansion took place - ;; from a field, make sure we advance it here at least to + ;; stacked expansion: if the original expansion took place from a + ;; field, make sure we advance it here at least to ;; `yas/snippet-end'... ;; (let ((previous-field (yas/snippet-previous-active-field snippet))) @@ -1072,8 +1051,9 @@ snippet, if so cleans up the whole snippet up." (or (not yas/active-field-overlay) (not (overlay-buffer yas/active-field-overlay)))) ;; - ;; this case is mainly for recent snippet exits that - ;; place us back int the field of another snippet + ;; stacked expansion: this case is mainly for recent + ;; snippet exits that place us back int the field of + ;; another snippet ;; (save-excursion (yas/move-to-field snippet active-field) @@ -1143,6 +1123,25 @@ This is needed since markers don't \"rear-advance\" like overlays" (when (yas/field-parent-field field) (yas/advance-field-and-parents-maybe (yas/field-parent-field field) end)))) +(defun yas/make-move-active-field-overlay (snippet field) + "Place the active field overlay in SNIPPET's FIELD. + +Move the overlay, or create it if it does not exit." + (if (and yas/active-field-overlay + (overlay-buffer yas/active-field-overlay)) + (move-overlay yas/active-field-overlay + (yas/field-start field) + (yas/field-end field)) + (setq yas/active-field-overlay + (make-overlay (yas/field-start field) + (yas/field-end field) + nil nil t)) + (overlay-put yas/active-field-overlay 'face 'yas/field-highlight-face) + ;;(overlay-put yas/active-field-overlay 'evaporate t) + (overlay-put yas/active-field-overlay 'modification-hooks '(yas/on-field-overlay-modification)) + (overlay-put yas/active-field-overlay 'insert-in-front-hooks '(yas/on-field-overlay-modification)) + (overlay-put yas/active-field-overlay 'insert-behind-hooks '(yas/on-field-overlay-modification)))) + (defun yas/on-field-overlay-modification (overlay after? beg end &optional length) "Clears the field and updates mirrors, conditionally. @@ -1163,8 +1162,35 @@ progress." (yas/clear-field field)) (setf (yas/field-modified-p field) t)))))) +;; Apropos "protection overlays:"... These exist for nasty users who +;; will try to delete parts of the snippet outside the active +;; field. Actual protection happens in +;; `yas/on-protection-overlay-modification'. +;; +;; Currently, this commits the snippet before actually calling +;; `this-command' interactively, and then signals an eror, which is +;; ignored. but blocks all other million modification hooks. I might +;; decide to not let the command be executed at all... +;; +(defun yas/make-move-field-protection-overlays (snippet field) + "Place protection overlays surrounding SNIPPET's FIELD. + +Move the overlays, or create them if they do not exit." + (cond ((and yas/field-protection-overlays + (every #'overlay-buffer yas/field-protection-overlays)) + (move-overlay (first yas/field-protection-overlays) (1- (yas/field-start field)) (yas/field-start field)) + (move-overlay (second yas/field-protection-overlays) (yas/field-end field) (1+ (yas/field-end field)))) + (t + (setq yas/field-protection-overlays + (list (make-overlay (1- (yas/field-start field)) (yas/field-start field) nil t nil) + (make-overlay (yas/field-end field) (1+ (yas/field-end field)) nil t nil))) + (dolist (ov yas/field-protection-overlays) + (overlay-put ov 'face 'yas/field-debug-face) + ;; (overlay-put ov 'evaporate t) + (overlay-put ov 'modification-hooks '(yas/on-protection-overlay-modification)))))) + (defun yas/on-protection-overlay-modification (overlay after? beg end &optional length) - "To be written" + "Commits the snippet before calling `this-command' interactively, then issues error." (cond ((not (or after? (yas/undo-in-progress))) (let ((snippet (car (yas/snippets-at-point)))) @@ -1173,19 +1199,45 @@ progress." (call-interactively this-command) (error "Snippet exited")))))) +;;; +;;; Apropos "stacked expansion:"... +;;; +;;; the parent snippet does not run its fields modification hooks +;;; (`yas/on-field-overlay-modification' and +;;; `yas/on-protection-overlay-modification') while the child snippet +;;; is active. This means, among other things, that the mirrors of the +;;; parent snippet are not updated, this only happening when one exits +;;; the child snippet. +;;; +;;; Unfortunately, this also puts some ugly (and not fully-tested) +;;; bits of code in `yas/expand-snippet' and +;;; `yas/commit-snippet'. I've tried to mark them with "stacked +;;; expansion:". +;;; +;;; This was thought to be safer in in an undo/redo perpective, but +;;; maybe the correct implementation is to make the globals +;;; `yas/active-field-overlay' and `yas/field-protection-overlays' be +;;; snippet-local and be active even while the child snippet is +;;; running. This is a whole lot of hooks running, but they should +;;; account for all +;;; (defun yas/expand-snippet (start end template) "Expand snippet at current point. Text between START and END will be deleted before inserting template." (run-hooks 'yas/before-expand-snippet-hook) (goto-char start) + ;; stacked expansion: shoosh the modification hooks + ;; (let ((key (buffer-substring-no-properties start end)) (inhibit-modification-hooks t) (column (current-column)) snippet) ;; Narrow the region down to the template, shoosh the - ;; buffer-undo-list and any modification hooks, then come out as - ;; if all that happened was a normal, undo-recorded, insertion. + ;; buffer-undo-list, and create the snippet, the new snippet + ;; updates its mirrors once, so we are left with some plain text. + ;; The undo action for deleting this plain text will get recorded + ;; at the end of this function. ;; (save-restriction (let ((buffer-undo-list t) @@ -1193,10 +1245,11 @@ will be deleted before inserting template." (narrow-to-region template-start template-start) (insert template) (setq snippet (yas/snippet-create (point-min) (point-max))))) - ;; Delete the trigger key, this should trigger modification hooks + ;; Delete the trigger key, this *does* get undo-recorded. ;; (delete-region start end) - ;; This checks for stacked expansion + ;; stacked-expansion: This checks for stacked expansion, save the + ;; `yas/previous-active-field' and advance its boudary. ;; (let ((existing-field (and yas/active-field-overlay (overlay-buffer yas/active-field-overlay) @@ -1213,7 +1266,10 @@ will be deleted before inserting template." (yas/move-to-field snippet first-field)) (t (yas/exit-snippet snippet)))) - ;; Push an undo action + ;; Push two undo actions: the deletion of the inserted contents of + ;; the new snippet (whitout the "key") followed by an apply of + ;; `yas/take-care-of-redo' on the newly inserted snippet boundaries + ;; (let ((start (overlay-start (yas/snippet-control-overlay snippet))) (end (overlay-end (yas/snippet-control-overlay snippet)))) (push (cons start end) buffer-undo-list) @@ -1221,18 +1277,35 @@ will be deleted before inserting template." buffer-undo-list)))) (defun yas/take-care-of-redo (beg end snippet) + "Commits SNIPPET, which in turn pushes an undo action for +reviving it. + +Meant to exit in the `buffer-undo-list'." (yas/commit-snippet snippet)) (defun yas/snippet-revive (beg end snippet) + "Revives the SNIPPET and creates a control overlay from BEG to +END. + +BEG and END are, we hope, the original snippets boudaries. All +the markers/points exiting existing inside SNIPPET should point +to their correct locations *at the time the snippet is revived*. + +After revival, push the `yas/take-care-of-redo' in the +`buffer-undo-list'" (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay beg end)) (overlay-put (yas/snippet-control-overlay snippet) 'yas/snippet snippet) (yas/move-to-field snippet (or (yas/snippet-active-field snippet) (car (yas/snippet-fields snippet)))) ;; (if yas/allow-buggy-redo (yas/points-to-markers snippet)) + (push `(apply yas/take-care-of-redo ,beg ,end ,snippet) buffer-undo-list)) (defun yas/snippet-create (begin end) + "Creates a snippet from an template inserted between BEGIN and END. + +Returns the newly created snippet." (let ((snippet (yas/make-snippet))) (goto-char begin) (yas/snippet-parse-create snippet) @@ -1248,7 +1321,6 @@ will be deleted before inserting template." ;; Move to end (goto-char (point-max)) - snippet)) @@ -1266,9 +1338,9 @@ will be deleted before inserting template." (defun yas/snippet-parse-create (snippet) "Parse a recently inserted snippet template, creating all -necessary fields. +necessary fields, mirrors and exit points. -Allows nested placeholder in the style of Textmate." +Meant to be called in a narrowed buffer, does three passes" (let ((parse-start (point))) (yas/field-parse-create snippet) (goto-char parse-start) @@ -1277,6 +1349,7 @@ Allows nested placeholder in the style of Textmate." (yas/simple-mirror-parse-create snippet))) (defun yas/field-parse-create (snippet &optional parent-field) + "Parse the \"${n: }\" or \"${n:`(lisp-expression)`}\" fields." (while (re-search-forward yas/field-regexp nil t) (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) (number (string-to-number (match-string-no-properties 1))) @@ -1299,6 +1372,7 @@ Allows nested placeholder in the style of Textmate." (yas/field-parse-create snippet brand-new-field))))))) (defun yas/transform-mirror-parse-create (snippet) + "Parse the \"${n:(lisp-expression)}\" mirror transformations." (while (re-search-forward yas/transform-mirror-regexp nil t) (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) (number (string-to-number (match-string-no-properties 1))) @@ -1314,6 +1388,7 @@ Allows nested placeholder in the style of Textmate." (delete-region (match-beginning 0) real-match-end-0))))) (defun yas/simple-mirror-parse-create (snippet) + "Parse the simple \"$n\" mirrors and the exit-marker." (while (re-search-forward yas/simple-mirror-regexp nil t) (let ((number (string-to-number (match-string-no-properties 1)))) (cond ((zerop number) @@ -1330,13 +1405,19 @@ Allows nested placeholder in the style of Textmate." (delete-region (match-beginning 0) (match-end 0))))))))) (defun yas/update-mirrors (snippet) + "Updates all the mirrors of SNIPPET." (save-excursion (dolist (field (yas/snippet-fields snippet)) (dolist (mirror (yas/field-mirrors field)) + ;; stacked expansion: I added an `inhibit-modification-hooks' + ;; here, for safety, may need to remove if we the mechanism is + ;; altered. + ;; (let ((inhibit-modification-hooks t)) (yas/mirror-update-display mirror field)))))) (defun yas/mirror-update-display (mirror field) + "Update MIRROR according to FIELD (and mirror transform)." (goto-char (yas/mirror-start mirror)) (delete-region (yas/mirror-start mirror) (yas/mirror-end mirror)) (insert (yas/apply-transform mirror field)) @@ -1354,6 +1435,16 @@ Allows nested placeholder in the style of Textmate." (princ (format "\nPost command hook: %s\n" post-command-hook)) (princ (format "\nPre command hook: %s\n" pre-command-hook)) + (princ (format "%s live snippets in total" (length (yas/snippets-at-point (quote all-snippets))))) + (princ (format "%s live snippets at point:" (length (yas/snippets-at-point)))) + + (dolist (snippet (yas/snippets-at-point)) + (princ (format "\tid: %s and active field from %s to %s covering \"%s\"\n" + (yas/snippet-id snippet) + (marker-position (yas/field-start (yas/snippet-active-field snippet))) + (marker-position (yas/field-end (yas/snippet-active-field snippet))) + (buffer-substring-no-properties (yas/field-start (yas/snippet-active-field snippet)) (yas/field-end (yas/snippet-active-field snippet)))))) + (princ (format "\nUndo is %s and point-max is %s.\n" (if (eq buffer-undo-list t) "DISABLED" From 9e9f5f477c044c3891941c645c619a086c7f3a9c Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Tue, 7 Jul 2009 17:19:56 +0000 Subject: [PATCH 34/75] Commenting... --- yasnippet.el | 100 ++++++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index be03e15..944b469 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -377,6 +377,8 @@ a list of modes like this to help the judgement." (or (fboundp mode) (find mode yas/known-modes))) +;; TODO: This is a possible optimization point, the expression could +;; be stored in cons format instead of string, (defun yas/eval-string (string) "Evaluate STRING and convert the result to string." (condition-case err @@ -953,44 +955,44 @@ up the snippet does not delete it!" (defun yas/delete-overlay-region (overlay) (delete-region (overlay-start overlay) (overlay-end overlay))) -;; Markers to points: This can be useful for performance reasons, so -;; that an excessive number of live markers arent kept aroung in the -;; `buffer-undo-list'. However in `markers-to-points', the set-to-nil -;; markers can't simply be discarded and replaced with fresh ones in -;; `points-to-markers'. The original set-to-nil marker has to be -;; reused. +;;; Apropos markers-to-points: This can be useful for performance reasons, so +;;; that an excessive number of live markers arent kept aroung in the +;;; `buffer-undo-list'. However in `markers-to-points', the set-to-nil +;;; markers can't simply be discarded and replaced with fresh ones in +;;; `points-to-markers'. The original set-to-nil marker has to be +;;; reused. +;;; +;;; (defun yas/markers-to-points (snippet) +;;; "Convert all markers in SNIPPET to simple integer buffer positions." +;;; (dolist (field (yas/snippet-fields snippet)) +;;; (let ((start (marker-position (yas/field-start field))) +;;; (end (marker-position (yas/field-end field)))) +;;; (set-marker (yas/field-start field) nil) +;;; (set-marker (yas/field-end field) nil) +;;; (setf (yas/field-start field) start) +;;; (setf (yas/field-end field) end)) +;;; (dolist (mirror (yas/field-mirrors field)) +;;; (let ((start (marker-position (yas/mirror-start mirror))) +;;; (end (marker-position (yas/mirror-end mirror)))) +;;; (set-marker (yas/mirror-start mirror) nil) +;;; (set-marker (yas/mirror-end mirror) nil) +;;; (setf (yas/mirror-start mirror) start) +;;; (setf (yas/mirror-end mirror) end)))) +;;; (when (yas/snippet-exit snippet) +;;; (let ((exit (marker-position (yas/snippet-exit snippet)))) +;;; (set-marker (yas/snippet-exit snippet) nil) +;;; (setf (yas/snippet-exit snippet) exit)))) ;; -;; (defun yas/markers-to-points (snippet) -;; "Convert all markers in SNIPPET to simple integer buffer positions." -;; (dolist (field (yas/snippet-fields snippet)) -;; (let ((start (marker-position (yas/field-start field))) -;; (end (marker-position (yas/field-end field)))) -;; (set-marker (yas/field-start field) nil) -;; (set-marker (yas/field-end field) nil) -;; (setf (yas/field-start field) start) -;; (setf (yas/field-end field) end)) -;; (dolist (mirror (yas/field-mirrors field)) -;; (let ((start (marker-position (yas/mirror-start mirror))) -;; (end (marker-position (yas/mirror-end mirror)))) -;; (set-marker (yas/mirror-start mirror) nil) -;; (set-marker (yas/mirror-end mirror) nil) -;; (setf (yas/mirror-start mirror) start) -;; (setf (yas/mirror-end mirror) end)))) -;; (when (yas/snippet-exit snippet) -;; (let ((exit (marker-position (yas/snippet-exit snippet)))) -;; (set-marker (yas/snippet-exit snippet) nil) -;; (setf (yas/snippet-exit snippet) exit)))) - -;; (defun yas/points-to-markers (snippet) -;; "Convert all simple integer buffer positions in SNIPPET to markers" -;; (dolist (field (yas/snippet-fields snippet)) -;; (setf (yas/field-start field) (set-marker (make-marker) (yas/field-start field))) -;; (setf (yas/field-end field) (set-marker (make-marker) (yas/field-end field))) -;; (dolist (mirror (yas/field-mirrors field)) -;; (setf (yas/mirror-start mirror) (set-marker (make-marker) (yas/mirror-start mirror))) -;; (setf (yas/mirror-end mirror) (set-marker (make-marker) (yas/mirror-end mirror))))) -;; (when (yas/snippet-exit snippet) -;; (setf (yas/snippet-exit snippet) (set-marker (make-marker) (yas/snippet-exit snippet))))) +;;; (defun yas/points-to-markers (snippet) +;;; "Convert all simple integer buffer positions in SNIPPET to markers" +;;; (dolist (field (yas/snippet-fields snippet)) +;;; (setf (yas/field-start field) (set-marker (make-marker) (yas/field-start field))) +;;; (setf (yas/field-end field) (set-marker (make-marker) (yas/field-end field))) +;;; (dolist (mirror (yas/field-mirrors field)) +;;; (setf (yas/mirror-start mirror) (set-marker (make-marker) (yas/mirror-start mirror))) +;;; (setf (yas/mirror-end mirror) (set-marker (make-marker) (yas/mirror-end mirror))))) +;;; (when (yas/snippet-exit snippet) +;;; (setf (yas/snippet-exit snippet) (set-marker (make-marker) (yas/snippet-exit snippet))))) (defun yas/commit-snippet (snippet &optional no-hooks) "Commit SNIPPET, but leave point as it is. This renders the @@ -1162,16 +1164,18 @@ progress." (yas/clear-field field)) (setf (yas/field-modified-p field) t)))))) -;; Apropos "protection overlays:"... These exist for nasty users who -;; will try to delete parts of the snippet outside the active -;; field. Actual protection happens in -;; `yas/on-protection-overlay-modification'. -;; -;; Currently, this commits the snippet before actually calling -;; `this-command' interactively, and then signals an eror, which is -;; ignored. but blocks all other million modification hooks. I might -;; decide to not let the command be executed at all... -;; +;;; +;;; Apropos protection overlays:... +;;; +;;; These exist for nasty users who will try to delete parts of the +;;; snippet outside the active field. Actual protection happens in +;;; `yas/on-protection-overlay-modification'. +;;; +;;; Currently, this commits the snippet before actually calling +;;; `this-command' interactively, and then signals an eror, which is +;;; ignored. but blocks all other million modification hooks. I might +;;; decide to not let the command be executed at all... +;;; (defun yas/make-move-field-protection-overlays (snippet field) "Place protection overlays surrounding SNIPPET's FIELD. @@ -1200,7 +1204,7 @@ Move the overlays, or create them if they do not exit." (error "Snippet exited")))))) ;;; -;;; Apropos "stacked expansion:"... +;;; Apropos stacked expansion:... ;;; ;;; the parent snippet does not run its fields modification hooks ;;; (`yas/on-field-overlay-modification' and From dec6fcda41c5518f0efd086a4fe1de8e20cb4a7f Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Wed, 8 Jul 2009 13:24:40 +0000 Subject: [PATCH 35/75] * primary field transformation working more or less, but seem to have screwed with undo/redo, shouldn't be too hard... --- yasnippet.el | 181 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 135 insertions(+), 46 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 944b469..f587e9d 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -227,6 +227,10 @@ to expand. "${\\([0-9]+:\\)?\\([^}]*\\)}" "A regexp to *almost* recognize a field") +(defconst yas/expression-regexp + "$\\(([^)]*)\\)" + "A regexp to *almost* recognize a \"$(...)\" expression") + (defconst yas/transform-mirror-regexp "${\\(?:\\([0-9]+\\):\\)?$\\([^}]*\\)" "A regexp to *almost* recognize a mirror with a transform") @@ -386,7 +390,9 @@ a list of modes like this to help the judgement." (save-restriction (save-match-data (widen) - (format "%s" (eval (read string)))))) + (let ((result (eval (read string)))) + (when result + (format "%s" result)))))) (error (format "(error in elisp evaluation: %s)" (error-message-string err))))) @@ -831,7 +837,8 @@ when the condition evaluated to non-nil." active-field ;; stacked expansion: this slot saves the active field where the ;; child expansion took place - previous-active-field) + previous-active-field + exit-hook) (defstruct (yas/field (:constructor yas/make-field (number start end parent-field))) "A field." @@ -849,22 +856,20 @@ when the condition evaluated to non-nil." start end (transform nil)) -(defun yas/apply-transform (field-or-mirror field) - "Calculate the value of the field. If there's a transform +(defun yas/apply-transform (field-or-mirror field &optional nil-on-empty) + "Calculate the value of the field/mirror. If there's a transform for this field, apply it. Otherwise, the value is returned -unmodified. +unmodified." + (let* ((text (yas/field-text-for-display field)) + (modified-p (yas/field-modified-p field)) + (transform (if (yas/mirror-p field-or-mirror) + (yas/mirror-transform field-or-mirror) + (yas/field-transform field-or-mirror))) + (transformed (and transform + (yas/eval-string transform)))) + (or transformed + (unless nil-on-empty text)))) -TODO: I really dont think field transforms are easily done, but oh -well - -" - (let ((text (yas/field-text-for-display field)) - (transform (if (yas/mirror-p field-or-mirror) - (yas/mirror-transform field-or-mirror) - (yas/field-transform field-or-mirror)))) - (if transform - (yas/eval-string transform) - text))) (defsubst yas/replace-all (from to) "Replace all occurance from FROM to TO." @@ -937,7 +942,12 @@ Also create some protection overlays" (setf (yas/snippet-active-field snippet) field) (yas/make-move-active-field-overlay snippet field) (yas/make-move-field-protection-overlays snippet field) - (overlay-put yas/active-field-overlay 'yas/field field)) + (overlay-put yas/active-field-overlay 'yas/field field) + (unless (yas/field-modified-p field) + (if (yas/update-field field snippet) + (let ((inhibit-modification-hooks t)) + (yas/update-mirrors snippet)) + (setf (yas/field-modified-p field) nil)))) (defun yas/prev-field () "Navigate to prev field. If there's none, exit the snippet." @@ -1063,20 +1073,32 @@ snippet, if so cleans up the whole snippet up." (t nil)))))) -(defun yas/field-contains-point-p (field) - (and (>= (point) (yas/field-start field)) - (<= (point) (yas/field-end field)))) +(defun yas/field-contains-point-p (field &optional point) + (let ((point (or point + (point)))) + (and (>= point (yas/field-start field)) + (<= point (yas/field-end field))))) -(defun yas/pre-command-handler () - ) +(defun yas/pre-command-handler () ) (defun yas/post-command-handler () - (cond ((eq 'undo this-command) - (let ((snippet (car (yas/snippets-at-point)))) - (when snippet - (yas/move-to-field snippet (or (yas/snippet-active-field snippet) - (car (yas/snippet-fields snippet))))))) + "Handles various yasnippet conditions after each command." + (cond (yas/protection-violation + (goto-char yas/protection-violation) + (setq yas/protection-violation nil)) + ((eq 'undo this-command) + ;; + ;; After undo's the correct field is sometimes not restored + ;; correctly, this condition handles that + ;; + (let* ((snippet (car (yas/snippets-at-point))) + (target-field (and snippet + (find-if-not #'yas/field-probably-deleted-p (cons (yas/snippet-active-field snippet) + (yas/snippet-fields snippet)))))) + (when target-field + (yas/move-to-field snippet target-field)))) ((not (yas/undo-in-progress)) + ;; When not in an undo, check if we must commit the snippet (use exited it). (yas/check-commit-snippet)))) (defun yas/field-text-for-display (field) @@ -1084,11 +1106,14 @@ snippet, if so cleans up the whole snippet up." (buffer-substring (yas/field-start field) (yas/field-end field))) (defun yas/undo-in-progress () + "True if some kind of undo is in progress" (or undo-in-progress - (eq this-command 'undo))) + (eq this-command 'undo) + (eq this-command 'redo))) (defun yas/make-control-overlay (start end) - "..." + "Creates the control overlay that surrounds the snippet and +holds the keymap." (let ((overlay (make-overlay start end nil @@ -1100,13 +1125,16 @@ snippet, if so cleans up the whole snippet up." overlay)) (defun yas/clear-field-or-delete-char (&optional field) + "Clears an unmodified field if at field start, otherwise +deletes a character normally." (interactive) (let ((field (or field (and yas/active-field-overlay (overlay-buffer yas/active-field-overlay) (overlay-get yas/active-field-overlay 'yas/field))))) (cond ((and field - (not (yas/field-modified-p field))) + (not (yas/field-modified-p field)) + (eq (point) (marker-position (yas/field-start field)))) (yas/clear-field field)) (t (call-interactively 'delete-char))))) @@ -1154,6 +1182,7 @@ progress." (let ((field (overlay-get yas/active-field-overlay 'yas/field))) (cond (after? (yas/advance-field-and-parents-maybe field (overlay-end overlay)) + (yas/update-field field (car (yas/snippets-at-point))) (yas/update-mirrors (car (yas/snippets-at-point)))) (field (when (and (not after?) @@ -1164,6 +1193,17 @@ progress." (yas/clear-field field)) (setf (yas/field-modified-p field) t)))))) +(defun yas/update-field (field snippet) + (when (yas/field-transform field) + (let ((inhibit-modification-hooks t) + (transformed (yas/apply-transform field field 'nil-on-empty)) + (point (point))) + (when transformed + (yas/clear-field field) + (insert transformed) + (yas/advance-field-and-parents-maybe field (point)) + (when (yas/field-contains-point-p field point) + (goto-char point)))))) ;;; ;;; Apropos protection overlays:... ;;; @@ -1171,10 +1211,16 @@ progress." ;;; snippet outside the active field. Actual protection happens in ;;; `yas/on-protection-overlay-modification'. ;;; -;;; Currently, this commits the snippet before actually calling -;;; `this-command' interactively, and then signals an eror, which is -;;; ignored. but blocks all other million modification hooks. I might -;;; decide to not let the command be executed at all... +;;; Currently this signals an error which inhibits the command. For +;;; commands that move point (like `kill-line'), point is restored in +;;; the `yas/post-command-handler' using a global +;;; `yas/protection-violation' variable. +;;; +;;; Alternatively, I've experimented with an implementation that +;;; commits the snippet before actually calling `this-command' +;;; interactively, and then signals an eror, which is ignored. but +;;; blocks all other million modification hooks. This presented some +;;; problems with stacked expansion. ;;; (defun yas/make-move-field-protection-overlays (snippet field) "Place protection overlays surrounding SNIPPET's FIELD. @@ -1193,15 +1239,22 @@ Move the overlays, or create them if they do not exit." ;; (overlay-put ov 'evaporate t) (overlay-put ov 'modification-hooks '(yas/on-protection-overlay-modification)))))) +(defvar yas/protection-violation nil + "When non-nil, signals attempts to erronesly exit or modify the snippet. + +Functions in the `post-command-hook', for example +`yas/post-command-handler' can check it and reset its value to nil. The variables value is the point where the violation originated") + + (defun yas/on-protection-overlay-modification (overlay after? beg end &optional length) - "Commits the snippet before calling `this-command' interactively, then issues error." + "Signals a snippet violation, then issues error. + +The error should be ignored in `debug-ignored-errors'" (cond ((not (or after? (yas/undo-in-progress))) - (let ((snippet (car (yas/snippets-at-point)))) - (when snippet - (yas/commit-snippet snippet) - (call-interactively this-command) - (error "Snippet exited")))))) + (setq yas/protection-violation (point)) + (error "Exit the snippet first!")))) +(add-to-list 'debug-ignored-errors "^Exit the snippet first!$") ;;; ;;; Apropos stacked expansion:... @@ -1231,14 +1284,14 @@ will be deleted before inserting template." (run-hooks 'yas/before-expand-snippet-hook) (goto-char start) - ;; stacked expansion: shoosh the modification hooks + ;; stacked expansion: shoosh the overlay modification hooks ;; (let ((key (buffer-substring-no-properties start end)) (inhibit-modification-hooks t) (column (current-column)) snippet) ;; Narrow the region down to the template, shoosh the - ;; buffer-undo-list, and create the snippet, the new snippet + ;; `buffer-undo-list', and create the snippet, the new snippet ;; updates its mirrors once, so we are left with some plain text. ;; The undo action for deleting this plain text will get recorded ;; at the end of this function. @@ -1344,21 +1397,49 @@ Returns the newly created snippet." "Parse a recently inserted snippet template, creating all necessary fields, mirrors and exit points. -Meant to be called in a narrowed buffer, does three passes" +Meant to be called in a narrowed buffer, does various passes" (let ((parse-start (point))) + ;; protect escapes + ;; + (yas/protect-escapes) + ;; parse fields + ;; + (goto-char parse-start) (yas/field-parse-create snippet) + ;; parse mirror transforms + ;; (goto-char parse-start) (yas/transform-mirror-parse-create snippet) + ;; parse simple mirrors + ;; (goto-char parse-start) - (yas/simple-mirror-parse-create snippet))) + (yas/simple-mirror-parse-create snippet) + ;; restore escapes + ;; + (yas/restore-escapes))) + +(defun yas/protect-escapes () + "Protect all escaped characters with their numeric ASCII value.") +(defun yas/restore-escapes () + "Restore all escaped characters from their numeric ASCII value.") (defun yas/field-parse-create (snippet &optional parent-field) - "Parse the \"${n: }\" or \"${n:`(lisp-expression)`}\" fields." + "Parse the \"${n: }\" or \"$(lisp-expression)\" expressions, in +two separate passes. + +For \"$(lisp-expression)\" expressions \"lisp-expression\" is set to: + + * The snippets exit-hook if PARENT-FIELD is nil; + * PARENT-FIELD's transform, otherwise. + +When multiple such expressions are found, only the last one counts." + (save-excursion (while (re-search-forward yas/field-regexp nil t) (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) (number (string-to-number (match-string-no-properties 1))) (brand-new-field (and real-match-end-0 - (save-match-data (not (string-match "$(" (match-string-no-properties 2)))) + ;; (save-match-data (not (string-match "$(" (match-string-no-properties 2)))) + ;; .. shit... don't know why I added this line anymore number (not (zerop number)) (yas/make-field number @@ -1374,6 +1455,14 @@ Meant to be called in a narrowed buffer, does three passes" (narrow-to-region (yas/field-start brand-new-field) (yas/field-end brand-new-field)) (goto-char (point-min)) (yas/field-parse-create snippet brand-new-field))))))) + (save-excursion + (while (re-search-forward yas/expression-regexp nil t) + (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1))) + (when real-match-end-0 + (if parent-field + (setf (yas/field-transform parent-field) (buffer-substring-no-properties (match-beginning 1) real-match-end-0)) + (setf (yas/snippet-exit-hook snippet) (buffer-substring-no-properties (match-beginning 1) real-match-end-0))) + (delete-region (match-beginning 0) real-match-end-0)))))) (defun yas/transform-mirror-parse-create (snippet) "Parse the \"${n:(lisp-expression)}\" mirror transformations." From 39aab51b28145529de27ca1b14c90d6dfebf013c Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Thu, 9 Jul 2009 16:12:48 +0000 Subject: [PATCH 36/75] * implemented `yas/points-to-markers' and friend, look OK. * mirrors only update when something actually changes * cant reproduce strange undo/redo bug that I think I saw * escapes apparently working OK * expressions in fields working OK, but now tested extensively TODO: Implement the per-snippet exit hook just like the "condition" Handle indent the best possible way Merge changes from trunk --- yasnippet.el | 167 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 109 insertions(+), 58 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index f587e9d..5a21b2f 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -227,10 +227,14 @@ to expand. "${\\([0-9]+:\\)?\\([^}]*\\)}" "A regexp to *almost* recognize a field") -(defconst yas/expression-regexp +(defconst yas/dollar-lisp-expression-regexp "$\\(([^)]*)\\)" "A regexp to *almost* recognize a \"$(...)\" expression") +(defconst yas/backquote-lisp-expression-regexp + "`\\([^`]*\\)`" + "A regexp to recognize a \"`(...)`\" expression") + (defconst yas/transform-mirror-regexp "${\\(?:\\([0-9]+\\):\\)?$\\([^}]*\\)" "A regexp to *almost* recognize a mirror with a transform") @@ -944,7 +948,7 @@ Also create some protection overlays" (yas/make-move-field-protection-overlays snippet field) (overlay-put yas/active-field-overlay 'yas/field field) (unless (yas/field-modified-p field) - (if (yas/update-field field snippet) + (if (yas/field-update-display field snippet) (let ((inhibit-modification-hooks t)) (yas/update-mirrors snippet)) (setf (yas/field-modified-p field) nil)))) @@ -967,42 +971,48 @@ up the snippet does not delete it!" ;;; Apropos markers-to-points: This can be useful for performance reasons, so ;;; that an excessive number of live markers arent kept aroung in the -;;; `buffer-undo-list'. However in `markers-to-points', the set-to-nil +;;; `buffer-undo-list'. In `markers-to-points', the set-to-nil ;;; markers can't simply be discarded and replaced with fresh ones in -;;; `points-to-markers'. The original set-to-nil marker has to be +;;; `points-to-markers'. The original marker that was just set to nilhas to be ;;; reused. ;;; -;;; (defun yas/markers-to-points (snippet) -;;; "Convert all markers in SNIPPET to simple integer buffer positions." -;;; (dolist (field (yas/snippet-fields snippet)) -;;; (let ((start (marker-position (yas/field-start field))) -;;; (end (marker-position (yas/field-end field)))) -;;; (set-marker (yas/field-start field) nil) -;;; (set-marker (yas/field-end field) nil) -;;; (setf (yas/field-start field) start) -;;; (setf (yas/field-end field) end)) -;;; (dolist (mirror (yas/field-mirrors field)) -;;; (let ((start (marker-position (yas/mirror-start mirror))) -;;; (end (marker-position (yas/mirror-end mirror)))) -;;; (set-marker (yas/mirror-start mirror) nil) -;;; (set-marker (yas/mirror-end mirror) nil) -;;; (setf (yas/mirror-start mirror) start) -;;; (setf (yas/mirror-end mirror) end)))) -;;; (when (yas/snippet-exit snippet) -;;; (let ((exit (marker-position (yas/snippet-exit snippet)))) -;;; (set-marker (yas/snippet-exit snippet) nil) -;;; (setf (yas/snippet-exit snippet) exit)))) -;; -;;; (defun yas/points-to-markers (snippet) -;;; "Convert all simple integer buffer positions in SNIPPET to markers" -;;; (dolist (field (yas/snippet-fields snippet)) -;;; (setf (yas/field-start field) (set-marker (make-marker) (yas/field-start field))) -;;; (setf (yas/field-end field) (set-marker (make-marker) (yas/field-end field))) -;;; (dolist (mirror (yas/field-mirrors field)) -;;; (setf (yas/mirror-start mirror) (set-marker (make-marker) (yas/mirror-start mirror))) -;;; (setf (yas/mirror-end mirror) (set-marker (make-marker) (yas/mirror-end mirror))))) -;;; (when (yas/snippet-exit snippet) -;;; (setf (yas/snippet-exit snippet) (set-marker (make-marker) (yas/snippet-exit snippet))))) +;;; This shouldn't bring horrible problems with undo/redo, but it +;;; would be one of the the first thing I'd remove if I was debugging that... +;;; +(defun yas/markers-to-points (snippet) + "Convert all markers in SNIPPET to a cons (POINT . MARKER) +where POINT is the original position of the marker and MARKER is +the original marker object with the position set to nil." + (dolist (field (yas/snippet-fields snippet)) + (let ((start (marker-position (yas/field-start field))) + (end (marker-position (yas/field-end field)))) + (set-marker (yas/field-start field) nil) + (set-marker (yas/field-end field) nil) + (setf (yas/field-start field) (cons start (yas/field-start field))) + (setf (yas/field-end field) (cons end (yas/field-end field)))) + (dolist (mirror (yas/field-mirrors field)) + (let ((start (marker-position (yas/mirror-start mirror))) + (end (marker-position (yas/mirror-end mirror)))) + (set-marker (yas/mirror-start mirror) nil) + (set-marker (yas/mirror-end mirror) nil) + (setf (yas/mirror-start mirror) (cons start (yas/mirror-start mirror))) + (setf (yas/mirror-end mirror) (cons end (yas/mirror-end mirror)))))) + (when (yas/snippet-exit snippet) + (let ((exit (marker-position (yas/snippet-exit snippet)))) + (set-marker (yas/snippet-exit snippet) nil) + (setf (yas/snippet-exit snippet) (cons exit (yas/snippet-exit snippet)))))) + +(defun yas/points-to-markers (snippet) + "Convert all cons (POINT . MARKER) in SNIPPET to markers. This +is done by setting MARKER to POINT with `set-marker'." + (dolist (field (yas/snippet-fields snippet)) + (setf (yas/field-start field) (set-marker (cdr (yas/field-start field)) (car (yas/field-start field)))) + (setf (yas/field-end field) (set-marker (cdr (yas/field-end field)) (car (yas/field-end field)))) + (dolist (mirror (yas/field-mirrors field)) + (setf (yas/mirror-start mirror) (set-marker (cdr (yas/mirror-start mirror)) (car (yas/mirror-start mirror)))) + (setf (yas/mirror-end mirror) (set-marker (cdr (yas/mirror-end mirror)) (car (yas/mirror-end mirror)))))) + (when (yas/snippet-exit snippet) + (setf (yas/snippet-exit snippet) (set-marker (cdr (yas/snippet-exit snippet)) (car (yas/snippet-exit snippet)))))) (defun yas/commit-snippet (snippet &optional no-hooks) "Commit SNIPPET, but leave point as it is. This renders the @@ -1037,9 +1047,12 @@ exiting the snippet." (when previous-field (yas/advance-field-and-parents-maybe previous-field yas/snippet-end))) + ;; Convert all markers to points, + ;; + (yas/markers-to-points snippet) + ;; Push an action for snippet revival ;; - ;; (if yas/allow-buggy-redo (yas/points-to-markers snippet)) (push `(apply yas/snippet-revive ,yas/snippet-beg ,yas/snippet-end ,snippet) buffer-undo-list) @@ -1182,7 +1195,7 @@ progress." (let ((field (overlay-get yas/active-field-overlay 'yas/field))) (cond (after? (yas/advance-field-and-parents-maybe field (overlay-end overlay)) - (yas/update-field field (car (yas/snippets-at-point))) + (yas/field-update-display field (car (yas/snippets-at-point))) (yas/update-mirrors (car (yas/snippets-at-point)))) (field (when (and (not after?) @@ -1193,17 +1206,21 @@ progress." (yas/clear-field field)) (setf (yas/field-modified-p field) t)))))) -(defun yas/update-field (field snippet) +(defun yas/field-update-display (field snippet) + "Much like `yas/mirror-update-display', but for fields" (when (yas/field-transform field) (let ((inhibit-modification-hooks t) (transformed (yas/apply-transform field field 'nil-on-empty)) (point (point))) - (when transformed - (yas/clear-field field) + (when (and transformed + (not (string= transformed (buffer-substring-no-properties (yas/field-start field) (yas/field-end field))))) + (goto-char (yas/field-start field)) (insert transformed) - (yas/advance-field-and-parents-maybe field (point)) - (when (yas/field-contains-point-p field point) - (goto-char point)))))) + (if (> (yas/field-end field) (point)) + (delete-region (point) (yas/field-end field)) + (set-marker (yas/field-end field) (point))) + t)))) + ;;; ;;; Apropos protection overlays:... ;;; @@ -1350,11 +1367,14 @@ to their correct locations *at the time the snippet is revived*. After revival, push the `yas/take-care-of-redo' in the `buffer-undo-list'" + ;; Reconvert all the points to markers + ;; + (yas/points-to-markers snippet) + (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay beg end)) (overlay-put (yas/snippet-control-overlay snippet) 'yas/snippet snippet) (yas/move-to-field snippet (or (yas/snippet-active-field snippet) (car (yas/snippet-fields snippet)))) - ;; (if yas/allow-buggy-redo (yas/points-to-markers snippet)) (push `(apply yas/take-care-of-redo ,beg ,end ,snippet) buffer-undo-list)) @@ -1402,6 +1422,10 @@ Meant to be called in a narrowed buffer, does various passes" ;; protect escapes ;; (yas/protect-escapes) + ;; replace all backquoted expressions + ;; + (goto-char parse-start) + (yas/replace-backquotes) ;; parse fields ;; (goto-char parse-start) @@ -1418,19 +1442,40 @@ Meant to be called in a narrowed buffer, does various passes" ;; (yas/restore-escapes))) +(defun yas/escape-string (escaped) + (concat "YASESCAPE" (format "%d" escaped) "PROTECTGUARD")) + (defun yas/protect-escapes () - "Protect all escaped characters with their numeric ASCII value.") + "Protect all escaped characters with their numeric ASCII value." + (mapc #'(lambda (escaped) + (yas/replace-all (concat "\\" (char-to-string escaped)) + (yas/escape-string escaped))) + '(?\\ ?` ?$ ?} ))) + (defun yas/restore-escapes () - "Restore all escaped characters from their numeric ASCII value.") + "Restore all escaped characters from their numeric ASCII value." + (mapc #'(lambda (escaped) + (yas/replace-all (concat "YASESCAPE" (format "%d" escaped) "PROTECTGUARD") + (char-to-string escaped))) + '(?\\ ?` ?$ ?} ))) + +(defun yas/replace-backquotes () + "Replace all the \"`(lisp-expression)`\"-style expression + with their evaluated value" + (while (re-search-forward yas/backquote-lisp-expression-regexp nil t) + (let ((transformed (yas/eval-string (match-string 1)))) + (goto-char (match-end 0)) + (insert transformed) + (delete-region (match-beginning 0) (match-end 0))))) (defun yas/field-parse-create (snippet &optional parent-field) "Parse the \"${n: }\" or \"$(lisp-expression)\" expressions, in two separate passes. -For \"$(lisp-expression)\" expressions \"lisp-expression\" is set to: +For \"$(lisp-expression)\" expressions \"lisp-expression\" is: - * The snippets exit-hook if PARENT-FIELD is nil; - * PARENT-FIELD's transform, otherwise. + * Replaced in-place with its value; + * set PARENT-FIELD's transform, otherwise. When multiple such expressions are found, only the last one counts." (save-excursion @@ -1456,13 +1501,16 @@ When multiple such expressions are found, only the last one counts." (goto-char (point-min)) (yas/field-parse-create snippet brand-new-field))))))) (save-excursion - (while (re-search-forward yas/expression-regexp nil t) + (while (re-search-forward yas/dollar-lisp-expression-regexp nil t) (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1))) (when real-match-end-0 + (let ((lisp-expression-string (buffer-substring-no-properties (match-beginning 1) real-match-end-0))) (if parent-field - (setf (yas/field-transform parent-field) (buffer-substring-no-properties (match-beginning 1) real-match-end-0)) - (setf (yas/snippet-exit-hook snippet) (buffer-substring-no-properties (match-beginning 1) real-match-end-0))) - (delete-region (match-beginning 0) real-match-end-0)))))) + (setf (yas/field-transform parent-field) lisp-expression-string) + (let ((transformed (yas/eval-string lisp-expression-string))) + (goto-char real-match-end-0) + (insert transformed))) + (delete-region (match-beginning 0) real-match-end-0))))))) (defun yas/transform-mirror-parse-create (snippet) "Parse the \"${n:(lisp-expression)}\" mirror transformations." @@ -1511,11 +1559,13 @@ When multiple such expressions are found, only the last one counts." (defun yas/mirror-update-display (mirror field) "Update MIRROR according to FIELD (and mirror transform)." - (goto-char (yas/mirror-start mirror)) - (delete-region (yas/mirror-start mirror) (yas/mirror-end mirror)) - (insert (yas/apply-transform mirror field)) - (set-marker (yas/mirror-end mirror) (point))) - + (let ((transformed (yas/apply-transform mirror field))) + (when (not (string= transformed (buffer-substring-no-properties (yas/mirror-start mirror) (yas/mirror-end mirror)))) + (goto-char (yas/mirror-start mirror)) + (insert transformed) + (if (> (yas/mirror-end mirror) (point)) + (delete-region (point) (yas/mirror-end mirror)) + (set-marker (yas/mirror-end mirror) (point)))))) ;; Debug functions. Use (or change) at will whenever needed. ;; @@ -1562,6 +1612,7 @@ When multiple such expressions are found, only the last one counts." (yas/load-directory "~/Source/yasnippet/snippets/") ;;(kill-buffer (get-buffer "*YAS TEST*")) (set-buffer (switch-to-buffer "*YAS TEST*")) + (mapcar #'yas/commit-snippet (yas/snippets-at-point 'all-snippets)) (erase-buffer) (setq buffer-undo-list nil) (html-mode) From 460bf76bae6b563d70b358536b8df24d38dfa1a3 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Thu, 9 Jul 2009 16:31:07 +0000 Subject: [PATCH 37/75] * Some little bug fixes, regexps could be cleaner, but oh well... --- yasnippet.el | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 5a21b2f..bc346ce 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -216,12 +216,10 @@ to expand. (defvar yas/known-modes '(ruby-mode rst-mode markdown-mode) "A list of mode which is well known but not part of emacs.") -(defconst yas/escape-backslash - (concat "YASESCAPE" "BACKSLASH" "PROTECTGUARD")) -(defconst yas/escape-dollar - (concat "YASESCAPE" "DOLLAR" "PROTECTGUARD")) -(defconst yas/escape-backquote - (concat "YASESCAPE" "BACKQUOTE" "PROTECTGUARD")) +(defvar yas/escaped-characters + '(?\\ ?` ?$ ?} ) + "A list of characters which *might* need to be escaped in +snippet templates") (defconst yas/field-regexp "${\\([0-9]+:\\)?\\([^}]*\\)}" @@ -236,7 +234,7 @@ to expand. "A regexp to recognize a \"`(...)`\" expression") (defconst yas/transform-mirror-regexp - "${\\(?:\\([0-9]+\\):\\)?$\\([^}]*\\)" + "${\\(?:\\([0-9]+\\):\\)?\\([^}]*\\)" "A regexp to *almost* recognize a mirror with a transform") (defconst yas/simple-mirror-regexp @@ -1450,14 +1448,14 @@ Meant to be called in a narrowed buffer, does various passes" (mapc #'(lambda (escaped) (yas/replace-all (concat "\\" (char-to-string escaped)) (yas/escape-string escaped))) - '(?\\ ?` ?$ ?} ))) + yas/escaped-characters)) (defun yas/restore-escapes () "Restore all escaped characters from their numeric ASCII value." (mapc #'(lambda (escaped) - (yas/replace-all (concat "YASESCAPE" (format "%d" escaped) "PROTECTGUARD") + (yas/replace-all (yas/escape-string escaped) (char-to-string escaped))) - '(?\\ ?` ?$ ?} ))) + yas/escaped-characters)) (defun yas/replace-backquotes () "Replace all the \"`(lisp-expression)`\"-style expression @@ -1483,8 +1481,7 @@ When multiple such expressions are found, only the last one counts." (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) (number (string-to-number (match-string-no-properties 1))) (brand-new-field (and real-match-end-0 - ;; (save-match-data (not (string-match "$(" (match-string-no-properties 2)))) - ;; .. shit... don't know why I added this line anymore + (not (eq ?\( (aref (match-string-no-properties 2) 0))) number (not (zerop number)) (yas/make-field number From daf3c788a5c1b1b4d782a791da6c5ea6bdb9354c Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Fri, 10 Jul 2009 15:37:21 +0000 Subject: [PATCH 38/75] * a little cheating sometimes inserts a newline at end of the buffer... --- yasnippet.el | 163 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 109 insertions(+), 54 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index bc346ce..e459a2c 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -95,6 +95,10 @@ mode will be listed under the menu \"yasnippet\".") (defvar yas/trigger-symbol " =>" "The text that will be used in menu to represent the trigger.") +(defvar yas/good-grace nil + "If non-nil, don't raise errors in inline elisp evaluation, +return the error string instead.") + (defface yas/field-highlight-face '((((class color) (background light)) (:background "DarkSeaGreen1")) (t (:background "DimGrey"))) @@ -272,13 +276,7 @@ You can customize the key through `yas/trigger-key'." ;; The indicator for the mode line. " yas" :group 'editing - (define-key yas/minor-mode-map yas/trigger-key 'yas/expand) - (if yas/minor-mode - (progn - (add-hook 'post-command-hook 'yas/post-command-handler nil t) - (add-hook 'pre-command-hook 'yas/pre-command-handler t t)) - (remove-hook 'post-command-hook 'yas/post-command-handler) - (remove-hook 'pre-command-hook 'yas/pre-command-handler))) + (define-key yas/minor-mode-map yas/trigger-key 'yas/expand)) (defun yas/minor-mode-auto-on () "Turn on YASnippet minor mode unless `yas/dont-activate' is @@ -383,8 +381,10 @@ a list of modes like this to help the judgement." (or (fboundp mode) (find mode yas/known-modes))) + + ;; TODO: This is a possible optimization point, the expression could -;; be stored in cons format instead of string, +;; be stored in cons format instead of string, (defun yas/eval-string (string) "Evaluate STRING and convert the result to string." (condition-case err @@ -395,8 +395,11 @@ a list of modes like this to help the judgement." (let ((result (eval (read string)))) (when result (format "%s" result)))))) - (error (format "(error in elisp evaluation: %s)" - (error-message-string err))))) + (error (if yas/good-grace + (format "(yasnippet: error in elisp evaluation: %s)" + (error-message-string err)) + (error (format "(yassnippet: error in elisp evaluation: %s)" + (error-message-string err))))))) (defun yas/snippet-table (mode) "Get the snippet table corresponding to MODE." @@ -862,8 +865,10 @@ when the condition evaluated to non-nil." "Calculate the value of the field/mirror. If there's a transform for this field, apply it. Otherwise, the value is returned unmodified." - (let* ((text (yas/field-text-for-display field)) - (modified-p (yas/field-modified-p field)) + (let* ((yas/text (yas/field-text-for-display field)) + (text yas/text) + (yas/modified-p (yas/field-modified-p field)) + (yas/moving-away nil) (transform (if (yas/mirror-p field-or-mirror) (yas/mirror-transform field-or-mirror) (yas/field-transform field-or-mirror))) @@ -908,11 +913,11 @@ have, compare through the field's start point" "Return a sorted list of snippets at point, most recently inserted first." (sort - (remove nil (mapcar #'(lambda (ov) - (overlay-get ov 'yas/snippet)) - (if all-snippets - (overlays-in (point-min) (point-max)) - (overlays-at (point))))) + (remove nil (remove-duplicates (mapcar #'(lambda (ov) + (overlay-get ov 'yas/snippet)) + (if all-snippets + (overlays-in (point-min) (point-max)) + (overlays-at (point)))))) #'(lambda (s1 s2) (<= (yas/snippet-id s2) (yas/snippet-id s1))))) @@ -928,6 +933,16 @@ inserted first." (yas/field-number active-field)))) (live-fields (remove-if #'yas/field-probably-deleted-p (yas/snippet-fields snippet))) (target-field (yas/snippet-find-field snippet number))) + ;; First check if we're moving out of a field + ;; + (when (and active-field + (yas/field-transform active-field)) + (let* ((yas/moving-away t) + (yas/text (yas/field-text-for-display active-field)) + (text yas/text) + (yas/modified-p (yas/field-modified-p active-field))) + (yas/eval-string (yas/field-transform active-field)))) + ;; Now actually move... (cond ((and number (> number (length live-fields))) (yas/exit-snippet snippet)) @@ -964,9 +979,6 @@ up the snippet does not delete it!" (yas/snippet-exit snippet) (overlay-end (yas/snippet-control-overlay snippet))))) -(defun yas/delete-overlay-region (overlay) - (delete-region (overlay-start overlay) (overlay-end overlay))) - ;;; Apropos markers-to-points: This can be useful for performance reasons, so ;;; that an excessive number of live markers arent kept aroung in the ;;; `buffer-undo-list'. In `markers-to-points', the set-to-nil @@ -1065,11 +1077,13 @@ exiting the snippet." (defun yas/check-commit-snippet () "Checks if point exited the currently active field of the snippet, if so cleans up the whole snippet up." - (let* ((snippets (yas/snippets-at-point 'all-snippets))) + (let* ((snippets (yas/snippets-at-point 'all-snippets)) + (snippets-left snippets)) (dolist (snippet snippets) (let ((active-field (yas/snippet-active-field snippet))) (cond ((not (and active-field (yas/field-contains-point-p active-field))) - (yas/commit-snippet snippet)) + (yas/commit-snippet snippet) + (setq snippets-left (delete snippet snippets-left))) ((and active-field (or (not yas/active-field-overlay) (not (overlay-buffer yas/active-field-overlay)))) @@ -1082,7 +1096,10 @@ snippet, if so cleans up the whole snippet up." (yas/move-to-field snippet active-field) (yas/update-mirrors snippet))) (t - nil)))))) + nil)))) + (unless snippets-left + (remove-hook 'post-command-hook 'yas/post-command-handler 'local) + (remove-hook 'pre-command-hook 'yas/pre-command-handler 'local)))) (defun yas/field-contains-point-p (field &optional point) (let ((point (or point @@ -1204,21 +1221,6 @@ progress." (yas/clear-field field)) (setf (yas/field-modified-p field) t)))))) -(defun yas/field-update-display (field snippet) - "Much like `yas/mirror-update-display', but for fields" - (when (yas/field-transform field) - (let ((inhibit-modification-hooks t) - (transformed (yas/apply-transform field field 'nil-on-empty)) - (point (point))) - (when (and transformed - (not (string= transformed (buffer-substring-no-properties (yas/field-start field) (yas/field-end field))))) - (goto-char (yas/field-start field)) - (insert transformed) - (if (> (yas/field-end field) (point)) - (delete-region (point) (yas/field-end field)) - (set-marker (yas/field-end field) (point))) - t)))) - ;;; ;;; Apropos protection overlays:... ;;; @@ -1240,19 +1242,31 @@ progress." (defun yas/make-move-field-protection-overlays (snippet field) "Place protection overlays surrounding SNIPPET's FIELD. -Move the overlays, or create them if they do not exit." - (cond ((and yas/field-protection-overlays - (every #'overlay-buffer yas/field-protection-overlays)) - (move-overlay (first yas/field-protection-overlays) (1- (yas/field-start field)) (yas/field-start field)) - (move-overlay (second yas/field-protection-overlays) (yas/field-end field) (1+ (yas/field-end field)))) - (t - (setq yas/field-protection-overlays - (list (make-overlay (1- (yas/field-start field)) (yas/field-start field) nil t nil) - (make-overlay (yas/field-end field) (1+ (yas/field-end field)) nil t nil))) - (dolist (ov yas/field-protection-overlays) - (overlay-put ov 'face 'yas/field-debug-face) - ;; (overlay-put ov 'evaporate t) - (overlay-put ov 'modification-hooks '(yas/on-protection-overlay-modification)))))) +Move the overlays, or create them if they do not exit." + (let ((start (yas/field-start field)) + (end (yas/field-end field))) + ;; First check if the (1+ end) is contained in the buffer, + ;; otherwise we'll have to do a bit of cheating and silently + ;; insert a newline. + (when (< (point-max) (1+ end)) + (save-excursion + (let ((inhibit-modification-hooks t)) + (goto-char (point-max)) + (newline)))) + ;; go on to normal overlay creation/moving + (cond ((and yas/field-protection-overlays + (every #'overlay-buffer yas/field-protection-overlays)) + (move-overlay (first yas/field-protection-overlays) (1- start) start) + (move-overlay (second yas/field-protection-overlays) end (1+ end))) + (t + (setq yas/field-protection-overlays + (list (make-overlay (1- start) start nil t nil) + (make-overlay end (1+ end) nil t nil))) + (dolist (ov yas/field-protection-overlays) + (overlay-put ov 'face 'yas/field-debug-face) + (overlay-put ov 'yas/snippet snippet) + ;; (overlay-put ov 'evaporate t) + (overlay-put ov 'modification-hooks '(yas/on-protection-overlay-modification))))))) (defvar yas/protection-violation nil "When non-nil, signals attempts to erronesly exit or modify the snippet. @@ -1374,6 +1388,9 @@ After revival, push the `yas/take-care-of-redo' in the (yas/move-to-field snippet (or (yas/snippet-active-field snippet) (car (yas/snippet-fields snippet)))) + (add-hook 'post-command-hook 'yas/post-command-handler nil t) + (add-hook 'pre-command-hook 'yas/pre-command-handler t t) + (push `(apply yas/take-care-of-redo ,beg ,end ,snippet) buffer-undo-list)) @@ -1396,6 +1413,10 @@ Returns the newly created snippet." ;; Move to end (goto-char (point-max)) + + ;; Setup hooks + (add-hook 'post-command-hook 'yas/post-command-handler nil t) + (add-hook 'pre-command-hook 'yas/pre-command-handler t t) snippet)) @@ -1438,7 +1459,14 @@ Meant to be called in a narrowed buffer, does various passes" (yas/simple-mirror-parse-create snippet) ;; restore escapes ;; - (yas/restore-escapes))) + (goto-char parse-start) + (yas/restore-escapes) + ;; indent the best we can + ;; + + + + )) (defun yas/escape-string (escaped) (concat "YASESCAPE" (format "%d" escaped) "PROTECTGUARD")) @@ -1556,14 +1584,41 @@ When multiple such expressions are found, only the last one counts." (defun yas/mirror-update-display (mirror field) "Update MIRROR according to FIELD (and mirror transform)." - (let ((transformed (yas/apply-transform mirror field))) - (when (not (string= transformed (buffer-substring-no-properties (yas/mirror-start mirror) (yas/mirror-end mirror)))) + (let ((transformed (yas/apply-transform mirror field 'nil-on-empty))) + (when (and transformed + (not (string= transformed (buffer-substring-no-properties (yas/mirror-start mirror) (yas/mirror-end mirror))))) (goto-char (yas/mirror-start mirror)) (insert transformed) (if (> (yas/mirror-end mirror) (point)) (delete-region (point) (yas/mirror-end mirror)) (set-marker (yas/mirror-end mirror) (point)))))) +(defun yas/field-update-display (field snippet) + "Much like `yas/mirror-update-display', but for fields" + (when (yas/field-transform field) + (let ((inhibit-modification-hooks t) + (transformed (yas/apply-transform field field 'nil-on-empty)) + (point (point))) + (when (and transformed + (not (string= transformed (buffer-substring-no-properties (yas/field-start field) (yas/field-end field))))) + (setf (yas/field-modified-p field) t) + (goto-char (yas/field-start field)) + (insert transformed) + (if (> (yas/field-end field) (point)) + (delete-region (point) (yas/field-end field)) + (set-marker (yas/field-end field) (point))) + t)))) + +;; User convenience functions + +(defun yas/choose (&rest possibilities) + (ido-completing-read "Choose: " possibilities nil nil nil nil (car possibilities))) + +(defun yas/restrict (&rest possibilities) + (when (notany #'(lambda (pos) (string= pos yas/text)) possibilities) + (error "hey don't move away just now"))) + + ;; Debug functions. Use (or change) at will whenever needed. ;; From 10e8b41f7c26a9d4fe30f17090f43054390bc1a3 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Sat, 11 Jul 2009 00:31:56 +0000 Subject: [PATCH 39/75] * fixed two small bugs introduced earlier, have to retest stacked expansion once in a while, wish I had unit tests... --- yasnippet.el | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index e459a2c..1ae2449 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -381,8 +381,6 @@ a list of modes like this to help the judgement." (or (fboundp mode) (find mode yas/known-modes))) - - ;; TODO: This is a possible optimization point, the expression could ;; be stored in cons format instead of string, (defun yas/eval-string (string) @@ -398,7 +396,7 @@ a list of modes like this to help the judgement." (error (if yas/good-grace (format "(yasnippet: error in elisp evaluation: %s)" (error-message-string err)) - (error (format "(yassnippet: error in elisp evaluation: %s)" + (error (format "(yasnippet: error in elisp evaluation: %s)" (error-message-string err))))))) (defun yas/snippet-table (mode) @@ -861,10 +859,9 @@ when the condition evaluated to non-nil." start end (transform nil)) -(defun yas/apply-transform (field-or-mirror field &optional nil-on-empty) +(defun yas/apply-transform (field-or-mirror field) "Calculate the value of the field/mirror. If there's a transform -for this field, apply it. Otherwise, the value is returned -unmodified." +for this field, apply it. Otherwise, returned nil." (let* ((yas/text (yas/field-text-for-display field)) (text yas/text) (yas/modified-p (yas/field-modified-p field)) @@ -874,8 +871,7 @@ unmodified." (yas/field-transform field-or-mirror))) (transformed (and transform (yas/eval-string transform)))) - (or transformed - (unless nil-on-empty text)))) + transformed)) (defsubst yas/replace-all (from to) @@ -1145,7 +1141,7 @@ holds the keymap." (let ((overlay (make-overlay start end nil - t + nil t))) (overlay-put overlay 'keymap yas/keymap) (overlay-put overlay 'yas/snippet snippet) @@ -1247,13 +1243,16 @@ Move the overlays, or create them if they do not exit." (end (yas/field-end field))) ;; First check if the (1+ end) is contained in the buffer, ;; otherwise we'll have to do a bit of cheating and silently - ;; insert a newline. - (when (< (point-max) (1+ end)) + ;; insert a newline. the `(1+ (buffer-size))' should prevent this + ;; when using stacked expansion + ;; + (when (< (1+ (buffer-size)) (1+ end)) (save-excursion (let ((inhibit-modification-hooks t)) (goto-char (point-max)) (newline)))) ;; go on to normal overlay creation/moving + ;; (cond ((and yas/field-protection-overlays (every #'overlay-buffer yas/field-protection-overlays)) (move-overlay (first yas/field-protection-overlays) (1- start) start) @@ -1584,11 +1583,12 @@ When multiple such expressions are found, only the last one counts." (defun yas/mirror-update-display (mirror field) "Update MIRROR according to FIELD (and mirror transform)." - (let ((transformed (yas/apply-transform mirror field 'nil-on-empty))) - (when (and transformed - (not (string= transformed (buffer-substring-no-properties (yas/mirror-start mirror) (yas/mirror-end mirror))))) + (let ((reflection (or (yas/apply-transform mirror field) + (yas/field-text-for-display field)))) + (when (and reflection + (not (string= reflection (buffer-substring-no-properties (yas/mirror-start mirror) (yas/mirror-end mirror))))) (goto-char (yas/mirror-start mirror)) - (insert transformed) + (insert reflection) (if (> (yas/mirror-end mirror) (point)) (delete-region (point) (yas/mirror-end mirror)) (set-marker (yas/mirror-end mirror) (point)))))) @@ -1597,7 +1597,7 @@ When multiple such expressions are found, only the last one counts." "Much like `yas/mirror-update-display', but for fields" (when (yas/field-transform field) (let ((inhibit-modification-hooks t) - (transformed (yas/apply-transform field field 'nil-on-empty)) + (transformed (yas/apply-transform field field)) (point (point))) (when (and transformed (not (string= transformed (buffer-substring-no-properties (yas/field-start field) (yas/field-end field))))) @@ -1614,8 +1614,8 @@ When multiple such expressions are found, only the last one counts." (defun yas/choose (&rest possibilities) (ido-completing-read "Choose: " possibilities nil nil nil nil (car possibilities))) -(defun yas/restrict (&rest possibilities) - (when (notany #'(lambda (pos) (string= pos yas/text)) possibilities) +(defun yas/verify (&rest possibilities) + (when (and yas/moving-away (notany #'(lambda (pos) (string= pos yas/text)) possibilities)) (error "hey don't move away just now"))) @@ -1667,7 +1667,7 @@ When multiple such expressions are found, only the last one counts." (mapcar #'yas/commit-snippet (yas/snippets-at-point 'all-snippets)) (erase-buffer) (setq buffer-undo-list nil) - (html-mode) + (text-mode) (yas/minor-mode) (let ((abbrev)) ;; (if (require 'ido nil t) From 555897a4ade4009b94f1e996f62ecfb531fa7aa8 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Sat, 11 Jul 2009 00:59:27 +0000 Subject: [PATCH 40/75] * corrected another problem when evaling user elisp with errors --- yasnippet.el | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 1ae2449..e9cbf3f 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -114,11 +114,11 @@ return the error string instead.") (t (:background "tomato"))) "The face used for debugging") -(defvar yas/window-system-popup-function #'yas/dropdown-list-popup-for-template +(defvar yas/window-system-popup-function #'yas/x-popup-menu-for-template "When there's multiple candidate for a snippet key. This function is called to let user select one of them. `yas/text-popup-function' is used instead when not in a window system.") -(defvar yas/text-popup-function #'yas/dropdown-list-popup-for-template +(defvar yas/text-popup-function #'yas/x-popup-menu-for-template "When there's multiple candidate for a snippet key. If not in a window system, this function is called to let user select one of them. `yas/window-system-popup-function' is used instead when in @@ -1325,11 +1325,18 @@ will be deleted before inserting template." ;; at the end of this function. ;; (save-restriction - (let ((buffer-undo-list t) - (template-start end)) - (narrow-to-region template-start template-start) - (insert template) - (setq snippet (yas/snippet-create (point-min) (point-max))))) + (narrow-to-region end end) + (condition-case err + (let ((buffer-undo-list t)) + ;; snippet creation might evaluate users elisp, which + ;; might generate errors, so we have to be ready to catch + ;; them mostly to make the undo information + ;; + (insert template) + (setq snippet (yas/snippet-create (point-min) (point-max)))) + (error + (push (cons (point-min) (point-max)) buffer-undo-list) + (error (error-message-string err))))) ;; Delete the trigger key, this *does* get undo-recorded. ;; (delete-region start end) From ad570bf9e5be42bb8ab2e686cd2cc96e54f94e26 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Sat, 11 Jul 2009 17:29:46 +0000 Subject: [PATCH 41/75] * region now represented as `yas/deleted-text' * syntax now correctly backward compatible * more little bugs fixed... --- yasnippet.el | 101 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 40 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index e9cbf3f..13c1fa2 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -229,7 +229,7 @@ snippet templates") "${\\([0-9]+:\\)?\\([^}]*\\)}" "A regexp to *almost* recognize a field") -(defconst yas/dollar-lisp-expression-regexp +(defconst yas/multi-dollar-lisp-expression-regexp "$\\(([^)]*)\\)" "A regexp to *almost* recognize a \"$(...)\" expression") @@ -238,7 +238,7 @@ snippet templates") "A regexp to recognize a \"`(...)`\" expression") (defconst yas/transform-mirror-regexp - "${\\(?:\\([0-9]+\\):\\)?\\([^}]*\\)" + "${\\(?:\\([0-9]+\\):\\)?$\\([^}]*\\)" "A regexp to *almost* recognize a mirror with a transform") (defconst yas/simple-mirror-regexp @@ -494,9 +494,12 @@ Here's a list of currently recognized variables: (lexical-let ((template template)) (lambda () (interactive) - (yas/expand-snippet (point) - (point) - template)))) + (let ((where (if mark-active + (cons (region-beginning) (region-end)) + (cons (point) (point))))) + (yas/expand-snippet (car where) + (cdr where) + template))))) (defun yas/modify-alist (alist key value) "Modify ALIST to map KEY to VALUE. return the new alist." @@ -826,8 +829,12 @@ when the condition evaluated to non-nil." (defvar yas/field-protection-overlays nil "Two overlays protect the current active field ") +(defvar yas/deleted-text nil + "The text deleted in the last snippet expansion") + (make-variable-buffer-local 'yas/active-field-overlay) (make-variable-buffer-local 'yas/field-protection-overlays) +(make-variable-buffer-local 'yas/deleted-text) (defstruct (yas/snippet (:constructor yas/make-snippet ())) "A snippet. @@ -1117,8 +1124,10 @@ snippet, if so cleans up the whole snippet up." ;; (let* ((snippet (car (yas/snippets-at-point))) (target-field (and snippet - (find-if-not #'yas/field-probably-deleted-p (cons (yas/snippet-active-field snippet) - (yas/snippet-fields snippet)))))) + (find-if-not #'yas/field-probably-deleted-p + (remove nil + (cons (yas/snippet-active-field snippet) + (yas/snippet-fields snippet))))))) (when target-field (yas/move-to-field snippet target-field)))) ((not (yas/undo-in-progress)) @@ -1333,10 +1342,12 @@ will be deleted before inserting template." ;; them mostly to make the undo information ;; (insert template) + (setq yas/deleted-text key) (setq snippet (yas/snippet-create (point-min) (point-max)))) (error (push (cons (point-min) (point-max)) buffer-undo-list) - (error (error-message-string err))))) + (signal (car err) (cadr err))))) + ;; Delete the trigger key, this *does* get undo-recorded. ;; (delete-region start end) @@ -1388,17 +1399,22 @@ After revival, push the `yas/take-care-of-redo' in the ;; Reconvert all the points to markers ;; (yas/points-to-markers snippet) - - (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay beg end)) - (overlay-put (yas/snippet-control-overlay snippet) 'yas/snippet snippet) - (yas/move-to-field snippet (or (yas/snippet-active-field snippet) - (car (yas/snippet-fields snippet)))) + ;; When at least one editable field existed in the zombie snippet, + ;; try to revive the whole thing... + ;; + (let ((target-field (or (yas/snippet-active-field snippet) + (car (yas/snippet-fields snippet))))) + (when target-field + (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay beg end)) + (overlay-put (yas/snippet-control-overlay snippet) 'yas/snippet snippet) + + (yas/move-to-field snippet target-field) - (add-hook 'post-command-hook 'yas/post-command-handler nil t) - (add-hook 'pre-command-hook 'yas/pre-command-handler t t) + (add-hook 'post-command-hook 'yas/post-command-handler nil t) + (add-hook 'pre-command-hook 'yas/pre-command-handler t t) - (push `(apply yas/take-care-of-redo ,beg ,end ,snippet) - buffer-undo-list)) + (push `(apply yas/take-care-of-redo ,beg ,end ,snippet) + buffer-undo-list)))) (defun yas/snippet-create (begin end) "Creates a snippet from an template inserted between BEGIN and END. @@ -1515,7 +1531,8 @@ When multiple such expressions are found, only the last one counts." (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) (number (string-to-number (match-string-no-properties 1))) (brand-new-field (and real-match-end-0 - (not (eq ?\( (aref (match-string-no-properties 2) 0))) + (not (save-match-data + (eq (string-match "$(" (match-string-no-properties 2)) 0))) number (not (zerop number)) (yas/make-field number @@ -1531,27 +1548,25 @@ When multiple such expressions are found, only the last one counts." (narrow-to-region (yas/field-start brand-new-field) (yas/field-end brand-new-field)) (goto-char (point-min)) (yas/field-parse-create snippet brand-new-field))))))) - (save-excursion - (while (re-search-forward yas/dollar-lisp-expression-regexp nil t) - (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1))) - (when real-match-end-0 - (let ((lisp-expression-string (buffer-substring-no-properties (match-beginning 1) real-match-end-0))) - (if parent-field - (setf (yas/field-transform parent-field) lisp-expression-string) - (let ((transformed (yas/eval-string lisp-expression-string))) - (goto-char real-match-end-0) - (insert transformed))) - (delete-region (match-beginning 0) real-match-end-0))))))) + (when parent-field + (save-excursion + (while (re-search-forward yas/multi-dollar-lisp-expression-regexp nil t) + (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1))) + (when real-match-end-0 + (let ((lisp-expression-string (buffer-substring-no-properties (match-beginning 1) real-match-end-0))) + (setf (yas/field-transform parent-field) lisp-expression-string)) + (delete-region (match-beginning 0) real-match-end-0))))))) (defun yas/transform-mirror-parse-create (snippet) - "Parse the \"${n:(lisp-expression)}\" mirror transformations." + "Parse the \"${n:$(lisp-expression)}\" mirror transformations." (while (re-search-forward yas/transform-mirror-regexp nil t) (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) (number (string-to-number (match-string-no-properties 1))) (field (and number (not (zerop number)) (yas/snippet-find-field snippet number)))) - (when (and real-match-end-0 field) + (when (and real-match-end-0 + field) (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) (set-marker (make-marker) (match-beginning 0)) (buffer-substring-no-properties (match-beginning 2) @@ -1569,12 +1584,17 @@ When multiple such expressions are found, only the last one counts." (delete-region (match-beginning 0) (match-end 0))) (t (let ((field (yas/snippet-find-field snippet number))) - (when field - (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) - (set-marker (make-marker) (match-beginning 0)) - nil) - (yas/field-mirrors field)) - (delete-region (match-beginning 0) (match-end 0))))))))) + (if field + (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) + (set-marker (make-marker) (match-beginning 0)) + nil) + (yas/field-mirrors field)) + (push (yas/make-field number + (set-marker (make-marker) (match-beginning 0)) + (set-marker (make-marker) (match-beginning 0)) + nil) + (yas/snippet-fields snippet)))) + (delete-region (match-beginning 0) (match-end 0))))))) (defun yas/update-mirrors (snippet) "Updates all the mirrors of SNIPPET." @@ -1674,15 +1694,16 @@ When multiple such expressions are found, only the last one counts." (mapcar #'yas/commit-snippet (yas/snippets-at-point 'all-snippets)) (erase-buffer) (setq buffer-undo-list nil) - (text-mode) - (yas/minor-mode) + (c-mode) + (yas/initialize) + (yas/minor-mode 1) (let ((abbrev)) ;; (if (require 'ido nil t) ;; (setq abbrev (ido-completing-read "Snippet abbrev: " '("crazy" "prip" "prop"))) ;; (setq abbrev "prop")) (setq abbrev "bosta") (insert abbrev)) - (unless quiet + (when quiet (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local)) ) From 01c626efbdca2909f6fbcfdad9f12d7d3effafed Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Sat, 11 Jul 2009 18:09:25 +0000 Subject: [PATCH 42/75] * started attack on template code as well * still got indenting to do --- yasnippet.el | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/yasnippet.el b/yasnippet.el index 13c1fa2..46e0153 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -350,6 +350,25 @@ fetch from parent if any." (yas/snippet-table-parent table) key))) templates)) + +(defun yas/snippet-table-all-templates (table) + (when table + (let ((acc)) + (maphash #'(lambda (key templates) + (setq acc (append acc templates))) + (yas/snippet-table-hash table)) + (append acc + (yas/snippet-table-all-templates (yas/snippet-table-parent table)))))) + +(defun yas/snippet-table-all-keys (table) + (when table + (let ((acc)) + (maphash #'(lambda (key templates) + (push key acc)) + (yas/snippet-table-hash table)) + (append acc + (yas/snippet-table-all-templates (yas/snippet-table-parent table)))))) + (defun yas/snippet-table-store (table full-key key template) "Store a snippet template in the table." (puthash key @@ -792,6 +811,19 @@ when the condition evaluated to non-nil." (undo 1)) nil)) +;; (defun yas/completing-expand () +;; "Choose a snippet to expand, pop-up a list of choices according +;; to `yas/popup-function'" +;; (let ((keys) +;; (choice)) +;; (maphash #'(lambda (key val) +;; (push key keys)) (yas/current-snippet-table)) +;; (let ((choice (and keys +;; (ido-completing-read "Choose: " keys nil nil nil nil (car possibilities)))) +;; (template (and choice +;; (gethash choice (yas/current-snippet-table))))) + + (defun yas/expand () "Expand a snippet." (interactive) From 3dd0ba61066a9d3e302a0b7af33c5e20d9b7462c Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Sun, 12 Jul 2009 19:59:32 +0000 Subject: [PATCH 43/75] * Butchered template choosing code TODO: * Still some old syntax to take care of (simple ${field} without number) * Add messages to snippet events, not just errors * fix many bugs * make the customization group * merge from trunk ... --- yasnippet.el | 239 ++++++++++++++++++++++++++++----------------------- 1 file changed, 132 insertions(+), 107 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 46e0153..2efe522 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -114,15 +114,13 @@ return the error string instead.") (t (:background "tomato"))) "The face used for debugging") -(defvar yas/window-system-popup-function #'yas/x-popup-menu-for-template - "When there's multiple candidate for a snippet key. This function -is called to let user select one of them. `yas/text-popup-function' -is used instead when not in a window system.") -(defvar yas/text-popup-function #'yas/x-popup-menu-for-template - "When there's multiple candidate for a snippet key. If not in a -window system, this function is called to let user select one of -them. `yas/window-system-popup-function' is used instead when in -a window system.") +(defvar yas/popup-functions + '( yas/x-popup + yas/ido-popup + yas/dropdown-popup + yas/completing-popup + yas/no-popup )) + (defvar yas/extra-mode-hooks '() @@ -327,23 +325,25 @@ set to t." (remove-if-not '(lambda (pair) (let ((condition (yas/template-condition (cdr pair)))) (if (null condition) - (if yas/require-template-condition + (if (yas/require-template-condition) nil t) (let ((result (yas/template-condition-predicate condition))) - (if yas/require-template-condition - (if (eq yas/require-template-condition t) + (if (yas/require-template-condition) + (if (eq (yas/require-template-condition) t) result - (eq result yas/require-template-condition)) + (eq result (yas/require-template-condition))) result))))) templates)) -(defun yas/snippet-table-fetch (table key) +(defun yas/snippet-table-fetch (table key &optional no-condition) "Fetch a snippet binding to KEY from TABLE. If not found, fetch from parent if any." - (let ((templates (yas/filter-templates-by-condition - (gethash key (yas/snippet-table-hash table))))) + (let* ((unfiltered (gethash key (yas/snippet-table-hash table))) + (templates (or (and no-condition + unfiltered) + (yas/filter-templates-by-condition unfiltered)))) (when (and (null templates) (not (null (yas/snippet-table-parent table)))) (setq templates (yas/snippet-table-fetch @@ -357,17 +357,18 @@ fetch from parent if any." (maphash #'(lambda (key templates) (setq acc (append acc templates))) (yas/snippet-table-hash table)) - (append acc + (append (yas/filter-templates-by-condition acc) (yas/snippet-table-all-templates (yas/snippet-table-parent table)))))) (defun yas/snippet-table-all-keys (table) (when table (let ((acc)) (maphash #'(lambda (key templates) - (push key acc)) + (when (yas/filter-templates-by-condition templates) + (push key acc))) (yas/snippet-table-hash table)) (append acc - (yas/snippet-table-all-templates (yas/snippet-table-parent table)))))) + (yas/snippet-table-all-keys (yas/snippet-table-parent table)))))) (defun yas/snippet-table-store (table full-key key template) "Store a snippet template in the table." @@ -400,9 +401,9 @@ a list of modes like this to help the judgement." (or (fboundp mode) (find mode yas/known-modes))) -;; TODO: This is a possible optimization point, the expression could -;; be stored in cons format instead of string, (defun yas/eval-string (string) + ;; TODO: This is a possible optimization point, the expression could + ;; be stored in cons format instead of string, "Evaluate STRING and convert the result to string." (condition-case err (save-excursion @@ -413,9 +414,9 @@ a list of modes like this to help the judgement." (when result (format "%s" result)))))) (error (if yas/good-grace - (format "(yasnippet: error in elisp evaluation: %s)" + (format "([yas] elisp error: %s" (error-message-string err)) - (error (format "(yasnippet: error in elisp evaluation: %s)" + (error (format "([yas] elisp error: %s" (error-message-string err))))))) (defun yas/snippet-table (mode) @@ -529,59 +530,74 @@ Here's a list of currently recognized variables: (setcdr pair value) alist))) -(defun yas/fake-keymap-for-popup (templates) - "Create a fake keymap for popup menu usage." - (cons 'keymap - (mapcar (lambda (pair) - (let* ((template (cdr pair)) - (name (yas/template-name template)) - (content (yas/template-content template))) - (list content 'menu-item name t))) - templates))) +;; Popping up for keys and templates +;; +(defun yas/popup-for-template-content (templates) + "Interactively choose a template's content from the list +TEMPLATES." + (let ((template (some #'(lambda (fn) + (funcall fn "Choose a snippet: " templates #'(lambda (template) + (yas/template-name template)))) + yas/popup-functions))) + (when template + (yas/template-content template)))) -(defun yas/point-to-coord (&optional point) - "Get the xoffset/yoffset information of POINT. -If POINT is not given, default is to current point. -If `posn-at-point' is not available (like in Emacs 21.3), -t is returned simply." - (if (fboundp 'posn-at-point) - (let ((x-y (posn-x-y (posn-at-point (or point (point)))))) - (list (list (+ (car x-y) 10) - (+ (cdr x-y) 20)) - (selected-window))) - t)) +(defun yas/popup-for-keys (keys) + "Interactively choose a template key from the list KEYS." + (some #'(lambda (fn) + (funcall fn "Choose a snippet key: " keys)) + yas/popup-functions)) -(defun yas/x-popup-menu-for-template (templates) - "Show a popup menu listing templates to let the user select one." - (car (x-popup-menu (yas/point-to-coord) - (yas/fake-keymap-for-popup templates)))) +(defun yas/x-popup (prompt choices &optional display-fn) + (when window-system + (let ((keymap (cons 'keymap + (cons + prompt + (mapcar (lambda (choice) + (list choice + 'menu-item + (if display-fn + (funcall display-fn choice) + choice) + t)) + choices))))) + (when (cdr keymap) + (car (x-popup-menu (if (fboundp 'posn-at-point) + (let ((x-y (posn-x-y (posn-at-point (point))))) + (list (list (+ (car x-y) 10) + (+ (cdr x-y) 20)) + (selected-window))) + t) + keymap)))))) -(defun yas/text-popup-for-template (templates) - "Can't display popup menu in text mode. Just select the first one." - (yas/template-content (cdar templates))) -(defun yas/dropdown-list-popup-for-template (templates) - "Use dropdown-list.el to popup for templates. Better than the -default \"select first\" behavior of `yas/text-popup-for-template'. -You can also use this in window-system. +(defun yas/ido-popup (prompt choices &optional display-fn) + (when (featurep 'ido) + (let* ((formatted-choices (or (and display-fn + (mapcar display-fn choices)) + choices)) + (chosen (and choices + (ido-completing-read prompt + formatted-choices + nil + 'require-match + nil + nil)))) + (when chosen + (nth (position chosen formatted-choices) choices))))) -NOTE: You need to download and install dropdown-list.el to use this." - (if (fboundp 'dropdown-list) - (let ((n (dropdown-list (mapcar (lambda (i) - (yas/template-name - (cdr i))) - templates)))) - (if n - (yas/template-content - (cdr (nth n templates))) - nil)) - (error "Please download and install dropdown-list.el to use this"))) +(defun yas/dropdown-popup (prompt choices &optional display-fn) + (when (featurep 'dropdown-list) + )) -(defun yas/popup-for-template (templates) - (if window-system - (funcall yas/window-system-popup-function templates) - (funcall yas/text-popup-function templates))) +(defun yas/completing-popup (prompt choices &optional display-fn) + ) +(defun yas/no-popup (prompt choices &optional display-fn) + ) + +;; Loading snippets +;; (defun yas/load-directory-1 (directory &optional parent) "Really do the job of loading snippets from a directory hierarchy." @@ -811,46 +827,55 @@ when the condition evaluated to non-nil." (undo 1)) nil)) -;; (defun yas/completing-expand () -;; "Choose a snippet to expand, pop-up a list of choices according -;; to `yas/popup-function'" -;; (let ((keys) -;; (choice)) -;; (maphash #'(lambda (key val) -;; (push key keys)) (yas/current-snippet-table)) -;; (let ((choice (and keys -;; (ido-completing-read "Choose: " keys nil nil nil nil (car possibilities)))) -;; (template (and choice -;; (gethash choice (yas/current-snippet-table))))) - +(defun yas/require-template-condition () + (let ((local-condition (yas/template-condition-predicate + yas/buffer-local-condition))) + (and local-condition + (consp local-condition) + (eq 'require-snippet-condition (car local-condition)) + (symbolp (cdr local-condition)) + (cdr local-condition)))) (defun yas/expand () "Expand a snippet." (interactive) - (let ((local-condition (yas/template-condition-predicate - yas/buffer-local-condition))) - (if local-condition - (let ((yas/require-template-condition - (if (and (consp local-condition) - (eq 'require-snippet-condition (car local-condition)) - (symbolp (cdr local-condition))) - (cdr local-condition) - nil))) - (multiple-value-bind (templates start end) (yas/current-key) - (if templates - (let ((template (if (null (cdr templates)) ; only 1 template - (yas/template-content (cdar templates)) - (yas/popup-for-template templates)))) - (if template - (progn (yas/expand-snippet start end template) - 'expanded) ; expanded successfully - 'interrupted)) ; interrupted by user - (if (eq yas/fallback-behavior 'return-nil) - nil ; return nil - (let* ((yas/minor-mode nil) - (command (key-binding yas/trigger-key))) - (when (commandp command) - (call-interactively command)))))))))) + (multiple-value-bind (templates start end) (yas/current-key) + (if templates + (let ((template-content (yas/popup-for-template-content templates))) + (when template-content + (yas/expand-snippet start end template-content))) + (if (eq yas/fallback-behavior 'return-nil) + nil ; return nil + (let* ((yas/minor-mode nil) + (command (key-binding yas/trigger-key))) + (when (commandp command) + (call-interactively command))))))) + +(defvar yas/complete-for-keys t + "If non-nil, `yas/completing-expand' prompts for key, then for template. + +Otherwise 'yas/completing-expand' prompts for all possible +templates and inserts the selected one.") + +(defun yas/completing-expand () + "Choose a snippet to expand, pop-up a list of choices according +to `yas/popup-function'." + (interactive) + (let* ((templates (mapcar #'cdr + (if yas/complete-for-keys + (let ((key (yas/popup-for-keys (yas/snippet-table-all-keys (yas/current-snippet-table))))) + (when key + (yas/snippet-table-fetch (yas/current-snippet-table) key 'no-condition))) + (yas/snippet-table-all-templates (yas/current-snippet-table))))) + (template-content (and templates + (or (and (cdr templates) + (yas/popup-for-template-content templates)) + (yas/template-content (car templates))))) + (where (if mark-active + (cons (region-beginning) (region-end)) + (cons (point) (point))))) + (when template-content + (yas/expand-snippet (car where) (cdr where) template-content)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Snippet expansion and field managment @@ -859,7 +884,7 @@ when the condition evaluated to non-nil." "Overlays the currently active field") (defvar yas/field-protection-overlays nil - "Two overlays protect the current active field ") + "Two overlays protect the current actipve field ") (defvar yas/deleted-text nil "The text deleted in the last snippet expansion") @@ -1378,7 +1403,7 @@ will be deleted before inserting template." (setq snippet (yas/snippet-create (point-min) (point-max)))) (error (push (cons (point-min) (point-max)) buffer-undo-list) - (signal (car err) (cadr err))))) + (error (cadr err))))) ;; Delete the trigger key, this *does* get undo-recorded. ;; From 953ffbf29942e57eb25ace6a3576c54cd7ca0514 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Sun, 12 Jul 2009 20:18:40 +0000 Subject: [PATCH 44/75] * `yas/next-field' needs to be rewritten to account for unnumbered fields --- yasnippet.el | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 2efe522..9f4d44e 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -989,6 +989,7 @@ inserted first." (snippet (first (yas/snippets-at-point))) (active-field (overlay-get yas/active-field-overlay 'yas/field)) (number (and snippet + (yas/field-number active-field) (+ arg (yas/field-number active-field)))) (live-fields (remove-if #'yas/field-probably-deleted-p (yas/snippet-fields snippet))) @@ -1574,24 +1575,24 @@ Meant to be called in a narrowed buffer, does various passes" (delete-region (match-beginning 0) (match-end 0))))) (defun yas/field-parse-create (snippet &optional parent-field) - "Parse the \"${n: }\" or \"$(lisp-expression)\" expressions, in -two separate passes. + "Parse most field expression, except for the simple one \"$n\". -For \"$(lisp-expression)\" expressions \"lisp-expression\" is: +The following count as a field: - * Replaced in-place with its value; - * set PARENT-FIELD's transform, otherwise. +* \"${n: text}\", for a numbered field with default text, as long as N is not 0; +* \"${n: text$(expression)}, the same with a lisp expression; +* the same as above but unnumbered, (no N:) and number is calculated automatically. -When multiple such expressions are found, only the last one counts." +When multiple expressions are found, only the last one counts." (save-excursion (while (re-search-forward yas/field-regexp nil t) (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) - (number (string-to-number (match-string-no-properties 1))) + (number (and (match-string-no-properties 1) + (string-to-number (match-string-no-properties 1)))) (brand-new-field (and real-match-end-0 (not (save-match-data (eq (string-match "$(" (match-string-no-properties 2)) 0))) - number - (not (zerop number)) + (not (and number (zerop number))) (yas/make-field number (set-marker (make-marker) (match-beginning 2)) (set-marker (make-marker) (1- real-match-end-0)) From 0622075ba95093a8725052043bb4c7c0a836e535 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 13 Jul 2009 07:13:58 +0000 Subject: [PATCH 45/75] * `yas/next-field' rewritten, now `yas/expand' fails... --- yasnippet.el | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 9f4d44e..97145fe 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -988,13 +988,11 @@ inserted first." 1)) (snippet (first (yas/snippets-at-point))) (active-field (overlay-get yas/active-field-overlay 'yas/field)) - (number (and snippet - (yas/field-number active-field) - (+ arg - (yas/field-number active-field)))) (live-fields (remove-if #'yas/field-probably-deleted-p (yas/snippet-fields snippet))) - (target-field (yas/snippet-find-field snippet number))) - ;; First check if we're moving out of a field + (active-field-pos (position active-field live-fields)) + (target-pos (+ arg active-field-pos)) + (target-field (nth target-pos live-fields))) + ;; First check if we're moving out of a field with a transform ;; (when (and active-field (yas/field-transform active-field)) @@ -1004,8 +1002,7 @@ inserted first." (yas/modified-p (yas/field-modified-p active-field))) (yas/eval-string (yas/field-transform active-field)))) ;; Now actually move... - (cond ((and number - (> number (length live-fields))) + (cond ((>= target-pos (length live-fields)) (yas/exit-snippet snippet)) (target-field (yas/move-to-field snippet target-field)) From bc7acbaf99649aeabf7fec85dec51ffd410b897e Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 13 Jul 2009 22:19:41 +0000 Subject: [PATCH 46/75] * typed a lot of cleanup code, probably many many bugs * at least most of the customization group is here I think * still got a lot to do for the minor mode (including making it global) * TODO: write wrap regions * TODO: write per-snippet variable overrides * goto sleep --- yasnippet.el | 553 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 338 insertions(+), 215 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 97145fe..ebcd5f2 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -2,8 +2,8 @@ ;; Copyright 2008 pluskid ;; -;; Author: pluskid -;; Version: 0.5.6 XXX: Change this +;; Authors: pluskid , joaotavora +;; Version: 0.6.0 XXX: Change this ;; X-URL: http://code.google.com/p/yasnippet/ ;; This file is free software; you can redistribute it and/or modify @@ -39,10 +39,179 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; User customizable variables +;; + +(defgroup yasnippet nil + "Yet Another Snippet extension" + :group 'editing) + +(defcustom yas/root-directory nil + "The (list of) root directory that stores the snippets for each +major mode." + :type '(string) + :group 'yasnippet) + +(defcustom yas/prompt-functions '(yas/x-prompt + yas/ido-prompt + yas/dropdown-prompt + yas/completing-prompt + yas/no-prompt) + "List of functions to prompt the user for keys, templates and +other values interactively." + :type 'list + :group 'yasnippet) + +(defcustom yas/indent-line 'auto + "Controls indenting applied to a recent snippet expansion. + +The following values are possible: + +`nothing' Don't apply any indendation after expansion; + +`fixed' Indent the snippet to the current column; + +`auto' Indent each line of the snippet with `indent-according-to-mode'" + :type '(choice (const :tag "Nothing" nothing) + (const :tag "Fixed" always) + (const :tag "Auto" auto)) + :group 'yasnippet) + +(defcustom yas/snippet-revival t + "Non-nil means re-activate snippet fields after an when undoing +an exit from an active snippet or redoing a snippet expansion" + :type 'boolean + :group 'yasnippet) + +(defcustom yas/trigger-key "" + "The key to bind as a trigger of snippet when `yas/minor-mode' +is active. + +Value is a string that is converted to the internal Emacs key +representation using `read-kbd-macro'. " + :type 'string + :group 'yasnippet) + +(defcustom yas/next-field-key "" + "The key to navigate to next field when a snippet is active. + +Value is a string that is converted to the internal Emacs key +representation using `read-kbd-macro'. " + :type 'string + :group 'yasnippet) + +(defcustom yas/prev-field-key "S-" + "The key to navigate to previous field when a snippet is active. + +Value is a string that is converted to the internal Emacs key +representation using `read-kbd-macro'. " + :type 'string + :group 'yasnippet) + +(defcustom yas/clear-field-key "C-d" + "The key to clear the currently active field. + +Value is a string that is converted to the internal Emacs key +representation using `read-kbd-macro'. " + :type 'string + :group 'yasnippet) + +(defcustom yas/triggers-in-field t + "If non-nil, allow `yas/next-field-key' to trigger a stacked + snippet expansion. + +Otherwise, `yas/next-field-key' just tries to move on to the next field" + :type 'boolean + :group 'yasnippet) + +(defcustom yas/fallback-behavior 'call-other-command + "How to act when `yas/trigger-key' does *not* expand a snippet. + +The fall back behavior of YASnippet when it can't find a snippet +to expand. + +`call-other-command' means try to temporarily disable + YASnippet and call other command bound to `yas/trigger-key'. + +`return-nil' means return do nothing." + :type '(choice (const :tag "Call previous command" 'call-other-command) + (const :tag "Do nothing" 'return-nil)) + :group 'yasnippet) + +(defcustom yas/choose-keys-first t + "If non-nil, `yas/choose-snippet' prompts for key, then for template. + +Otherwise `yas/choose-snippet' prompts for all possible +templates and inserts the selected one." + :type 'boolean + :group 'yasnippet) + +(defcustom yas/use-menu t + "Display a YASnippet menu in the menu bar. + +If this is set to `t', all snippet template of the current +mode will be listed under the menu \"yasnippet\"." + :type 'boolean + :group 'yasnippet) + +(defcustom yas/trigger-symbol " =>" + "The text that will be used in menu to represent the trigger." + :type 'string + :group 'yasnippet) + +(defcustom yas/show-all-modes-in-menu nil + "Display \"artificial\" major modes in menu bar as well. + +Currently, YASnippet only all \"real modes\" to menubar. For +example, you define snippets for \"cc-mode\" and make it the +parent of `c-mode', `c++-mode' and `java-mode'. There's really +no such mode like \"cc-mode\". So we don't show it in the yasnippet +menu to avoid the menu becoming too big with strange modes. The +snippets defined for \"cc-mode\" can still be accessed from +menu-bar->c-mode->parent (or c++-mode, java-mode, all are ok). +However, if you really like to show all modes in the menu, set +this variable to t." + :type 'boolean + :group 'yasnippet) + +(defcustom yas/wrap-around-region t + "If non-nil, wrap selected region in the snippet being + expanded. + +The wrapping occurs just before the snippet's exit marker. This +can be overriden on a per-snippet basis." + :type 'boolean + :group 'yasnippet) + +(defcustom yas/good-grace nil + "If non-nil, don't raise errors in inline elisp evaluation. + +The erorr string is instead returned." + :type 'boolean + :group 'yasnippet) + +(defface yas/field-highlight-face + '((((class color) (background light)) (:background "DarkSeaGreen1")) + (t (:background "DimGrey"))) + "The face used to highlight the currently active field of a snippet" + :group 'yasnippet) + +(defface yas/field-debug-face + '((((class color) (background light)) (:background "tomato")) + (t (:background "tomato"))) + "The face used for debugging some overlays normally hidden" + :group 'yasnippet) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar yas/dont-activate nil - "If set to t, don't activate yas/minor-mode automatically.") -(make-variable-buffer-local 'yas/dont-activate) +;; User semi-customizable variables +;; + +(defvar yas/keymap (make-sparse-keymap) + "The keymap of snippet.") + +(eval-when-compile + (define-key yas/keymap (read-kbd-macro yas/next-field-key) 'yas/next-field-or-maybe-expand) + (define-key yas/keymap (read-kbd-macro yas/prev-field-key) 'yas/prev-field) + (define-key yas/keymap (read-kbd-macro yas/clear-field-key) 'yas/clear-field-or-delete-char)) (defvar yas/key-syntaxes (list "w" "w_" "w_." "^ ") "A list of syntax of a key. This list is tried in the order @@ -53,96 +222,22 @@ foo-bar will first try \"bar\", if not found, then \"foo-bar\" is tried.") -(defvar yas/root-directory nil - "The (list of) root directory that stores the snippets for each -major modes.") - -(defvar yas/indent-line t - "Each (except the 1st) line of the snippet template is indented to -current column if this variable is non-`nil'.") -(make-variable-buffer-local 'yas/indent-line) - -(defvar yas/trigger-key (kbd "") - "The key to bind as a trigger of snippet.") -(defvar yas/next-field-key (kbd "") - "The key to navigate to next field.") -(defvar yas/clear-field-key (kbd "C-d") - "The key to clear the currently active field.") - -(defvar yas/keymap (make-sparse-keymap) - "The keymap of snippet.") -(define-key yas/keymap yas/next-field-key 'yas/next-field) -(define-key yas/keymap yas/clear-field-key 'yas/clear-field-or-delete-char) -(define-key yas/keymap (kbd "S-TAB") 'yas/prev-field) -(define-key yas/keymap (kbd "") 'yas/prev-field) -(define-key yas/keymap (kbd "") 'yas/prev-field) -(define-key yas/keymap (kbd "") 'yas/prev-field) -(define-key yas/keymap (kbd "") 'yas/prev-field) - -(defvar yas/show-all-modes-in-menu nil - "Currently yasnippet only all \"real modes\" to menubar. For -example, you define snippets for \"cc-mode\" and make it the -parent of `c-mode', `c++-mode' and `java-mode'. There's really -no such mode like \"cc-mode\". So we don't show it in the yasnippet -menu to avoid the menu becoming too big with strange modes. The -snippets defined for \"cc-mode\" can still be accessed from -menu-bar->c-mode->parent (or c++-mode, java-mode, all are ok). -However, if you really like to show all modes in the menu, set -this variable to t.") -(defvar yas/use-menu t - "If this is set to `t', all snippet template of the current -mode will be listed under the menu \"yasnippet\".") -(defvar yas/trigger-symbol " =>" - "The text that will be used in menu to represent the trigger.") - -(defvar yas/good-grace nil - "If non-nil, don't raise errors in inline elisp evaluation, -return the error string instead.") - -(defface yas/field-highlight-face - '((((class color) (background light)) (:background "DarkSeaGreen1")) - (t (:background "DimGrey"))) - "The face used to highlight the currently active field of a snippet") - -(defface yas/mirror-highlight-face - '((((class color) (background light)) (:background "Dodgerblue")) - (t (:background "DimGrey"))) - "The face used to highlight a mirror of a snippet") - -(defface yas/field-debug-face - '((((class color) (background light)) (:background "tomato")) - (t (:background "tomato"))) - "The face used for debugging") - -(defvar yas/popup-functions - '( yas/x-popup - yas/ido-popup - yas/dropdown-popup - yas/completing-popup - yas/no-popup )) - - -(defvar yas/extra-mode-hooks - '() - "A list of mode-hook that should be hooked to enable yas/minor-mode. -Most modes need no special consideration. Some mode (like `ruby-mode') -doesn't call `after-change-major-mode-hook' need to be hooked explicitly.") -(mapc '(lambda (x) - (add-to-list 'yas/extra-mode-hooks - x)) - '(ruby-mode-hook actionscript-mode-hook ox-mode-hook python-mode-hook)) - (defvar yas/after-exit-snippet-hook '() "Hooks to run after a snippet exited. + The hooks will be run in an environment where some variables bound to proper values: - * yas/snippet-beg : The beginning of the region of the snippet. - * yas/snippet-end : Similar to beg.") + +`yas/snippet-beg' : The beginning of the region of the snippet. + +`yas/snippet-end' : Similar to beg. + +Attention: These hooks are not run when exiting nested/stackd snippet expansion!") (defvar yas/before-expand-snippet-hook '() - "Hooks to run after a before expanding a snippet.") + "Hooks to run just before expanding a snippet.") (defvar yas/buffer-local-condition '(if (and (not (bobp)) @@ -187,37 +282,38 @@ Here's an example: '(require-snippet-condition . force-in-comment) t))))") -(defvar yas/fallback-behavior 'call-other-command - "The fall back behavior of YASnippet when it can't find a snippet -to expand. - - * 'call-other-command means try to temporarily disable - YASnippet and call other command bound to `yas/trigger-key'. - * 'return-nil means return nil.") - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Internal variables -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; + (defvar yas/version "0.5.6-nested-placeholders") (defvar yas/snippet-tables (make-hash-table) "A hash table of snippet tables corresponding to each major-mode.") + (defvar yas/menu-table (make-hash-table) "A hash table of menus of corresponding major-mode.") + (defvar yas/menu-keymap (make-sparse-keymap "YASnippet")) ;; empty menu will cause problems, so we insert some items -(define-key yas/menu-keymap [yas/about] - '(menu-item "About" yas/about)) -(define-key yas/menu-keymap [yas/reload] - '(menu-item "Reload all snippets" yas/reload-all)) -(define-key yas/menu-keymap [yas/load] - '(menu-item "Load snippets..." yas/load-directory)) -(define-key yas/menu-keymap [yas/separator] - '(menu-item "--")) + +(eval-when-compile + (define-key yas/menu-keymap [yas/about] + '(menu-item "About" yas/about)) + + (define-key yas/menu-keymap [yas/reload] + '(menu-item "Reload all snippets" yas/reload-all)) + + (define-key yas/menu-keymap [yas/load] + '(menu-item "Load snippets..." yas/load-directory)) + + (define-key yas/menu-keymap [yas/separator] + '(menu-item "--"))) (defvar yas/known-modes '(ruby-mode rst-mode markdown-mode) "A list of mode which is well known but not part of emacs.") + (defvar yas/escaped-characters '(?\\ ?` ?$ ?} ) "A list of characters which *might* need to be escaped in @@ -245,6 +341,7 @@ snippet templates") (defvar yas/snippet-id-seed 0 "Contains the next id for a snippet.") + (defun yas/snippet-next-id () (let ((id yas/snippet-id-seed)) (incf yas/snippet-id-seed) @@ -252,13 +349,11 @@ snippet templates") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; YASnippet minor mode -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; + (defvar yas/minor-mode-map (make-sparse-keymap) "The keymap of yas/minor-mode") -(defvar yas/minor-mode-on-hook nil - "Hook to call when yas/minor-mode is on.") -(defvar yas/minor-mode-off-hook nil - "Hook to call when yas/minor-mode is off.") + (define-minor-mode yas/minor-mode "Toggle YASnippet mode. With no argument, this command toggles the mode. @@ -274,25 +369,22 @@ You can customize the key through `yas/trigger-key'." ;; The indicator for the mode line. " yas" :group 'editing - (define-key yas/minor-mode-map yas/trigger-key 'yas/expand)) + (define-key yas/minor-mode-map (read-kbd-macro yas/trigger-key) 'yas/expand)) -(defun yas/minor-mode-auto-on () - "Turn on YASnippet minor mode unless `yas/dont-activate' is -set to t." - (unless yas/dont-activate - (yas/minor-mode-on))) (defun yas/minor-mode-on () "Turn on YASnippet minor mode." (interactive) (yas/minor-mode 1)) + (defun yas/minor-mode-off () "Turn off YASnippet minor mode." (interactive) (yas/minor-mode -1)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Internal Structs -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Internal structs for template management +;; + (defstruct (yas/template (:constructor yas/make-template (content name condition))) "A template for a snippet." @@ -381,7 +473,8 @@ fetch from parent if any." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Internal functions -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; + (defun yas/ensure-minor-mode-priority () "Ensure that the key binding of yas/minor-mode takes priority." (unless (eq 'yas/minor-mode @@ -530,25 +623,26 @@ Here's a list of currently recognized variables: (setcdr pair value) alist))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Popping up for keys and templates ;; -(defun yas/popup-for-template-content (templates) +(defun yas/prompt-for-template-content (templates) "Interactively choose a template's content from the list TEMPLATES." (let ((template (some #'(lambda (fn) (funcall fn "Choose a snippet: " templates #'(lambda (template) (yas/template-name template)))) - yas/popup-functions))) + yas/prompt-functions))) (when template (yas/template-content template)))) -(defun yas/popup-for-keys (keys) +(defun yas/prompt-for-keys (keys) "Interactively choose a template key from the list KEYS." (some #'(lambda (fn) (funcall fn "Choose a snippet key: " keys)) - yas/popup-functions)) + yas/prompt-functions)) -(defun yas/x-popup (prompt choices &optional display-fn) +(defun yas/x-prompt (prompt choices &optional display-fn) (when window-system (let ((keymap (cons 'keymap (cons @@ -570,8 +664,7 @@ TEMPLATES." t) keymap)))))) - -(defun yas/ido-popup (prompt choices &optional display-fn) +(defun yas/ido-prompt (prompt choices &optional display-fn) (when (featurep 'ido) (let* ((formatted-choices (or (and display-fn (mapcar display-fn choices)) @@ -586,19 +679,21 @@ TEMPLATES." (when chosen (nth (position chosen formatted-choices) choices))))) -(defun yas/dropdown-popup (prompt choices &optional display-fn) +(defun yas/dropdown-prompt (prompt choices &optional display-fn) (when (featurep 'dropdown-list) )) -(defun yas/completing-popup (prompt choices &optional display-fn) +(defun yas/completing-prompt (prompt choices &optional display-fn) ) -(defun yas/no-popup (prompt choices &optional display-fn) +(defun yas/no-prompt (prompt choices &optional display-fn) ) -;; Loading snippets +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Loading snippets from files ;; (defun yas/load-directory-1 (directory &optional parent) + "Really do the job of loading snippets from a directory hierarchy." (let ((mode-sym (intern (file-name-nondirectory directory))) @@ -617,6 +712,32 @@ hierarchy." (dolist (subdir (yas/directory-files directory nil)) (yas/load-directory-1 subdir mode-sym)))) +(defun yas/load-directory (directory) + "Load snippet definition from a directory hierarchy. +Below the top-level directory, each directory is a mode +name. And under each subdirectory, each file is a definition +of a snippet. The file name is the trigger key and the +content of the file is the template." + (interactive "DSelect the root directory: ") + (unless (file-directory-p directory) + (error "Error %s not a directory" directory)) + (add-to-list 'yas/root-directory directory) + (dolist (dir (yas/directory-files directory nil)) + (yas/load-directory-1 dir)) + (when (interactive-p) + (message "done."))) + +(defun yas/reload-all () + "Reload all snippets." + (interactive) + (if yas/root-directory + (if (listp yas/root-directory) + (dolist (directory yas/root-directory) + (yas/load-directory directory)) + (yas/load-directory yas/root-directory)) + (call-interactively 'yas/load-directory)) + (message "done.")) + (defun yas/quote-string (string) "Escape and quote STRING. foo\"bar\\! -> \"foo\\\"bar\\\\!\"" @@ -698,6 +819,7 @@ all the parameters: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; User level functions +;;; (defun yas/about () (interactive) @@ -705,39 +827,8 @@ all the parameters: yas/version ") -- pluskid "))) -(defun yas/reload-all () - "Reload all snippets." - (interactive) - (if yas/root-directory - (if (listp yas/root-directory) - (dolist (directory yas/root-directory) - (yas/load-directory directory)) - (yas/load-directory yas/root-directory)) - (call-interactively 'yas/load-directory)) - (message "done.")) - -(defun yas/load-directory (directory) - "Load snippet definition from a directory hierarchy. -Below the top-level directory, each directory is a mode -name. And under each subdirectory, each file is a definition -of a snippet. The file name is the trigger key and the -content of the file is the template." - (interactive "DSelect the root directory: ") - (unless (file-directory-p directory) - (error "Error %s not a directory" directory)) - (add-to-list 'yas/root-directory directory) - (dolist (dir (yas/directory-files directory nil)) - (yas/load-directory-1 dir)) - (when (interactive-p) - (message "done."))) - (defun yas/initialize () - "Do necessary initialization." - (add-hook 'after-change-major-mode-hook - 'yas/minor-mode-auto-on) - (dolist (hook yas/extra-mode-hooks) - (add-hook hook - 'yas/minor-mode-auto-on)) + "Do necessary initialization. When turning on `yas/minor-mode'" (add-hook 'yas/minor-mode-on-hook 'yas/ensure-minor-mode-priority) (when yas/use-menu @@ -841,7 +932,9 @@ when the condition evaluated to non-nil." (interactive) (multiple-value-bind (templates start end) (yas/current-key) (if templates - (let ((template-content (yas/popup-for-template-content templates))) + (let ((template-content (or (and (rest templates) ;; more than one + (mapcar #'cdr (yas/prompt-for-template-content templates))) + (yas/template-content (cdar templates))))) (when template-content (yas/expand-snippet start end template-content))) (if (eq yas/fallback-behavior 'return-nil) @@ -851,25 +944,19 @@ when the condition evaluated to non-nil." (when (commandp command) (call-interactively command))))))) -(defvar yas/complete-for-keys t - "If non-nil, `yas/completing-expand' prompts for key, then for template. - -Otherwise 'yas/completing-expand' prompts for all possible -templates and inserts the selected one.") - -(defun yas/completing-expand () +(defun yas/choose-snippet () "Choose a snippet to expand, pop-up a list of choices according -to `yas/popup-function'." +to `yas/prompt-function'." (interactive) (let* ((templates (mapcar #'cdr - (if yas/complete-for-keys - (let ((key (yas/popup-for-keys (yas/snippet-table-all-keys (yas/current-snippet-table))))) + (if yas/choose-keys-first + (let ((key (yas/prompt-for-keys (yas/snippet-table-all-keys (yas/current-snippet-table))))) (when key (yas/snippet-table-fetch (yas/current-snippet-table) key 'no-condition))) (yas/snippet-table-all-templates (yas/current-snippet-table))))) (template-content (and templates - (or (and (cdr templates) - (yas/popup-for-template-content templates)) + (or (and (rest templates) ;; more than one template for same key + (yas/prompt-for-template-content templates)) (yas/template-content (car templates))))) (where (if mark-active (cons (region-beginning) (region-end)) @@ -878,7 +965,27 @@ to `yas/popup-function'." (yas/expand-snippet (car where) (cdr where) template-content)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Snippet expansion and field managment +;;; User conveniente functions, for using in snippet definitions +;;; + +(defun yas/choose-value (&rest possibilities) + (some #'(lambda (fn) + (funcall fn "Choose: " possibilities)) + yas/prompt-functions)) + +(defun yas/verify-value (&rest possibilities) + (when (and yas/moving-away (notany #'(lambda (pos) (string= pos yas/text)) possibilities)) + (error "field only allows " possibilities))) + +(defun yas/field-value (number) + (let ((snippet (car (yas/snippets-at-point))) + (field (and snippet + (yas/snippet-find-field snippet number)))) + (when field + (yas/field-text-for-display field)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Snippet expansion and field management (defvar yas/active-field-overlay nil "Overlays the currently active field") @@ -889,9 +996,14 @@ to `yas/popup-function'." (defvar yas/deleted-text nil "The text deleted in the last snippet expansion") -(make-variable-buffer-local 'yas/active-field-overlay) -(make-variable-buffer-local 'yas/field-protection-overlays) -(make-variable-buffer-local 'yas/deleted-text) +(defvar yas/selection-text nil + "The previously selected region deleted in the last snippet + expansion") + +(eval-when-compile + (make-variable-buffer-local 'yas/active-field-overlay) + (make-variable-buffer-local 'yas/field-protection-overlays) + (make-variable-buffer-local 'yas/deleted-text)) (defstruct (yas/snippet (:constructor yas/make-snippet ())) "A snippet. @@ -937,7 +1049,6 @@ for this field, apply it. Otherwise, returned nil." (yas/eval-string transform)))) transformed)) - (defsubst yas/replace-all (from to) "Replace all occurance from FROM to TO." (goto-char (point-min)) @@ -981,6 +1092,16 @@ inserted first." #'(lambda (s1 s2) (<= (yas/snippet-id s2) (yas/snippet-id s1))))) +(defun yas/next-field-or-maybe-expand () + "Try to expand a snippet at a key before point, otherwise +delegate to `yas/next-field'." + (interactive) + (if yas/triggers-in-field + (let ((yas/fallback-behavior 'return-nil)) + (unless (yas/expand) + (yas/next-field))) + (yas/next-field))) + (defun yas/next-field (&optional arg) "Navigate to next field. If there's none, exit the snippet." (interactive) @@ -1037,16 +1158,19 @@ up the snippet does not delete it!" (yas/snippet-exit snippet) (overlay-end (yas/snippet-control-overlay snippet))))) -;;; Apropos markers-to-points: This can be useful for performance reasons, so -;;; that an excessive number of live markers arent kept aroung in the -;;; `buffer-undo-list'. In `markers-to-points', the set-to-nil -;;; markers can't simply be discarded and replaced with fresh ones in -;;; `points-to-markers'. The original marker that was just set to nilhas to be -;;; reused. +;;; Apropos markers-to-points: +;;; +;;; This was ground useful for performance +;;; reasons, so that an excessive number of live markers arent kept +;;; aroung in the `buffer-undo-list'. However, in `markers-to-points', +;;; the set-to-nil markers can't simply be discarded and replaced with +;;; fresh ones in `points-to-markers'. The original marker that was +;;; just set to nilhas to be reused. ;;; ;;; This shouldn't bring horrible problems with undo/redo, but it ;;; would be one of the the first thing I'd remove if I was debugging that... ;;; + (defun yas/markers-to-points (snippet) "Convert all markers in SNIPPET to a cons (POINT . MARKER) where POINT is the original position of the marker and MARKER is @@ -1121,8 +1245,9 @@ exiting the snippet." ;; Push an action for snippet revival ;; - (push `(apply yas/snippet-revive ,yas/snippet-beg ,yas/snippet-end ,snippet) - buffer-undo-list) + (when yas/snippet-revival + (push `(apply yas/snippet-revive ,yas/snippet-beg ,yas/snippet-end ,snippet) + buffer-undo-list)) ;; XXX: `yas/after-exit-snippet-hook' should be run with ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not @@ -1130,7 +1255,9 @@ exiting the snippet." ;; disappeared, which sometimes happens when the snippet's messed ;; up... ;; - (unless no-hooks (run-hooks 'yas/after-exit-snippet-hook)))) + (unless no-hooks (run-hooks 'yas/after-exit-snippet-hook))) + + (message "[yas] snippet exited.")) (defun yas/check-commit-snippet () "Checks if point exited the currently active field of the @@ -1281,8 +1408,7 @@ progress." (yas/clear-field field)) (setf (yas/field-modified-p field) t)))))) -;;; -;;; Apropos protection overlays:... +;;; Apropos protection overlays: ;;; ;;; These exist for nasty users who will try to delete parts of the ;;; snippet outside the active field. Actual protection happens in @@ -1299,6 +1425,7 @@ progress." ;;; blocks all other million modification hooks. This presented some ;;; problems with stacked expansion. ;;; + (defun yas/make-move-field-protection-overlays (snippet field) "Place protection overlays surrounding SNIPPET's FIELD. @@ -1337,7 +1464,6 @@ Move the overlays, or create them if they do not exit." Functions in the `post-command-hook', for example `yas/post-command-handler' can check it and reset its value to nil. The variables value is the point where the violation originated") - (defun yas/on-protection-overlay-modification (overlay after? beg end &optional length) "Signals a snippet violation, then issues error. @@ -1346,10 +1472,10 @@ The error should be ignored in `debug-ignored-errors'" (yas/undo-in-progress))) (setq yas/protection-violation (point)) (error "Exit the snippet first!")))) + (add-to-list 'debug-ignored-errors "^Exit the snippet first!$") -;;; -;;; Apropos stacked expansion:... +;;; Apropos stacked expansion: ;;; ;;; the parent snippet does not run its fields modification hooks ;;; (`yas/on-field-overlay-modification' and @@ -1367,9 +1493,11 @@ The error should be ignored in `debug-ignored-errors'" ;;; maybe the correct implementation is to make the globals ;;; `yas/active-field-overlay' and `yas/field-protection-overlays' be ;;; snippet-local and be active even while the child snippet is -;;; running. This is a whole lot of hooks running, but they should -;;; account for all -;;; +;;; running. This would mean a lot of overlay modification hooks +;;; running, but if managed correctly (including overlay priorities) +;;; they should account for all situations... +;;; + (defun yas/expand-snippet (start end template) "Expand snippet at current point. Text between START and END will be deleted before inserting template." @@ -1398,6 +1526,7 @@ will be deleted before inserting template." ;; (insert template) (setq yas/deleted-text key) + (setq yas/selected-text (if mark-active key "")) (setq snippet (yas/snippet-create (point-min) (point-max)))) (error (push (cons (point-min) (point-max)) buffer-undo-list) @@ -1432,7 +1561,8 @@ will be deleted before inserting template." (end (overlay-end (yas/snippet-control-overlay snippet)))) (push (cons start end) buffer-undo-list) (push `(apply yas/take-care-of-redo ,start ,end ,snippet) - buffer-undo-list)))) + buffer-undo-list))) + (message "[yas] snippet expanded.")) (defun yas/take-care-of-redo (beg end snippet) "Commits SNIPPET, which in turn pushes an undo action for @@ -1515,13 +1645,18 @@ necessary fields, mirrors and exit points. Meant to be called in a narrowed buffer, does various passes" (let ((parse-start (point))) - ;; protect escapes + ;; protect backquote escapes ;; - (yas/protect-escapes) + (yas/protect-escapes '(?`)) ;; replace all backquoted expressions ;; (goto-char parse-start) (yas/replace-backquotes) + ;; protect escapes again since previous stepds might have + ;; generated more characters needing escapinge + ;; + (goto-char parse-start) + (yas/protect-escapes) ;; parse fields ;; (goto-char parse-start) @@ -1540,7 +1675,6 @@ Meant to be called in a narrowed buffer, does various passes" (yas/restore-escapes) ;; indent the best we can ;; - )) @@ -1548,12 +1682,12 @@ Meant to be called in a narrowed buffer, does various passes" (defun yas/escape-string (escaped) (concat "YASESCAPE" (format "%d" escaped) "PROTECTGUARD")) -(defun yas/protect-escapes () +(defun yas/protect-escapes (&optional escaped) "Protect all escaped characters with their numeric ASCII value." (mapc #'(lambda (escaped) (yas/replace-all (concat "\\" (char-to-string escaped)) (yas/escape-string escaped))) - yas/escaped-characters)) + (or escaped yas/escaped-characters))) (defun yas/restore-escapes () "Restore all escaped characters from their numeric ASCII value." @@ -1691,18 +1825,9 @@ When multiple expressions are found, only the last one counts." (set-marker (yas/field-end field) (point))) t)))) -;; User convenience functions - -(defun yas/choose (&rest possibilities) - (ido-completing-read "Choose: " possibilities nil nil nil nil (car possibilities))) - -(defun yas/verify (&rest possibilities) - (when (and yas/moving-away (notany #'(lambda (pos) (string= pos yas/text)) possibilities)) - (error "hey don't move away just now"))) - - +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Debug functions. Use (or change) at will whenever needed. -;; +;; (defun yas/debug-some-vars () (interactive) @@ -1733,7 +1858,6 @@ When multiple expressions are found, only the last one counts." (dolist (undo-elem first-ten) (princ (format "%2s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 70)))))))) - (defun yas/exterminate-package () (interactive) (yas/minor-mode -1) @@ -1761,7 +1885,6 @@ When multiple expressions are found, only the last one counts." (when quiet (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local)) ) - (provide 'yasnippet) From 0ee010ce65bcf58928e0a59ec0335ba177cd9864 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Tue, 14 Jul 2009 12:57:32 +0000 Subject: [PATCH 47/75] * working on indentation... --- yasnippet.el | 126 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 36 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index ebcd5f2..2b76db1 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -72,7 +72,7 @@ The following values are possible: `auto' Indent each line of the snippet with `indent-according-to-mode'" :type '(choice (const :tag "Nothing" nothing) - (const :tag "Fixed" always) + (const :tag "Fixed" fixed) (const :tag "Auto" auto)) :group 'yasnippet) @@ -82,7 +82,7 @@ an exit from an active snippet or redoing a snippet expansion" :type 'boolean :group 'yasnippet) -(defcustom yas/trigger-key "" +(defcustom yas/trigger-key "TAB" "The key to bind as a trigger of snippet when `yas/minor-mode' is active. @@ -99,7 +99,7 @@ representation using `read-kbd-macro'. " :type 'string :group 'yasnippet) -(defcustom yas/prev-field-key "S-" +(defcustom yas/prev-field-key "S-TAB" "The key to navigate to previous field when a snippet is active. Value is a string that is converted to the internal Emacs key @@ -174,8 +174,7 @@ this variable to t." :group 'yasnippet) (defcustom yas/wrap-around-region t - "If non-nil, wrap selected region in the snippet being - expanded. + "If non-nil, snippet expansion wraps around selected region. The wrapping occurs just before the snippet's exit marker. This can be overriden on a per-snippet basis." @@ -315,7 +314,7 @@ Here's an example: "A list of mode which is well known but not part of emacs.") (defvar yas/escaped-characters - '(?\\ ?` ?$ ?} ) + '(?\\ ?` ?' ?$ ?} ) "A list of characters which *might* need to be escaped in snippet templates") @@ -933,14 +932,14 @@ when the condition evaluated to non-nil." (multiple-value-bind (templates start end) (yas/current-key) (if templates (let ((template-content (or (and (rest templates) ;; more than one - (mapcar #'cdr (yas/prompt-for-template-content templates))) + (yas/prompt-for-template-content (mapcar #'cdr templates))) (yas/template-content (cdar templates))))) (when template-content (yas/expand-snippet start end template-content))) (if (eq yas/fallback-behavior 'return-nil) nil ; return nil (let* ((yas/minor-mode nil) - (command (key-binding yas/trigger-key))) + (command (key-binding (read-kbd-macro yas/trigger-key)))) (when (commandp command) (call-interactively command))))))) @@ -994,11 +993,14 @@ to `yas/prompt-function'." "Two overlays protect the current actipve field ") (defvar yas/deleted-text nil - "The text deleted in the last snippet expansion") + "The text deleted in the last snippet expansion.") -(defvar yas/selection-text nil - "The previously selected region deleted in the last snippet - expansion") +(defvar yas/selected-text nil + "The selected region deleted before the last snippet + expansion.") + +(defvar yas/start-column nil + "The column where the snippet expansion started.") (eval-when-compile (make-variable-buffer-local 'yas/active-field-overlay) @@ -1510,31 +1512,48 @@ will be deleted before inserting template." (inhibit-modification-hooks t) (column (current-column)) snippet) + + ;; Delete the trigger key, this *does* get undo-recorded. + ;; + (delete-region start end) + ;; Narrow the region down to the template, shoosh the ;; `buffer-undo-list', and create the snippet, the new snippet ;; updates its mirrors once, so we are left with some plain text. ;; The undo action for deleting this plain text will get recorded ;; at the end of this function. ;; - (save-restriction - (narrow-to-region end end) - (condition-case err - (let ((buffer-undo-list t)) - ;; snippet creation might evaluate users elisp, which - ;; might generate errors, so we have to be ready to catch - ;; them mostly to make the undo information - ;; - (insert template) - (setq yas/deleted-text key) - (setq yas/selected-text (if mark-active key "")) - (setq snippet (yas/snippet-create (point-min) (point-max)))) - (error - (push (cons (point-min) (point-max)) buffer-undo-list) - (error (cadr err))))) + ;; (save-restriction + ;; (narrow-to-region end end) + ;; (condition-case err + ;; (let ((buffer-undo-list t)) + ;; ;; snippet creation might evaluate users elisp, which + ;; ;; might generate errors, so we have to be ready to catch + ;; ;; them mostly to make the undo information + ;; ;; + ;; (insert template) + ;; (setq yas/deleted-text key) + ;; (setq yas/selected-text (if mark-active key "")) + ;; (setq snippet (yas/snippet-create (point-min) (point-max)))) + ;; (error + ;; (push (cons (point-min) (point-max)) buffer-undo-list) + ;; (error (cadr err))))) + + + + (save-restriction + (narrow-to-region start start) + (let ((buffer-undo-list t)) + ;; snippet creation might evaluate users elisp, which + ;; might generate errors, so we have to be ready to catch + ;; them mostly to make the undo information + ;; + (setq yas/start-column (save-restriction (widen) (current-column))) + (insert template) + (setq yas/deleted-text key) + (setq yas/selected-text (when mark-active key)) + (setq snippet (yas/snippet-create (point-min) (point-max))))) - ;; Delete the trigger key, this *does* get undo-recorded. - ;; - (delete-region start end) ;; stacked-expansion: This checks for stacked expansion, save the ;; `yas/previous-active-field' and advance its boudary. ;; @@ -1645,9 +1664,9 @@ necessary fields, mirrors and exit points. Meant to be called in a narrowed buffer, does various passes" (let ((parse-start (point))) - ;; protect backquote escapes + ;; protect quote and backquote escapes ;; - (yas/protect-escapes '(?`)) + (yas/protect-escapes '(?` ?')) ;; replace all backquoted expressions ;; (goto-char parse-start) @@ -1675,9 +1694,39 @@ Meant to be called in a narrowed buffer, does various passes" (yas/restore-escapes) ;; indent the best we can ;; + (goto-char parse-start) + (yas/indent snippet))) - - )) +(defun yas/indent (snippet) + (message "would be indenting") + (cond ((eq yas/indent-line 'fixed) + (let ((fill-prefix (make-string yas/start-column ? ))) + (indent-region (point-min) (point-max)))) + ((eq yas/indent-line 'auto) + (let ((begin (point-min)) + (end (point-max))) + (save-restriction + (widen) + (indent-region (line-beginning-position) (point-max)) + (when (yas/snippet-exit snippet) + (goto-char (yas/snippet-exit snippet)) + (indent-according-to-mode) + ;; XXX: Here is the indent problem: + ;; + ;; `indent-according-to-mode' uses whatever + ;; `indent-line-function' is available. Some + ;; implementations of these functions delete text + ;; before they insert. If there happens to be a marker + ;; just after the text being deleted, the insertion + ;; actually happens after the marker, which misplaces + ;; it. + ;; + ;; This would also happen if we had used overlays with + ;; the `front-advance' property set to nil. + ;; + (set-marker (yas/snippet-exit snippet) (point)))))) + (t + nil))) (defun yas/escape-string (escaped) (concat "YASESCAPE" (format "%d" escaped) "PROTECTGUARD")) @@ -1768,9 +1817,14 @@ When multiple expressions are found, only the last one counts." (while (re-search-forward yas/simple-mirror-regexp nil t) (let ((number (string-to-number (match-string-no-properties 1)))) (cond ((zerop number) + (setf (yas/snippet-exit snippet) - (set-marker (make-marker) (match-beginning 0))) - (delete-region (match-beginning 0) (match-end 0))) + (set-marker (make-marker) (match-end 0))) + (save-excursion + (goto-char (match-beginning 0)) + (when (and yas/wrap-around-region yas/selected-text) + (insert yas/selected-text)) + (delete-region (point) (yas/snippet-exit snippet)))) (t (let ((field (yas/snippet-find-field snippet number))) (if field From 4429104dfc958da51855d772ea835643bde94b12 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Tue, 14 Jul 2009 17:07:42 +0000 Subject: [PATCH 48/75] * more bug fixes and a global mode --- yasnippet.el | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 2b76db1..3e26f72 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -3,7 +3,7 @@ ;; Copyright 2008 pluskid ;; ;; Authors: pluskid , joaotavora -;; Version: 0.6.0 XXX: Change this +;; Version: 0.6.0 ;; X-URL: http://code.google.com/p/yasnippet/ ;; This file is free software; you can redistribute it and/or modify @@ -355,19 +355,20 @@ snippet templates") (define-minor-mode yas/minor-mode "Toggle YASnippet mode. + +When YASnippet mode is enabled, the `tas/trigger-key' key expands +snippets of code depending on the mode. + With no argument, this command toggles the mode. positive prefix argument turns on the mode. Negative prefix argument turns off the mode. -When YASnippet mode is enabled, the TAB key -expands snippets of code depending on the mode. - You can customize the key through `yas/trigger-key'." ;; The initial value. nil ;; The indicator for the mode line. " yas" - :group 'editing + :group 'yasnippet (define-key yas/minor-mode-map (read-kbd-macro yas/trigger-key) 'yas/expand)) (defun yas/minor-mode-on () @@ -380,6 +381,9 @@ You can customize the key through `yas/trigger-key'." (interactive) (yas/minor-mode -1)) +(define-globalized-minor-mode yas/global-mode yas/minor-mode yas/minor-mode-on + :group 'yasnippet) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Internal structs for template management ;; @@ -1016,8 +1020,8 @@ to `yas/prompt-function'." (id (yas/snippet-next-id) :read-only t) (control-overlay nil) active-field - ;; stacked expansion: this slot saves the active field where the - ;; child expansion took place + ;; stacked expansion: the `previous-active-field' slot saves the + ;; active field where the child expansion took place previous-active-field exit-hook) @@ -1213,7 +1217,10 @@ is done by setting MARKER to POINT with `set-marker'." snippet as ordinary text. Return a buffer position where the point should be placed if -exiting the snippet." +exiting the snippet. + +NO-HOOKS means don't run the `yas/after-exit-snippet-hook' hooks." + (let ((control-overlay (yas/snippet-control-overlay snippet)) yas/snippet-beg yas/snippet-end) @@ -1245,11 +1252,14 @@ exiting the snippet." ;; (yas/markers-to-points snippet) - ;; Push an action for snippet revival + ;; Take care of snippet revival ;; - (when yas/snippet-revival - (push `(apply yas/snippet-revive ,yas/snippet-beg ,yas/snippet-end ,snippet) - buffer-undo-list)) + (if yas/snippet-revival + (push `(apply yas/snippet-revive ,yas/snippet-beg ,yas/snippet-end ,snippet) + buffer-undo-list) + ;; Dismember the snippet... this is useful if we get called + ;; again from `yas/take-care-of-redo'.... + (setf (yas/snippet-fields snippet) nil)) ;; XXX: `yas/after-exit-snippet-hook' should be run with ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not @@ -1588,7 +1598,7 @@ will be deleted before inserting template." reviving it. Meant to exit in the `buffer-undo-list'." - (yas/commit-snippet snippet)) + (yas/commit-snippet snippet 'no-hooks)) (defun yas/snippet-revive (beg end snippet) "Revives the SNIPPET and creates a control overlay from BEG to @@ -1698,7 +1708,7 @@ Meant to be called in a narrowed buffer, does various passes" (yas/indent snippet))) (defun yas/indent (snippet) - (message "would be indenting") + ;;; XXX: fixed indentation not working (cond ((eq yas/indent-line 'fixed) (let ((fill-prefix (make-string yas/start-column ? ))) (indent-region (point-min) (point-max)))) @@ -1915,6 +1925,7 @@ When multiple expressions are found, only the last one counts." (defun yas/exterminate-package () (interactive) (yas/minor-mode -1) + (unintern 'yasnippet) (mapatoms #'(lambda (atom) (when (string-match "yas/" (symbol-name atom)) (unintern atom))))) From a1a4bfc7733a35bdf0037fa8ef12a2ec13fa1372 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Wed, 15 Jul 2009 07:02:13 +0000 Subject: [PATCH 49/75] * "$>" now working * `yas/hippie-try-expand' fixed to work with new undo system --- yasnippet.el | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 3e26f72..37bf2b0 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -66,11 +66,13 @@ other values interactively." The following values are possible: -`nothing' Don't apply any indendation after expansion; - `fixed' Indent the snippet to the current column; -`auto' Indent each line of the snippet with `indent-according-to-mode'" +`auto' Indent each line of the snippet with `indent-according-to-mode' + +Every other value means don't apply any snippet-side indendation +after expansion (the manual per-line \"$>\" indentation still +applies)." :type '(choice (const :tag "Nothing" nothing) (const :tag "Fixed" fixed) (const :tag "Auto" auto)) @@ -913,12 +915,7 @@ when the condition evaluated to non-nil." (if (not first-time?) (let ((yas/fallback-behavior 'return-nil)) (yas/expand)) - (when (and (null (car buffer-undo-list)) - (eq 'apply - (car (cadr buffer-undo-list))) - (eq 'yas/undo-expand-snippet - (cadr (cadr buffer-undo-list)))) - (undo 1)) + (undo 1) nil)) (defun yas/require-template-condition () @@ -1736,7 +1733,12 @@ Meant to be called in a narrowed buffer, does various passes" ;; (set-marker (yas/snippet-exit snippet) (point)))))) (t - nil))) + nil)) + (while (re-search-forward "$>" nil t) + (delete-region (match-beginning 0) (match-end 0)) + (when (not (eq yas/indent-line 'auto)) + (indent-according-to-mode)))) + (defun yas/escape-string (escaped) (concat "YASESCAPE" (format "%d" escaped) "PROTECTGUARD")) From 63378d00b2ab4094d7033537385096100cc18355 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Wed, 15 Jul 2009 07:23:13 +0000 Subject: [PATCH 50/75] * replaced by TAB and fixed a small bug in $> indendation --- yasnippet.el | 70 +++++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 37bf2b0..54011ee 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -93,7 +93,7 @@ representation using `read-kbd-macro'. " :type 'string :group 'yasnippet) -(defcustom yas/next-field-key "" +(defcustom yas/next-field-key "TAB" "The key to navigate to next field when a snippet is active. Value is a string that is converted to the internal Emacs key @@ -1705,39 +1705,41 @@ Meant to be called in a narrowed buffer, does various passes" (yas/indent snippet))) (defun yas/indent (snippet) - ;;; XXX: fixed indentation not working - (cond ((eq yas/indent-line 'fixed) - (let ((fill-prefix (make-string yas/start-column ? ))) - (indent-region (point-min) (point-max)))) - ((eq yas/indent-line 'auto) - (let ((begin (point-min)) - (end (point-max))) - (save-restriction - (widen) - (indent-region (line-beginning-position) (point-max)) - (when (yas/snippet-exit snippet) - (goto-char (yas/snippet-exit snippet)) - (indent-according-to-mode) - ;; XXX: Here is the indent problem: - ;; - ;; `indent-according-to-mode' uses whatever - ;; `indent-line-function' is available. Some - ;; implementations of these functions delete text - ;; before they insert. If there happens to be a marker - ;; just after the text being deleted, the insertion - ;; actually happens after the marker, which misplaces - ;; it. - ;; - ;; This would also happen if we had used overlays with - ;; the `front-advance' property set to nil. - ;; - (set-marker (yas/snippet-exit snippet) (point)))))) - (t - nil)) - (while (re-search-forward "$>" nil t) - (delete-region (match-beginning 0) (match-end 0)) - (when (not (eq yas/indent-line 'auto)) - (indent-according-to-mode)))) +;;; XXX: fixed indentation not working + (save-excursion + (cond ((eq yas/indent-line 'fixed) + (let ((fill-prefix (make-string yas/start-column ? ))) + (indent-region (point-min) (point-max)))) + ((eq yas/indent-line 'auto) + (let ((begin (point-min)) + (end (point-max))) + (save-restriction + (widen) + (indent-region (line-beginning-position) (point-max)) + (when (yas/snippet-exit snippet) + (goto-char (yas/snippet-exit snippet)) + (indent-according-to-mode) + ;; XXX: Here is the indent problem: + ;; + ;; `indent-according-to-mode' uses whatever + ;; `indent-line-function' is available. Some + ;; implementations of these functions delete text + ;; before they insert. If there happens to be a marker + ;; just after the text being deleted, the insertion + ;; actually happens after the marker, which misplaces + ;; it. + ;; + ;; This would also happen if we had used overlays with + ;; the `front-advance' property set to nil. + ;; + (set-marker (yas/snippet-exit snippet) (point)))))) + (t + nil))) + (save-excursion + (while (re-search-forward "$>" nil t) + (delete-region (match-beginning 0) (match-end 0)) + (when (not (eq yas/indent-line 'auto)) + (indent-according-to-mode))))) (defun yas/escape-string (escaped) From 5c7e870891ed0b4d8a79a104ecb08a24dedd00e8 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Wed, 15 Jul 2009 16:18:44 +0000 Subject: [PATCH 51/75] Got to: * get fixed indentation working * merge from trunk * try to implement snippet-local vars * implement a command `find-snippet-file' that defaults to the dir and sets `yas/snippet-editing-mode' --- yasnippet.el | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/yasnippet.el b/yasnippet.el index 54011ee..d1ae558 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -1893,6 +1893,28 @@ When multiple expressions are found, only the last one counts." (set-marker (yas/field-end field) (point))) t)))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Major mode stuff +;; +(defvar yas/font-lock-keywords + (append '(("^#.*$" . font-lock-comment-face)) + lisp-font-lock-keywords + lisp-font-lock-keywords-1 + lisp-font-lock-keywords-2 + '(("$\\([0-9]+\\)" + (0 font-lock-keyword-face) + (1 font-lock-string-face t)) + ("${\\([0-9]+\\):?" + (0 font-lock-keyword-face) + (1 font-lock-warning-face t)) + ("\\(\\$\\)(" 1 font-lock-preprocessor-face) + ("}" + (0 font-lock-keyword-face))))) + +(define-derived-mode yas/snippet-editing-mode emacs-lisp-mode "YASnippet" + "A mode for editing yasnippets" + (setq font-lock-defaults '(yas/font-lock-keywords))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Debug functions. Use (or change) at will whenever needed. ;; From c15f037874ede083e543b188f21679ed7ae8f830 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Thu, 16 Jul 2009 06:23:05 +0000 Subject: [PATCH 52/75] * enhanced good grace and fixed `yas/trigger-in-field' bug --- yasnippet.el | 55 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index d1ae558..8e54a9d 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -186,7 +186,9 @@ can be overriden on a per-snippet basis." (defcustom yas/good-grace nil "If non-nil, don't raise errors in inline elisp evaluation. -The erorr string is instead returned." +An error string \"[yas] error\" is returned instead." + + :type 'boolean :group 'yasnippet) @@ -503,19 +505,23 @@ a list of modes like this to help the judgement." ;; TODO: This is a possible optimization point, the expression could ;; be stored in cons format instead of string, "Evaluate STRING and convert the result to string." - (condition-case err - (save-excursion - (save-restriction - (save-match-data - (widen) - (let ((result (eval (read string)))) - (when result - (format "%s" result)))))) - (error (if yas/good-grace - (format "([yas] elisp error: %s" - (error-message-string err)) - (error (format "([yas] elisp error: %s" - (error-message-string err))))))) + (let ((retval (catch 'yas/exception + (condition-case err + (save-excursion + (save-restriction + (save-match-data + (widen) + (let ((result (eval (read string)))) + (when result + (format "%s" result)))))) + (error (if yas/good-grace + "[yas] elisp error!" + (error (format "[yas] elisp error: %s" + (error-message-string err))))))))) + (when (and (consp retval) + (eq 'yas/exception (car retval))) + (error (cdr retval))) + retval)) (defun yas/snippet-table (mode) "Get the snippet table corresponding to MODE." @@ -927,10 +933,14 @@ when the condition evaluated to non-nil." (symbolp (cdr local-condition)) (cdr local-condition)))) -(defun yas/expand () +(defun yas/expand (&optional field) "Expand a snippet." (interactive) - (multiple-value-bind (templates start end) (yas/current-key) + (multiple-value-bind (templates start end) (if field + (save-restriction + (narrow-to-region (yas/field-start field) (yas/field-end field)) + (yas/current-key)) + (yas/current-key)) (if templates (let ((template-content (or (and (rest templates) ;; more than one (yas/prompt-for-template-content (mapcar #'cdr templates))) @@ -973,9 +983,12 @@ to `yas/prompt-function'." (funcall fn "Choose: " possibilities)) yas/prompt-functions)) +(defun yas/throw (text) + (throw 'yas/exception (cons 'yas/exception text))) + (defun yas/verify-value (&rest possibilities) (when (and yas/moving-away (notany #'(lambda (pos) (string= pos yas/text)) possibilities)) - (error "field only allows " possibilities))) + (yas/throw (format "[yas] field only allows %s" possibilities)))) (defun yas/field-value (number) (let ((snippet (car (yas/snippets-at-point))) @@ -1100,8 +1113,10 @@ inserted first." delegate to `yas/next-field'." (interactive) (if yas/triggers-in-field - (let ((yas/fallback-behavior 'return-nil)) - (unless (yas/expand) + (let ((yas/fallback-behavior 'return-nil) + (active-field (overlay-get yas/active-field-overlay 'yas/field))) + (when active-field + (unless (yas/expand active-field)) (yas/next-field))) (yas/next-field))) @@ -1911,7 +1926,7 @@ When multiple expressions are found, only the last one counts." ("}" (0 font-lock-keyword-face))))) -(define-derived-mode yas/snippet-editing-mode emacs-lisp-mode "YASnippet" +(define-derived-mode yas/snippet-editing-mode fundamental-mode "YASnippet" "A mode for editing yasnippets" (setq font-lock-defaults '(yas/font-lock-keywords))) From c454bd2451cc1b6b976049216d2659f3a39e9d26 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Thu, 16 Jul 2009 16:00:02 +0000 Subject: [PATCH 53/75] * merged from trunk finally (expect more bugs) * fixed indentation working * auto-indentation apparently working with dirty hack * , but did get to see the famous python bug twice, * contrived existing python snippets are working, even with `yas/indent-line' set to 'auto * fixed more bugs... --- yasnippet.el | 700 +++++++++++++++++++++++++++++---------------------- 1 file changed, 403 insertions(+), 297 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 8e54a9d..2a71879 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -1,10 +1,13 @@ ;;; yasnippet.el --- Yet another snippet extension for Emacs. ;; Copyright 2008 pluskid -;; + ;; Authors: pluskid , joaotavora ;; Version: 0.6.0 ;; X-URL: http://code.google.com/p/yasnippet/ +;; Keywords: snippet, textmate +;; URL: http://code.google.com/p/yasnippet/ +;; EmacsWiki: YaSnippetMode ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -35,7 +38,9 @@ ;; For more information and detailed usage, refer to the project page: ;; http://code.google.com/p/yasnippet/ -(require 'cl) +;;; Code: + +(eval-when-compile (require 'cl)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; User customizable variables @@ -244,34 +249,43 @@ Attention: These hooks are not run when exiting nested/stackd snippet expansion! (defvar yas/buffer-local-condition '(if (and (not (bobp)) - (or (equal "font-lock-comment-face" + (or (equal 'font-lock-comment-face (get-char-property (1- (point)) 'face)) - (equal "font-lock-string-face" + (equal 'font-lock-string-face (get-char-property (1- (point)) 'face)))) '(require-snippet-condition . force-in-comment) t) "Condition to yasnippet local to each buffer. +The default value helps filtering out potential snippet +expansions inside comments and string literals, unless the +snippet itself contains a condition that returns the symbol +`force-in-comment'. + * If yas/buffer-local-condition evaluate to nil, snippet won't be expanded. * If it evaluate to the a cons cell where the car is the - symbol require-snippet-condition and the cdr is a - symbol (let's call it requirement): + symbol `require-snippet-condition' and the cdr is a + symbol (let's call it \"requirement\"): * If the snippet has no condition, then it won't be expanded. - * If the snippet has a condition but evaluate to nil or + * If the snippet has a condition but it evaluates to nil or error occured during evaluation, it won't be expanded. * If the snippet has a condition that evaluate to - non-nil (let's call it result): - * If requirement is t, the snippet is ready to be + non-nil (let's call it \"result\"): + * If \"requirement\" is t, the snippet is ready to be expanded. - * If requirement is eq to result, the snippet is ready + * If \"requirement\" is eq to \"result\", the snippet is ready to be expanded. * Otherwise the snippet won't be expanded. - * If it evaluate to other non-nil value: + + * If it evaluates to `always', snippet is unconditionally + expanded. + + * If it evaluates to other non-nil value: * If the snippet has no condition, or has a condition that evaluate to non-nil, it is ready to be expanded. * Otherwise, it won't be expanded. @@ -284,6 +298,13 @@ Here's an example: '(if (python-in-string/comment) '(require-snippet-condition . force-in-comment) t))))") +(make-variable-buffer-local 'yas/buffer-local-condition) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Utility functions for transformations +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Internal variables @@ -415,34 +436,33 @@ You can customize the key through `yas/trigger-key'." (error-message-string err))) nil)))) + (defun yas/filter-templates-by-condition (templates) - "Filter the templates using the condition. The rules are: + "Filter the templates using the applicable condition. - * If the template has no condition, it is kept. - * If the template's condition eval to non-nil, it is kept. - * Otherwise (eval error or eval to nil) it is filtered." - (remove-if-not '(lambda (pair) - (let ((condition (yas/template-condition (cdr pair)))) - (if (null condition) - (if (yas/require-template-condition) - nil - t) - (let ((result - (yas/template-condition-predicate condition))) - (if (yas/require-template-condition) - (if (eq (yas/require-template-condition) t) - result - (eq result (yas/require-template-condition))) - result))))) - templates)) +TEMPLATES is a list of cons (KEY . TEMPLATE) where KEY is a +string and TEMPLATE is a `yas/template' structure. -(defun yas/snippet-table-fetch (table key &optional no-condition) +This function implements the rules described in +`yas/buffer-local-condition'. See that variables documentation." + (let ((requirement (yas/require-template-specific-condition-p))) + (if (eq requirement 'always) + templates + (remove-if-not #'(lambda (pair) + (let* ((condition (yas/template-condition (cdr pair))) + (result (and condition + (yas/template-condition-predicate condition)))) + (cond ((eq requirement t) + result) + (t + (eq requirement result))))) + templates)))) + +(defun yas/snippet-table-fetch (table key) "Fetch a snippet binding to KEY from TABLE. If not found, fetch from parent if any." (let* ((unfiltered (gethash key (yas/snippet-table-hash table))) - (templates (or (and no-condition - unfiltered) - (yas/filter-templates-by-condition unfiltered)))) + (templates (yas/filter-templates-by-condition unfiltered))) (when (and (null templates) (not (null (yas/snippet-table-parent table)))) (setq templates (yas/snippet-table-fetch @@ -518,10 +538,10 @@ a list of modes like this to help the judgement." "[yas] elisp error!" (error (format "[yas] elisp error: %s" (error-message-string err))))))))) - (when (and (consp retval) - (eq 'yas/exception (car retval))) - (error (cdr retval))) - retval)) + (when (and (consp retval) + (eq 'yas/exception (car retval))) + (error (cdr retval))) + retval)) (defun yas/snippet-table (mode) "Get the snippet table corresponding to MODE." @@ -583,26 +603,32 @@ Here's a list of currently recognized variables: * name * contributor * condition + * key + * group #name: #include \"...\" # -- #include \"$1\"" (goto-char (point-min)) - (let ((name file-name) template bound condition) + (let ((name file-name) template bound condition key group) (if (re-search-forward "^# --\n" nil t) (progn (setq template (buffer-substring-no-properties (point) (point-max))) (setq bound (point)) (goto-char (point-min)) - (while (re-search-forward "^#\\([^ ]+\\) *: *\\(.*\\)$" bound t) + (while (re-search-forward "^#\\([^ ]+?\\) *: *\\(.*\\)$" bound t) (when (string= "name" (match-string-no-properties 1)) (setq name (match-string-no-properties 2))) (when (string= "condition" (match-string-no-properties 1)) - (setq condition (read (match-string-no-properties 2)))))) + (setq condition (read (match-string-no-properties 2)))) + (when (string= "group" (match-string-no-properties 1)) + (setq group (match-string-no-properties 2))) + (when (string= "key" (match-string-no-properties 1)) + (setq key (match-string-no-properties 2))))) (setq template (buffer-substring-no-properties (point-min) (point-max)))) - (list template name condition))) + (list key template name condition group))) (defun yas/directory-files (directory file?) "Return directory files or subdirectories in full path." @@ -642,19 +668,21 @@ Here's a list of currently recognized variables: TEMPLATES." (let ((template (some #'(lambda (fn) (funcall fn "Choose a snippet: " templates #'(lambda (template) - (yas/template-name template)))) + (yas/template-name template)))) yas/prompt-functions))) (when template (yas/template-content template)))) (defun yas/prompt-for-keys (keys) "Interactively choose a template key from the list KEYS." - (some #'(lambda (fn) - (funcall fn "Choose a snippet key: " keys)) - yas/prompt-functions)) + (if keys + (some #'(lambda (fn) + (funcall fn "Choose a snippet key: " keys)) + yas/prompt-functions) + (message "[yas] no expansions possible here!"))) (defun yas/x-prompt (prompt choices &optional display-fn) - (when window-system + (when (and window-system choices) (let ((keymap (cons 'keymap (cons prompt @@ -713,10 +741,11 @@ hierarchy." (dolist (file (yas/directory-files directory t)) (when (file-readable-p file) (insert-file-contents file nil nil nil t) - (let ((snippet-file-name (file-name-nondirectory file))) - (push (cons snippet-file-name - (yas/parse-template snippet-file-name)) - snippets))))) + (let* ((snip (yas/parse-template)) + (key (or (car snip) + (file-name-nondirectory file))) + (snip (cdr snip))) + (push (cons key snip) snippets))))) (yas/define-snippets mode-sym snippets parent) @@ -779,19 +808,25 @@ all the parameters: (when (null snippet-roots) (setq snippet-roots '("snippets"))) (when (null code) - (setq code "(yas/initialize)")) + (setq code (concat "(yas/initialize-bundle)" + "\n;;;###autoload" ; break through so that won't + "(require 'yasnippet-bundle)"))) ; be treated as magic comment (let ((dirs (or (and (listp snippet-roots) snippet-roots) (list snippet-roots))) (bundle-buffer nil)) (with-temp-buffer (setq bundle-buffer (current-buffer)) + (insert ";;; yasnippet-bundle.el --- " + "Yet another snippet extension (Auto compiled bundle)\n") (insert-file-contents yasnippet) (goto-char (point-max)) (insert ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n") (insert ";;;; Auto-generated code ;;;;\n") (insert ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n") - (insert code "\n") + (insert "(defun yas/initialize-bundle ()\n" + " \"Initialize YASnippet and load snippets in the bundle.\"" + " (yas/initialize)\n") (flet ((yas/define-snippets (mode snippets &optional parent) (with-current-buffer bundle-buffer @@ -802,15 +837,19 @@ all the parameters: (insert " (" (yas/quote-string (car snippet)) " " - (yas/quote-string (cadr snippet)) + (yas/quote-string (nth 1 snippet)) " " - (if (caddr snippet) - (yas/quote-string (caddr snippet)) + (if (nth 2 snippet) + (yas/quote-string (nth 2 snippet)) "nil") " " (if (nth 3 snippet) (format "'%s" (nth 3 snippet)) "nil") + " " + (if (nth 4 snippet) + (yas/quote-string (nth 4 snippet)) + "nil") ")\n")) (insert " )\n") (insert (if parent @@ -820,11 +859,16 @@ all the parameters: (dolist (dir dirs) (dolist (subdir (yas/directory-files dir nil)) (yas/load-directory-1 subdir nil)))) + + (insert ")\n\n" code "\n") (insert "(provide '" (file-name-nondirectory (file-name-sans-extension yasnippet-bundle)) ")\n") + (insert ";;; " + (file-name-nondirectory yasnippet-bundle) + " ends here\n") (setq buffer-file-name yasnippet-bundle) (save-buffer)))) @@ -853,9 +897,9 @@ all the parameters: "Define snippets for MODE. SNIPPETS is a list of snippet definition, of the following form: - (KEY TEMPLATE NAME CONDITION) + (KEY TEMPLATE NAME CONDITION GROUP) -or the NAME and CONDITION may be omitted. The optional 3rd +or the NAME, CONDITION or GROUP may be omitted. The optional 3rd parameter can be used to specify the parent mode of MODE. That is, when looking a snippet in MODE failed, it can refer to its parent mode. The PARENT-MODE may not need to be a real mode." @@ -880,9 +924,10 @@ parent mode. The PARENT-MODE may not need to be a real mode." (dolist (snippet snippets) (let* ((full-key (car snippet)) (key (file-name-sans-extension full-key)) - (name (or (caddr snippet) (file-name-extension full-key))) + (name (or (nth 2 snippet) (file-name-extension full-key))) (condition (nth 3 snippet)) - (template (yas/make-template (cadr snippet) + (group (nth 4 snippet)) + (template (yas/make-template (nth 1 snippet) (or name key) condition))) (yas/snippet-table-store snippet-table @@ -890,10 +935,24 @@ parent mode. The PARENT-MODE may not need to be a real mode." key template) (when yas/use-menu - (define-key keymap (vector (make-symbol full-key)) - `(menu-item ,(yas/template-name template) - ,(yas/make-menu-binding (yas/template-content template)) - :keys ,(concat key yas/trigger-symbol)))))))) + (let ((group-keymap keymap)) + (when (and (not (null group)) + (not (string= "" group))) + (dolist (subgroup (mapcar #'make-symbol + (split-string group "\\."))) + (let ((subgroup-keymap (lookup-key group-keymap + (vector subgroup)))) + (when (null subgroup-keymap) + (setq subgroup-keymap (make-sparse-keymap)) + (define-key group-keymap (vector subgroup) + `(menu-item ,(symbol-name subgroup) + ,subgroup-keymap))) + (setq group-keymap subgroup-keymap)))) + (define-key group-keymap (vector (make-symbol full-key)) + `(menu-item ,(yas/template-name template) + ,(yas/make-menu-binding (yas/template-content + template)) + :keys ,(concat key yas/trigger-symbol))))))))) (defun yas/set-mode-parent (mode parent) "Set parent mode of MODE to PARENT." @@ -905,7 +964,7 @@ parent mode. The PARENT-MODE may not need to be a real mode." `(menu-item "parent mode" ,(yas/menu-keymap-for-mode parent))))) -(defun yas/define (mode key template &optional name condition) +(defun yas/define (mode key template &optional name condition group) "Define a snippet. Expanding KEY into TEMPLATE. NAME is a description to this template. Also update the menu if `yas/use-menu' is `t'. CONDITION is the @@ -913,7 +972,7 @@ condition attached to this snippet. If you attach a condition to a snippet, then it will only be expanded when the condition evaluated to non-nil." (yas/define-snippets mode - (list (list key template name condition)))) + (list (list key template name condition group)))) (defun yas/hippie-try-expand (first-time?) "Integrate with hippie expand. Just put this function in @@ -924,14 +983,18 @@ when the condition evaluated to non-nil." (undo 1) nil)) -(defun yas/require-template-condition () - (let ((local-condition (yas/template-condition-predicate - yas/buffer-local-condition))) - (and local-condition - (consp local-condition) - (eq 'require-snippet-condition (car local-condition)) - (symbolp (cdr local-condition)) - (cdr local-condition)))) +(defun yas/require-template-specific-condition-p () + "Decides if this buffer requests/requires snippet-specific +conditions to filter out potential expansions." + (if (eq 'always yas/buffer-local-condition) + 'always + (let ((local-condition (yas/template-condition-predicate + yas/buffer-local-condition))) + (and local-condition + (consp local-condition) + (eq 'require-snippet-condition (car local-condition)) + (symbolp (cdr local-condition)) + (cdr local-condition))))) (defun yas/expand (&optional field) "Expand a snippet." @@ -946,7 +1009,7 @@ when the condition evaluated to non-nil." (yas/prompt-for-template-content (mapcar #'cdr templates))) (yas/template-content (cdar templates))))) (when template-content - (yas/expand-snippet start end template-content))) + (yas/expand-snippet start end template-content))) (if (eq yas/fallback-behavior 'return-nil) nil ; return nil (let* ((yas/minor-mode nil) @@ -954,15 +1017,21 @@ when the condition evaluated to non-nil." (when (commandp command) (call-interactively command))))))) -(defun yas/choose-snippet () +(defun yas/choose-snippet (&optional no-condition) "Choose a snippet to expand, pop-up a list of choices according -to `yas/prompt-function'." - (interactive) - (let* ((templates (mapcar #'cdr +to `yas/prompt-function'. + +With prefix argument NO-CONDITION, bypass filtering of snippets +by condition." + (interactive "P") + (let* ((yas/buffer-local-condition (or (and no-condition + 'always) + yas/buffer-local-condition)) + (templates (mapcar #'cdr (if yas/choose-keys-first (let ((key (yas/prompt-for-keys (yas/snippet-table-all-keys (yas/current-snippet-table))))) (when key - (yas/snippet-table-fetch (yas/current-snippet-table) key 'no-condition))) + (yas/snippet-table-fetch (yas/current-snippet-table) key))) (yas/snippet-table-all-templates (yas/current-snippet-table))))) (template-content (and templates (or (and (rest templates) ;; more than one template for same key @@ -978,7 +1047,19 @@ to `yas/prompt-function'." ;;; User conveniente functions, for using in snippet definitions ;;; +(defun yas/substr (str pattern &optional subexp) + "Search PATTERN in STR and return SUBEXPth match. + +If found, the content of subexp group SUBEXP (default 0) is + returned, or else the original STR will be returned." + (let ((grp (or subexp 0))) + (save-match-data + (if (string-match pattern str) + (match-string-no-properties grp str) + str)))) + (defun yas/choose-value (&rest possibilities) + "Prompt for a string in the list POSSIBILITIES." (some #'(lambda (fn) (funcall fn "Choose: " possibilities)) yas/prompt-functions)) @@ -987,13 +1068,16 @@ to `yas/prompt-function'." (throw 'yas/exception (cons 'yas/exception text))) (defun yas/verify-value (&rest possibilities) + "Verify that the current field value is in POSSIBILITIES + +Otherwise throw exception." (when (and yas/moving-away (notany #'(lambda (pos) (string= pos yas/text)) possibilities)) (yas/throw (format "[yas] field only allows %s" possibilities)))) (defun yas/field-value (number) - (let ((snippet (car (yas/snippets-at-point))) - (field (and snippet - (yas/snippet-find-field snippet number)))) + (let* ((snippet (car (yas/snippets-at-point))) + (field (and snippet + (yas/snippet-find-field snippet number)))) (when field (yas/field-text-for-display field)))) @@ -1016,10 +1100,9 @@ to `yas/prompt-function'." (defvar yas/start-column nil "The column where the snippet expansion started.") -(eval-when-compile - (make-variable-buffer-local 'yas/active-field-overlay) - (make-variable-buffer-local 'yas/field-protection-overlays) - (make-variable-buffer-local 'yas/deleted-text)) +(make-variable-buffer-local 'yas/active-field-overlay) +(make-variable-buffer-local 'yas/field-protection-overlays) +(make-variable-buffer-local 'yas/deleted-text) (defstruct (yas/snippet (:constructor yas/make-snippet ())) "A snippet. @@ -1061,8 +1144,13 @@ for this field, apply it. Otherwise, returned nil." (transform (if (yas/mirror-p field-or-mirror) (yas/mirror-transform field-or-mirror) (yas/field-transform field-or-mirror))) + (start-point (if (yas/mirror-p field-or-mirror) + (yas/mirror-start field-or-mirror) + (yas/field-start field-or-mirror))) (transformed (and transform - (yas/eval-string transform)))) + (save-excursion + (goto-char start-point) + (yas/eval-string transform))))) transformed)) (defsubst yas/replace-all (from to) @@ -1234,8 +1322,8 @@ exiting the snippet. NO-HOOKS means don't run the `yas/after-exit-snippet-hook' hooks." (let ((control-overlay (yas/snippet-control-overlay snippet)) - yas/snippet-beg - yas/snippet-end) + yas/snippet-beg + yas/snippet-end) ;; ;; Save the end of the moribund snippet in case we need to revive it ;; its original expansion. @@ -1330,10 +1418,10 @@ snippet, if so cleans up the whole snippet up." ;; (let* ((snippet (car (yas/snippets-at-point))) (target-field (and snippet - (find-if-not #'yas/field-probably-deleted-p - (remove nil - (cons (yas/snippet-active-field snippet) - (yas/snippet-fields snippet))))))) + (find-if-not #'yas/field-probably-deleted-p + (remove nil + (cons (yas/snippet-active-field snippet) + (yas/snippet-fields snippet))))))) (when target-field (yas/move-to-field snippet target-field)))) ((not (yas/undo-in-progress)) @@ -1350,7 +1438,7 @@ snippet, if so cleans up the whole snippet up." (eq this-command 'undo) (eq this-command 'redo))) -(defun yas/make-control-overlay (start end) +(defun yas/make-control-overlay (snippet start end) "Creates the control overlay that surrounds the snippet and holds the keymap." (let ((overlay (make-overlay start @@ -1462,10 +1550,10 @@ Move the overlays, or create them if they do not exit." ;; when using stacked expansion ;; (when (< (1+ (buffer-size)) (1+ end)) - (save-excursion - (let ((inhibit-modification-hooks t)) - (goto-char (point-max)) - (newline)))) + (save-excursion + (let ((inhibit-modification-hooks t)) + (goto-char (point-max)) + (newline)))) ;; go on to normal overlay creation/moving ;; (cond ((and yas/field-protection-overlays @@ -1544,37 +1632,22 @@ will be deleted before inserting template." ;; updates its mirrors once, so we are left with some plain text. ;; The undo action for deleting this plain text will get recorded ;; at the end of this function. - ;; - ;; (save-restriction - ;; (narrow-to-region end end) - ;; (condition-case err - ;; (let ((buffer-undo-list t)) - ;; ;; snippet creation might evaluate users elisp, which - ;; ;; might generate errors, so we have to be ready to catch - ;; ;; them mostly to make the undo information - ;; ;; - ;; (insert template) - ;; (setq yas/deleted-text key) - ;; (setq yas/selected-text (if mark-active key "")) - ;; (setq snippet (yas/snippet-create (point-min) (point-max)))) - ;; (error - ;; (push (cons (point-min) (point-max)) buffer-undo-list) - ;; (error (cadr err))))) - - - (save-restriction (narrow-to-region start start) - (let ((buffer-undo-list t)) - ;; snippet creation might evaluate users elisp, which - ;; might generate errors, so we have to be ready to catch - ;; them mostly to make the undo information - ;; - (setq yas/start-column (save-restriction (widen) (current-column))) - (insert template) - (setq yas/deleted-text key) - (setq yas/selected-text (when mark-active key)) - (setq snippet (yas/snippet-create (point-min) (point-max))))) + (condition-case err + (let ((buffer-undo-list t)) + ;; snippet creation might evaluate users elisp, which + ;; might generate errors, so we have to be ready to catch + ;; them mostly to make the undo information + ;; + (setq yas/start-column (save-restriction (widen) (current-column))) + (insert template) + (setq yas/deleted-text key) + (setq yas/selected-text (when mark-active key)) + (setq snippet (yas/snippet-create (point-min) (point-max)))) + (error + (push (cons (point-min) (point-max)) buffer-undo-list) + (error (format "[yas] parse error: %s" (cadr err)))))) ;; stacked-expansion: This checks for stacked expansion, save the ;; `yas/previous-active-field' and advance its boudary. @@ -1631,7 +1704,7 @@ After revival, push the `yas/take-care-of-redo' in the (let ((target-field (or (yas/snippet-active-field snippet) (car (yas/snippet-fields snippet))))) (when target-field - (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay beg end)) + (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay snippet beg end)) (overlay-put (yas/snippet-control-overlay snippet) 'yas/snippet snippet) (yas/move-to-field snippet target-field) @@ -1657,7 +1730,7 @@ Returns the newly created snippet." (yas/update-mirrors snippet) ;; Create keymap overlay for snippet - (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay (point-min) (point-max))) + (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay snippet (point-min) (point-max))) ;; Move to end (goto-char (point-max)) @@ -1702,14 +1775,14 @@ Meant to be called in a narrowed buffer, does various passes" ;; (goto-char parse-start) (yas/field-parse-create snippet) - ;; parse mirror transforms - ;; - (goto-char parse-start) - (yas/transform-mirror-parse-create snippet) ;; parse simple mirrors ;; (goto-char parse-start) (yas/simple-mirror-parse-create snippet) + ;; parse mirror transforms + ;; + (goto-char parse-start) + (yas/transform-mirror-parse-create snippet) ;; restore escapes ;; (goto-char parse-start) @@ -1720,42 +1793,57 @@ Meant to be called in a narrowed buffer, does various passes" (yas/indent snippet))) (defun yas/indent (snippet) -;;; XXX: fixed indentation not working (save-excursion - (cond ((eq yas/indent-line 'fixed) - (let ((fill-prefix (make-string yas/start-column ? ))) - (indent-region (point-min) (point-max)))) - ((eq yas/indent-line 'auto) - (let ((begin (point-min)) - (end (point-max))) - (save-restriction - (widen) - (indent-region (line-beginning-position) (point-max)) - (when (yas/snippet-exit snippet) - (goto-char (yas/snippet-exit snippet)) - (indent-according-to-mode) - ;; XXX: Here is the indent problem: - ;; - ;; `indent-according-to-mode' uses whatever - ;; `indent-line-function' is available. Some - ;; implementations of these functions delete text - ;; before they insert. If there happens to be a marker - ;; just after the text being deleted, the insertion - ;; actually happens after the marker, which misplaces - ;; it. - ;; - ;; This would also happen if we had used overlays with - ;; the `front-advance' property set to nil. - ;; - (set-marker (yas/snippet-exit snippet) (point)))))) - (t - nil))) + (cond ((eq yas/indent-line 'fixed) + (let* ((indent (if indent-tabs-mode + (concat (make-string (/ column tab-width) ?\t) + (make-string (% column tab-width) ?\ )) + (make-string (current-colum) ?\ )))) + (goto-char (point-min)) + (while (and (zerop (forward-line)) + (= (current-column) 0)) + (insert indent)))) + ((eq yas/indent-line 'auto) + (let ((end (set-marker (make-marker) (point-max)))) + (save-restriction + (widen) + ;; XXX: Here seems to be the indent problem: + ;; + ;; `indent-according-to-mode' uses whatever + ;; `indent-line-function' is available. Some + ;; implementations of these functions delete text + ;; before they insert. If there happens to be a marker + ;; just after the text being deleted, the insertion + ;; actually happens after the marker, which misplaces + ;; it. + ;; + ;; This would also happen if we had used overlays with + ;; the `front-advance' property set to nil. + ;; + (while (and (zerop (forward-line)) + (<= (point) end)) + (goto-char (yas/real-line-beginning)) + (insert-before-markers "Y") + (indent-according-to-mode) + (backward-delete-char 1)) + (set-marker end nil)))) + (t + nil))) (save-excursion (while (re-search-forward "$>" nil t) (delete-region (match-beginning 0) (match-end 0)) (when (not (eq yas/indent-line 'auto)) (indent-according-to-mode))))) +(defun yas/real-line-beginning () + (let ((c (char-after (line-beginning-position))) + (n (line-beginning-position))) + (while (or (eql c ?\ ) + (eql c ?\t)) + (incf n) + (setq c (char-after n))) + n)) + (defun yas/escape-string (escaped) (concat "YASESCAPE" (format "%d" escaped) "PROTECTGUARD")) @@ -1765,23 +1853,30 @@ Meant to be called in a narrowed buffer, does various passes" (mapc #'(lambda (escaped) (yas/replace-all (concat "\\" (char-to-string escaped)) (yas/escape-string escaped))) - (or escaped yas/escaped-characters))) + (or escaped yas/escaped-characters))) (defun yas/restore-escapes () "Restore all escaped characters from their numeric ASCII value." (mapc #'(lambda (escaped) (yas/replace-all (yas/escape-string escaped) (char-to-string escaped))) - yas/escaped-characters)) + yas/escaped-characters)) (defun yas/replace-backquotes () "Replace all the \"`(lisp-expression)`\"-style expression with their evaluated value" (while (re-search-forward yas/backquote-lisp-expression-regexp nil t) - (let ((transformed (yas/eval-string (match-string 1)))) - (goto-char (match-end 0)) - (insert transformed) - (delete-region (match-beginning 0) (match-end 0))))) + (let ((transformed (yas/eval-string (match-string 1)))) + (goto-char (match-end 0)) + (insert transformed) + (delete-region (match-beginning 0) (match-end 0))))) + +(defun yas/scan-sexps (from count) + (condition-case err + (with-syntax-table (standard-syntax-table) + (scan-sexps from count)) + (error + nil))) (defun yas/field-parse-create (snippet &optional parent-field) "Parse most field expression, except for the simple one \"$n\". @@ -1794,137 +1889,137 @@ The following count as a field: When multiple expressions are found, only the last one counts." (save-excursion - (while (re-search-forward yas/field-regexp nil t) - (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) - (number (and (match-string-no-properties 1) - (string-to-number (match-string-no-properties 1)))) - (brand-new-field (and real-match-end-0 - (not (save-match-data - (eq (string-match "$(" (match-string-no-properties 2)) 0))) - (not (and number (zerop number))) - (yas/make-field number - (set-marker (make-marker) (match-beginning 2)) - (set-marker (make-marker) (1- real-match-end-0)) - parent-field)))) - (when brand-new-field - (delete-region (1- real-match-end-0) real-match-end-0) - (delete-region (match-beginning 0) (match-beginning 2)) - (push brand-new-field (yas/snippet-fields snippet)) - (save-excursion - (save-restriction - (narrow-to-region (yas/field-start brand-new-field) (yas/field-end brand-new-field)) - (goto-char (point-min)) - (yas/field-parse-create snippet brand-new-field))))))) + (while (re-search-forward yas/field-regexp nil t) + (let* ((real-match-end-0 (yas/scan-sexps (1+ (match-beginning 0)) 1)) + (number (and (match-string-no-properties 1) + (string-to-number (match-string-no-properties 1)))) + (brand-new-field (and real-match-end-0 + (not (save-match-data + (eq (string-match "$[ \t\n]+(" (match-string-no-properties 2)) 0))) + (not (and number (zerop number))) + (yas/make-field number + (set-marker (make-marker) (match-beginning 2)) + (set-marker (make-marker) (1- real-match-end-0)) + parent-field)))) + (when brand-new-field + (delete-region (1- real-match-end-0) real-match-end-0) + (delete-region (match-beginning 0) (match-beginning 2)) + (push brand-new-field (yas/snippet-fields snippet)) + (save-excursion + (save-restriction + (narrow-to-region (yas/field-start brand-new-field) (yas/field-end brand-new-field)) + (goto-char (point-min)) + (yas/field-parse-create snippet brand-new-field))))))) (when parent-field - (save-excursion - (while (re-search-forward yas/multi-dollar-lisp-expression-regexp nil t) - (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1))) - (when real-match-end-0 - (let ((lisp-expression-string (buffer-substring-no-properties (match-beginning 1) real-match-end-0))) - (setf (yas/field-transform parent-field) lisp-expression-string)) - (delete-region (match-beginning 0) real-match-end-0))))))) + (save-excursion + (while (re-search-forward yas/multi-dollar-lisp-expression-regexp nil t) + (let* ((real-match-end-0 (yas/scan-sexps (1+ (match-beginning 0)) 1))) + (when real-match-end-0 + (let ((lisp-expression-string (buffer-substring-no-properties (match-beginning 1) real-match-end-0))) + (setf (yas/field-transform parent-field) lisp-expression-string)) + (delete-region (match-beginning 0) real-match-end-0))))))) (defun yas/transform-mirror-parse-create (snippet) "Parse the \"${n:$(lisp-expression)}\" mirror transformations." (while (re-search-forward yas/transform-mirror-regexp nil t) - (let* ((real-match-end-0 (scan-sexps (1+ (match-beginning 0)) 1)) - (number (string-to-number (match-string-no-properties 1))) - (field (and number - (not (zerop number)) - (yas/snippet-find-field snippet number)))) - (when (and real-match-end-0 - field) - (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) - (set-marker (make-marker) (match-beginning 0)) - (buffer-substring-no-properties (match-beginning 2) - (1- real-match-end-0))) - (yas/field-mirrors field)) - (delete-region (match-beginning 0) real-match-end-0))))) + (let* ((real-match-end-0 (yas/scan-sexps (1+ (match-beginning 0)) 1)) + (number (string-to-number (match-string-no-properties 1))) + (field (and number + (not (zerop number)) + (yas/snippet-find-field snippet number)))) + (when (and real-match-end-0 + field) + (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) + (set-marker (make-marker) (match-beginning 0)) + (buffer-substring-no-properties (match-beginning 2) + (1- real-match-end-0))) + (yas/field-mirrors field)) + (delete-region (match-beginning 0) real-match-end-0))))) (defun yas/simple-mirror-parse-create (snippet) "Parse the simple \"$n\" mirrors and the exit-marker." (while (re-search-forward yas/simple-mirror-regexp nil t) - (let ((number (string-to-number (match-string-no-properties 1)))) - (cond ((zerop number) + (let ((number (string-to-number (match-string-no-properties 1)))) + (cond ((zerop number) - (setf (yas/snippet-exit snippet) - (set-marker (make-marker) (match-end 0))) - (save-excursion - (goto-char (match-beginning 0)) - (when (and yas/wrap-around-region yas/selected-text) - (insert yas/selected-text)) - (delete-region (point) (yas/snippet-exit snippet)))) - (t - (let ((field (yas/snippet-find-field snippet number))) - (if field - (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) - (set-marker (make-marker) (match-beginning 0)) - nil) - (yas/field-mirrors field)) - (push (yas/make-field number - (set-marker (make-marker) (match-beginning 0)) - (set-marker (make-marker) (match-beginning 0)) - nil) - (yas/snippet-fields snippet)))) - (delete-region (match-beginning 0) (match-end 0))))))) + (setf (yas/snippet-exit snippet) + (set-marker (make-marker) (match-end 0))) + (save-excursion + (goto-char (match-beginning 0)) + (when (and yas/wrap-around-region yas/selected-text) + (insert yas/selected-text)) + (delete-region (point) (yas/snippet-exit snippet)))) + (t + (let ((field (yas/snippet-find-field snippet number))) + (if field + (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) + (set-marker (make-marker) (match-beginning 0)) + nil) + (yas/field-mirrors field)) + (push (yas/make-field number + (set-marker (make-marker) (match-beginning 0)) + (set-marker (make-marker) (match-beginning 0)) + nil) + (yas/snippet-fields snippet)))) + (delete-region (match-beginning 0) (match-end 0))))))) (defun yas/update-mirrors (snippet) "Updates all the mirrors of SNIPPET." (save-excursion - (dolist (field (yas/snippet-fields snippet)) - (dolist (mirror (yas/field-mirrors field)) - ;; stacked expansion: I added an `inhibit-modification-hooks' - ;; here, for safety, may need to remove if we the mechanism is - ;; altered. - ;; - (let ((inhibit-modification-hooks t)) - (yas/mirror-update-display mirror field)))))) + (dolist (field (yas/snippet-fields snippet)) + (dolist (mirror (yas/field-mirrors field)) + ;; stacked expansion: I added an `inhibit-modification-hooks' + ;; here, for safety, may need to remove if we the mechanism is + ;; altered. + ;; + (let ((inhibit-modification-hooks t)) + (yas/mirror-update-display mirror field)))))) (defun yas/mirror-update-display (mirror field) "Update MIRROR according to FIELD (and mirror transform)." (let ((reflection (or (yas/apply-transform mirror field) - (yas/field-text-for-display field)))) - (when (and reflection - (not (string= reflection (buffer-substring-no-properties (yas/mirror-start mirror) (yas/mirror-end mirror))))) - (goto-char (yas/mirror-start mirror)) - (insert reflection) - (if (> (yas/mirror-end mirror) (point)) - (delete-region (point) (yas/mirror-end mirror)) - (set-marker (yas/mirror-end mirror) (point)))))) + (yas/field-text-for-display field)))) + (when (and reflection + (not (string= reflection (buffer-substring-no-properties (yas/mirror-start mirror) (yas/mirror-end mirror))))) + (goto-char (yas/mirror-start mirror)) + (insert reflection) + (if (> (yas/mirror-end mirror) (point)) + (delete-region (point) (yas/mirror-end mirror)) + (set-marker (yas/mirror-end mirror) (point)))))) (defun yas/field-update-display (field snippet) "Much like `yas/mirror-update-display', but for fields" (when (yas/field-transform field) - (let ((inhibit-modification-hooks t) - (transformed (yas/apply-transform field field)) - (point (point))) - (when (and transformed - (not (string= transformed (buffer-substring-no-properties (yas/field-start field) (yas/field-end field))))) - (setf (yas/field-modified-p field) t) - (goto-char (yas/field-start field)) - (insert transformed) - (if (> (yas/field-end field) (point)) - (delete-region (point) (yas/field-end field)) - (set-marker (yas/field-end field) (point))) - t)))) + (let ((inhibit-modification-hooks t) + (transformed (yas/apply-transform field field)) + (point (point))) + (when (and transformed + (not (string= transformed (buffer-substring-no-properties (yas/field-start field) (yas/field-end field))))) + (setf (yas/field-modified-p field) t) + (goto-char (yas/field-start field)) + (insert transformed) + (if (> (yas/field-end field) (point)) + (delete-region (point) (yas/field-end field)) + (set-marker (yas/field-end field) (point))) + t)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Major mode stuff ;; (defvar yas/font-lock-keywords (append '(("^#.*$" . font-lock-comment-face)) - lisp-font-lock-keywords - lisp-font-lock-keywords-1 - lisp-font-lock-keywords-2 - '(("$\\([0-9]+\\)" - (0 font-lock-keyword-face) - (1 font-lock-string-face t)) - ("${\\([0-9]+\\):?" - (0 font-lock-keyword-face) - (1 font-lock-warning-face t)) - ("\\(\\$\\)(" 1 font-lock-preprocessor-face) - ("}" - (0 font-lock-keyword-face))))) + lisp-font-lock-keywords + lisp-font-lock-keywords-1 + lisp-font-lock-keywords-2 + '(("$\\([0-9]+\\)" + (0 font-lock-keyword-face) + (1 font-lock-string-face t)) + ("${\\([0-9]+\\):?" + (0 font-lock-keyword-face) + (1 font-lock-warning-face t)) + ("\\(\\$\\)(" 1 font-lock-preprocessor-face) + ("}" + (0 font-lock-keyword-face))))) (define-derived-mode yas/snippet-editing-mode fundamental-mode "YASnippet" "A mode for editing yasnippets" @@ -1937,31 +2032,31 @@ When multiple expressions are found, only the last one counts." (defun yas/debug-some-vars () (interactive) (with-output-to-temp-buffer "*YASnippet trace*" - (princ "Interesting YASnippet vars: \n\n") + (princ "Interesting YASnippet vars: \n\n") - (princ (format "\nPost command hook: %s\n" post-command-hook)) - (princ (format "\nPre command hook: %s\n" pre-command-hook)) + (princ (format "\nPost command hook: %s\n" post-command-hook)) + (princ (format "\nPre command hook: %s\n" pre-command-hook)) - (princ (format "%s live snippets in total" (length (yas/snippets-at-point (quote all-snippets))))) - (princ (format "%s live snippets at point:" (length (yas/snippets-at-point)))) + (princ (format "%s live snippets in total" (length (yas/snippets-at-point (quote all-snippets))))) + (princ (format "%s live snippets at point:" (length (yas/snippets-at-point)))) - (dolist (snippet (yas/snippets-at-point)) - (princ (format "\tid: %s and active field from %s to %s covering \"%s\"\n" - (yas/snippet-id snippet) - (marker-position (yas/field-start (yas/snippet-active-field snippet))) - (marker-position (yas/field-end (yas/snippet-active-field snippet))) - (buffer-substring-no-properties (yas/field-start (yas/snippet-active-field snippet)) (yas/field-end (yas/snippet-active-field snippet)))))) + (dolist (snippet (yas/snippets-at-point)) + (princ (format "\tid: %s and active field from %s to %s covering \"%s\"\n" + (yas/snippet-id snippet) + (marker-position (yas/field-start (yas/snippet-active-field snippet))) + (marker-position (yas/field-end (yas/snippet-active-field snippet))) + (buffer-substring-no-properties (yas/field-start (yas/snippet-active-field snippet)) (yas/field-end (yas/snippet-active-field snippet)))))) - (princ (format "\nUndo is %s and point-max is %s.\n" - (if (eq buffer-undo-list t) - "DISABLED" - "ENABLED") - (point-max))) - (unless (eq buffer-undo-list t) - (princ (format "Undpolist has %s elements. First 10 elements follow:\n" (length buffer-undo-list))) - (let ((first-ten (subseq buffer-undo-list 0 19))) - (dolist (undo-elem first-ten) - (princ (format "%2s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 70)))))))) + (princ (format "\nUndo is %s and point-max is %s.\n" + (if (eq buffer-undo-list t) + "DISABLED" + "ENABLED") + (point-max))) + (unless (eq buffer-undo-list t) + (princ (format "Undpolist has %s elements. First 10 elements follow:\n" (length buffer-undo-list))) + (let ((first-ten (subseq buffer-undo-list 0 19))) + (dolist (undo-elem first-ten) + (princ (format "%2s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 70)))))))) (defun yas/exterminate-package () (interactive) @@ -1986,10 +2081,10 @@ When multiple expressions are found, only the last one counts." ;; (if (require 'ido nil t) ;; (setq abbrev (ido-completing-read "Snippet abbrev: " '("crazy" "prip" "prop"))) ;; (setq abbrev "prop")) - (setq abbrev "bosta") - (insert abbrev)) + (setq abbrev "bosta") + (insert abbrev)) (when quiet - (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local)) + (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local)) ) (provide 'yasnippet) @@ -2006,5 +2101,16 @@ When multiple expressions are found, only the last one counts." handle the end-of-buffer error fired in it by calling `forward-char' at the end of buffer." (condition-case err - ad-do-it - (error (message (error-message-string err))))) + ad-do-it + (error (message (error-message-string err))))) + +;; disable c-electric-* serial command in YAS fields +(add-hook 'c-mode-common-hook + '(lambda () + (make-variable-buffer-local 'yas/keymap) + (dolist (k '(":" ">" ";" "<" "{" "}")) + (define-key yas/keymap + k 'self-insert-command)))) + + +;;; yasnippet.el ends here From 399852d10acd8177a493a10a316a31d99dcd92e3 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Thu, 16 Jul 2009 21:07:37 +0000 Subject: [PATCH 54/75] * as one of the final touches, have to make the menu a "easy-define-menu" thing --- yasnippet.el | 54 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 2a71879..7bf7b4e 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -28,12 +28,33 @@ ;; Basic steps to setup: ;; 1. Place `yasnippet.el' in your `load-path'. +;; (add-to-list 'load-path "/dir/to/yasnippet.el") ;; 2. In your .emacs file: ;; (require 'yasnippet) ;; 3. Place the `snippets' directory somewhere. E.g: ~/.emacs.d/snippets ;; 4. In your .emacs file -;; (yas/initialize) -;; (yas/load-directory "~/.emacs.d/snippets") +;; (setq yas/root-directory "~/.emacs/snippets") +;; (yas/load-directory yas/root-directory) +;; 5. To enable the YASnippet menu and tab-trigger expansion +;; M-x yas/minor-mode +;; 6. To globally enable the minor mode in *all* buffers +;; M-x yas/global-mode +;; +;; Steps 5. and 6. are optional, you can insert use snippets without +;; them via: +;; M-x yas/choose-snippet +;; +;; The `dropdown-list.el' extension is bundled with YASnippet, you +;; can optionally use it the preferred "prompting method", puting in +;; your .emacs file, for example: +;; +;; (require 'dropdown-list) +;; (setq 'yas/prompt-functions '(yas/dropdown-prompt +;; yas/ido-prompt +;; yas/completing-prompt)) +;; +;; Also check out the customization group +;; M-x customize-group RET yasnippet RET ;; ;; For more information and detailed usage, refer to the project page: ;; http://code.google.com/p/yasnippet/ @@ -204,8 +225,7 @@ An error string \"[yas] error\" is returned instead." :group 'yasnippet) (defface yas/field-debug-face - '((((class color) (background light)) (:background "tomato")) - (t (:background "tomato"))) + '() "The face used for debugging some overlays normally hidden" :group 'yasnippet) @@ -880,18 +900,7 @@ all the parameters: (interactive) (message (concat "yasnippet (version " yas/version - ") -- pluskid "))) - -(defun yas/initialize () - "Do necessary initialization. When turning on `yas/minor-mode'" - (add-hook 'yas/minor-mode-on-hook - 'yas/ensure-minor-mode-priority) - (when yas/use-menu - (define-key-after - (lookup-key global-map [menu-bar]) - [yasnippet] - (cons "YASnippet" yas/menu-keymap) - 'buffer))) + ") -- pluskid /joaotavora "))) (defun yas/define-snippets (mode snippets &optional parent-mode) "Define snippets for MODE. SNIPPETS is a list of @@ -1044,7 +1053,7 @@ by condition." (yas/expand-snippet (car where) (cdr where) template-content)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; User conveniente functions, for using in snippet definitions +;;; User convenience functions, for using in snippet definitions ;;; (defun yas/substr (str pattern &optional subexp) @@ -1115,8 +1124,7 @@ Otherwise throw exception." active-field ;; stacked expansion: the `previous-active-field' slot saves the ;; active field where the child expansion took place - previous-active-field - exit-hook) + previous-active-field) (defstruct (yas/field (:constructor yas/make-field (number start end parent-field))) "A field." @@ -1379,8 +1387,8 @@ snippet, if so cleans up the whole snippet up." (dolist (snippet snippets) (let ((active-field (yas/snippet-active-field snippet))) (cond ((not (and active-field (yas/field-contains-point-p active-field))) - (yas/commit-snippet snippet) - (setq snippets-left (delete snippet snippets-left))) + (setq snippets-left (delete snippet snippets-left)) + (yas/commit-snippet snippet snippets-left)) ((and active-field (or (not yas/active-field-overlay) (not (overlay-buffer yas/active-field-overlay)))) @@ -1413,8 +1421,8 @@ snippet, if so cleans up the whole snippet up." (setq yas/protection-violation nil)) ((eq 'undo this-command) ;; - ;; After undo's the correct field is sometimes not restored - ;; correctly, this condition handles that + ;; After undo revival the correct field is sometimes not + ;; restored correctly, this condition handles that ;; (let* ((snippet (car (yas/snippets-at-point))) (target-field (and snippet From 02117f65e61d3e303846b3af49d93dcf8854b9d7 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Fri, 17 Jul 2009 14:40:44 +0000 Subject: [PATCH 55/75] * better menu, more bugs found/fixed * auto-indent bug and a lot more stuff could be made much easier if I had known about marker-insertion-type s!! --- yasnippet.el | 92 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 34 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 7bf7b4e..f0cd011 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -61,7 +61,8 @@ ;;; Code: -(eval-when-compile (require 'cl)) +(require 'cl) +(require 'easymenu) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; User customizable variables @@ -234,7 +235,7 @@ An error string \"[yas] error\" is returned instead." ;; (defvar yas/keymap (make-sparse-keymap) - "The keymap of snippet.") + "The keymap active while a snippet expansion is in progress.") (eval-when-compile (define-key yas/keymap (read-kbd-macro yas/next-field-key) 'yas/next-field-or-maybe-expand) @@ -338,22 +339,6 @@ Here's an example: (defvar yas/menu-table (make-hash-table) "A hash table of menus of corresponding major-mode.") -(defvar yas/menu-keymap (make-sparse-keymap "YASnippet")) -;; empty menu will cause problems, so we insert some items - -(eval-when-compile - (define-key yas/menu-keymap [yas/about] - '(menu-item "About" yas/about)) - - (define-key yas/menu-keymap [yas/reload] - '(menu-item "Reload all snippets" yas/reload-all)) - - (define-key yas/menu-keymap [yas/load] - '(menu-item "Load snippets..." yas/load-directory)) - - (define-key yas/menu-keymap [yas/separator] - '(menu-item "--"))) - (defvar yas/known-modes '(ruby-mode rst-mode markdown-mode) "A list of mode which is well known but not part of emacs.") @@ -395,9 +380,37 @@ snippet templates") ;; YASnippet minor mode ;; -(defvar yas/minor-mode-map (make-sparse-keymap) +(defvar yas/minor-mode-map nil "The keymap of yas/minor-mode") +;; +;; This bit of code stolen from hideshow.el +;; +(defun yas/init-keymap-and-menu () + (setq yas/minor-mode-map (make-sparse-keymap)) + (setq yas/minor-mode-menu nil) + + (easy-menu-define yas/minor-mode-menu + yas/minor-mode-map + "Menu used when YAS/minor-mode is active." + (cons "YASnippet" + (mapcar #'(lambda (ent) + (when (third ent) + (define-key yas/minor-mode-map (third ent) (second ent))) + (vector (first ent) (second ent) t)) + (list (list "--") + (list "Expand trigger" 'yas/expand (read-kbd-macro yas/trigger-key)) + (list "Insert at point" 'yas/choose-snippet "\C-c&\C-s") + (list "About" 'yas/about) + (list "Reload-all-snippets" 'yas/reload-all) + (list "Load snippets..." 'yas/load-directory)))))) + +;; +;; Init this on compilation/evaluation +;; +(unless yas/minor-mode-menu + (yas/init-keymap-and-menu)) + (define-minor-mode yas/minor-mode "Toggle YASnippet mode. @@ -408,13 +421,19 @@ With no argument, this command toggles the mode. positive prefix argument turns on the mode. Negative prefix argument turns off the mode. -You can customize the key through `yas/trigger-key'." +You can customize the key through `yas/trigger-key'. + +Key bindings: +\\{yas/minor-mode-map}" ;; The initial value. nil ;; The indicator for the mode line. " yas" :group 'yasnippet - (define-key yas/minor-mode-map (read-kbd-macro yas/trigger-key) 'yas/expand)) + (if yas/minor-mode + (progn + (easy-menu-add yas/minor-mode-menu)) + (easy-menu-remove yas/minor-mode-menu))) (defun yas/minor-mode-on () "Turn on YASnippet minor mode." @@ -662,14 +681,15 @@ Here's a list of currently recognized variables: (defun yas/make-menu-binding (template) (lexical-let ((template template)) - (lambda () - (interactive) - (let ((where (if mark-active - (cons (region-beginning) (region-end)) - (cons (point) (point))))) - (yas/expand-snippet (car where) - (cdr where) - template))))) + #'(lambda () (interactive) (yas/expand-from-menu template)))) + +(defun yas/expand-from-menu (template) + (let ((where (if mark-active + (cons (region-beginning) (region-end)) + (cons (point) (point))))) + (yas/expand-snippet (car where) + (cdr where) + template))) (defun yas/modify-alist (alist key value) "Modify ALIST to map KEY to VALUE. return the new alist." @@ -928,7 +948,7 @@ parent mode. The PARENT-MODE may not need to be a real mode." ,(yas/menu-keymap-for-mode parent-mode))))) (when (and yas/use-menu (yas/real-mode? mode)) - (define-key yas/menu-keymap (vector mode) + (define-key yas/minor-mode-menu (vector mode) `(menu-item ,(symbol-name mode) ,keymap))) (dolist (snippet snippets) (let* ((full-key (car snippet)) @@ -1828,12 +1848,16 @@ Meant to be called in a narrowed buffer, does various passes" ;; This would also happen if we had used overlays with ;; the `front-advance' property set to nil. ;; - (while (and (zerop (forward-line)) + (while (and (zerop (forward-line 1)) + (not (eobp)) (<= (point) end)) (goto-char (yas/real-line-beginning)) - (insert-before-markers "Y") - (indent-according-to-mode) - (backward-delete-char 1)) + (if (buffer-has-markers-at (point)) + (progn + (insert-before-markers "Y") + (indent-according-to-mode) + (backward-delete-char 1)) + (indent-according-to-mode))) (set-marker end nil)))) (t nil))) From fe8dd70072ea9bb757e879ae598d1fc433b2086c Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Sun, 19 Jul 2009 17:00:11 +0000 Subject: [PATCH 56/75] * a million new bugs introduced by a million new features, enough for today... --- yasnippet.el | 355 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 234 insertions(+), 121 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index f0cd011..34da49e 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -42,7 +42,7 @@ ;; ;; Steps 5. and 6. are optional, you can insert use snippets without ;; them via: -;; M-x yas/choose-snippet +;; M-x yas/insert-snippet ;; ;; The `dropdown-list.el' extension is bundled with YASnippet, you ;; can optionally use it the preferred "prompting method", puting in @@ -167,9 +167,9 @@ to expand. :group 'yasnippet) (defcustom yas/choose-keys-first t - "If non-nil, `yas/choose-snippet' prompts for key, then for template. + "If non-nil, `yas/insert-snippet' prompts for key, then for template. -Otherwise `yas/choose-snippet' prompts for all possible +Otherwise `yas/insert-snippet' prompts for all possible templates and inserts the selected one." :type 'boolean :group 'yasnippet) @@ -381,35 +381,35 @@ snippet templates") ;; (defvar yas/minor-mode-map nil - "The keymap of yas/minor-mode") + "The keymap used when `yas/minor-mode' is active.") -;; -;; This bit of code stolen from hideshow.el -;; -(defun yas/init-keymap-and-menu () - (setq yas/minor-mode-map (make-sparse-keymap)) - (setq yas/minor-mode-menu nil) - - (easy-menu-define yas/minor-mode-menu - yas/minor-mode-map - "Menu used when YAS/minor-mode is active." - (cons "YASnippet" - (mapcar #'(lambda (ent) - (when (third ent) - (define-key yas/minor-mode-map (third ent) (second ent))) - (vector (first ent) (second ent) t)) - (list (list "--") - (list "Expand trigger" 'yas/expand (read-kbd-macro yas/trigger-key)) - (list "Insert at point" 'yas/choose-snippet "\C-c&\C-s") - (list "About" 'yas/about) - (list "Reload-all-snippets" 'yas/reload-all) - (list "Load snippets..." 'yas/load-directory)))))) +(defvar yas/minor-mode-menu nil + "The menu bar menu used when `yas/minor-mode' is active.") +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Major mode stuff ;; -;; Init this on compilation/evaluation -;; -(unless yas/minor-mode-menu - (yas/init-keymap-and-menu)) +(defvar yas/font-lock-keywords + (append '(("^#.*$" . font-lock-comment-face)) + lisp-font-lock-keywords + lisp-font-lock-keywords-1 + lisp-font-lock-keywords-2 + '(("$\\([0-9]+\\)" + (0 font-lock-keyword-face) + (1 font-lock-string-face t)) + ("${\\([0-9]+\\):?" + (0 font-lock-keyword-face) + (1 font-lock-warning-face t)) + ("\\(\\$\\)(" 1 font-lock-preprocessor-face) + ("}" + (0 font-lock-keyword-face))))) + +(defvar yas/snippet-editing-mode-map (make-sparse-keymap)) + +(define-derived-mode yas/snippet-editing-mode fundamental-mode "YASnippet" + "A mode for editing yasnippets" + (setq font-lock-defaults '(yas/font-lock-keywords)) + (use-local-map yas/snippet-editing-mode-map)) (define-minor-mode yas/minor-mode "Toggle YASnippet mode. @@ -446,18 +446,49 @@ Key bindings: (yas/minor-mode -1)) (define-globalized-minor-mode yas/global-mode yas/minor-mode yas/minor-mode-on - :group 'yasnippet) + :group 'yasnippet) + +;; +;; This bit of code inspired from hideshow.el +;; +(defun yas/init-keymap-and-menu () + (setq yas/minor-mode-map (make-sparse-keymap)) + (setq yas/minor-mode-menu nil) + + (easy-menu-define yas/minor-mode-menu + yas/minor-mode-map + "Menu used when YAS/minor-mode is active." + (cons "YASnippet" + (mapcar #'(lambda (ent) + (when (third ent) + (define-key yas/minor-mode-map (third ent) (second ent))) + (vector (first ent) (second ent) t)) + (list (list "--") + (list "Expand trigger" 'yas/expand (read-kbd-macro yas/trigger-key)) + (list "Insert at point" 'yas/insert-snippet "\C-c&\C-s") + (list "About" 'yas/about) + (list "Reload-all-snippets" 'yas/reload-all) + (list "Load snippets..." 'yas/load-directory))))) + (define-key yas/snippet-editing-mode-map "\C-c\C-c" 'yas/load-snippet-buffer)) + +;; +;; Init this on compilation/evaluation +;; +(unless yas/minor-mode-menu + (yas/init-keymap-and-menu)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Internal structs for template management ;; (defstruct (yas/template (:constructor yas/make-template - (content name condition))) + (content name condition env file))) "A template for a snippet." content name - condition) + condition + env + file) (defstruct (yas/snippet-table (:constructor yas/make-snippet-table ())) "A table to store snippets for a perticular mode." @@ -629,8 +660,16 @@ the template of a snippet in the current snippet-table." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Template-related and snippet loading functions -(defun yas/parse-template (&optional file-name) +(defun yas/parse-template (file) "Parse the template in the current buffer. + +Optional FILE is the absolute file name of the file being +parsed. + +Return a snippet-definition, i.e. a list + + (KEY TEMPLATE NAME CONDITION GROUP ENV) + If the buffer contains a line of \"# --\" then the contents above this line are ignored. Variables can be set above this line through the syntax: @@ -644,33 +683,42 @@ Here's a list of currently recognized variables: * condition * key * group + * env #name: #include \"...\" # -- #include \"$1\"" (goto-char (point-min)) - (let ((name file-name) template bound condition key group) + (let* ((name (and file (file-name-nondirectory file))) + (key name) + template + bound + condition + group + env) (if (re-search-forward "^# --\n" nil t) - (progn (setq template - (buffer-substring-no-properties (point) - (point-max))) - (setq bound (point)) - (goto-char (point-min)) - (while (re-search-forward "^#\\([^ ]+?\\) *: *\\(.*\\)$" bound t) - (when (string= "name" (match-string-no-properties 1)) - (setq name (match-string-no-properties 2))) - (when (string= "condition" (match-string-no-properties 1)) - (setq condition (read (match-string-no-properties 2)))) - (when (string= "group" (match-string-no-properties 1)) - (setq group (match-string-no-properties 2))) - (when (string= "key" (match-string-no-properties 1)) - (setq key (match-string-no-properties 2))))) + (progn (setq template + (buffer-substring-no-properties (point) + (point-max))) + (setq bound (point)) + (goto-char (point-min)) + (while (re-search-forward "^# *\\([^ ]+?\\) *: *\\(.*\\)$" bound t) + (when (string= "name" (match-string-no-properties 1)) + (setq name (match-string-no-properties 2))) + (when (string= "condition" (match-string-no-properties 1)) + (setq condition (read (match-string-no-properties 2)))) + (when (string= "group" (match-string-no-properties 1)) + (setq group (match-string-no-properties 2))) + (when (string= "env" (match-string-no-properties 1)) + (setq env (match-string-no-properties 2))) + (when (string= "key" (match-string-no-properties 1)) + (setq key (match-string-no-properties 2))))) (setq template - (buffer-substring-no-properties (point-min) (point-max)))) - (list key template name condition group))) + (buffer-substring-no-properties (point-min) (point-max)))) + (list key template name condition group env file))) -(defun yas/directory-files (directory file?) - "Return directory files or subdirectories in full path." +(defun yas/subdirs (directory &optional file?) + "Return subdirs or files of DIRECTORY according to FILE?." (remove-if (lambda (file) (or (string-match "^\\." (file-name-nondirectory file)) @@ -680,8 +728,7 @@ Here's a list of currently recognized variables: (directory-files directory t))) (defun yas/make-menu-binding (template) - (lexical-let ((template template)) - #'(lambda () (interactive) (yas/expand-from-menu template)))) + `(lambda () (interactive) (yas/expand-from-menu ,template))) (defun yas/expand-from-menu (template) (let ((where (if mark-active @@ -689,7 +736,7 @@ Here's a list of currently recognized variables: (cons (point) (point))))) (yas/expand-snippet (car where) (cdr where) - template))) + (yas/template-content template)))) (defun yas/modify-alist (alist key value) "Modify ALIST to map KEY to VALUE. return the new alist." @@ -703,23 +750,22 @@ Here's a list of currently recognized variables: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Popping up for keys and templates ;; -(defun yas/prompt-for-template-content (templates) - "Interactively choose a template's content from the list -TEMPLATES." +(defun yas/prompt-for-template (templates) + "Interactively choose a template from the list TEMPLATES. + +TEMPLATES is a list of `yas/template'." (let ((template (some #'(lambda (fn) (funcall fn "Choose a snippet: " templates #'(lambda (template) (yas/template-name template)))) yas/prompt-functions))) - (when template - (yas/template-content template)))) + template)) (defun yas/prompt-for-keys (keys) "Interactively choose a template key from the list KEYS." (if keys (some #'(lambda (fn) (funcall fn "Choose a snippet key: " keys)) - yas/prompt-functions) - (message "[yas] no expansions possible here!"))) + yas/prompt-functions))) (defun yas/x-prompt (prompt choices &optional display-fn) (when (and window-system choices) @@ -773,23 +819,20 @@ TEMPLATES." ;; (defun yas/load-directory-1 (directory &optional parent) - "Really do the job of loading snippets from a directory -hierarchy." + "Recursively load snippet templates from DIRECTORY." + (let ((mode-sym (intern (file-name-nondirectory directory))) - (snippets nil)) + (snippet-defs nil)) (with-temp-buffer - (dolist (file (yas/directory-files directory t)) + (dolist (file (yas/subdirs directory 'no-subdirs-just-files)) (when (file-readable-p file) (insert-file-contents file nil nil nil t) - (let* ((snip (yas/parse-template)) - (key (or (car snip) - (file-name-nondirectory file))) - (snip (cdr snip))) - (push (cons key snip) snippets))))) + (push (yas/parse-template file) + snippet-defs)))) (yas/define-snippets mode-sym - snippets + snippet-defs parent) - (dolist (subdir (yas/directory-files directory nil)) + (dolist (subdir (yas/subdirs directory)) (yas/load-directory-1 subdir mode-sym)))) (defun yas/load-directory (directory) @@ -802,7 +845,7 @@ content of the file is the template." (unless (file-directory-p directory) (error "Error %s not a directory" directory)) (add-to-list 'yas/root-directory directory) - (dolist (dir (yas/directory-files directory nil)) + (dolist (dir (yas/subdirs directory)) (yas/load-directory-1 dir)) (when (interactive-p) (message "done."))) @@ -897,7 +940,7 @@ all the parameters: "nil") ")\n\n")))) (dolist (dir dirs) - (dolist (subdir (yas/directory-files dir nil)) + (dolist (subdir (yas/subdirs dir)) (yas/load-directory-1 subdir nil)))) (insert ")\n\n" code "\n") @@ -924,14 +967,14 @@ all the parameters: (defun yas/define-snippets (mode snippets &optional parent-mode) "Define snippets for MODE. SNIPPETS is a list of -snippet definition, of the following form: +snippet definitions, each taking the following form: (KEY TEMPLATE NAME CONDITION GROUP) -or the NAME, CONDITION or GROUP may be omitted. The optional 3rd -parameter can be used to specify the parent mode of MODE. That -is, when looking a snippet in MODE failed, it can refer to its -parent mode. The PARENT-MODE may not need to be a real mode." +NAME, CONDITION or GROUP may be omitted. Optional PARENT-MODE +can be used to specify the parent mode of MODE. That is, when +looking a snippet in MODE failed, it can refer to its parent +mode. The PARENT-MODE does not need to be a real mode." (let ((snippet-table (yas/snippet-table mode)) (parent-table (if parent-mode (yas/snippet-table parent-mode) @@ -956,15 +999,23 @@ parent mode. The PARENT-MODE may not need to be a real mode." (name (or (nth 2 snippet) (file-name-extension full-key))) (condition (nth 3 snippet)) (group (nth 4 snippet)) - (template (yas/make-template (nth 1 snippet) + (template (yas/make-template (nth 1 snippet) (or name key) - condition))) + condition + (nth 5 snippet) + (nth 6 snippet)))) (yas/snippet-table-store snippet-table full-key key template) (when yas/use-menu (let ((group-keymap keymap)) + ;; delete this entry from another group if already exists + ;; in some other group. An entry is considered as existing + ;; in another group if its name string-matches. + (yas/delete-from-keymap group-keymap name) + + ;; ... then add this entry to the correct group (when (and (not (null group)) (not (string= "" group))) (dolist (subgroup (mapcar #'make-symbol @@ -979,10 +1030,41 @@ parent mode. The PARENT-MODE may not need to be a real mode." (setq group-keymap subgroup-keymap)))) (define-key group-keymap (vector (make-symbol full-key)) `(menu-item ,(yas/template-name template) - ,(yas/make-menu-binding (yas/template-content - template)) + ,(yas/make-menu-binding template) :keys ,(concat key yas/trigger-symbol))))))))) +(defun yas/delete-from-keymap (keymap name) + "Recursively delete items name NAME from KEYMAP and its submenus. + +Skip any submenus named \"parent mode\"" + ;; First of all, r ecursively enter submenus, i.e. the tree is + ;; searched depth first so that stale submenus can be found in the + ;; higher passes. + ;; + (mapc #'(lambda (item) + (when (and (keymapp (fourth item)) + (stringp (third item)) + (not (string= (third item) + "parent mode"))) + (yas/delete-from-keymap (fourth item) name))) + (rest keymap)) + ;; + (when (keymapp keymap) + (let ((pos-in-keymap)) + (while (setq pos-in-keymap (position-if #'(lambda (item) + (and (listp item) + (or + ;; the menu item we want to delete + (and (eq 'menu-item (second item)) + (third item) + (and (string= (third item) name))) + ;; a stale subgroup + (and (keymapp (fourth item)) + (null (rest (fourth item))))))) + keymap)) + (setf (nthcdr pos-in-keymap keymap) + (nthcdr (+ 1 pos-in-keymap) keymap)))))) + (defun yas/set-mode-parent (mode parent) "Set parent mode of MODE to PARENT." (setf (yas/snippet-table-parent @@ -1034,11 +1116,14 @@ conditions to filter out potential expansions." (yas/current-key)) (yas/current-key)) (if templates - (let ((template-content (or (and (rest templates) ;; more than one - (yas/prompt-for-template-content (mapcar #'cdr templates))) - (yas/template-content (cdar templates))))) - (when template-content - (yas/expand-snippet start end template-content))) + (let ((template (or (and (rest templates) ;; more than one + (yas/prompt-for-template (mapcar #'cdr templates))) + (cdar templates)))) + (when template + (yas/expand-snippet start + end + (yas/template-content template) + (yas/template-env template)))) (if (eq yas/fallback-behavior 'return-nil) nil ; return nil (let* ((yas/minor-mode nil) @@ -1046,7 +1131,7 @@ conditions to filter out potential expansions." (when (commandp command) (call-interactively command))))))) -(defun yas/choose-snippet (&optional no-condition) +(defun yas/insert-snippet (&optional no-condition) "Choose a snippet to expand, pop-up a list of choices according to `yas/prompt-function'. @@ -1062,15 +1147,63 @@ by condition." (when key (yas/snippet-table-fetch (yas/current-snippet-table) key))) (yas/snippet-table-all-templates (yas/current-snippet-table))))) - (template-content (and templates - (or (and (rest templates) ;; more than one template for same key - (yas/prompt-for-template-content templates)) - (yas/template-content (car templates))))) + (template (and templates + (or (and (rest templates) ;; more than one template for same key + (yas/prompt-for-template templates)) + (car templates)))) (where (if mark-active (cons (region-beginning) (region-end)) (cons (point) (point))))) - (when template-content - (yas/expand-snippet (car where) (cdr where) template-content)))) + (when template + (yas/expand-snippet (car where) + (cdr where) + (yas/template-content template) + (yas/template-env template))))) + +(defun yas/find-snippet-file () + "Choose a snippet to edit." + (interactive) + (let* ((yas/buffer-local-condition 'always) + (templates (mapcar #'cdr + (if yas/choose-keys-first + (let ((key (yas/prompt-for-keys (yas/snippet-table-all-keys (yas/current-snippet-table))))) + (when key + (yas/snippet-table-fetch (yas/current-snippet-table) key))) + (yas/snippet-table-all-templates (yas/current-snippet-table))))) + (template (and templates + (or (and (rest templates) ;; more than one template for same key + (yas/prompt-for-template templates)) + (car templates))))) + + (when template + (find-file-other-window (yas/template-file template)) + (yas/snippet-editing-mode)))) + +(defun yas/compute-major-mode-and-parent (file) + (let* ((file-dir (directory-file-name (file-name-directory file))) + (major-mode-name (file-name-nondirectory file-dir)) + (parent-file-dir (directory-file-name (file-name-directory file-dir))) + (parent-mode-name (file-name-nondirectory parent-file-dir)) + (major-mode-sym (intern major-mode-name)) + (parent-mode-sym (intern parent-mode-name))) + (cons (when (fboundp major-mode-sym) + major-mode-sym) + (when (fboundp parent-mode-sym) + parent-mode-sym)))) + +(defun yas/load-snippet-buffer (&optional kill-buffer) + "Parse and load current buffer's snippet definition." + (interactive "P") + (if buffer-file-name + (let ((major-mode-and-parent (yas/compute-major-mode-and-parent buffer-file-name))) + (when major-mode-and-parent + (yas/define-snippets (car major-mode-and-parent) + (list (yas/parse-template buffer-file-name)) + (cdr major-mode-and-parent))) + (if kill-buffer + (kill-buffer) + (delete-window))) + (message "Save the buffer as a file first!"))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; User convenience functions, for using in snippet definitions @@ -1638,7 +1771,7 @@ The error should be ignored in `debug-ignored-errors'" ;;; they should account for all situations... ;;; -(defun yas/expand-snippet (start end template) +(defun yas/expand-snippet (start end template &optional snippet-vars) "Expand snippet at current point. Text between START and END will be deleted before inserting template." (run-hooks 'yas/before-expand-snippet-hook) @@ -1672,7 +1805,9 @@ will be deleted before inserting template." (insert template) (setq yas/deleted-text key) (setq yas/selected-text (when mark-active key)) - (setq snippet (yas/snippet-create (point-min) (point-max)))) + (setq snippet + (eval `(let ,(read snippet-vars) + (yas/snippet-create (point-min) (point-max)))))) (error (push (cons (point-min) (point-max)) buffer-undo-list) (error (format "[yas] parse error: %s" (cadr err)))))) @@ -2035,28 +2170,6 @@ When multiple expressions are found, only the last one counts." (set-marker (yas/field-end field) (point))) t)))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Major mode stuff -;; -(defvar yas/font-lock-keywords - (append '(("^#.*$" . font-lock-comment-face)) - lisp-font-lock-keywords - lisp-font-lock-keywords-1 - lisp-font-lock-keywords-2 - '(("$\\([0-9]+\\)" - (0 font-lock-keyword-face) - (1 font-lock-string-face t)) - ("${\\([0-9]+\\):?" - (0 font-lock-keyword-face) - (1 font-lock-warning-face t)) - ("\\(\\$\\)(" 1 font-lock-preprocessor-face) - ("}" - (0 font-lock-keyword-face))))) - -(define-derived-mode yas/snippet-editing-mode fundamental-mode "YASnippet" - "A mode for editing yasnippets" - (setq font-lock-defaults '(yas/font-lock-keywords))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Debug functions. Use (or change) at will whenever needed. ;; @@ -2092,8 +2205,8 @@ When multiple expressions are found, only the last one counts." (defun yas/exterminate-package () (interactive) + (yas/global-mode -1) (yas/minor-mode -1) - (unintern 'yasnippet) (mapatoms #'(lambda (atom) (when (string-match "yas/" (symbol-name atom)) (unintern atom))))) From 84e9294074ee6755e81ca3cce34c6b4d97b84860 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 20 Jul 2009 06:36:26 +0000 Subject: [PATCH 57/75] * having trouble with that menu... --- yasnippet.el | 70 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 34da49e..23725e6 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -358,7 +358,7 @@ snippet templates") (defconst yas/backquote-lisp-expression-regexp "`\\([^`]*\\)`" - "A regexp to recognize a \"`(...)`\" expression") + "A regexp to recognize a \"`lisp-expression`\" expression" ) (defconst yas/transform-mirror-regexp "${\\(?:\\([0-9]+\\):\\)?$\\([^}]*\\)" @@ -386,6 +386,29 @@ snippet templates") (defvar yas/minor-mode-menu nil "The menu bar menu used when `yas/minor-mode' is active.") +;; +;; This bit of code inspired from hideshow.el +;; +(defun yas/init-keymap-and-menu () + (setq yas/minor-mode-map (make-sparse-keymap)) + (setq yas/minor-mode-menu nil) + + (easy-menu-define yas/minor-mode-menu + yas/minor-mode-map + "Menu used when YAS/minor-mode is active." + (cons "YASnippet" + (mapcar #'(lambda (ent) + (when (third ent) + (define-key yas/minor-mode-map (third ent) (second ent))) + (vector (first ent) (second ent) t)) + (list (list "--") + (list "Expand trigger" 'yas/expand (read-kbd-macro yas/trigger-key)) + (list "Insert at point" 'yas/insert-snippet "\C-c&\C-s") + (list "About" 'yas/about) + (list "Reload-all-snippets" 'yas/reload-all) + (list "Load snippets..." 'yas/load-directory))))) + (define-key yas/snippet-editing-mode-map "\C-c\C-c" 'yas/load-snippet-buffer)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Major mode stuff ;; @@ -430,10 +453,9 @@ Key bindings: ;; The indicator for the mode line. " yas" :group 'yasnippet - (if yas/minor-mode - (progn - (easy-menu-add yas/minor-mode-menu)) - (easy-menu-remove yas/minor-mode-menu))) + (unless yas/minor-mode-menu + (yas/init-keymap-and-menu)) + (easy-menu-add yas/minor-mode-menu)) (defun yas/minor-mode-on () "Turn on YASnippet minor mode." @@ -448,35 +470,6 @@ Key bindings: (define-globalized-minor-mode yas/global-mode yas/minor-mode yas/minor-mode-on :group 'yasnippet) -;; -;; This bit of code inspired from hideshow.el -;; -(defun yas/init-keymap-and-menu () - (setq yas/minor-mode-map (make-sparse-keymap)) - (setq yas/minor-mode-menu nil) - - (easy-menu-define yas/minor-mode-menu - yas/minor-mode-map - "Menu used when YAS/minor-mode is active." - (cons "YASnippet" - (mapcar #'(lambda (ent) - (when (third ent) - (define-key yas/minor-mode-map (third ent) (second ent))) - (vector (first ent) (second ent) t)) - (list (list "--") - (list "Expand trigger" 'yas/expand (read-kbd-macro yas/trigger-key)) - (list "Insert at point" 'yas/insert-snippet "\C-c&\C-s") - (list "About" 'yas/about) - (list "Reload-all-snippets" 'yas/reload-all) - (list "Load snippets..." 'yas/load-directory))))) - (define-key yas/snippet-editing-mode-map "\C-c\C-c" 'yas/load-snippet-buffer)) - -;; -;; Init this on compilation/evaluation -;; -(unless yas/minor-mode-menu - (yas/init-keymap-and-menu)) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Internal structs for template management ;; @@ -722,6 +715,8 @@ Here's a list of currently recognized variables: (remove-if (lambda (file) (or (string-match "^\\." (file-name-nondirectory file)) + (string-match "~$" + (file-name-nondirectory file)) (if file? (file-directory-p file) (not (file-directory-p file))))) @@ -851,8 +846,13 @@ content of the file is the template." (message "done."))) (defun yas/reload-all () - "Reload all snippets." + "Reload all snippets and rebuild the YASnippet menu." (interactive) + (setq yas/snippet-tables (make-hash-table)) + (setq yas/menu-table (make-hash-table)) + (setq yas/minor-mode-menu nil) + (setq yas/minor-mode-map (make-sparse-keymap)) + (yas/init-keymap-and-menu) (if yas/root-directory (if (listp yas/root-directory) (dolist (directory yas/root-directory) From 6c5a0fd4fd477eebdc4d1f968d2f3bb1e7e1de77 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 20 Jul 2009 14:26:47 +0000 Subject: [PATCH 58/75] * bugs fixed, the real testing starts now... --- yasnippet.el | 188 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 136 insertions(+), 52 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 23725e6..082f282 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -128,15 +128,17 @@ representation using `read-kbd-macro'. " :type 'string :group 'yasnippet) -(defcustom yas/prev-field-key "S-TAB" +(defcustom yas/prev-field-key "" "The key to navigate to previous field when a snippet is active. +Can also be a list of keys. + Value is a string that is converted to the internal Emacs key representation using `read-kbd-macro'. " :type 'string :group 'yasnippet) -(defcustom yas/clear-field-key "C-d" +(defcustom yas/skip-and-clear-key "C-d" "The key to clear the currently active field. Value is a string that is converted to the internal Emacs key @@ -237,10 +239,17 @@ An error string \"[yas] error\" is returned instead." (defvar yas/keymap (make-sparse-keymap) "The keymap active while a snippet expansion is in progress.") +(defun yas/define-some-keys (keys keymap definition) + "Bind KEYS to DEFINITION in KEYMAP, read with `read-kbd-macro'" + (let ((keys (or (and (listp keys) keys) + (list keys)))) + (dolist (key keys) + (define-key keymap (read-kbd-macro key) definition)))) + (eval-when-compile - (define-key yas/keymap (read-kbd-macro yas/next-field-key) 'yas/next-field-or-maybe-expand) - (define-key yas/keymap (read-kbd-macro yas/prev-field-key) 'yas/prev-field) - (define-key yas/keymap (read-kbd-macro yas/clear-field-key) 'yas/clear-field-or-delete-char)) + (yas/define-some-keys yas/next-field-key yas/keymap 'yas/next-field-or-maybe-expand) + (yas/define-some-keys yas/prev-field-key yas/keymap 'yas/prev-field) + (yas/define-some-keys yas/skip-and-clear-key yas/keymap 'yas/skip-and-clear-or-delete-char)) (defvar yas/key-syntaxes (list "w" "w_" "w_." "^ ") "A list of syntax of a key. This list is tried in the order @@ -345,7 +354,7 @@ Here's an example: (defvar yas/escaped-characters '(?\\ ?` ?' ?$ ?} ) - "A list of characters which *might* need to be escaped in + "A list of characters which *might*n need to be escaped in snippet templates") (defconst yas/field-regexp @@ -380,10 +389,10 @@ snippet templates") ;; YASnippet minor mode ;; -(defvar yas/minor-mode-map nil +(defvar yas/minor-mode-map (make-sparse-keymap) "The keymap used when `yas/minor-mode' is active.") -(defvar yas/minor-mode-menu nil +(defvar yas/minor-mode-menu (make-sparse-keymap) "The menu bar menu used when `yas/minor-mode' is active.") ;; @@ -407,7 +416,7 @@ snippet templates") (list "About" 'yas/about) (list "Reload-all-snippets" 'yas/reload-all) (list "Load snippets..." 'yas/load-directory))))) - (define-key yas/snippet-editing-mode-map "\C-c\C-c" 'yas/load-snippet-buffer)) + (define-key snippet-mode-map "\C-c\C-c" 'yas/load-snippet-buffer)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Major mode stuff @@ -427,12 +436,12 @@ snippet templates") ("}" (0 font-lock-keyword-face))))) -(defvar yas/snippet-editing-mode-map (make-sparse-keymap)) +(defvar snippet-mode-map (make-sparse-keymap)) -(define-derived-mode yas/snippet-editing-mode fundamental-mode "YASnippet" +(define-derived-mode snippet-mode text-mode "YASnippet" "A mode for editing yasnippets" (setq font-lock-defaults '(yas/font-lock-keywords)) - (use-local-map yas/snippet-editing-mode-map)) + (use-local-map snippet-mode-map)) (define-minor-mode yas/minor-mode "Toggle YASnippet mode. @@ -449,11 +458,12 @@ You can customize the key through `yas/trigger-key'. Key bindings: \\{yas/minor-mode-map}" ;; The initial value. - nil + :keymap yas/minor-mode-map ;; The indicator for the mode line. " yas" :group 'yasnippet - (unless yas/minor-mode-menu + (unless (and yas/minor-mode-map + (second yas/minor-mode-map)) (yas/init-keymap-and-menu)) (easy-menu-add yas/minor-mode-menu)) @@ -486,6 +496,7 @@ Key bindings: (defstruct (yas/snippet-table (:constructor yas/make-snippet-table ())) "A table to store snippets for a perticular mode." (hash (make-hash-table :test 'equal)) + (default-directory nil) (parent nil)) (defun yas/template-condition-predicate (condition) @@ -524,14 +535,15 @@ This function implements the rules described in (defun yas/snippet-table-fetch (table key) "Fetch a snippet binding to KEY from TABLE. If not found, fetch from parent if any." - (let* ((unfiltered (gethash key (yas/snippet-table-hash table))) - (templates (yas/filter-templates-by-condition unfiltered))) - (when (and (null templates) - (not (null (yas/snippet-table-parent table)))) - (setq templates (yas/snippet-table-fetch - (yas/snippet-table-parent table) - key))) - templates)) + (when table + (let* ((unfiltered (gethash key (yas/snippet-table-hash table))) + (templates (yas/filter-templates-by-condition unfiltered))) + (when (and (null templates) + (not (null (yas/snippet-table-parent table)))) + (setq templates (yas/snippet-table-fetch + (yas/snippet-table-parent table) + key))) + templates))) (defun yas/snippet-table-all-templates (table) (when table @@ -606,24 +618,39 @@ a list of modes like this to help the judgement." (error (cdr retval))) retval)) -(defun yas/snippet-table (mode) - "Get the snippet table corresponding to MODE." - (let ((table (gethash mode yas/snippet-tables))) +(defun yas/snippet-table-get-create (mode &optional directory) + "Get the snippet table corresponding to MODE. + +Optional DIRECTORY gets recorded as the default directory to +search for snippet files if the retrieved/created table didn't +already have such a property." + (let ((table (gethash mode + yas/snippet-tables))) (unless table (setq table (yas/make-snippet-table)) (puthash mode table yas/snippet-tables)) + (unless (or (not directory) (yas/snippet-table-default-directory table)) + (setf (yas/snippet-table-default-directory table) + directory)) table)) -(defsubst yas/current-snippet-table () +(defun yas/current-snippet-table (&optional mode-symbol dont-search-parents) "Get the snippet table for current major-mode." - (yas/snippet-table major-mode)) + (let ((mode (or mode-symbol + major-mode))) + (or (gethash mode + yas/snippet-tables) + (and (not dont-search-parents) + (get mode 'derived-mode-parent) + (yas/current-snippet-table (get mode 'derived-mode-parent)))))) (defun yas/menu-keymap-for-mode (mode) "Get the menu keymap correspondong to MODE." (let ((keymap (gethash mode yas/menu-table))) (unless keymap (setq keymap (make-sparse-keymap)) - (puthash mode keymap yas/menu-table)) + (puthash mode + keymap yas/menu-table)) keymap)) (defun yas/current-key () @@ -826,7 +853,8 @@ TEMPLATES is a list of `yas/template'." snippet-defs)))) (yas/define-snippets mode-sym snippet-defs - parent) + parent + directory) (dolist (subdir (yas/subdirs directory)) (yas/load-directory-1 subdir mode-sym)))) @@ -965,7 +993,7 @@ all the parameters: yas/version ") -- pluskid /joaotavora "))) -(defun yas/define-snippets (mode snippets &optional parent-mode) +(defun yas/define-snippets (mode snippets &optional parent-mode directory) "Define snippets for MODE. SNIPPETS is a list of snippet definitions, each taking the following form: @@ -974,10 +1002,17 @@ snippet definitions, each taking the following form: NAME, CONDITION or GROUP may be omitted. Optional PARENT-MODE can be used to specify the parent mode of MODE. That is, when looking a snippet in MODE failed, it can refer to its parent -mode. The PARENT-MODE does not need to be a real mode." - (let ((snippet-table (yas/snippet-table mode)) +mode. The PARENT-MODE does not need to be a real mode. + +Optional DIRECTORY is recorded in the `yas/snippet-table' if it +is created for the first time. Then, it becomes the default +directory to find snippet files. + + +" + (let ((snippet-table (yas/snippet-table-get-create mode directory)) (parent-table (if parent-mode - (yas/snippet-table parent-mode) + (yas/snippet-table-get-create parent-mode) nil)) (keymap (if yas/use-menu (yas/menu-keymap-for-mode mode) @@ -1068,8 +1103,8 @@ Skip any submenus named \"parent mode\"" (defun yas/set-mode-parent (mode parent) "Set parent mode of MODE to PARENT." (setf (yas/snippet-table-parent - (yas/snippet-table mode)) - (yas/snippet-table parent)) + (yas/snippet-table-get-create mode)) + (yas/snippet-table-get-create parent)) (when yas/use-menu (define-key (yas/menu-keymap-for-mode mode) (vector 'parent-mode) `(menu-item "parent mode" @@ -1154,14 +1189,15 @@ by condition." (where (if mark-active (cons (region-beginning) (region-end)) (cons (point) (point))))) - (when template - (yas/expand-snippet (car where) - (cdr where) - (yas/template-content template) - (yas/template-env template))))) + (if template + (yas/expand-snippet (car where) + (cdr where) + (yas/template-content template) + (yas/template-env template)) + (message "[yas] No snippets can be inserted here!")))) -(defun yas/find-snippet-file () - "Choose a snippet to edit." +(defun yas/find-snippet-by-key () + "Choose a snippet to edit, selection like `yas/insert-snippet'." (interactive) (let* ((yas/buffer-local-condition 'always) (templates (mapcar #'cdr @@ -1177,7 +1213,52 @@ by condition." (when template (find-file-other-window (yas/template-file template)) - (yas/snippet-editing-mode)))) + (snippet-mode)))) + +(defun yas/guess-snippet-directory () + "Try to guess the suitable yassnippet based on `major-mode'" + (let ((loaded-root (or (and (listp yas/root-directory) + (first yas/root-directory)) + yas/root-directory)) + (mode major-mode) + (path)) + (when loaded-root + (while mode + (setq path (format "%s/%s" + mode + (or path + ""))) + (setq mode (get mode 'derived-mode-parent))) + (concat loaded-root "/" path)))) + +(defun yas/find-snippet (&optional same-window) + "Find a snippet file in a suitable directory." + (interactive "P") + (let* ((current-table (yas/current-snippet-table major-mode 'dont-search-parents)) + (parents-table (yas/current-snippet-table major-mode)) + (parents-directory (and parents-table + (yas/snippet-table-default-directory parents-table))) + (guessed-directory (or (and current-table + (yas/snippet-table-default-directory current-table)) + (yas/guess-snippet-directory) + default-directory)) + (buffer)) + (unless (file-exists-p guessed-directory) + (if (y-or-n-p (format "Guessed directory (%s) does not exist! Create? " guessed-directory)) + (make-directory guessed-directory 'also-make-parents) + (if parents-directory + (setq guessed-directory parents-directory) + (setq guessed-directory default-directory)))) + (let ((default-directory guessed-directory)) + (setq buffer (call-interactively (if same-window + 'find-file + 'find-file-other-window))) + (when buffer + (save-excursion + (set-buffer buffer) + (when (eq major-mode 'fundamental-mode) + (snippet-mode))))))) + (defun yas/compute-major-mode-and-parent (file) (let* ((file-dir (directory-file-name (file-name-directory file))) @@ -1200,9 +1281,9 @@ by condition." (yas/define-snippets (car major-mode-and-parent) (list (yas/parse-template buffer-file-name)) (cdr major-mode-and-parent))) - (if kill-buffer - (kill-buffer) - (delete-window))) + (when (and (buffer-modified-p) + (y-or-n-p "Save snippet? ")) + (save-buffer))) (message "Save the buffer as a file first!"))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1612,7 +1693,7 @@ holds the keymap." (overlay-put overlay 'evaporate t) overlay)) -(defun yas/clear-field-or-delete-char (&optional field) +(defun yas/skip-and-clear-or-delete-char (&optional field) "Clears an unmodified field if at field start, otherwise deletes a character normally." (interactive) @@ -1623,11 +1704,12 @@ deletes a character normally." (cond ((and field (not (yas/field-modified-p field)) (eq (point) (marker-position (yas/field-start field)))) - (yas/clear-field field)) + (yas/skip-and-clear field) + (yas/next-field 1)) (t (call-interactively 'delete-char))))) -(defun yas/clear-field (field) +(defun yas/skip-and-clear (field) "Deletes the region of FIELD and sets it modified state to t" (setf (yas/field-modified-p field) t) (delete-region (yas/field-start field) (yas/field-end field))) @@ -1678,7 +1760,7 @@ progress." (eq (point) (if (markerp (yas/field-start field)) (marker-position (yas/field-start field)) (yas/field-start field)))) - (yas/clear-field field)) + (yas/skip-and-clear field)) (setf (yas/field-modified-p field) t)))))) ;;; Apropos protection overlays: @@ -1806,8 +1888,10 @@ will be deleted before inserting template." (setq yas/deleted-text key) (setq yas/selected-text (when mark-active key)) (setq snippet - (eval `(let ,(read snippet-vars) - (yas/snippet-create (point-min) (point-max)))))) + (if snippet-vars + (eval `(let ,(read snippet-vars) + (yas/snippet-create (point-min) (point-max)))) + (yas/snippet-create (point-min) (point-max))))) (error (push (cons (point-min) (point-max)) buffer-undo-list) (error (format "[yas] parse error: %s" (cadr err)))))) From b654545be3a9bd8e5f6570bbec638ada911f88b1 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 20 Jul 2009 16:27:12 +0000 Subject: [PATCH 59/75] * the marker-insertion-type thing is very very tricky with undo. Will probably have to go back to my "advance-maybe" strategy (which has less bugs), but still I'm commiting this. --- yasnippet.el | 135 +++++++++++++++++++++++++++++---------------------- 1 file changed, 77 insertions(+), 58 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 082f282..413f0b9 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -432,7 +432,9 @@ snippet templates") ("${\\([0-9]+\\):?" (0 font-lock-keyword-face) (1 font-lock-warning-face t)) - ("\\(\\$\\)(" 1 font-lock-preprocessor-face) + ("${" font-lock-keyword-face) + ("$[0-9]+?" font-lock-preprocessor-face) + ("\\(\\$(\\)" 1 font-lock-preprocessor-face) ("}" (0 font-lock-keyword-face))))) @@ -772,21 +774,22 @@ Here's a list of currently recognized variables: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Popping up for keys and templates ;; -(defun yas/prompt-for-template (templates) +(defun yas/prompt-for-template (templates &optional prompt) "Interactively choose a template from the list TEMPLATES. TEMPLATES is a list of `yas/template'." (let ((template (some #'(lambda (fn) - (funcall fn "Choose a snippet: " templates #'(lambda (template) - (yas/template-name template)))) + (funcall fn (or prompt "Choose a snippet: ") + templates #'(lambda (template) + (yas/template-name template)))) yas/prompt-functions))) template)) -(defun yas/prompt-for-keys (keys) +(defun yas/prompt-for-keys (keys &optional prompt) "Interactively choose a template key from the list KEYS." (if keys (some #'(lambda (fn) - (funcall fn "Choose a snippet key: " keys)) + (funcall fn (or prompt "Choose a snippet key: ") keys)) yas/prompt-functions))) (defun yas/x-prompt (prompt choices &optional display-fn) @@ -1202,13 +1205,15 @@ by condition." (let* ((yas/buffer-local-condition 'always) (templates (mapcar #'cdr (if yas/choose-keys-first - (let ((key (yas/prompt-for-keys (yas/snippet-table-all-keys (yas/current-snippet-table))))) + (let ((key (yas/prompt-for-keys (yas/snippet-table-all-keys (yas/current-snippet-table)) + "Choose a snippet key to edit: "))) (when key (yas/snippet-table-fetch (yas/current-snippet-table) key))) (yas/snippet-table-all-templates (yas/current-snippet-table))))) (template (and templates (or (and (rest templates) ;; more than one template for same key - (yas/prompt-for-template templates)) + (yas/prompt-for-template templates + "Choose a snippet template to edit: ")) (car templates))))) (when template @@ -1229,7 +1234,10 @@ by condition." (or path ""))) (setq mode (get mode 'derived-mode-parent))) - (concat loaded-root "/" path)))) + (concat loaded-root + (unless (string-match "/$" loaded-root) "/") + path)))) + (defun yas/find-snippet (&optional same-window) "Find a snippet file in a suitable directory." @@ -1461,15 +1469,16 @@ delegate to `yas/next-field'." (active-field-pos (position active-field live-fields)) (target-pos (+ arg active-field-pos)) (target-field (nth target-pos live-fields))) - ;; First check if we're moving out of a field with a transform + ;; Are we moving out of a field? ;; - (when (and active-field - (yas/field-transform active-field)) - (let* ((yas/moving-away t) - (yas/text (yas/field-text-for-display active-field)) - (text yas/text) - (yas/modified-p (yas/field-modified-p active-field))) - (yas/eval-string (yas/field-transform active-field)))) + (when active-field + (yas/open-field-and-parents active-field 'close-instead) + (when (yas/field-transform active-field) + (let* ((yas/moving-away t) + (yas/text (yas/field-text-for-display active-field)) + (text yas/text) + (yas/modified-p (yas/field-modified-p active-field))) + (yas/eval-string (yas/field-transform active-field))))) ;; Now actually move... (cond ((>= target-pos (length live-fields)) (yas/exit-snippet snippet)) @@ -1481,12 +1490,17 @@ delegate to `yas/next-field'." (defun yas/move-to-field (snippet field) "Update SNIPPET to move to field FIELD. -Also create some protection overlays" +Also: + +* \"open\" the field, i.e nullify its start-marker insertion type + +* create some protection overlays" (goto-char (yas/field-start field)) (setf (yas/snippet-active-field snippet) field) (yas/make-move-active-field-overlay snippet field) (yas/make-move-field-protection-overlays snippet field) (overlay-put yas/active-field-overlay 'yas/field field) + (yas/open-field-and-parents field) (unless (yas/field-modified-p field) (if (yas/field-update-display field snippet) (let ((inhibit-modification-hooks t)) @@ -1544,7 +1558,9 @@ the original marker object with the position set to nil." (defun yas/points-to-markers (snippet) "Convert all cons (POINT . MARKER) in SNIPPET to markers. This -is done by setting MARKER to POINT with `set-marker'." +is done by setting MARKER to POINT with `set-marker'. + +Also closes all the fields before marker conversion." (dolist (field (yas/snippet-fields snippet)) (setf (yas/field-start field) (set-marker (cdr (yas/field-start field)) (car (yas/field-start field)))) (setf (yas/field-end field) (set-marker (cdr (yas/field-end field)) (car (yas/field-end field)))) @@ -1582,14 +1598,6 @@ NO-HOOKS means don't run the `yas/after-exit-snippet-hook' hooks." (when yas/field-protection-overlays (mapcar #'delete-overlay yas/field-protection-overlays))) - ;; stacked expansion: if the original expansion took place from a - ;; field, make sure we advance it here at least to - ;; `yas/snippet-end'... - ;; - (let ((previous-field (yas/snippet-previous-active-field snippet))) - (when previous-field - (yas/advance-field-and-parents-maybe previous-field yas/snippet-end))) - ;; Convert all markers to points, ;; (yas/markers-to-points snippet) @@ -1714,14 +1722,14 @@ deletes a character normally." (setf (yas/field-modified-p field) t) (delete-region (yas/field-start field) (yas/field-end field))) -(defun yas/advance-field-and-parents-maybe (field end) - "Advance FIELDs end-marker to END and recurse for parent fields - -This is needed since markers don't \"rear-advance\" like overlays" - (when (< (yas/field-end field) end) - (set-marker (yas/field-end field) end) - (when (yas/field-parent-field field) - (yas/advance-field-and-parents-maybe (yas/field-parent-field field) end)))) +(defun yas/open-field-and-parents (field &optional close-instead) + "Open FIELD, i.e. fiddle with its start-marker" + (set-marker-insertion-type (yas/field-start field) + (if close-instead + t + nil)) + (when (yas/field-parent-field field) + (yas/open-field-and-parents (yas/field-parent-field field)))) (defun yas/make-move-active-field-overlay (snippet field) "Place the active field overlay in SNIPPET's FIELD. @@ -1751,7 +1759,6 @@ progress." (unless (yas/undo-in-progress) (let ((field (overlay-get yas/active-field-overlay 'yas/field))) (cond (after? - (yas/advance-field-and-parents-maybe field (overlay-end overlay)) (yas/field-update-display field (car (yas/snippets-at-point))) (yas/update-mirrors (car (yas/snippets-at-point)))) (field @@ -1897,14 +1904,13 @@ will be deleted before inserting template." (error (format "[yas] parse error: %s" (cadr err)))))) ;; stacked-expansion: This checks for stacked expansion, save the - ;; `yas/previous-active-field' and advance its boudary. + ;; `yas/previous-active-field'. ;; (let ((existing-field (and yas/active-field-overlay (overlay-buffer yas/active-field-overlay) (overlay-get yas/active-field-overlay 'yas/field)))) (when existing-field - (setf (yas/snippet-previous-active-field snippet) existing-field) - (yas/advance-field-and-parents-maybe existing-field (overlay-end yas/active-field-overlay)))) + (setf (yas/snippet-previous-active-field snippet) existing-field))) ;; Move to the first of fields, or exit the snippet to its exit ;; point @@ -2129,6 +2135,12 @@ Meant to be called in a narrowed buffer, does various passes" (error nil))) +(defun yas/make-marker (pos) + "Create a marker at POS with `t' `marker-insertion-type'" + (let ((marker (set-marker (make-marker) pos))) + (set-marker-insertion-type marker t) + marker)) + (defun yas/field-parse-create (snippet &optional parent-field) "Parse most field expression, except for the simple one \"$n\". @@ -2149,8 +2161,8 @@ When multiple expressions are found, only the last one counts." (eq (string-match "$[ \t\n]+(" (match-string-no-properties 2)) 0))) (not (and number (zerop number))) (yas/make-field number - (set-marker (make-marker) (match-beginning 2)) - (set-marker (make-marker) (1- real-match-end-0)) + (yas/make-marker (match-beginning 2)) + (yas/make-marker (1- real-match-end-0)) parent-field)))) (when brand-new-field (delete-region (1- real-match-end-0) real-match-end-0) @@ -2180,8 +2192,8 @@ When multiple expressions are found, only the last one counts." (yas/snippet-find-field snippet number)))) (when (and real-match-end-0 field) - (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) - (set-marker (make-marker) (match-beginning 0)) + (push (yas/make-mirror (yas/make-marker (match-beginning 0)) + (yas/make-marker (match-beginning 0)) (buffer-substring-no-properties (match-beginning 2) (1- real-match-end-0))) (yas/field-mirrors field)) @@ -2194,7 +2206,7 @@ When multiple expressions are found, only the last one counts." (cond ((zerop number) (setf (yas/snippet-exit snippet) - (set-marker (make-marker) (match-end 0))) + (yas/make-marker (match-end 0))) (save-excursion (goto-char (match-beginning 0)) (when (and yas/wrap-around-region yas/selected-text) @@ -2203,13 +2215,13 @@ When multiple expressions are found, only the last one counts." (t (let ((field (yas/snippet-find-field snippet number))) (if field - (push (yas/make-mirror (set-marker (make-marker) (match-beginning 0)) - (set-marker (make-marker) (match-beginning 0)) + (push (yas/make-mirror (yas/make-marker (match-beginning 0)) + (yas/make-marker (match-beginning 0)) nil) (yas/field-mirrors field)) (push (yas/make-field number - (set-marker (make-marker) (match-beginning 0)) - (set-marker (make-marker) (match-beginning 0)) + (yas/make-marker (match-beginning 0)) + (yas/make-marker (match-beginning 0)) nil) (yas/snippet-fields snippet)))) (delete-region (match-beginning 0) (match-end 0))))))) @@ -2232,11 +2244,12 @@ When multiple expressions are found, only the last one counts." (yas/field-text-for-display field)))) (when (and reflection (not (string= reflection (buffer-substring-no-properties (yas/mirror-start mirror) (yas/mirror-end mirror))))) + (goto-char (yas/mirror-start mirror)) + (set-marker-insertion-type (yas/mirror-start mirror) nil) (insert reflection) - (if (> (yas/mirror-end mirror) (point)) - (delete-region (point) (yas/mirror-end mirror)) - (set-marker (yas/mirror-end mirror) (point)))))) + (delete-region (point) (yas/mirror-end mirror)) + (set-marker-insertion-type (yas/mirror-start mirror) t)))) (defun yas/field-update-display (field snippet) "Much like `yas/mirror-update-display', but for fields" @@ -2266,15 +2279,23 @@ When multiple expressions are found, only the last one counts." (princ (format "\nPost command hook: %s\n" post-command-hook)) (princ (format "\nPre command hook: %s\n" pre-command-hook)) - (princ (format "%s live snippets in total" (length (yas/snippets-at-point (quote all-snippets))))) - (princ (format "%s live snippets at point:" (length (yas/snippets-at-point)))) + (princ (format "%s live snippets in total\n" (length (yas/snippets-at-point (quote all-snippets))))) + (princ (format "%s live snippets at point:\n\n" (length (yas/snippets-at-point)))) (dolist (snippet (yas/snippets-at-point)) (princ (format "\tid: %s and active field from %s to %s covering \"%s\"\n" (yas/snippet-id snippet) (marker-position (yas/field-start (yas/snippet-active-field snippet))) (marker-position (yas/field-end (yas/snippet-active-field snippet))) - (buffer-substring-no-properties (yas/field-start (yas/snippet-active-field snippet)) (yas/field-end (yas/snippet-active-field snippet)))))) + (buffer-substring-no-properties (yas/field-start (yas/snippet-active-field snippet)) (yas/field-end (yas/snippet-active-field snippet))))) + (dolist (field (yas/snippet-fields snippet)) + (princ (format "\tn: %d field from %s to %s covering \"%s\"\n" + (yas/field-number field) + (marker-position (yas/field-start field)) + (marker-position (yas/field-end field)) + (buffer-substring-no-properties (yas/field-start field) (yas/field-end field)))))) + + (princ (format "\nUndo is %s and point-max is %s.\n" (if (eq buffer-undo-list t) @@ -2304,7 +2325,6 @@ When multiple expressions are found, only the last one counts." (erase-buffer) (setq buffer-undo-list nil) (c-mode) - (yas/initialize) (yas/minor-mode 1) (let ((abbrev)) ;; (if (require 'ido nil t) @@ -2312,9 +2332,8 @@ When multiple expressions are found, only the last one counts." ;; (setq abbrev "prop")) (setq abbrev "bosta") (insert abbrev)) - (when quiet - (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local)) - ) + (unless quiet + (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local))) (provide 'yasnippet) From f42d96e2dcfc99a3df69ca82c149c67f09f15183 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 20 Jul 2009 16:39:33 +0000 Subject: [PATCH 60/75] * went back to the previous "advance" strategy and have it seems I have isolated the bug... --- yasnippet.el | 70 +++++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 413f0b9..7457b95 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -1469,16 +1469,15 @@ delegate to `yas/next-field'." (active-field-pos (position active-field live-fields)) (target-pos (+ arg active-field-pos)) (target-field (nth target-pos live-fields))) - ;; Are we moving out of a field? + ;; First check if we're moving out of a field with a transform ;; - (when active-field - (yas/open-field-and-parents active-field 'close-instead) - (when (yas/field-transform active-field) - (let* ((yas/moving-away t) - (yas/text (yas/field-text-for-display active-field)) - (text yas/text) - (yas/modified-p (yas/field-modified-p active-field))) - (yas/eval-string (yas/field-transform active-field))))) + (when (and active-field + (yas/field-transform active-field)) + (let* ((yas/moving-away t) + (yas/text (yas/field-text-for-display active-field)) + (text yas/text) + (yas/modified-p (yas/field-modified-p active-field))) + (yas/eval-string (yas/field-transform active-field)))) ;; Now actually move... (cond ((>= target-pos (length live-fields)) (yas/exit-snippet snippet)) @@ -1490,17 +1489,12 @@ delegate to `yas/next-field'." (defun yas/move-to-field (snippet field) "Update SNIPPET to move to field FIELD. -Also: - -* \"open\" the field, i.e nullify its start-marker insertion type - -* create some protection overlays" +Also create some protection overlays" (goto-char (yas/field-start field)) (setf (yas/snippet-active-field snippet) field) (yas/make-move-active-field-overlay snippet field) (yas/make-move-field-protection-overlays snippet field) (overlay-put yas/active-field-overlay 'yas/field field) - (yas/open-field-and-parents field) (unless (yas/field-modified-p field) (if (yas/field-update-display field snippet) (let ((inhibit-modification-hooks t)) @@ -1558,9 +1552,7 @@ the original marker object with the position set to nil." (defun yas/points-to-markers (snippet) "Convert all cons (POINT . MARKER) in SNIPPET to markers. This -is done by setting MARKER to POINT with `set-marker'. - -Also closes all the fields before marker conversion." +is done by setting MARKER to POINT with `set-marker'." (dolist (field (yas/snippet-fields snippet)) (setf (yas/field-start field) (set-marker (cdr (yas/field-start field)) (car (yas/field-start field)))) (setf (yas/field-end field) (set-marker (cdr (yas/field-end field)) (car (yas/field-end field)))) @@ -1598,6 +1590,14 @@ NO-HOOKS means don't run the `yas/after-exit-snippet-hook' hooks." (when yas/field-protection-overlays (mapcar #'delete-overlay yas/field-protection-overlays))) + ;; stacked expansion: if the original expansion took place from a + ;; field, make sure we advance it here at least to + ;; `yas/snippet-end'... + ;; + (let ((previous-field (yas/snippet-previous-active-field snippet))) + (when previous-field + (yas/advance-field-and-parents-maybe previous-field yas/snippet-end))) + ;; Convert all markers to points, ;; (yas/markers-to-points snippet) @@ -1722,14 +1722,14 @@ deletes a character normally." (setf (yas/field-modified-p field) t) (delete-region (yas/field-start field) (yas/field-end field))) -(defun yas/open-field-and-parents (field &optional close-instead) - "Open FIELD, i.e. fiddle with its start-marker" - (set-marker-insertion-type (yas/field-start field) - (if close-instead - t - nil)) - (when (yas/field-parent-field field) - (yas/open-field-and-parents (yas/field-parent-field field)))) +(defun yas/advance-field-and-parents-maybe (field end) + "Advance FIELDs end-marker to END and recurse for parent fields + +This is needed since markers don't \"rear-advance\" like overlays" + (when (< (yas/field-end field) end) + (set-marker (yas/field-end field) end) + (when (yas/field-parent-field field) + (yas/advance-field-and-parents-maybe (yas/field-parent-field field) end)))) (defun yas/make-move-active-field-overlay (snippet field) "Place the active field overlay in SNIPPET's FIELD. @@ -1759,6 +1759,7 @@ progress." (unless (yas/undo-in-progress) (let ((field (overlay-get yas/active-field-overlay 'yas/field))) (cond (after? + (yas/advance-field-and-parents-maybe field (overlay-end overlay)) (yas/field-update-display field (car (yas/snippets-at-point))) (yas/update-mirrors (car (yas/snippets-at-point)))) (field @@ -1904,13 +1905,14 @@ will be deleted before inserting template." (error (format "[yas] parse error: %s" (cadr err)))))) ;; stacked-expansion: This checks for stacked expansion, save the - ;; `yas/previous-active-field'. + ;; `yas/previous-active-field' and advance its boudary. ;; (let ((existing-field (and yas/active-field-overlay (overlay-buffer yas/active-field-overlay) (overlay-get yas/active-field-overlay 'yas/field)))) (when existing-field - (setf (yas/snippet-previous-active-field snippet) existing-field))) + (setf (yas/snippet-previous-active-field snippet) existing-field) + (yas/advance-field-and-parents-maybe existing-field (overlay-end yas/active-field-overlay)))) ;; Move to the first of fields, or exit the snippet to its exit ;; point @@ -2136,9 +2138,9 @@ Meant to be called in a narrowed buffer, does various passes" nil))) (defun yas/make-marker (pos) - "Create a marker at POS with `t' `marker-insertion-type'" + "Create a marker at POS with `nil' `marker-insertion-type'" (let ((marker (set-marker (make-marker) pos))) - (set-marker-insertion-type marker t) + (set-marker-insertion-type marker nil) marker)) (defun yas/field-parse-create (snippet &optional parent-field) @@ -2244,12 +2246,11 @@ When multiple expressions are found, only the last one counts." (yas/field-text-for-display field)))) (when (and reflection (not (string= reflection (buffer-substring-no-properties (yas/mirror-start mirror) (yas/mirror-end mirror))))) - (goto-char (yas/mirror-start mirror)) - (set-marker-insertion-type (yas/mirror-start mirror) nil) (insert reflection) - (delete-region (point) (yas/mirror-end mirror)) - (set-marker-insertion-type (yas/mirror-start mirror) t)))) + (if (> (yas/mirror-end mirror) (point)) + (delete-region (point) (yas/mirror-end mirror)) + (set-marker (yas/mirror-end mirror) (point)))))) (defun yas/field-update-display (field snippet) "Much like `yas/mirror-update-display', but for fields" @@ -2308,6 +2309,7 @@ When multiple expressions are found, only the last one counts." (dolist (undo-elem first-ten) (princ (format "%2s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 70)))))))) + (defun yas/exterminate-package () (interactive) (yas/global-mode -1) From 5b48e2ec1517bf1216613956696c265c366af8e5 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Mon, 20 Jul 2009 22:08:53 +0000 Subject: [PATCH 61/75] * little bugs and the adjacent field problem fixed --- yasnippet.el | 53 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 7457b95..672dcc7 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -443,6 +443,7 @@ snippet templates") (define-derived-mode snippet-mode text-mode "YASnippet" "A mode for editing yasnippets" (setq font-lock-defaults '(yas/font-lock-keywords)) + (set (make-local-variable 'require-final-newline) nil) (use-local-map snippet-mode-map)) (define-minor-mode yas/minor-mode @@ -1280,7 +1281,7 @@ by condition." (when (fboundp parent-mode-sym) parent-mode-sym)))) -(defun yas/load-snippet-buffer (&optional kill-buffer) +(defun yas/load-snippet-buffer (&optional kill) "Parse and load current buffer's snippet definition." (interactive "P") (if buffer-file-name @@ -1291,7 +1292,8 @@ by condition." (cdr major-mode-and-parent))) (when (and (buffer-modified-p) (y-or-n-p "Save snippet? ")) - (save-buffer))) + (save-buffer)) + (quit-window)) (message "Save the buffer as a file first!"))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1374,10 +1376,9 @@ Otherwise throw exception." start end parent-field (mirrors '()) - (next nil) - (prev nil) (transform nil) - (modified-p nil)) + (modified-p nil) + (back-adjacent-fields nil)) (defstruct (yas/mirror (:constructor yas/make-mirror (start end transform))) "A mirror." @@ -1454,8 +1455,8 @@ delegate to `yas/next-field'." (let ((yas/fallback-behavior 'return-nil) (active-field (overlay-get yas/active-field-overlay 'yas/field))) (when active-field - (unless (yas/expand active-field)) - (yas/next-field))) + (unless (yas/expand active-field) + (yas/next-field)))) (yas/next-field))) (defun yas/next-field (&optional arg) @@ -1477,6 +1478,7 @@ delegate to `yas/next-field'." (yas/text (yas/field-text-for-display active-field)) (text yas/text) (yas/modified-p (yas/field-modified-p active-field))) + ;;; primary field transform: exit call to field-transform (yas/eval-string (yas/field-transform active-field)))) ;; Now actually move... (cond ((>= target-pos (length live-fields)) @@ -1495,8 +1497,9 @@ Also create some protection overlays" (yas/make-move-active-field-overlay snippet field) (yas/make-move-field-protection-overlays snippet field) (overlay-put yas/active-field-overlay 'yas/field field) + ;;; primary field transform: first call to snippet transform (unless (yas/field-modified-p field) - (if (yas/field-update-display field snippet) + (if (save-excursion (yas/field-update-display field snippet)) (let ((inhibit-modification-hooks t)) (yas/update-mirrors snippet)) (setf (yas/field-modified-p field) nil)))) @@ -1595,7 +1598,7 @@ NO-HOOKS means don't run the `yas/after-exit-snippet-hook' hooks." ;; `yas/snippet-end'... ;; (let ((previous-field (yas/snippet-previous-active-field snippet))) - (when previous-field + (when (and yas/snippet-end previous-field) (yas/advance-field-and-parents-maybe previous-field yas/snippet-end))) ;; Convert all markers to points, @@ -1729,7 +1732,12 @@ This is needed since markers don't \"rear-advance\" like overlays" (when (< (yas/field-end field) end) (set-marker (yas/field-end field) end) (when (yas/field-parent-field field) - (yas/advance-field-and-parents-maybe (yas/field-parent-field field) end)))) + (yas/advance-field-and-parents-maybe (yas/field-parent-field field) end))) + ;; take care of adjacents + (let ((adjacents (yas/field-back-adjacent-fields field))) + (when adjacents + (dolist (adjacent adjacents) + (set-marker (yas/field-start adjacent) end))))) (defun yas/make-move-active-field-overlay (snippet field) "Place the active field overlay in SNIPPET's FIELD. @@ -1760,7 +1768,10 @@ progress." (let ((field (overlay-get yas/active-field-overlay 'yas/field))) (cond (after? (yas/advance-field-and-parents-maybe field (overlay-end overlay)) - (yas/field-update-display field (car (yas/snippets-at-point))) + ;;; primary field transform: normal calls to expression + (let ((saved-point (point))) + (yas/field-update-display field (car (yas/snippets-at-point))) + (goto-char saved-point)) (yas/update-mirrors (car (yas/snippets-at-point)))) (field (when (and (not after?) @@ -1938,7 +1949,10 @@ will be deleted before inserting template." reviving it. Meant to exit in the `buffer-undo-list'." - (yas/commit-snippet snippet 'no-hooks)) + ;; slightly optimize: this action is only needed for snippets with + ;; at least one field + (when (yas/snippet-fields snippet) + (yas/commit-snippet snippet 'no-hooks))) (defun yas/snippet-revive (beg end snippet) "Revives the SNIPPET and creates a control overlay from BEG to @@ -2003,10 +2017,11 @@ Returns the newly created snippet." (yas/snippet-field-compare field1 field2)))) (let ((prev nil)) (dolist (field (yas/snippet-fields snippet)) - (setf (yas/field-prev field) prev) - (when prev - (setf (yas/field-next prev) field)) - (setq prev field)))) + ;; also check for other fields adjacent to this fields back + (dolist (otherfield (yas/snippet-fields snippet)) + (when (and (not (eq otherfield field)) + (= (yas/field-end field) (yas/field-start otherfield))) + (push otherfield (yas/field-back-adjacent-fields field))))))) (defun yas/snippet-parse-create (snippet) "Parse a recently inserted snippet template, creating all @@ -2290,11 +2305,12 @@ When multiple expressions are found, only the last one counts." (marker-position (yas/field-end (yas/snippet-active-field snippet))) (buffer-substring-no-properties (yas/field-start (yas/snippet-active-field snippet)) (yas/field-end (yas/snippet-active-field snippet))))) (dolist (field (yas/snippet-fields snippet)) - (princ (format "\tn: %d field from %s to %s covering \"%s\"\n" + (princ (format "\tn: %d field from %s to %s covering \"%s\" adjacent %s\n" (yas/field-number field) (marker-position (yas/field-start field)) (marker-position (yas/field-end field)) - (buffer-substring-no-properties (yas/field-start field) (yas/field-end field)))))) + (buffer-substring-no-properties (yas/field-start field) (yas/field-end field)) + (length (yas/field-back-adjacent-fields field)))))) @@ -2326,6 +2342,7 @@ When multiple expressions are found, only the last one counts." (mapcar #'yas/commit-snippet (yas/snippets-at-point 'all-snippets)) (erase-buffer) (setq buffer-undo-list nil) + (setq undo-in-progress nil) (c-mode) (yas/minor-mode 1) (let ((abbrev)) From 17e7a385f73b2871ab6a8f1a4020a14048322f71 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Tue, 21 Jul 2009 10:06:11 +0000 Subject: [PATCH 62/75] * more little bugs with field deletions and adjancencies fixed... * also the menu problem is fixed now --- yasnippet.el | 132 ++++++++++++++++++++++++--------------------------- 1 file changed, 63 insertions(+), 69 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 672dcc7..66021f2 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -1,9 +1,9 @@ -;;; yasnippet.el --- Yet another snippet extension for Emacs. +;;; Yasnippet.el --- Yet another snippet extension for Emacs. ;; Copyright 2008 pluskid ;; Authors: pluskid , joaotavora -;; Version: 0.6.0 +;; Version: 0.6.0 beta ;; X-URL: http://code.google.com/p/yasnippet/ ;; Keywords: snippet, textmate ;; URL: http://code.google.com/p/yasnippet/ @@ -340,7 +340,7 @@ Here's an example: ;; Internal variables ;; -(defvar yas/version "0.5.6-nested-placeholders") +(defvar yas/version "0.6.0-beta") (defvar yas/snippet-tables (make-hash-table) "A hash table of snippet tables corresponding to each major-mode.") @@ -386,37 +386,58 @@ snippet templates") id)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; YASnippet minor mode +;; Minor mode stuff ;; (defvar yas/minor-mode-map (make-sparse-keymap) "The keymap used when `yas/minor-mode' is active.") -(defvar yas/minor-mode-menu (make-sparse-keymap) - "The menu bar menu used when `yas/minor-mode' is active.") +(easy-menu-define yas/minor-mode-menu + yas/minor-mode-map + "Menu used when YAS/minor-mode is active." + (cons "YASnippet" + (mapcar #'(lambda (ent) + (when (third ent) + (define-key yas/minor-mode-map (third ent) (second ent))) + (vector (first ent) (second ent) t)) + (list (list "--") + (list "Expand trigger" 'yas/expand (read-kbd-macro yas/trigger-key)) + (list "Insert at point" 'yas/insert-snippet "\C-c&\C-s") + (list "About" 'yas/about) + (list "Reload-all-snippets" 'yas/reload-all) + (list "Load snippets..." 'yas/load-directory))))) -;; -;; This bit of code inspired from hideshow.el -;; -(defun yas/init-keymap-and-menu () - (setq yas/minor-mode-map (make-sparse-keymap)) - (setq yas/minor-mode-menu nil) - - (easy-menu-define yas/minor-mode-menu - yas/minor-mode-map - "Menu used when YAS/minor-mode is active." - (cons "YASnippet" - (mapcar #'(lambda (ent) - (when (third ent) - (define-key yas/minor-mode-map (third ent) (second ent))) - (vector (first ent) (second ent) t)) - (list (list "--") - (list "Expand trigger" 'yas/expand (read-kbd-macro yas/trigger-key)) - (list "Insert at point" 'yas/insert-snippet "\C-c&\C-s") - (list "About" 'yas/about) - (list "Reload-all-snippets" 'yas/reload-all) - (list "Load snippets..." 'yas/load-directory))))) - (define-key snippet-mode-map "\C-c\C-c" 'yas/load-snippet-buffer)) +(define-minor-mode yas/minor-mode + "Toggle YASnippet mode. + +When YASnippet mode is enabled, the `tas/trigger-key' key expands +snippets of code depending on the mode. + +With no argument, this command toggles the mode. +positive prefix argument turns on the mode. +Negative prefix argument turns off the mode. + +You can customize the key through `yas/trigger-key'. + +Key bindings: +\\{yas/minor-mode-map}" + nil + ;; The indicator for the mode line. + " yas" + :group 'yasnippet) + +(defun yas/minor-mode-on () + "Turn on YASnippet minor mode." + (interactive) + (yas/minor-mode 1)) + +(defun yas/minor-mode-off () + "Turn off YASnippet minor mode." + (interactive) + (yas/minor-mode -1)) + +(define-globalized-minor-mode yas/global-mode yas/minor-mode yas/minor-mode-on + :group 'yasnippet) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Major mode stuff @@ -439,6 +460,8 @@ snippet templates") (0 font-lock-keyword-face))))) (defvar snippet-mode-map (make-sparse-keymap)) +(define-key snippet-mode-map "\C-c\C-c" 'yas/load-snippet-buffer) + (define-derived-mode snippet-mode text-mode "YASnippet" "A mode for editing yasnippets" @@ -446,42 +469,7 @@ snippet templates") (set (make-local-variable 'require-final-newline) nil) (use-local-map snippet-mode-map)) -(define-minor-mode yas/minor-mode - "Toggle YASnippet mode. -When YASnippet mode is enabled, the `tas/trigger-key' key expands -snippets of code depending on the mode. - -With no argument, this command toggles the mode. -positive prefix argument turns on the mode. -Negative prefix argument turns off the mode. - -You can customize the key through `yas/trigger-key'. - -Key bindings: -\\{yas/minor-mode-map}" - ;; The initial value. - :keymap yas/minor-mode-map - ;; The indicator for the mode line. - " yas" - :group 'yasnippet - (unless (and yas/minor-mode-map - (second yas/minor-mode-map)) - (yas/init-keymap-and-menu)) - (easy-menu-add yas/minor-mode-menu)) - -(defun yas/minor-mode-on () - "Turn on YASnippet minor mode." - (interactive) - (yas/minor-mode 1)) - -(defun yas/minor-mode-off () - "Turn off YASnippet minor mode." - (interactive) - (yas/minor-mode -1)) - -(define-globalized-minor-mode yas/global-mode yas/minor-mode yas/minor-mode-on - :group 'yasnippet) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Internal structs for template management @@ -1466,9 +1454,12 @@ delegate to `yas/next-field'." 1)) (snippet (first (yas/snippets-at-point))) (active-field (overlay-get yas/active-field-overlay 'yas/field)) - (live-fields (remove-if #'yas/field-probably-deleted-p (yas/snippet-fields snippet))) + (live-fields (remove-if #'(lambda (field) + (and (not (eq field active-field)) + (yas/field-probably-deleted-p field))) + (yas/snippet-fields snippet))) (active-field-pos (position active-field live-fields)) - (target-pos (+ arg active-field-pos)) + (target-pos (and active-field-pos (+ arg active-field-pos))) (target-field (nth target-pos live-fields))) ;; First check if we're moving out of a field with a transform ;; @@ -1737,7 +1728,9 @@ This is needed since markers don't \"rear-advance\" like overlays" (let ((adjacents (yas/field-back-adjacent-fields field))) (when adjacents (dolist (adjacent adjacents) - (set-marker (yas/field-start adjacent) end))))) + (when (< (yas/field-start adjacent) end) + (set-marker (yas/field-start adjacent) end)) + (yas/advance-field-and-parents-maybe adjacent end))))) (defun yas/make-move-active-field-overlay (snippet field) "Place the active field overlay in SNIPPET's FIELD. @@ -2299,8 +2292,9 @@ When multiple expressions are found, only the last one counts." (princ (format "%s live snippets at point:\n\n" (length (yas/snippets-at-point)))) (dolist (snippet (yas/snippets-at-point)) - (princ (format "\tid: %s and active field from %s to %s covering \"%s\"\n" + (princ (format "\tid: %s active field %d from %s to %s covering \"%s\"\n" (yas/snippet-id snippet) + (yas/field-number (yas/snippet-active-field snippet)) (marker-position (yas/field-start (yas/snippet-active-field snippet))) (marker-position (yas/field-end (yas/snippet-active-field snippet))) (buffer-substring-no-properties (yas/field-start (yas/snippet-active-field snippet)) (yas/field-end (yas/snippet-active-field snippet))))) @@ -2343,13 +2337,13 @@ When multiple expressions are found, only the last one counts." (erase-buffer) (setq buffer-undo-list nil) (setq undo-in-progress nil) - (c-mode) + (snippet-mode) (yas/minor-mode 1) (let ((abbrev)) ;; (if (require 'ido nil t) ;; (setq abbrev (ido-completing-read "Snippet abbrev: " '("crazy" "prip" "prop"))) ;; (setq abbrev "prop")) - (setq abbrev "bosta") + (setq abbrev "$f") (insert abbrev)) (unless quiet (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local))) From 4849ac6546776c48a0ca5de0d30efd273a83b124 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Tue, 21 Jul 2009 12:31:23 +0000 Subject: [PATCH 63/75] * Finally, the last details, I think, commenting, etc... --- yasnippet.el | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 66021f2..fe50fe2 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -929,9 +929,9 @@ all the parameters: (insert ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n") (insert "(defun yas/initialize-bundle ()\n" " \"Initialize YASnippet and load snippets in the bundle.\"" - " (yas/initialize)\n") + " (yas/global-mode 1)\n") (flet ((yas/define-snippets - (mode snippets &optional parent) + (mode snippets &optional parent directory) (with-current-buffer bundle-buffer (insert ";;; snippets for " (symbol-name mode) "\n") (insert "(yas/define-snippets '" (symbol-name mode) "\n") @@ -958,6 +958,9 @@ all the parameters: (insert (if parent (concat "'" (symbol-name parent)) "nil") + ;; (if directory + ;; (concat "\"" directory "\"") + ;; "nil") ")\n\n")))) (dolist (dir dirs) (dolist (subdir (yas/subdirs dir)) @@ -1356,7 +1359,8 @@ Otherwise throw exception." active-field ;; stacked expansion: the `previous-active-field' slot saves the ;; active field where the child expansion took place - previous-active-field) + previous-active-field + force-exit) (defstruct (yas/field (:constructor yas/make-field (number start end parent-field))) "A field." @@ -1501,9 +1505,9 @@ Also create some protection overlays" (yas/next-field -1)) (defun yas/exit-snippet (snippet) - "Goto exit-marker of SNIPPET and commit the snippet. Cleaning -up the snippet does not delete it!" + "Goto exit-marker of SNIPPET." (interactive) + (setf (yas/snippet-force-exit snippet) t) (goto-char (if (yas/snippet-exit snippet) (yas/snippet-exit snippet) (overlay-end (yas/snippet-control-overlay snippet))))) @@ -1622,7 +1626,9 @@ snippet, if so cleans up the whole snippet up." (snippets-left snippets)) (dolist (snippet snippets) (let ((active-field (yas/snippet-active-field snippet))) - (cond ((not (and active-field (yas/field-contains-point-p active-field))) + (cond ((or (prog1 (yas/snippet-force-exit snippet) + (setf (yas/snippet-force-exit snippet) nil)) + (not (and active-field (yas/field-contains-point-p active-field)))) (setq snippets-left (delete snippet snippets-left)) (yas/commit-snippet snippet snippets-left)) ((and active-field @@ -2168,7 +2174,7 @@ When multiple expressions are found, only the last one counts." (string-to-number (match-string-no-properties 1)))) (brand-new-field (and real-match-end-0 (not (save-match-data - (eq (string-match "$[ \t\n]+(" (match-string-no-properties 2)) 0))) + (eq (string-match "$[ \t\n]*(" (match-string-no-properties 2)) 0))) (not (and number (zerop number))) (yas/make-field number (yas/make-marker (match-beginning 2)) From 408413a81122e89061946e3a03acecb854c52c49 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Tue, 21 Jul 2009 13:03:13 +0000 Subject: [PATCH 64/75] * all prompt fuctions working (tried a bit in text emacs) * compile-bundle now includes dropdown-list.el and works (in text mode at least) --- yasnippet.el | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index fe50fe2..192483a 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -804,11 +804,12 @@ TEMPLATES is a list of `yas/template'." keymap)))))) (defun yas/ido-prompt (prompt choices &optional display-fn) - (when (featurep 'ido) + (when (and (featurep 'ido) + ido-mode) (let* ((formatted-choices (or (and display-fn (mapcar display-fn choices)) choices)) - (chosen (and choices + (chosen (and formatted-choices (ido-completing-read prompt formatted-choices nil @@ -820,13 +821,30 @@ TEMPLATES is a list of `yas/template'." (defun yas/dropdown-prompt (prompt choices &optional display-fn) (when (featurep 'dropdown-list) - )) + (let* ((formatted-choices (or (and display-fn + (mapcar display-fn choices)) + choices)) + (chosen (and formatted-choices + (nth (dropdown-list formatted-choices) + choices)))) + chosen))) (defun yas/completing-prompt (prompt choices &optional display-fn) - ) + (let* ((formatted-choices (or (and display-fn + (mapcar display-fn choices)) + choices)) + (chosen (and formatted-choices + (completing-read prompt + formatted-choices + nil + 'require-match + nil + nil)))) + (when chosen + (nth (position chosen formatted-choices) choices)))) (defun yas/no-prompt (prompt choices &optional display-fn) - ) + (first choices)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Loading snippets from files @@ -892,7 +910,7 @@ foo\"bar\\! -> \"foo\\\"bar\\\\!\"" "\"")) (defun yas/compile-bundle - (&optional yasnippet yasnippet-bundle snippet-roots code) + (&optional yasnippet yasnippet-bundle snippet-roots code dropdown) "Compile snippets in SNIPPET-ROOTS to a single bundle file. SNIPPET-ROOTS is a list of root directories that contains the snippets definition. YASNIPPET is the yasnippet.el file path. YASNIPPET-BUNDLE @@ -903,7 +921,12 @@ all the parameters: (yas/compile-bundle \"yasnippet.el\" \"./yasnippet-bundle.el\" '(\"snippets\") - \"(yas/initialize)\")" + \"(yas/initialize)\") + +Last optional argument DROPDOWN is the filename of the +dropdown-list.el library... + +" (when (null yasnippet) (setq yasnippet "yasnippet.el")) (when (null yasnippet-bundle) @@ -923,6 +946,8 @@ all the parameters: (insert ";;; yasnippet-bundle.el --- " "Yet another snippet extension (Auto compiled bundle)\n") (insert-file-contents yasnippet) + (when dropdown + (insert-file-contents dropdown)) (goto-char (point-max)) (insert ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n") (insert ";;;; Auto-generated code ;;;;\n") @@ -2336,7 +2361,10 @@ When multiple expressions are found, only the last one counts." (defun yas/debug-test (&optional quiet) (interactive "P") - (yas/load-directory "~/Source/yasnippet/snippets/") + (yas/load-directory (or (and (listp yas/root-directory) + (first yas/root-directory)) + yas/root-directory + "~/Source/yasnippet/snippets/")) ;;(kill-buffer (get-buffer "*YAS TEST*")) (set-buffer (switch-to-buffer "*YAS TEST*")) (mapcar #'yas/commit-snippet (yas/snippets-at-point 'all-snippets)) From 519c3514d2d06247245140abcf8fed24d81cbc0c Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Wed, 22 Jul 2009 10:38:44 +0000 Subject: [PATCH 65/75] reverse-merged the python-mode snippet directory, will forward merge the trunk again --- snippets/text-mode/python-mode/class | 32 ++++++++++++---------------- snippets/text-mode/python-mode/def | 25 +++++++++++++++++++++- snippets/text-mode/python-mode/prop | 15 ------------- 3 files changed, 38 insertions(+), 34 deletions(-) delete mode 100644 snippets/text-mode/python-mode/prop diff --git a/snippets/text-mode/python-mode/class b/snippets/text-mode/python-mode/class index 9617c07..72f339b 100644 --- a/snippets/text-mode/python-mode/class +++ b/snippets/text-mode/python-mode/class @@ -36,23 +36,19 @@ class ${1:ClassName}(${2:object}): indent))) } ${4:$ - (let* ((indent (concat "\n" (make-string (current-column) 32))) - (self-vars (mapconcat - '(lambda (x) - (if (not (string= (nth 0 x) "")) - (concat "self._" (nth 0 x) " = " (nth 0 x)))) - (mapcar - '(lambda (x) - (mapcar - '(lambda (x) - (replace-regexp-in-string "[[:blank:]]*$" "" - (replace-regexp-in-string "^[[:blank:]]*" "" x))) - x)) - (mapcar '(lambda (x) (split-string x "=")) - (split-string text ","))) - (concat indent)))) - (if (string= self-vars "") - indent - self-vars)) + (mapconcat + '(lambda (x) + (if (not (string= (nth 0 x) "")) + (concat "self._" (nth 0 x) " = " (nth 0 x)))) + (mapcar + '(lambda (x) + (mapcar + '(lambda (x) + (replace-regexp-in-string "[[:blank:]]*$" "" + (replace-regexp-in-string "^[[:blank:]]*" "" x))) + x)) + (mapcar '(lambda (x) (split-string x "=")) + (split-string text ","))) + (concat "\n" (make-string (current-column) 32))) } $0 diff --git a/snippets/text-mode/python-mode/def b/snippets/text-mode/python-mode/def index 6898dd6..1ea3aa5 100644 --- a/snippets/text-mode/python-mode/def +++ b/snippets/text-mode/python-mode/def @@ -6,6 +6,29 @@ def ${1:name}($2): """$3 ${2:$ - (yas/python-rebuscate) + (let* ((indent + (concat "\n" (make-string (current-column) 32))) + (args + (mapconcat + '(lambda (x) + (if (not (string= (nth 0 x) "")) + (concat "- " (char-to-string 96) (nth 0 x) + (char-to-string 96) ":"))) + (mapcar + '(lambda (x) + (mapcar + '(lambda (x) + (replace-regexp-in-string "[[:blank:]]*$" "" + (replace-regexp-in-string "^[[:blank:]]*" "" x))) + x)) + (mapcar '(lambda (x) (split-string x "=")) + (split-string text ","))) + indent))) + (if (string= args "") + (make-string 3 34) + (mapconcat + 'identity + (list "" "Arguments:" args (make-string 3 34)) + indent))) } $0 diff --git a/snippets/text-mode/python-mode/prop b/snippets/text-mode/python-mode/prop deleted file mode 100644 index 107730a..0000000 --- a/snippets/text-mode/python-mode/prop +++ /dev/null @@ -1,15 +0,0 @@ -# contributor: Mads D. Kristensen -# name: prop -# -- -def ${1:foo}(): - doc = """${2:Doc string}""" - def fget(self): - return self._$1 - def fset(self, value): - self._$1 = value - def fdel(self): - del self._$1 - return locals() -$1 = property(**$1()) - -$0 From 8a48811cec92fda72c536c8972ed475a5ea177ed Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Wed, 22 Jul 2009 10:41:07 +0000 Subject: [PATCH 66/75] couldn't forward-merge again the python-mode snippet directory, so just added the prop snippet again manually --- snippets/text-mode/python-mode/prop | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 snippets/text-mode/python-mode/prop diff --git a/snippets/text-mode/python-mode/prop b/snippets/text-mode/python-mode/prop new file mode 100644 index 0000000..107730a --- /dev/null +++ b/snippets/text-mode/python-mode/prop @@ -0,0 +1,15 @@ +# contributor: Mads D. Kristensen +# name: prop +# -- +def ${1:foo}(): + doc = """${2:Doc string}""" + def fget(self): + return self._$1 + def fset(self, value): + self._$1 = value + def fdel(self): + del self._$1 + return locals() +$1 = property(**$1()) + +$0 From 111aa25382a39da7c4e352d38a37b964a00fc253 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Wed, 22 Jul 2009 16:25:12 +0000 Subject: [PATCH 67/75] taking care of some details --- Rakefile | 4 +- doc/changelog.html | 295 ---------- doc/define_snippet.html | 760 ------------------------- doc/faq.html | 107 ---- doc/index.html | 137 ----- dropdown-list.el | 251 ++++++++ snippets/text-mode/email | 2 +- snippets/text-mode/objc-mode/crazy | 4 - snippets/text-mode/objc-mode/prop | 14 - snippets/text-mode/snippet-mode/field | 5 + snippets/text-mode/snippet-mode/mirror | 6 + snippets/text-mode/snippet-mode/vars | 9 + yasnippet.el | 174 ++++-- 13 files changed, 400 insertions(+), 1368 deletions(-) delete mode 100644 doc/changelog.html delete mode 100644 doc/define_snippet.html delete mode 100644 doc/faq.html delete mode 100644 doc/index.html create mode 100644 dropdown-list.el delete mode 100644 snippets/text-mode/objc-mode/crazy delete mode 100644 snippets/text-mode/objc-mode/prop create mode 100644 snippets/text-mode/snippet-mode/field create mode 100644 snippets/text-mode/snippet-mode/mirror create mode 100644 snippets/text-mode/snippet-mode/vars diff --git a/Rakefile b/Rakefile index 54015a8..7d22c43 100644 --- a/Rakefile +++ b/Rakefile @@ -3,7 +3,7 @@ require 'fileutils' def find_version - File.read("yasnippet.el") =~ /;; Version: *([0-9.]+) *$/ + File.read("yasnippet.el") =~ /;; Version: *([0-9.]+[a-z]?) *$/ $version = $1 end find_version @@ -19,7 +19,7 @@ desc "create a release package" task :package do release_dir = "pkg/yasnippet-#{$version}" FileUtils.mkdir_p(release_dir) - files = ['snippets', 'yasnippet.el'] + files = ['snippets', 'yasnippet.el', 'dropdown-list.el'] FileUtils.cp_r files, release_dir FileUtils.rm_r Dir[release_dir + "/**/.svn"] FileUtils.cd 'pkg' diff --git a/doc/changelog.html b/doc/changelog.html deleted file mode 100644 index ab1b98b..0000000 --- a/doc/changelog.html +++ /dev/null @@ -1,295 +0,0 @@ - - - - - - -ChangeLog - - - - - -
-
-
-
- -
-
-
-
-
-

0.5.10 / 2009-02-11

-
    -
  • Added grouping support so that the snippets in the menu can be -groupped together.
  • -
  • Make the bundle ELPA -compatible.
  • -
-
-
-

0.5.9 / 2009-01-21

-
    -
  • Fixed the bug of disabling the auto-indenting of cc-mode.
  • -
-
-
-

0.5.8 / 2009-01-15

-
    -
  • Added a key property in snippet definition for snippet names -that are not valid path name.
  • -
  • Fixed some bugs of indenting (Issue 44, Issue -46).
  • -
  • Fixed Issue 45 by -providing a proper default value for yas/buffer-local-condition.
  • -
  • Added helper function yas/substr for convenient mirror -transformation.
  • -
  • Make variable yas/registered-snippet properly initialized.
  • -
  • Fixed the overlay error when overlay becomes empty (Issue 49 and -Issue 48). This -bug has occurred and been fixed earlier, and should not have -happened if we have proper regression test.
  • -
  • Added a workaround for c-electric- serial commands (Issue 27).
  • -
-
-
-

0.5.7 / 2008-12-03

-
    -
  • Fixed Issue 28 of -properly clean up snippet (by joaotavora).
  • -
  • Added a new section "Field-level undo functionality" to correct -Issue 33 -(by joaotavora).
  • -
  • Added some snippets from users for sql, erlang, scala, html, xml, latex, etc.
  • -
  • Fixed Issue 16 by adding -$> support. Here's the doc for $> indenting.
  • -
-
-
-

0.5.6 / 2008-08-07

-
    -
  • Added a buffer local variable yas/dont-activate to turn off -yas/minor-mode in some major modes. See Issue 29.
  • -
  • Make the environment of elisp evaluation more friendly to -(current-column).
  • -
  • Fixed the regular expression bug in python-mode snippets.
  • -
  • Use filename or full key extension for snippet name if no name -property is defined.
  • -
-
-
-

0.5.5 / 2008-05-29

-
    -
  • Tweak yas/extra-mode-hooks so that it can be more easily -customized.
  • -
  • Add an entry in FAQ about why TAB key doesn't work in some -modes.
  • -
-
-
-

0.5.4 / 2008-05-15

-
    -
  • Added ox-mode-hook and python-mode-hook to -yas/extra-mode-hooks to fix the problem YASnippet is not enabled -in those modes.
  • -
-
-
-

0.5.3 / 2008-05-07

-
    -
  • Fix indent of python-mode snippets.
  • -
  • Fix a bug of dropdown-list: conflicts with color-theme (Issue 23). Thanks -Mike.
  • -
  • Fix a bug of condition system.
  • -
-
-
-

0.5.2 / 2008-04-20

-
    -
  • Fix a bug for comparing string to symbol using string= (which -will fire an error).
  • -
-
-
-

0.5.1 / 2008-04-14

-
    -
  • Use a beautiful css style in the document.
  • -
-
-
-

0.5.0 / 2008-04-10

-
    -
  • Integrate with hippie-expand. Just add yas/hippie-try-expand to -hippie-expand-try-functions-list.
  • -
  • If you set yas/fall-back-behavior to 'return-nil, YASnippet -will return nil when it can't find a snippet to expand.
  • -
  • Defect fix: the condition of a snippet was evaluated twice in -earlier version.
  • -
  • Deleting snippet (using C-w or C-k) won't cause serious -problem now.
  • -
  • Several complex snippet for python-mode from Yasser included in the -distribution.
  • -
-
-
-

0.4.5 / 2008-04-07

-
    -
  • Merge the latest dropdown-list.el.
  • -
  • Add snippets for f90-mode from Li Zhu.
  • -
  • Bug fix: l-safe-expr-p: Lisp nesting exceeds max-lisp-eval-depth -error when several (more than two) snippets overlaps. Thanks -sunwaybupt@newsmth for reporting this bug.
  • -
-
-
-

0.4.4 / 2008-03-24

-
    -
  • Bug fix: dropdown-list.el doesn't recognize [return] properly.
  • -
-
-
-

0.4.3 / 2008-03-23

-
    -
  • Bug fix: failed to recognize user customized yas/trigger-key.
  • -
-
-
-

0.4.2 / 2008-03-22

-
    -
  • Make a separate document package for release. Also make document -available online.
  • -
-
-
-

0.4.1 / 2008-03-21

-
    -
  • Make sure yas/minor-mode's key bindings always take priority to -other minor modes.
  • -
-
-
-

0.4.0 / 2008-03-20

-
    -
  • Document refinement and released with YASnippet. Most of the Online -wiki document will be deprecated soon.
  • -
  • Powerful condition system added to yasnippet!
  • -
  • Incorporate dropdown-list.el and make it default way for -selecting multiple candidates. Thanks to Jaeyoun Chung.
  • -
  • yas/before-expand-snippet-hook
  • -
-
-
-

0.3.2 / 2008-03-19

-
    -
  • Enhancement: A better way to define minor-mode. Thanks to Kentaro -Kuribayashi. See this thread -for more details.
  • -
-
-
-

0.3.1 / 2008-03-17

-
    -
  • Bug fix: Emacs get confused when a field is deleted. See issue 10.
  • -
-
-
-

0.3.0 / 2008-03-16

-
    -
  • Add a yas/after-exit-snippet-hook so that you can do something like -indent-region or fill-region after finish the snippet.
  • -
  • Use minor-mode instead of global-set-key to bind the trigger -key. Now the trigger key and fall-back behavior can be more -flexible. Not constrained to <tab>. Thanks to Trey Jackson. See -this thread -for more details.
  • -
  • Now user can customize the popup function for selecting multiple -candidate for the same snippet key.
  • -
  • Support dropdown-list.el to be a better way to select multiple -candidate when in text mode.
  • -
-
-
-

0.2.3 / 2008-03-15

-
    -
  • Bug in non-window (-nw) mode when there's multiple candidate to -expand. See issue 7.
  • -
  • Allow expanding another snippet as long as not currently inside a -field.
  • -
-
-
-

0.2.2 / 2008-03-13

-
    -
  • Added customized face for fields and mirrors. Better in dark -background. And users can customize it.
  • -
-
-
-

0.2.1 / 2008-03-10

-
    -
  • Fix the insert-behind problem under both Emacs 22 and Emacs 23.
  • -
-
-
-

0.2.0 / 2008-03-10

-
    -
  • Use big keymap overlay to detect insert-behind event manually to -avoid sometimes missed hook calls. See issue 3 for more -details.
  • -
  • Support parent snippet table. Now you can set (for example) -cc-mode as common mode for c++-mode, c-mode and -java-mode. They'll share snippets defined for cc-mode.
  • -
-
-
-

0.1.1 / 2008-03-08

-
    -
  • Add a rake task to upload to google code.
  • -
  • Use elisp compile-bundle function instead of python scrip
  • -
-
-
-

0.1.0 / 2008-03-07

-
    -
  • Embedded elisp support.
  • -
  • Fields navigation support.
  • -
  • Mirror of fields support.
  • -
  • Menu-bar support.
  • -
  • Multiple snippets with same name support.
  • -
  • Popup menu for multiple snippet with same name support.
  • -
  • Transformation of fields support.
  • -
  • Load directory support.
  • -
  • Compile bundle support.
  • -
-
-
-
-
-
-
-
-
- - diff --git a/doc/define_snippet.html b/doc/define_snippet.html deleted file mode 100644 index 64db46b..0000000 --- a/doc/define_snippet.html +++ /dev/null @@ -1,760 +0,0 @@ - - - - - - -How to define a snippet ? - - - - - -
-
-
-
- -
-
-
-
- -

The most convenient way to define snippets for YASnippet is to put -them in a directory arranged by the mode and use -yas/load-directory to load them.

-

However, this might slow down the Emacs startup speed if you have many -snippets. You can use yas/define-snippets to define a bunch of -snippets for a perticular mode. But this is hard to maintain! So, -there's a better way: define your snippets in directory and use -yas/compile-bundle to compile it into a bundle file when you -modified your snippets.

-

The release bundle of YASnippet is produced by -yas/compile-bundle. The bundle use yas/define-snippets to -define snippets. This avoid the IO and parsing overhead when loading -snippets.

-

Finally, you can use yas/define to define a single snippet at your -convenience. I ofthen use this to do some testing.

-
-

Define snippets in files

-
-

Directory hierarchy

-

Here's the directory hierarchy of the snippets directory comes -with YASnippet:

-
snippets
-`-- text-mode/
-    |-- cc-mode/
-    |   |-- c++-mode/
-    |   |   |-- beginend
-    |   |   |-- class
-    |   |   `-- using
-    |   |-- c-mode/
-    |   |   `-- fopen
-    |   |-- do
-    |   |-- for
-    |   |-- if
-    |   |-- inc
-    |   |-- inc.1
-    |   |-- main
-    |   |-- once
-    |   `-- struct
-    |-- css-mode/
-    |   |-- background
-    |   |-- background.1
-    |   `-- border
-    |-- email
-    |-- html-mode/
-    |   |-- div
-    |   |-- doctype
-    |   |-- doctype.xhml1
-    |   |-- doctype.xhtml1_1
-    |   |-- doctype.xhtml1_strict
-    |   `-- doctype.xhtml1_transitional
-    |-- objc-mode/
-    |   `-- prop
-    |-- perl-mode/
-    |   |-- cperl-mode/
-    |   |-- eval
-    |   |-- for
-    |   |-- fore
-    |   |-- if
-    |   |-- ife
-    |   |-- ifee
-    |   |-- sub
-    |   |-- unless
-    |   |-- while
-    |   |-- xfore
-    |   |-- xif
-    |   |-- xunless
-    |   `-- xwhile
-    |-- python-mode/
-    |   |-- __
-    |   |-- class
-    |   |-- def
-    |   |-- for
-    |   |-- ifmain
-    |   `-- while
-    |-- rst-mode/
-    |   |-- chapter
-    |   |-- section
-    |   `-- title
-    |-- ruby-mode/
-    |   |-- #
-    |   |-- =b
-    |   |-- Comp
-    |   |-- all
-    |   |-- am
-    |   |-- any
-    |   |-- app
-    |   |-- bm
-    |   |-- case
-    |   |-- cla
-    |   |-- classify
-    |   |-- cls
-    |   |-- collect
-    |   |-- dee
-    |   |-- deli
-    |   |-- det
-    |   |-- ea
-    |   |-- eac
-    |   |-- eai
-    |   |-- eav
-    |   |-- eawi
-    |   |-- forin
-    |   |-- if
-    |   |-- ife
-    |   |-- inject
-    |   |-- mm
-    |   |-- r
-    |   |-- rb
-    |   |-- reject
-    |   |-- req
-    |   |-- rreq
-    |   |-- rw
-    |   |-- select
-    |   |-- w
-    |   |-- y
-    |   `-- zip
-    `-- time
-
-

Snippet definitions are put in plain text files. They are arranged by -subdirectories. For example, snippets for c-mode are put in the -c-mode directory.

-

The parent directory acts as the parent mode. This is the way of -YASnippet to share snippet definitions among different modes. As you -can see above, c-mode and c++-mode share the same parents -cc-mode, while all modes are derived from text-mode. This can -be also used to as an alias -- cperl-mode is an empty directory -whose parent is perl-mode.

-

File names act as the snippet trigger key. Note files starting with a -dot (.) are ignored.

-
-
-

File content

-

A file defining a snippet may just contain the template for the -snippet. Optionally it can also contains some meta data for the -snippet as well as comments.

-

Generally speaking, if the file contains a line of # --, then all -contents above that line are considered as meta data and comments; -below are template. Or else the whole file content is considered as -the template.

-

Here's a typical example:

-
#contributor : pluskid <pluskid@gmail.com>
-#name : __...__
-# --
-__${init}__
-
-

Meta data are specified in the syntax of

-
#data-name : data value
-
-

Any other text above # -- is considered as comment and -ignored. Here's a list of currently supported meta data:

-images/group.png -
    -
  • name: The name of the snippet. This is a one-line description of -the snippet. It will be displayed in the menu. So it's a good idea -to select a descriptive name fo a snippet -- especially -distinguishable among similar snippets.
  • -
  • contributor: The contributor of the snippet.
  • -
  • condition: The condition of the snippet. This is a piece of -elisp code. If a snippet has a condition, then it will only be -expanded when the condition code evaluate to some non-nil value.
  • -
  • key: The key to expand the snippet. Sometimes the key of a -snippet is non-ASCII or not valid filename (e.g. contains -/). One can then define the key property which will -overwrite the filename as the key to expand the snippet.
  • -
  • group: The snippets for a mode can be grouped. Grouped snippets -will be grouped in sub-menu. This is useful if one has too many -snippets for a mode which will make the menu too long. group -property only affect menu construction (See The Menu). Refer to -the snippets for ruby-mode for examples. Group can also be -nested, e.g. control structure.loops tells that the snippet is -under the loops group which is under the control structure -group.
  • -
-
-
-

Define snippets using elisp code

-

As I mentioned above, you can define snippets directly by writing -elisp code.

-
-

yas/define-snippets

-

The basic syntax of yas/define-snippets is

-
(yas/define-snippets MODE SNIPPETS &optional PARENT)
-
-

The parameters are self-descriptive. If you specify a PARENT, then -the snippets of the parents may be shared by MODE. Note if you use -this function several times, the later specified PARENT will -overwrite the original one. However, not specifying a PARENT won't -erase the original parent.

-

The SNIPPETS parameter is a list of snippet definitions. Each -element should have the following form:

-
(KEY TEMPLATE NAME CONDITION GROUP)
-
-

The NAME, CONDITION and GROUP can be omitted if you don't -want to provide one. Here's an example:

-
(yas/define-snippets 'c++-mode
-'(
-  ("using" "using namespace ${std};
-$0" "using namespace ... " nil)
-  ("class" "class ${1:Name}
-{
-public:
-    $1($2);
-    virtual ~$1();
-};" "class ... { ... }" nil)
-  ("beginend" "${1:v}.begin(), $1.end" "v.begin(), v.end()" nil)
-  )
-'cc-mode)
-
-

The example above is auto-generated code by yas/compile-bundle.

-
-
-

yas/compile-bundle

-

yas/compile-bundle can be used to parse the snippets from a -directory hierarchy and translate them into the elisp form. The -translated code is faster to load. Further more, the generated bundle -is a stand-alone file not depending on yasnippet.el. The released -bundles of YASnippet are all generated this way.

-

The basic syntax of yas/compile-bundle is

-
(yas/compile-bundle &optional yasnippet yasnippet-bundle snippet-roots code)
-
-

As you can see, all the parameters are optional. The default values -for those parameters are convenient for me to produce the default -release bundle:

-
(yas/compile-bundle "yasnippet.el"
-                    "./yasnippet-bundle.el"
-                    '("snippets")
-                    "(yas/initialize)")
-
-

The snippet-roots can be a list of root directories. This is -useful when you have multiple snippet directories (maybe from other -users). The code parameter can be used to specify your own -customization code instead of the default (yas/initialize). For -example, you can set yas/trigger-key to (kbd "SPC") here if -you like.

-
-
-

yas/define

-

The basic syntax for yas/define is

-
(yas/define mode key template &optional name condition group)
-
-

This is only a syntax sugar for

-
(yas/define-snippets mode
-                     (list (list key template name condition group)))
-
-
-
-
-
-

The strategy to select a snippet

-

When user press the yas/trigger-key, YASnippet try to find a -proper snippet to expand. The strategy to find such a snippet is -explained here.

-
-

Finding the key

-

YASnippet search from current point backward trying to find the -snippet to be expanded. The default searching strategy is quite -powerful. For example, in c-mode, "bar", "foo_bar", -"#foo_bar" can all be recognized as a template key. Further more, -the searching is in that order. In other words, if "bar" is found -to be a key to some valid snippet, then "foo_bar" and -"#foobar" won't be searched.

-

However, this strategy can also be customized easily from the -yas/key-syntaxes variable. It is a list of syntax rules, the -default value is ("w" "w_" "w_." "^ "). Which means search the -following thing until found one:

-
    -
  • a word.
  • -
  • a symbol. In lisp, - and ? can all be part of a symbol.
  • -
  • a sequence of characters of either word, symbol or punctuation.
  • -
  • a sequence of characters of non-whitespace characters.
  • -
-

But you'd better keep the default value unless you understand what -Emacs's syntax rule mean.

-
-
-

The condition system

-

I write forked snippet.el to make the smart-snippet.el. I call it -smart-snippet because a condition can be attached to a snippet. This -is really a good idea. However, writing condition for a snippet -usually needs good elisp and Emacs knowledge, so it is strange to many -user.

-

Later I write YASnippet and persuade people to use it instead of -smart-snippet.el. However, some user still love smart-snippet because -it is smart. So I make YASnippet smart. Even smarter than -smart-snippet.el. :p

-

Consider this scenario: you are an old Emacs hacker. You like the -abbrev-way and set yas/trigger-key to (kbd "SPC"). However, -you don't want if to be expanded as a snippet when you are typing -in a comment block or a string (e.g. in python-mode).

-

It's OK, just specify the condition for if to be (not -(python-in-string/comment)). But how about while, for, -etc. ? Writing the same condition for all the snippets is just -boring. So YASnippet introduce a buffer local variable -yas/buffer-local-condition. You can set this variable to (not -(python-in-string/comment)) in python-mode-hook. There's no way -to do this in smart-snippet.el!

-

Then, what if you really want some snippet even in comment? This is -also possible! But let's stop telling the story and look at the rules:

-
    -
  • If yas/buffer-local-condition evaluate to nil, snippet won't be -expanded.
  • -
  • If it evaluate to the a cons cell where the car is the symbol -require-snippet-condition and the cdr is a symbol (let's -call it requirement):
      -
    • If the snippet has no condition, then it won't be expanded.
    • -
    • If the snippet has a condition but evaluate to nil or error -occured during evaluation, it won't be expanded.
    • -
    • If the snippet has a condition that evaluate to non-nil (let's -call it result):
        -
      • If requirement is t, the snippet is ready to be -expanded.
      • -
      • If requirement is eq to result, the snippet is ready -to be expanded.
      • -
      • Otherwise the snippet won't be expanded.
      • -
      -
    • -
    -
  • -
  • If it evaluate to other non-nil value:
      -
    • If the snippet has no condition, or has a condition that evaluate -to non-nil, it is ready to be expanded.
    • -
    • Otherwise, it won't be expanded.
    • -
    -
  • -
-

So set yas/buffer-local-condition like this

-
(add-hook 'python-mode-hook
-          '(lambda ()
-             (setq yas/buffer-local-condition
-                   '(if (python-in-string/comment)
-                        '(require-snippet-condition . force-in-comment)
-                      t))))
-
-

And specify the condition for a snippet that you're going to expand in -comment to be evaluated to the symbol force-in-comment. Then it -can be expanded as you expected, while other snippets like if -still can't expanded in comment.

-
-
-

Multiple snippet with the same key

-

There can be multiple snippet bind to the same key. If you define a -snippet with a key that is already used, you'll overwrite the original -snippet definition. However, you can add a different postfix to the -key.

-

In general, the extension (consider a file name) is ignored when -defining a snippet. So def, def.1 and def.mine will all be -valid candidates when the key is def.

-

When there are multiple candidates, YASnippet will let you select -one. The UI for selecting multiple candidate can be -customized. There're two variable related:

-
    -
  • yas/window-system-popup-function: the function used when you -have a window system.
  • -
  • yas/text-popup-function: the function used when you don't have a -window system, i.e. when you are working in a terminal.
  • -
-
-Currently there're three solution come with YASnippet.
-images/popup-menu.png - -
-

Just select the first one

-

This one is originally used in terminal mode. It doesn't let you to -choose anything, it just select the first one on behalf of you. So I -bet you never want to use this. :p

-
-
-

Use a dropdown-menu.el

-images/dropdown-menu.png -

Originally, only the above two function is available in -YASnippet. They are difficult to use -- especially in a -terminal. Until later Jaeyoun Chung show me his dropdown-menu.el, -I say wow! It's wonderful!

-
    -
  • It works in both window system and terminal.
  • -
  • It is customizable, you can use C-n, C-p to navigate, q -to quite and even press 6 as a shortcut to select the 6th -candidate.
  • -
-

So I added yas/dropdown-list-popup-for-template to support -dropdown-list.el. And upload dropdown-list.el to YASnippet -hompage for an optional download (since Jaeyoun didn't provide a URL).

-

Then finally, in 0.4.0, I included a copy of the content of -dropdown-list.el [1] in yasnippet.el and made it the default -way for selecting multiple candidates.

-

However, the original functions are still there, you can still use this

-
(setq yas/window-system-popup-function
-      'yas/x-popup-menu-for-template)
-
-

if you prefer a modern UI. :)

-
-
-
-

The Trigger Key

-

YASnippet is implemented as a minor-mode (yas/minor-mode). The -trigger key yas/trigger-key is defined in yas/minor-mode-map -to call yas/expand to try to expand a snippet.

-
-

The Minor Mode

-images/minor-mode-indicator.png -

When yas/minor-mode is enabled, the trigger key will take -effect. The default key is (kbd "TAB"), however, you can freely -set it to some other key. By default, YASnippet add a hook to -after-change-major-mode-hook to enable yas/minor-mode [2] in -every buffer. This works fine for most modes, however, some mode -doesn't follow the Emacs convention and doens't call this hook. You -can either explicitly hook for those mode or just add it to -yas/extra-mode-hooks to let YASnippet do it for you:

-
(require 'yasnippet)
-(add-to-list 'yas/extra-mode-hooks
-             'ruby-mode-hook)
-(yas/initialize)
-
-

Note that should be put after (require 'yasnippet) and before -(yas/initialize). Further more, you may report it to me, I'll add -that to the default value.

-
-
-

The Fallback

-

If yas/expand failed to find any suitable snippet to expand, it -will disable the minor mode temporarily and find if there's any other -command bind the yas/trigger-key. If found, the command will be -called. Usually this works very well -- when there's a snippet, expand -it, otherwise, call whatever command originally bind to the trigger -key.

-

However, you can change this behavior by customizing the -yas/fallback-behavior variable. If you set this variable to -'return-nil, it will return nil instead of trying to call the -original command when no snippet is found. This is useful when you -would like YASnippet to work with other extensions, -e.g. hippie-expand. I'm also glad to tell you that integration -with hippie-expand is already included in YASnippet.

-
-
-

Integration with hippie-expand

-

To integrate with hippie-expand, just put -yas/hippie-try-expand in -hippie-expand-try-functions-list. Personally I would like to put -in front of the list, but it can be put anywhere you prefer.

-
-
-
-

Other way to select a snippet

-

When you use the trigger key (so yas/expand) to expand a snippet, -the key for the snippet is deleted before the template for the snippet -is inserted.

-

However, there're other ways to insert a snippet.

-
-

The Menu

-

YASnippet will setup a menu just after the Buffers Menu in the -menubar. The snippets for all real modes are listed there under the -menu. You can select a snippet from the menu to expand it. Since you -select manually from the menu, you can expand any snippet. For -example, you can expand a snippet defined for python-mode in a -c-mode buffer by selecting it from the menu:

-images/menubar.png -
    -
  • Condition system is ignored since you select to expand it -explicitly.
  • -
  • There will be no muliple candidates since they are listed in the -menu as different items.
  • -
-

This can be convenient sometimes. However, if you don't like the -menubar of Emacs and never use it. You can tell YASnippet don't boring -to build a menu by setting yas/use-menu to nil.

-

Another thing to note is that only real modes are listed under the -menu. As you know, common snippets can be shared by making up a -virtual parent mode. It's too bad if the menu is floored by those -virtual modes. So YASnippet only show menus for those real -modes. But the snippets fo the virtual modes can still be accessed -through the parent submenu of some real mode.

-

YASnippet use a simple way to check whether a mode is real or -virtual: (fboundp mode). For example, the symbol c-mode is -bound to a function while cc-mode is not. But this is not enough, -some modes aren't part of Emacs, and maybe when initializing -YASnippet, those modes haven't been initialized. So YASnippet also -maintain a list of known modes (yas/known-modes). You can add item -to that list if you need.

-
-
-

Expanding From Elisp Code

-

Sometimes you might want to expand a snippet directly by calling a -functin from elisp code. You should call yas/expand-snippet -instead of yas/expand in this case.

-

As with expanding from the menubar, condition system and multiple -candidates won't exists here. In fact, expanding from menubar has the -same effect of evaluating the follow code:

-
(yas/expand-snippet (point) (point) template)
-
-

Where template is the template of a snippet. It is never required -to belong to any snippet -- you can even make up it on the fly. The -1st and 2nd parameter defines the region to be deleted after YASnippet -inserted the template. It is used by yas/expand to indicate the -region of the key. There's usually no need to delete any region when -we are expanding a snippet from elisp code, so passing two (point) -is fine. Note only (point) will be fine because the 1st parameter -also indicate where to insert and expand the template.

-
-
-
-
-

The Syntax of the Template

-

The syntax of the snippet template is simple but powerful, very -similar to TextMate's.

-
-

Plain Text

-

Arbitrary text can be included as the content of a template. They are -usually interpreted as plain text, except $ and `. You need to -use \ to escape them: \$ and \`. The \ itself may also -needed to be escaped as \\ sometimes.

-
-
-

Embedded elisp code

-

Elisp code can be embedded inside the template. They are written -inside back-quotes (`):

-

They are evaluated when the snippet is being expanded. The evaluation -is done in the same buffer as the snippet being expanded. Here's an -example for c-mode to calculate the header file guard dynamically:

-
#ifndef ${1:_`(upcase (file-name-nondirectory (file-name-sans-extension (buffer-file-name))))`_H_}
-#define $1
-
-$0
-
-#endif /* $1 */
-
-
-
-

Tab Stops

-

Tab stops are fields that you can navigate back and forth by TAB -and S-TAB [3]. They are written by $ followed with a -number. $0 has the special meaning of the exit point of a -snippet. That is the last place to go when you've traveled all the -fields. Here's a typical example:

-
<div$1>
-    $0
-</div>
-
-
-
-

Placeholders

-

Tab stops can have default values -- a.k.a placeholders. The syntax is -like this:

-
${N:default value}
-
-

They acts as the default value for a tab stop. But when you firstly -type at a tab stop, the default value will be replaced by your -typing. The number can be omitted if you don't want to create -mirrors or transformations for this field.

-
-
-

Mirrors

-

We refer the tab stops with placeholders as a field. A field can have -mirrors. Its mirrors will get updated when you change the text of a -field. Here's an example:

-
\begin{${1:enumerate}}
-    $0
-\end{$1}
-
-

When you type "document" at ${1:enumerate}, the word -"document" will also be inserted at \end{$1}. The best -explanation is to see the screencast(YouTube or avi video).

-

The tab stops with the same number to the field act as its mirrors. If -none of the tab stops has an initial value, the first one is selected -as the field and others mirrors.

-
-
-

Transformations

-

If the default value of a field starts with $, then it is interpreted -as the transformation code instead of default value. A transformation -is some arbitrary elisp code that will get evaluated in an environment -when the variable text is bind to the inputted text of the -field. Here's an example for Objective-C:

-
- (${1:id})${2:foo}
-{
-    return $2;
-}
-
-- (void)set${2:$(capitalize text)}:($1)aValue
-{
-    [$2 autorelease];
-    $2 = [aValue retain];
-}
-$0
-
-

Look at ${2:$(capitalize text)}, it is a transformation instead of -a placeholder. The actual placeholder is at the first line: -${2:foo}. When you type text in ${2:foo}, the transformation -will be evaluated and the result will be placed there as the -transformated text. So in this example, if you type baz in the field, -the transformed text will be Baz. This example is also available in -the screencast.

-

Another example is for rst-mode. In reStructuredText, the document -title can be some text surrounded by "===" below and above. The "===" -should be at least as long as the text. So

-
=====
-Title
-=====
-
-

is a valid title but

-
===
-Title
-===
-
-

is not. Here's an snippet for rst title:

-
${1:$(make-string (string-width text) ?\=)}
-${1:Title}
-${1:$(make-string (string-width text) ?\=)}
-
-$0
-
- - - - - -
[1]With some minor change, mainly for fixing some trivial bugs.
- - - - - -
[2]This is done when you call yas/initialize.
- - - - - -
[3]Of course, this can be customized.
-
-
-

Indenting

-

Many people miss the indenting feature of smart-snippet: when you -place a $> in your snippet, an (indent-according-to-mode) will -be executed there to indent the line. So you'll not need to hard-code -the indenting in the snippet template, and it will be very convenient -when you need to work with several different project where coding -styles are different.

-

The reason why this feature wasn't added to YASnippet until after -0.5.6 is that it doesn't work well for all modes. In some cases -(e.g. python-mode), calling indent-according-to-mode will break -the overlays created by YASnippet.

-

However, since many people asked for this feature, I finally added -this to YASnippet. Here's an example of the usage:

-
for (${int i = 0}; ${i < 10}; ${++i})
-{$>
-$0$>
-}$>
-
-
-
-
-
-
-
-
-
-
- - diff --git a/doc/faq.html b/doc/faq.html deleted file mode 100644 index f9a40e0..0000000 --- a/doc/faq.html +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - -How to define a snippet ? - - - - - -
-
-
-
- -
-
-
-
-
-

Why there's an extra newline?

-

If you have a newline at the end of the snippet definition file, then -YASnippet will add a newline when you expanding a snippet. Please -don't add a newline at the end if you don't want it when you saving -the snippet file.

-

Note some editors will automatically add a newline for you. In Emacs, -if you set require-final-newline to t, it will add the final -newline for you automatically.

-
-
-

Why TAB key doesn't expand a snippet?

-

First check the mode line to see if there's yas. If no, then try -M-x yas/minor-mode-on to manually turn on yas/minor-mode and -try to expand the snippet again. If it works, then, you can add the -following code to your .emacs before loading YASnippet:

-
(setq yas/extra-mode-hooks '(the-major-mode))
-
-

where the-major-mode is the major mode in which yas/minor-mode -isn't enabled by default.

-

If yas/minor-mode is on but the snippet still not expanded. Then -try to see what command is bound to the TAB key: press C-h k -and then press TAB. Emacs will show you the result.

-

You'll see a buffer prompted by Emacs saying that TAB runs the -command .... Alternatively, you might see <tab> runs the command -..., note the difference between TAB and <tab> where the -latter has priority. If you see <tab> bound to a command other -than yas/expand, (e.g. in org-mode) you can try the following -code to work around:

-
(add-hook 'org-mode-hook
-          '(lambda ()
-             (make-variable-buffer-local 'yas/trigger-key)
-             (setq yas/trigger-key [tab])))
-
-

replace org-mode-hook with the major mode hook you are dealing -with (C-h m to see what major mode you are in).

-

If it says TAB but YASnippet still doesn't work, check your -configuration and you may also ask for help on the discussion group. Don't forget to -attach the information on what command is bound to TAB as well as the -mode information (Can be obtained by C-h m).

-
-
-

How to define snippets with named by characters not supported by the filesystem?

-

For example, you want to define a snippet by the key < which is not a -valid character for filename on Windows. In this case, you may use -yas/define to define the snippet. If you want to enjoy defining -snippets in a file, you can use the key property to specify the key of -the defined snippet explicitly.

-

Just name your snippet with an arbitrary valid filename, lt for -example. and specify < for the key property:

-
#key: <
-#name: <...></...>
-# --
-<${1:div}>$0</$1>
-
-
-
-
-
-
-
-
-
- - diff --git a/doc/index.html b/doc/index.html deleted file mode 100644 index 192028e..0000000 --- a/doc/index.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - -Yet Another Snippet extension - - - - - -
-
-
-
- -
-
-
-
- -

Yasnippet is a template system for emacs. It allows you to type a -abbrevation and automatically expand the abbreviation into function -templates.

-

Bundled language templates includes: C, C++, C#, Perl, Python, Ruby, -SQL, LaTeX, HTML, CSS and more.

-

Yasnippet system is inspired from TextMate's template system. You can -use a tool -to import any TextMate template you have to Yasnippet. It is a -re-design and re-write of my original extension smart-snippet. It -is much cleaner and more powerful than smart-snippet.

-
-

Video Demo

-

Watch the demo at YouTube (download a higher -resolution version: yasnippet.avi).

-
-
-

Brief Install Instruction

-

There are two archives of YASnippet. One is a single file compiled -“bundle”, and the other is normal. If all you need is to use the -builtin templates, download the bundle one. If you want to add your -own templates, download the normal one.

-
-

Bundle Install

-
    -
  1. Download the latest yasnippet-bundle-x.y.z.el.tgz and unpack it.
  2. -
  3. You'll get a file named yasnippet-bundle.el, put it under -~/.emacs.d/plugins/ (create the directory if not exists).
  4. -
  5. Open the file in Emacs, and type Alt+x eval-buffer.
  6. -
-

That's it. Now open any one of your language file, you'll see a menu -YASnippet. you can pull the menu to insert a template. Or, you can -type the pre-defined abbrev and press TAB to expand it.

-

To have emacs load YASnippet automatically when it starts, put the -following in your ~/.emacs file:

-
-
(add-to-list 'load-path
-              "~/.emacs.d/plugins")
-(require 'yasnippet-bundle)
-
-
-
-
-

Normal Install

-

For full install of the normal archive, just download and unpack the -latest yasnippet-x.y.z.tar.bz2. You'll get a directory named -yasnippet, put it in your ~/.emacs.d/plugins and add the -following in your .emacs file:

-
-
(add-to-list 'load-path
-              "~/.emacs.d/plugins")
-(require 'yasnippet) ;; not yasnippet-bundle
-(yas/initialize)
-(yas/load-directory "~/.emacs.d/plugins/yasnippet/snippets")
-
-
-

Please refer to the documentation for full customization.

-
-
-
-

Bugs, Contribution and Support

- -

Thank you very much for using YASnippet!

-
-
-
-
-
-
-
-
- - diff --git a/dropdown-list.el b/dropdown-list.el new file mode 100644 index 0000000..7b451d5 --- /dev/null +++ b/dropdown-list.el @@ -0,0 +1,251 @@ +;;; dropdown-list.el --- Drop-down menu interface +;; +;; Filename: dropdown-list.el +;; Description: Drop-down menu interface +;; Author: Jaeyoun Chung [jay.chung@gmail.com] +;; Maintainer: +;; Copyright (C) 2008 Jaeyoun Chung +;; Created: Sun Mar 16 11:20:45 2008 (Pacific Daylight Time) +;; Version: +;; Last-Updated: Sun Mar 16 12:19:49 2008 (Pacific Daylight Time) +;; By: dradams +;; Update #: 43 +;; URL: http://www.emacswiki.org/cgi-bin/wiki/dropdown-list.el +;; Keywords: convenience menu +;; Compatibility: GNU Emacs 21.x, GNU Emacs 22.x +;; +;; Features that might be required by this library: +;; +;; `cl'. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Commentary: +;; +;; According to Jaeyoun Chung, "overlay code stolen from company-mode.el." +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Change log: +;; +;; 2008/03/16 dadams +;; Clean-up - e.g. use char-to-string for control chars removed by email posting. +;; Moved example usage code (define-key*, command-selector) inside the library. +;; Require cl.el at byte-compile time. +;; Added GPL statement. +;; 2008/01/06 Jaeyoun Chung +;; Posted to gnu-emacs-sources@gnu.org at 9:10 p.m. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 3, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth +;; Floor, Boston, MA 02110-1301, USA. +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Code: + +(eval-when-compile (require 'cl)) ;; decf, fourth, incf, loop, mapcar* + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defface dropdown-list-face + '((t :inherit default :background "lightyellow" :foreground "black")) + "*Bla." :group 'dropdown-list) + +(defface dropdown-list-selection-face + '((t :inherit dropdown-list-face :background "purple")) + "*Bla." :group 'dropdown-list) + +(defvar dropdown-list-overlays nil) + +(defun dropdown-list-hide () + (while dropdown-list-overlays + (delete-overlay (pop dropdown-list-overlays)))) + +(defun dropdown-list-put-overlay (beg end &optional prop value prop2 value2) + (let ((ov (make-overlay beg end))) + (overlay-put ov 'window t) + (when prop + (overlay-put ov prop value) + (when prop2 (overlay-put ov prop2 value2))) + ov)) + +(defun dropdown-list-line (start replacement &optional no-insert) + ;; start might be in the middle of a tab, which means we need to hide the + ;; tab and add spaces + (let ((end (+ start (length replacement))) + beg-point end-point + before-string after-string) + (goto-char (point-at-eol)) + (if (< (current-column) start) + (progn (setq before-string (make-string (- start (current-column)) ? )) + (setq beg-point (point))) + (goto-char (point-at-bol)) ;; Emacs bug, move-to-column is wrong otherwise + (move-to-column start) + (setq beg-point (point)) + (when (> (current-column) start) + (goto-char (1- (point))) + (setq beg-point (point)) + (setq before-string (make-string (- start (current-column)) ? )))) + (move-to-column end) + (setq end-point (point)) + (let ((end-offset (- (current-column) end))) + (when (> end-offset 0) (setq after-string (make-string end-offset ?b)))) + (when no-insert + ;; prevent inheriting of faces + (setq before-string (when before-string (propertize before-string 'face 'default))) + (setq after-string (when after-string (propertize after-string 'face 'default)))) + (let ((string (concat before-string replacement after-string))) + (if no-insert + string + (push (dropdown-list-put-overlay beg-point end-point 'invisible t + 'after-string string) + dropdown-list-overlays))))) + +(defun dropdown-list-start-column (display-width) + (let ((column (mod (current-column) (window-width))) + (width (window-width))) + (cond ((<= (+ column display-width) width) column) + ((> column display-width) (- column display-width)) + ((>= width display-width) (- width display-width)) + (t nil)))) + +(defun dropdown-list-move-to-start-line (candidate-count) + (decf candidate-count) + (let ((above-line-count (save-excursion (- (vertical-motion (- candidate-count))))) + (below-line-count (save-excursion (vertical-motion candidate-count)))) + (cond ((= below-line-count candidate-count) + t) + ((= above-line-count candidate-count) + (vertical-motion (- candidate-count)) + t) + ((>= (+ below-line-count above-line-count) candidate-count) + (vertical-motion (- (- candidate-count below-line-count))) + t) + (t nil)))) + +(defun dropdown-list-at-point (candidates &optional selidx) + (dropdown-list-hide) + (let* ((lengths (mapcar #'length candidates)) + (max-length (apply #'max lengths)) + (start (dropdown-list-start-column (+ max-length 3))) + (i -1) + (candidates (mapcar* (lambda (candidate length) + (let ((diff (- max-length length))) + (propertize + (concat (if (> diff 0) + (concat candidate (make-string diff ? )) + (substring candidate 0 max-length)) + (format "%3d" (+ 2 i))) + 'face (if (eql (incf i) selidx) + 'dropdown-list-selection-face + 'dropdown-list-face)))) + candidates + lengths))) + (save-excursion + (and start + (dropdown-list-move-to-start-line (length candidates)) + (loop initially (vertical-motion 0) + for candidate in candidates + do (dropdown-list-line (+ (current-column) start) candidate) + while (/= (vertical-motion 1) 0) + finally return t))))) + +(defun dropdown-list (candidates) + (let ((selection) + (temp-buffer)) + (save-window-excursion + (unwind-protect + (let ((candidate-count (length candidates)) + done key (selidx 0)) + (while (not done) + (unless (dropdown-list-at-point candidates selidx) + (switch-to-buffer (setq temp-buffer (get-buffer-create "*selection*")) + 'norecord) + (delete-other-windows) + (delete-region (point-min) (point-max)) + (insert (make-string (length candidates) ?\n)) + (goto-char (point-min)) + (dropdown-list-at-point candidates selidx)) + (setq key (read-key-sequence "")) + (cond ((and (stringp key) + (>= (aref key 0) ?1) + (<= (aref key 0) (+ ?0 (min 9 candidate-count)))) + (setq selection (- (aref key 0) ?1) + done t)) + ((member key `(,(char-to-string ?\C-p) [up] "p")) + (setq selidx (mod (+ candidate-count (1- (or selidx 0))) + candidate-count))) + ((member key `(,(char-to-string ?\C-n) [down] "n")) + (setq selidx (mod (1+ (or selidx -1)) candidate-count))) + ((member key `(,(char-to-string ?\f)))) + ((member key `(,(char-to-string ?\r) [return])) + (setq selection selidx + done t)) + (t (setq done t))))) + (dropdown-list-hide) + (and temp-buffer (kill-buffer temp-buffer))) + ;; (when selection + ;; (message "your selection => %d: %s" selection (nth selection candidates)) + ;; (sit-for 1)) + selection))) + +(defun define-key* (keymap key command) + "Add COMMAND to the multiple-command binding of KEY in KEYMAP. +Use multiple times to bind different COMMANDs to the same KEY." + (define-key keymap key (combine-command command (lookup-key keymap key)))) + +(defun combine-command (command defs) + "$$$$$ FIXME - no doc string" + (cond ((null defs) command) + ((and (listp defs) + (eq 'lambda (car defs)) + (= (length defs) 4) + (listp (fourth defs)) + (eq 'command-selector (car (fourth defs)))) + (unless (member `',command (cdr (fourth defs))) + (setcdr (fourth defs) (nconc (cdr (fourth defs)) `(',command)))) + defs) + (t + `(lambda () (interactive) (command-selector ',defs ',command))))) + +(defvar command-selector-last-command nil "$$$$$ FIXME - no doc string") + +(defun command-selector (&rest candidates) + "$$$$$ FIXME - no doc string" + (if (and (eq last-command this-command) command-selector-last-command) + (call-interactively command-selector-last-command) + (let* ((candidate-strings + (mapcar (lambda (candidate) + (format "%s" (if (symbolp candidate) + candidate + (let ((s (format "%s" candidate))) + (if (>= (length s) 7) + (concat (substring s 0 7) "...") + s))))) + candidates)) + (selection (dropdown-list candidate-strings))) + (when selection + (let ((cmd (nth selection candidates))) + (call-interactively cmd) + (setq command-selector-last-command cmd)))))) + +;;;;;;;;;;;;;;;;;;;; + +(provide 'dropdown-list) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; dropdown-list.el ends here \ No newline at end of file diff --git a/snippets/text-mode/email b/snippets/text-mode/email index b953111..1ac7f94 100644 --- a/snippets/text-mode/email +++ b/snippets/text-mode/email @@ -1,3 +1,3 @@ #name : (user's email) # -- -`user-mail-address` \ No newline at end of file +`(replace-regexp-in-string "@" "@NOSPAM." user-mail-address)` \ No newline at end of file diff --git a/snippets/text-mode/objc-mode/crazy b/snippets/text-mode/objc-mode/crazy deleted file mode 100644 index 6aa0220..0000000 --- a/snippets/text-mode/objc-mode/crazy +++ /dev/null @@ -1,4 +0,0 @@ -#name : foo { ... } ; setFoo { ... } -# -- -I say ${1:hello} and so ${2: goodbye $1} -She also says $2 \ No newline at end of file diff --git a/snippets/text-mode/objc-mode/prop b/snippets/text-mode/objc-mode/prop deleted file mode 100644 index f17ae13..0000000 --- a/snippets/text-mode/objc-mode/prop +++ /dev/null @@ -1,14 +0,0 @@ -#name : foo { ... } ; setFoo { ... } -# -- -- ${1:id} ${2:foo and its ${3:nested} shit} -{ - return $2; - // dont forget we have $3 -} - -- (void)set${2:$(capitalize text)}:($1)aValue -{ - [$2 autorelease]; - $2 = [aValue retain]; -} -$0 \ No newline at end of file diff --git a/snippets/text-mode/snippet-mode/field b/snippets/text-mode/snippet-mode/field new file mode 100644 index 0000000..bdaf0d4 --- /dev/null +++ b/snippets/text-mode/snippet-mode/field @@ -0,0 +1,5 @@ +# name : ${ ... } field +# contributor : joaotavora +# key : $f +# -- +\${${1:${2:n}:}$3${4:\$(${5:lisp-fn})}\}$0 \ No newline at end of file diff --git a/snippets/text-mode/snippet-mode/mirror b/snippets/text-mode/snippet-mode/mirror new file mode 100644 index 0000000..94c8e56 --- /dev/null +++ b/snippets/text-mode/snippet-mode/mirror @@ -0,0 +1,6 @@ +# name : ${n:$(...)} mirror +# key : $m +# contributor : joaotavora +# -- +\${${2:n}:${4:\$(${5:reflection-fn})}\}$0 + diff --git a/snippets/text-mode/snippet-mode/vars b/snippets/text-mode/snippet-mode/vars new file mode 100644 index 0000000..f271767 --- /dev/null +++ b/snippets/text-mode/snippet-mode/vars @@ -0,0 +1,9 @@ +# name : Snippet header +# contributor : joaotavora +# -- +# name : $1${2: +# key : ${3:expand-key}}${4: +# key : ${5:group}} +# contributor : $6 +# -- +$0 \ No newline at end of file diff --git a/yasnippet.el b/yasnippet.el index 192483a..acfcb9c 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -3,7 +3,7 @@ ;; Copyright 2008 pluskid ;; Authors: pluskid , joaotavora -;; Version: 0.6.0 beta +;; Version: 0.6.0b ;; X-URL: http://code.google.com/p/yasnippet/ ;; Keywords: snippet, textmate ;; URL: http://code.google.com/p/yasnippet/ @@ -27,22 +27,59 @@ ;;; Commentary: ;; Basic steps to setup: -;; 1. Place `yasnippet.el' in your `load-path'. +;; +;; 1. In your .emacs file: ;; (add-to-list 'load-path "/dir/to/yasnippet.el") -;; 2. In your .emacs file: ;; (require 'yasnippet) -;; 3. Place the `snippets' directory somewhere. E.g: ~/.emacs.d/snippets -;; 4. In your .emacs file +;; 2. Place the `snippets' directory somewhere. E.g: ~/.emacs.d/snippets +;; 3. In your .emacs file ;; (setq yas/root-directory "~/.emacs/snippets") ;; (yas/load-directory yas/root-directory) -;; 5. To enable the YASnippet menu and tab-trigger expansion +;; 4. To enable the YASnippet menu and tab-trigger expansion ;; M-x yas/minor-mode -;; 6. To globally enable the minor mode in *all* buffers +;; 5. To globally enable the minor mode in *all* buffers ;; M-x yas/global-mode ;; -;; Steps 5. and 6. are optional, you can insert use snippets without -;; them via: -;; M-x yas/insert-snippet +;; Steps 4. and 5. are optional, you don't have to use the minor +;; mode to use YASnippet. +;; +;; +;; Major commands are: +;; +;; M-x yas/expand +;; +;; Try to expand snippets before point. In `yas/minor-mode', +;; this is bound to `yas/trigger-key' which you can customize. +;; +;; M-x yas/load-directory +;; +;; Prompts you for a directory hierarchy of snippets to load. +;; +;; M-x yas/insert-snippet +;; +;; Prompts you for possible snippet expansion if that is +;; possible according to buffer-local and snippet-local +;; expansion conditions. With prefix argument, ignore these +;; conditions. +;; +;; M-x yas/find-snippets +;; +;; Lets you find the snippet file in the directory the +;; snippet was loaded from (if it exists) like +;; `find-file-other-window'. +;; +;; M-x yas/find-snippet-file +;; +;; Prompts you for possible snippet expasions like +;; `yas/insert-snippet', but instead of expanding it, takes +;; you directly to the snippet definition's file, if it +;; exits. +;; +;; M-x yas/load-snippet-buffer +;; +;; When editing a snippet, this loads the snippet. This is +;; bound to "C-c C-c" while in the `snippet-mode' editing +;; mode. ;; ;; The `dropdown-list.el' extension is bundled with YASnippet, you ;; can optionally use it the preferred "prompting method", puting in @@ -54,6 +91,7 @@ ;; yas/completing-prompt)) ;; ;; Also check out the customization group +;; ;; M-x customize-group RET yasnippet RET ;; ;; For more information and detailed usage, refer to the project page: @@ -392,20 +430,24 @@ snippet templates") (defvar yas/minor-mode-map (make-sparse-keymap) "The keymap used when `yas/minor-mode' is active.") -(easy-menu-define yas/minor-mode-menu - yas/minor-mode-map - "Menu used when YAS/minor-mode is active." - (cons "YASnippet" - (mapcar #'(lambda (ent) - (when (third ent) - (define-key yas/minor-mode-map (third ent) (second ent))) - (vector (first ent) (second ent) t)) - (list (list "--") - (list "Expand trigger" 'yas/expand (read-kbd-macro yas/trigger-key)) - (list "Insert at point" 'yas/insert-snippet "\C-c&\C-s") - (list "About" 'yas/about) - (list "Reload-all-snippets" 'yas/reload-all) - (list "Load snippets..." 'yas/load-directory))))) +(defun yas/init-keymap-and-menu () + (easy-menu-define yas/minor-mode-menu + yas/minor-mode-map + "Menu used when YAS/minor-mode is active." + (cons "YASnippet" + (mapcar #'(lambda (ent) + (when (third ent) + (define-key yas/minor-mode-map (third ent) (second ent))) + (vector (first ent) (second ent) t)) + (list (list "--") + (list "Expand trigger" 'yas/expand (read-kbd-macro yas/trigger-key)) + (list "Insert at point" 'yas/insert-snippet "\C-c&\C-s") + (list "About" 'yas/about) + (list "Reload-all-snippets" 'yas/reload-all) + (list "Load snippets..." 'yas/load-directory)))))) + +(eval-when-compile + (yas/init-keymap-and-menu)) (define-minor-mode yas/minor-mode "Toggle YASnippet mode. @@ -884,20 +926,40 @@ content of the file is the template." (message "done."))) (defun yas/reload-all () - "Reload all snippets and rebuild the YASnippet menu." + "Reload all snippets and rebuild the YASnippet menu. " + (interactive) - (setq yas/snippet-tables (make-hash-table)) - (setq yas/menu-table (make-hash-table)) - (setq yas/minor-mode-menu nil) - (setq yas/minor-mode-map (make-sparse-keymap)) - (yas/init-keymap-and-menu) - (if yas/root-directory - (if (listp yas/root-directory) - (dolist (directory yas/root-directory) - (yas/load-directory directory)) - (yas/load-directory yas/root-directory)) - (call-interactively 'yas/load-directory)) - (message "done.")) + (let ((restore-global-mode nil) + (restore-minor-mode nil)) + (setq yas/snippet-tables (make-hash-table)) + (setq yas/menu-table (make-hash-table)) + (setf (cdr yas/minor-mode-menu) nil) + (setf (cdr yas/minor-mode-map) nil) + (when yas/global-mode + (yas/global-mode -1) + (setq restore-global-mode t)) + + (when yas/minor-mode + (yas/minor-mode -1) + (setq restore-minor-mode t)) + + (yas/init-keymap-and-menu) + + (if yas/root-directory + (if (listp yas/root-directory) + (dolist (directory yas/root-directory) + (yas/load-directory directory)) + (yas/load-directory yas/root-directory)) + (call-interactively 'yas/load-directory)) + + + (when restore-minor-mode + (yas/minor-mode 1)) + + (when restore-global-mode + (yas/global-mode 1)) + + (message "done."))) (defun yas/quote-string (string) "Escape and quote STRING. @@ -908,23 +970,31 @@ foo\"bar\\! -> \"foo\\\"bar\\\\!\"" string t) "\"")) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Yasnipept Bundle + +(defun yas/initialize () + "For backward compatibility, enable `yas/minor-mode' globally" + (yas/global-mode 1)) (defun yas/compile-bundle (&optional yasnippet yasnippet-bundle snippet-roots code dropdown) "Compile snippets in SNIPPET-ROOTS to a single bundle file. -SNIPPET-ROOTS is a list of root directories that contains the snippets -definition. YASNIPPET is the yasnippet.el file path. YASNIPPET-BUNDLE -is the output file of the compile result. CODE is the code you would -like to used to initialize yasnippet. Here's the default value for -all the parameters: +SNIPPET-ROOTS is a list of root directories that contains the +snippets definition. YASNIPPET is the yasnippet.el file +path. YASNIPPET-BUNDLE is the output file of the compile +result. CODE is the code you would like to used to initialize +yasnippet. Last optional argument DROPDOWN is the filename of the +dropdown-list.el library. + +Here's the default value for all the parameters: (yas/compile-bundle \"yasnippet.el\" \"./yasnippet-bundle.el\" '(\"snippets\") \"(yas/initialize)\") -Last optional argument DROPDOWN is the filename of the -dropdown-list.el library... +.. " (when (null yasnippet) @@ -933,6 +1003,8 @@ dropdown-list.el library... (setq yasnippet-bundle "./yasnippet-bundle.el")) (when (null snippet-roots) (setq snippet-roots '("snippets"))) + (when (null dropdown) + (setq dropdown "dropdown-list.el")) (when (null code) (setq code (concat "(yas/initialize-bundle)" "\n;;;###autoload" ; break through so that won't @@ -1216,7 +1288,7 @@ by condition." (yas/template-env template)) (message "[yas] No snippets can be inserted here!")))) -(defun yas/find-snippet-by-key () +(defun yas/find-snippet-file () "Choose a snippet to edit, selection like `yas/insert-snippet'." (interactive) (let* ((yas/buffer-local-condition 'always) @@ -1234,8 +1306,14 @@ by condition." (car templates))))) (when template - (find-file-other-window (yas/template-file template)) - (snippet-mode)))) + (let ((file (yas/template-file template))) + (cond ((and file (file-exists-p file)) + (find-file-other-window file) + (snippet-mode)) + (file + (message "Original file %s no longer exists!" file)) + (t + (message "This snippet was not loaded from a file!"))))))) (defun yas/guess-snippet-directory () "Try to guess the suitable yassnippet based on `major-mode'" @@ -1256,7 +1334,7 @@ by condition." path)))) -(defun yas/find-snippet (&optional same-window) +(defun yas/find-snippets (&optional same-window) "Find a snippet file in a suitable directory." (interactive "P") (let* ((current-table (yas/current-snippet-table major-mode 'dont-search-parents)) From b2f7e0e1023b9554f868b287bc2f5ddf850acd01 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Thu, 23 Jul 2009 13:30:02 +0000 Subject: [PATCH 68/75] * added yas/tryout-snippet * corrected some overlay problems, but the remaining known problems are going to remain for a while... --- yasnippet.el | 132 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 96 insertions(+), 36 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index acfcb9c..06addf8 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -503,6 +503,7 @@ Key bindings: (defvar snippet-mode-map (make-sparse-keymap)) (define-key snippet-mode-map "\C-c\C-c" 'yas/load-snippet-buffer) +(define-key snippet-mode-map "\C-c\C-t" 'yas/tryout-snippet) (define-derived-mode snippet-mode text-mode "YASnippet" @@ -713,7 +714,7 @@ the template of a snippet in the current snippet-table." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Template-related and snippet loading functions -(defun yas/parse-template (file) +(defun yas/parse-template (&optional file) "Parse the template in the current buffer. Optional FILE is the absolute file name of the file being @@ -1370,10 +1371,10 @@ by condition." (parent-mode-name (file-name-nondirectory parent-file-dir)) (major-mode-sym (intern major-mode-name)) (parent-mode-sym (intern parent-mode-name))) - (cons (when (fboundp major-mode-sym) - major-mode-sym) - (when (fboundp parent-mode-sym) - parent-mode-sym)))) + (when (fboundp major-mode-sym) + (cons major-mode-sym + (when (fboundp parent-mode-sym) + parent-mode-sym))))) (defun yas/load-snippet-buffer (&optional kill) "Parse and load current buffer's snippet definition." @@ -1390,6 +1391,27 @@ by condition." (quit-window)) (message "Save the buffer as a file first!"))) +(defun yas/tryout-snippet (&optional debug) + "Parse and load current buffer's snippet definition." + (interactive "P") + (let* ((major-mode-and-parent (or (and buffer-file-name + (yas/compute-major-mode-and-parent buffer-file-name)) + (cons (intern (read-from-minibuffer "Cannot auto-detect major mode! Enter a major mode: ")) + nil))) + (parsed (and major-mode-and-parent + (fboundp (car major-mode-and-parent)) + (yas/parse-template (symbol-name (car major-mode-and-parent))))) + (template (and parsed + (yas/make-template (second parsed) (third parsed) nil (sixth parsed) nil)))) + (cond (template + (set-buffer (switch-to-buffer (format "*YAS TEST: %s*" (yas/template-name template)))) + (funcall (car major-mode-and-parent)) + (yas/expand-snippet (point-min) (point-max) (yas/template-content template) (yas/template-env template)) + (when debug + (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local))) + (t + (message "[yas] Coulnd not parse template!"))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; User convenience functions, for using in snippet definitions ;;; @@ -1473,7 +1495,8 @@ Otherwise throw exception." (mirrors '()) (transform nil) (modified-p nil) - (back-adjacent-fields nil)) + (back-adjacent-fields nil) + (back-adjacent-mirrors nil)) (defstruct (yas/mirror (:constructor yas/make-mirror (start end transform))) "A mirror." @@ -1586,14 +1609,18 @@ delegate to `yas/next-field'." (t nil)))) +(defun yas/place-overlays (snippet field) + "Correctly place overlays for SNIPPET's FIELD" + (yas/make-move-active-field-overlay snippet field) + (yas/make-move-field-protection-overlays snippet field)) + (defun yas/move-to-field (snippet field) "Update SNIPPET to move to field FIELD. Also create some protection overlays" (goto-char (yas/field-start field)) (setf (yas/snippet-active-field snippet) field) - (yas/make-move-active-field-overlay snippet field) - (yas/make-move-field-protection-overlays snippet field) + (yas/place-overlays snippet field) (overlay-put yas/active-field-overlay 'yas/field field) ;;; primary field transform: first call to snippet transform (unless (yas/field-modified-p field) @@ -1697,7 +1724,7 @@ NO-HOOKS means don't run the `yas/after-exit-snippet-hook' hooks." ;; (let ((previous-field (yas/snippet-previous-active-field snippet))) (when (and yas/snippet-end previous-field) - (yas/advance-field-and-parents-maybe previous-field yas/snippet-end))) + (yas/advance-field-end-marker previous-field yas/snippet-end))) ;; Convert all markers to points, ;; @@ -1825,21 +1852,25 @@ deletes a character normally." (setf (yas/field-modified-p field) t) (delete-region (yas/field-start field) (yas/field-end field))) -(defun yas/advance-field-and-parents-maybe (field end) - "Advance FIELDs end-marker to END and recurse for parent fields - -This is needed since markers don't \"rear-advance\" like overlays" - (when (< (yas/field-end field) end) - (set-marker (yas/field-end field) end) +(defun yas/advance-field-end-marker (field newend) + "Advance FIELDs end-marker to NEWEND and recurse for parent fields" + (when (< (yas/field-end field) newend) + (set-marker (yas/field-end field) newend) (when (yas/field-parent-field field) - (yas/advance-field-and-parents-maybe (yas/field-parent-field field) end))) - ;; take care of adjacents + (yas/advance-field-end-marker (yas/field-parent-field field) newend))) + ;; take care of adjacent fields (let ((adjacents (yas/field-back-adjacent-fields field))) (when adjacents (dolist (adjacent adjacents) - (when (< (yas/field-start adjacent) end) - (set-marker (yas/field-start adjacent) end)) - (yas/advance-field-and-parents-maybe adjacent end))))) + (when (< (yas/field-start adjacent) newend) + (set-marker (yas/field-start adjacent) newend)) + (yas/advance-field-end-marker adjacent newend)))) + ;; take care of adjacent mirrors + (let ((adjacents (yas/field-back-adjacent-mirrors field))) + (when adjacents + (dolist (adjacent adjacents) + (when (< (yas/mirror-start adjacent) newend) + (set-marker (yas/mirror-start adjacent) newend)))))) (defun yas/make-move-active-field-overlay (snippet field) "Place the active field overlay in SNIPPET's FIELD. @@ -1869,7 +1900,7 @@ progress." (unless (yas/undo-in-progress) (let ((field (overlay-get yas/active-field-overlay 'yas/field))) (cond (after? - (yas/advance-field-and-parents-maybe field (overlay-end overlay)) + (yas/advance-field-end-marker field (overlay-end overlay)) ;;; primary field transform: normal calls to expression (let ((saved-point (point))) (yas/field-update-display field (car (yas/snippets-at-point))) @@ -2025,7 +2056,7 @@ will be deleted before inserting template." (overlay-get yas/active-field-overlay 'yas/field)))) (when existing-field (setf (yas/snippet-previous-active-field snippet) existing-field) - (yas/advance-field-and-parents-maybe existing-field (overlay-end yas/active-field-overlay)))) + (yas/advance-field-end-marker existing-field (overlay-end yas/active-field-overlay)))) ;; Move to the first of fields, or exit the snippet to its exit ;; point @@ -2096,6 +2127,9 @@ Returns the newly created snippet." ;; Sort and link each field (yas/snippet-sort-link-fields snippet) + + ;; Calculate field and mirror adjacencies + (yas/calculate-adjacencies snippet) ;; Update the mirrors for the first time (yas/update-mirrors snippet) @@ -2116,14 +2150,25 @@ Returns the newly created snippet." (setf (yas/snippet-fields snippet) (sort (yas/snippet-fields snippet) '(lambda (field1 field2) - (yas/snippet-field-compare field1 field2)))) - (let ((prev nil)) - (dolist (field (yas/snippet-fields snippet)) - ;; also check for other fields adjacent to this fields back - (dolist (otherfield (yas/snippet-fields snippet)) - (when (and (not (eq otherfield field)) - (= (yas/field-end field) (yas/field-start otherfield))) - (push otherfield (yas/field-back-adjacent-fields field))))))) + (yas/snippet-field-compare field1 field2))))) + +(defun yas/calculate-adjacencies (snippet) + ;; For each field in the snippet + ;; + (dolist (field (yas/snippet-fields snippet)) + ;; Calculate its adjacencies to other mirrors and fields + ;; + (dolist (otherfield (yas/snippet-fields snippet)) + (dolist (mirror (yas/field-mirrors otherfield)) + (when (= (yas/field-end field) (yas/mirror-start mirror)) + (push mirror (yas/field-back-adjacent-mirrors field)))) + (when (and (not (eq otherfield field)) + (= (yas/field-end field) (yas/field-start otherfield))) + (push otherfield (yas/field-back-adjacent-fields field)))) + ;; Calculate the adjacencies of each one of its mirrors + ;; + ;; TODO: Known bug. + )) (defun yas/snippet-parse-create (snippet) "Parse a recently inserted snippet template, creating all @@ -2143,11 +2188,11 @@ Meant to be called in a narrowed buffer, does various passes" ;; (goto-char parse-start) (yas/protect-escapes) - ;; parse fields + ;; parse fields with {} ;; (goto-char parse-start) (yas/field-parse-create snippet) - ;; parse simple mirrors + ;; parse simple mirrors and fields ;; (goto-char parse-start) (yas/simple-mirror-parse-create snippet) @@ -2355,7 +2400,16 @@ When multiple expressions are found, only the last one counts." ;; altered. ;; (let ((inhibit-modification-hooks t)) - (yas/mirror-update-display mirror field)))))) + (yas/mirror-update-display mirror field) + ;; Take care of the fields adjacent to this mirror's back + ;; TODO: Known bug + + ;; `yas/place-overlays' is needed if the active field and + ;; protected overlays have been changed because of insertions + ;; in `yas/mirror-update-display' + ;; + (when (eq field (yas/snippet-active-field snippet)) + (yas/place-overlays snippet field))))))) (defun yas/mirror-update-display (mirror field) "Update MIRROR according to FIELD (and mirror transform)." @@ -2401,19 +2455,25 @@ When multiple expressions are found, only the last one counts." (princ (format "%s live snippets at point:\n\n" (length (yas/snippets-at-point)))) (dolist (snippet (yas/snippets-at-point)) - (princ (format "\tid: %s active field %d from %s to %s covering \"%s\"\n" + (princ (format "\tsid: %s active field %d from %s to %s covering \"%s\"\n" (yas/snippet-id snippet) (yas/field-number (yas/snippet-active-field snippet)) (marker-position (yas/field-start (yas/snippet-active-field snippet))) (marker-position (yas/field-end (yas/snippet-active-field snippet))) (buffer-substring-no-properties (yas/field-start (yas/snippet-active-field snippet)) (yas/field-end (yas/snippet-active-field snippet))))) (dolist (field (yas/snippet-fields snippet)) - (princ (format "\tn: %d field from %s to %s covering \"%s\" adjacent %s\n" + (princ (format "\tfield %d from %s to %s covering \"%s\" adj-fields %s adj-mirrors %s\n" (yas/field-number field) (marker-position (yas/field-start field)) (marker-position (yas/field-end field)) (buffer-substring-no-properties (yas/field-start field) (yas/field-end field)) - (length (yas/field-back-adjacent-fields field)))))) + (length (yas/field-back-adjacent-fields field)) + (length (yas/field-back-adjacent-mirrors field)))) + (dolist (mirror (yas/field-mirrors field)) + (princ (format "\t\tmirror from %s to %s covering \"%s\"\n" + (marker-position (yas/mirror-start mirror)) + (marker-position (yas/mirror-end mirror)) + (buffer-substring-no-properties (yas/mirror-start mirror) (yas/mirror-end mirror))))))) From e426fdc73d429295565a00e9aae83d1f33336553 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Thu, 23 Jul 2009 21:59:37 +0000 Subject: [PATCH 69/75] * small adjustments to yas/choose-value and yas/verify-value --- yasnippet.el | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 06addf8..f5150b1 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -166,7 +166,7 @@ representation using `read-kbd-macro'. " :type 'string :group 'yasnippet) -(defcustom yas/prev-field-key "" +(defcustom yas/prev-field-key '("" "") "The key to navigate to previous field when a snippet is active. Can also be a list of keys. @@ -1404,11 +1404,14 @@ by condition." (template (and parsed (yas/make-template (second parsed) (third parsed) nil (sixth parsed) nil)))) (cond (template - (set-buffer (switch-to-buffer (format "*YAS TEST: %s*" (yas/template-name template)))) - (funcall (car major-mode-and-parent)) - (yas/expand-snippet (point-min) (point-max) (yas/template-content template) (yas/template-env template)) - (when debug - (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local))) + (let ((buffer-name (format "*YAS TEST: %s*" (yas/template-name template)))) + (set-buffer (switch-to-buffer buffer-name)) + (erase-buffer) + (setq buffer-undo-list nil) + (funcall (car major-mode-and-parent)) + (yas/expand-snippet (point-min) (point-max) (yas/template-content template) (yas/template-env template)) + (when debug + (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local)))) (t (message "[yas] Coulnd not parse template!"))))) @@ -1427,20 +1430,31 @@ If found, the content of subexp group SUBEXP (default 0) is (match-string-no-properties grp str) str)))) -(defun yas/choose-value (&rest possibilities) - "Prompt for a string in the list POSSIBILITIES." - (some #'(lambda (fn) - (funcall fn "Choose: " possibilities)) - yas/prompt-functions)) +(defun yas/choose-value (possibilities) + "Prompt for a string in the list POSSIBILITIES and return it." + (unless (or yas/moving-away-p + yas/modified-p) + (some #'(lambda (fn) + (funcall fn "Choose: " possibilities)) + yas/prompt-functions))) + +(defun yas/key-to-value (alist) + "Prompt for a string in the list POSSIBILITIES and return it." + (unless (or yas/moving-away-p + yas/modified-p) + (let ((key (read-key-sequence ""))) + (when (stringp key) + (cdr (find key alist :key #'car :test #'string=)))))) (defun yas/throw (text) + "Throw a yas/exception with TEXT as the reason." (throw 'yas/exception (cons 'yas/exception text))) -(defun yas/verify-value (&rest possibilities) +(defun yas/verify-value (possibilities) "Verify that the current field value is in POSSIBILITIES Otherwise throw exception." - (when (and yas/moving-away (notany #'(lambda (pos) (string= pos yas/text)) possibilities)) + (when (and yas/moving-away-p (notany #'(lambda (pos) (string= pos yas/text)) possibilities)) (yas/throw (format "[yas] field only allows %s" possibilities)))) (defun yas/field-value (number) @@ -1509,7 +1523,7 @@ for this field, apply it. Otherwise, returned nil." (let* ((yas/text (yas/field-text-for-display field)) (text yas/text) (yas/modified-p (yas/field-modified-p field)) - (yas/moving-away nil) + (yas/moving-away-p nil) (transform (if (yas/mirror-p field-or-mirror) (yas/mirror-transform field-or-mirror) (yas/field-transform field-or-mirror))) @@ -1595,7 +1609,7 @@ delegate to `yas/next-field'." ;; (when (and active-field (yas/field-transform active-field)) - (let* ((yas/moving-away t) + (let* ((yas/moving-away-p t) (yas/text (yas/field-text-for-display active-field)) (text yas/text) (yas/modified-p (yas/field-modified-p active-field))) From 5ca14b495e9f84bf5ee7900ec63e667015645a76 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Fri, 24 Jul 2009 10:24:01 +0000 Subject: [PATCH 70/75] * fixed small problem with the `yas/multi-dollar-lisp-expression-regexp' * only move to the first snippet field after pushing snippet undo actions --- yasnippet.el | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index f5150b1..d86890d 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -400,7 +400,7 @@ snippet templates") "A regexp to *almost* recognize a field") (defconst yas/multi-dollar-lisp-expression-regexp - "$\\(([^)]*)\\)" + "$+\\(([^)]*)\\)" "A regexp to *almost* recognize a \"$(...)\" expression") (defconst yas/backquote-lisp-expression-regexp @@ -1444,7 +1444,8 @@ If found, the content of subexp group SUBEXP (default 0) is yas/modified-p) (let ((key (read-key-sequence ""))) (when (stringp key) - (cdr (find key alist :key #'car :test #'string=)))))) + (or (cdr (find key alist :key #'car :test #'string=)) + key))))) (defun yas/throw (text) "Throw a yas/exception with TEXT as the reason." @@ -1638,7 +1639,7 @@ Also create some protection overlays" (overlay-put yas/active-field-overlay 'yas/field field) ;;; primary field transform: first call to snippet transform (unless (yas/field-modified-p field) - (if (save-excursion (yas/field-update-display field snippet)) + (if (yas/field-update-display field snippet) (let ((inhibit-modification-hooks t)) (yas/update-mirrors snippet)) (setf (yas/field-modified-p field) nil)))) @@ -2072,14 +2073,11 @@ will be deleted before inserting template." (setf (yas/snippet-previous-active-field snippet) existing-field) (yas/advance-field-end-marker existing-field (overlay-end yas/active-field-overlay)))) - ;; Move to the first of fields, or exit the snippet to its exit - ;; point - ;; - (let ((first-field (car (yas/snippet-fields snippet)))) - (cond (first-field - (yas/move-to-field snippet first-field)) - (t - (yas/exit-snippet snippet)))) + ;; Exit the snippet immediately if no fields + ;; + (unless (yas/snippet-fields snippet) + (yas/exit-snippet snippet)) + ;; Push two undo actions: the deletion of the inserted contents of ;; the new snippet (whitout the "key") followed by an apply of ;; `yas/take-care-of-redo' on the newly inserted snippet boundaries @@ -2088,7 +2086,12 @@ will be deleted before inserting template." (end (overlay-end (yas/snippet-control-overlay snippet)))) (push (cons start end) buffer-undo-list) (push `(apply yas/take-care-of-redo ,start ,end ,snippet) - buffer-undo-list))) + buffer-undo-list)) + ;; Now, move to the first field + ;; + (let ((first-field (car (yas/snippet-fields snippet)))) + (when first-field + (yas/move-to-field snippet first-field)))) (message "[yas] snippet expanded.")) (defun yas/take-care-of-redo (beg end snippet) @@ -2354,11 +2357,11 @@ When multiple expressions are found, only the last one counts." (when parent-field (save-excursion (while (re-search-forward yas/multi-dollar-lisp-expression-regexp nil t) - (let* ((real-match-end-0 (yas/scan-sexps (1+ (match-beginning 0)) 1))) - (when real-match-end-0 - (let ((lisp-expression-string (buffer-substring-no-properties (match-beginning 1) real-match-end-0))) + (let* ((real-match-end-1 (yas/scan-sexps (match-beginning 1) 1))) + (when real-match-end-1 + (let ((lisp-expression-string (buffer-substring-no-properties (match-beginning 1) real-match-end-1))) (setf (yas/field-transform parent-field) lisp-expression-string)) - (delete-region (match-beginning 0) real-match-end-0))))))) + (delete-region (match-beginning 0) real-match-end-1))))))) (defun yas/transform-mirror-parse-create (snippet) "Parse the \"${n:$(lisp-expression)}\" mirror transformations." From 8dd3a78c014ef07df71e8f3804a36f42b40a4e9b Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Fri, 24 Jul 2009 15:06:09 +0000 Subject: [PATCH 71/75] * Fixed html auto-indent (redesigned yas/indent slightly to collect all the snipept markers then restore them. --- yasnippet.el | 113 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 46 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index d86890d..6cc903f 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -1626,8 +1626,8 @@ delegate to `yas/next-field'." (defun yas/place-overlays (snippet field) "Correctly place overlays for SNIPPET's FIELD" - (yas/make-move-active-field-overlay snippet field) - (yas/make-move-field-protection-overlays snippet field)) + (yas/make-move-field-protection-overlays snippet field) + (yas/make-move-active-field-overlay snippet field)) (defun yas/move-to-field (snippet field) "Update SNIPPET to move to field FIELD. @@ -1901,7 +1901,7 @@ Move the overlay, or create it if it does not exit." (yas/field-end field) nil nil t)) (overlay-put yas/active-field-overlay 'face 'yas/field-highlight-face) - ;;(overlay-put yas/active-field-overlay 'evaporate t) + (overlay-put yas/active-field-overlay 'yas/snippet snippet) (overlay-put yas/active-field-overlay 'modification-hooks '(yas/on-field-overlay-modification)) (overlay-put yas/active-field-overlay 'insert-in-front-hooks '(yas/on-field-overlay-modification)) (overlay-put yas/active-field-overlay 'insert-behind-hooks '(yas/on-field-overlay-modification)))) @@ -1959,7 +1959,7 @@ Move the overlays, or create them if they do not exit." ;; insert a newline. the `(1+ (buffer-size))' should prevent this ;; when using stacked expansion ;; - (when (< (1+ (buffer-size)) (1+ end)) + (when (< (buffer-size) end) (save-excursion (let ((inhibit-modification-hooks t)) (goto-char (point-max)) @@ -2181,7 +2181,8 @@ Returns the newly created snippet." (push mirror (yas/field-back-adjacent-mirrors field)))) (when (and (not (eq otherfield field)) (= (yas/field-end field) (yas/field-start otherfield))) - (push otherfield (yas/field-back-adjacent-fields field)))) + (when (not (find field (yas/field-back-adjacent-fields otherfield))) + (push otherfield (yas/field-back-adjacent-fields field))))) ;; Calculate the adjacencies of each one of its mirrors ;; ;; TODO: Known bug. @@ -2221,57 +2222,77 @@ Meant to be called in a narrowed buffer, does various passes" ;; (goto-char parse-start) (yas/restore-escapes) + ;; update mirrors for the first time + ;; + (yas/update-mirrors snippet) ;; indent the best we can ;; (goto-char parse-start) (yas/indent snippet))) (defun yas/indent (snippet) - (save-excursion - (cond ((eq yas/indent-line 'fixed) - (let* ((indent (if indent-tabs-mode - (concat (make-string (/ column tab-width) ?\t) - (make-string (% column tab-width) ?\ )) - (make-string (current-colum) ?\ )))) - (goto-char (point-min)) - (while (and (zerop (forward-line)) - (= (current-column) 0)) - (insert indent)))) - ((eq yas/indent-line 'auto) - (let ((end (set-marker (make-marker) (point-max)))) - (save-restriction - (widen) - ;; XXX: Here seems to be the indent problem: - ;; - ;; `indent-according-to-mode' uses whatever - ;; `indent-line-function' is available. Some - ;; implementations of these functions delete text - ;; before they insert. If there happens to be a marker - ;; just after the text being deleted, the insertion - ;; actually happens after the marker, which misplaces - ;; it. - ;; - ;; This would also happen if we had used overlays with - ;; the `front-advance' property set to nil. - ;; - (while (and (zerop (forward-line 1)) - (not (eobp)) - (<= (point) end)) - (goto-char (yas/real-line-beginning)) - (if (buffer-has-markers-at (point)) - (progn - (insert-before-markers "Y") - (indent-according-to-mode) - (backward-delete-char 1)) - (indent-according-to-mode))) - (set-marker end nil)))) - (t - nil))) (save-excursion (while (re-search-forward "$>" nil t) (delete-region (match-beginning 0) (match-end 0)) (when (not (eq yas/indent-line 'auto)) - (indent-according-to-mode))))) + (indent-according-to-mode)))) + (save-excursion + (cond ((eq yas/indent-line 'fixed) + (let* ((indent (if indent-tabs-mode + (concat (make-string (/ column tab-width) ?\t) + (make-string (% column tab-width) ?\ )) + (make-string (current-colum) ?\ )))) + (goto-char (point-min)) + (while (and (zerop (forward-line)) + (= (current-column) 0)) + (insert indent)))) + ((eq yas/indent-line 'auto) + (let ((end (set-marker (make-marker) (point-max))) + (snippet-markers (yas/collect-snippet-markers snippet))) + (save-restriction + (widen) + ;; XXX: Here seems to be the indent problem: + ;; + ;; `indent-according-to-mode' uses whatever + ;; `indent-line-function' is available. Some + ;; implementations of these functions delete text + ;; before they insert. If there happens to be a marker + ;; just after the text being deleted, the insertion + ;; actually happens after the marker, which misplaces + ;; it. + ;; + ;; This would also happen if we had used overlays with + ;; the `front-advance' property set to nil. + ;; + (while (and (zerop (forward-line 1)) + (not (eobp)) + (<= (point) end)) + (goto-char (yas/real-line-beginning)) + (let ((trouble-markers (remove-if-not #'(lambda (marker) + (= marker (point))) + snippet-markers))) + (indent-according-to-mode) + (mapc #'(lambda (marker) + (set-marker marker (point))) + trouble-markers) + (indent-according-to-mode))) + (set-marker end nil)))) + (t + nil)))) + +(defun yas/collect-snippet-markers (snippet) + "Make a list of all the markers used by SNIPPET." + (let (markers) + (dolist (field (yas/snippet-fields snippet)) + (push (yas/field-start field) markers) + (push (yas/field-end field) markers) + (dolist (mirror (yas/field-mirrors field)) + (push (yas/mirror-start mirror) markers) + (push (yas/mirror-end mirror) markers))) + (when (and (yas/snippet-exit snippet) + (marker-buffer (yas/snippet-exit snippet))) + (push (yas/snippet-exit snippet) markers)) + markers)) (defun yas/real-line-beginning () (let ((c (char-after (line-beginning-position))) From 102424118ee0dd657894e85a9a5922231a2ddc30 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Fri, 24 Jul 2009 15:41:12 +0000 Subject: [PATCH 72/75] * Release notes and issue list updated * Little problems fixed * ruby mode cls snippet fixed --- doc/changelog.rst | 22 ++++++++++++++++++++++ snippets/text-mode/ruby-mode/cls | 2 +- yasnippet.el | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index 3e956c8..7c7ebd3 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -6,6 +6,28 @@ ChangeLog :Contact: pluskid@gmail.com :Date: 2008-03-22 + +0.6.0b / 2009-07-2x +=================== + +* Nested placeholders of the type $0 +* More robust undo/redo support +* Stacked snippet expansion (snippet in snippet) +* Transformation on a primary field, the syntax being +* Validation on field exit +* Wrapping the region in the exit marker $0 of the snippet +* Auto-indentation +* Easier definition of snippets +* Customization group +* Overriding customization variables in snippets + +* Fixed `Issue 60 + `_ +* Fixed `Issue 65 + `_ +* Fixed `Issue 56 + `_ + 0.5.10 / 2009-02-11 =================== diff --git a/snippets/text-mode/ruby-mode/cls b/snippets/text-mode/ruby-mode/cls index 4ce8a0b..c86cbfe 100644 --- a/snippets/text-mode/ruby-mode/cls +++ b/snippets/text-mode/ruby-mode/cls @@ -2,7 +2,7 @@ #contributor : hitesh #group : definitions # -- -class ${1:$ +class ${1:$$ (let ((fn (capitalize (file-name-nondirectory (file-name-sans-extension (buffer-file-name)))))) diff --git a/yasnippet.el b/yasnippet.el index 6cc903f..e016197 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -400,7 +400,7 @@ snippet templates") "A regexp to *almost* recognize a field") (defconst yas/multi-dollar-lisp-expression-regexp - "$+\\(([^)]*)\\)" + "$+[ \t\n]*\\(([^)]*)\\)" "A regexp to *almost* recognize a \"$(...)\" expression") (defconst yas/backquote-lisp-expression-regexp From 2f21c06622658122001587354025607859ef6b61 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Fri, 24 Jul 2009 19:29:36 +0000 Subject: [PATCH 73/75] Cleaning up before release commit... --- doc/changelog.rst | 34 +++++--- doc/define_snippet.rst | 189 +++++++++++++++++++++++++++++++++++------ doc/faq.rst | 6 +- yasnippet.el | 161 +++++++++++++++++++---------------- 4 files changed, 277 insertions(+), 113 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index 7c7ebd3..58a627b 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -10,16 +10,30 @@ ChangeLog 0.6.0b / 2009-07-2x =================== -* Nested placeholders of the type $0 -* More robust undo/redo support -* Stacked snippet expansion (snippet in snippet) -* Transformation on a primary field, the syntax being -* Validation on field exit -* Wrapping the region in the exit marker $0 of the snippet -* Auto-indentation -* Easier definition of snippets -* Customization group -* Overriding customization variables in snippets +* Nested placeholders of the type `` $0``. + +* More robust undo/redo support. + +* Stacked snippet expansion (*snippet in snippet*). + +* Transformation on a primary field with syntax ``${1:default$(transform)}`` + +* Validations on field exit through the ``yas/verify-value`` + primary field transformation. + +* Wrapping the region in the exit marker ``$0`` of the snippet. Use + ``yas/wrap-around-region``. + +* Auto-indentation. Use ``yas/indent-line`` set to ``'auto`` + +* Easier definition of snippets. Use ``yas/find-snippets`` or + ``yas/visit-snippet-file``. In the new ``snippet-mode`` use + ``yas/load-snippet-buffer`` and ``yas/tryout-snippet``. + +* Customization group ``yasnippet``. + +* Overriding customization variables in snippets. Use the ``env: + let-form`` template keyword. * Fixed `Issue 60 `_ diff --git a/doc/define_snippet.rst b/doc/define_snippet.rst index dbd9baa..58d31f0 100644 --- a/doc/define_snippet.rst +++ b/doc/define_snippet.rst @@ -2,9 +2,9 @@ How to define a snippet ? ========================= -:Author: pluskid +:Author: pluskid, joaotavora :Contact: pluskid@gmail.com -:Date: 2008-03-20 +:Date: 2009-07-24 .. contents:: @@ -202,7 +202,30 @@ ignored. Here's a list of currently supported meta data: under the ``loops`` group which is under the ``control structure`` group. +Using the ``snippet-mode`` major mode +------------------------------------- +From version 0.6 upwards there is a major mode ``snippet-mode`` to +edit snippets. You can set the buffer to this mode with ``M-x +snippet-mode``. It provides reasonably useful syntax highlighting. + +Two commands are defined in this mode: + +* ``M-x yas/load-snippet-buffer`` + + When editing a snippet, this loads the snippet into the correct + mode and menu. Bound to ``C-c C-c`` by default while in + ``snippet-mode``. + +* ``M-x yas/tryout-snippet`` + + When editing a snippet, this opens a new empty buffer, sets it to + the appropriate major mode and inserts the snippet there, so you + can see what it looks like. This is bound to ``C-c C-t`` while in + ``snippet-mode``. + +There are also snippets for making snippets: ``vars``, ``field`` and +``mirror``. Define snippets using elisp code -------------------------------- @@ -266,7 +289,7 @@ The basic syntax of ``yas/compile-bundle`` is .. sourcecode:: common-lisp - (yas/compile-bundle &optional yasnippet yasnippet-bundle snippet-roots code) + (yas/compile-bundle &optional yasnippet yasnippet-bundle snippet-roots code dropdown) As you can see, all the parameters are optional. The default values for those parameters are convenient for me to produce the default @@ -277,7 +300,8 @@ release bundle: (yas/compile-bundle "yasnippet.el" "./yasnippet-bundle.el" '("snippets") - "(yas/initialize)") + "(yas/initialize)" + "dropdown-list.el") The ``snippet-roots`` can be a list of root directories. This is useful when you have multiple snippet directories (maybe from other @@ -286,6 +310,9 @@ customization code instead of the default ``(yas/initialize)``. For example, you can set ``yas/trigger-key`` to ``(kbd "SPC")`` here if you like. +From release 0.6 you have to specify the ``dropdown-list.el`` file if +you want it to be a part of the generated bundle. + yas/define ~~~~~~~~~~ @@ -419,22 +446,28 @@ When there are multiple candidates, YASnippet will let you select one. The UI for selecting multiple candidate can be customized. There're two variable related: -* ``yas/window-system-popup-function``: the function used when you - have a window system. -* ``yas/text-popup-function``: the function used when you don't have a - window system, i.e. when you are working in a terminal. +From version 0.6 of YASnippet this has changed significantly. A +customization variable, called ``yas/prompt-functions`` defines your +preferred method of being prompted for snippets. - Currently there're three solution come with YASnippet. +You can customize it with ``M-x customize-variable RET +yas/prompt-functions RET``. Alternatively you can put in your +emacs-file: + +.. sourcecode:: common-lisp + (setq yas/prompt-functions '(yas/x-prompt yas/dropdown-prompt)) + +Currently there are some alternatives solution with YASnippet. .. image:: images/popup-menu.png :align: right -Popup Menu -~~~~~~~~~~ +Use the X window system +~~~~~~~~~~~~~~~~~~~~~~~ -The function ``yas/x-popup-menu-for-template`` can be used to show a -popup menu for you to select. This menu will be part of you native -window system widget, which means: +The function ``yas/x-prompt`` can be used to show a popup menu for you +to select. This menu will be part of you native window system widget, +which means: * It usually looks beautiful. E.g. when you compile Emacs with gtk support, this menu will be rendered with your gtk theme. @@ -442,15 +475,18 @@ window system widget, which means: ``C-p`` to navigate. * This function can't be used when in a terminal. -Just select the first one -~~~~~~~~~~~~~~~~~~~~~~~~~ +Use built-in Emacs selection methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This one is originally used in terminal mode. It doesn't let you to -choose anything, it just select the first one on behalf of you. So I -bet you never want to use this. :p +You can use functions ``yas/completing-prompt`` for the classic emacs +completion method or ``yas/ido-prompt`` for a much nicer looking +method. The best way is to try it. This works in a terminal. -Use a dropdown-menu.el -~~~~~~~~~~~~~~~~~~~~~~ +Use ``dropdown-menu.el`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +The function ``yas/dropdown-prompt`` can also be placed in the +``yas/prompt-functions`` list. .. image:: images/dropdown-menu.png :align: right @@ -497,7 +533,9 @@ The Minor Mode When ``yas/minor-mode`` is enabled, the trigger key will take effect. The default key is ``(kbd "TAB")``, however, you can freely -set it to some other key. By default, YASnippet add a hook to +set it to some other key. + +In version 0.5, YASnippet add a hook to ``after-change-major-mode-hook`` to enable ``yas/minor-mode`` [2]_ in every buffer. This works fine for most modes, however, some mode doesn't follow the Emacs convention and doens't call this hook. You @@ -515,6 +553,10 @@ Note that **should** be put after ``(require 'yasnippet)`` and before ``(yas/initialize)``. Further more, you may report it to me, I'll add that to the default value. +In version 0.6, just use ``yas/global-mode`` to enable YASnippet in +all major modes. Or put ``yas/minor-mode-on`` in that modes hook. See +the `FAQ `_. + The Fallback ~~~~~~~~~~~~ @@ -644,8 +686,22 @@ example for ``c-mode`` to calculate the header file guard dynamically: #endif /* $1 */ -Tab Stops ---------- +From version 0.6.0, snippets expansions are run with some special +emacs-lisp variables bound. One of this is ``yas/selected-text``. You +can therefore define a snippet like: + +.. sourcecode:: text + + for ($1;$2;$3) { + `yas/selected-text`$0 + } + +to "wrap" the selected region inside your recently inserted +snippet. Alternatively, you can also customize the variable +``yas/wrap-around-region`` to ``t`` which will do this automatically. + +Fields of type "Tab Stop" +------------------------- Tab stops are fields that you can navigate back and forth by ``TAB`` and ``S-TAB`` [3]_. They are written by ``$`` followed with a @@ -659,8 +715,8 @@ fields. Here's a typical example: $0 -Placeholders ------------- +Fields of type "Placeholder" +---------------------------- Tab stops can have default values -- a.k.a placeholders. The syntax is like this: @@ -701,8 +757,8 @@ as the field and others mirrors. .. _transformations: -Transformations ---------------- +Mirrors with transformations +---------------------------- If the default value of a field starts with ``$``, then it is interpreted as the transformation code instead of default value. A transformation @@ -764,6 +820,79 @@ is not. Here's an snippet for rst title: .. [2] This is done when you call ``yas/initialize``. .. [3] Of course, this can be customized. +Fields with transformations +--------------------------- + +From version 0.6 on, you can also have lisp transformation inside +fields. These work mostly mirror transformations but are evaluated +when you first enter the field, after each change you make to the +field and also just before you exit the field. + +The syntax is also a tiny bit different, so that the parser can +distinguish between fields and mirrors. In the following example + +.. sourcecode:: text + + #define "${1:mydefine$(upcase yas/text)}" + +``mydefine`` gets automatically upcased to ``MYDEFINE`` once you enter +the field. As you type text, it gets filtered through the +transformation every time. + +Note that this is differentiated from a mirror with a transformation +by the existance of extra text between the ``:`` and the +transformation's ``$``. If you don't want this extra-text, you can use +two ``$``'s instead. + +.. sourcecode:: text + + #define "${1:$$(upcase yas/text)}" + +Please note that as soon as a transformation takes place, it changes +the value of the field and sets it its internal modification state to +``true``. As a consequence, the auto-deletion behaviour of normal +fields does not take place. This is by design. + +Choosing fields value from a list +--------------------------------- + +As mentioned, the field transformation is invoked just after you enter +the field, and with some useful variables bound, notably +``yas/field-modified-p`` and ``yas/moving-away-p``. Because of this +feature you can place a transformation in the primary field that lets +you select default values for it. + +The ``yas/choose-value`` does this work for you. For example: + +.. sourcecode:: text + +
+ $0 +
+ +See the definition of ``yas/choose-value`` to see how it was written +using the two variables. Also check out ``yas/verify-value`` for +another neat trick. + +Nested placeholder fields +------------------------- + +From version 0.6 on, you can also have nested placeholders of the type: + +.. sourcecode:: text + + $0 + +This allows you to choose if you want to give this ``div`` an ``id`` +attribute. If you tab forward after expanding it will let you change +"some_id" to whatever you like. Alternatively, you can just press +``C-d`` (which executes ``yas/skip-and-clear-or-delete-char``) and go +straight to the exit marker. + +By the way, ``C-d`` will only clear the field if you cursor is at the +beginning of the field *and* it hasn't been changed yet. Otherwise, it +performs the normal Emacs ``delete-char`` command. + Indenting --------- @@ -789,3 +918,7 @@ this to YASnippet. Here's an example of the usage: $0$> }$> +In 0.6.0 You should **not** need to use this feature although it's +supported for backward compatibility. Just set ``yas/indent-line`` to +``'auto``. + diff --git a/doc/faq.rst b/doc/faq.rst index abc7b58..aa8baf8 100644 --- a/doc/faq.rst +++ b/doc/faq.rst @@ -28,11 +28,15 @@ following code to your ``.emacs`` *before* loading YASnippet: .. sourcecode:: lisp - (setq yas/extra-mode-hooks '(the-major-mode)) + (add-hook 'the-major-mode-hook 'yas/minor-mode-on) where ``the-major-mode`` is the major mode in which ``yas/minor-mode`` isn't enabled by default. +From YASnippet 0.6 you can also use the command ``M-x +yas/global-mode`` to turn on YASnippet automatically for *all* major +modes. + If ``yas/minor-mode`` is on but the snippet still not expanded. Then try to see what command is bound to the ``TAB`` key: press ``C-h k`` and then press ``TAB``. Emacs will show you the result. diff --git a/yasnippet.el b/yasnippet.el index e016197..1bf7f3f 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -33,7 +33,7 @@ ;; (require 'yasnippet) ;; 2. Place the `snippets' directory somewhere. E.g: ~/.emacs.d/snippets ;; 3. In your .emacs file -;; (setq yas/root-directory "~/.emacs/snippets") +;; (setq yas/root-directory "~/.emacs/snippets") ;; (yas/load-directory yas/root-directory) ;; 4. To enable the YASnippet menu and tab-trigger expansion ;; M-x yas/minor-mode @@ -48,7 +48,7 @@ ;; ;; M-x yas/expand ;; -;; Try to expand snippets before point. In `yas/minor-mode', +;; Try to expand snippets before point. In `yas/minor-mode', ;; this is bound to `yas/trigger-key' which you can customize. ;; ;; M-x yas/load-directory @@ -59,7 +59,7 @@ ;; ;; Prompts you for possible snippet expansion if that is ;; possible according to buffer-local and snippet-local -;; expansion conditions. With prefix argument, ignore these +;; expansion conditions. With prefix argument, ignore these ;; conditions. ;; ;; M-x yas/find-snippets @@ -68,7 +68,7 @@ ;; snippet was loaded from (if it exists) like ;; `find-file-other-window'. ;; -;; M-x yas/find-snippet-file +;; M-x yas/visit-snippet-file ;; ;; Prompts you for possible snippet expasions like ;; `yas/insert-snippet', but instead of expanding it, takes @@ -77,10 +77,17 @@ ;; ;; M-x yas/load-snippet-buffer ;; -;; When editing a snippet, this loads the snippet. This is +;; When editing a snippet, this loads the snippet. This is ;; bound to "C-c C-c" while in the `snippet-mode' editing ;; mode. ;; +;; M-x yas/tryout-snippet +;; +;; When editing a snippet, this opens a new empty buffer, +;; sets it to the appropriate major mode and inserts the +;; snippet there, so you can see what it looks like. This is +;; bound to "C-c C-t" while in `snippet-mode'. +;; ;; The `dropdown-list.el' extension is bundled with YASnippet, you ;; can optionally use it the preferred "prompting method", puting in ;; your .emacs file, for example: @@ -92,9 +99,9 @@ ;; ;; Also check out the customization group ;; -;; M-x customize-group RET yasnippet RET +;; M-x customize-group RET yasnippet RET ;; -;; For more information and detailed usage, refer to the project page: +;; For more information and detailed usage, refer to the project page: ;; http://code.google.com/p/yasnippet/ ;;; Code: @@ -111,18 +118,18 @@ :group 'editing) (defcustom yas/root-directory nil - "The (list of) root directory that stores the snippets for each -major mode." + "Root directory that stores the snippets for each major mode. + +Can also be a list of strings, for multiple root directories." :type '(string) :group 'yasnippet) (defcustom yas/prompt-functions '(yas/x-prompt - yas/ido-prompt yas/dropdown-prompt yas/completing-prompt + yas/ido-prompt yas/no-prompt) - "List of functions to prompt the user for keys, templates and -other values interactively." + "Functions to prompt for keys, templates, etc interactively." :type 'list :group 'yasnippet) @@ -144,17 +151,15 @@ applies)." :group 'yasnippet) (defcustom yas/snippet-revival t - "Non-nil means re-activate snippet fields after an when undoing -an exit from an active snippet or redoing a snippet expansion" + "Non-nil means re-activate snippet fields after undo/redo." :type 'boolean :group 'yasnippet) (defcustom yas/trigger-key "TAB" - "The key to bind as a trigger of snippet when `yas/minor-mode' -is active. + "The key bound to `yas/expand' when function `yas/minor-mode' is active. Value is a string that is converted to the internal Emacs key -representation using `read-kbd-macro'. " +representation using `read-kbd-macro'." :type 'string :group 'yasnippet) @@ -162,7 +167,7 @@ representation using `read-kbd-macro'. " "The key to navigate to next field when a snippet is active. Value is a string that is converted to the internal Emacs key -representation using `read-kbd-macro'. " +representation using `read-kbd-macro'." :type 'string :group 'yasnippet) @@ -172,7 +177,7 @@ representation using `read-kbd-macro'. " Can also be a list of keys. Value is a string that is converted to the internal Emacs key -representation using `read-kbd-macro'. " +representation using `read-kbd-macro'." :type 'string :group 'yasnippet) @@ -180,15 +185,15 @@ representation using `read-kbd-macro'. " "The key to clear the currently active field. Value is a string that is converted to the internal Emacs key -representation using `read-kbd-macro'. " +representation using `read-kbd-macro'." :type 'string :group 'yasnippet) -(defcustom yas/triggers-in-field t - "If non-nil, allow `yas/next-field-key' to trigger a stacked - snippet expansion. +(defcustom yas/triggers-in-field nil + "If non-nil, `yas/next-field-key' can trigger stacked expansions. -Otherwise, `yas/next-field-key' just tries to move on to the next field" +Otherwise, `yas/next-field-key' just tries to move on to the next +field" :type 'boolean :group 'yasnippet) @@ -217,7 +222,7 @@ templates and inserts the selected one." (defcustom yas/use-menu t "Display a YASnippet menu in the menu bar. -If this is set to `t', all snippet template of the current +If this is set to t, all snippet template of the current mode will be listed under the menu \"yasnippet\"." :type 'boolean :group 'yasnippet) @@ -230,11 +235,11 @@ mode will be listed under the menu \"yasnippet\"." (defcustom yas/show-all-modes-in-menu nil "Display \"artificial\" major modes in menu bar as well. -Currently, YASnippet only all \"real modes\" to menubar. For +Currently, YASnippet only all \"real modes\" to menubar. For example, you define snippets for \"cc-mode\" and make it the -parent of `c-mode', `c++-mode' and `java-mode'. There's really -no such mode like \"cc-mode\". So we don't show it in the yasnippet -menu to avoid the menu becoming too big with strange modes. The +parent of `c-mode', `c++-mode' and `java-mode'. There's really +no such mode like \"cc-mode\". So we don't show it in the yasnippet +menu to avoid the menu becoming too big with strange modes. The snippets defined for \"cc-mode\" can still be accessed from menu-bar->c-mode->parent (or c++-mode, java-mode, all are ok). However, if you really like to show all modes in the menu, set @@ -242,15 +247,15 @@ this variable to t." :type 'boolean :group 'yasnippet) -(defcustom yas/wrap-around-region t +(defcustom yas/wrap-around-region nil "If non-nil, snippet expansion wraps around selected region. -The wrapping occurs just before the snippet's exit marker. This +The wrapping occurs just before the snippet's exit marker. This can be overriden on a per-snippet basis." :type 'boolean :group 'yasnippet) -(defcustom yas/good-grace nil +(defcustom yas/good-grace t "If non-nil, don't raise errors in inline elisp evaluation. An error string \"[yas] error\" is returned instead." @@ -278,7 +283,7 @@ An error string \"[yas] error\" is returned instead." "The keymap active while a snippet expansion is in progress.") (defun yas/define-some-keys (keys keymap definition) - "Bind KEYS to DEFINITION in KEYMAP, read with `read-kbd-macro'" + "Bind KEYS to DEFINITION in KEYMAP, read with `read-kbd-macro'." (let ((keys (or (and (listp keys) keys) (list keys)))) (dolist (key keys) @@ -378,13 +383,13 @@ Here's an example: ;; Internal variables ;; -(defvar yas/version "0.6.0-beta") +(defvar yas/version "0.6.0b") (defvar yas/snippet-tables (make-hash-table) - "A hash table of snippet tables corresponding to each major-mode.") + "A hash table of snippet tables corresponding to each major mode.") (defvar yas/menu-table (make-hash-table) - "A hash table of menus of corresponding major-mode.") + "A hash table of menus of corresponding major mode.") (defvar yas/known-modes '(ruby-mode rst-mode markdown-mode) @@ -392,28 +397,27 @@ Here's an example: (defvar yas/escaped-characters '(?\\ ?` ?' ?$ ?} ) - "A list of characters which *might*n need to be escaped in -snippet templates") + "List of characters which *might* need to be escaped.") (defconst yas/field-regexp "${\\([0-9]+:\\)?\\([^}]*\\)}" - "A regexp to *almost* recognize a field") + "A regexp to *almost* recognize a field.") (defconst yas/multi-dollar-lisp-expression-regexp "$+[ \t\n]*\\(([^)]*)\\)" - "A regexp to *almost* recognize a \"$(...)\" expression") + "A regexp to *almost* recognize a \"$(...)\" expression.") (defconst yas/backquote-lisp-expression-regexp "`\\([^`]*\\)`" - "A regexp to recognize a \"`lisp-expression`\" expression" ) + "A regexp to recognize a \"`lisp-expression`\" expression." ) (defconst yas/transform-mirror-regexp "${\\(?:\\([0-9]+\\):\\)?$\\([^}]*\\)" - "A regexp to *almost* recognize a mirror with a transform") + "A regexp to *almost* recognize a mirror with a transform.") (defconst yas/simple-mirror-regexp "$\\([0-9]+\\)" - "A regexp to recognize a simple mirror") + "A regexp to recognize a simple mirror.") (defvar yas/snippet-id-seed 0 "Contains the next id for a snippet.") @@ -428,7 +432,7 @@ snippet templates") ;; (defvar yas/minor-mode-map (make-sparse-keymap) - "The keymap used when `yas/minor-mode' is active.") + "The keymap used when function `yas/minor-mode' is active.") (defun yas/init-keymap-and-menu () (easy-menu-define yas/minor-mode-menu @@ -552,7 +556,7 @@ TEMPLATES is a list of cons (KEY . TEMPLATE) where KEY is a string and TEMPLATE is a `yas/template' structure. This function implements the rules described in -`yas/buffer-local-condition'. See that variables documentation." +`yas/buffer-local-condition'. See that variables documentation." (let ((requirement (yas/require-template-specific-condition-p))) (if (eq requirement 'always) templates @@ -563,7 +567,7 @@ This function implements the rules described in (cond ((eq requirement t) result) (t - (eq requirement result))))) + (eq requirement result))))) templates)))) (defun yas/snippet-table-fetch (table key) @@ -599,7 +603,7 @@ fetch from parent if any." (yas/snippet-table-all-keys (yas/snippet-table-parent table)))))) (defun yas/snippet-table-store (table full-key key template) - "Store a snippet template in the table." + "Store a snippet template in the TABLE." (puthash key (yas/modify-alist (gethash key (yas/snippet-table-hash table)) @@ -913,6 +917,7 @@ TEMPLATES is a list of `yas/template'." (defun yas/load-directory (directory) "Load snippet definition from a directory hierarchy. + Below the top-level directory, each directory is a mode name. And under each subdirectory, each file is a definition of a snippet. The file name is the trigger key and the @@ -940,7 +945,7 @@ content of the file is the template." (yas/global-mode -1) (setq restore-global-mode t)) - (when yas/minor-mode + (when yas/minor-mode (yas/minor-mode -1) (setq restore-minor-mode t)) @@ -975,7 +980,7 @@ foo\"bar\\! -> \"foo\\\"bar\\\\!\"" ;;; Yasnipept Bundle (defun yas/initialize () - "For backward compatibility, enable `yas/minor-mode' globally" + "For backward compatibility, enable `yas/minor-mode' globally" (yas/global-mode 1)) (defun yas/compile-bundle @@ -1148,7 +1153,7 @@ directory to find snippet files. (not (string= "" group))) (dolist (subgroup (mapcar #'make-symbol (split-string group "\\."))) - (let ((subgroup-keymap (lookup-key group-keymap + (let ((subgroup-keymap (lookup-key group-keymap (vector subgroup)))) (when (null subgroup-keymap) (setq subgroup-keymap (make-sparse-keymap)) @@ -1289,8 +1294,11 @@ by condition." (yas/template-env template)) (message "[yas] No snippets can be inserted here!")))) -(defun yas/find-snippet-file () - "Choose a snippet to edit, selection like `yas/insert-snippet'." +(defun yas/visit-snippet-file () + "Choose a snippet to edit, selection like `yas/insert-snippet'. + +Only success if selected snippet was loaded from a file. Put the +visited file in `snippet-mode'." (interactive) (let* ((yas/buffer-local-condition 'always) (templates (mapcar #'cdr @@ -1317,7 +1325,7 @@ by condition." (message "This snippet was not loaded from a file!"))))))) (defun yas/guess-snippet-directory () - "Try to guess the suitable yassnippet based on `major-mode'" + "Try to guess the suitable yassnippet based on `major-mode'" (let ((loaded-root (or (and (listp yas/root-directory) (first yas/root-directory)) yas/root-directory)) @@ -1336,13 +1344,16 @@ by condition." (defun yas/find-snippets (&optional same-window) - "Find a snippet file in a suitable directory." + "Looks for snippets file in the current mode's directory. + +This can be used to create new snippets for the currently active +major mode." (interactive "P") (let* ((current-table (yas/current-snippet-table major-mode 'dont-search-parents)) (parents-table (yas/current-snippet-table major-mode)) (parents-directory (and parents-table (yas/snippet-table-default-directory parents-table))) - (guessed-directory (or (and current-table + (guessed-directory (or (and current-table (yas/snippet-table-default-directory current-table)) (yas/guess-snippet-directory) default-directory)) @@ -1392,7 +1403,7 @@ by condition." (message "Save the buffer as a file first!"))) (defun yas/tryout-snippet (&optional debug) - "Parse and load current buffer's snippet definition." + "Test current buffers's snippet template in other buffer." (interactive "P") (let* ((major-mode-and-parent (or (and buffer-file-name (yas/compute-major-mode-and-parent buffer-file-name)) @@ -1525,10 +1536,10 @@ for this field, apply it. Otherwise, returned nil." (text yas/text) (yas/modified-p (yas/field-modified-p field)) (yas/moving-away-p nil) - (transform (if (yas/mirror-p field-or-mirror) + (transform (if (yas/mirror-p field-or-mirror) (yas/mirror-transform field-or-mirror) (yas/field-transform field-or-mirror))) - (start-point (if (yas/mirror-p field-or-mirror) + (start-point (if (yas/mirror-p field-or-mirror) (yas/mirror-start field-or-mirror) (yas/field-start field-or-mirror))) (transformed (and transform @@ -1543,7 +1554,7 @@ for this field, apply it. Otherwise, returned nil." (while (search-forward from nil t) (replace-match to t t))) -(defun yas/snippet-find-field (snippet number) +(defun yas/snippet-find-field (snippet number) (find-if #'(lambda (field) (eq number (yas/field-number field))) (yas/snippet-fields snippet))) @@ -1564,7 +1575,7 @@ have, compare through the field's start point" (yas/field-start field2)))))) (defun yas/field-probably-deleted-p (field) - "Guess if FIELD was deleted because of his parent-field" + "Guess if FIELD was deleted because of his parent-field" (and (zerop (- (yas/field-start field) (yas/field-end field))) (yas/field-parent-field field))) @@ -1637,7 +1648,7 @@ Also create some protection overlays" (setf (yas/snippet-active-field snippet) field) (yas/place-overlays snippet field) (overlay-put yas/active-field-overlay 'yas/field field) - ;;; primary field transform: first call to snippet transform + ;;; primary field transform: first call to snippet transform (unless (yas/field-modified-p field) (if (yas/field-update-display field snippet) (let ((inhibit-modification-hooks t)) @@ -1741,7 +1752,7 @@ NO-HOOKS means don't run the `yas/after-exit-snippet-hook' hooks." (when (and yas/snippet-end previous-field) (yas/advance-field-end-marker previous-field yas/snippet-end))) - ;; Convert all markers to points, + ;; Convert all markers to points, ;; (yas/markers-to-points snippet) @@ -1770,7 +1781,7 @@ snippet, if so cleans up the whole snippet up." (let* ((snippets (yas/snippets-at-point 'all-snippets)) (snippets-left snippets)) (dolist (snippet snippets) - (let ((active-field (yas/snippet-active-field snippet))) + (let ((active-field (yas/snippet-active-field snippet))) (cond ((or (prog1 (yas/snippet-force-exit snippet) (setf (yas/snippet-force-exit snippet) nil)) (not (and active-field (yas/field-contains-point-p active-field)))) @@ -1820,7 +1831,7 @@ snippet, if so cleans up the whole snippet up." (when target-field (yas/move-to-field snippet target-field)))) ((not (yas/undo-in-progress)) - ;; When not in an undo, check if we must commit the snippet (use exited it). + ;; When not in an undo, check if we must commit the snippet (use exited it). (yas/check-commit-snippet)))) (defun yas/field-text-for-display (field) @@ -1839,7 +1850,7 @@ holds the keymap." (let ((overlay (make-overlay start end nil - nil + nil t))) (overlay-put overlay 'keymap yas/keymap) (overlay-put overlay 'yas/snippet snippet) @@ -1847,8 +1858,9 @@ holds the keymap." overlay)) (defun yas/skip-and-clear-or-delete-char (&optional field) - "Clears an unmodified field if at field start, otherwise -deletes a character normally." + "Clears unmodified field if at field start, skips to next tab. + +Otherwise deletes a character normally by calling `delete-char'." (interactive) (let ((field (or field (and yas/active-field-overlay @@ -1863,7 +1875,7 @@ deletes a character normally." (call-interactively 'delete-char))))) (defun yas/skip-and-clear (field) - "Deletes the region of FIELD and sets it modified state to t" + "Deletes the region of FIELD and sets it modified state to t" (setf (yas/field-modified-p field) t) (delete-region (yas/field-start field) (yas/field-end field))) @@ -2137,7 +2149,7 @@ After revival, push the `yas/take-care-of-redo' in the (defun yas/snippet-create (begin end) "Creates a snippet from an template inserted between BEGIN and END. -Returns the newly created snippet." +Returns the newly created snippet." (let ((snippet (yas/make-snippet))) (goto-char begin) (yas/snippet-parse-create snippet) @@ -2206,7 +2218,7 @@ Meant to be called in a narrowed buffer, does various passes" ;; (goto-char parse-start) (yas/protect-escapes) - ;; parse fields with {} + ;; parse fields with {} ;; (goto-char parse-start) (yas/field-parse-create snippet) @@ -2234,7 +2246,7 @@ Meant to be called in a narrowed buffer, does various passes" (save-excursion (while (re-search-forward "$>" nil t) (delete-region (match-beginning 0) (match-end 0)) - (when (not (eq yas/indent-line 'auto)) + (when (not (eq yas/indent-line 'auto)) (indent-according-to-mode)))) (save-excursion (cond ((eq yas/indent-line 'fixed) @@ -2380,7 +2392,7 @@ When multiple expressions are found, only the last one counts." (while (re-search-forward yas/multi-dollar-lisp-expression-regexp nil t) (let* ((real-match-end-1 (yas/scan-sexps (match-beginning 1) 1))) (when real-match-end-1 - (let ((lisp-expression-string (buffer-substring-no-properties (match-beginning 1) real-match-end-1))) + (let ((lisp-expression-string (buffer-substring-no-properties (match-beginning 1) real-match-end-1))) (setf (yas/field-transform parent-field) lisp-expression-string)) (delete-region (match-beginning 0) real-match-end-1))))))) @@ -2393,7 +2405,7 @@ When multiple expressions are found, only the last one counts." (not (zerop number)) (yas/snippet-find-field snippet number)))) (when (and real-match-end-0 - field) + field) (push (yas/make-mirror (yas/make-marker (match-beginning 0)) (yas/make-marker (match-beginning 0)) (buffer-substring-no-properties (match-beginning 2) @@ -2440,7 +2452,7 @@ When multiple expressions are found, only the last one counts." (let ((inhibit-modification-hooks t)) (yas/mirror-update-display mirror field) ;; Take care of the fields adjacent to this mirror's back - ;; TODO: Known bug + ;; TODO: Known bug ;; `yas/place-overlays' is needed if the active field and ;; protected overlays have been changed because of insertions @@ -2482,6 +2494,7 @@ When multiple expressions are found, only the last one counts." ;; (defun yas/debug-some-vars () + "Debug snippets, fields, mirrors and the `buffer-undo-list'." (interactive) (with-output-to-temp-buffer "*YASnippet trace*" (princ "Interesting YASnippet vars: \n\n") From b90926499a6606783477d4fa33311aeb35fad750 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Sat, 25 Jul 2009 10:50:40 +0000 Subject: [PATCH 74/75] * escaping now works inside lisp expressions * new keybindings * minor fixes * more doc --- yasnippet.el | 120 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 45 deletions(-) diff --git a/yasnippet.el b/yasnippet.el index 1bf7f3f..cdf2d5d 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -70,10 +70,10 @@ ;; ;; M-x yas/visit-snippet-file ;; -;; Prompts you for possible snippet expasions like +;; Prompts you for possible snippet expansions like ;; `yas/insert-snippet', but instead of expanding it, takes ;; you directly to the snippet definition's file, if it -;; exits. +;; exists. ;; ;; M-x yas/load-snippet-buffer ;; @@ -430,6 +430,8 @@ Here's an example: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Minor mode stuff ;; +;; TODO: XXX: This is somehow needed in Carbon Emacs for MacOSX +(defvar last-buffer-undo-list nil) (defvar yas/minor-mode-map (make-sparse-keymap) "The keymap used when function `yas/minor-mode' is active.") @@ -445,7 +447,9 @@ Here's an example: (vector (first ent) (second ent) t)) (list (list "--") (list "Expand trigger" 'yas/expand (read-kbd-macro yas/trigger-key)) - (list "Insert at point" 'yas/insert-snippet "\C-c&\C-s") + (list "Insert at point..." 'yas/insert-snippet "\C-c&\C-s") + (list "Visit snippet file..." 'yas/visit-snippet-file "\C-c&\C-v") + (list "Find snippets..." 'yas/find-snippets "\C-c&\C-f") (list "About" 'yas/about) (list "Reload-all-snippets" 'yas/reload-all) (list "Load snippets..." 'yas/load-directory)))))) @@ -1024,6 +1028,8 @@ Here's the default value for all the parameters: (insert ";;; yasnippet-bundle.el --- " "Yet another snippet extension (Auto compiled bundle)\n") (insert-file-contents yasnippet) + (goto-char (point-max)) + (insert "\n") (when dropdown (insert-file-contents dropdown)) (goto-char (point-max)) @@ -1375,40 +1381,54 @@ major mode." (snippet-mode))))))) -(defun yas/compute-major-mode-and-parent (file) - (let* ((file-dir (directory-file-name (file-name-directory file))) - (major-mode-name (file-name-nondirectory file-dir)) - (parent-file-dir (directory-file-name (file-name-directory file-dir))) - (parent-mode-name (file-name-nondirectory parent-file-dir)) - (major-mode-sym (intern major-mode-name)) - (parent-mode-sym (intern parent-mode-name))) - (when (fboundp major-mode-sym) - (cons major-mode-sym - (when (fboundp parent-mode-sym) - parent-mode-sym))))) +(defun yas/compute-major-mode-and-parent (file &optional prompt-if-failed) + (let* ((file-dir (and file + (directory-file-name (file-name-directory file)))) + (major-mode-name (and file-dir + (file-name-nondirectory file-dir))) + (parent-file-dir (and file-dir + (directory-file-name (file-name-directory file-dir)))) + (parent-mode-name (and parent-file-dir + (file-name-nondirectory parent-file-dir))) + (major-mode-sym (or (and major-mode-name + (intern major-mode-name)) + (when prompt-if-failed + (read-from-minibuffer "[yas] Cannot auto-detect major mode! Enter a major mode: ")))) + (parent-mode-sym (and parent-mode-name + (intern parent-mode-name)))) + (if (fboundp major-mode-sym) + (cons major-mode-sym + (when (fboundp parent-mode-sym) + parent-mode-sym))))) (defun yas/load-snippet-buffer (&optional kill) - "Parse and load current buffer's snippet definition." + "Parse and load current buffer's snippet definition. + +With optional prefix argument KILL quit the window and buffer." (interactive "P") (if buffer-file-name (let ((major-mode-and-parent (yas/compute-major-mode-and-parent buffer-file-name))) - (when major-mode-and-parent - (yas/define-snippets (car major-mode-and-parent) - (list (yas/parse-template buffer-file-name)) - (cdr major-mode-and-parent))) - (when (and (buffer-modified-p) - (y-or-n-p "Save snippet? ")) - (save-buffer)) - (quit-window)) + (if major-mode-and-parent + (let* ((parsed (yas/parse-template buffer-file-name)) + (name (and parsed + (third parsed)))) + (when name + (yas/define-snippets (car major-mode-and-parent) + (list parsed) + (cdr major-mode-and-parent)) + (when (and (buffer-modified-p) + (y-or-n-p "Save snippet? ")) + (save-buffer)) + (if kill + (quit-window kill) + (message "[yas] Snippet \"%s\" loaded for %s." name (car major-mode-and-parent))))) + (message "[yas] Cannot load snippet for unknown major mode"))) (message "Save the buffer as a file first!"))) (defun yas/tryout-snippet (&optional debug) "Test current buffers's snippet template in other buffer." (interactive "P") - (let* ((major-mode-and-parent (or (and buffer-file-name - (yas/compute-major-mode-and-parent buffer-file-name)) - (cons (intern (read-from-minibuffer "Cannot auto-detect major mode! Enter a major mode: ")) - nil))) + (let* ((major-mode-and-parent (yas/compute-major-mode-and-parent buffer-file-name)) (parsed (and major-mode-and-parent (fboundp (car major-mode-and-parent)) (yas/parse-template (symbol-name (car major-mode-and-parent))))) @@ -1480,17 +1500,16 @@ Otherwise throw exception." ;;; Snippet expansion and field management (defvar yas/active-field-overlay nil - "Overlays the currently active field") + "Overlays the currently active field.") (defvar yas/field-protection-overlays nil - "Two overlays protect the current actipve field ") + "Two overlays protect the current active field ") (defvar yas/deleted-text nil "The text deleted in the last snippet expansion.") (defvar yas/selected-text nil - "The selected region deleted before the last snippet - expansion.") + "The selected region deleted on the last snippet expansion.") (defvar yas/start-column nil "The column where the snippet expansion started.") @@ -1548,11 +1567,15 @@ for this field, apply it. Otherwise, returned nil." (yas/eval-string transform))))) transformed)) -(defsubst yas/replace-all (from to) - "Replace all occurance from FROM to TO." +(defsubst yas/replace-all (from to &optional text) + "Replace all occurance from FROM to TO. + +With optional string TEXT do it in that string." (goto-char (point-min)) - (while (search-forward from nil t) - (replace-match to t t))) + (if text + (replace-regexp-in-string from to text t t) + (while (search-forward from nil t) + (replace-match to t t text)))) (defun yas/snippet-find-field (snippet number) (find-if #'(lambda (field) @@ -2326,12 +2349,19 @@ Meant to be called in a narrowed buffer, does various passes" (yas/escape-string escaped))) (or escaped yas/escaped-characters))) -(defun yas/restore-escapes () - "Restore all escaped characters from their numeric ASCII value." - (mapc #'(lambda (escaped) - (yas/replace-all (yas/escape-string escaped) - (char-to-string escaped))) - yas/escaped-characters)) +(defun yas/restore-escapes (&optional text) + "Restore all escaped characters from their numeric ASCII value. + +With optional string TEXT do it in string instead" + (let ((changed-text text) + (text-provided-p text)) + (mapc #'(lambda (escaped) + (setq changed-text + (yas/replace-all (yas/escape-string escaped) + (char-to-string escaped) + (when text-provided-p changed-text)))) + yas/escaped-characters) + changed-text)) (defun yas/replace-backquotes () "Replace all the \"`(lisp-expression)`\"-style expression @@ -2339,7 +2369,7 @@ Meant to be called in a narrowed buffer, does various passes" (while (re-search-forward yas/backquote-lisp-expression-regexp nil t) (let ((transformed (yas/eval-string (match-string 1)))) (goto-char (match-end 0)) - (insert transformed) + (when transformed (insert transformed)) (delete-region (match-beginning 0) (match-end 0))))) (defun yas/scan-sexps (from count) @@ -2393,7 +2423,7 @@ When multiple expressions are found, only the last one counts." (let* ((real-match-end-1 (yas/scan-sexps (match-beginning 1) 1))) (when real-match-end-1 (let ((lisp-expression-string (buffer-substring-no-properties (match-beginning 1) real-match-end-1))) - (setf (yas/field-transform parent-field) lisp-expression-string)) + (setf (yas/field-transform parent-field) (yas/restore-escapes lisp-expression-string))) (delete-region (match-beginning 0) real-match-end-1))))))) (defun yas/transform-mirror-parse-create (snippet) @@ -2408,8 +2438,8 @@ When multiple expressions are found, only the last one counts." field) (push (yas/make-mirror (yas/make-marker (match-beginning 0)) (yas/make-marker (match-beginning 0)) - (buffer-substring-no-properties (match-beginning 2) - (1- real-match-end-0))) + (yas/restore-escapes (buffer-substring-no-properties (match-beginning 2) + (1- real-match-end-0)))) (yas/field-mirrors field)) (delete-region (match-beginning 0) real-match-end-0))))) From f40a013dec1551bfbad0caedbea189677ae74fc3 Mon Sep 17 00:00:00 2001 From: capitaomorte Date: Sat, 25 Jul 2009 11:14:22 +0000 Subject: [PATCH 75/75] Last commit before releasing 0.6.0 --- Rakefile | 2 +- doc/changelog.html | 318 ++++++++++ doc/changelog.rst | 2 +- doc/define_snippet.html | 900 +++++++++++++++++++++++++++++ doc/define_snippet.rst | 51 +- doc/faq.html | 110 ++++ doc/images/customization-group.png | Bin 0 -> 54785 bytes doc/images/idrop-menu.png | Bin 0 -> 10294 bytes doc/index.html | 151 +++++ doc/index.rst | 21 +- snippets/text-mode/ruby-mode/cls | 8 +- yasnippet.el | 5 +- 12 files changed, 1553 insertions(+), 15 deletions(-) create mode 100644 doc/changelog.html create mode 100644 doc/define_snippet.html create mode 100644 doc/faq.html create mode 100644 doc/images/customization-group.png create mode 100644 doc/images/idrop-menu.png create mode 100644 doc/index.html diff --git a/Rakefile b/Rakefile index 7d22c43..04cb216 100644 --- a/Rakefile +++ b/Rakefile @@ -3,7 +3,7 @@ require 'fileutils' def find_version - File.read("yasnippet.el") =~ /;; Version: *([0-9.]+[a-z]?) *$/ + File.read("yasnippet.el") =~ /;; Package-version: *([0-9.]+[a-z]?) *$/ $version = $1 end find_version diff --git a/doc/changelog.html b/doc/changelog.html new file mode 100644 index 0000000..8dec07f --- /dev/null +++ b/doc/changelog.html @@ -0,0 +1,318 @@ + + + + + + +ChangeLog + + + + + +
+
+
+
+ +
+
+
+
+
+

0.6.0b / 2009-07-2x

+
    +
  • Nested placeholders of the type <div${1: id="${2:someid}"}> $0.
  • +
  • More robust undo/redo support.
  • +
  • Stacked snippet expansion (snippet in snippet).
  • +
  • Transformation on a primary field with syntax ${1:default$(transform)}
  • +
  • Validations on field exit through the yas/verify-value +primary field transformation.
  • +
  • Wrapping the region in the exit marker $0 of the snippet. Use +yas/wrap-around-region.
  • +
  • Auto-indentation. Use yas/indent-line set to 'auto
  • +
  • Easier definition of snippets. Use yas/find-snippets or +yas/visit-snippet-file. In the new snippet-mode use +yas/load-snippet-buffer and yas/tryout-snippet.
  • +
  • Customization group yasnippet.
  • +
  • Overriding customization variables in snippets. Use the env: +let-form template keyword.
  • +
  • Fixed Issue 60
  • +
  • Fixed Issue 65
  • +
  • Fixed Issue 56
  • +
+
+
+

0.5.10 / 2009-02-11

+
    +
  • Added grouping support so that the snippets in the menu can be +groupped together.
  • +
  • Make the bundle ELPA +compatible.
  • +
+
+
+

0.5.9 / 2009-01-21

+
    +
  • Fixed the bug of disabling the auto-indenting of cc-mode.
  • +
+
+
+

0.5.8 / 2009-01-15

+
    +
  • Added a key property in snippet definition for snippet names +that are not valid path name.
  • +
  • Fixed some bugs of indenting (Issue 44, Issue +46).
  • +
  • Fixed Issue 45 by +providing a proper default value for yas/buffer-local-condition.
  • +
  • Added helper function yas/substr for convenient mirror +transformation.
  • +
  • Make variable yas/registered-snippet properly initialized.
  • +
  • Fixed the overlay error when overlay becomes empty (Issue 49 and +Issue 48). This +bug has occurred and been fixed earlier, and should not have +happened if we have proper regression test.
  • +
  • Added a workaround for c-electric- serial commands (Issue 27).
  • +
+
+
+

0.5.7 / 2008-12-03

+
    +
  • Fixed Issue 28 of +properly clean up snippet (by joaotavora).
  • +
  • Added a new section "Field-level undo functionality" to correct +Issue 33 +(by joaotavora).
  • +
  • Added some snippets from users for sql, erlang, scala, html, xml, latex, etc.
  • +
  • Fixed Issue 16 by adding +$> support. Here's the doc for $> indenting.
  • +
+
+
+

0.5.6 / 2008-08-07

+
    +
  • Added a buffer local variable yas/dont-activate to turn off +yas/minor-mode in some major modes. See Issue 29.
  • +
  • Make the environment of elisp evaluation more friendly to +(current-column).
  • +
  • Fixed the regular expression bug in python-mode snippets.
  • +
  • Use filename or full key extension for snippet name if no name +property is defined.
  • +
+
+
+

0.5.5 / 2008-05-29

+
    +
  • Tweak yas/extra-mode-hooks so that it can be more easily +customized.
  • +
  • Add an entry in FAQ about why TAB key doesn't work in some +modes.
  • +
+
+
+

0.5.4 / 2008-05-15

+
    +
  • Added ox-mode-hook and python-mode-hook to +yas/extra-mode-hooks to fix the problem YASnippet is not enabled +in those modes.
  • +
+
+
+

0.5.3 / 2008-05-07

+
    +
  • Fix indent of python-mode snippets.
  • +
  • Fix a bug of dropdown-list: conflicts with color-theme (Issue 23). Thanks +Mike.
  • +
  • Fix a bug of condition system.
  • +
+
+
+

0.5.2 / 2008-04-20

+
    +
  • Fix a bug for comparing string to symbol using string= (which +will fire an error).
  • +
+
+
+

0.5.1 / 2008-04-14

+
    +
  • Use a beautiful css style in the document.
  • +
+
+
+

0.5.0 / 2008-04-10

+
    +
  • Integrate with hippie-expand. Just add yas/hippie-try-expand to +hippie-expand-try-functions-list.
  • +
  • If you set yas/fall-back-behavior to 'return-nil, YASnippet +will return nil when it can't find a snippet to expand.
  • +
  • Defect fix: the condition of a snippet was evaluated twice in +earlier version.
  • +
  • Deleting snippet (using C-w or C-k) won't cause serious +problem now.
  • +
  • Several complex snippet for python-mode from Yasser included in the +distribution.
  • +
+
+
+

0.4.5 / 2008-04-07

+
    +
  • Merge the latest dropdown-list.el.
  • +
  • Add snippets for f90-mode from Li Zhu.
  • +
  • Bug fix: l-safe-expr-p: Lisp nesting exceeds max-lisp-eval-depth +error when several (more than two) snippets overlaps. Thanks +sunwaybupt@newsmth for reporting this bug.
  • +
+
+
+

0.4.4 / 2008-03-24

+
    +
  • Bug fix: dropdown-list.el doesn't recognize [return] properly.
  • +
+
+
+

0.4.3 / 2008-03-23

+
    +
  • Bug fix: failed to recognize user customized yas/trigger-key.
  • +
+
+
+

0.4.2 / 2008-03-22

+
    +
  • Make a separate document package for release. Also make document +available online.
  • +
+
+
+

0.4.1 / 2008-03-21

+
    +
  • Make sure yas/minor-mode's key bindings always take priority to +other minor modes.
  • +
+
+
+

0.4.0 / 2008-03-20

+
    +
  • Document refinement and released with YASnippet. Most of the Online +wiki document will be deprecated soon.
  • +
  • Powerful condition system added to yasnippet!
  • +
  • Incorporate dropdown-list.el and make it default way for +selecting multiple candidates. Thanks to Jaeyoun Chung.
  • +
  • yas/before-expand-snippet-hook
  • +
+
+
+

0.3.2 / 2008-03-19

+
    +
  • Enhancement: A better way to define minor-mode. Thanks to Kentaro +Kuribayashi. See this thread +for more details.
  • +
+
+
+

0.3.1 / 2008-03-17

+
    +
  • Bug fix: Emacs get confused when a field is deleted. See issue 10.
  • +
+
+
+

0.3.0 / 2008-03-16

+
    +
  • Add a yas/after-exit-snippet-hook so that you can do something like +indent-region or fill-region after finish the snippet.
  • +
  • Use minor-mode instead of global-set-key to bind the trigger +key. Now the trigger key and fall-back behavior can be more +flexible. Not constrained to <tab>. Thanks to Trey Jackson. See +this thread +for more details.
  • +
  • Now user can customize the popup function for selecting multiple +candidate for the same snippet key.
  • +
  • Support dropdown-list.el to be a better way to select multiple +candidate when in text mode.
  • +
+
+
+

0.2.3 / 2008-03-15

+
    +
  • Bug in non-window (-nw) mode when there's multiple candidate to +expand. See issue 7.
  • +
  • Allow expanding another snippet as long as not currently inside a +field.
  • +
+
+
+

0.2.2 / 2008-03-13

+
    +
  • Added customized face for fields and mirrors. Better in dark +background. And users can customize it.
  • +
+
+
+

0.2.1 / 2008-03-10

+
    +
  • Fix the insert-behind problem under both Emacs 22 and Emacs 23.
  • +
+
+
+

0.2.0 / 2008-03-10

+
    +
  • Use big keymap overlay to detect insert-behind event manually to +avoid sometimes missed hook calls. See issue 3 for more +details.
  • +
  • Support parent snippet table. Now you can set (for example) +cc-mode as common mode for c++-mode, c-mode and +java-mode. They'll share snippets defined for cc-mode.
  • +
+
+
+

0.1.1 / 2008-03-08

+
    +
  • Add a rake task to upload to google code.
  • +
  • Use elisp compile-bundle function instead of python scrip
  • +
+
+
+

0.1.0 / 2008-03-07

+
    +
  • Embedded elisp support.
  • +
  • Fields navigation support.
  • +
  • Mirror of fields support.
  • +
  • Menu-bar support.
  • +
  • Multiple snippets with same name support.
  • +
  • Popup menu for multiple snippet with same name support.
  • +
  • Transformation of fields support.
  • +
  • Load directory support.
  • +
  • Compile bundle support.
  • +
+
+
+
+
+
+
+
+
+ + diff --git a/doc/changelog.rst b/doc/changelog.rst index 58a627b..5a762a3 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -7,7 +7,7 @@ ChangeLog :Date: 2008-03-22 -0.6.0b / 2009-07-2x +0.6.0b / 2009-07-25 =================== * Nested placeholders of the type `` $0``. diff --git a/doc/define_snippet.html b/doc/define_snippet.html new file mode 100644 index 0000000..d4fa347 --- /dev/null +++ b/doc/define_snippet.html @@ -0,0 +1,900 @@ + + + + + + +How to define a snippet ? + + + + + +
+
+
+
+ +
+
+
+
+ +

The most convenient way to define snippets for YASnippet is to put +them in a directory arranged by the mode and use +yas/load-directory to load them.

+

However, this might slow down the Emacs startup speed if you have many +snippets. You can use yas/define-snippets to define a bunch of +snippets for a perticular mode. But this is hard to maintain! So, +there's a better way: define your snippets in directory and use +yas/compile-bundle to compile it into a bundle file when you +modified your snippets.

+

The release bundle of YASnippet is produced by +yas/compile-bundle. The bundle use yas/define-snippets to +define snippets. This avoid the IO and parsing overhead when loading +snippets.

+

Finally, you can use yas/define to define a single snippet at your +convenience. I ofthen use this to do some testing.

+
+

Define snippets in files

+
+

Directory hierarchy

+

Here's the directory hierarchy of the snippets directory comes +with YASnippet:

+
snippets
+`-- text-mode/
+    |-- cc-mode/
+    |   |-- c++-mode/
+    |   |   |-- beginend
+    |   |   |-- class
+    |   |   `-- using
+    |   |-- c-mode/
+    |   |   `-- fopen
+    |   |-- do
+    |   |-- for
+    |   |-- if
+    |   |-- inc
+    |   |-- inc.1
+    |   |-- main
+    |   |-- once
+    |   `-- struct
+    |-- css-mode/
+    |   |-- background
+    |   |-- background.1
+    |   `-- border
+    |-- email
+    |-- html-mode/
+    |   |-- div
+    |   |-- doctype
+    |   |-- doctype.xhml1
+    |   |-- doctype.xhtml1_1
+    |   |-- doctype.xhtml1_strict
+    |   `-- doctype.xhtml1_transitional
+    |-- objc-mode/
+    |   `-- prop
+    |-- perl-mode/
+    |   |-- cperl-mode/
+    |   |-- eval
+    |   |-- for
+    |   |-- fore
+    |   |-- if
+    |   |-- ife
+    |   |-- ifee
+    |   |-- sub
+    |   |-- unless
+    |   |-- while
+    |   |-- xfore
+    |   |-- xif
+    |   |-- xunless
+    |   `-- xwhile
+    |-- python-mode/
+    |   |-- __
+    |   |-- class
+    |   |-- def
+    |   |-- for
+    |   |-- ifmain
+    |   `-- while
+    |-- rst-mode/
+    |   |-- chapter
+    |   |-- section
+    |   `-- title
+    |-- ruby-mode/
+    |   |-- #
+    |   |-- =b
+    |   |-- Comp
+    |   |-- all
+    |   |-- am
+    |   |-- any
+    |   |-- app
+    |   |-- bm
+    |   |-- case
+    |   |-- cla
+    |   |-- classify
+    |   |-- cls
+    |   |-- collect
+    |   |-- dee
+    |   |-- deli
+    |   |-- det
+    |   |-- ea
+    |   |-- eac
+    |   |-- eai
+    |   |-- eav
+    |   |-- eawi
+    |   |-- forin
+    |   |-- if
+    |   |-- ife
+    |   |-- inject
+    |   |-- mm
+    |   |-- r
+    |   |-- rb
+    |   |-- reject
+    |   |-- req
+    |   |-- rreq
+    |   |-- rw
+    |   |-- select
+    |   |-- w
+    |   |-- y
+    |   `-- zip
+    `-- time
+
+

Snippet definitions are put in plain text files. They are arranged by +subdirectories. For example, snippets for c-mode are put in the +c-mode directory.

+

The parent directory acts as the parent mode. This is the way of +YASnippet to share snippet definitions among different modes. As you +can see above, c-mode and c++-mode share the same parents +cc-mode, while all modes are derived from text-mode. This can +be also used to as an alias -- cperl-mode is an empty directory +whose parent is perl-mode.

+

File names act as the snippet trigger key. Note files starting with a +dot (.) are ignored.

+
+
+

File content

+

A file defining a snippet may just contain the template for the +snippet. Optionally it can also contains some meta data for the +snippet as well as comments.

+

Generally speaking, if the file contains a line of # --, then all +contents above that line are considered as meta data and comments; +below are template. Or else the whole file content is considered as +the template.

+

Here's a typical example:

+
#contributor : pluskid <pluskid@gmail.com>
+#name : __...__
+# --
+__${init}__
+
+

Meta data are specified in the syntax of

+
#data-name : data value
+
+

Any other text above # -- is considered as comment and +ignored. Here's a list of currently supported meta data:

+images/group.png +
    +
  • name: The name of the snippet. This is a one-line description of +the snippet. It will be displayed in the menu. So it's a good idea +to select a descriptive name fo a snippet -- especially +distinguishable among similar snippets.
  • +
  • contributor: The contributor of the snippet.
  • +
  • condition: The condition of the snippet. This is a piece of +elisp code. If a snippet has a condition, then it will only be +expanded when the condition code evaluate to some non-nil value.
  • +
  • key: The key to expand the snippet. Sometimes the key of a +snippet is non-ASCII or not valid filename (e.g. contains +/). One can then define the key property which will +overwrite the filename as the key to expand the snippet.
  • +
  • group: The snippets for a mode can be grouped. Grouped snippets +will be grouped in sub-menu. This is useful if one has too many +snippets for a mode which will make the menu too long. group +property only affect menu construction (See The Menu). Refer to +the snippets for ruby-mode for examples. Group can also be +nested, e.g. control structure.loops tells that the snippet is +under the loops group which is under the control structure +group.
  • +
+
+
+

Quickly finding/defining snippets

+

From version 0.6 upwards there are two ways you can quickly find a +snippet file. Once you find this file it will be set to +snippet-mode (see ahead)

+
    +
  • M-x yas/find-snippets

    +

    Lets you find the snippet file in the directory the snippet was +loaded from (if it exists) like find-file-other-window.

    +
  • +
  • M-x yas/visit-snippet-file

    +

    Prompts you for possible snippet expansions like +yas/insert-snippet, but instead of expanding it, takes you +directly to the snippet definition's file, if it exists.

    +
  • +
+
+
+

Using the snippet-mode major mode

+

From version 0.6 upwards there is a major mode snippet-mode to +edit snippets. You can set the buffer to this mode with M-x +snippet-mode. It provides reasonably useful syntax highlighting.

+

Two commands are defined in this mode:

+
    +
  • M-x yas/load-snippet-buffer

    +
    +

    When editing a snippet, this loads the snippet into the correct +mode and menu. Bound to C-c C-c by default while in +snippet-mode.

    +
    +
  • +
  • M-x yas/tryout-snippet

    +
    +

    When editing a snippet, this opens a new empty buffer, sets it to +the appropriate major mode and inserts the snippet there, so you +can see what it looks like. This is bound to C-c C-t while in +snippet-mode.

    +
    +
  • +
+

There are also snippets for making snippets: vars, field and +mirror.

+
+
+

Define snippets using elisp code

+

As I mentioned above, you can define snippets directly by writing +elisp code.

+
+

yas/define-snippets

+

The basic syntax of yas/define-snippets is

+
(yas/define-snippets MODE SNIPPETS &optional PARENT)
+
+

The parameters are self-descriptive. If you specify a PARENT, then +the snippets of the parents may be shared by MODE. Note if you use +this function several times, the later specified PARENT will +overwrite the original one. However, not specifying a PARENT won't +erase the original parent.

+

The SNIPPETS parameter is a list of snippet definitions. Each +element should have the following form:

+
(KEY TEMPLATE NAME CONDITION GROUP)
+
+

The NAME, CONDITION and GROUP can be omitted if you don't +want to provide one. Here's an example:

+
(yas/define-snippets 'c++-mode
+'(
+  ("using" "using namespace ${std};
+$0" "using namespace ... " nil)
+  ("class" "class ${1:Name}
+{
+public:
+    $1($2);
+    virtual ~$1();
+};" "class ... { ... }" nil)
+  ("beginend" "${1:v}.begin(), $1.end" "v.begin(), v.end()" nil)
+  )
+'cc-mode)
+
+

The example above is auto-generated code by yas/compile-bundle.

+
+
+

yas/compile-bundle

+

yas/compile-bundle can be used to parse the snippets from a +directory hierarchy and translate them into the elisp form. The +translated code is faster to load. Further more, the generated bundle +is a stand-alone file not depending on yasnippet.el. The released +bundles of YASnippet are all generated this way.

+

The basic syntax of yas/compile-bundle is

+
(yas/compile-bundle &optional yasnippet yasnippet-bundle snippet-roots code dropdown)
+
+

As you can see, all the parameters are optional. The default values +for those parameters are convenient for me to produce the default +release bundle:

+
(yas/compile-bundle "yasnippet.el"
+                    "./yasnippet-bundle.el"
+                    '("snippets")
+                    "(yas/initialize)"
+                    "dropdown-list.el")
+
+

The snippet-roots can be a list of root directories. This is +useful when you have multiple snippet directories (maybe from other +users). The code parameter can be used to specify your own +customization code instead of the default (yas/initialize). For +example, you can set yas/trigger-key to (kbd "SPC") here if +you like.

+

From release 0.6 you have to specify the dropdown-list.el file if +you want it to be a part of the generated bundle.

+
+
+

yas/define

+

The basic syntax for yas/define is

+
(yas/define mode key template &optional name condition group)
+
+

This is only a syntax sugar for

+
(yas/define-snippets mode
+                     (list (list key template name condition group)))
+
+
+
+
+
+

The strategy to select a snippet

+

When user press the yas/trigger-key, YASnippet try to find a +proper snippet to expand. The strategy to find such a snippet is +explained here.

+
+

Finding the key

+

YASnippet search from current point backward trying to find the +snippet to be expanded. The default searching strategy is quite +powerful. For example, in c-mode, "bar", "foo_bar", +"#foo_bar" can all be recognized as a template key. Further more, +the searching is in that order. In other words, if "bar" is found +to be a key to some valid snippet, then "foo_bar" and +"#foobar" won't be searched.

+

However, this strategy can also be customized easily from the +yas/key-syntaxes variable. It is a list of syntax rules, the +default value is ("w" "w_" "w_." "^ "). Which means search the +following thing until found one:

+
    +
  • a word.
  • +
  • a symbol. In lisp, - and ? can all be part of a symbol.
  • +
  • a sequence of characters of either word, symbol or punctuation.
  • +
  • a sequence of characters of non-whitespace characters.
  • +
+

But you'd better keep the default value unless you understand what +Emacs's syntax rule mean.

+
+
+

The condition system

+

I write forked snippet.el to make the smart-snippet.el. I call it +smart-snippet because a condition can be attached to a snippet. This +is really a good idea. However, writing condition for a snippet +usually needs good elisp and Emacs knowledge, so it is strange to many +user.

+

Later I write YASnippet and persuade people to use it instead of +smart-snippet.el. However, some user still love smart-snippet because +it is smart. So I make YASnippet smart. Even smarter than +smart-snippet.el. :p

+

Consider this scenario: you are an old Emacs hacker. You like the +abbrev-way and set yas/trigger-key to (kbd "SPC"). However, +you don't want if to be expanded as a snippet when you are typing +in a comment block or a string (e.g. in python-mode).

+

It's OK, just specify the condition for if to be (not +(python-in-string/comment)). But how about while, for, +etc. ? Writing the same condition for all the snippets is just +boring. So YASnippet introduce a buffer local variable +yas/buffer-local-condition. You can set this variable to (not +(python-in-string/comment)) in python-mode-hook. There's no way +to do this in smart-snippet.el!

+

Then, what if you really want some snippet even in comment? This is +also possible! But let's stop telling the story and look at the rules:

+
    +
  • If yas/buffer-local-condition evaluate to nil, snippet won't be +expanded.
  • +
  • If it evaluate to the a cons cell where the car is the symbol +require-snippet-condition and the cdr is a symbol (let's +call it requirement):
      +
    • If the snippet has no condition, then it won't be expanded.
    • +
    • If the snippet has a condition but evaluate to nil or error +occured during evaluation, it won't be expanded.
    • +
    • If the snippet has a condition that evaluate to non-nil (let's +call it result):
        +
      • If requirement is t, the snippet is ready to be +expanded.
      • +
      • If requirement is eq to result, the snippet is ready +to be expanded.
      • +
      • Otherwise the snippet won't be expanded.
      • +
      +
    • +
    +
  • +
  • If it evaluate to other non-nil value:
      +
    • If the snippet has no condition, or has a condition that evaluate +to non-nil, it is ready to be expanded.
    • +
    • Otherwise, it won't be expanded.
    • +
    +
  • +
+

So set yas/buffer-local-condition like this

+
(add-hook 'python-mode-hook
+          '(lambda ()
+             (setq yas/buffer-local-condition
+                   '(if (python-in-string/comment)
+                        '(require-snippet-condition . force-in-comment)
+                      t))))
+
+

And specify the condition for a snippet that you're going to expand in +comment to be evaluated to the symbol force-in-comment. Then it +can be expanded as you expected, while other snippets like if +still can't expanded in comment.

+
+
+

Multiple snippet with the same key

+

There can be multiple snippet bind to the same key. If you define a +snippet with a key that is already used, you'll overwrite the original +snippet definition. However, you can add a different postfix to the +key.

+

In general, the extension (consider a file name) is ignored when +defining a snippet. So def, def.1 and def.mine will all be +valid candidates when the key is def.

+

When there are multiple candidates, YASnippet will let you select +one. The UI for selecting multiple candidate can be +customized. There're two variable related:

+

From version 0.6 of YASnippet this has changed significantly. A +customization variable, called yas/prompt-functions defines your +preferred method of being prompted for snippets.

+

You can customize it with M-x customize-variable RET +yas/prompt-functions RET. Alternatively you can put in your +emacs-file:

+
(setq yas/prompt-functions '(yas/x-prompt yas/dropdown-prompt))
+
+

Currently there are some alternatives solution with YASnippet.

+images/popup-menu.png +
+

Use the X window system

+

The function yas/x-prompt can be used to show a popup menu for you +to select. This menu will be part of you native window system widget, +which means:

+
    +
  • It usually looks beautiful. E.g. when you compile Emacs with gtk +support, this menu will be rendered with your gtk theme.
  • +
  • Emacs have little control over it. E.g. you can't use C-n, +C-p to navigate.
  • +
  • This function can't be used when in a terminal.
  • +
+
+
+

Use built-in Emacs selection methods

+

You can use functions yas/completing-prompt for the classic emacs +completion method or yas/ido-prompt for a much nicer looking +method. The best way is to try it. This works in a terminal.

+
images/idrop-menu.png
+
+
+

Use dropdown-menu.el

+

The function yas/dropdown-prompt can also be placed in the +yas/prompt-functions list.

+images/dropdown-menu.png +

Originally, only the above two function is available in +YASnippet. They are difficult to use -- especially in a +terminal. Until later Jaeyoun Chung show me his dropdown-menu.el, +I say wow! It's wonderful!

+
    +
  • It works in both window system and terminal.
  • +
  • It is customizable, you can use C-n, C-p to navigate, q +to quite and even press 6 as a shortcut to select the 6th +candidate.
  • +
+

So I added yas/dropdown-list-popup-for-template to support +dropdown-list.el. And upload dropdown-list.el to YASnippet +hompage for an optional download (since Jaeyoun didn't provide a URL).

+

Then finally, in 0.4.0, I included a copy of the content of +dropdown-list.el [1] in yasnippet.el and made it the default +way for selecting multiple candidates.

+

However, the original functions are still there, you can still use this

+
(setq yas/window-system-popup-function
+      'yas/x-popup-menu-for-template)
+
+

if you prefer a modern UI. :)

+
+
+
+

The Trigger Key

+

YASnippet is implemented as a minor-mode (yas/minor-mode). The +trigger key yas/trigger-key is defined in yas/minor-mode-map +to call yas/expand to try to expand a snippet.

+
+

The Minor Mode

+images/minor-mode-indicator.png +

When yas/minor-mode is enabled, the trigger key will take +effect. The default key is (kbd "TAB"), however, you can freely +set it to some other key.

+

In version 0.5, YASnippet add a hook to +after-change-major-mode-hook to enable yas/minor-mode [2] in +every buffer. This works fine for most modes, however, some mode +doesn't follow the Emacs convention and doens't call this hook. You +can either explicitly hook for those mode or just add it to +yas/extra-mode-hooks to let YASnippet do it for you:

+
(require 'yasnippet)
+(add-to-list 'yas/extra-mode-hooks
+             'ruby-mode-hook)
+(yas/initialize)
+
+

Note that should be put after (require 'yasnippet) and before +(yas/initialize). Further more, you may report it to me, I'll add +that to the default value.

+

In version 0.6, just use yas/global-mode to enable YASnippet in +all major modes. Or put yas/minor-mode-on in that modes hook. See +the FAQ.

+
+
+

The Fallback

+

If yas/expand failed to find any suitable snippet to expand, it +will disable the minor mode temporarily and find if there's any other +command bind the yas/trigger-key. If found, the command will be +called. Usually this works very well -- when there's a snippet, expand +it, otherwise, call whatever command originally bind to the trigger +key.

+

However, you can change this behavior by customizing the +yas/fallback-behavior variable. If you set this variable to +'return-nil, it will return nil instead of trying to call the +original command when no snippet is found. This is useful when you +would like YASnippet to work with other extensions, +e.g. hippie-expand. I'm also glad to tell you that integration +with hippie-expand is already included in YASnippet.

+
+
+

Integration with hippie-expand

+

To integrate with hippie-expand, just put +yas/hippie-try-expand in +hippie-expand-try-functions-list. Personally I would like to put +in front of the list, but it can be put anywhere you prefer.

+
+
+
+

Other way to select a snippet

+

When you use the trigger key (so yas/expand) to expand a snippet, +the key for the snippet is deleted before the template for the snippet +is inserted.

+

However, there're other ways to insert a snippet.

+
+

yas/insert-snippet

+

The command M-x yas/insert-snippet lets you insert snippets at +point for you current major mode. It prompts you for the snippet +key first, and then for a snippet template if more than one template +exists for the same key.

+

The list presented contains the snippets that can be inserted at +point, according to the condition system. If you want to see all +applicable snippets for the major mode, prefix this command with +C-u.

+

The prompting methods used are again controlled by +yas/prompt-functions.

+
+
+

The Menu

+

YASnippet will setup a menu just after the Buffers Menu in the +menubar. The snippets for all real modes are listed there under the +menu. You can select a snippet from the menu to expand it. Since you +select manually from the menu, you can expand any snippet. For +example, you can expand a snippet defined for python-mode in a +c-mode buffer by selecting it from the menu:

+images/menubar.png +
    +
  • Condition system is ignored since you select to expand it +explicitly.
  • +
  • There will be no muliple candidates since they are listed in the +menu as different items.
  • +
+

This can be convenient sometimes. However, if you don't like the +menubar of Emacs and never use it. You can tell YASnippet don't boring +to build a menu by setting yas/use-menu to nil.

+

Another thing to note is that only real modes are listed under the +menu. As you know, common snippets can be shared by making up a +virtual parent mode. It's too bad if the menu is floored by those +virtual modes. So YASnippet only show menus for those real +modes. But the snippets fo the virtual modes can still be accessed +through the parent submenu of some real mode.

+

YASnippet use a simple way to check whether a mode is real or +virtual: (fboundp mode). For example, the symbol c-mode is +bound to a function while cc-mode is not. But this is not enough, +some modes aren't part of Emacs, and maybe when initializing +YASnippet, those modes haven't been initialized. So YASnippet also +maintain a list of known modes (yas/known-modes). You can add item +to that list if you need.

+
+
+

Expanding From Elisp Code

+

Sometimes you might want to expand a snippet directly by calling a +functin from elisp code. You should call yas/expand-snippet +instead of yas/expand in this case.

+

As with expanding from the menubar, condition system and multiple +candidates won't exists here. In fact, expanding from menubar has the +same effect of evaluating the follow code:

+
(yas/expand-snippet (point) (point) template)
+
+

Where template is the template of a snippet. It is never required +to belong to any snippet -- you can even make up it on the fly. The +1st and 2nd parameter defines the region to be deleted after YASnippet +inserted the template. It is used by yas/expand to indicate the +region of the key. There's usually no need to delete any region when +we are expanding a snippet from elisp code, so passing two (point) +is fine. Note only (point) will be fine because the 1st parameter +also indicate where to insert and expand the template.

+
+
+
+
+

The Syntax of the Template

+

The syntax of the snippet template is simple but powerful, very +similar to TextMate's.

+
+

Plain Text

+

Arbitrary text can be included as the content of a template. They are +usually interpreted as plain text, except $ and `. You need to +use \ to escape them: \$ and \`. The \ itself may also +needed to be escaped as \\ sometimes.

+
+
+

Embedded elisp code

+

Elisp code can be embedded inside the template. They are written +inside back-quotes (`):

+

They are evaluated when the snippet is being expanded. The evaluation +is done in the same buffer as the snippet being expanded. Here's an +example for c-mode to calculate the header file guard dynamically:

+
#ifndef ${1:_`(upcase (file-name-nondirectory (file-name-sans-extension (buffer-file-name))))`_H_}
+#define $1
+
+$0
+
+#endif /* $1 */
+
+

From version 0.6.0, snippets expansions are run with some special +emacs-lisp variables bound. One of this is yas/selected-text. You +can therefore define a snippet like:

+
for ($1;$2;$3) {
+  `yas/selected-text`$0
+}
+
+

to "wrap" the selected region inside your recently inserted +snippet. Alternatively, you can also customize the variable +yas/wrap-around-region to t which will do this automatically.

+
+
+

Tab stop fields

+

Tab stops are fields that you can navigate back and forth by TAB +and S-TAB [3]. They are written by $ followed with a +number. $0 has the special meaning of the exit point of a +snippet. That is the last place to go when you've traveled all the +fields. Here's a typical example:

+
<div$1>
+    $0
+</div>
+
+
+
+

Placeholder fields

+

Tab stops can have default values -- a.k.a placeholders. The syntax is +like this:

+
${N:default value}
+
+

They acts as the default value for a tab stop. But when you firstly +type at a tab stop, the default value will be replaced by your +typing. The number can be omitted if you don't want to create +mirrors or transformations for this field.

+
+
+

Mirrors

+

We refer the tab stops with placeholders as a field. A field can have +mirrors. Its mirrors will get updated when you change the text of a +field. Here's an example:

+
\begin{${1:enumerate}}
+    $0
+\end{$1}
+
+

When you type "document" at ${1:enumerate}, the word +"document" will also be inserted at \end{$1}. The best +explanation is to see the screencast(YouTube or avi video).

+

The tab stops with the same number to the field act as its mirrors. If +none of the tab stops has an initial value, the first one is selected +as the field and others mirrors.

+
+
+

Mirrors with transformations

+

If the default value of a field starts with $, then it is interpreted +as the transformation code instead of default value. A transformation +is some arbitrary elisp code that will get evaluated in an environment +when the variable text is bind to the inputted text of the +field. Here's an example for Objective-C:

+
- (${1:id})${2:foo}
+{
+    return $2;
+}
+
+- (void)set${2:$(capitalize text)}:($1)aValue
+{
+    [$2 autorelease];
+    $2 = [aValue retain];
+}
+$0
+
+

Look at ${2:$(capitalize text)}, it is a transformation instead of +a placeholder. The actual placeholder is at the first line: +${2:foo}. When you type text in ${2:foo}, the transformation +will be evaluated and the result will be placed there as the +transformated text. So in this example, if you type baz in the field, +the transformed text will be Baz. This example is also available in +the screencast.

+

Another example is for rst-mode. In reStructuredText, the document +title can be some text surrounded by "===" below and above. The "===" +should be at least as long as the text. So

+
=====
+Title
+=====
+
+

is a valid title but

+
===
+Title
+===
+
+

is not. Here's an snippet for rst title:

+
${1:$(make-string (string-width text) ?\=)}
+${1:Title}
+${1:$(make-string (string-width text) ?\=)}
+
+$0
+
+ + + + + +
[1]With some minor change, mainly for fixing some trivial bugs.
+ + + + + +
[2]This is done when you call yas/initialize.
+ + + + + +
[3]Of course, this can be customized.
+
+
+

Fields with transformations

+

From version 0.6 on, you can also have lisp transformation inside +fields. These work mostly mirror transformations but are evaluated +when you first enter the field, after each change you make to the +field and also just before you exit the field.

+

The syntax is also a tiny bit different, so that the parser can +distinguish between fields and mirrors. In the following example

+
#define "${1:mydefine$(upcase yas/text)}"
+
+

mydefine gets automatically upcased to MYDEFINE once you enter +the field. As you type text, it gets filtered through the +transformation every time.

+

Note that this is differentiated from a mirror with a transformation +by the existance of extra text between the : and the +transformation's $. If you don't want this extra-text, you can use +two $'s instead.

+
#define "${1:$$(upcase yas/text)}"
+
+

Please note that as soon as a transformation takes place, it changes +the value of the field and sets it its internal modification state to +true. As a consequence, the auto-deletion behaviour of normal +fields does not take place. This is by design.

+
+
+

Choosing fields value from a list

+

As mentioned, the field transformation is invoked just after you enter +the field, and with some useful variables bound, notably +yas/field-modified-p and yas/moving-away-p. Because of this +feature you can place a transformation in the primary field that lets +you select default values for it.

+

The yas/choose-value does this work for you. For example:

+
<div align="${2:$$(yas/choose-value '("right" "center" "left"))}">
+  $0
+</div>
+
+

See the definition of yas/choose-value to see how it was written +using the two variables. Also check out yas/verify-value for +another neat trick.

+
+
+

Nested placeholder fields

+

From version 0.6 on, you can also have nested placeholders of the type:

+
<div${1: id="${2:some_id}"}>$0</div>
+
+

This allows you to choose if you want to give this div an id +attribute. If you tab forward after expanding it will let you change +"some_id" to whatever you like. Alternatively, you can just press +C-d (which executes yas/skip-and-clear-or-delete-char) and go +straight to the exit marker.

+

By the way, C-d will only clear the field if you cursor is at the +beginning of the field and it hasn't been changed yet. Otherwise, it +performs the normal Emacs delete-char command.

+
+
+

Indenting

+

Many people miss the indenting feature of smart-snippet: when you +place a $> in your snippet, an (indent-according-to-mode) will +be executed there to indent the line. So you'll not need to hard-code +the indenting in the snippet template, and it will be very convenient +when you need to work with several different project where coding +styles are different.

+

The reason why this feature wasn't added to YASnippet until after +0.5.6 is that it doesn't work well for all modes. In some cases +(e.g. python-mode), calling indent-according-to-mode will break +the overlays created by YASnippet.

+

However, since many people asked for this feature, I finally added +this to YASnippet. Here's an example of the usage:

+
for (${int i = 0}; ${i < 10}; ${++i})
+{$>
+$0$>
+}$>
+
+

In 0.6.0 You should not need to use this feature although it's +supported for backward compatibility. Just set yas/indent-line to +'auto.

+
+
+
+
+
+
+
+
+
+ + diff --git a/doc/define_snippet.rst b/doc/define_snippet.rst index 58d31f0..049995a 100644 --- a/doc/define_snippet.rst +++ b/doc/define_snippet.rst @@ -202,6 +202,25 @@ ignored. Here's a list of currently supported meta data: under the ``loops`` group which is under the ``control structure`` group. +Quickly finding/defining snippets +--------------------------------- + +From version 0.6 upwards there are two ways you can quickly find a +snippet file. Once you find this file it will be set to +``snippet-mode`` (see ahead) + +* ``M-x yas/find-snippets`` + + Lets you find the snippet file in the directory the snippet was + loaded from (if it exists) like ``find-file-other-window``. + +* ``M-x yas/visit-snippet-file`` + + Prompts you for possible snippet expansions like + ``yas/insert-snippet``, but instead of expanding it, takes you + directly to the snippet definition's file, if it exists. + + Using the ``snippet-mode`` major mode ------------------------------------- @@ -446,7 +465,7 @@ When there are multiple candidates, YASnippet will let you select one. The UI for selecting multiple candidate can be customized. There're two variable related: -From version 0.6 of YASnippet this has changed significantly. A +From version 0.6 of YASnippet this has changed significantly. A new customization variable, called ``yas/prompt-functions`` defines your preferred method of being prompted for snippets. @@ -455,7 +474,8 @@ yas/prompt-functions RET``. Alternatively you can put in your emacs-file: .. sourcecode:: common-lisp - (setq yas/prompt-functions '(yas/x-prompt yas/dropdown-prompt)) + + (setq yas/prompt-functions '(yas/x-prompt yas/dropdown-prompt)) Currently there are some alternatives solution with YASnippet. @@ -482,6 +502,9 @@ You can use functions ``yas/completing-prompt`` for the classic emacs completion method or ``yas/ido-prompt`` for a much nicer looking method. The best way is to try it. This works in a terminal. +.. image:: images/idrop-menu.png + :align: center + Use ``dropdown-menu.el`` ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -592,6 +615,22 @@ is inserted. However, there're other ways to insert a snippet. +``yas/insert-snippet`` +~~~~~~~~~~~~~~~~~~~~~~ + +The command ``M-x yas/insert-snippet`` lets you insert snippets at +point *for you current major mode*. It prompts you for the snippet +key first, and then for a snippet template if more than one template +exists for the same key. + +The list presented contains the snippets that can be inserted at +point, according to the condition system. If you want to see all +applicable snippets for the major mode, prefix this command with +``C-u``. + +The prompting methods used are again controlled by +``yas/prompt-functions``. + The Menu ~~~~~~~~ @@ -700,8 +739,8 @@ to "wrap" the selected region inside your recently inserted snippet. Alternatively, you can also customize the variable ``yas/wrap-around-region`` to ``t`` which will do this automatically. -Fields of type "Tab Stop" -------------------------- +Tab stop fields +--------------- Tab stops are fields that you can navigate back and forth by ``TAB`` and ``S-TAB`` [3]_. They are written by ``$`` followed with a @@ -715,8 +754,8 @@ fields. Here's a typical example: $0 -Fields of type "Placeholder" ----------------------------- +Placeholder fields +------------------ Tab stops can have default values -- a.k.a placeholders. The syntax is like this: diff --git a/doc/faq.html b/doc/faq.html new file mode 100644 index 0000000..2a40684 --- /dev/null +++ b/doc/faq.html @@ -0,0 +1,110 @@ + + + + + + +How to define a snippet ? + + + + + +
+
+
+
+ +
+
+
+
+
+

Why there's an extra newline?

+

If you have a newline at the end of the snippet definition file, then +YASnippet will add a newline when you expanding a snippet. Please +don't add a newline at the end if you don't want it when you saving +the snippet file.

+

Note some editors will automatically add a newline for you. In Emacs, +if you set require-final-newline to t, it will add the final +newline for you automatically.

+
+
+

Why TAB key doesn't expand a snippet?

+

First check the mode line to see if there's yas. If no, then try +M-x yas/minor-mode-on to manually turn on yas/minor-mode and +try to expand the snippet again. If it works, then, you can add the +following code to your .emacs before loading YASnippet:

+
(add-hook 'the-major-mode-hook 'yas/minor-mode-on)
+
+

where the-major-mode is the major mode in which yas/minor-mode +isn't enabled by default.

+

From YASnippet 0.6 you can also use the command M-x +yas/global-mode to turn on YASnippet automatically for all major +modes.

+

If yas/minor-mode is on but the snippet still not expanded. Then +try to see what command is bound to the TAB key: press C-h k +and then press TAB. Emacs will show you the result.

+

You'll see a buffer prompted by Emacs saying that TAB runs the +command .... Alternatively, you might see <tab> runs the command +..., note the difference between TAB and <tab> where the +latter has priority. If you see <tab> bound to a command other +than yas/expand, (e.g. in org-mode) you can try the following +code to work around:

+
(add-hook 'org-mode-hook
+          '(lambda ()
+             (make-variable-buffer-local 'yas/trigger-key)
+             (setq yas/trigger-key [tab])))
+
+

replace org-mode-hook with the major mode hook you are dealing +with (C-h m to see what major mode you are in).

+

If it says TAB but YASnippet still doesn't work, check your +configuration and you may also ask for help on the discussion group. Don't forget to +attach the information on what command is bound to TAB as well as the +mode information (Can be obtained by C-h m).

+
+
+

How to define snippets with named by characters not supported by the filesystem?

+

For example, you want to define a snippet by the key < which is not a +valid character for filename on Windows. In this case, you may use +yas/define to define the snippet. If you want to enjoy defining +snippets in a file, you can use the key property to specify the key of +the defined snippet explicitly.

+

Just name your snippet with an arbitrary valid filename, lt for +example. and specify < for the key property:

+
#key: <
+#name: <...></...>
+# --
+<${1:div}>$0</$1>
+
+
+
+
+
+
+
+
+
+ + diff --git a/doc/images/customization-group.png b/doc/images/customization-group.png new file mode 100644 index 0000000000000000000000000000000000000000..d98f7a0bb8ceff7331af576a0d9aacb10efd74ac GIT binary patch literal 54785 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 literal 0 HcmV?d00001 diff --git a/doc/images/idrop-menu.png b/doc/images/idrop-menu.png new file mode 100644 index 0000000000000000000000000000000000000000..f9a021733a98d6a1efdd464dfa299b69b3ef591e GIT binary patch literal 10294 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 literal 0 HcmV?d00001 diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..69e6a33 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,151 @@ + + + + + + +Yet Another Snippet extension + + + + + +
+
+
+
+ +
+
+
+
+ +

Yasnippet is a template system for emacs. It allows you to type a +abbrevation and automatically expand the abbreviation into function +templates.

+

Bundled language templates includes: C, C++, C#, Perl, Python, Ruby, +SQL, LaTeX, HTML, CSS and more.

+

Yasnippet system is inspired from TextMate's template system. You can +use a tool +to import any TextMate template you have to Yasnippet. It is a +re-design and re-write of my original extension smart-snippet. It +is much cleaner and more powerful than smart-snippet.

+
+

Video Demo

+

Watch the demo at YouTube (download a higher +resolution version: yasnippet.avi).

+
+
+

Brief Install Instruction

+

There are two archives of YASnippet. One is a single file compiled +“bundle”, and the other is normal. If all you need is to use the +builtin templates, download the bundle one. If you want to add your +own templates, download the normal one.

+
+

Bundle Install

+
    +
  1. Download the latest yasnippet-bundle-x.y.z.el.tgz and unpack it.
  2. +
  3. You'll get a file named yasnippet-bundle.el, put it under +~/.emacs.d/plugins/ (create the directory if not exists).
  4. +
  5. Open the file in Emacs, and type Alt+x eval-buffer.
  6. +
+

That's it. Now open any one of your language file, you'll see a menu +YASnippet. you can pull the menu to insert a template. Or, you can +type the pre-defined abbrev and press TAB to expand it.

+

To have emacs load YASnippet automatically when it starts, put the +following in your ~/.emacs file:

+
+
(add-to-list 'load-path
+              "~/.emacs.d/plugins")
+(require 'yasnippet-bundle)
+
+
+
+
+

Normal Install

+

For full install of the normal archive, just download and unpack the +latest yasnippet-x.y.z.tar.bz2. You'll get a directory named +yasnippet, put it in your ~/.emacs.d/plugins and add the +following in your .emacs file:

+
+
(add-to-list 'load-path
+              "~/.emacs.d/plugins")
+(require 'yasnippet) ;; not yasnippet-bundle
+(yas/initialize)
+(yas/load-directory "~/.emacs.d/plugins/yasnippet/snippets")
+
+
+

Please refer to the documentation for full customization, or use the +customization group.

+images/customization-group.png +
+
+
+

Customization group

+

From version 0.6 onwards, there is a customization group that you can +access with:

+

M-x customize-group RET yasnippet RET

+

Each customization variable affects how some part of YASnippet works, +for example automatic snippet indentation, what prompting method to +use, whether to expand snippets inside snippets, etc...

+

Inside the customization group, each variable is reasonably documented +to explain what it does.

+
+
+

Bugs, Contribution and Support

+ +

Thank you very much for using YASnippet!

+
+
+
+
+
+
+
+
+ + diff --git a/doc/index.rst b/doc/index.rst index fa01873..3eb8391 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -78,7 +78,26 @@ following in your ``.emacs`` file: (yas/initialize) (yas/load-directory "~/.emacs.d/plugins/yasnippet/snippets") -Please refer to the documentation for full customization. +Please refer to the documentation for full customization, or use the +customization group. + +.. image:: images/customization-group.png + :align: right + +Customization group +=================== + +From version 0.6 onwards, there is a customization group that you can +access with: + +``M-x customize-group RET yasnippet RET`` + +Each customization variable affects how some part of YASnippet works, +for example automatic snippet indentation, what prompting method to +use, whether to expand snippets inside snippets, etc... + +Inside the customization group, each variable is reasonably documented +to explain what it does. Bugs, Contribution and Support ============================== diff --git a/snippets/text-mode/ruby-mode/cls b/snippets/text-mode/ruby-mode/cls index c86cbfe..da28fb7 100644 --- a/snippets/text-mode/ruby-mode/cls +++ b/snippets/text-mode/ruby-mode/cls @@ -2,12 +2,12 @@ #contributor : hitesh #group : definitions # -- -class ${1:$$ - (let ((fn (capitalize (file-name-nondirectory +class ${1:`(let ((fn (capitalize (file-name-nondirectory (file-name-sans-extension - (buffer-file-name)))))) + (or (buffer-file-name) + (buffer-name (current-buffer)))))))) (cond ((string-match "_" fn) (replace-match "" nil nil fn)) - (t fn)))} + (t fn)))`} $0 end diff --git a/yasnippet.el b/yasnippet.el index cdf2d5d..084d2c1 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -3,7 +3,8 @@ ;; Copyright 2008 pluskid ;; Authors: pluskid , joaotavora -;; Version: 0.6.0b +;; Version: 0.6.0 +;; Package-version: 0.6.0b ;; X-URL: http://code.google.com/p/yasnippet/ ;; Keywords: snippet, textmate ;; URL: http://code.google.com/p/yasnippet/ @@ -2367,7 +2368,7 @@ With optional string TEXT do it in string instead" "Replace all the \"`(lisp-expression)`\"-style expression with their evaluated value" (while (re-search-forward yas/backquote-lisp-expression-regexp nil t) - (let ((transformed (yas/eval-string (match-string 1)))) + (let ((transformed (yas/eval-string (yas/restore-escapes (match-string 1))))) (goto-char (match-end 0)) (when transformed (insert transformed)) (delete-region (match-beginning 0) (match-end 0)))))