Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: mutiple key support #1068

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 246 additions & 0 deletions yasnippet.el
Original file line number Diff line number Diff line change
Expand Up @@ -1652,6 +1652,89 @@ Here's a list of currently recognized directives:
(setq group (split-string group "\\.")))
(list key template name condition group expand-env file binding uuid)))

(defun yas--parse-template-for-mulkey (&optional file)
"Parse the template in the current buffer.

Optional FILE is the absolute file name of the file being
parsed.

Optional GROUP is the group where the template is to go,
otherwise we attempt to calculate it from FILE.

Return a snippet-definition, i.e. a list

(KEY TEMPLATE NAME CONDITION GROUP VARS LOAD-FILE KEYBINDING UUID)

If the buffer contains a line of \"# --\" then the contents above
this line are ignored. Directives can set most of these with the syntax:

# directive-name : directive-value

Here's a list of currently recognized directives:

* type
* name
* contributor
* condition
* group
* key
* expand-env
* binding
* uuid"
(goto-char (point-min))
(let* ((type 'snippet)
(name (and file
(file-name-nondirectory file)))
(key nil)
template
bound
condition
(group (and file
(yas--calculate-group file)))
expand-env
binding
uuid
results)
(if (re-search-forward "^# --\\s-*\n" nil t)
(progn (setq template
(buffer-substring-no-properties (point)
(point-max)))
(setq bound (point))
(goto-char (point-min))
(while (re-search-forward "^# *\\([^ ]+?\\) *: *\\(.*?\\)[[:space:]]*$" bound t)
(when (string= "uuid" (match-string-no-properties 1))
(setq uuid (match-string-no-properties 2)))
(when (string= "type" (match-string-no-properties 1))
(setq type (if (string= "command" (match-string-no-properties 2))
'command
'snippet)))
(when (string= "key" (match-string-no-properties 1))
(cl-pushnew (match-string-no-properties 2) key))
(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 (yas--read-lisp (match-string-no-properties 2))))
(when (string= "group" (match-string-no-properties 1))
(setq group (match-string-no-properties 2)))
(when (string= "expand-env" (match-string-no-properties 1))
(setq expand-env (yas--read-lisp (match-string-no-properties 2)
'nil-on-error)))
(when (string= "binding" (match-string-no-properties 1))
(setq binding (match-string-no-properties 2)))))
(setq template
(buffer-substring-no-properties (point-min) (point-max))))
(unless (or key binding)
(cl-pushnew (and file (file-name-nondirectory file)) key))
(when (eq type 'command)
(setq template (yas--read-lisp (concat "(progn" template ")"))))
(when group
(setq group (split-string group "\\.")))
(if key
(dolist (k key results)
(push (list k template (format "%s (%s)" name k) condition group expand-env file binding uuid) results))
(setq results (list (list key template name condition group expand-env file binding uuid))))
results))

(defun yas--calculate-group (file)
"Calculate the group for snippet file path FILE."
(let* ((dominating-dir (locate-dominating-file file
Expand Down Expand Up @@ -1832,6 +1915,55 @@ defining snippets.
UUID is the snippet's \"unique-id\". Loading a second snippet
file with the same uuid would replace the previous snippet.

You can use `yas--parse-template' to return such lists based on
the current buffers contents."
(if yas--creating-compiled-snippets
(let ((print-length nil))
(insert ";;; Snippet definitions:\n;;;\n")
(dolist (snippet snippets)
;; Fill in missing elements with nil.
(setq snippet (append snippet (make-list (- 10 (length snippet)) nil)))
;; Move LOAD-FILE to SAVE-FILE because we will load from the
;; compiled file, not LOAD-FILE.
(let ((load-file (nth 6 snippet)))
(setcar (nthcdr 6 snippet) nil)
(setcar (nthcdr 9 snippet) load-file)))
(insert (pp-to-string
`(yas-define-snippets ',mode ',snippets)))
(insert "\n\n"))
;; Normal case.
(let ((snippet-table (yas--table-get-create mode))
(template nil))
(dolist (snippet snippets)
(setq template (yas--define-snippets-1 snippet
snippet-table)))
template)))

(defun yas-define-snippets-for-mulkey (mode snippets)
"Define SNIPPETS for MODE.

SNIPPETS is a list of snippet definitions, each taking the
following form

(KEY TEMPLATE NAME CONDITION GROUP EXPAND-ENV LOAD-FILE KEYBINDING UUID SAVE-FILE)

Within these, only KEY and TEMPLATE are actually mandatory.

TEMPLATE might be a Lisp form or a string, depending on whether
this is a snippet or a snippet-command.

CONDITION, EXPAND-ENV and KEYBINDING are Lisp forms, they have
been `yas--read-lisp'-ed and will eventually be
`yas--eval-for-string'-ed.

The remaining elements are strings.

FILE is probably of very little use if you're programatically
defining snippets.

UUID is the snippet's \"unique-id\". Loading a second snippet
file with the same uuid would replace the previous snippet.

You can use `yas--parse-template' to return such lists based on
the current buffers contents."
(if yas--creating-compiled-snippets
Expand Down Expand Up @@ -1936,6 +2068,58 @@ With prefix argument USE-JIT do jit-loading of snippets."
(when interactive
(yas--message 3 "Loaded snippets from %s." top-level-dir)))

(defun yas-load-directory-for-mulkey (top-level-dir &optional use-jit interactive)
"Load snippets in directory hierarchy TOP-LEVEL-DIR.

Below TOP-LEVEL-DIR each directory should be a mode name.

With prefix argument USE-JIT do jit-loading of snippets."
(interactive
(list (read-directory-name "Select the root directory: " nil nil t)
current-prefix-arg t))
(unless yas-snippet-dirs
(setq yas-snippet-dirs top-level-dir))
(let ((impatient-buffers))
(dolist (dir (yas--subdirs top-level-dir))
(let* ((major-mode-and-parents (yas--compute-major-mode-and-parents
(concat dir "/dummy")))
(mode-sym (car major-mode-and-parents))
(parents (cdr major-mode-and-parents)))
;; Attention: The parents and the menus are already defined
;; here, even if the snippets are later jit-loaded.
;;
;; * We need to know the parents at this point since entering a
;; given mode should jit load for its parents
;; immediately. This could be reviewed, the parents could be
;; discovered just-in-time-as well
;;
;; * We need to create the menus here to support the `full'
;; option to `yas-use-menu' (all known snippet menus are shown to the user)
;;
(yas--define-parents mode-sym parents)
(yas--menu-keymap-get-create mode-sym)
(let ((fun (apply-partially #'yas--load-directory-1-for-mulkey dir mode-sym)))
(if use-jit
(yas--schedule-jit mode-sym fun)
(funcall fun)))
;; Look for buffers that are already in `mode-sym', and so
;; need the new snippets immediately...
;;
(when use-jit
(cl-loop for buffer in (buffer-list)
do (with-current-buffer buffer
(when (eq major-mode mode-sym)
(yas--message 4 "Discovered there was already %s in %s" buffer mode-sym)
(push buffer impatient-buffers)))))))
;; ...after TOP-LEVEL-DIR has been completely loaded, call
;; `yas--load-pending-jits' in these impatient buffers.
;;
(cl-loop for buffer in impatient-buffers
do (with-current-buffer buffer (yas--load-pending-jits))))
(when interactive
(yas--message 3 "Loaded snippets from %s." top-level-dir)))


(defun yas--load-directory-1 (directory mode-sym)
"Recursively load snippet templates from DIRECTORY."
(if yas--creating-compiled-snippets
Expand All @@ -1954,6 +2138,24 @@ With prefix argument USE-JIT do jit-loading of snippets."
(yas--message 4 "Loading snippet files from %s" directory)
(yas--load-directory-2 directory mode-sym)))))

(defun yas--load-directory-1-for-mulkey (directory mode-sym)
"Recursively load snippet templates from DIRECTORY."
(if yas--creating-compiled-snippets
(let ((output-file (expand-file-name ".yas-compiled-snippets.el"
directory)))
(with-temp-file output-file
(insert (format ";;; Compiled snippets and support files for `%s'\n"
mode-sym))
(yas--load-directory-2-for-mulkey directory mode-sym)
(insert (format ";;; Do not edit! File generated at %s\n"
(current-time-string)))))
;; Normal case.
(unless (file-exists-p (expand-file-name ".yas-skip" directory))
(unless (and (load (expand-file-name ".yas-compiled-snippets" directory) 'noerror (<= yas-verbosity 3))
(progn (yas--message 4 "Loaded compiled snippets from %s" directory) t))
(yas--message 4 "Loading snippet files from %s" directory)
(yas--load-directory-2 directory mode-sym)))))

(defun yas--load-directory-2 (directory mode-sym)
;; Load .yas-setup.el files wherever we find them
;;
Expand All @@ -1980,6 +2182,36 @@ With prefix argument USE-JIT do jit-loading of snippets."
(yas--load-directory-2 subdir
mode-sym))))

(defun yas--load-directory-2-for-mulkey (directory mode-sym)
;; Load .yas-setup.el files wherever we find them
;;
(yas--load-yas-setup-file (expand-file-name ".yas-setup" directory))
(let* ((default-directory directory)
(snippet-defs nil)
parsed)
;; load the snippet files
;;
(with-temp-buffer
(dolist (file (yas--subdirs directory 'no-subdirs-just-files))
(when (file-readable-p file)
;; Erase the buffer instead of passing non-nil REPLACE to
;; `insert-file-contents' (avoids Emacs bug #23659).
(erase-buffer)
(insert-file-contents file)
(setq parsed (yas--parse-template-for-mulkey file))
(if (listp (car parsed)) ;; If car is a list
(dolist (sp parsed)
(push sp snippet-defs))
(push parsed snippet-defs)))))
(when snippet-defs
(yas-define-snippets-for-mulkey mode-sym
snippet-defs))
;; now recurse to a lower level
;;
(dolist (subdir (yas--subdirs directory))
(yas--load-directory-2-for-mulkey subdir
mode-sym))))

(defun yas--load-snippet-dirs (&optional nojit)
"Reload the directories listed in `yas-snippet-dirs' or
prompt the user to select one."
Expand Down Expand Up @@ -2093,6 +2325,15 @@ prefix argument."

;;; Snippet compilation function

(defun yas-compile-directory-for-mulkey (top-level-dir)
"Create .yas-compiled-snippets.el files under subdirs of TOP-LEVEL-DIR.

This works by stubbing a few functions, then calling
`yas-load-directory'."
(interactive "DTop level snippet directory?")
(let ((yas--creating-compiled-snippets t))
(yas-load-directory-for-mulkey top-level-dir nil)))

(defun yas-compile-directory (top-level-dir)
"Create .yas-compiled-snippets.el files under subdirs of TOP-LEVEL-DIR.

Expand All @@ -2107,6 +2348,11 @@ This works by stubbing a few functions, then calling
(interactive)
(mapc #'yas-compile-directory (yas-snippet-dirs)))

(defun yas-recompile-all-for-mulkey ()
"Compile every dir in `yas-snippet-dirs'."
(interactive)
(mapc #'yas-compile-directory-for-mulkey (yas-snippet-dirs)))


;;; JIT loading
;;;
Expand Down