;;; yasnippet.el --- Yet another snippet extension for Emacs.
-;; Copyright (C) 2008-2012 Free Software Foundation, Inc.
+;; Copyright (C) 2008-2013 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
;; Package-version: 0.8.0
;; X-URL: http://github.com/capitaomorte/yasnippet
;; `yas-snippet-dirs'
;;
;; The directory where user-created snippets are to be
-;; stored. Can also be a list of directories. In that case,
+;; 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 shadow other dir's snippets. Also, the first
;; directory is taken as the default for storing the user's
;; new snippets.
;;
;; `yas-extra-modes'
;;
;; A local variable that you can set in a hook to override
-;; snippet-lookup based on major mode. It is a a symbol (or
+;; snippet-lookup based on major mode. It is a symbol (or
;; list of symbols) that correspond to subdirectories of
;; `yas-snippet-dirs' and is used for deciding which
;; snippets to consider for the active buffer.
;; M-x yas-expand
;;
;; Try to expand snippets before point. In `yas-minor-mode',
-;; this is bound to `yas-trigger-key' which you can customize.
+;; this is normally bound to TAB, but you can customize it in
+;; `yas-minor-mode-map'.
;;
;; M-x yas-load-directory
;;
;;
;; M-x yas-describe-tables
;;
-;; Lists known snippets in a separate buffer. User is
+;; Lists known snippets in a separate buffer. User is
;; prompted as to whether only the currently active tables
;; are to be displayed, or all the tables for all major
;; modes.
;;
-;; The `dropdown-list.el' extension is bundled with YASnippet, you
-;; can optionally use it the preferred "prompting method", puting in
-;; your .emacs file, for example:
+;; If you have `dropdown-list' installed, you can optionally use it
+;; as the preferred "prompting method", putting in your .emacs file,
+;; for example:
;;
;; (require 'dropdown-list)
;; (setq yas-prompt-functions '(yas-dropdown-prompt
(require 'easymenu)
(require 'help-mode)
+(defvar yas--editing-template)
+(defvar yas--guessed-modes)
+(defvar yas--indent-original-column)
+(defvar yas--scheduled-jit-loads)
+(defvar yas-keymap)
+(defvar yas-selected-text)
+(defvar yas-verbosity)
+(defvar yas--current-template)
+
\f
;;; User customizable variables
(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
+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
+shadow other dir's snippets. Also, the first directory is taken
as the default for storing the user's new snippets."
:type '(choice (string :tag "Single directory (string)")
(repeat :args (string) :tag "List of directories (strings)"))
(yas-reload-all)))))
(defun yas-snippet-dirs ()
- "Returns `yas-snippet-dirs' (which see) as a list."
+ "Return `yas-snippet-dirs' (which see) as a list."
(if (listp yas-snippet-dirs) yas-snippet-dirs (list yas-snippet-dirs)))
(defvaralias 'yas/root-directory 'yas-snippet-dirs)
+(defcustom yas-new-snippet-default "\
+# -*- mode: snippet -*-
+# 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}
+# --
+$0"
+ "Default snippet to use when creating a new snippet.
+If nil, don't use any snippet."
+ :type 'string
+ :group 'yasnippet)
+
(defcustom yas-prompt-functions '(yas-x-prompt
yas-dropdown-prompt
yas-completing-prompt
- `auto' Indent each line of the snippet with `indent-according-to-mode'
-Every other value means don't apply any snippet-side indendation
+Every other value means don't apply any snippet-side indentation
after expansion (the manual per-line \"$>\" indentation still
applies)."
:type '(choice (const :tag "Nothing" nothing)
:type 'boolean
:group 'yasnippet)
-(defcustom yas-trigger-key "TAB"
- "The key bound to `yas-expand' when function `yas-minor-mode' is active.
-
-Value is a string that is converted to the internal Emacs key
-representation using `read-kbd-macro'."
- :type 'string
- :group 'yasnippet
- :set #'(lambda (symbol key)
- (let ((old (and (boundp symbol)
- (symbol-value symbol))))
- (set-default symbol key)
- ;; On very first loading of this defcustom,
- ;; `yas-trigger-key' is *not* loaded.
- (if (fboundp 'yas--trigger-key-reload)
- (yas--trigger-key-reload old)))))
-
-(defcustom yas-next-field-key '("TAB" "<tab>")
- "The key to navigate to next field when a snippet is active.
-
-Value is a string that is converted to the internal Emacs key
-representation using `read-kbd-macro'.
-
-Can also be a list of strings."
- :type '(choice (string :tag "String")
- (repeat :args (string) :tag "List of strings"))
- :group 'yasnippet
- :set #'(lambda (symbol val)
- (set-default symbol val)
- (if (fboundp 'yas--init-yas-in-snippet-keymap)
- (yas--init-yas-in-snippet-keymap))))
-
-
-(defcustom yas-prev-field-key '("<backtab>" "<S-tab>")
- "The key to navigate to previous field when a snippet is active.
-
-Value is a string that is converted to the internal Emacs key
-representation using `read-kbd-macro'.
-
-Can also be a list of strings."
- :type '(choice (string :tag "String")
- (repeat :args (string) :tag "List of strings"))
- :group 'yasnippet
- :set #'(lambda (symbol val)
- (set-default symbol val)
- (if (fboundp 'yas--init-yas-in-snippet-keymap)
- (yas--init-yas-in-snippet-keymap))))
-
-(defcustom yas-skip-and-clear-key '("C-d" "<delete>" "<deletechar>")
- "The key to clear the currently active field.
-
-Value is a string that is converted to the internal Emacs key
-representation using `read-kbd-macro'.
-
-Can also be a list of strings."
- :type '(choice (string :tag "String")
- (repeat :args (string) :tag "List of strings"))
- :group 'yasnippet
- :set #'(lambda (symbol val)
- (set-default symbol val)
- (if (fboundp 'yas--init-yas-in-snippet-keymap)
- (yas--init-yas-in-snippet-keymap))))
-
(defcustom yas-triggers-in-field nil
- "If non-nil, `yas-next-field-key' can trigger stacked expansions.
+ "If non-nil, allow stacked expansions (snippets inside snippets).
-Otherwise, `yas-next-field-key' just tries to move on to the next
-field"
+Otherwise `yas-next-field-or-maybe-expand' just moves on to the
+next field"
:type 'boolean
:group 'yasnippet)
(defcustom yas-fallback-behavior 'call-other-command
- "How to act when `yas-trigger-key' does *not* expand a snippet.
+ "How to act when `yas-expand' does *not* expand a snippet.
- `call-other-command' means try to temporarily disable YASnippet
- and call the next command bound to `yas-trigger-key'.
+ and call the next command bound to whatever key was used to
+ invoke `yas-expand'.
- nil or the symbol `return-nil' mean do nothing. (and
`yas-expand' returns nil)
-- A lisp form (apply COMMAND . ARGS) means interactively call
+- A Lisp form (apply COMMAND . ARGS) means interactively call
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 "No menu" nil))
:group 'yasnippet)
-(defcustom yas-trigger-symbol (if (eq window-system 'mac)
- (char-to-string ?\x21E5) ;; little ->| sign
+(defcustom yas-trigger-symbol (or (and (eq window-system 'mac)
+ (ignore-errors
+ (char-to-string ?\x21E5))) ;; little ->| sign
" =>")
"The text that will be used in menu to represent the trigger."
:type 'string
"If non-nil, snippet expansion wraps around selected region.
The wrapping occurs just before the snippet's exit marker. This
-can be overriden on a per-snippet basis."
+can be overridden on a per-snippet basis."
:type 'boolean
:group 'yasnippet)
(defcustom yas-visit-from-menu nil
"If non-nil visit snippets's files from menu, instead of expanding them.
-This cafn only work when snippets are loaded from files."
+This can only work when snippets are loaded from files."
:type 'boolean
:group 'yasnippet)
expansion simply by placing the cursor after a valid tab trigger,
using whichever commands.
-Optionallly, set this to something like '(self-insert-command) if
+Optionally, set this to something like '(self-insert-command) if
you to wish restrict expansion to only happen when the last
letter of the snippet tab trigger was typed immediately before
the trigger key itself."
:group 'yasnippet)
\f
-;;; User can also customize the next defvars
-
-(defun yas--define-some-keys (keys keymap definition)
- "Bind KEYS to DEFINITION in KEYMAP, read with `read-kbd-macro'."
- (let ((keys (or (and (listp keys) keys)
- (list keys))))
- (dolist (key keys)
- (define-key keymap (read-kbd-macro key) definition))))
-
-(defvar yas-keymap
- (let ((map (make-sparse-keymap)))
- (mapc #'(lambda (binding)
- (yas--define-some-keys (car binding) map (cdr binding)))
- `((,yas-next-field-key . yas-next-field-or-maybe-expand)
- (,yas-prev-field-key . yas-prev-field)
- ("C-g" . yas-abort-snippet)
- (,yas-skip-and-clear-key . yas-skip-and-clear-or-delete-char)))
- map)
- "The keymap active while a snippet expansion is in progress.")
+;;; User-visible variables
+
+(defvar yas-keymap (let ((map (make-sparse-keymap)))
+ (define-key map [(tab)] 'yas-next-field-or-maybe-expand)
+ (define-key map (kbd "TAB") 'yas-next-field-or-maybe-expand)
+ (define-key map [(shift tab)] 'yas-prev-field)
+ (define-key map [backtab] 'yas-prev-field)
+ (define-key map (kbd "C-g") 'yas-abort-snippet)
+ (define-key map (kbd "C-d") 'yas-skip-and-clear-or-delete-char)
+ 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_\")
+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
+syntax. Triggering after
foo-bar
-will, according to the \"w\" element first try \"bar\". If that
+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.")
`yas-snippet-end' : Similar to beg.
-Attention: These hooks are not run when exiting nested/stackd snippet expansion!")
+Attention: These hooks are not run when exiting nested/stacked snippet expansion!")
(defvar yas-before-expand-snippet-hook
'()
(defvar yas-buffer-local-condition
'(if (and (or (fourth (syntax-ppss))
(fifth (syntax-ppss)))
- (eq (symbol-function this-command) 'yas-expand-from-trigger-key))
+ this-command
+ (eq this-command 'yas-expand-from-trigger-key))
'(require-snippet-condition . force-in-comment)
t)
"Snippet expanding condition.
-This variable is a lisp form which is evaluated everytime a
-snippet expansion is attemped:
+This variable is a Lisp form which is evaluated every time a
+snippet expansion is attempted:
* If it evaluates to nil, no snippets can be expanded.
considered
* Snippets bearing conditions that evaluate to nil (or
- produce an error) won't be onsidered.
+ produce an error) won't be considered.
* If the snippet has a condition that evaluates to non-nil
RESULT:
\f
;;; Internal variables
-(defvar yas--version "0.8.0 (beta)")
+(defvar yas--version "0.8.0beta")
(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.")
+ "A list of mode which is well known but not part of Emacs.")
(defvar yas--escaped-characters
'(?\\ ?` ?\" ?' ?$ ?} ?{ ?\( ?\))
(defvar last-buffer-undo-list nil)
(defvar yas--minor-mode-menu nil
- "Holds the YASnippet menu")
+ "Holds the YASnippet menu.")
(defun yas--init-minor-keymap ()
+ "Set up the `yas-minor-mode' keymap."
(let ((map (make-sparse-keymap)))
(when yas-use-menu
(easy-menu-define yas--minor-mode-menu
["Good grace "
(setq yas-good-grace
(not yas-good-grace))
- :help "If non-nil don't raise errors in bad embedded eslip in snippets"
+ :help "If non-nil don't raise errors in bad embedded elisp in snippets"
:style toggle :selected yas-good-grace]
)
"----"
["Reload everything" yas-reload-all
:help "Cleanup stuff, reload snippets, rebuild menus"]
["About" yas-about
- :help "Display some information about YASsnippet"])))
+ :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)
(defvar yas-minor-mode-map (yas--init-minor-keymap)
"The keymap used when `yas-minor-mode' is active.")
-(defun yas--trigger-key-reload (&optional unbind-key)
- "Rebind `yas-expand' to the new value of `yas-trigger-key'.
-
-With optional UNBIND-KEY, try to unbind that key from
-`yas-minor-mode-map'."
- (when (and unbind-key
- (stringp unbind-key)
- (not (string= unbind-key "")))
- (define-key yas-minor-mode-map (read-kbd-macro unbind-key) nil))
- (when (and yas-trigger-key
- (stringp yas-trigger-key)
- (not (string= yas-trigger-key "")))
- (define-key yas-minor-mode-map (read-kbd-macro yas-trigger-key) 'yas-expand)))
-
(defvar yas--tables (make-hash-table)
"A hash table of mode symbols to `yas--table' objects.")
found when traversing snippet directories with
`yas-load-directory'.
-There might be additionalal parenting information stored in the
+There might be additional parenting information stored in the
`derived-mode-parent' property of some mode symbols, but that is
not recorded here.")
+(defvar yas--ancestors (make-hash-table)
+ "A hash table of mode symbols do lists of all parent mode symbols.
+
+A cache managed by `yas--all-parents'")
+
(defvar yas--direct-keymaps (list)
"Keymap alist supporting direct snippet keybindings.
-This variable is is placed in `emulation-mode-map-alists'.
+This variable is placed in `emulation-mode-map-alists'.
-Its elements looks like (TABLE-NAME . KEYMAP). They're
+Its elements looks like (TABLE-NAME . KEYMAP). They're
instantiated on `yas-reload-all' but KEYMAP is added to only when
-loading snippets. `yas--direct-TABLE-NAME' is then a variable set
-buffer-locally when entering `yas-minor-mode'. KEYMAP binds all
+loading snippets. `yas--direct-TABLE-NAME' is then a variable set
+buffer-locally when entering `yas-minor-mode'. KEYMAP binds all
defined direct keybindings to the command
`yas-expand-from-keymap' which then which snippet to expand.")
modes-to-activate)))))
(defvar yas-minor-mode-hook nil
- "Hook run when yas-minor-mode is turned on")
+ "Hook run when `yas-minor-mode' is turned on.")
;;;###autoload
(define-minor-mode yas-minor-mode
"Toggle YASnippet mode.
-When YASnippet mode is enabled, the `yas-trigger-key' key expands
-snippets of code depending on the major mode.
+When YASnippet mode is enabled, `yas-expand', normally bound to
+the TAB key, expands snippets of code depending on the major
+mode.
With no argument, this command toggles the mode.
positive prefix argument turns on the mode.
Negative prefix argument turns off the mode.
-You can customize the key through `yas-trigger-key'.
-
Key bindings:
\\{yas-minor-mode-map}"
nil
" yas"
:group 'yasnippet
(cond (yas-minor-mode
- ;; Reload the trigger key
- ;;
- (yas--trigger-key-reload)
;; Install the direct keymaps in `emulation-mode-map-alists'
;; (we use `add-hook' even though it's not technically a hook,
;; but it works). Then define variables named after modes to
(remove-hook 'post-command-hook 'yas--post-command-handler t)
(remove-hook 'emulation-mode-map-alists 'yas--direct-keymaps))))
-(defvar yas--dont-activate '(minibufferp)
- "If non-nil don't let `yas-minor-mode-on' active yas for this buffer.
+(defvar yas-dont-activate '(minibufferp)
+ "If non-nil don't let `yas-global-mode' affect some buffers.
-If a function, then its result is used.
+If a function of zero arguments, then its result is used.
If a list of functions, then all functions must return nil to
activate yas for this buffer.
-`yas-minor-mode-on' is usually called by `yas-global-mode' so
-this effectively lets you define exceptions to the \"global\"
-behaviour. Can also be a function of zero arguments.")
-(make-variable-buffer-local 'yas--dont-activate)
+In Emacsen <= 23, this variable is buffer-local. Because
+`yas-minor-mode-on' is called by `yas-global-mode' after
+executing the buffer's major mode hook, setting this variable
+there is an effective way to define exceptions to the \"global\"
+activation behaviour.
+
+In Emacsen > 23, only the global value is used. To define
+per-mode exceptions to the \"global\" activation behaviour, call
+`yas-minor-mode' with a negative argument directily in the major
+mode's hook.")
+(unless (> emacs-major-version 23)
+ (with-no-warnings
+ (make-variable-buffer-local 'yas-dont-activate)))
+
(defun yas-minor-mode-on ()
"Turn on YASnippet minor mode.
-Do this unless `yas--dont-activate' is truish "
+Honour `yas-dont-activate', which see."
(interactive)
- (unless (cond ((functionp yas--dont-activate)
- (funcall yas--dont-activate))
- ((consp yas--dont-activate)
- (some #'funcall yas--dont-activate))
- (yas--dont-activate))
- ;; Load all snippets definitions unless we still don't have a
- ;; root-directory or some snippets have already been loaded.
- ;;
+ ;; Check `yas-dont-activate'
+ (unless (cond ((functionp yas-dont-activate)
+ (funcall yas-dont-activate))
+ ((consp yas-dont-activate)
+ (some #'funcall yas-dont-activate))
+ (yas-dont-activate))
(yas-minor-mode 1)))
;;;###autoload
:group 'yasnippet
:require 'yasnippet)
-(defadvice yas-global-mode (before yas--reload-with-jit (arg) activate)
- (cond ((and arg
- (numberp arg)
- (> arg 1))
- ;; explicitly enabling
- (yas-reload-all))
- ((not yas-global-mode)
- ;; toggling
- (yas-reload-all))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Major mode stuff
-;;
+(defun yas--global-mode-reload-with-jit-maybe ()
+ "Run `yas-reload-all' when `yas-global-mode' is on."
+ (when yas-global-mode (yas-reload-all)))
+
+(add-hook 'yas-global-mode-hook 'yas--global-mode-reload-with-jit-maybe)
+
+\f
+;;; Major mode stuff
+
(defvar yas--font-lock-keywords
(append '(("^#.*$" . font-lock-comment-face))
lisp-font-lock-keywords
("}"
(0 font-lock-keyword-face)))))
-(defun yas--init-major-keymap ()
+(defvar snippet-mode-map
(let ((map (make-sparse-keymap)))
(easy-menu-define nil
map
(when (third ent)
(define-key map (third ent) (second ent)))
(vector (first ent) (second ent) t))
- (list
- (list "Load this snippet" 'yas-load-snippet-buffer "\C-c\C-c")
- (list "Try out this snippet" 'yas-tryout-snippet "\C-c\C-t")))))
- map))
-
-(defvar snippet-mode-map
- (yas--init-major-keymap)
- "The keymap used when `snippet-mode' is active")
+ '(("Load this snippet" yas-load-snippet-buffer "\C-c\C-c")
+ ("Try out this snippet" yas-tryout-snippet "\C-c\C-t")))))
+ map)
+ "The keymap used when `snippet-mode' is active.")
(define-derived-mode snippet-mode text-mode "Snippet"
)
(defun yas--populate-template (template &rest args)
- "Helper function to populate a template with properties"
- (let (p v)
- (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))
+ "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-uuidhash'
A hash table mapping snippets uuid's to the same `yas--template'
- objects. A snippet uuid defaults to the snippet's name.
-"
+ objects. A snippet uuid defaults to the snippet's name."
name
(hash (make-hash-table :test 'equal))
(uuidhash (make-hash-table :test 'equal))
;; Apropos storing/updating in TABLE, this works in two steps:
;;
;; 1. `yas--remove-template-by-uuid' removes any
-;; keyhash-namehash-template mappings from TABLE, grabing the
+;; keyhash-namehash-template mappings from TABLE, grabbing the
;; snippet by its uuid. Also removes mappings from TABLE's
;; `yas--table-direct-keymap' (FIXME: and should probably take care
;; of potentially stale menu bindings right?.)
(let ((name (yas--template-name template))
(key (yas--template-key template))
(keybinding (yas--template-keybinding template))
- (menu-binding-pair (yas--template-menu-binding-pair-get-create template)))
+ (_menu-binding-pair (yas--template-menu-binding-pair-get-create template)))
(dolist (k (remove nil (list key keybinding)))
(puthash name
template
(yas--update-template-menu table template)))
(defun yas--update-template-menu (table template)
- "Update every menu-related for TEMPLATE"
+ "Update every menu-related for TEMPLATE."
(let ((menu-binding-pair (yas--template-menu-binding-pair-get-create template))
(key (yas--template-key template))
(keybinding (yas--template-keybinding template)))
(car (yas--template-menu-binding-pair template))))))
(defun yas--namehash-templates-alist (namehash)
+ "Return NAMEHASH as an alist."
(let (alist)
(maphash #'(lambda (k v)
(push (cons k v) alist))
templates))))
(defun yas--require-template-specific-condition-p ()
- "Decides if this buffer requests/requires snippet-specific
+ "Decide if this buffer requests/requires snippet-specific
conditions to filter out potential expansions."
(if (eq 'always yas-buffer-local-condition)
'always
(cdr local-condition)))))))
(defun yas--template-can-expand-p (condition requirement)
- "Evaluates CONDITION and REQUIREMENT and returns a boolean"
+ "Evaluate CONDITION and REQUIREMENT and return a boolean."
(let* ((result (or (null condition)
(yas--eval-condition condition))))
(cond ((eq requirement t)
(eq requirement result)))))
(defun yas--all-parents (mode)
- "Returns a list of all parent modes of MODE"
- (let ((parents (gethash mode yas--parents)))
- (append parents
- (mapcan #'yas--all-parents parents))))
+ "Return a list of all parent modes of MODE."
+ (or (gethash mode yas--ancestors)
+ (let ((seen '()))
+ (labels ((yas--all-parents-1
+ (m)
+ (cond ((memq m seen)
+ (yas--message 1
+ "Cyclic parenthood: mode %s has already seen as a parent of mode %s"
+ m mode)
+ nil)
+ (t
+ (let* ((parents (gethash m yas--parents)))
+ (setq seen (append seen parents))
+ (append parents (mapcan #'yas--all-parents-1 parents)))))))
+ (puthash mode (yas--all-parents-1 mode)
+ yas--ancestors)))))
(defun yas--table-templates (table)
(when table
(let ((acc (list)))
- (maphash #'(lambda (key namehash)
+ (maphash #'(lambda (_key namehash)
(maphash #'(lambda (name template)
(push (cons name template) acc))
namehash))
(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."
+ "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)
(defun yas--table-all-keys (table)
- (when table
- (let ((acc))
- (maphash #'(lambda (key namehash)
- (when (yas--filter-templates-by-condition (yas--namehash-templates-alist namehash))
- (push key acc)))
- (yas--table-hash table))
- acc)))
+ "Get trigger keys of all active snippets in TABLE."
+ (let ((acc))
+ (maphash #'(lambda (key namehash)
+ (when (yas--filter-templates-by-condition (yas--namehash-templates-alist namehash))
+ (push key acc)))
+ (yas--table-hash table))
+ acc))
(defun yas--table-mode (table)
(intern (yas--table-name table)))
\f
-;;; Internal functions:
+;;; 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 judgement."
+ "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--get-snippet-tables ()
"Get snippet tables for current buffer.
-Return a list of `yas--table' objects. The list of modes to
+Return a list of `yas--table' objects. The list of modes to
consider is returned by `yas--modes-to-activate'"
(remove nil
- (mapcar #'(lambda (mode-name)
- (gethash mode-name yas--tables))
+ (mapcar #'(lambda (name)
+ (gethash name yas--tables))
(yas--modes-to-activate))))
(defun yas--menu-keymap-get-create (mode &optional 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
(defun yas--parse-template (&optional file)
(cdr where)
(yas--template-expand-env yas--current-template)))))))
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Popping up for keys and templates
-;;
+(defun yas--key-from-desc (text)
+ "Return a yasnippet key from a description string TEXT."
+ (replace-regexp-in-string "\\(\\w+\\).*" "\\1" text))
+
+\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.
-TEMPLATES is a list of `yas--template'."
+TEMPLATES is a list of `yas--template'.
+
+Optional PROMPT sets the prompt to use."
(when templates
(setq templates
(sort templates #'(lambda (t1 t2)
yas-prompt-functions))))
(defun yas--prompt-for-keys (keys &optional prompt)
- "Interactively choose a template key from the list KEYS."
+ "Interactively choose a template key from the list KEYS.
+
+Optional PROMPT sets the prompt to use."
(when keys
(some #'(lambda (fn)
(funcall fn (or prompt "Choose a snippet key: ") keys))
yas-prompt-functions)))
(defun yas--prompt-for-table (tables &optional prompt)
+ "Interactively choose a table from the list TABLES.
+
+Optional PROMPT sets the prompt to use."
(when tables
(some #'(lambda (fn)
(funcall fn (or prompt "Choose a snippet table: ")
;; actually a `yas--template', defer to `yas--x-prompt-pretty-templates'
;;
;; This would be better implemented by passing CHOICES as a
- ;; strucutred tree rather than a list. Modifications would go as far
+ ;; structured tree rather than a list. Modifications would go as far
;; up as `yas--all-templates' I think.
;;
(when (and window-system choices)
ido-mode))
(yas-completing-prompt prompt choices display-fn #'ido-completing-read)))
-(eval-when-compile (require 'dropdown-list nil t))
-(defun yas-dropdown-prompt (prompt choices &optional display-fn)
- (when (featurep 'dropdown-list)
+(defun yas-dropdown-prompt (_prompt choices &optional display-fn)
+ (when (fboundp 'dropdown-list)
(let (formatted-choices
filtered-choices
d
0)))
(nth position filtered-choices))))
-(defun yas-no-prompt (prompt choices &optional display-fn)
+(defun yas-no-prompt (_prompt choices &optional _display-fn)
(first choices))
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Loading snippets from files
+\f
+;;; Defining snippets
+;; This consists of creating and registering `yas--template' objects in the
+;; correct tables.
;;
+
+(defvar yas--creating-compiled-snippets nil)
+
+(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))
+
+(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)
+
+Within these, only KEY and TEMPLATE are actually mandatory.
+
+TEMPLATE might be a Lisp form or a string, depending on whether
+this is a snippet or a snippet-command.
+
+CONDITION, EXPAND-ENV and KEYBINDING are Lisp forms, they have
+been `yas--read-lisp'-ed and will eventually be
+`yas--eval-lisp'-ed.
+
+The remaining elements are strings.
+
+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.
+
+You can use `yas--parse-template' to return such lists based on
+the current buffers contents."
+ (if yas--creating-compiled-snippets
+ (progn
+ (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")))
+ ;; Normal case.
+ (let ((snippet-table (yas--table-get-create mode))
+ (template nil))
+ (dolist (snippet snippets)
+ (setq template (yas--define-snippets-1 snippet
+ snippet-table)))
+ template)))
+
+\f
+;;; Loading snippets from files
+
(defun yas--load-yas-setup-file (file)
- (load file 'noerror))
+ (if (not yas--creating-compiled-snippets)
+ ;; Normal case.
+ (load file 'noerror)
+ (let ((elfile (concat file ".el")))
+ (when (file-exists-p elfile)
+ (insert ";;; .yas-setup.el support file if any:\n;;;\n")
+ (insert-file-contents elfile)
+ (goto-char (point-max))))))
+
+(defun yas--define-parents (mode parents)
+ "Add PARENTS to the list of MODE's parents."
+ (puthash mode (remove-duplicates
+ (append parents
+ (gethash mode yas--parents)))
+ yas--parents))
-(defun yas-load-directory (top-level-dir &optional use-jit)
+(defun yas-load-directory (top-level-dir &optional use-jit interactive)
"Load snippets in directory hierarchy TOP-LEVEL-DIR.
Below TOP-LEVEL-DIR each directory should be a mode name.
Optional USE-JIT use jit-loading of snippets."
- (interactive "DSelect the root directory: ")
+ (interactive "DSelect the root directory: ni\np")
(unless yas-snippet-dirs
(setq yas-snippet-dirs top-level-dir))
(dolist (dir (yas--subdirs top-level-dir))
;;
(yas--define-parents mode-sym parents)
(yas--menu-keymap-get-create mode-sym)
- (let ((form `(yas--load-directory-1 ,dir
- ',mode-sym
- ',parents)))
- (if use-jit
- (yas--schedule-jit mode-sym form)
- (eval form)))))
- (when (interactive-p)
+ (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)))))
+ (when interactive
(yas--message 3 "Loaded snippets from %s." top-level-dir)))
-(defun yas--load-directory-1 (directory mode-sym parents &optional no-compiled-snippets)
+(defun yas--load-directory-1 (directory mode-sym)
"Recursively load snippet templates from DIRECTORY."
- (unless (file-exists-p (concat directory "/" ".yas-skip"))
- (if (and (not no-compiled-snippets)
- (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)
- (yas--load-directory-2 directory mode-sym))))
+ (if yas--creating-compiled-snippets
+ (let ((output-file (expand-file-name ".yas-compiled-snippets.el"
+ directory)))
+ (with-temp-file output-file
+ (insert (format ";;; Compiled snippets and support files for `%s'\n"
+ mode-sym))
+ (yas--load-directory-2 directory mode-sym)
+ (insert (format ";;; Do not edit! File generated at %s\n"
+ (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)
+ (yas--load-directory-2 directory mode-sym)))))
(defun yas--load-directory-2 (directory mode-sym)
;; Load .yas-setup.el files wherever we find them
(defun yas--load-snippet-dirs (&optional nojit)
"Reload the directories listed in `yas-snippet-dirs' or
- prompt the user to select one."
+prompt the user to select one."
(let (errors)
(if yas-snippet-dirs
(dolist (directory (reverse (yas-snippet-dirs)))
;;
(when snippet-editing-buffers
(if interactive
- (if (y-or-n-p "Some buffers editing live snippets, close them and proceed with reload?")
+ (if (y-or-n-p "Some buffers editing live snippets, close them and proceed with reload? ")
(mapc #'kill-buffer snippet-editing-buffers)
(yas--message 1 "Aborted reload...")
(throw 'abort nil))
;; in a non-interactive use, at least set
;; `yas--editing-template' to nil, make it guess it next time around
- (mapc #'(lambda (buffer) (setq yas--editing-template nil)) (buffer-list))))
+ (mapc #'(lambda (buffer)
+ (with-current-buffer buffer
+ (kill-local-variable 'yas--editing-template)))
+ (buffer-list))))
- ;; Empty all snippet tables, parenting info and all menu tables
+ ;; Empty all snippet tables and parenting info
;;
(setq yas--tables (make-hash-table))
(setq yas--parents (make-hash-table))
+ (setq yas--ancestors (make-hash-table))
+
+ ;; Before killing `yas--menu-table' use its keys to cleanup the
+ ;; mode menu parts of `yas--minor-mode-menu' (thus also cleaning
+ ;; up `yas-minor-mode-map', which points to it)
+ ;;
+ (maphash #'(lambda (menu-symbol _keymap)
+ (define-key yas--minor-mode-menu (vector menu-symbol) nil))
+ yas--menu-table)
+ ;; Now empty `yas--menu-table' as well
(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
- ;; menu....
- ;;
- (setcdr yas-minor-mode-map (cdr (yas--init-minor-keymap)))
-
;; Reload the directories listed in `yas-snippet-dirs' or prompt
;; the user to select one.
;;
;; Reload the direct keybindings
;;
(yas-direct-keymaps-reload)
- ;; Reload the trigger-key (shoudn't be needed, but see issue #237)
- ;;
- (yas--trigger-key-reload)
(yas--message 3 "Reloaded everything%s...%s."
(if interactive "" " (snippets will load just-in-time)")
(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 for `%s', just-in-time: %s!" mode form)
- (eval form))
- (remhash mode yas--scheduled-jit-loads)))))
+ (dolist (mode (yas--modes-to-activate))
+ (let ((funs (reverse (gethash mode yas--scheduled-jit-loads))))
+ ;; must reverse to maintain coherence with `yas-snippet-dirs'
+ (dolist (fun funs)
+ (yas--message 3 "Loading for `%s', just-in-time: %s!" mode fun)
+ (funcall fun))
+ (remhash mode yas--scheduled-jit-loads))))
;; (when (<= emacs-major-version 22)
;; (add-hook 'after-change-major-mode-hook 'yas--load-pending-jits))
string
t)
"\""))
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+\f
;;; Snippet compilation function
(defun yas--initialize ()
- "For backward compatibility, enable `yas-minor-mode' globally"
+ "For backward compatibility, enable `yas-minor-mode' globally."
(yas-global-mode 1))
(defun yas-compile-directory (top-level-dir)
This works by stubbing a few functions, then calling
`yas-load-directory'."
(interactive "DTop level snippet directory?")
- (flet ((yas--load-yas-setup-file
- (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)
- (insert (format ";;; Do not edit! File generated at %s\n" (current-time-string)))))))
+ (let ((yas--creating-compiled-snippets t))
(yas-load-directory top-level-dir nil)))
(defun yas-recompile-all ()
(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))
+(defun yas--schedule-jit (mode fun)
+ (push fun (gethash mode yas--scheduled-jit-loads)))
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+\f
;;; Some user level functions
-;;;
(defun yas-about ()
(interactive)
yas--version
") -- pluskid <pluskid@gmail.com>/joaotavora <joaotavora@gmail.com>")))
-(defun yas--define-parents (mode parents)
- "Add PARENTS to the list of MODE's parents"
- (puthash mode (remove-duplicates
- (append parents
- (gethash mode yas--parents)))
- yas--parents))
-
-(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)
-
-Within these, only KEY and TEMPLATE are actually mandatory.
-
-TEMPLATE might be a lisp form or a string, depending on whether
-this is a snippet or a snippet-command.
-
-CONDITION, EXPAND-ENV and KEYBINDING are lisp forms, they have
-been `yas--read-lisp'-ed and will eventually be
-`yas--eval-lisp'-ed.
-
-The remaining elements are strings.
-
-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.
-
-You can use `yas--parse-template' to return such lists based on
-the current buffers contents."
- (let ((snippet-table (yas--table-get-create mode))
- (template nil))
- (dolist (snippet snippets)
- (setq template (yas-define-snippets-1 snippet
- snippet-table)))
- template))
-
-(defun yas-define-snippets-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))
-
\f
;;; Apropos snippet menu:
;;
(defun yas--template-menu-binding-pair-get-create (template &optional type)
"Get TEMPLATE's menu binding or assign it a new one.
-TYPE may be `:stay', signalling this menu binding should be
+TYPE may be `:stay', signaling this menu binding should be
static in the menu."
(or (yas--template-menu-binding-pair template)
- (let ((key (yas--template-key template))
- (keybinding (yas--template-keybinding template)))
+ (let (;; (key (yas--template-key template))
+ ;; (keybinding (yas--template-keybinding template))
+ )
(setf (yas--template-menu-binding-pair template)
(cons `(menu-item ,(or (yas--template-name template)
(yas--template-uuid template))
(rest keymap))))
(defun yas-define-menu (mode menu &optional omit-items)
- "Define a snippet menu for MODE according to MENU, ommitting OMIT-ITEMS.
+ "Define a snippet menu for MODE according to MENU, omitting OMIT-ITEMS.
MENU is a list, its elements can be:
- (yas-item UUID) : Creates an entry the snippet identified with
- UUID. The menu entry for a snippet thus identified is
+ UUID. The menu entry for a snippet thus identified is
permanent, i.e. it will never move (be reordered) in the menu.
- (yas-separator) : Creates a separator
- (yas-submenu NAME SUBMENU) : Creates a submenu with NAME,
- SUBMENU has the same form as MENU. NAME is also added to the
+ SUBMENU has the same form as MENU. NAME is also added to the
list of groups of the snippets defined thereafter.
OMIT-ITEMS is a list of snippet uuid's that will always be
-ommited from MODE's menu, even if they're manually loaded.
+omitted from MODE's menu, even if they're manually loaded.
-This function does nothing if `yas-use-menu' is nil.
-"
+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)))
(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)
(list (list key template name condition group))))
(defun yas-hippie-try-expand (first-time?)
- "Integrate with hippie expand. Just put this function in
-`hippie-expand-try-functions-list'."
+ "Integrate with hippie expand.
+
+Just put this function in `hippie-expand-try-functions-list'."
(when yas-minor-mode
(if (not first-time?)
(let ((yas-fallback-behavior 'return-nil))
;;;
(defvar yas--condition-cache-timestamp nil)
(defmacro yas-define-condition-cache (func doc &rest body)
- "Define a function FUNC with doc DOC and body BODY, BODY is
-executed at most once every snippet expansion attempt, to check
+ "Define a function FUNC with doc DOC and body BODY.
+BODY is executed at most once every snippet expansion attempt, to check
expansion conditions.
It doesn't make any sense to call FUNC programatically."
(yas--expand-or-prompt-for-template (first templates-and-pos)
(second templates-and-pos)
(third templates-and-pos))
- (yas--fallback 'trigger-key))))
+ (yas--fallback))))
(defun yas-expand-from-keymap ()
"Directly expand some snippets, searching `yas--direct-keymaps'.
If expansion fails, execute the previous binding for this key"
(interactive)
(setq yas--condition-cache-timestamp (current-time))
- (let* ((yas--prefix current-prefix-arg)
- (vec (subseq (this-command-keys-vector) (if current-prefix-arg
+ (let* ((vec (subseq (this-command-keys-vector) (if current-prefix-arg
universal-argument-num-events
0)))
(templates (mapcan #'(lambda (table)
"Expand one of TEMPLATES from START to END.
Prompt the user if TEMPLATES has more than one element, else
-expand immediately. Common gateway for
+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)))
end
(yas--template-expand-env yas--current-template)))))
-(defun yas--fallback (&optional from-trigger-key-p)
+;; Apropos the trigger key and the fallback binding:
+;;
+;; When `yas-minor-mode-map' binds <tab>, that correctly overrides
+;; org-mode's <tab>, for example and searching for fallbacks correctly
+;; returns `org-cycle'. However, most other modes bind "TAB". TODO,
+;; improve this explanation.
+;;
+(defun yas--fallback ()
"Fallback after expansion has failed.
Common gateway for `yas-expand-from-trigger-key' and
;; return nil
nil)
((eq yas-fallback-behavior 'call-other-command)
- (let* ((yas-minor-mode nil)
- (yas--direct-keymaps nil)
- (keys-1 (this-command-keys-vector))
- (keys-2 (and yas-trigger-key
- from-trigger-key-p
- (stringp yas-trigger-key)
- (read-kbd-macro yas-trigger-key)))
- (command-1 (and keys-1 (key-binding keys-1)))
- (command-2 (and keys-2 (key-binding keys-2)))
- ;; An (ugly) safety: prevents infinite recursion of
- ;; yas-expand* calls.
- (command (or (and (symbolp command-1)
- (not (string-match "yas-expand" (symbol-name command-1)))
- command-1)
- (and (symbolp command-2)
- command-2))))
- (when (and (commandp command)
- (not (string-match "yas-expand" (symbol-name command))))
- (setq this-command command)
- (call-interactively command))))
+ (let* ((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)))
((and (listp yas-fallback-behavior)
(cdr yas-fallback-behavior)
(eq 'apply (car yas-fallback-behavior)))
;; also return nil if all the other fallbacks have failed
nil)))
+(defun yas--keybinding-beyond-yasnippet ()
+ "Return the ??"
+ (let* ((yas-minor-mode nil)
+ (yas--direct-keymaps nil)
+ (keys (this-single-command-keys)))
+ (or (key-binding keys t)
+ (key-binding (yas--fallback-translate-input keys) t))))
+
+(defun yas--fallback-translate-input (keys)
+ "Emulate `read-key-sequence', at least what I think it does.
+
+Keys should be an untranslated key vector. Returns a translated
+vector of keys. FIXME not thoroughly tested."
+ (let ((retval [])
+ (i 0))
+ (while (< i (length keys))
+ (let ((j i)
+ (translated local-function-key-map))
+ (while (and (< j (length keys))
+ translated
+ (keymapp translated))
+ (setq translated (cdr (assoc (aref keys j) (remove 'keymap translated)))
+ j (1+ j)))
+ (setq retval (vconcat retval (cond ((symbolp translated)
+ `[,translated])
+ ((vectorp translated)
+ translated)
+ (t
+ (substring keys i j)))))
+ (setq i j)))
+ retval))
\f
;;; Utils for snippet development:
(defun yas--all-templates (tables)
- "Return all snippet tables applicable for the current buffer.
+ "Get `yas--template' objects in TABLES, applicable for buffer and point.
Honours `yas-choose-tables-first', `yas-choose-keys-first' and
`yas-buffer-local-condition'"
(message "No snippets tables active!"))))
(defun yas--visit-snippet-file-1 (template)
+ "Helper for `yas-visit-snippet-file'."
(let ((file (yas--template-file template)))
(cond ((and file (file-readable-p file))
(find-file-other-window file)
(set (make-local-variable 'yas--editing-template) template)))))
(defun yas--guess-snippet-directories-1 (table)
- "Guesses possible snippet subdirectories for TABLE."
+ "Guess possible snippet subdirectories for TABLE."
(cons (yas--table-name table)
(mapcan #'(lambda (parent)
(yas--guess-snippet-directories-1
"Try to guess suitable directories based on the current active
tables (or optional TABLE).
-Returns a list of elemts (TABLE . DIRS) where TABLE is a
+Returns a list of elements (TABLE . DIRS) where TABLE is a
`yas--table' object and DIRS is a list of all possible directories
where snippets of table might exist."
(let ((main-dir (replace-regexp-in-string
tables)))
(defun yas--make-directory-maybe (table-and-dirs &optional main-table-string)
- "Returns a dir inside TABLE-AND-DIRS, prompts for creation if none exists."
+ "Return a dir inside TABLE-AND-DIRS, prompts for creation if none exists."
(or (some #'(lambda (dir) (when (file-directory-p dir) dir)) (cdr table-and-dirs))
(let ((candidate (first (cdr table-and-dirs))))
(unless (file-writable-p (file-name-directory candidate))
(set (make-local-variable 'yas--guessed-modes) (mapcar #'(lambda (d)
(yas--table-mode (car d)))
guessed-directories))
- (unless no-template (yas-expand-snippet "\
-# -*- mode: snippet -*-
-# name: $1
-# key: ${2:${1:$(replace-regexp-in-string \"\\\\\\\\(\\\\\\\\w+\\\\\\\\).*\" \"\\\\\\\\1\" yas-text)}}${3:
-# binding: ${4:direct-keybinding}}${5:
-# expand-env: ((${6:some-var} ${7:some-value}))}${8:
-# type: command}
-# --
-$0"))))
+ (if (and (not no-template) yas-new-snippet-default)
+ (yas-expand-snippet yas-new-snippet-default))))
(defun yas--compute-major-mode-and-parents (file)
- "Given FILE, find the nearest snippet directory for a given
-mode, then return a list (MODE-SYM PARENTS), the mode's symbol and a list
+ "Given FILE, find the nearest snippet directory for a given mode.
+
+Returns a list (MODE-SYM PARENTS), the mode's symbol and a list
representing one or more of the mode's parents.
Note that MODE-SYM need not be the symbol of a real major mode,
(buffer-substring-no-properties (point-min)
(point-max))))))))
(when major-mode-sym
- (cons major-mode-sym parents))))
+ (cons major-mode-sym (remove major-mode-sym parents)))))
(defvar yas--editing-template nil
- "Supporting variable for `yas-load-snippet-buffer' and `yas--visit-snippet'")
+ "Supporting variable for `yas-load-snippet-buffer' and `yas--visit-snippet'.")
(defvar yas--current-template nil
"Holds the current template being expanded into a snippet.")
(defvar yas--guessed-modes nil
"List of guessed modes supporting `yas-load-snippet-buffer'.")
-(defun yas-load-snippet-buffer (&optional kill)
- "Parse and load current buffer's snippet definition.
-
-With optional prefix argument KILL quit the window and buffer."
- (interactive "P")
+(defun yas--read-table ()
+ "Ask user for a snippet table, help with some guessing."
+ (let ((prompt (if (and (featurep 'ido)
+ ido-mode)
+ 'ido-completing-read 'completing-read)))
+ (unless yas--guessed-modes
+ (set (make-local-variable 'yas--guessed-modes)
+ (or (yas--compute-major-mode-and-parents buffer-file-name))))
+ (intern
+ (funcall prompt (format "Choose or enter a table (yas guesses %s): "
+ (if yas--guessed-modes
+ (first yas--guessed-modes)
+ "nothing"))
+ (mapcar #'symbol-name yas--guessed-modes)
+ nil
+ nil
+ nil
+ nil
+ (if (first yas--guessed-modes)
+ (symbol-name (first yas--guessed-modes)))))))
+
+(defun yas-load-snippet-buffer (table &optional interactive)
+ "Parse and load current buffer's snippet definition into TABLE.
+
+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."
+ (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,...
+ ;; 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-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'
(t
(unless yas--guessed-modes
(set (make-local-variable 'yas--guessed-modes) (or (yas--compute-major-mode-and-parents buffer-file-name))))
- (let* ((prompt (if (and (featurep 'ido)
- ido-mode)
- 'ido-completing-read 'completing-read))
- (table (yas--table-get-create
- (intern
- (funcall prompt (format "Choose or enter a table (yas guesses %s): "
- (if yas--guessed-modes
- (first yas--guessed-modes)
- "nothing"))
- (mapcar #'symbol-name yas--guessed-modes)
- nil
- nil
- nil
- nil
- (if (first yas--guessed-modes)
- (symbol-name (first yas--guessed-modes))))))))
+ (let* ((table (yas--table-get-create table)))
(set (make-local-variable 'yas--editing-template)
- (yas-define-snippets-1 (yas--parse-template buffer-file-name)
+ (yas--define-snippets-1 (yas--parse-template buffer-file-name)
table)))))
- ;; Now, offer to save this iff:
- ;;
- ;; 1) `yas-snippet-dirs' is a list and its first element does not
- ;; match this template's file (i.e. this is a library snippet, not
- ;; a user snippet) OR
- ;;
- ;; 2) yas--editing-template comes from a file that we cannot write to...
- ;;
- (when (or (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)))))
-
- (when (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 kill
- (quit-window kill))
- (yas--message 3 "Snippet \"%s\" loaded for %s."
- (yas--template-name yas--editing-template)
- (yas--table-name (yas--template-table yas--editing-template))))
+ (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)))
(defun yas-tryout-snippet (&optional debug)
- "Test current buffers's snippet template in other buffer."
+ "Test current buffer's snippet template in other buffer."
(interactive "P")
(let* ((major-mode-and-parent (yas--compute-major-mode-and-parents buffer-file-name))
(parsed (yas--parse-template))
(switch-to-buffer (get-buffer-create buffer-name))
(setq buffer-undo-list nil)
(condition-case nil (funcall test-mode) (error nil))
+ (yas-minor-mode 1)
(setq buffer-read-only nil)
(yas-expand-snippet (yas--template-content yas--current-template)
(point-min)
(t
(yas--message 3 "Cannot test snippet for unknown major mode")))))
+(defun yas-active-keys ()
+ "Return all active trigger keys for current buffer and point."
+ (remove-duplicates
+ (remove-if-not #'stringp (mapcan #'yas--table-all-keys (yas--get-snippet-tables)))
+ :test #'string=))
+
(defun yas--template-fine-group (template)
(car (last (or (yas--template-group template)
(yas--template-perm-group template)))))
(buffer (get-buffer-create "*YASnippet tables*"))
(active-tables (yas--get-snippet-tables))
(remain-tables (let ((all))
- (maphash #'(lambda (k v)
+ (maphash #'(lambda (_k v)
(unless (find v active-tables)
(push v all)))
yas--tables)
(dolist (table (append active-tables remain-tables))
(insert (format "\nSnippet table `%s':\n\n" (yas--table-name table)))
(let ((keys))
- (maphash #'(lambda (k v)
+ (maphash #'(lambda (k _v)
(push k keys))
(yas--table-hash table))
(dolist (key keys)
(insert (format " key %s maps snippets: %s\n" key
(let ((names))
- (maphash #'(lambda (k v)
+ (maphash #'(lambda (k _v)
(push k names))
(gethash key (yas--table-hash table)))
names))))))))
(insert (make-string 100 ?-) "\n")
(insert "group state name key binding\n")
(let ((groups-hash (make-hash-table :test #'equal)))
- (maphash #'(lambda (k v)
+ (maphash #'(lambda (_k v)
(let ((group (or (yas--template-fine-group v)
"(top level)")))
(when (yas--template-name v)
(match-string-no-properties grp str)
str))))
-(defun yas-choose-value (possibilities)
- "Prompt for a string in the list POSSIBILITIES and return it."
+(defun yas-choose-value (&rest possibilities)
+ "Prompt for a string in POSSIBILITIES and return it.
+
+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))
(some #'(lambda (fn)
(funcall fn "Choose: " possibilities))
yas-prompt-functions)))
(defun yas-key-to-value (alist)
- "Prompt for a string in the list POSSIBILITIES and return it."
(unless (or yas-moving-away-p
yas-modified-p)
(let ((key (read-key-sequence "")))
(throw 'yas--exception (cons 'yas--exception text)))
(defun yas-verify-value (possibilities)
- "Verify that the current field value is in POSSIBILITIES
+ "Verify that the current field value is in POSSIBILITIES.
Otherwise throw exception."
(when (and yas-moving-away-p (notany #'(lambda (pos) (string= pos yas-text)) possibilities))
(not (string= "" yas-text)))
yas-text))
-;; (defun yas-selected-text ()
-;; "Return `yas-selected-text' if that exists and is non-empty, else nil."
-;; (if (and yas-selected-text
-;; (not (string= "" yas-selected-text)))
-;; yas-selected-text))
+(defun yas-selected-text ()
+ "Return `yas-selected-text' if that exists and is non-empty, else nil."
+ (if (and yas-selected-text
+ (not (string= "" yas-selected-text)))
+ yas-selected-text))
(defun yas--get-field-once (number &optional transform-fn)
(unless yas-modified-p
(yas-field-value number)))
(defun yas-inside-string ()
+ "Return non-nil if the point is inside a string according to font-lock."
(equal 'font-lock-string-face (get-char-property (1- (point)) 'face)))
(defun yas-unimplemented (&optional missing-feature)
"Overlays the currently active field.")
(defvar yas--field-protection-overlays nil
- "Two overlays protect the current active field ")
-
-(defconst yas--prefix nil
- "A prefix argument for expansion direct from keybindings")
+ "Two overlays protect the current active field.")
(defvar yas-selected-text nil
"The selected region deleted on the last snippet expansion.")
force-exit)
(defstruct (yas--field (:constructor yas--make-field (number start end parent-field)))
- "A field."
+ "A field.
+
+NUMBER is the field number.
+START and END are mostly buffer markers, but see \"apropos markers-to-points\".
+PARENT-FIELD is a `yas--field' this field is nested under, or nil.
+MIRRORS is a list of `yas--mirror's
+TRANSFORM is a lisp form.
+MODIFIED-P is a boolean set to true once user inputs text.
+NEXT is another `yas--field' or `yas--mirror' or `yas--exit'.
+"
number
start end
parent-field
(modified-p nil)
next)
+
(defstruct (yas--mirror (:constructor yas--make-mirror (start end transform)))
- "A mirror."
+ "A mirror.
+
+START and END are mostly buffer markers, but see \"apropos markers-to-points\".
+TRANSFORM is a lisp form.
+PARENT-FIELD is a `yas--field' this mirror is nested under, or nil.
+NEXT is another `yas--field' or `yas--mirror' or `yas--exit'
+DEPTH is a count of how many nested mirrors can affect this mirror"
start end
(transform nil)
parent-field
- next)
+ next
+ depth)
(defstruct (yas--exit (:constructor yas--make-exit (marker)))
marker
If there is a transform but it returns nil, return the empty
string iff EMPTY-ON-NIL-P is true."
(let* ((yas-text (yas--field-text-for-display field))
- (text yas-text)
(yas-modified-p (yas--field-modified-p field))
(yas-moving-away-p nil)
(transform (if (yas--mirror-p field-or-mirror)
transformed))
(defsubst yas--replace-all (from to &optional text)
- "Replace all occurance from FROM to TO.
+ "Replace all occurrences from FROM to TO.
With optional string TEXT do it in that string."
(if text
#'yas--snippet-field-compare)))
(defun yas--snippet-field-compare (field1 field2)
- "Compare two fields. The field with a number is sorted first.
-If they both have a number, compare through the number. If neither
-have, compare through the field's start point"
+ "Compare FIELD1 and FIELD2.
+
+The field with a number is sorted first. If they both have a
+number, compare through the number. If neither have, compare
+through the field's start point"
(let ((n1 (yas--field-number field1))
(n2 (yas--field-number field2)))
(if n1
(defun yas--field-probably-deleted-p (snippet field)
"Guess if SNIPPET's FIELD should be skipped."
(and
- ;; field must be zero lentgh
+ ;; field must be zero length
;;
(zerop (- (yas--field-start field) (yas--field-end field)))
;; skip if:
(not (zerop (yas--field-number field)))))
(defun yas--snippets-at-point (&optional all-snippets)
- "Return a sorted list of snippets at point, most recently
-inserted first."
+ "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))
(<= (yas--snippet-id s2) (yas--snippet-id s1)))))
(defun yas-next-field-or-maybe-expand ()
- "Try to expand a snippet at a key before point, otherwise
-delegate to `yas-next-field'."
+ "Try to expand a snippet at a key before point.
+
+Otherwise delegate to `yas-next-field'."
(interactive)
(if yas-triggers-in-field
(let ((yas-fallback-behavior 'return-nil)
(yas-next-field)))
(defun yas-next-field (&optional arg)
- "Navigate to next field. If there's none, exit the snippet."
+ "Navigate to the ARGth next field.
+
+If there's none, exit the snippet."
(interactive)
(let* ((arg (or arg
1))
(yas--field-transform active-field))
(let* ((yas-moving-away-p t)
(yas-text (yas--field-text-for-display active-field))
- (text yas-text)
(yas-modified-p (yas--field-modified-p active-field)))
;; primary field transform: exit call to field-transform
(yas--eval-lisp (yas--field-transform active-field))))
nil))))
(defun yas--place-overlays (snippet field)
- "Correctly place overlays for SNIPPET's FIELD"
+ "Correctly place overlays for SNIPPET's FIELD."
(yas--make-move-field-protection-overlays snippet field)
(yas--make-move-active-field-overlay snippet field))
(setf (yas--snippet-active-field snippet) field)
;; primary field transform: first call to snippet transform
(unless (yas--field-modified-p field)
- (if (yas--field-update-display field snippet)
+ (if (yas--field-update-display field)
(yas--update-mirrors snippet)
(setf (yas--field-modified-p field) nil))))))
\f
;;; Some low level snippet-routines:
+(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))
- (progn ,@body)))
+ ,@body))
-(defvar yas-snippet-beg nil "Beginning position of the last snippet commited.")
-(defvar yas-snippet-end nil "End position of the last snippet commited.")
+(defvar yas-snippet-beg nil "Beginning position of the last snippet committed.")
+(defvar yas-snippet-end nil "End position of the last snippet committed.")
(defun yas--commit-snippet (snippet)
- "Commit SNIPPET, but leave point as it is. This renders the
-snippet as ordinary text."
+ "Commit SNIPPET, but leave point as it is.
+
+This renders the snippet as ordinary text."
(let ((control-overlay (yas--snippet-control-overlay snippet)))
;;
(defun yas--check-commit-snippet ()
- "Checks if point exited the currently active field of the
-snippet, if so cleans up the whole snippet up."
+ "Check if point exited the currently active field of the snippet.
+
+If so cleans up the whole snippet up."
(let* ((snippets (yas--snippets-at-point 'all-snippets))
(snippets-left snippets)
(snippet-exit-transform))
(setf (yas--exit-marker snippet-exit) (cons exit (yas--exit-marker snippet-exit)))))))
(defun yas--points-to-markers (snippet)
- "Convert all cons (POINT . MARKER) in SNIPPET to markers. This
-is done by setting MARKER to POINT with `set-marker'."
+ "Convert all cons (POINT . MARKER) in SNIPPET to markers.
+
+This is done by setting MARKER to POINT with `set-marker'."
(dolist (field (yas--snippet-fields snippet))
(setf (yas--field-start field) (set-marker (cdr (yas--field-start field))
(car (yas--field-start field))))
(<= point (yas--field-end field)))))
(defun yas--field-text-for-display (field)
- "Return the propertized display text for field FIELD. "
+ "Return the propertized display text for field FIELD."
(buffer-substring (yas--field-start field) (yas--field-end field)))
(defun yas--undo-in-progress ()
- "True if some kind of undo is in progress"
+ "True if some kind of undo is in progress."
(or undo-in-progress
(eq this-command 'undo)
(eq this-command 'redo)))
(defun yas--make-control-overlay (snippet start end)
- "Creates the control overlay that surrounds the snippet and
+ "Create the control overlay that surrounds the snippet and
holds the keymap."
(let ((overlay (make-overlay start
end
(call-interactively 'delete-char)))))
(defun yas--skip-and-clear (field)
- "Deletes the region of FIELD and sets it modified state to t"
+ "Deletes the region of FIELD and sets it's modified state to t."
;; Just before skipping-and-clearing the field, mark its children
- ;; fields as modified, too. If the childen have mirrors-in-fields
+ ;; fields as modified, too. If the children have mirrors-in-fields
;; this prevents them from updating erroneously (we're skipping and
;; deleting!).
;;
(overlay-put yas--active-field-overlay 'insert-behind-hooks
'(yas--on-field-overlay-modification))))
-(defvar yas--inhibit-overlay-hooks nil
- "Bind this temporarity to non-nil to prevent running `yas--on-*-modification'.")
-
-(defun yas--on-field-overlay-modification (overlay after? beg end &optional length)
+(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
+at field start. This hook doesn't do anything if an undo is in
progress."
(unless (or yas--inhibit-overlay-hooks
(yas--undo-in-progress))
(let* ((field (overlay-get overlay 'yas--field))
- (number (and field (yas--field-number field)))
(snippet (overlay-get yas--active-field-overlay 'yas--snippet)))
(cond (after?
(yas--advance-end-maybe field (overlay-end overlay))
(save-excursion
- (yas--field-update-display field snippet))
+ (yas--field-update-display field))
(yas--update-mirrors snippet))
(field
(when (and (not after?)
;;
;; Alternatively, I've experimented with an implementation that
;; commits the snippet before actually calling `this-command'
-;; interactively, and then signals an eror, which is ignored. but
+;; interactively, and then signals an error, which is ignored. but
;; blocks all other million modification hooks. This presented some
;; problems with stacked expansion.
;;
(overlay-put ov 'modification-hooks '(yas--on-protection-overlay-modification)))))))
(defvar yas--protection-violation nil
- "When non-nil, signals attempts to erronesly exit or modify the snippet.
+ "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
+nil. The variables value is the point where the violation
originated")
-(defun yas--on-protection-overlay-modification (overlay after? beg end &optional length)
+(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'"
(add-to-list 'debug-ignored-errors "^Exit the snippet first!$")
\f
-;; Snippet expansion and "stacked" expansion:
+;;; Snippet expansion and "stacked" expansion:
;;
;; Stacked expansion is when you try to expand a snippet when already
;; inside a snippet expansion.
;; `yas--commit-snippet'. I've tried to mark them with "stacked
;; expansion:".
;;
-;; This was thought to be safer in in an undo/redo perpective, but
+;; This was thought to be safer in an undo/redo perspective, but
;; maybe the correct implementation is to make the globals
;; `yas--active-field-overlay' and `yas--field-protection-overlays' be
;; snippet-local and be active even while the child snippet is
"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 are let-style variable to value bindings
considered when expanding the snippet."
(run-hooks 'yas-before-expand-snippet-hook)
- ;; If a region is active, set `yas-selected-text'
- (setq yas-selected-text
- (when (region-active-p)
- (prog1 (buffer-substring-no-properties (region-beginning)
- (region-end))
- (unless start (setq start (region-beginning))
- (unless end (setq end (region-end)))))))
-
- (when start
- (goto-char start))
-
;;
- (let ((to-delete (and start end (buffer-substring-no-properties start end)))
- (start (or start (point)))
- (end (or end (point)))
- snippet)
+ (let* ((yas-selected-text (or yas-selected-text
+ (and (region-active-p)
+ (buffer-substring-no-properties (region-beginning)
+ (region-end)))))
+ (start (or start
+ (and (region-active-p)
+ (region-beginning))
+ (point)))
+ (end (or end
+ (and (region-active-p)
+ (region-end))
+ (point)))
+ (to-delete (and start
+ end
+ (buffer-substring-no-properties start end)))
+ snippet)
+ (goto-char start)
(setq yas--indent-original-column (current-column))
;; Delete the region to delete, this *does* get undo-recorded.
;;
(if expand-env
(eval `(let* ,expand-env
(insert content)
- (yas--snippet-create (point-min) (point-max))))
+ (yas--snippet-create (point-min))))
(insert content)
- (yas--snippet-create (point-min) (point-max)))))))
+ (yas--snippet-create (point-min)))))))
;; stacked-expansion: This checks for stacked expansion, save the
- ;; `yas--previous-active-field' and advance its boudary.
+ ;; `yas--previous-active-field' and advance its boundary.
;;
(let ((existing-field (and yas--active-field-overlay
(overlay-buffer yas--active-field-overlay)
(yas--message 3 "snippet expanded.")
t))))
-(defun yas--take-care-of-redo (beg end snippet)
- "Commits SNIPPET, which in turn pushes an undo action for
-reviving it.
+(defun yas--take-care-of-redo (_beg _end snippet)
+ "Commits SNIPPET, which in turn pushes an undo action for reviving it.
Meant to exit in the `buffer-undo-list'."
;; slightly optimize: this action is only needed for snippets with
(yas--commit-snippet snippet)))
(defun yas--snippet-revive (beg end snippet)
- "Revives the SNIPPET and creates a control overlay from BEG to
-END.
+ "Revives SNIPPET and creates a control overlay from BEG to END.
-BEG and END are, we hope, the original snippets boudaries. All
-the markers/points exiting existing inside SNIPPET should point
+BEG and END are, we hope, the original snippets boundaries.
+All the markers/points exiting existing inside SNIPPET should point
to their correct locations *at the time the snippet is revived*.
After revival, push the `yas--take-care-of-redo' in the
(push `(apply yas--take-care-of-redo ,beg ,end ,snippet)
buffer-undo-list))))
-(defun yas--snippet-create (begin end)
- "Creates a snippet from an template inserted between BEGIN and END.
+(defun yas--snippet-create (begin)
+ "Create a snippet from a template inserted at BEGIN.
Returns the newly created snippet."
(let ((snippet (yas--make-snippet)))
This is according to their relative positions in the buffer, and
has to be called before the $-constructs are deleted."
- (flet ((yas--fom-set-next-fom (fom nextfom)
- (cond ((yas--field-p fom)
- (setf (yas--field-next fom) nextfom))
- ((yas--mirror-p fom)
- (setf (yas--mirror-next fom) nextfom))
- (t
- (setf (yas--exit-next fom) nextfom))))
- (yas--compare-fom-begs (fom1 fom2)
- (if (= (yas--fom-start fom2) (yas--fom-start fom1))
- (yas--mirror-p fom2)
- (>= (yas--fom-start fom2) (yas--fom-start fom1))))
- (yas--link-foms (fom1 fom2)
- (yas--fom-set-next-fom fom1 fom2)))
+ (let* ((fom-set-next-fom
+ (lambda (fom nextfom)
+ (cond ((yas--field-p fom)
+ (setf (yas--field-next fom) nextfom))
+ ((yas--mirror-p fom)
+ (setf (yas--mirror-next fom) nextfom))
+ (t
+ (setf (yas--exit-next fom) nextfom)))))
+ (compare-fom-begs
+ (lambda (fom1 fom2)
+ (if (= (yas--fom-start fom2) (yas--fom-start fom1))
+ (yas--mirror-p fom2)
+ (>= (yas--fom-start fom2) (yas--fom-start fom1)))))
+ (link-foms fom-set-next-fom))
;; make some yas--field, yas--mirror and yas--exit soup
(let ((soup))
(when (yas--snippet-exit snippet)
(dolist (mirror (yas--field-mirrors field))
(push mirror soup)))
(setq soup
- (sort soup
- #'yas--compare-fom-begs))
+ (sort soup compare-fom-begs))
(when soup
- (reduce #'yas--link-foms soup)))))
+ (reduce link-foms soup)))))
(defun yas--calculate-mirrors-in-fields (snippet mirror)
"Attempt to assign a parent field of SNIPPET to the mirror MIRROR.
-Use the tighest containing field if more than one field contains
-the mirror. Intended to be called *before* the dollar-regions are
+Use the tightest containing field if more than one field contains
+the mirror. Intended to be called *before* the dollar-regions are
deleted."
(let ((min (point-min))
(max (point-max)))
field
Also, if FOM is an exit-marker, always call
-`yas--advance-start-maybe' on its next fom. This is beacuse
-exit-marker have identical start and end markers.
-
-"
+`yas--advance-start-maybe' on its next fom. This is because
+exit-marker have identical start and end markers."
(cond ((and fom (< (yas--fom-end fom) newend))
(set-marker (yas--fom-end fom) newend)
(yas--advance-start-maybe (yas--fom-next fom) newend)
"Like `yas--advance-end-maybe' but for parent fields.
Only works for fields and doesn't care about the start of the
-next FOM. Works its way up recursively for parents of parents."
+next FOM. Works its way up recursively for parents of parents."
(when (and field
(< (yas--field-end field) newend))
(set-marker (yas--field-end field) newend)
(defvar yas--dollar-regions nil
"When expanding the snippet the \"parse-create\" functions add
- cons cells to this var")
+cons cells to this var.")
+
+(defvar yas--backquote-markers-and-strings nil
+ "List of (MARKER . STRING) marking where the values from
+backquoted Lisp expressions should be inserted at the end of
+expansion.")
(defun yas--snippet-parse-create (snippet)
"Parse a recently inserted snippet template, creating all
;; Reset the yas--dollar-regions
;;
(setq yas--dollar-regions nil)
- ;; protect escaped quote, backquotes and backslashes
+ ;; protect just the backquotes
;;
- (yas--protect-escapes nil `(?\\ ?` ?'))
+ (yas--protect-escapes nil '(?`))
;; replace all backquoted expressions
;;
(goto-char parse-start)
- (yas--replace-backquotes)
- ;; protect escapes again since previous steps might have generated
- ;; more characters needing escaping
+ (yas--save-backquotes)
+ ;; protect escaped characters
;;
- (goto-char parse-start)
(yas--protect-escapes)
;; parse fields with {}
;;
;; Delete $-constructs
;;
(yas--delete-regions yas--dollar-regions)
+ ;; restore backquoted expression values
+ ;;
+ (yas--restore-backquotes)
;; restore escapes
;;
(goto-char parse-start)
(yas--indent snippet)))
(defun yas--indent-according-to-mode (snippet-markers)
- "Indent current line according to mode, preserving
-SNIPPET-MARKERS."
+ "Indent current line according to mode, preserving SNIPPET-MARKERS."
;;; Apropos indenting problems....
;;
;; `indent-according-to-mode' uses whatever `indent-line-function'
snippet-markers)))
(save-restriction
(widen)
- (condition-case err
+ (condition-case _
(indent-according-to-mode)
(error (yas--message 3 "Warning: `yas--indent-according-to-mode' having problems running %s" indent-line-function)
nil)))
(or escaped yas--escaped-characters))
changed-text))
-(defun yas--replace-backquotes ()
- "Replace all the \"`(lisp-expression)`\"-style expression
- with their evaluated value"
+(defun yas--save-backquotes ()
+ "Save all the \"`(lisp-expression)`\"-style expressions
+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 1)) transformed)
+ (let ((current-string (match-string-no-properties 1)) transformed)
(delete-region (match-beginning 0) (match-end 0))
- (setq transformed (yas--eval-lisp (yas--read-lisp (yas--restore-escapes current-string))))
+ (setq transformed (yas--eval-lisp (yas--read-lisp (yas--restore-escapes current-string '(?`)))))
(goto-char (match-beginning 0))
- (when transformed (insert transformed)))))
+ (when transformed
+ (let ((marker (make-marker)))
+ (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 ()
+ "Replace markers in `yas--backquote-markers-and-strings' with their values."
+ (while yas--backquote-markers-and-strings
+ (let* ((marker-and-string (pop yas--backquote-markers-and-strings))
+ (marker (car marker-and-string))
+ (string (cdr marker-and-string)))
+ (save-excursion
+ (goto-char marker)
+ (delete-char -1)
+ (insert string)
+ (delete-char 1)
+ (set-marker marker nil)))))
(defun yas--scan-sexps (from count)
- (condition-case err
+ (condition-case _
(with-syntax-table (standard-syntax-table)
(scan-sexps from count))
(error
nil)))
(defun yas--make-marker (pos)
- "Create a marker at POS with `nil' `marker-insertion-type'"
+ "Create a marker at POS with nil `marker-insertion-type'."
(let ((marker (set-marker (make-marker) pos)))
(set-marker-insertion-type marker nil)
marker))
(defun yas--field-parse-create (snippet &optional parent-field)
- "Parse most field expressions, except for the simple one \"$n\".
+ "Parse most field expressions in SNIPPET, except for the simple one \"$n\".
The following count as a field:
* \"${n: text}\", for a numbered field with default text, as long as N is not 0;
-* \"${n: text$(expression)}, the same with a lisp expression;
+* \"${n: text$(expression)}, the same with a Lisp expression;
this is caught with the curiously named `yas--multi-dollar-lisp-expression-regexp'
* the same as above but unnumbered, (no N:) and number is calculated automatically.
(goto-char (point-min))
(yas--field-parse-create snippet brand-new-field)))))))
;; if we entered from a parent field, now search for the
- ;; `yas--multi-dollar-lisp-expression-regexp'. THis is used for
+ ;; `yas--multi-dollar-lisp-expression-regexp'. This is used for
;; primary field transformations
;;
(when parent-field
yas--dollar-regions)))))))
(defun yas--transform-mirror-parse-create (snippet)
- "Parse the \"${n:$(lisp-expression)}\" mirror transformations."
+ "Parse the \"${n:$(lisp-expression)}\" mirror transformations in SNIPPET."
(while (re-search-forward yas--transform-mirror-regexp nil t)
(let* ((real-match-end-0 (yas--scan-sexps (1+ (match-beginning 0)) 1))
(number (string-to-number (match-string-no-properties 1)))
(push (cons (match-beginning 0) real-match-end-0) yas--dollar-regions)))))
(defun yas--simple-mirror-parse-create (snippet)
- "Parse the simple \"$n\" fields/mirrors/exitmarkers."
+ "Parse the simple \"$n\" fields/mirrors/exitmarkers in SNIPPET."
(while (re-search-forward yas--simple-mirror-regexp nil t)
(let ((number (string-to-number (match-string-no-properties 1))))
(cond ((zerop number)
#'(lambda (r1 r2)
(>= (car r1) (car r2))))))
+(defun yas--calculate-mirror-depth (mirror &optional traversed)
+ (let* ((parent (yas--mirror-parent-field mirror))
+ (parents-mirrors (and parent
+ (yas--field-mirrors parent))))
+ (or (yas--mirror-depth mirror)
+ (setf (yas--mirror-depth mirror)
+ (cond ((memq mirror traversed)
+ 0)
+ ((and parent parents-mirrors)
+ (1+ (reduce #'max
+ (mapcar #'(lambda (m)
+ (yas--calculate-mirror-depth m
+ (cons mirror
+ traversed)))
+ parents-mirrors))))
+ (parent
+ 1)
+ (t
+ 0))))))
+
(defun yas--update-mirrors (snippet)
- "Updates all the mirrors of SNIPPET."
+ "Update all the mirrors of SNIPPET."
(save-excursion
- (let* ((fields (copy-list (yas--snippet-fields snippet)))
- (field (car fields)))
- (while field
- (dolist (mirror (yas--field-mirrors field))
- (let ((mirror-parent-field (yas--mirror-parent-field mirror)))
- ;; updatte this mirror
- ;;
- (yas--mirror-update-display mirror field)
- ;; for mirrors-in-fields: schedule a possible
- ;; parent field for reupdting later on
- ;;
- (when mirror-parent-field
- (add-to-list 'fields mirror-parent-field 'append #'eq))
- ;; `yas--place-overlays' is needed if the active field and
- ;; protected overlays have been changed because of insertions
- ;; in `yas--mirror-update-display'
- ;;
- (when (eq field (yas--snippet-active-field snippet))
- (yas--place-overlays snippet field))))
- (setq fields (cdr fields))
- (setq field (car fields))))))
+ (dolist (field-and-mirror (sort
+ ;; make a list of ((F1 . M1) (F1 . M2) (F2 . M3) (F2 . M4) ...)
+ ;; where F is the field that M is mirroring
+ ;;
+ (mapcan #'(lambda (field)
+ (mapcar #'(lambda (mirror)
+ (cons field mirror))
+ (yas--field-mirrors field)))
+ (yas--snippet-fields snippet))
+ ;; then sort this list so that entries with mirrors with parent
+ ;; fields appear before. This was important for fixing #290, and
+ ;; luckily also handles the case where a mirror in a field causes
+ ;; another mirror to need reupdating
+ ;;
+ #'(lambda (field-and-mirror1 field-and-mirror2)
+ (> (yas--calculate-mirror-depth (cdr field-and-mirror1))
+ (yas--calculate-mirror-depth (cdr field-and-mirror2))))))
+ (let* ((field (car field-and-mirror))
+ (mirror (cdr field-and-mirror))
+ (parent-field (yas--mirror-parent-field mirror)))
+ ;; before updating a mirror with a parent-field, maybe advance
+ ;; its start (#290)
+ ;;
+ (when parent-field
+ (yas--advance-start-maybe mirror (yas--fom-start parent-field)))
+ ;; update this mirror
+ ;;
+ (yas--mirror-update-display mirror field)
+ ;; `yas--place-overlays' is needed if the active field and
+ ;; protected overlays have been changed because of insertions
+ ;; in `yas--mirror-update-display'
+ ;;
+ (when (eq field (yas--snippet-active-field snippet))
+ (yas--place-overlays snippet field))))))
(defun yas--mirror-update-display (mirror field)
"Update MIRROR according to FIELD (and mirror transform)."
;; super-special advance
(yas--advance-end-of-parents-maybe mirror-parent-field (point))))))
-(defun yas--field-update-display (field snippet)
- "Much like `yas--mirror-update-display', but for fields"
+(defun yas--field-update-display (field)
+ "Much like `yas--mirror-update-display', but for fields."
(when (yas--field-transform field)
(let ((transformed (and (not (eq (yas--field-number field) 0))
- (yas--apply-transform field field)))
- (point (point)))
+ (yas--apply-transform field field))))
(when (and transformed
(not (string= transformed (buffer-substring-no-properties (yas--field-start field)
(yas--field-end field)))))
\f
;;; Post-command hook:
-
+;;
(defun yas--post-command-handler ()
"Handles various yasnippet conditions after each command."
(cond (yas--protection-violation
(put 'yas-expand 'function-documentation
'(yas--expand-from-trigger-key-doc))
(defun yas--expand-from-trigger-key-doc ()
- "A doc synthethizer for `yas--expand-from-trigger-key-doc'."
+ "A doc synthesizer for `yas--expand-from-trigger-key-doc'."
(let ((fallback-description
(cond ((eq yas-fallback-behavior 'call-other-command)
- (let* ((yas-minor-mode nil)
- (fallback (key-binding (read-kbd-macro yas-trigger-key))))
+ (let* ((fallback (yas--keybinding-beyond-yasnippet)))
(or (and fallback
(format " call command `%s'." (pp-to-string fallback)))
- " do nothing.")))
+ " do nothing (`yas-expand' doesn't shadow\nanything)")))
((eq yas-fallback-behavior 'return-nil)
", do nothing.")
(t
- ", defer to `yas--fallback-behaviour' :-)"))))
+ ", defer to `yas-fallback-behaviour' (which see)"))))
(concat "Expand a snippet before point. If no snippet
expansion is possible,"
fallback-description
(put 'yas-expand-from-keymap 'function-documentation '(yas--expand-from-keymap-doc))
(defun yas--expand-from-keymap-doc ()
- "A doc synthethizer for `yas--expand-from-keymap-doc'."
+ "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)
(help-xref-button 1 'help-snippet-def template)
(kill-region (match-end 1) (match-end 0))
(kill-region (match-beginning 0) (match-beginning 1)))))))
-
-(defun yas--expand-uuid (mode-symbol uuid &optional start end expand-env)
- "Expand a snippet registered in MODE-SYMBOL's table with UUID.
-
-Remaining args as in `yas-expand-snippet'."
- (let* ((table (gethash mode-symbol yas--tables))
- (yas--current-template (and table
- (gethash uuid (yas--table-uuidhash table)))))
- (when yas--current-template
- (yas-expand-snippet (yas--template-content yas--current-template)))))
\f
;;; Utils
"Log level for `yas--message' 4 means trace most anything, 0 means nothing.")
(defun yas--message (level message &rest args)
+ "When LEVEL is above `yas-verbosity-level', log MESSAGE and ARGS."
(when (> yas-verbosity level)
- (message (apply #'yas--format message args))))
+ (message "%s" (apply #'yas--format message args))))
(defun yas--format (format-control &rest format-args)
(apply #'format (concat "[yas] " format-control) format-args))
;; `name' in /home or in /.
(setq file (abbreviate-file-name file))
(let ((root nil)
- (prev-file file)
- ;; `user' is not initialized outside the loop because
- ;; `file' may not exist, so we may have to walk up part of the
- ;; hierarchy before we find the "initial UUID".
- (user nil)
try)
(while (not (or root
(null file)
(string-match locate-dominating-stop-dir-regexp file)))
(setq try (file-exists-p (expand-file-name name file)))
(cond (try (setq root file))
- ((equal file (setq prev-file file
- file (file-name-directory
+ ((equal file (setq file (file-name-directory
(directory-file-name file))))
(setq file nil))))
root))))
(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
+handle the `end-of-buffer' error fired in it by calling
`forward-char' at the end of buffer."
(condition-case err
ad-do-it
(define-key (symbol-value (make-local-variable 'yas-keymap))
k 'self-insert-command))))
\f
-;;; Backward compatibility to to yasnippet <= 0.7
+;;; Backward compatibility to yasnippet <= 0.7
+
(defvar yas--exported-syms '(;; `defcustom's
;;
yas-snippet-dirs
yas-indent-line
yas-also-auto-indent-first-line
yas-snippet-revival
- yas-trigger-key
- yas-next-field-key
- yas-prev-field-key
- yas-skip-and-clear-key
yas-triggers-in-field
yas-fallback-behavior
yas-choose-keys-first
yas-after-exit-snippet-hook
yas-before-expand-snippet-hook
yas-buffer-local-condition
+ yas-dont-activate
;; prompting functions
;;
yas-unimplemented
yas-define-condition-cache
yas-hippie-try-expand
+ yas-active-keys
;; debug definitions
;; yas-debug-snippet-vars
;; yas-saving-variables
;; 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.")
+
+(defvar yas--dont-backport '(yas-active-keys)
+ "Exported symbols that don't map back to \"yas/*\" variants.")
-(dolist (sym yas--exported-syms)
+(dolist (sym (set-difference yas--exported-syms yas--dont-backport))
(let ((backported (intern (replace-regexp-in-string "^yas-" "yas/" (symbol-name sym)))))
(when (boundp sym)
(make-obsolete-variable backported sym "yasnippet 0.8")
;;; yasnippet.el ends here
;; Local Variables:
;; coding: utf-8
+;; byte-compile-warnings: (not cl-functions)
;; End: