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'
|
||||
;;
|
||||
;; The directory where user-created snippets are to be
|
||||
;; stored. Can also be a list of directories that
|
||||
;; `yas/reload-all' will use for bulk-reloading snippets. In
|
||||
;; that case the first directory is the default for storing new
|
||||
;; snippets.
|
||||
;; stored. Can also be a list of directories. In that case,
|
||||
;; when used for bulk (re)loading of snippets (at startup or
|
||||
;; via `yas/reload-all'), directories appearing earlier in
|
||||
;; 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'
|
||||
;;
|
||||
@ -114,6 +115,13 @@
|
||||
;; snippet there, so you can see what it looks like. This is
|
||||
;; 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
|
||||
;; can optionally use it the preferred "prompting method", puting in
|
||||
;; your .emacs file, for example:
|
||||
@ -152,11 +160,12 @@
|
||||
(defcustom yas/snippet-dirs nil
|
||||
"Directory or list of snippet dirs for each major mode.
|
||||
|
||||
If you set this from your .emacs, can also be a list of strings,
|
||||
for multiple root directories. If you make this a list, the first
|
||||
element is always the user-created snippets directory. Other
|
||||
directories are used for bulk reloading of all snippets using
|
||||
`yas/reload-all'"
|
||||
The directory where user-created snippets are to be stored. Can
|
||||
also be a list of directories. In that case, when used for
|
||||
bulk (re)loading of snippets (at startup or via
|
||||
`yas/reload-all'), directories appearing earlier in the list
|
||||
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)")
|
||||
(repeat :args (string) :tag "List of directories (strings)"))
|
||||
:group 'yasnippet
|
||||
@ -867,7 +876,7 @@ Do this unless `yas/dont-activate' is t or the function
|
||||
;;; Internal structs for template management
|
||||
|
||||
(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."
|
||||
table
|
||||
key
|
||||
@ -876,7 +885,8 @@ Do this unless `yas/dont-activate' is t or the function
|
||||
condition
|
||||
expand-env
|
||||
file
|
||||
keybinding)
|
||||
keybinding
|
||||
uid)
|
||||
|
||||
(defstruct (yas/snippet-table (:constructor yas/make-snippet-table (name)))
|
||||
"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
|
||||
the elements of the keyhash that are vectors appear here as
|
||||
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
|
||||
(hash (make-hash-table :test 'equal))
|
||||
(uidhash (make-hash-table :test 'equal))
|
||||
(parents nil)
|
||||
(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
|
||||
;; searches:
|
||||
;;
|
||||
;; 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:
|
||||
;; 1. `yas/remove-snippet-by-uid' to remove any existing mappings by
|
||||
;; snippet uid
|
||||
;;
|
||||
;; 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
|
||||
;; entry.
|
||||
;;
|
||||
;; TODO: This is still not ideal. A well designed system (like
|
||||
;; 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)
|
||||
(defun yas/remove-snippet-by-uid (table uid)
|
||||
"Attempt to remove from TABLE a template with NAME and KEY.
|
||||
|
||||
TYPE-FN indicates if KEY is a trigger key (string) or a
|
||||
keybinding (vector)."
|
||||
(let ((key-and-namehash-alist '())
|
||||
(namehash-for-key (gethash key (yas/snippet-table-hash table))))
|
||||
(when namehash-for-key
|
||||
(push (cons key namehash-for-key) key-and-namehash-alist))
|
||||
(when (and (null key-and-namehash-alist)
|
||||
yas/better-guess-for-replacements)
|
||||
;; "cand" means "candidate for removal"
|
||||
(maphash #'(lambda (cand namehash)
|
||||
(when (and (gethash name namehash)
|
||||
(funcall type-fn cand))
|
||||
(push (cons cand namehash) key-and-namehash-alist)))
|
||||
(yas/snippet-table-hash table)))
|
||||
(dolist (elem key-and-namehash-alist)
|
||||
(remhash name (cdr elem))
|
||||
(when (= 0 (hash-table-count (cdr elem)))
|
||||
(remhash (car elem) (yas/snippet-table-hash table))
|
||||
(when (vectorp (car elem))
|
||||
(define-key (yas/snippet-table-direct-keymap table) (car elem) nil))))))
|
||||
(let ((template (gethash uid (yas/snippet-table-uidhash table))))
|
||||
(when template
|
||||
(let* ((name (yas/template-name template))
|
||||
(key (yas/template-key template))
|
||||
(keybinding (yas/template-keybinding template))
|
||||
(key-namehash (and key (gethash key (yas/snippet-table-hash table))))
|
||||
(keybinding-namehash (and keybinding (gethash keybinding (yas/snippet-table-hash table)))))
|
||||
;; Remove the name from each of the targeted namehashes
|
||||
;;
|
||||
(dolist (namehash (remove nil (list key-namehash
|
||||
keybinding-namehash)))
|
||||
(remhash name namehash))
|
||||
;; Cleanup if any of the namehashes in now empty. The
|
||||
;; keybinding namehash, if empty, leads to the actual
|
||||
;; keybinding being removed as well.
|
||||
;;
|
||||
(when (and key-namehash (zerop (hash-table-count key-namehash)))
|
||||
(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)
|
||||
"Store in TABLE the snippet template TEMPLATE.
|
||||
@ -997,16 +990,14 @@ keybinding)."
|
||||
(make-hash-table :test 'equal)
|
||||
(yas/snippet-table-hash table))))
|
||||
(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)
|
||||
"Add TEMPLATE to SNIPPET-TABLE, first removing existing according to remaining args.
|
||||
|
||||
TEMPLATE, NAME, KEY and KEYBINDING."
|
||||
;; The direct keybinding
|
||||
(yas/remove-snippet snippet-table name keybinding #'vectorp)
|
||||
(yas/remove-snippet snippet-table name key #'stringp)
|
||||
(defun yas/update-snippet (snippet-table template)
|
||||
"Add or update TEMPLATE in SNIPPET-TABLE"
|
||||
|
||||
(yas/remove-snippet-by-uid snippet-table (yas/template-uid template))
|
||||
(yas/add-snippet snippet-table template))
|
||||
|
||||
(defun yas/fetch (table key)
|
||||
@ -1567,7 +1558,7 @@ content of the file is the template."
|
||||
;;
|
||||
(if 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 yas/snippet-dirs))
|
||||
(call-interactively 'yas/load-directory))
|
||||
@ -1824,6 +1815,8 @@ not need to be a real mode."
|
||||
(condition (fourth snippet))
|
||||
(group (fifth snippet))
|
||||
(keybinding (yas/read-keybinding (eighth snippet)))
|
||||
(uid (or (ninth snippet)
|
||||
name))
|
||||
(template nil))
|
||||
|
||||
;; Create the `yas/template' object and store in the
|
||||
@ -1838,9 +1831,10 @@ not need to be a real mode."
|
||||
condition
|
||||
(sixth snippet)
|
||||
(seventh snippet)
|
||||
keybinding))
|
||||
keybinding
|
||||
uid))
|
||||
(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
|
||||
;; 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-keybinding yas/current-template) (yas/read-keybinding (eighth parsed)))
|
||||
(yas/update-snippet (yas/template-table yas/current-template)
|
||||
yas/current-template
|
||||
old-name
|
||||
old-key
|
||||
old-keybinding))
|
||||
yas/current-template))
|
||||
;; Now, prompt for new file creation much like
|
||||
;; `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
|
||||
(sixth parsed)
|
||||
nil
|
||||
nil
|
||||
nil))))
|
||||
(cond (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
|
||||
(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
|
||||
|
||||
@ -3926,22 +3964,21 @@ object satisfying `yas/field-p' to restrict the expansion to.")))
|
||||
;; (yas/snippet-table-hash (gethash 'ruby-mode yas/snippet-tables)))
|
||||
;; shit)))
|
||||
;;
|
||||
(defun yas/debug-tables ()
|
||||
(interactive)
|
||||
(with-output-to-temp-buffer "*YASnippet tables*"
|
||||
(dolist (table (yas/get-snippet-tables))
|
||||
(princ (format "Table hash keys for %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)))))))))
|
||||
;; and here's a way to do it by namehash
|
||||
;;
|
||||
;; (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)))))
|
||||
;;
|
||||
|
||||
|
||||
(defun yas/debug-snippet-vars ()
|
||||
"Debug snippets, fields, mirrors and the `buffer-undo-list'."
|
||||
|
Loading…
x
Reference in New Issue
Block a user