Merge branch 'jit-loading', implementing #160.

This commit is contained in:
João Távora 2012-04-29 12:48:22 +01:00
commit 6d416253ac
2 changed files with 301 additions and 218 deletions

View File

@ -20,7 +20,7 @@
;;; Commentary: ;;; Commentary:
;; Attempt to test basic snippet mechanics and the loading system ;; Test basic snippet mechanics and the loading system
;;; Code: ;;; Code:
@ -29,6 +29,7 @@
(require 'ert-x) (require 'ert-x)
;;; Snippet mechanics ;;; Snippet mechanics
(ert-deftest field-navigation () (ert-deftest field-navigation ()
@ -111,34 +112,84 @@ TODO: correct this bug!"
;;; Loading ;;; Loading
;;; ;;;
(ert-deftest basic-loading () (defmacro with-some-interesting-snippet-dirs (&rest body)
`(yas/saving-variables
(with-snippet-dirs
'((".emacs.d/snippets"
("c-mode"
(".yas-parents" . "cc-mode")
("printf" . "printf($1);"))
("emacs-lisp-mode" ("ert-deftest" . "(ert-deftest ${1:name} () $0)"))
("lisp-interaction-mode" (".yas-parents" . "emacs-lisp-mode")))
("library/snippets"
("c-mode" (".yas-parents" . "c++-mode"))
("cc-mode" ("def" . "# define"))
("emacs-lisp-mode" ("dolist" . "(dolist)"))
("lisp-interaction-mode" ("sc" . "brother from another mother"))))
,@body)))
(ert-deftest basic-jit-loading ()
"Test basic loading and expansion of snippets" "Test basic loading and expansion of snippets"
(yas/saving-variables (with-some-interesting-snippet-dirs
(with-snippet-dirs (yas/reload-all)
'((".emacs.d/snippets" (yas/basic-jit-loading-1)))
("c-mode"
(".yas-parents" . "cc-mode")
("printf" . "printf($1);"))
("emacs-lisp-mode" ("ert-deftest" . "(ert-deftest ${1:name} () $0)"))
("lisp-interaction-mode" (".yas-parents" . "emacs-lisp-mode")))
("library/snippets"
("c-mode" (".yas-parents" . "c++-mode"))
("cc-mode" ("def" . "# define"))
("emacs-lisp-mode" ("dolist" . "(dolist)"))
("lisp-interaction-mode" ("sc" . "brother from another mother"))))
(yas/reload-all)
(with-temp-buffer
(lisp-interaction-mode)
(yas/minor-mode 1)
(insert "sc")
(ert-simulate-command '(yas/expand))
(should (string= (buffer-substring-no-properties (point-min) (point-max))
"brother from another mother"))))))
(ert-deftest basic-jit-loading-with-compiled-snippets ()
"Test basic loading and expansion of snippets"
(with-some-interesting-snippet-dirs
(yas/reload-all)
(yas/recompile-all)
(flet ((yas/load-directory-2
(&rest dummies)
(ert-fail "yas/load-directory-2 shouldn't be called when snippets have been compiled")))
(yas/reload-all)
(yas/basic-jit-loading-1))))
(defun yas/basic-jit-loading-1 (&optional compile)
(with-temp-buffer
(should (= 4 (hash-table-count yas/scheduled-jit-loads)))
(should (= 0 (hash-table-count yas/tables)))
(lisp-interaction-mode)
(yas/minor-mode 1)
(should (= 2 (hash-table-count yas/scheduled-jit-loads)))
(should (= 2 (hash-table-count yas/tables)))
(should (= 1 (hash-table-count (yas/table-uuidhash (gethash 'lisp-interaction-mode yas/tables)))))
(should (= 2 (hash-table-count (yas/table-uuidhash (gethash 'emacs-lisp-mode yas/tables)))))
(yas/should-expand '(("sc" . "brother from another mother")
("dolist" . "(dolist)")
("ert-deftest" . "(ert-deftest name () )")))
(c-mode)
(yas/minor-mode 1)
(should (= 0 (hash-table-count yas/scheduled-jit-loads)))
(should (= 4 (hash-table-count yas/tables)))
(should (= 1 (hash-table-count (yas/table-uuidhash (gethash 'c-mode yas/tables)))))
(should (= 1 (hash-table-count (yas/table-uuidhash (gethash 'cc-mode yas/tables)))))
(yas/should-expand '(("printf" . "printf();")
("def" . "# define")))
(yas/should-not-expand '("sc" "dolist" "ert-deftest"))))
;;; Helpers ;;; Helpers
;;; ;;;
(defun yas/should-expand (keys-and-expansions)
(dolist (key-and-expansion keys-and-expansions)
(yas/exit-all-snippets)
(erase-buffer)
(insert (car key-and-expansion))
(let ((yas/fallback-behavior nil))
(ert-simulate-command '(yas/expand)))
(should (string= (buffer-substring-no-properties (point-min) (point-max))
(cdr key-and-expansion))))
(yas/exit-all-snippets))
(defun yas/should-not-expand (keys)
(dolist (key keys)
(yas/exit-all-snippets)
(erase-buffer)
(insert key)
(let ((yas/fallback-behavior nil))
(ert-simulate-command '(yas/expand)))
(should (string= (buffer-substring-no-properties (point-min) (point-max)) key))))
(defun yas/mock-insert (string) (defun yas/mock-insert (string)
(interactive) (interactive)
@ -157,7 +208,7 @@ TODO: correct this bug!"
(with-current-buffer (find-file file-or-dir-name) (with-current-buffer (find-file file-or-dir-name)
(insert content) (insert content)
(save-buffer) (save-buffer)
(kill-buffer))) (kill-buffer (current-buffer))))
(t (t
(message "[yas] oops don't know this content"))))) (message "[yas] oops don't know this content")))))
@ -179,10 +230,23 @@ TODO: correct this bug!"
(defmacro with-snippet-dirs (dirs &rest body) (defmacro with-snippet-dirs (dirs &rest body)
`(let ((default-directory (make-temp-file "yasnippet-fixture" t))) `(let ((default-directory (make-temp-file "yasnippet-fixture" t)))
(setq yas/snippet-dirs ',(mapcar #'car (cadr dirs))) (unwind-protect
(mapc #'yas/make-file-or-dirs ,dirs) (progn
,@body)) (setq yas/snippet-dirs ',(mapcar #'car (cadr dirs)))
(mapc #'yas/make-file-or-dirs ,dirs)
,@body)
(when (>= emacs-major-version 23)
(delete-directory default-directory 'recursive)))))
;;; Older emacsen
;;;
(unless (fboundp 'special-mode)
(define-minor-mode special-mode "Just a placeholder for something isn't in emacs 22"))
;;; btw to test this in emacs22 mac osx:
;;; curl -L -O https://github.com/mirrors/emacs/raw/master/lisp/emacs-lisp/ert.el
;;; curl -L -O https://github.com/mirrors/emacs/raw/master/lisp/emacs-lisp/ert-x.el
;;; /usr/bin/emacs -nw -Q -L . -l yasnippet-tests.el --batch -e ert
(provide 'yasnippet-tests) (provide 'yasnippet-tests)
;;; yasnippet-tests.el ends here ;;; yasnippet-tests.el ends here

View File

@ -181,6 +181,7 @@ as the default for storing the user's new snippets."
(equal old new)) (equal old new))
(yas/reload-all))))) (yas/reload-all)))))
(defun yas/snippet-dirs () (defun yas/snippet-dirs ()
"Returns `yas/snippet-dirs' (which see) as a list."
(if (listp yas/snippet-dirs) yas/snippet-dirs (list yas/snippet-dirs))) (if (listp yas/snippet-dirs) yas/snippet-dirs (list yas/snippet-dirs)))
(defvaralias 'yas/root-directory 'yas/snippet-dirs) (defvaralias 'yas/root-directory 'yas/snippet-dirs)
@ -721,42 +722,55 @@ With optional UNBIND-KEY, try to unbind that key from
(define-key yas/minor-mode-map (read-kbd-macro yas/trigger-key) 'yas/expand))) (define-key yas/minor-mode-map (read-kbd-macro yas/trigger-key) 'yas/expand)))
(defvar yas/tables (make-hash-table) (defvar yas/tables (make-hash-table)
"A hash table of MAJOR-MODE symbols to `yas/table' objects.") "A hash table of mode symbols to `yas/table' objects.")
(defvar yas/parents (make-hash-table)
"A hash table of mode symbols do lists of direct parent mode symbols.
This list is populated when reading the \".yas-parents\" files
found when traversing snippet directories with
`yas/load-directory'.
There might be additionalal parenting information stored in the
`derived-mode-parent' property of some mode symbols, but that is
not recorded here.")
(defvar yas/direct-keymaps (list) (defvar yas/direct-keymaps (list)
"Keymap alist supporting direct snippet keybindings. "Keymap alist supporting direct snippet keybindings.
This variable is is placed `emulation-mode-map-alists'. This variable is is placed in `emulation-mode-map-alists'.
Its elements looks like (TABLE-NAME . KEYMAP) and are Its elements looks like (TABLE-NAME . KEYMAP). They're
calculated when loading snippets. TABLE-NAME is a variable instantiated on `yas/reload-all' but KEYMAP is added to only when
set buffer-locally when entering `yas/minor-mode'. KEYMAP binds loading snippets. `yas//direct-TABLE-NAME' is then a variable set
all defined direct keybindings to the command buffer-locally when entering `yas/minor-mode'. KEYMAP binds all
`yas/expand-from-keymap', which acts similarly to `yas/expand'") defined direct keybindings to the command
`yas/expand-from-keymap' which then which snippet to expand.")
(defun yas/direct-keymaps-reload () (defun yas/direct-keymaps-reload ()
"Force reload the direct keybinding for active snippet tables." "Force reload the direct keybinding for active snippet tables."
(interactive) (interactive)
(setq yas/direct-keymaps nil) (setq yas/direct-keymaps nil)
(maphash #'(lambda (name table) (maphash #'(lambda (name table)
(mapc #'(lambda (table) (push (cons (intern (format "yas//direct-%s" name))
(push (cons (intern (format "yas//direct-%s" name)) (yas/table-direct-keymap table))
(yas/table-direct-keymap table)) yas/direct-keymaps))
yas/direct-keymaps))
(cons table (yas/table-get-all-parents table))))
yas/tables)) yas/tables))
(defun yas/direct-keymaps-set-vars () (defun yas/modes-to-activate ()
"Compute list of mode symbols that are active for `yas/expand'
and friends."
(let ((modes-to-activate (list major-mode)) (let ((modes-to-activate (list major-mode))
(mode major-mode)) (mode major-mode))
(while (setq mode (get mode 'derived-mode-parent)) (while (setq mode (get mode 'derived-mode-parent))
(push mode modes-to-activate)) (push mode modes-to-activate))
(dolist (mode (yas/extra-modes)) (dolist (mode (yas/extra-modes))
(push mode modes-to-activate)) (push mode modes-to-activate))
(dolist (mode modes-to-activate) (remove-duplicates
(let ((name (intern (format "yas//direct-%s" mode)))) (append modes-to-activate
(set-default name nil) (mapcan #'(lambda (mode)
(set (make-local-variable name) t))))) (yas/all-parents mode))
modes-to-activate)))))
(defvar yas/minor-mode-hook nil (defvar yas/minor-mode-hook nil
"Hook run when yas/minor-mode is turned on") "Hook run when yas/minor-mode is turned on")
@ -793,16 +807,21 @@ Key bindings:
;; ;;
(add-hook 'emulation-mode-map-alists 'yas/direct-keymaps) (add-hook 'emulation-mode-map-alists 'yas/direct-keymaps)
(add-hook 'post-command-hook 'yas/post-command-handler nil t) (add-hook 'post-command-hook 'yas/post-command-handler nil t)
(add-hook 'yas/minor-mode-hook 'yas/direct-keymaps-set-vars-runonce 'append)) ;; Set the `yas//direct-%s' vars for direct keymap expansion
;;
(dolist (mode (yas/modes-to-activate))
(let ((name (intern (format "yas//direct-%s" mode))))
(set-default name nil)
(set (make-local-variable name) t)))
;; Perform JIT loads
;;
(yas/load-pending-jits))
(t (t
;; Uninstall the direct keymaps and the post-command hook ;; Uninstall the direct keymaps and the post-command hook
;; ;;
(remove-hook 'post-command-hook 'yas/post-command-handler t) (remove-hook 'post-command-hook 'yas/post-command-handler t)
(remove-hook 'emulation-mode-map-alists 'yas/direct-keymaps)))) (remove-hook 'emulation-mode-map-alists 'yas/direct-keymaps))))
(defun yas/direct-keymaps-set-vars-runonce ()
(yas/direct-keymaps-set-vars)
(remove-hook 'yas/minor-mode-hook 'yas/direct-keymaps-set-vars-runonce))
(defvar yas/dont-activate nil (defvar yas/dont-activate nil
"If non-nil don't let `yas/minor-mode-on' active yas for this buffer. "If non-nil don't let `yas/minor-mode-on' active yas for this buffer.
@ -831,10 +850,15 @@ Do this unless `yas/dont-activate' is truish "
:group 'yasnippet :group 'yasnippet
:require 'yasnippet) :require 'yasnippet)
(add-hook 'yas/global-mode-hook 'yas/reload-all-maybe) (defadvice yas/global-mode (before yas/reload-with-jit (arg) activate)
(defun yas/reload-all-maybe () (cond ((and arg
(if yas/global-mode (numberp arg)
(yas/reload-all))) (> arg 1))
;; explicitly enabling
(yas/reload-all 'with-jit))
((not yas/global-mode)
;; toggling
(yas/reload-all 'with-jit))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Major mode stuff ;; Major mode stuff
@ -933,11 +957,6 @@ Has the following fields:
another hash of (NAME . TEMPLATE) where NAME is the snippet's another hash of (NAME . TEMPLATE) where NAME is the snippet's
name and TEMPLATE is a `yas/template' object. name and TEMPLATE is a `yas/template' object.
`yas/table-parents'
A list of tables considered parents of this table: i.e. when
searching for expansions they are searched as well.
`yas/table-direct-keymap' `yas/table-direct-keymap'
A keymap for the snippets in this table that have direct A keymap for the snippets in this table that have direct
@ -1138,12 +1157,11 @@ conditions to filter out potential expansions."
(t (t
(eq requirement result))))) (eq requirement result)))))
(defun yas/table-get-all-parents (table) (defun yas/all-parents (mode)
"Returns a list of all parent tables of TABLE" "Returns a list of all parent modes of MODE"
(let ((parents (yas/table-parents table))) (let ((parents (gethash mode yas/parents)))
(when parents (append parents
(append (copy-list parents) (mapcan #'yas/all-parents parents))))
(mapcan #'yas/table-get-all-parents parents)))))
(defun yas/table-templates (table) (defun yas/table-templates (table)
(when table (when table
@ -1267,11 +1285,7 @@ ensure your use `make-local-variable' when you set it.")
(defvaralias 'yas/mode-symbol 'yas/extra-modes) (defvaralias 'yas/mode-symbol 'yas/extra-modes)
(defun yas/table-get-create (mode) (defun yas/table-get-create (mode)
"Get the snippet table corresponding to MODE. "Get or create 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 (let ((table (gethash mode
yas/tables))) yas/tables)))
(unless table (unless table
@ -1281,31 +1295,15 @@ already have such a property."
(yas/table-direct-keymap table))) (yas/table-direct-keymap table)))
table)) table))
(defun yas/get-snippet-tables (&optional mode-symbol dont-search-parents) (defun yas/get-snippet-tables ()
"Get snippet tables for current buffer. "Get snippet tables for current buffer.
Return a list of 'yas/table' objects indexed by mode. Return a list of `yas/table' objects. The list of modes to
consider is returned by `yas/modes-to-activate'"
The modes are tried in this order: optional MODE-SYMBOL, then (remove nil
`yas/extra-modes', then `major-mode' then, unless (mapcar #'(lambda (mode-name)
DONT-SEARCH-PARENTS is non-nil, the guessed parent mode of either (gethash mode-name yas/tables))
MODE-SYMBOL or `major-mode'. (yas/modes-to-activate))))
Guessing is done by looking up the MODE-SYMBOL's
`derived-mode-parent' property, see also `derived-mode-p'."
(let ((mode-tables
(remove nil
(mapcar #'(lambda (mode)
(gethash mode yas/tables))
(remove nil (append (list mode-symbol)
(yas/extra-modes)
(list major-mode
(and (not dont-search-parents)
(get major-mode
'derived-mode-parent)))))))))
(remove-duplicates
(append mode-tables
(mapcan #'yas/table-get-all-parents mode-tables)))))
(defun yas/menu-keymap-get-create (table) (defun yas/menu-keymap-get-create (table)
"Get or create the main menu keymap correspondong to MODE. "Get or create the main menu keymap correspondong to MODE.
@ -1578,7 +1576,7 @@ TEMPLATES is a list of `yas/template'."
filtered-choices filtered-choices
chosen chosen
d d
(completion-fn (or completion-fn (completion-fn (or completion-fnn
#'completing-read))) #'completing-read)))
(dolist (choice choices) (dolist (choice choices)
(setq d (or (and display-fn (funcall display-fn choice)) (setq d (or (and display-fn (funcall display-fn choice))
@ -1602,57 +1600,67 @@ TEMPLATES is a list of `yas/template'."
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Loading snippets from files ;; Loading snippets from files
;; ;;
(defun yas/load-directory-1 (directory mode-sym parents &optional no-compiled-snippets) (defun yas/load-yas-setup-file (file)
"Recursively load snippet templates from DIRECTORY." (load file 'noerror))
(unless (file-exists-p (concat directory "/" ".yas-skip"))
;; Load .yas-setup.el files wherever we find them
;;
(load (expand-file-name ".yas-setup" directory) 'noerror)
(if (and (not no-compiled-snippets)
(load (expand-file-name ".yas-compiled-snippets" directory) 'noerror))
(message "Loading much faster .yas-compiled-snippets from %s" directory)
(let* ((default-directory directory)
(snippet-defs nil))
;; load the snippet files
;;
(with-temp-buffer
(dolist (file (yas/subdirs directory 'no-subdirs-just-files))
(when (file-readable-p file)
(insert-file-contents file nil nil nil t)
(push (yas/parse-template file)
snippet-defs))))
(when (or snippet-defs
(cdr major-mode-and-parents))
(yas/define-snippets mode-sym
snippet-defs
parents))
;; now recurse to a lower level
;;
(dolist (subdir (yas/subdirs directory))
(yas/load-directory-1 subdir
mode-sym
parents
t))))))
(defun yas/load-directory (top-level-dir) (defun yas/load-directory (top-level-dir &optional nojit)
"Load snippet definition from directory hierarchy under TOP-LEVEL-DIR. "Load snippet definition from directory hierarchy under TOP-LEVEL-DIR.
Below TOP-LEVEL-DIR., each directory is a mode name." Below TOP-LEVEL-DIR each directory is a mode name."
(interactive "DSelect the root directory: ") (interactive "DSelect the root directory: ")
(unless (file-directory-p top-level-dir) (unless (file-directory-p top-level-dir)
(error "%s is not a directory" top-level-dir)) (error "%s is not a directory" top-level-dir))
(unless yas/snippet-dirs (unless yas/snippet-dirs
(setq yas/snippet-dirs top-level-dir)) (setq yas/snippet-dirs top-level-dir))
(dolist (dir (yas/subdirs top-level-dir)) (dolist (dir (yas/subdirs top-level-dir))
(let ((major-mode-and-parents (yas/compute-major-mode-and-parents (let* ((major-mode-and-parents (yas/compute-major-mode-and-parents
(concat dir "/dummy")))) (concat dir "/dummy")))
(yas/load-directory-1 dir (mode-sym (car major-mode-and-parents))
(car major-mode-and-parents) (parents (cdr major-mode-and-parents)))
(cdr major-mode-and-parents)))) (yas/define-parents mode-sym parents)
(let ((form `(yas/load-directory-1 ,dir
',mode-sym
',parents)))
(if (or (called-interactively-p)
nojit)
(eval form)
(yas/schedule-jit mode-sym form)))))
(when (interactive-p) (when (interactive-p)
(yas/message 3 "Loaded snippets from %s." top-level-dir))) (yas/message 3 "Loaded snippets from %s." top-level-dir)))
(defun yas/load-snippet-dirs () (defun yas/load-directory-1 (directory mode-sym parents &optional no-compiled-snippets)
"Recursively load snippet templates from DIRECTORY."
(unless (file-exists-p (concat directory "/" ".yas-skip"))
(if (and (not no-compiled-snippets)
(load (expand-file-name ".yas-compiled-snippets" directory) 'noerror (<= yas/verbosity 2)))
(yas/message 2 "Loading much faster .yas-compiled-snippets from %s" directory)
(yas/load-directory-2 directory mode-sym parents))))
(defun yas/load-directory-2 (directory mode-sym parents)
;; 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))
;; load the snippet files
;;
(with-temp-buffer
(dolist (file (yas/subdirs directory 'no-subdirs-just-files))
(when (file-readable-p file)
(insert-file-contents file nil nil nil t)
(push (yas/parse-template file)
snippet-defs))))
(when snippet-defs
(yas/define-snippets mode-sym
snippet-defs))
;; now recurse to a lower level
;;
(dolist (subdir (yas/subdirs directory))
(yas/load-directory-2 subdir
mode-sym
nil))))
(defun yas/load-snippet-dirs (&optional nojit)
"Reload the directories listed in `yas/snippet-dirs' or "Reload the directories listed in `yas/snippet-dirs' or
prompt the user to select one." prompt the user to select one."
(let (errors) (let (errors)
@ -1660,21 +1668,26 @@ Below TOP-LEVEL-DIR., each directory is a mode name."
(dolist (directory (reverse (yas/snippet-dirs))) (dolist (directory (reverse (yas/snippet-dirs)))
(condition-case oops (condition-case oops
(progn (progn
(yas/load-directory directory) (yas/load-directory directory nojit)
(yas/message 3 "Loaded %s" directory)) (yas/message 3 "Loaded %s" directory))
(error (push oops errors) (error (push oops errors)
(yas/message 3 "Check your `yas/snippet-dirs': %s" (second oops))))) (yas/message 3 "Check your `yas/snippet-dirs': %s" (second oops)))))
(call-interactively 'yas/load-directory)) (call-interactively 'yas/load-directory))
errors)) errors))
(defun yas/reload-all (&optional interactive) (defun yas/reload-all (&optional nojit)
"Reload all snippets and rebuild the YASnippet menu. " "Reload all snippets and rebuild the YASnippet menu. "
(interactive "p") (interactive "p")
(let ((errors)) (let ((errors))
;; Empty all snippet tables and all menu tables ;; Empty all snippet tables, parenting info and all menu tables
;; ;;
(setq yas/tables (make-hash-table)) (setq yas/tables (make-hash-table))
(setq yas/parents (make-hash-table))
(setq yas/menu-table (make-hash-table)) (setq yas/menu-table (make-hash-table))
;; Cancel all pending 'yas/scheduled-jit-loads'
;;
(setq yas/scheduled-jit-loads (make-hash-table))
;; Init the `yas/minor-mode-map', taking care not to break the ;; Init the `yas/minor-mode-map', taking care not to break the
;; menu.... ;; menu....
@ -1685,7 +1698,7 @@ Below TOP-LEVEL-DIR., each directory is a mode name."
;; Reload the directories listed in `yas/snippet-dirs' or prompt ;; Reload the directories listed in `yas/snippet-dirs' or prompt
;; the user to select one. ;; the user to select one.
;; ;;
(setq errors (yas/load-snippet-dirs)) (setq errors (yas/load-snippet-dirs nojit))
;; Reload the direct keybindings ;; Reload the direct keybindings
;; ;;
(yas/direct-keymaps-reload) (yas/direct-keymaps-reload)
@ -1694,6 +1707,18 @@ Below TOP-LEVEL-DIR., each directory is a mode name."
(yas/trigger-key-reload) (yas/trigger-key-reload)
(yas/message 3 "Reloaded everything...%s." (if errors " (some errors, check *Messages*)" "")))) (yas/message 3 "Reloaded everything...%s." (if errors " (some errors, check *Messages*)" ""))))
(defun yas/load-pending-jits ()
(when yas/minor-mode
(dolist (mode (yas/modes-to-activate))
(let ((forms (gethash mode yas/scheduled-jit-loads)))
(dolist (form forms)
(yas/message 3 "Loading snippets for %s, just in time: %s!" mode form)
(eval form))
(remhash mode yas/scheduled-jit-loads)))))
;; (when (<= emacs-major-version 22)
;; (add-hook 'after-change-major-mode-hook 'yas/load-pending-jits))
(defun yas/quote-string (string) (defun yas/quote-string (string)
"Escape and quote STRING. "Escape and quote STRING.
foo\"bar\\! -> \"foo\\\"bar\\\\!\"" foo\"bar\\! -> \"foo\\\"bar\\\\!\""
@ -1710,63 +1735,73 @@ foo\"bar\\! -> \"foo\\\"bar\\\\!\""
"For backward compatibility, enable `yas/minor-mode' globally" "For backward compatibility, enable `yas/minor-mode' globally"
(yas/global-mode 1)) (yas/global-mode 1))
(defun yas/compile-top-level-dir (top-level-dir) (defun yas/compile-directory (top-level-dir)
"Create .yas-compiled-snippets.el files under subdirs of 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?") (interactive "DTop level snippet directory?")
(dolist (dir (yas/subdirs top-level-dir)) (flet ((yas/load-yas-setup-file
(yas/compile-snippets dir))) (file)
(let ((elfile (concat file ".el")))
(when (file-exists-p elfile)
(insert ";;; .yas-setup.el support file if any:\n;;;\n")
(insert-file-contents elfile)
(end-of-buffer)
)))
(yas/define-snippets
(mode snippets)
(insert ";;; Snippet definitions:\n;;;\n")
(let ((literal-snippets (list))
(print-length nil))
(dolist (snippet snippets)
(let ((key (first snippet))
(template-content (second snippet))
(name (third snippet))
(condition (fourth snippet))
(group (fifth snippet))
(expand-env (sixth snippet))
(file nil) ;; (seventh snippet)) ;; omit on purpose
(binding (eighth snippet))
(uuid (ninth snippet)))
(push `(,key
,template-content
,name
,condition
,group
,expand-env
,file
,binding
,uuid)
literal-snippets)))
(insert (pp-to-string `(yas/define-snippets ',mode ',literal-snippets)))
(insert "\n\n")))
(yas/load-directory-1
(dir mode parents &rest ignore)
(let ((output-file (concat (file-name-as-directory dir) ".yas-compiled-snippets.el")))
(with-temp-file output-file
(insert (format ";;; Compiled snippets and support files for `%s'\n" mode))
(yas/load-directory-2 dir mode parents)
(insert (format ";;; Do not edit! File generated at %s\n" (current-time-string)))))))
(yas/load-directory top-level-dir 'im-compiling-so-no-jit-ok?)))
(defun yas/compile-snippets (input-dir &optional output-file) (defun yas/recompile-all ()
"Compile snippets files in INPUT-DIR to OUTPUT-FILE file. "Compile every dir in `yas/snippet-dirs'."
(interactive)
(mapc #'yas/compile-directory (yas/snippet-dirs)))
Prompts for INPUT-DIR and OUTPUT-FILE if called-interactively"
(interactive (let* ((input-dir (read-directory-name "Snippet dir "))
(output-file (let ((ido-everywhere nil))
(read-file-name "Output file "
input-dir nil nil
".yas-compiled-snippets.el"
nil))))
(list input-dir output-file)))
(let ((default-directory input-dir))
(with-temp-file (setq output-file (or output-file ".yas-compiled-snippets.el"))
(flet ((yas/define-snippets
(mode snippets &optional parent-or-parents)
(insert (format ";;; %s - automatically compiled snippets for `%s' , do not edit!\n"
(file-name-nondirectory output-file) mode))
(insert ";;;\n")
(let ((literal-snippets (list)))
(dolist (snippet snippets)
(let ((key (first snippet))
(template-content (second snippet))
(name (third snippet))
(condition (fourth snippet))
(group (fifth snippet))
(expand-env (sixth snippet))
(file nil) ;; (seventh snippet)) ;; omit on purpose
(binding (eighth snippet))
(uuid (ninth snippet)))
(push `(,key
,template-content
,name
,condition
,group
,expand-env
,file
,binding
,uuid)
literal-snippets)))
(insert (pp-to-string `(yas/define-snippets ',mode ',literal-snippets ',parent-or-parents)))
(insert "\n\n")
(insert (format ";;; %s - automatically compiled snippets for `%s' end here\n"
(file-name-nondirectory output-file) mode))
(insert ";;;"))))
(yas/load-directory-1 input-dir nil nil 'no-compiled-snippets))))
(if (and (called-interactively-p)
(yes-or-no-p (format "Open the resulting file (%s)? "
(expand-file-name output-file))))
(find-file-other-window output-file)))
;;; JIT loading
;;;
(defvar yas/scheduled-jit-loads (make-hash-table)
"Alist of mode-symbols to forms to be evaled when `yas/minor-mode' kicks in.")
(defun yas/schedule-jit (mode form)
(puthash mode
(cons form
(gethash mode yas/scheduled-jit-loads))
yas/scheduled-jit-loads))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -1779,7 +1814,14 @@ Prompts for INPUT-DIR and OUTPUT-FILE if called-interactively"
yas/version yas/version
") -- pluskid <pluskid@gmail.com>/joaotavora <joaotavora@gmail.com>"))) ") -- pluskid <pluskid@gmail.com>/joaotavora <joaotavora@gmail.com>")))
(defun yas/define-snippets (mode snippets &optional parent-mode) (defun yas/define-parents (mode parents)
"Add PARENTS to the list of MODE's parents"
(puthash mode-sym (remove-duplicates
(append parents
(gethash mode-sym yas/parents)))
yas/parents))
(defun yas/define-snippets (mode snippets)
"Define SNIPPETS for MODE. "Define SNIPPETS for MODE.
SNIPPETS is a list of snippet definitions, each taking the SNIPPETS is a list of snippet definitions, each taking the
@ -1805,32 +1847,9 @@ UUID is the snippets \"unique-id\". Loading a second snippet file
with the same uuid replaced the previous snippet. with the same uuid replaced the previous snippet.
You can use `yas/parse-template' to return such lists based on You can use `yas/parse-template' to return such lists based on
the current buffers contents. the current buffers contents."
Optional PARENT-MODE can be used to specify the parent tables of
MODE. It can be a mode symbol of a list of mode symbols. It does
not need to be a real mode."
;; X) `snippet-table' is created or retrieved for MODE, same goes
;; for the list of snippet tables `parent-tables'.
;;
(let ((snippet-table (yas/table-get-create mode)) (let ((snippet-table (yas/table-get-create mode))
(parent-tables (mapcar #'yas/table-get-create
(if (listp parent-mode)
parent-mode
(list parent-mode))))
(template nil)) (template nil))
;; X) Connect `snippet-table' with `parent-tables'.
;;
;; TODO: this should be a remove-duplicates of the concatenation
;; of `snippet-table's existings parents with the new parents...
;;
(dolist (parent parent-tables)
(unless (find parent (yas/table-parents snippet-table))
(push parent
(yas/table-parents snippet-table))))
;; X) Now, iterate for evey snippet def list
;;
(dolist (snippet snippets) (dolist (snippet snippets)
(setq template (yas/define-snippets-1 snippet (setq template (yas/define-snippets-1 snippet
snippet-table))) snippet-table)))
@ -1982,7 +2001,7 @@ ommited from MODE's menu, even if they're manually loaded.
(define-key keymap (vector (gensym)) (define-key keymap (vector (gensym))
'(menu-item "----"))) '(menu-item "----")))
(t (t
(yas/message 3 "don't know anything about menu entry %s" (first e)))))) (yas/message 3 "Don't know anything about menu entry %s" (first e))))))
(defun yas/define (mode key template &optional name condition group) (defun yas/define (mode key template &optional name condition group)
"Define a snippet. Expanding KEY into TEMPLATE. "Define a snippet. Expanding KEY into TEMPLATE.
@ -2999,7 +3018,7 @@ snippet as ordinary text."
;; again from `yas/take-care-of-redo'.... ;; again from `yas/take-care-of-redo'....
(setf (yas/snippet-fields snippet) nil))) (setf (yas/snippet-fields snippet) nil)))
(yas/message 3 "snippet %s exited." (yas/snippet-id snippet))) (yas/message 3 "Snippet %s exited." (yas/snippet-id snippet)))
(defun yas/safely-run-hooks (hook-var) (defun yas/safely-run-hooks (hook-var)
(condition-case error (condition-case error
@ -3688,7 +3707,7 @@ SNIPPET-MARKERS."
(widen) (widen)
(condition-case err (condition-case err
(indent-according-to-mode) (indent-according-to-mode)
(error (yas/message 3 "warning: yas/indent-according-to-mode habing problems running %s" indent-line-function) (error (yas/message 3 "Warning: `yas/indent-according-to-mode' having problems running %s" indent-line-function)
nil))) nil)))
(mapc #'(lambda (marker) (mapc #'(lambda (marker)
(set-marker marker (point))) (set-marker marker (point)))
@ -4031,7 +4050,7 @@ that the rest of `yas/post-command-handler' runs.")
(apply (car fn-and-args) (apply (car fn-and-args)
(cdr fn-and-args))) (cdr fn-and-args)))
yas/post-command-runonce-actions) yas/post-command-runonce-actions)
(error (yas/message 3 "problem running `yas/post-command-runonce-actions'!"))) (error (yas/message 3 "Problem running `yas/post-command-runonce-actions'!")))
(setq yas/post-command-runonce-actions nil)) (setq yas/post-command-runonce-actions nil))
(cond (yas/protection-violation (cond (yas/protection-violation
(goto-char yas/protection-violation) (goto-char yas/protection-violation)