;;; yasnippet.el --- Yet another snippet extension for Emacs.
-;; Copyright (C) 2008-2013 Free Software Foundation, Inc.
+;; Copyright (C) 2008-2013, 2015 Free Software Foundation, Inc.
;; Authors: pluskid <pluskid@gmail.com>, João Távora <joaotavora@gmail.com>
;; Maintainer: João Távora <joaotavora@gmail.com>
-;; Version: 0.8.0
+;; Version: 0.8.1
;; Package-version: 0.8.0
;; X-URL: http://github.com/capitaomorte/yasnippet
;; Keywords: convenience, emulation
;; 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
+;; the list override other dir's snippets. Also, the first
;; directory is taken as the default for storing the user's
;; new snippets.
;;
;; The deprecated `yas/root-directory' aliases this variable
;; for backward-compatibility.
;;
-;; `yas-extra-modes'
-;;
-;; A local variable that you can set in a hook to override
-;; snippet-lookup based on major mode. It is a list of
-;; symbols that correspond to subdirectories of
-;; `yas-snippet-dirs' and is used for deciding which
-;; snippets to consider for the active buffer.
;;
;; Major commands are:
;;
;;
;; Prompts you for a directory hierarchy of snippets to load.
;;
+;; M-x yas-activate-extra-mode
+;;
+;; Prompts you for an extra mode to add snippets for in the
+;; current buffer.
+;;
;; M-x yas-insert-snippet
;;
;; Prompts you for possible snippet expansion if that is
;;; Code:
(require 'cl)
+(require 'cl-lib)
(require 'easymenu)
(require 'help-mode)
(defgroup yasnippet nil
"Yet Another Snippet extension"
+ :prefix "yas-"
:group 'editing)
-(defvar yas--load-file-name load-file-name
- "Store the filename that yasnippet.el was originally loaded from.")
+(defvar yas-installed-snippets-dir nil)
+(setq yas-installed-snippets-dir
+ (when load-file-name
+ (concat (file-name-directory load-file-name) "snippets")))
+
+(defconst yas--default-user-snippets-dir
+ (concat user-emacs-directory "snippets"))
(defcustom yas-snippet-dirs (remove nil
- (list "~/.emacs.d/snippets"
- (when yas--load-file-name
- (concat (file-name-directory yas--load-file-name) "snippets"))))
- "Directory or list of snippet dirs for each major mode.
-
-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."
+ (list yas--default-user-snippets-dir
+ 'yas-installed-snippets-dir))
+ "List of top-level snippet directories.
+
+Each element, a string or a symbol whose value is a string,
+designates a top-level directory where per-mode snippet
+directories can be found.
+
+Elements appearing earlier in the list override later elements'
+snippets.
+
+The first directory is taken as the default for storing snippet's
+created with `yas-new-snippet'. "
:type '(choice (string :tag "Single directory (string)")
(repeat :args (string) :tag "List of directories (strings)"))
:group 'yasnippet
(yas-reload-all)))))
(defun yas-snippet-dirs ()
- "Return `yas-snippet-dirs' (which see) as a list."
- (if (listp yas-snippet-dirs) yas-snippet-dirs (list yas-snippet-dirs)))
+ "Return variable `yas-snippet-dirs' as list of strings."
+ (cl-loop for e in (if (listp yas-snippet-dirs)
+ yas-snippet-dirs
+ (list yas-snippet-dirs))
+ collect
+ (cond ((stringp e) e)
+ ((and (symbolp e)
+ (boundp e)
+ (stringp (symbol-value e)))
+ (symbol-value e))
+ (t
+ (error "[yas] invalid element %s in `yas-snippet-dirs'" e)))))
(defvaralias 'yas/root-directory 'yas-snippet-dirs)
(defcustom yas-new-snippet-default "\
-# -*- mode: snippet -*-
+# -*- mode: snippet; require-final-newline: nil -*-
# name: $1
# key: ${2:${1:$(yas--key-from-desc yas-text)}}${3:
-# binding: ${4:direct-keybinding}}${5:
-# expand-env: ((${6:some-var} ${7:some-value}))}${8:
-# type: command}
+# binding: ${4:direct-keybinding}}
# --
$0"
"Default snippet to use when creating a new snippet.
`yas-expand' returns nil)
- A Lisp form (apply COMMAND . ARGS) means interactively call
- COMMAND, if ARGS is non-nil, call COMMAND non-interactively
+ COMMAND. If ARGS is non-nil, call COMMAND non-interactively
with ARGS as arguments."
:type '(choice (const :tag "Call previous command" call-other-command)
(const :tag "Do nothing" return-nil))
under the menu \"Yasnippet\".
- If set to `abbreviate', only the current major-mode
-menu and the modes set in `yas-extra-modes' are listed.
+menu and the modes set in `yas--extra-modes' are listed.
- If set to `full', every submenu is listed
-- It set to nil, don't display a menu at all (this requires a
- `yas-reload-all' call if the menu is already visible).
+- If set to `nil', hide the menu.
Any other non-nil value, every submenu is listed."
:type '(choice (const :tag "Full" full)
map)
"The active keymap while a snippet expansion is in progress.")
-(defvar yas-key-syntaxes (list "w" "w_" "w_." "w_.()" "^ ")
- "List of character syntaxes used to find a trigger key before point.
-The list is tried in the order while scanning characters
-backwards from point. For example, if the list is '(\"w\" \"w_\")
-first look for trigger keys which are composed exclusively of
-\"word\"-syntax characters, and then, if that fails, look for
-keys which are either of \"word\" or \"symbol\"
-syntax. Triggering after
+(defvar yas-key-syntaxes (list "w" "w_" "w_." "w_.()"
+ #'yas-try-key-from-whitespace)
+ "Syntaxes and functions to help look for trigger keys before point.
+
+Each element in this list specifies how to skip buffer positions
+backwards and look for the start of a trigger key.
+
+Each element can be either a string or a function receiving the
+original point as an argument. A string element is simply passed
+to `skip-syntax-backward' whereas a function element is called
+with no arguments and should also place point before the original
+position.
+
+The string between the resulting buffer position and the original
+point is matched against the trigger keys in the active snippet
+tables.
+
+If no expandable snippets are found, the next element is the list
+is tried, unless a function element returned the symbol `again',
+in which case it is called again from the previous position and
+may once more reposition point.
+
+For example, if `yas-key-syntaxes'' value is '(\"w\" \"w_\"),
+trigger keys composed exclusively of \"word\"-syntax characters
+are looked for first. Failing that, longer keys composed of
+\"word\" or \"symbol\" syntax are looked for. Therefore,
+triggering after
foo-bar
-will, according to the \"w\" element first try \"bar\". If that
-isn't a trigger key, \"foo-bar\" is tried, respecting a second
-\"w_\" element.")
+will, according to the \"w\" element first try \"barbaz\". If
+that isn't a trigger key, \"foo-barbaz\" is tried, respecting the
+second \"w_\" element. Notice that even if \"baz\" is a trigger
+key for an active snippet, it won't be expanded, unless a
+function is added to `yas-key-syntaxes' that eventually places
+point between \"bar\" and \"baz\".
+
+See also Info node `(elisp) Syntax Descriptors'.")
(defvar yas-after-exit-snippet-hook
'()
(defvar yas--menu-table (make-hash-table)
"A hash table of MAJOR-MODE symbols to menu keymaps.")
-(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
'(?\\ ?` ?\" ?' ?$ ?} ?{ ?\( ?\))
"List of characters which *might* need to be escaped.")
(defun yas--snippet-next-id ()
(let ((id yas--snippet-id-seed))
- (incf yas--snippet-id-seed)
+ (cl-incf yas--snippet-id-seed)
id))
\f
(defvar yas--minor-mode-menu nil
"Holds the YASnippet menu.")
-(defun yas--init-minor-keymap ()
- "Set up the `yas-minor-mode' keymap."
+(defvar yas-minor-mode-map
(let ((map (make-sparse-keymap)))
- (when yas-use-menu
- (easy-menu-define yas--minor-mode-menu
- map
- "Menu used when `yas-minor-mode' is active."
- '("YASnippet"
- "----"
- ["Expand trigger" yas-expand
- :help "Possibly expand tab trigger before point"]
- ["Insert at point..." yas-insert-snippet
- :help "Prompt for an expandable snippet and expand it at point"]
- ["New snippet..." yas-new-snippet
- :help "Create a new snippet in an appropriate directory"]
- ["Visit snippet file..." yas-visit-snippet-file
- :help "Prompt for an expandable snippet and find its file"]
- "----"
- ("Snippet menu behaviour"
- ["Visit snippets" (setq yas-visit-from-menu t)
- :help "Visit snippets from the menu"
- :active t :style radio :selected yas-visit-from-menu]
- ["Expand snippets" (setq yas-visit-from-menu nil)
- :help "Expand snippets from the menu"
- :active t :style radio :selected (not yas-visit-from-menu)]
- "----"
- ["Show all known modes" (setq yas-use-menu 'full)
- :help "Show one snippet submenu for each loaded table"
- :active t :style radio :selected (eq yas-use-menu 'full)]
- ["Abbreviate according to current mode" (setq yas-use-menu 'abbreviate)
- :help "Show only snippet submenus for the current active modes"
- :active t :style radio :selected (eq yas-use-menu 'abbreviate)])
- ("Indenting"
- ["Auto" (setq yas-indent-line 'auto)
- :help "Indent each line of the snippet with `indent-according-to-mode'"
- :active t :style radio :selected (eq yas-indent-line 'auto)]
- ["Fixed" (setq yas-indent-line 'fixed)
- :help "Indent the snippet to the current column"
- :active t :style radio :selected (eq yas-indent-line 'fixed)]
- ["None" (setq yas-indent-line 'none)
- :help "Don't apply any particular snippet indentation after expansion"
- :active t :style radio :selected (not (member yas-indent-line '(fixed auto)))]
- "----"
- ["Also auto indent first line" (setq yas-also-auto-indent-first-line
- (not yas-also-auto-indent-first-line))
- :help "When auto-indenting also, auto indent the first line menu"
- :active (eq yas-indent-line 'auto)
- :style toggle :selected yas-also-auto-indent-first-line]
- )
- ("Prompting method"
- ["System X-widget" (setq yas-prompt-functions
- (cons 'yas-x-prompt
- (remove 'yas-x-prompt
- yas-prompt-functions)))
- :help "Use your windowing system's (gtk, mac, windows, etc...) default menu"
- :active t :style radio :selected (eq (car yas-prompt-functions)
- 'yas-x-prompt)]
- ["Dropdown-list" (setq yas-prompt-functions
- (cons 'yas-dropdown-prompt
- (remove 'yas-dropdown-prompt
- yas-prompt-functions)))
- :help "Use a special dropdown list"
- :active t :style radio :selected (eq (car yas-prompt-functions)
- 'yas-dropdown-prompt)]
- ["Ido" (setq yas-prompt-functions
- (cons 'yas-ido-prompt
- (remove 'yas-ido-prompt
- yas-prompt-functions)))
- :help "Use an ido-style minibuffer prompt"
- :active t :style radio :selected (eq (car yas-prompt-functions)
- 'yas-ido-prompt)]
- ["Completing read" (setq yas-prompt-functions
- (cons 'yas-completing-prompt
- (remove 'yas-completing-prompt
- yas-prompt-functions)))
- :help "Use a normal minibuffer prompt"
- :active t :style radio :selected (eq (car yas-prompt-functions)
- 'yas-completing-prompt)]
- )
- ("Misc"
- ["Wrap region in exit marker"
- (setq yas-wrap-around-region
- (not yas-wrap-around-region))
- :help "If non-nil automatically wrap the selected text in the $0 snippet exit"
- :style toggle :selected yas-wrap-around-region]
- ["Allow stacked expansions "
- (setq yas-triggers-in-field
- (not yas-triggers-in-field))
- :help "If non-nil allow snippets to be triggered inside other snippet fields"
- :style toggle :selected yas-triggers-in-field]
- ["Revive snippets on undo "
- (setq yas-snippet-revival
- (not yas-snippet-revival))
- :help "If non-nil allow snippets to become active again after undo"
- :style toggle :selected yas-snippet-revival]
- ["Good grace "
- (setq yas-good-grace
- (not yas-good-grace))
- :help "If non-nil don't raise errors in bad embedded elisp in snippets"
- :style toggle :selected yas-good-grace]
- )
- "----"
- ["Load snippets..." yas-load-directory
- :help "Load snippets from a specific directory"]
- ["Reload everything" yas-reload-all
- :help "Cleanup stuff, reload snippets, rebuild menus"]
- ["About" yas-about
- :help "Display some information about YASnippet"])))
-
- ;; Now for the stuff that has direct keybindings
- ;;
(define-key map [(tab)] 'yas-expand)
(define-key map (kbd "TAB") 'yas-expand)
(define-key map "\C-c&\C-s" 'yas-insert-snippet)
(define-key map "\C-c&\C-n" 'yas-new-snippet)
(define-key map "\C-c&\C-v" 'yas-visit-snippet-file)
- map))
-
-(defvar yas-minor-mode-map (yas--init-minor-keymap)
+ map)
"The keymap used when `yas-minor-mode' is active.")
-(defvar yas-extra-modes nil
- "A list of modes for which to also lookup snippets.
+(easy-menu-define yas--minor-mode-menu
+ yas-minor-mode-map
+ "Menu used when `yas-minor-mode' is active."
+ '("YASnippet" :visible yas-use-menu
+ "----"
+ ["Expand trigger" yas-expand
+ :help "Possibly expand tab trigger before point"]
+ ["Insert at point..." yas-insert-snippet
+ :help "Prompt for an expandable snippet and expand it at point"]
+ ["New snippet..." yas-new-snippet
+ :help "Create a new snippet in an appropriate directory"]
+ ["Visit snippet file..." yas-visit-snippet-file
+ :help "Prompt for an expandable snippet and find its file"]
+ "----"
+ ("Snippet menu behaviour"
+ ["Visit snippets" (setq yas-visit-from-menu t)
+ :help "Visit snippets from the menu"
+ :active t :style radio :selected yas-visit-from-menu]
+ ["Expand snippets" (setq yas-visit-from-menu nil)
+ :help "Expand snippets from the menu"
+ :active t :style radio :selected (not yas-visit-from-menu)]
+ "----"
+ ["Show all known modes" (setq yas-use-menu 'full)
+ :help "Show one snippet submenu for each loaded table"
+ :active t :style radio :selected (eq yas-use-menu 'full)]
+ ["Abbreviate according to current mode" (setq yas-use-menu 'abbreviate)
+ :help "Show only snippet submenus for the current active modes"
+ :active t :style radio :selected (eq yas-use-menu 'abbreviate)])
+ ("Indenting"
+ ["Auto" (setq yas-indent-line 'auto)
+ :help "Indent each line of the snippet with `indent-according-to-mode'"
+ :active t :style radio :selected (eq yas-indent-line 'auto)]
+ ["Fixed" (setq yas-indent-line 'fixed)
+ :help "Indent the snippet to the current column"
+ :active t :style radio :selected (eq yas-indent-line 'fixed)]
+ ["None" (setq yas-indent-line 'none)
+ :help "Don't apply any particular snippet indentation after expansion"
+ :active t :style radio :selected (not (member yas-indent-line '(fixed auto)))]
+ "----"
+ ["Also auto indent first line" (setq yas-also-auto-indent-first-line
+ (not yas-also-auto-indent-first-line))
+ :help "When auto-indenting also, auto indent the first line menu"
+ :active (eq yas-indent-line 'auto)
+ :style toggle :selected yas-also-auto-indent-first-line]
+ )
+ ("Prompting method"
+ ["System X-widget" (setq yas-prompt-functions
+ (cons 'yas-x-prompt
+ (remove 'yas-x-prompt
+ yas-prompt-functions)))
+ :help "Use your windowing system's (gtk, mac, windows, etc...) default menu"
+ :active t :style radio :selected (eq (car yas-prompt-functions)
+ 'yas-x-prompt)]
+ ["Dropdown-list" (setq yas-prompt-functions
+ (cons 'yas-dropdown-prompt
+ (remove 'yas-dropdown-prompt
+ yas-prompt-functions)))
+ :help "Use a special dropdown list"
+ :active t :style radio :selected (eq (car yas-prompt-functions)
+ 'yas-dropdown-prompt)]
+ ["Ido" (setq yas-prompt-functions
+ (cons 'yas-ido-prompt
+ (remove 'yas-ido-prompt
+ yas-prompt-functions)))
+ :help "Use an ido-style minibuffer prompt"
+ :active t :style radio :selected (eq (car yas-prompt-functions)
+ 'yas-ido-prompt)]
+ ["Completing read" (setq yas-prompt-functions
+ (cons 'yas-completing-prompt
+ (remove 'yas-completing-prompt
+ yas-prompt-functions)))
+ :help "Use a normal minibuffer prompt"
+ :active t :style radio :selected (eq (car yas-prompt-functions)
+ 'yas-completing-prompt)]
+ )
+ ("Misc"
+ ["Wrap region in exit marker"
+ (setq yas-wrap-around-region
+ (not yas-wrap-around-region))
+ :help "If non-nil automatically wrap the selected text in the $0 snippet exit"
+ :style toggle :selected yas-wrap-around-region]
+ ["Allow stacked expansions "
+ (setq yas-triggers-in-field
+ (not yas-triggers-in-field))
+ :help "If non-nil allow snippets to be triggered inside other snippet fields"
+ :style toggle :selected yas-triggers-in-field]
+ ["Revive snippets on undo "
+ (setq yas-snippet-revival
+ (not yas-snippet-revival))
+ :help "If non-nil allow snippets to become active again after undo"
+ :style toggle :selected yas-snippet-revival]
+ ["Good grace "
+ (setq yas-good-grace
+ (not yas-good-grace))
+ :help "If non-nil don't raise errors in bad embedded elisp in snippets"
+ :style toggle :selected yas-good-grace]
+ )
+ "----"
+ ["Load snippets..." yas-load-directory
+ :help "Load snippets from a specific directory"]
+ ["Reload everything" yas-reload-all
+ :help "Cleanup stuff, reload snippets, rebuild menus"]
+ ["About" yas-about
+ :help "Display some information about YASnippet"]))
+
+(defvar yas--extra-modes nil
+ "An internal list of modes for which to also lookup snippets.
This variable probably makes more sense as buffer-local, so
ensure your use `make-local-variable' when you set it.")
+(define-obsolete-variable-alias 'yas-extra-modes 'yas--extra-modes "0.8.1")
(defvar yas--tables (make-hash-table)
"A hash table of mode symbols to `yas--table' objects.")
yas--direct-keymaps))
yas--tables))
-(defun yas--modes-to-activate ()
+(defun yas--modes-to-activate (&optional mode)
"Compute list of mode symbols that are active for `yas-expand'
and friends."
- (let (dfs)
- (setq dfs (lambda (mode &optional explored)
- (push mode explored)
- (cons mode
- (loop for neighbour
- in (remove nil (cons (get mode
- 'derived-mode-parent)
- (gethash mode yas--parents)))
-
- unless (memq neighbour explored)
- append (funcall dfs neighbour explored)))))
- (remove-duplicates (append yas-extra-modes
- (funcall dfs major-mode)))))
+ (let* ((explored (if mode (list mode) ; Building up list in reverse.
+ (cons major-mode (reverse yas--extra-modes))))
+ (dfs
+ (lambda (mode)
+ (cl-loop for neighbour
+ in (cl-list* (get mode 'derived-mode-parent)
+ (ignore-errors (symbol-function mode))
+ (gethash mode yas--parents))
+ when (and neighbour
+ (not (memq neighbour explored))
+ (symbolp neighbour))
+ do (push neighbour explored)
+ (funcall dfs neighbour)))))
+ (mapcar dfs explored)
+ (nreverse explored)))
(defvar yas-minor-mode-hook nil
"Hook run when `yas-minor-mode' is turned on.")
(remove-hook 'post-command-hook 'yas--post-command-handler t)
(remove-hook 'emulation-mode-map-alists 'yas--direct-keymaps))))
+(defun yas-activate-extra-mode (mode)
+ "Activates the snippets for the given `mode' in the buffer.
+
+The function can be called in the hook of a minor mode to
+activate snippets associated with that mode."
+ (interactive
+ (let (modes
+ symbol)
+ (maphash (lambda (k _)
+ (setq modes (cons (list k) modes)))
+ yas--parents)
+ (setq symbol (completing-read
+ "Activate mode: " modes nil t))
+ (list
+ (when (not (string= "" symbol))
+ (intern symbol)))))
+ (when mode
+ (add-to-list (make-local-variable 'yas--extra-modes) mode)
+ (yas--load-pending-jits)))
+
+(defun yas-deactivate-extra-mode (mode)
+ "Deactivates the snippets for the given `mode' in the buffer."
+ (interactive
+ (list (intern
+ (completing-read
+ "Deactivate mode: " (mapcar #'list yas--extra-modes) nil t))))
+ (set (make-local-variable 'yas--extra-modes)
+ (remove mode
+ yas--extra-modes)))
+
(defvar yas-dont-activate '(minibufferp)
"If non-nil don't let `yas-global-mode' affect some buffers.
(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)
(when (third ent)
(define-key map (third ent) (second ent)))
(vector (first ent) (second ent) t))
- '(("Load this snippet" yas-load-snippet-buffer "\C-c\C-c")
+ '(("Load this snippet" yas-load-snippet-buffer "\C-c\C-l")
+ ("Load and quit window" yas-load-snippet-buffer-and-close "\C-c\C-c")
("Try out this snippet" yas-tryout-snippet "\C-c\C-t")))))
map)
"The keymap used when `snippet-mode' is active.")
\f
;;; Internal structs for template management
-(defstruct (yas--template (:constructor yas--make-blank-template))
+(cl-defstruct (yas--template
+ (:constructor yas--make-template)
+ ;; Handles `yas-define-snippets' format, plus the
+ ;; initial TABLE argument.
+ (:constructor
+ yas--define-snippets-2
+ (table
+ key content
+ &optional xname condition group
+ expand-env load-file xkeybinding xuuid save-file
+ &aux
+ (name (or xname
+ ;; A little redundant: we always get a name
+ ;; from `yas--parse-template' except when
+ ;; there isn't a file.
+ (and load-file (file-name-nondirectory load-file))
+ (and save-file (file-name-nondirectory save-file))
+ key))
+ (keybinding (yas--read-keybinding xkeybinding))
+ (uuid (or xuuid name))
+ (old (gethash uuid (yas--table-uuidhash table)))
+ (menu-binding-pair
+ (and old (yas--template-menu-binding-pair old)))
+ (perm-group
+ (and old (yas--template-perm-group old))))))
"A template for a snippet."
key
content
name
condition
expand-env
- file
+ load-file
+ save-file
keybinding
uuid
menu-binding-pair
table
)
-(defun yas--populate-template (template &rest args)
- "Helper function to populate TEMPLATE with properties."
- (while args
- (aset template
- (position (intern (substring (symbol-name (car args)) 1))
- (mapcar #'car (get 'yas--template 'cl-struct-slots)))
- (second args))
- (setq args (cddr args)))
- template)
-
(defstruct (yas--table (:constructor yas--make-snippet-table (name)))
"A table to store snippets for a particular mode.
`yas--table-name'
A symbol name normally corresponding to a major mode, but can
- also be a pseudo major-mode to be referenced in
- `yas-extra-modes', for example.
+ also be a pseudo major-mode to be used in
+ `yas-activate-extra-mode', for example.
`yas--table-hash'
(defun yas--update-template (table template)
"Add or update TEMPLATE in TABLE.
-Also takes care of adding and updating to the associated menu."
+Also takes care of adding and updating to the associated menu.
+Return TEMPLATE."
;; Remove from table by uuid
;;
(yas--remove-template-by-uuid table (yas--template-uuid template))
(yas--add-template table template)
;; Take care of the menu
;;
- (when yas-use-menu
- (yas--update-template-menu table template)))
+ (yas--update-template-menu table template)
+ template)
(defun yas--update-template-menu (table template)
"Update every menu-related for TEMPLATE."
(yas--table-hash table))
(yas--filter-templates-by-condition acc))))
-(defun yas--current-key ()
- "Get the key under current position.
-A key is used to find the template of a snippet in the current snippet-table."
- (let ((start (point))
- (end (point))
- (syntaxes yas-key-syntaxes)
- syntax
- done
- templates)
- (while (and (not done) syntaxes)
- (setq syntax (car syntaxes))
- (setq syntaxes (cdr syntaxes))
- (save-excursion
- (skip-syntax-backward syntax)
- (setq start (point)))
- (setq templates
- (mapcan #'(lambda (table)
- (yas--fetch table (buffer-substring-no-properties start end)))
- (yas--get-snippet-tables)))
- (if templates
- (setq done t)
- (setq start end)))
- (list templates
- start
- end)))
-
+(defun yas--templates-for-key-at-point ()
+ "Find `yas--template' objects for any trigger keys preceding point.
+Returns (TEMPLATES START END). This function respects
+`yas-key-syntaxes', which see."
+ (save-excursion
+ (let ((original (point))
+ (methods yas-key-syntaxes)
+ (templates)
+ (method))
+ (while (and methods
+ (not templates))
+ (unless (eq method (car methods))
+ ;; TRICKY: `eq'-ness test means we can only be here if
+ ;; `method' is a function that returned `again', and hence
+ ;; don't revert back to original position as per
+ ;; `yas-key-syntaxes'.
+ (goto-char original))
+ (setq method (car methods))
+ (cond ((stringp method)
+ (skip-syntax-backward method)
+ (setq methods (cdr methods)))
+ ((functionp method)
+ (unless (eq (funcall method original)
+ 'again)
+ (setq methods (cdr methods))))
+ (t
+ (setq methods (cdr methods))
+ (yas--warning "Invalid element `%s' in `yas-key-syntaxes'" method)))
+ (let ((possible-key (buffer-substring-no-properties (point) original)))
+ (save-excursion
+ (goto-char original)
+ (setq templates
+ (mapcan #'(lambda (table)
+ (yas--fetch table possible-key))
+ (yas--get-snippet-tables))))))
+ (when templates
+ (list templates (point) original)))))
(defun yas--table-all-keys (table)
"Get trigger keys of all active snippets in TABLE."
\f
;;; Internal functions and macros:
-(defun yas--real-mode? (mode)
- "Try to find out if MODE is a real mode.
-
-The MODE bound to a function (like `c-mode') is considered real
-mode. Other well known mode like `ruby-mode' which is not part of
-Emacs might not bound to a function until it is loaded. So
-yasnippet keeps a list of modes like this to help the judgment."
- (or (fboundp mode)
- (find mode yas--known-modes)))
+(defun yas--handle-error (err)
+ "Handle error depending on value of `yas-good-grace'."
+ (let ((msg (yas--format "elisp error: %s" (error-message-string err))))
+ (if yas-good-grace msg
+ (error "%s" msg))))
(defun yas--eval-lisp (form)
"Evaluate FORM and convert the result to string."
(let ((result (eval form)))
(when result
(format "%s" result))))))
- (error (if yas-good-grace
- (yas--format "elisp error! %s" (error-message-string err))
- (error (yas--format "elisp error: %s"
- (error-message-string err)))))))))
+ (error (yas--handle-error err))))))
(when (and (consp retval)
(eq 'yas--exception (car retval)))
(error (cdr retval)))
(defun yas--eval-lisp-no-saves (form)
(condition-case err
(eval form)
- (error (if yas-good-grace
- (yas--format "elisp error! %s" (error-message-string err))
- (error (yas--format "elisp error: %s"
- (error-message-string err)))))))
+ (error (message "%s" (yas--handle-error err)))))
(defun yas--read-lisp (string &optional nil-on-error)
"Read STRING as a elisp expression and return it.
yas--direct-keymaps))
table))
-(defun yas--get-snippet-tables ()
- "Get snippet tables for current buffer.
+(defun yas--get-snippet-tables (&optional mode)
+ "Get snippet tables for MODE.
+
+MODE defaults to the current buffer's `major-mode'.
Return a list of `yas--table' objects. The list of modes to
consider is returned by `yas--modes-to-activate'"
(remove nil
(mapcar #'(lambda (name)
(gethash name yas--tables))
- (yas--modes-to-activate))))
+ (yas--modes-to-activate mode))))
(defun yas--menu-keymap-get-create (mode &optional parents)
"Get or create the menu keymap for MODE and its PARENTS.
:visible (yas--show-menu-p ',mode)))
menu-keymap))
-
-(defmacro yas--called-interactively-p (&optional kind)
- "A backward-compatible version of `called-interactively-p'.
-
-Optional KIND is as documented at `called-interactively-p'
-in GNU Emacs 24.1 or higher."
- (if (string< emacs-version "24.1")
- '(called-interactively-p)
- `(called-interactively-p ,kind)))
-
\f
;;; Template-related and snippet loading functions
Return a snippet-definition, i.e. a list
- (KEY TEMPLATE NAME CONDITION GROUP VARS FILE KEYBINDING UUID)
+ (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:
(point-max)))
(setq bound (point))
(goto-char (point-min))
- (while (re-search-forward "^# *\\([^ ]+?\\) *: *\\(.*\\)$" bound t)
+ (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))
\f
;;; Popping up for keys and templates
-(defvar yas--x-pretty-prompt-templates nil
- "If non-nil, attempt to prompt for templates like TextMate.")
-
-
(defun yas--prompt-for-template (templates &optional prompt)
"Interactively choose a template from the list TEMPLATES.
(sort templates #'(lambda (t1 t2)
(< (length (yas--template-name t1))
(length (yas--template-name t2))))))
- (if yas--x-pretty-prompt-templates
- (yas--x-pretty-prompt-templates "Choose a snippet" templates)
- (some #'(lambda (fn)
- (funcall fn (or prompt "Choose a snippet: ")
- templates
- #'yas--template-name))
- yas-prompt-functions))))
+ (some #'(lambda (fn)
+ (funcall fn (or prompt "Choose a snippet: ")
+ templates
+ #'yas--template-name))
+ yas-prompt-functions)))
(defun yas--prompt-for-keys (keys &optional prompt)
"Interactively choose a template key from the list KEYS.
(defun yas-x-prompt (prompt choices &optional display-fn)
"Display choices in a x-window prompt."
- ;; FIXME: HACK: if we notice that one of the objects in choices is
- ;; actually a `yas--template', defer to `yas--x-prompt-pretty-templates'
- ;;
- ;; This would be better implemented by passing CHOICES as a
- ;; structured tree rather than a list. Modifications would go as far
- ;; up as `yas--all-templates' I think.
- ;;
(when (and window-system choices)
- (let ((chosen
- (let (menu d) ;; d for display
- (dolist (c choices)
- (setq d (or (and display-fn (funcall display-fn c))
- c))
- (cond ((stringp d)
- (push (cons (concat " " d) c) menu))
- ((listp d)
- (push (car d) menu))))
- (setq menu (list prompt (push "title" menu)))
- (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)
- menu))))
- (or chosen
- (keyboard-quit)))))
-
-(defun yas--x-pretty-prompt-templates (prompt templates)
- "Display TEMPLATES, grouping neatly by table name."
- (let ((organized (make-hash-table :test #'equal))
- menu
- more-than-one-table
- prefix)
- (dolist (tl templates)
- (puthash (yas--template-table tl)
- (cons tl
- (gethash (yas--template-table tl) organized))
- organized))
- (setq more-than-one-table (> (hash-table-count organized) 1))
- (setq prefix (if more-than-one-table
- " " ""))
- (if more-than-one-table
- (maphash #'(lambda (table templates)
- (push (yas--table-name table) menu)
- (dolist (tl templates)
- (push (cons (concat prefix (yas--template-name tl)) tl) menu))) organized)
- (setq menu (mapcar #'(lambda (tl) (cons (concat prefix (yas--template-name tl)) tl)) templates)))
-
- (setq menu (nreverse menu))
- (or (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)
- (list prompt (push "title" menu)))
- (keyboard-quit))))
+ ;; Let window position be recalculated to ensure that
+ ;; `posn-at-point' returns non-nil.
+ (redisplay)
+ (or
+ (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)
+ `(,prompt ("title"
+ ,@(mapcar* (lambda (c d) `(,(concat " " d) . ,c))
+ choices
+ (if display-fn (mapcar display-fn choices) choices)))))
+ (keyboard-quit))))
(defun yas-ido-prompt (prompt choices &optional display-fn)
(when (and (fboundp 'ido-completing-read)
(defun yas-dropdown-prompt (_prompt choices &optional display-fn)
(when (fboundp 'dropdown-list)
- (let (formatted-choices
- filtered-choices
- d
- n)
- (dolist (choice choices)
- (setq d (or (and display-fn (funcall display-fn choice))
- choice))
- (when (stringp d)
- (push d formatted-choices)
- (push choice filtered-choices)))
-
- (setq n (and formatted-choices (dropdown-list formatted-choices)))
- (if n
- (nth n filtered-choices)
+ (let* ((formatted-choices
+ (if display-fn (mapcar display-fn choices) choices))
+ (n (dropdown-list formatted-choices)))
+ (if n (nth n choices)
(keyboard-quit)))))
(defun yas-completing-prompt (prompt choices &optional display-fn completion-fn)
- (let (formatted-choices
- filtered-choices
+ (let* ((formatted-choices
+ (if display-fn (mapcar display-fn choices) choices))
+ (chosen (funcall (or completion-fn #'completing-read)
+ prompt formatted-choices
+ nil 'require-match nil nil)))
+ (if (eq choices formatted-choices)
chosen
- d
- (completion-fn (or completion-fn
- #'completing-read)))
- (dolist (choice choices)
- (setq d (or (and display-fn (funcall display-fn choice))
- choice))
- (when (stringp d)
- (push d formatted-choices)
- (push choice filtered-choices)))
- (setq chosen (and formatted-choices
- (funcall completion-fn prompt
- formatted-choices
- nil
- 'require-match
- nil
- nil)))
- (let ((position (or (and chosen
- (position chosen formatted-choices :test #'string=))
- 0)))
- (nth position filtered-choices))))
+ (nth (or (position chosen formatted-choices :test #'string=) 0)
+ choices))))
(defun yas-no-prompt (_prompt choices &optional _display-fn)
(first choices))
(defun yas--define-snippets-1 (snippet snippet-table)
"Helper for `yas-define-snippets'."
- ;; X) Calculate some more defaults on the values returned by
- ;; `yas--parse-template'.
- ;;
- (let* ((file (seventh snippet))
- (key (car snippet))
- (name (or (third snippet)
- (and file
- (file-name-directory file))))
- (condition (fourth snippet))
- (group (fifth snippet))
- (keybinding (yas--read-keybinding (eighth snippet)))
- (uuid (or (ninth snippet)
- name))
- (template (or (gethash uuid (yas--table-uuidhash snippet-table))
- (yas--make-blank-template))))
- ;; X) populate the template object
- ;;
- (yas--populate-template template
- :table snippet-table
- :key key
- :content (second snippet)
- :name (or name key)
- :group group
- :condition condition
- :expand-env (sixth snippet)
- :file (seventh snippet)
- :keybinding keybinding
- :uuid uuid)
- ;; X) Update this template in the appropriate table. This step
- ;; also will take care of adding the key indicators in the
- ;; templates menu entry, if any
- ;;
- (yas--update-template snippet-table template)
- ;; X) Return the template
- ;;
- ;;
- template))
+ ;; Update the appropriate table. Also takes care of adding the
+ ;; key indicators in the templates menu entry, if any.
+ (yas--update-template
+ snippet-table (apply #'yas--define-snippets-2 snippet-table snippet)))
(defun yas-define-snippets (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 FILE KEYBINDING UUID)
+ (KEY TEMPLATE NAME CONDITION GROUP EXPAND-ENV LOAD-FILE KEYBINDING UUID SAVE-FILE)
Within these, only KEY and TEMPLATE are actually mandatory.
FILE is probably of very little use if you're programatically
defining snippets.
-UUID is the snippets \"unique-id\". Loading a second snippet file
-with the same uuid replaced the previous snippet.
+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
- (progn
+ (let ((print-length nil))
(insert ";;; Snippet definitions:\n;;;\n")
- (let ((literal-snippets (list))
- (print-length nil))
- (dolist (snippet snippets)
- (let ((key (nth 0 snippet))
- (template-content (nth 1 snippet))
- (name (nth 2 snippet))
- (condition (nth 3 snippet))
- (group (nth 4 snippet))
- (expand-env (nth 5 snippet))
- (file nil) ;; omit on purpose
- (binding (nth 7 snippet))
- (uuid (nth 8 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")))
+ (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))
\f
;;; Loading snippets from files
+(defun yas--template-get-file (template)
+ "Return TEMPLATE's LOAD-FILE or SAVE-FILE."
+ (or (yas--template-load-file template)
+ (let ((file (yas--template-save-file template)))
+ (when file
+ (yas--message 2 "%s has no load file, use save file, %s, instead."
+ (yas--template-name template) file))
+ file)))
+
(defun yas--load-yas-setup-file (file)
(if (not yas--creating-compiled-snippets)
;; Normal case.
- (load file 'noerror)
+ (load file 'noerror (<= yas-verbosity 2))
(let ((elfile (concat file ".el")))
(when (file-exists-p elfile)
- (insert ";;; .yas-setup.el support file if any:\n;;;\n")
+ (insert ";;; contents of the .yas-setup.el support file:\n;;;\n")
(insert-file-contents elfile)
(goto-char (point-max))))))
Below TOP-LEVEL-DIR each directory should be a mode name.
-Optional USE-JIT use jit-loading of snippets."
- (interactive "DSelect the root directory: ni\np")
+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))
- (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 `(lambda () ;; FIXME: Simulating lexical-binding.
- (yas--load-directory-1 ',dir ',mode-sym))))
- (if (and use-jit
- (not (some #'(lambda (buffer)
- (with-current-buffer buffer
- ;; FIXME: Shouldn't this use derived-mode-p?
- (when (eq major-mode mode-sym)
- (yas--message 3 "Discovered there was already %s in %s" buffer mode-sym)
- t)))
- (buffer-list))))
- (yas--schedule-jit mode-sym fun)
- (funcall fun)))))
+ (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 `(lambda () ;; FIXME: Simulating lexical-binding.
+ (yas--load-directory-1 ',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 3 "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)))
(current-time-string)))))
;; Normal case.
(unless (file-exists-p (concat directory "/" ".yas-skip"))
- (if (and (progn (yas--message 2 "Loading compiled snippets from %s" directory) t)
- (load (expand-file-name ".yas-compiled-snippets" directory) 'noerror (<= yas-verbosity 3)))
- (yas--message 2 "Loading snippet files from %s" directory)
+ (unless (and (load (expand-file-name ".yas-compiled-snippets" directory) 'noerror (<= yas-verbosity 3))
+ (progn (yas--message 2 "Loaded compiled snippets from %s" directory) t))
+ (yas--message 2 "Loading snippet files from %s" directory)
(yas--load-directory-2 directory mode-sym)))))
(defun yas--load-directory-2 (directory mode-sym)
(call-interactively 'yas-load-directory))
errors))
-(defun yas-reload-all (&optional interactive)
+(defun yas-reload-all (&optional no-jit interactive)
"Reload all snippets and rebuild the YASnippet menu.
-When called interactively force immediate reload of all known
+When NO-JIT is non-nil force immediate reload of all known
snippets under `yas-snippet-dirs', otherwise use just-in-time
-loading."
- (interactive "p")
+loading.
+
+When called interactively, use just-in-time loading when given a
+prefix argument."
+ (interactive (list (not current-prefix-arg) t))
(catch 'abort
(let ((errors)
(snippet-editing-buffers
;; Reload the directories listed in `yas-snippet-dirs' or prompt
;; the user to select one.
;;
- (setq errors (yas--load-snippet-dirs interactive))
+ (setq errors (yas--load-snippet-dirs no-jit))
;; Reload the direct keybindings
;;
(yas-direct-keymaps-reload)
+ (run-hooks 'yas-after-reload-hook)
(yas--message 3 "Reloaded everything%s...%s."
- (if interactive "" " (snippets will load just-in-time)")
+ (if no-jit "" " (snippets will load just-in-time)")
(if errors " (some errors, check *Messages*)" "")))))
+(defvar yas-after-reload-hook nil
+ "Hooks run after `yas-reload-all'.")
+
(defun yas--load-pending-jits ()
(dolist (mode (yas--modes-to-activate))
(let ((funs (reverse (gethash mode yas--scheduled-jit-loads))))
\f
;;; Snippet compilation function
-(defun yas--initialize ()
- "For backward compatibility, enable `yas-minor-mode' globally."
- (yas-global-mode 1))
-
(defun yas-compile-directory (top-level-dir)
"Create .yas-compiled-snippets.el files under subdirs of TOP-LEVEL-DIR.
(mapcar #'(lambda (table)
(yas--table-mode table))
(yas--get-snippet-tables))))
- ((eq yas-use-menu 'full)
- t)
- ((eq yas-use-menu t)
- t)))
+ (yas-use-menu t)))
(defun yas--delete-from-keymap (keymap uuid)
"Recursively delete items with UUID from KEYMAP and its submenus."
list of groups of the snippets defined thereafter.
OMIT-ITEMS is a list of snippet uuid's that will always be
-omitted from MODE's menu, even if they're manually loaded.
-
-This function does nothing if `yas-use-menu' is nil."
- (when yas-use-menu
- (let* ((table (yas--table-get-create mode))
- (hash (yas--table-uuidhash table)))
- (yas--define-menu-1 table
- (yas--menu-keymap-get-create mode)
- menu
- hash)
- (dolist (uuid omit-items)
- (let ((template (or (gethash uuid hash)
- (yas--populate-template (puthash uuid
- (yas--make-blank-template)
- hash)
- :table table
- :uuid uuid))))
- (setf (yas--template-menu-binding-pair template) (cons nil :none)))))))
+omitted from MODE's menu, even if they're manually loaded."
+ (let* ((table (yas--table-get-create mode))
+ (hash (yas--table-uuidhash table)))
+ (yas--define-menu-1 table
+ (yas--menu-keymap-get-create mode)
+ menu
+ hash)
+ (dolist (uuid omit-items)
+ (let ((template (or (gethash uuid hash)
+ (puthash uuid
+ (yas--make-template :table table
+ :uuid uuid)
+ hash))))
+ (setf (yas--template-menu-binding-pair template) (cons nil :none))))))
(defun yas--define-menu-1 (table menu-keymap menu uuidhash &optional group-list)
"Helper for `yas-define-menu'."
(dolist (e (reverse menu))
(cond ((eq (first e) 'yas-item)
(let ((template (or (gethash (second e) uuidhash)
- (yas--populate-template (puthash (second e)
- (yas--make-blank-template)
- uuidhash)
- :table table
- :perm-group group-list
- :uuid (second e)))))
+ (puthash (second e)
+ (yas--make-template
+ :table table
+ :perm-group group-list
+ :uuid (second e))
+ uuidhash))))
(define-key menu-keymap (vector (gensym))
(car (yas--template-menu-binding-pair-get-create template :stay)))))
((eq (first e) 'yas-submenu)
(save-restriction
(narrow-to-region (yas--field-start field)
(yas--field-end field))
- (yas--current-key))
- (yas--current-key))))
- (if (and templates-and-pos
- (first templates-and-pos))
+ (yas--templates-for-key-at-point))
+ (yas--templates-for-key-at-point))))
+ (if templates-and-pos
(yas--expand-or-prompt-for-template (first templates-and-pos)
- (second templates-and-pos)
- (third templates-and-pos))
+ (second templates-and-pos)
+ (third templates-and-pos))
(yas--fallback))))
(defun yas-expand-from-keymap ()
(interactive)
(setq yas--condition-cache-timestamp (current-time))
(let* ((vec (subseq (this-command-keys-vector) (if current-prefix-arg
- universal-argument-num-events
+ (length (this-command-keys))
0)))
(templates (mapcan #'(lambda (table)
(yas--fetch table vec))
expand immediately. Common gateway for
`yas-expand-from-trigger-key' and `yas-expand-from-keymap'."
(let ((yas--current-template (or (and (rest templates) ;; more than one
- (yas--prompt-for-template (mapcar #'cdr templates)))
- (cdar templates))))
+ (yas--prompt-for-template (mapcar #'cdr templates)))
+ (cdar templates))))
(when yas--current-template
(yas-expand-snippet (yas--template-content yas--current-template)
start
(cond ((eq yas-fallback-behavior 'return-nil)
;; return nil
nil)
+ ((eq yas-fallback-behavior 'yas--fallback)
+ (error (concat "yasnippet fallback loop!\n"
+ "This can happen when you bind `yas-expand' "
+ "outside of the `yas-minor-mode-map'.")))
((eq yas-fallback-behavior 'call-other-command)
- (let* ((beyond-yasnippet (yas--keybinding-beyond-yasnippet)))
+ (let* ((yas-fallback-behavior 'yas--fallback)
+ ;; Also bind `yas-minor-mode' to prevent fallback
+ ;; loops when other extensions use mechanisms similar
+ ;; to `yas--keybinding-beyond-yasnippet'. (github #525
+ ;; and #526)
+ ;;
+ (yas-minor-mode nil)
+ (beyond-yasnippet (yas--keybinding-beyond-yasnippet)))
(yas--message 4 "Falling back to %s" beyond-yasnippet)
(assert (or (null beyond-yasnippet) (commandp beyond-yasnippet)))
- (setq this-original-command beyond-yasnippet)
- (call-interactively beyond-yasnippet)))
+ (setq this-command beyond-yasnippet)
+ (when beyond-yasnippet
+ (call-interactively beyond-yasnippet))))
((and (listp yas-fallback-behavior)
(cdr yas-fallback-behavior)
(eq 'apply (car yas-fallback-behavior)))
- (if (cddr yas-fallback-behavior)
- (apply (cadr yas-fallback-behavior)
- (cddr yas-fallback-behavior))
- (when (commandp (cadr yas-fallback-behavior))
- (setq this-command (cadr yas-fallback-behavior))
- (call-interactively (cadr yas-fallback-behavior)))))
+ (let ((command-or-fn (cadr yas-fallback-behavior))
+ (args (cddr yas-fallback-behavior))
+ (yas-fallback-behavior 'yas--fallback)
+ (yas-minor-mode nil))
+ (if args
+ (apply command-or-fn args)
+ (when (commandp command-or-fn)
+ (setq this-command command-or-fn)
+ (call-interactively command-or-fn)))))
(t
;; also return nil if all the other fallbacks have failed
nil)))
(defun yas--keybinding-beyond-yasnippet ()
- "Return the ??"
+ "Get current keys's binding as if YASsnippet didn't exist."
(let* ((yas-minor-mode nil)
(yas--direct-keymaps nil)
(keys (this-single-command-keys)))
(remove-duplicates (mapcan #'yas--table-templates tables)
:test #'equal))))
+(defun yas--lookup-snippet-1 (name mode)
+ "Get the snippet called NAME in MODE's tables."
+ (let ((yas-choose-tables-first nil) ; avoid prompts
+ (yas-choose-keys-first nil))
+ (cl-find name (yas--all-templates
+ (yas--get-snippet-tables mode))
+ :key #'yas--template-name :test #'string=)))
+
+(defun yas-lookup-snippet (name &optional mode noerror)
+ "Get the snippet content for the snippet NAME in MODE's tables.
+
+MODE defaults to the current buffer's `major-mode'. If NOERROR
+is non-nil, then don't signal an error if there isn't any snippet
+called NAME.
+
+Honours `yas-buffer-local-condition'."
+ (let ((snippet (yas--lookup-snippet-1 name mode)))
+ (cond
+ (snippet (yas--template-content snippet))
+ (noerror nil)
+ (t (error "No snippet named: %s" name)))))
+
(defun yas-insert-snippet (&optional no-condition)
"Choose a snippet to expand, pop-up a list of choices according
-to `yas--prompt-function'.
+to `yas-prompt-functions'.
With prefix argument NO-CONDITION, bypass filtering of snippets
by condition."
(defun yas--visit-snippet-file-1 (template)
"Helper for `yas-visit-snippet-file'."
- (let ((file (yas--template-file template)))
+ (let ((file (yas--template-get-file template)))
(cond ((and file (file-readable-p file))
(find-file-other-window file)
(snippet-mode)
(let ((main-dir (replace-regexp-in-string
"/+$" ""
(or (first (or (yas-snippet-dirs)
- (setq yas-snippet-dirs '("~/.emacs.d/snippets")))))))
+ (setq yas-snippet-dirs (list yas--default-user-snippets-dir)))))))
(tables (or (and table
(list table))
(yas--get-snippet-tables))))
TABLE is a symbol naming a passed to `yas--table-get-create'.
-When called interactively, prompt for the table name and
-whether (and where) to save the snippet, then quit the window."
+When called interactively, prompt for the table name."
(interactive (list (yas--read-table) t))
(cond
;; We have `yas--editing-template', this buffer's content comes from a
;; template which is already loaded and neatly positioned,...
;;
(yas--editing-template
- (yas--define-snippets-1 (yas--parse-template (yas--template-file yas--editing-template))
+ (yas--define-snippets-1 (yas--parse-template (yas--template-load-file yas--editing-template))
(yas--template-table yas--editing-template)))
;; Try to use `yas--guessed-modes'. If we don't have that use the
;; value from `yas--compute-major-mode-and-parents'
(set (make-local-variable 'yas--editing-template)
(yas--define-snippets-1 (yas--parse-template buffer-file-name)
table)))))
-
- (when (and interactive
- (or
- ;; Only offer to save this if it looks like a library or new
- ;; snippet (loaded from elisp, from a dir in `yas-snippet-dirs'
- ;; which is not the first, or from an unwritable file)
- ;;
- (not (yas--template-file yas--editing-template))
- (not (file-writable-p (yas--template-file yas--editing-template)))
- (and (listp yas-snippet-dirs)
- (second yas-snippet-dirs)
- (not (string-match (expand-file-name (first yas-snippet-dirs))
- (yas--template-file yas--editing-template)))))
- (y-or-n-p (yas--format "Looks like a library or new snippet. Save to new file? ")))
- (let* ((option (first (yas--guess-snippet-directories (yas--template-table yas--editing-template))))
- (chosen (and option
- (yas--make-directory-maybe option))))
- (when chosen
- (let ((default-file-name (or (and (yas--template-file yas--editing-template)
- (file-name-nondirectory (yas--template-file yas--editing-template)))
- (yas--template-name yas--editing-template))))
- (write-file (concat chosen "/"
- (read-from-minibuffer (format "File name to create in %s? " chosen)
- default-file-name)))
- (setf (yas--template-file yas--editing-template) buffer-file-name)))))
(when interactive
(yas--message 3 "Snippet \"%s\" loaded for %s."
(yas--template-name yas--editing-template)
- (yas--table-name (yas--template-table yas--editing-template)))
- (quit-window interactive)))
+ (yas--table-name (yas--template-table yas--editing-template)))))
+
+(defun yas-load-snippet-buffer-and-close (table &optional kill)
+ "Load the snippet with `yas-load-snippet-buffer', possibly
+ save, then `quit-window' if saved.
+
+If the snippet is new, ask the user whether (and where) to save
+it. If the snippet already has a file, just save it.
+
+The prefix argument KILL is passed to `quit-window'.
+
+Don't use this from a Lisp program, call `yas-load-snippet-buffer'
+and `kill-buffer' instead."
+ (interactive (list (yas--read-table) current-prefix-arg))
+ (yas-load-snippet-buffer table t)
+ (let ((file (yas--template-get-file yas--editing-template)))
+ (when (and (or
+ ;; Only offer to save this if it looks like a library or new
+ ;; snippet (loaded from elisp, from a dir in `yas-snippet-dirs'
+ ;; which is not the first, or from an unwritable file)
+ ;;
+ (not file)
+ (not (file-writable-p file))
+ (and (cdr-safe yas-snippet-dirs)
+ (not (string-prefix-p (expand-file-name (car yas-snippet-dirs)) file))))
+ (y-or-n-p (yas--format "Looks like a library or new snippet. Save to new file? ")))
+ (let* ((option (first (yas--guess-snippet-directories (yas--template-table yas--editing-template))))
+ (chosen (and option
+ (yas--make-directory-maybe option))))
+ (when chosen
+ (let ((default-file-name (or (and file (file-name-nondirectory file))
+ (yas--template-name yas--editing-template))))
+ (write-file (concat chosen "/"
+ (read-from-minibuffer (format "File name to create in %s? " chosen)
+ default-file-name)))
+ (setf (yas--template-load-file yas--editing-template) buffer-file-name))))))
+ (when buffer-file-name
+ (save-buffer)
+ (quit-window kill)))
(defun yas-tryout-snippet (&optional debug)
"Test current buffer's snippet template in other buffer."
(yas--current-template
(and parsed
(fboundp test-mode)
- (yas--populate-template (yas--make-blank-template)
- :table nil ;; no tables for ephemeral snippets
- :key (first parsed)
- :content (second parsed)
- :name (third parsed)
- :expand-env (sixth parsed)))))
+ (yas--make-template :table nil ;; no tables for ephemeral snippets
+ :key (first parsed)
+ :content (second parsed)
+ :name (third parsed)
+ :expand-env (sixth parsed)))))
(cond (yas--current-template
(let ((buffer-name (format "*testing snippet: %s*" (yas--template-name yas--current-template))))
(kill-buffer (get-buffer-create buffer-name))
groups-hash)))
+\f
+;;; User convenience functions, for using in `yas-key-syntaxes'
+
+(defun yas-try-key-from-whitespace (_start-point)
+ "As `yas-key-syntaxes' element, look for whitespace delimited key.
+
+A newline will be considered whitespace even if the mode syntax
+marks it as something else (typically comment ender)."
+ (skip-chars-backward "^[:space:]\n"))
+
+(defun yas-shortest-key-until-whitespace (_start-point)
+ "Like `yas-longest-key-from-whitespace' but take the shortest key."
+ (when (/= (skip-chars-backward "^[:space:]\n" (1- (point))) 0)
+ 'again))
+
+(defun yas-longest-key-from-whitespace (start-point)
+ "As `yas-key-syntaxes' element, look for longest key between point and whitespace.
+
+A newline will be considered whitespace even if the mode syntax
+marks it as something else (typically comment ender)."
+ (if (= (point) start-point)
+ (yas-try-key-from-whitespace start-point)
+ (forward-char))
+ (unless (<= start-point (1+ (point)))
+ 'again))
+
+
\f
;;; User convenience functions, for using in snippet definitions
The last element of POSSIBILITIES may be a list of strings."
(unless (or yas-moving-away-p
yas-modified-p)
- (setq possibilities (nreverse possibilities))
- (setq possibilities (if (listp (car possibilities))
- (append (reverse (car possibilities)) (rest possibilities))
- possibilities))
+ (let* ((last-link (last possibilities))
+ (last-elem (car last-link)))
+ (when (listp last-elem)
+ (setcar last-link (car last-elem))
+ (setcdr last-link (cdr last-elem))))
(some #'(lambda (fn)
(funcall fn "Choose: " possibilities))
yas-prompt-functions)))
;; field must be zero length
;;
(zerop (- (yas--field-start field) (yas--field-end field)))
- ;; skip if:
+ ;; field must have been modified
+ ;;
+ (yas--field-modified-p field)
+ ;; either:
(or
- ;; 1) is a nested field and it's been modified
+ ;; 1) it's a nested field
;;
- (and (yas--field-parent-field field)
- (yas--field-modified-p field))
+ (yas--field-parent-field field)
;; 2) ends just before the snippet end
;;
(and (eq field (car (last (yas--snippet-fields snippet))))
;; the field numbered 0, just before the exit marker, should
;; never be skipped
;;
- (not (zerop (yas--field-number field)))))
+ (not (and (yas--field-number field)
+ (zerop (yas--field-number field))))))
(defun yas--snippets-at-point (&optional all-snippets)
"Return a sorted list of snippets at point.
The most recently-inserted snippets are returned first."
(sort
- (remove nil (remove-duplicates (mapcar #'(lambda (ov)
- (overlay-get ov 'yas--snippet))
- (if all-snippets
- (overlays-in (point-min) (point-max))
- (nconc (overlays-at (point)) (overlays-at (1- (point))))))))
+ (delq nil (delete-dups
+ (mapcar (lambda (ov) (overlay-get ov 'yas--snippet))
+ (if all-snippets (overlays-in (point-min) (point-max))
+ (nconc (overlays-at (point))
+ (overlays-at (1- (point))))))))
#'(lambda (s1 s2)
(<= (yas--snippet-id s2) (yas--snippet-id s1)))))
(defvar yas--inhibit-overlay-hooks nil
"Bind this temporarily to non-nil to prevent running `yas--on-*-modification'.")
-(defmacro yas--inhibit-overlay-hooks (&rest body)
- "Run BODY with `yas--inhibit-overlay-hooks' set to t."
- (declare (indent 0))
- `(let ((yas--inhibit-overlay-hooks t))
- ,@body))
-
(defvar yas-snippet-beg nil "Beginning position of the last snippet committed.")
(defvar yas-snippet-end nil "End position of the last snippet committed.")
(setq yas-snippet-end (overlay-end control-overlay))
(delete-overlay control-overlay))
- (yas--inhibit-overlay-hooks
+ (let ((yas--inhibit-overlay-hooks t))
(when yas--active-field-overlay
(delete-overlay yas--active-field-overlay))
(when yas--field-protection-overlays
(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."
+Only clears the field if it hasn't been modified and point is at
+field start. This hook does nothing if an undo is in progress."
(unless (or yas--inhibit-overlay-hooks
+ (not (overlayp yas--active-field-overlay)) ; Avoid Emacs bug #21824.
(yas--undo-in-progress))
(let* ((field (overlay-get overlay 'yas--field))
(snippet (overlay-get yas--active-field-overlay 'yas--snippet)))
(yas--field-update-display field))
(yas--update-mirrors snippet))
(field
- (when (and (not after?)
+ (when (and (eq this-command 'self-insert-command)
(not (yas--field-modified-p field))
- (eq (point) (if (markerp (yas--field-start field))
- (marker-position (yas--field-start field))
- (yas--field-start field))))
+ (= (point) (yas--field-start field)))
(yas--skip-and-clear field))
(setf (yas--field-modified-p field) t))))))
\f
;; snippet outside the active field. Actual protection happens in
;; `yas--on-protection-overlay-modification'.
;;
-;; 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 error, which is ignored. but
-;; blocks all other million modification hooks. This presented some
-;; problems with stacked expansion.
+;; As of github #537 this no longer inhibits the command by issuing an
+;; error: all the snippets at point, including nested snippets, are
+;; automatically commited and the current command can proceed.
;;
(defun yas--make-move-field-protection-overlays (snippet field)
"Place protection overlays surrounding SNIPPET's FIELD.
;;
(when (< (buffer-size) end)
(save-excursion
- (yas--inhibit-overlay-hooks
+ (let ((yas--inhibit-overlay-hooks t))
(goto-char (point-max))
(newline))))
;; go on to normal overlay creation/moving
;; (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 erroneously 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)
"Signals a snippet violation, then issues error.
The error should be ignored in `debug-ignored-errors'"
- (unless yas--inhibit-overlay-hooks
- (cond ((not (or after?
- (yas--undo-in-progress)))
- (setq yas--protection-violation (point))
- (error "Exit the snippet first!")))))
+ (unless (or yas--inhibit-overlay-hooks
+ after?
+ (yas--undo-in-progress))
+ (let ((snippets (yas--snippets-at-point)))
+ (yas--message 3 "Comitting snippets. Action would destroy a protection overlay.")
+ (cl-loop for snippet in snippets
+ do (yas--commit-snippet snippet)))))
(add-to-list 'debug-ignored-errors "^Exit the snippet first!$")
"Expand snippet CONTENT at current point.
Text between START and END will be deleted before inserting
-template. EXPAND-ENV is are let-style variable to value bindings
+template. EXPAND-ENV is a list of (SYM VALUE) let-style dynamic bindings
considered when expanding the snippet."
+ (cl-assert (and yas-minor-mode
+ (memq 'yas--post-command-handler post-command-hook))
+ nil
+ "[yas] `yas-expand-snippet' needs properly setup `yas-minor-mode'")
(run-hooks 'yas-before-expand-snippet-hook)
;;
;; plain text will get recorded at the end.
;;
;; stacked expansion: also shoosh the overlay modification hooks
- (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)))
- (yas--inhibit-overlay-hooks
- (setq snippet
- (if expand-env
- (eval `(let* ,expand-env
- (insert content)
- (yas--snippet-create (point-min))))
- (insert content)
- (yas--snippet-create (point-min)))))))
+ (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 (current-column))
+ (let ((yas--inhibit-overlay-hooks t))
+ (setq snippet
+ (if expand-env
+ (eval `(let* ,expand-env
+ (insert content)
+ (yas--snippet-create start (point))))
+ (insert content)
+ (yas--snippet-create start (point))))))
;; stacked-expansion: This checks for stacked expansion, save the
;; `yas--previous-active-field' and advance its boundary.
(push `(apply yas--take-care-of-redo ,beg ,end ,snippet)
buffer-undo-list))))
-(defun yas--snippet-create (begin)
- "Create a snippet from a template inserted at BEGIN.
+(defun yas--snippet-create (begin end)
+ "Create a snippet from a template inserted at BEGIN to END.
Returns the newly created snippet."
- (let ((snippet (yas--make-snippet)))
- (goto-char begin)
- (yas--snippet-parse-create snippet)
+ (save-restriction
+ (narrow-to-region begin end)
+ (let ((snippet (yas--make-snippet)))
+ (goto-char begin)
+ (yas--snippet-parse-create snippet)
- ;; Sort and link each field
- (yas--snippet-sort-fields snippet)
+ ;; Sort and link each field
+ (yas--snippet-sort-fields snippet)
- ;; Create keymap overlay for snippet
- (setf (yas--snippet-control-overlay snippet)
- (yas--make-control-overlay snippet (point-min) (point-max)))
+ ;; Create keymap overlay for snippet
+ (setf (yas--snippet-control-overlay snippet)
+ (yas--make-control-overlay snippet (point-min) (point-max)))
- ;; Move to end
- (goto-char (point-max))
+ ;; Move to end
+ (goto-char (point-max))
- snippet))
+ snippet)))
\f
;;; Apropos adjacencies and "fom's":
(yas--calculate-adjacencies snippet)
;; Delete $-constructs
;;
- (yas--delete-regions yas--dollar-regions)
+ (save-restriction (widen) (yas--delete-regions yas--dollar-regions))
;; restore backquoted expression values
;;
(yas--restore-backquotes)
(n (line-beginning-position)))
(while (or (eql c ?\ )
(eql c ?\t))
- (incf n)
+ (cl-incf n)
(setq c (char-after n)))
n))
with their evaluated value into `yas--backquote-markers-and-strings'."
(while (re-search-forward yas--backquote-lisp-expression-regexp nil t)
(let ((current-string (match-string-no-properties 1)) transformed)
- (delete-region (match-beginning 0) (match-end 0))
+ (save-restriction (widen)
+ (delete-region (match-beginning 0) (match-end 0)))
(setq transformed (yas--eval-lisp (yas--read-lisp (yas--restore-escapes current-string '(?`)))))
(goto-char (match-beginning 0))
(when transformed
(let ((marker (make-marker)))
- (insert "Y") ;; quite horrendous, I love it :)
- (set-marker marker (point))
- (insert "Y")
+ (save-restriction
+ (widen)
+ (insert "Y") ;; quite horrendous, I love it :)
+ (set-marker marker (point))
+ (insert "Y"))
(push (cons marker transformed) yas--backquote-markers-and-strings))))))
(defun yas--restore-backquotes ()
(string (cdr marker-and-string)))
(save-excursion
(goto-char marker)
- (delete-char -1)
- (insert string)
- (delete-char 1)
+ (save-restriction
+ (widen)
+ (delete-char -1)
+ (insert string)
+ (delete-char 1))
(set-marker marker nil)))))
(defun yas--scan-sexps (from count)
- (condition-case _
+ (ignore-errors
+ (save-match-data ; `scan-sexps' may modify match data.
(with-syntax-table (standard-syntax-table)
- (scan-sexps from count))
- (error
- nil)))
+ (scan-sexps from count)))))
(defun yas--make-marker (pos)
"Create a marker at POS with nil `marker-insertion-type'."
;; after the ":", this will be
;; caught as a mirror with
;; transform later.
- (not (save-match-data
- (eq (string-match "$[ \t\n]*("
- (match-string-no-properties 2)) 0)))
+ (not (string-match-p "\\`\\$[ \t\n]*("
+ (match-string-no-properties 2)))
;; allow ${0: some exit text}
;; (not (and number (zerop number)))
(yas--make-field number
(not (string= reflection (buffer-substring-no-properties (yas--mirror-start mirror)
(yas--mirror-end mirror)))))
(goto-char (yas--mirror-start mirror))
- (yas--inhibit-overlay-hooks
+ (let ((yas--inhibit-overlay-hooks t))
(insert reflection))
(if (> (yas--mirror-end mirror) (point))
(delete-region (point) (yas--mirror-end mirror))
(yas--field-end field)))))
(setf (yas--field-modified-p field) t)
(goto-char (yas--field-start field))
- (yas--inhibit-overlay-hooks
+ (let ((yas--inhibit-overlay-hooks t))
(insert transformed)
(if (> (yas--field-end field) (point))
(delete-region (point) (yas--field-end field))
;;
(defun yas--post-command-handler ()
"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)
+ (cond ((eq 'undo this-command)
;;
;; After undo revival the correct field is sometimes not
;; restored correctly, this condition handles that
;; depending on the context.
;;
(put 'yas-expand 'function-documentation
- '(yas--expand-from-trigger-key-doc))
-(defun yas--expand-from-trigger-key-doc ()
+ '(yas--expand-from-trigger-key-doc t))
+(defun yas--expand-from-trigger-key-doc (context)
"A doc synthesizer for `yas--expand-from-trigger-key-doc'."
- (let ((fallback-description
- (cond ((eq yas-fallback-behavior 'call-other-command)
- (let* ((fallback (yas--keybinding-beyond-yasnippet)))
- (or (and fallback
- (format " call command `%s'." (pp-to-string fallback)))
- " do nothing (`yas-expand' doesn't shadow\nanything)")))
- ((eq yas-fallback-behavior 'return-nil)
- ", do nothing.")
- (t
- ", defer to `yas-fallback-behaviour' (which see)"))))
+ (let* ((yas-fallback-behavior (and context yas-fallback-behavior))
+ (fallback-description
+ (cond ((eq yas-fallback-behavior 'call-other-command)
+ (let* ((fallback (yas--keybinding-beyond-yasnippet)))
+ (or (and fallback
+ (format "call command `%s'."
+ (pp-to-string fallback)))
+ "do nothing (`yas-expand' doesn't override\nanything).")))
+ ((eq yas-fallback-behavior 'return-nil)
+ "do nothing.")
+ (t "defer to `yas-fallback-behavior' (which see)."))))
(concat "Expand a snippet before point. If no snippet
-expansion is possible,"
+expansion is possible, "
fallback-description
"\n\nOptional argument FIELD is for non-interactive use and is an
object satisfying `yas--field-p' to restrict the expansion to.")))
-(put 'yas-expand-from-keymap 'function-documentation '(yas--expand-from-keymap-doc))
-(defun yas--expand-from-keymap-doc ()
+(put 'yas-expand-from-keymap 'function-documentation
+ '(yas--expand-from-keymap-doc t))
+(defun yas--expand-from-keymap-doc (context)
"A doc synthesizer for `yas--expand-from-keymap-doc'."
(add-hook 'temp-buffer-show-hook 'yas--snippet-description-finish-runonce)
(concat "Expand/run snippets from keymaps, possibly falling back to original binding.\n"
- (when (eq this-command 'describe-key)
+ (when (and context (eq this-command 'describe-key))
(let* ((vec (this-single-command-keys))
(templates (mapcan #'(lambda (table)
(yas--fetch table vec))
(when (> yas-verbosity level)
(message "%s" (apply #'yas--format message args))))
+(defun yas--warning (format-control &rest format-args)
+ (let ((msg (apply #'format format-control format-args)))
+ (display-warning 'yasnippet msg :warning)
+ (yas--message 1 msg)))
+
(defun yas--format (format-control &rest format-args)
(apply #'format (concat "[yas] " format-control) format-args))
(setq file nil))))
root))))
-;; `c-neutralize-syntax-in-CPP` sometimes fires "End of Buffer" error
-;; (when it execute forward-char) and interrupt the after change
-;; hook. Thus prevent the insert-behind hook of yasnippet to be
-;; invoked. Here's a way to reproduce it:
-
-;; # open a *new* Emacs.
-;; # load yasnippet.
-;; # open a *new* .cpp file.
-;; # input "inc" and press TAB to expand the snippet.
-;; # select the `#include <...>` snippet.
-;; # type inside `<>`
-
-(defadvice c-neutralize-syntax-in-CPP
- (around yas--mp/c-neutralize-syntax-in-CPP activate)
- "Adviced `c-neutralize-syntax-in-CPP' to properly
-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)))))
-
-;; disable c-electric-* serial command in YAS fields
-(add-hook 'c-mode-common-hook
- '(lambda ()
- (dolist (k '(":" ">" ";" "<" "{" "}"))
- (define-key (symbol-value (make-local-variable 'yas-keymap))
- k 'self-insert-command))))
\f
;;; Backward compatibility to yasnippet <= 0.7
-(defvar yas--exported-syms '(;; `defcustom's
+(defun yas-initialize ()
+ "For backward compatibility, enable `yas-minor-mode' globally."
+ (declare (obsolete "Use (yas-global-mode 1) instead." "0.8"))
+ (yas-global-mode 1))
+
+(defvar yas--backported-syms '(;; `defcustom's
;;
yas-snippet-dirs
yas-prompt-functions
yas-exit-snippet
yas-exit-all-snippets
yas-skip-and-clear-or-delete-char
+ yas-initialize
;; symbols that I "exported" for use
;; in snippets and hookage
yas-snippet-end
yas-modified-p
yas-moving-away-p
- yas-text
yas-substr
yas-choose-value
yas-key-to-value
yas-unimplemented
yas-define-condition-cache
yas-hippie-try-expand
- yas-active-keys
;; debug definitions
;; yas-debug-snippet-vars
;; yas-call-with-snippet-dirs
;; yas-with-snippet-dirs
)
- "Exported yasnippet symbols.
-
-i.e. ones that I will try to keep in future yasnippet versions
-and ones that other elisp libraries can more or less safely rely
-upon.")
+ "Backported yasnippet symbols.
-(defvar yas--dont-backport '(yas-active-keys)
- "Exported symbols that don't map back to \"yas/*\" variants.")
+They are mapped to \"yas/*\" variants.")
-(dolist (sym (set-difference yas--exported-syms yas--dont-backport))
+(dolist (sym yas--backported-syms)
(let ((backported (intern (replace-regexp-in-string "^yas-" "yas/" (symbol-name sym)))))
(when (boundp sym)
(make-obsolete-variable backported sym "yasnippet 0.8")
(make-obsolete backported sym "yasnippet 0.8")
(defalias backported sym))))
+(defvar yas--exported-syms
+ (let (exported)
+ (mapatoms (lambda (atom)
+ (if (and (or (and (boundp atom)
+ (not (get atom 'byte-obsolete-variable)))
+ (and (fboundp atom)
+ (not (get atom 'byte-obsolete-info))))
+ (string-match-p "^yas-[^-]" (symbol-name atom)))
+ (push atom exported))))
+ exported)
+ "Exported yasnippet symbols.
+
+i.e. the ones with \"yas-\" single dash prefix. I will try to
+keep them in future yasnippet versions and other elisp libraries
+can more or less safely rely upon them.")
+
\f
(provide 'yasnippet)
-
-;;; yasnippet.el ends here
;; Local Variables:
;; coding: utf-8
+;; indent-tabs-mode: nil
;; byte-compile-warnings: (not cl-functions)
;; End:
+;;; yasnippet.el ends here