mirror of
https://github.com/joaotavora/yasnippet.git
synced 2025-10-13 21:13:04 +00:00
* Finally, a more reasonable uuid-based template-loading framework...
This commit is contained in:
parent
c937048036
commit
ea025980d8
223
yasnippet.el
223
yasnippet.el
@ -50,10 +50,11 @@
|
|||||||
;; `yas/snippet-dirs'
|
;; `yas/snippet-dirs'
|
||||||
;;
|
;;
|
||||||
;; The directory where user-created snippets are to be
|
;; The directory where user-created snippets are to be
|
||||||
;; stored. Can also be a list of directories that
|
;; stored. Can also be a list of directories. In that case,
|
||||||
;; `yas/reload-all' will use for bulk-reloading snippets. In
|
;; when used for bulk (re)loading of snippets (at startup or
|
||||||
;; that case the first directory is the default for storing new
|
;; via `yas/reload-all'), directories appearing earlier in
|
||||||
;; snippets.
|
;; the list shadow other dir's snippets. Also, the first
|
||||||
|
;; directory is taken as the default for storing the user's new snippets.
|
||||||
;;
|
;;
|
||||||
;; `yas/mode-symbol'
|
;; `yas/mode-symbol'
|
||||||
;;
|
;;
|
||||||
@ -114,6 +115,13 @@
|
|||||||
;; snippet there, so you can see what it looks like. This is
|
;; snippet there, so you can see what it looks like. This is
|
||||||
;; bound to "C-c C-t" while in `snippet-mode'.
|
;; bound to "C-c C-t" while in `snippet-mode'.
|
||||||
;;
|
;;
|
||||||
|
;; M-x yas/list-snippets
|
||||||
|
;;
|
||||||
|
;; Lists known snippets in a separate buffer. User is
|
||||||
|
;; prompted as to whether only the currently active tables
|
||||||
|
;; are to be displayed, or all the tables for all major
|
||||||
|
;; modes.
|
||||||
|
;;
|
||||||
;; The `dropdown-list.el' extension is bundled with YASnippet, you
|
;; The `dropdown-list.el' extension is bundled with YASnippet, you
|
||||||
;; can optionally use it the preferred "prompting method", puting in
|
;; can optionally use it the preferred "prompting method", puting in
|
||||||
;; your .emacs file, for example:
|
;; your .emacs file, for example:
|
||||||
@ -152,11 +160,12 @@
|
|||||||
(defcustom yas/snippet-dirs nil
|
(defcustom yas/snippet-dirs nil
|
||||||
"Directory or list of snippet dirs for each major mode.
|
"Directory or list of snippet dirs for each major mode.
|
||||||
|
|
||||||
If you set this from your .emacs, can also be a list of strings,
|
The directory where user-created snippets are to be stored. Can
|
||||||
for multiple root directories. If you make this a list, the first
|
also be a list of directories. In that case, when used for
|
||||||
element is always the user-created snippets directory. Other
|
bulk (re)loading of snippets (at startup or via
|
||||||
directories are used for bulk reloading of all snippets using
|
`yas/reload-all'), directories appearing earlier in the list
|
||||||
`yas/reload-all'"
|
shadow other dir's snippets. Also, the first directory is taken
|
||||||
|
as the default for storing the user's new snippets."
|
||||||
:type '(choice (string :tag "Single directory (string)")
|
:type '(choice (string :tag "Single directory (string)")
|
||||||
(repeat :args (string) :tag "List of directories (strings)"))
|
(repeat :args (string) :tag "List of directories (strings)"))
|
||||||
:group 'yasnippet
|
:group 'yasnippet
|
||||||
@ -867,7 +876,7 @@ Do this unless `yas/dont-activate' is t or the function
|
|||||||
;;; Internal structs for template management
|
;;; Internal structs for template management
|
||||||
|
|
||||||
(defstruct (yas/template (:constructor yas/make-template
|
(defstruct (yas/template (:constructor yas/make-template
|
||||||
(table key content name condition expand-env file keybinding)))
|
(table key content name condition expand-env file keybinding uid)))
|
||||||
"A template for a snippet."
|
"A template for a snippet."
|
||||||
table
|
table
|
||||||
key
|
key
|
||||||
@ -876,7 +885,8 @@ Do this unless `yas/dont-activate' is t or the function
|
|||||||
condition
|
condition
|
||||||
expand-env
|
expand-env
|
||||||
file
|
file
|
||||||
keybinding)
|
keybinding
|
||||||
|
uid)
|
||||||
|
|
||||||
(defstruct (yas/snippet-table (:constructor yas/make-snippet-table (name)))
|
(defstruct (yas/snippet-table (:constructor yas/make-snippet-table (name)))
|
||||||
"A table to store snippets for a particular mode.
|
"A table to store snippets for a particular mode.
|
||||||
@ -909,33 +919,22 @@ Has the following fields:
|
|||||||
keybindings. This is kept in sync with the keyhash, i.e., all
|
keybindings. This is kept in sync with the keyhash, i.e., all
|
||||||
the elements of the keyhash that are vectors appear here as
|
the elements of the keyhash that are vectors appear here as
|
||||||
bindings to `yas/expand-from-keymap'.
|
bindings to `yas/expand-from-keymap'.
|
||||||
|
|
||||||
|
`yas/snippet-table-uidhash'
|
||||||
|
|
||||||
|
A hash table mapping snippets uid's to the same `yas/template'
|
||||||
|
objects. A snippet uid defaults to the snippet's name.
|
||||||
"
|
"
|
||||||
name
|
name
|
||||||
(hash (make-hash-table :test 'equal))
|
(hash (make-hash-table :test 'equal))
|
||||||
|
(uidhash (make-hash-table :test 'equal))
|
||||||
(parents nil)
|
(parents nil)
|
||||||
(direct-keymap (make-sparse-keymap)))
|
(direct-keymap (make-sparse-keymap)))
|
||||||
|
|
||||||
;; Apropos storing/updating, this is works with two steps:
|
;; Apropos storing/updating, this works with two steps:
|
||||||
;;
|
;;
|
||||||
;; 1. `yas/remove-snippet' to remove any existing mappings, with two
|
;; 1. `yas/remove-snippet-by-uid' to remove any existing mappings by
|
||||||
;; searches:
|
;; snippet uid
|
||||||
;;
|
|
||||||
;; a) Try to get the existing namehash from TABLE using key.
|
|
||||||
;;
|
|
||||||
;; b) When the user changed KEY, the previous key indexing the
|
|
||||||
;; namehash is lost, so try to get the existing namehash by
|
|
||||||
;; searching the *whole* snippet table for NAME *and* checking
|
|
||||||
;; that the key for that previous namehash is of the same type
|
|
||||||
;; as KEY. This latter detail enables independent changes in
|
|
||||||
;; the trigger key and direct keybinding for a snippet.
|
|
||||||
;;
|
|
||||||
;; Search b) is only performed if a) failed and
|
|
||||||
;; `yas/better-guess-for-replacements' is non-nil. The latter is
|
|
||||||
;; now on by default, but I vaguely recall some situation where we
|
|
||||||
;; needed it to be nil....
|
|
||||||
;;
|
|
||||||
;; If any existing namesomething is found it is deleted, and is
|
|
||||||
;; maybe added later on:
|
|
||||||
;;
|
;;
|
||||||
;; 2. `yas/add-snippet' to add the mappings again:
|
;; 2. `yas/add-snippet' to add the mappings again:
|
||||||
;;
|
;;
|
||||||
@ -944,41 +943,35 @@ Has the following fields:
|
|||||||
;; TEMPLATE, and is also created a new namehash inside that
|
;; TEMPLATE, and is also created a new namehash inside that
|
||||||
;; entry.
|
;; entry.
|
||||||
;;
|
;;
|
||||||
;; TODO: This is still not ideal. A well designed system (like
|
(defun yas/remove-snippet-by-uid (table uid)
|
||||||
;; TextMate's) indexes the snippets by UUID or filename or something
|
|
||||||
;; that uniquely identify a snippet. I.e. this replacement strategy
|
|
||||||
;; fails if we have lost the original key and name, in that case it's
|
|
||||||
;; as if a brand new snippet has been created.
|
|
||||||
;;
|
|
||||||
;; UPDATE: `yas/visit-snippet-file' attempts to circumvent this using
|
|
||||||
;; `yas/current-template' and seems to work quite nicely.
|
|
||||||
;;
|
|
||||||
(defvar yas/better-guess-for-replacements t
|
|
||||||
"If non-nil `yas/store' guesses snippet replacements \"better\".")
|
|
||||||
|
|
||||||
(defun yas/remove-snippet (table name key type-fn)
|
|
||||||
"Attempt to remove from TABLE a template with NAME and KEY.
|
"Attempt to remove from TABLE a template with NAME and KEY.
|
||||||
|
|
||||||
TYPE-FN indicates if KEY is a trigger key (string) or a
|
TYPE-FN indicates if KEY is a trigger key (string) or a
|
||||||
keybinding (vector)."
|
keybinding (vector)."
|
||||||
(let ((key-and-namehash-alist '())
|
(let ((template (gethash uid (yas/snippet-table-uidhash table))))
|
||||||
(namehash-for-key (gethash key (yas/snippet-table-hash table))))
|
(when template
|
||||||
(when namehash-for-key
|
(let* ((name (yas/template-name template))
|
||||||
(push (cons key namehash-for-key) key-and-namehash-alist))
|
(key (yas/template-key template))
|
||||||
(when (and (null key-and-namehash-alist)
|
(keybinding (yas/template-keybinding template))
|
||||||
yas/better-guess-for-replacements)
|
(key-namehash (and key (gethash key (yas/snippet-table-hash table))))
|
||||||
;; "cand" means "candidate for removal"
|
(keybinding-namehash (and keybinding (gethash keybinding (yas/snippet-table-hash table)))))
|
||||||
(maphash #'(lambda (cand namehash)
|
;; Remove the name from each of the targeted namehashes
|
||||||
(when (and (gethash name namehash)
|
;;
|
||||||
(funcall type-fn cand))
|
(dolist (namehash (remove nil (list key-namehash
|
||||||
(push (cons cand namehash) key-and-namehash-alist)))
|
keybinding-namehash)))
|
||||||
(yas/snippet-table-hash table)))
|
(remhash name namehash))
|
||||||
(dolist (elem key-and-namehash-alist)
|
;; Cleanup if any of the namehashes in now empty. The
|
||||||
(remhash name (cdr elem))
|
;; keybinding namehash, if empty, leads to the actual
|
||||||
(when (= 0 (hash-table-count (cdr elem)))
|
;; keybinding being removed as well.
|
||||||
(remhash (car elem) (yas/snippet-table-hash table))
|
;;
|
||||||
(when (vectorp (car elem))
|
(when (and key-namehash (zerop (hash-table-count key-namehash)))
|
||||||
(define-key (yas/snippet-table-direct-keymap table) (car elem) nil))))))
|
(remhash key (yas/snippet-table-hash table))
|
||||||
|
(when (and keybinding-namehash (zerop (hash-table-count keybinding-namehash)))
|
||||||
|
(define-key (yas/snippet-table-direct-keymap table) keybinding nil)
|
||||||
|
(remhash keybinding (yas/snippet-table-hash table)))
|
||||||
|
;; Finally, remove the uid from the uidhash
|
||||||
|
;;
|
||||||
|
(remhash uid (yas/snippet-table-uidhash table)))))))
|
||||||
|
|
||||||
(defun yas/add-snippet (table template)
|
(defun yas/add-snippet (table template)
|
||||||
"Store in TABLE the snippet template TEMPLATE.
|
"Store in TABLE the snippet template TEMPLATE.
|
||||||
@ -997,16 +990,14 @@ keybinding)."
|
|||||||
(make-hash-table :test 'equal)
|
(make-hash-table :test 'equal)
|
||||||
(yas/snippet-table-hash table))))
|
(yas/snippet-table-hash table))))
|
||||||
(when (vectorp key)
|
(when (vectorp key)
|
||||||
(define-key (yas/snippet-table-direct-keymap table) key 'yas/expand-from-keymap)))))
|
(define-key (yas/snippet-table-direct-keymap table) key 'yas/expand-from-keymap)))
|
||||||
|
(when keys
|
||||||
|
(puthash (yas/template-uid template) template (yas/snippet-table-uidhash table)))))
|
||||||
|
|
||||||
(defun yas/update-snippet (snippet-table template name key keybinding)
|
(defun yas/update-snippet (snippet-table template)
|
||||||
"Add TEMPLATE to SNIPPET-TABLE, first removing existing according to remaining args.
|
"Add or update TEMPLATE in SNIPPET-TABLE"
|
||||||
|
|
||||||
TEMPLATE, NAME, KEY and KEYBINDING."
|
|
||||||
;; The direct keybinding
|
|
||||||
(yas/remove-snippet snippet-table name keybinding #'vectorp)
|
|
||||||
(yas/remove-snippet snippet-table name key #'stringp)
|
|
||||||
|
|
||||||
|
(yas/remove-snippet-by-uid snippet-table (yas/template-uid template))
|
||||||
(yas/add-snippet snippet-table template))
|
(yas/add-snippet snippet-table template))
|
||||||
|
|
||||||
(defun yas/fetch (table key)
|
(defun yas/fetch (table key)
|
||||||
@ -1567,7 +1558,7 @@ content of the file is the template."
|
|||||||
;;
|
;;
|
||||||
(if yas/snippet-dirs
|
(if yas/snippet-dirs
|
||||||
(if (listp yas/snippet-dirs)
|
(if (listp yas/snippet-dirs)
|
||||||
(dolist (directory yas/snippet-dirs)
|
(dolist (directory (reverse yas/snippet-dirs))
|
||||||
(yas/load-directory directory))
|
(yas/load-directory directory))
|
||||||
(yas/load-directory yas/snippet-dirs))
|
(yas/load-directory yas/snippet-dirs))
|
||||||
(call-interactively 'yas/load-directory))
|
(call-interactively 'yas/load-directory))
|
||||||
@ -1824,6 +1815,8 @@ not need to be a real mode."
|
|||||||
(condition (fourth snippet))
|
(condition (fourth snippet))
|
||||||
(group (fifth snippet))
|
(group (fifth snippet))
|
||||||
(keybinding (yas/read-keybinding (eighth snippet)))
|
(keybinding (yas/read-keybinding (eighth snippet)))
|
||||||
|
(uid (or (ninth snippet)
|
||||||
|
name))
|
||||||
(template nil))
|
(template nil))
|
||||||
|
|
||||||
;; Create the `yas/template' object and store in the
|
;; Create the `yas/template' object and store in the
|
||||||
@ -1838,9 +1831,10 @@ not need to be a real mode."
|
|||||||
condition
|
condition
|
||||||
(sixth snippet)
|
(sixth snippet)
|
||||||
(seventh snippet)
|
(seventh snippet)
|
||||||
keybinding))
|
keybinding
|
||||||
|
uid))
|
||||||
(when name
|
(when name
|
||||||
(yas/update-snippet snippet-table template name key keybinding))
|
(yas/update-snippet snippet-table template))
|
||||||
|
|
||||||
;; Setup the menu groups, reorganizing from group to group if
|
;; Setup the menu groups, reorganizing from group to group if
|
||||||
;; necessary
|
;; necessary
|
||||||
@ -2333,10 +2327,7 @@ With optional prefix argument KILL quit the window and buffer."
|
|||||||
(setf (yas/template-expand-env yas/current-template) (sixth parsed))
|
(setf (yas/template-expand-env yas/current-template) (sixth parsed))
|
||||||
(setf (yas/template-keybinding yas/current-template) (yas/read-keybinding (eighth parsed)))
|
(setf (yas/template-keybinding yas/current-template) (yas/read-keybinding (eighth parsed)))
|
||||||
(yas/update-snippet (yas/template-table yas/current-template)
|
(yas/update-snippet (yas/template-table yas/current-template)
|
||||||
yas/current-template
|
yas/current-template))
|
||||||
old-name
|
|
||||||
old-key
|
|
||||||
old-keybinding))
|
|
||||||
;; Now, prompt for new file creation much like
|
;; Now, prompt for new file creation much like
|
||||||
;; `yas/new-snippet' if one of the following is true:
|
;; `yas/new-snippet' if one of the following is true:
|
||||||
;;
|
;;
|
||||||
@ -2459,6 +2450,7 @@ With optional prefix argument KILL quit the window and buffer."
|
|||||||
nil
|
nil
|
||||||
(sixth parsed)
|
(sixth parsed)
|
||||||
nil
|
nil
|
||||||
|
nil
|
||||||
nil))))
|
nil))))
|
||||||
(cond (template
|
(cond (template
|
||||||
(let ((buffer-name (format "*YAS TEST: %s*" (yas/template-name template))))
|
(let ((buffer-name (format "*YAS TEST: %s*" (yas/template-name template))))
|
||||||
@ -2475,6 +2467,52 @@ With optional prefix argument KILL quit the window and buffer."
|
|||||||
(t
|
(t
|
||||||
(message "[yas] Cannot test snippet for unknown major mode")))))
|
(message "[yas] Cannot test snippet for unknown major mode")))))
|
||||||
|
|
||||||
|
(defun yas/list-tables (all-tables &optional by-name-hash)
|
||||||
|
"Display snippets for each table."
|
||||||
|
(interactive (list (y-or-n-p "Show also non-active tables?")
|
||||||
|
nil))
|
||||||
|
(with-output-to-temp-buffer "*YASnippet tables*"
|
||||||
|
(let ((tables (or (and all-tables
|
||||||
|
(let ((all))
|
||||||
|
(maphash #'(lambda (k v)
|
||||||
|
(push v all))
|
||||||
|
yas/snippet-tables)
|
||||||
|
all))
|
||||||
|
(yas/get-snippet-tables))))
|
||||||
|
(cond ((not by-name-hash)
|
||||||
|
(princ "YASnippet tables by UUID: \n")
|
||||||
|
(dolist (table tables)
|
||||||
|
(princ (format "\nSnippet table `%s':\n\n" (yas/snippet-table-name table)))
|
||||||
|
(let ((templates))
|
||||||
|
(maphash #'(lambda (k v)
|
||||||
|
(push v templates))
|
||||||
|
(yas/snippet-table-uidhash table))
|
||||||
|
(dolist (p templates)
|
||||||
|
(let ((name (yas/template-name p)))
|
||||||
|
(princ (format " * %s" name))
|
||||||
|
(princ (make-string (max (- 50 (length name))
|
||||||
|
1) ? ))
|
||||||
|
(when (yas/template-key p)
|
||||||
|
(princ (format "key \"%s\" " (yas/template-key p))))
|
||||||
|
(when (yas/template-keybinding p)
|
||||||
|
(princ (format "bound to %s " (key-description (yas/template-keybinding p)))))
|
||||||
|
(princ "\n"))))))
|
||||||
|
(t
|
||||||
|
(princ "\n\nYASnippet tables by NAMEHASH: \n")
|
||||||
|
(dolist (table tables)
|
||||||
|
(princ (format "\nSnippet table `%s':\n\n" (yas/snippet-table-name table)))
|
||||||
|
(let ((keys))
|
||||||
|
(maphash #'(lambda (k v)
|
||||||
|
(push k keys))
|
||||||
|
(yas/snippet-table-hash table))
|
||||||
|
(dolist (key keys)
|
||||||
|
(princ (format " key %s maps snippets: %s\n" key
|
||||||
|
(let ((names))
|
||||||
|
(maphash #'(lambda (k v)
|
||||||
|
(push k names))
|
||||||
|
(gethash key (yas/snippet-table-hash table)))
|
||||||
|
names)))))))))))
|
||||||
|
|
||||||
|
|
||||||
;;; User convenience functions, for using in snippet definitions
|
;;; User convenience functions, for using in snippet definitions
|
||||||
|
|
||||||
@ -3926,22 +3964,21 @@ object satisfying `yas/field-p' to restrict the expansion to.")))
|
|||||||
;; (yas/snippet-table-hash (gethash 'ruby-mode yas/snippet-tables)))
|
;; (yas/snippet-table-hash (gethash 'ruby-mode yas/snippet-tables)))
|
||||||
;; shit)))
|
;; shit)))
|
||||||
;;
|
;;
|
||||||
(defun yas/debug-tables ()
|
;; and here's a way to do it by namehash
|
||||||
(interactive)
|
;;
|
||||||
(with-output-to-temp-buffer "*YASnippet tables*"
|
;; (let ((keys))
|
||||||
(dolist (table (yas/get-snippet-tables))
|
;; (maphash #'(lambda (k v)
|
||||||
(princ (format "Table hash keys for %s:\n\n" (yas/snippet-table-name table))
|
;; (push k keys))
|
||||||
(let ((keys))
|
;; (yas/snippet-table-hash table))
|
||||||
(maphash #'(lambda (k v)
|
;; (dolist (key keys)
|
||||||
(push k keys))
|
;; (princ (format " key %s maps snippets: %s\n" key
|
||||||
(yas/snippet-table-hash table))
|
;; (let ((names))
|
||||||
(dolist (key keys)
|
;; (maphash #'(lambda (k v)
|
||||||
(princ (format " key %s maps snippets: %s\n" key
|
;; (push k names))
|
||||||
(let ((names))
|
;; (gethash key (yas/snippet-table-hash table)))
|
||||||
(maphash #'(lambda (k v)
|
;; names)))))
|
||||||
(push k names))
|
;;
|
||||||
(gethash key (yas/snippet-table-hash table)))
|
|
||||||
names)))))))))
|
|
||||||
|
|
||||||
(defun yas/debug-snippet-vars ()
|
(defun yas/debug-snippet-vars ()
|
||||||
"Debug snippets, fields, mirrors and the `buffer-undo-list'."
|
"Debug snippets, fields, mirrors and the `buffer-undo-list'."
|
||||||
|
Loading…
x
Reference in New Issue
Block a user