/vendor/yasnippet/yasnippet.el
http://github.com/rejeep/emacs · Emacs Lisp · 3676 lines · 2704 code · 437 blank · 535 comment · 79 complexity · b772cd5ccc2eb9cb7294e279dfa92b89 MD5 · raw file
Large files are truncated click here to view the full file
- ;;; Yasnippet.el --- Yet another snippet extension for Emacs.
- ;; Copyright 2008 pluskid
- ;; 2009 pluskid, joaotavora
- ;; Authors: pluskid <pluskid@gmail.com>, joaotavora <joaotavora@gmail.com>
- ;; Version: 0.6.1
- ;; Package-version: 0.6.1c
- ;; X-URL: http://code.google.com/p/yasnippet/
- ;; Keywords: convenience, emulation
- ;; URL: http://code.google.com/p/yasnippet/
- ;; EmacsWiki: YaSnippetMode
- ;; This file is free software; you can redistribute it and/or modify
- ;; it under the terms of the GNU General Public License as published by
- ;; the Free Software Foundation; either version 2, or (at your option)
- ;; any later version.
- ;; This file is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;; GNU General Public License for more details.
- ;; You should have received a copy of the GNU General Public License
- ;; along with GNU Emacs; see the file COPYING. If not, write to
- ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- ;; Boston, MA 02111-1307, USA.
- ;;; Commentary:
- ;; Basic steps to setup:
- ;;
- ;; 1. In your .emacs file:
- ;; (add-to-list 'load-path "/dir/to/yasnippet.el")
- ;; (require 'yasnippet)
- ;; 2. Place the `snippets' directory somewhere. E.g: ~/.emacs.d/snippets
- ;; 3. In your .emacs file
- ;; (setq yas/root-directory "~/.emacs/snippets")
- ;; (yas/load-directory yas/root-directory)
- ;; 4. To enable the YASnippet menu and tab-trigger expansion
- ;; M-x yas/minor-mode
- ;; 5. To globally enable the minor mode in *all* buffers
- ;; M-x yas/global-mode
- ;;
- ;; Steps 4. and 5. are optional, you don't have to use the minor
- ;; mode to use YASnippet.
- ;;
- ;; Interesting variables are:
- ;;
- ;; `yas/root-directory'
- ;;
- ;; The directory where user-created snippets are to be
- ;; stored. Can also be a list of directories that
- ;; `yas/reload-all' will use for bulk-reloading snippets. In
- ;; that case the first directory the default for storing new
- ;; snippets.
- ;;
- ;; `yas/mode-symbol'
- ;;
- ;; A local variable that you can set in a hook to override
- ;; snippet-lookup based on major mode. It is a a symbol (or
- ;; list of symbols) that correspond to subdirectories of
- ;; `yas/root-directory' and is used for deciding which
- ;; snippets to consider for the active buffer.
- ;;
- ;; Major commands are:
- ;;
- ;; 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.
- ;;
- ;; M-x yas/load-directory
- ;;
- ;; Prompts you for a directory hierarchy of snippets to load.
- ;;
- ;; M-x yas/insert-snippet
- ;;
- ;; Prompts you for possible snippet expansion if that is
- ;; possible according to buffer-local and snippet-local
- ;; expansion conditions. With prefix argument, ignore these
- ;; conditions.
- ;;
- ;; M-x yas/find-snippets
- ;;
- ;; Lets you find the snippet files in the correct
- ;; subdirectory of `yas/root-directory', according to the
- ;; active major mode (if it exists) like
- ;; `find-file-other-window'.
- ;;
- ;; M-x yas/visit-snippet-file
- ;;
- ;; Prompts you for possible snippet expansions like
- ;; `yas/insert-snippet', but instead of expanding it, takes
- ;; you directly to the snippet definition's file, if it
- ;; exists.
- ;;
- ;; M-x yas/new-snippet
- ;;
- ;; Lets you create a new snippet file in the correct
- ;; subdirectory of `yas/root-directory', according to the
- ;; active major mode.
- ;;
- ;; M-x yas/load-snippet-buffer
- ;;
- ;; When editing a snippet, this loads the snippet. This is
- ;; bound to "C-c C-c" while in the `snippet-mode' editing
- ;; mode.
- ;;
- ;; M-x yas/tryout-snippet
- ;;
- ;; When editing a snippet, this opens a new empty buffer,
- ;; sets it to the appropriate major mode and inserts the
- ;; snippet there, so you can see what it looks like. This is
- ;; bound to "C-c C-t" while in `snippet-mode'.
- ;;
- ;; 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:
- ;;
- ;; (require 'dropdown-list)
- ;; (setq yas/prompt-functions '(yas/dropdown-prompt
- ;; yas/ido-prompt
- ;; yas/completing-prompt))
- ;;
- ;; Also check out the customization group
- ;;
- ;; M-x customize-group RET yasnippet RET
- ;;
- ;; If you use the customization group to set variables
- ;; `yas/root-directory' or `yas/global-mode', make sure the path to
- ;; "yasnippet.el" is present in the `load-path' *before* the
- ;; `custom-set-variables' is executed in your .emacs file.
- ;;
- ;; For more information and detailed usage, refer to the project page:
- ;; http://code.google.com/p/yasnippet/
- ;;; Code:
- (require 'cl)
- (require 'assoc)
- (require 'easymenu)
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; User customizable variables
- (defgroup yasnippet nil
- "Yet Another Snippet extension"
- :group 'editing)
- ;;;###autoload
- (defcustom yas/root-directory nil
- "Root directory that stores the snippets for each major mode.
- If you set this from your .emacs, can also be a list of strings,
- for multiple root directories. If you make this a list, the first
- element is always the user-created snippets directory. Other
- directories are used for bulk reloading of all snippets using
- `yas/reload-all'"
- :type '(choice (string :tag "Single directory (string)")
- (repeat :args (string) :tag "List of directories (strings)"))
- :group 'yasnippet
- :require 'yasnippet
- :set #'(lambda (symbol new)
- (let ((old (and (boundp symbol)
- (symbol-value symbol))))
- (set-default symbol new)
- (unless (or (not (fboundp 'yas/reload-all))
- (equal old new))
- (yas/reload-all)))))
- (defcustom yas/prompt-functions '(yas/x-prompt
- yas/dropdown-prompt
- yas/completing-prompt
- yas/ido-prompt
- yas/no-prompt)
- "Functions to prompt for keys, templates, etc interactively.
- These functions are called with the following arguments:
- - PROMPT: A string to prompt the user
- - CHOICES: a list of strings or objects.
- - optional DISPLAY-FN : A function that, when applied to each of
- the objects in CHOICES will return a string.
- The return value of any function you put here should be one of
- the objects in CHOICES, properly formatted with DISPLAY-FN (if
- that is passed).
- - To signal that your particular style of prompting is
- unavailable at the moment, you can also have the function return
- nil.
- - To signal that the user quit the prompting process, you can
- signal `quit' with
- (signal 'quit \"user quit!\")."
- :type '(repeat function)
- :group 'yasnippet)
- (defcustom yas/indent-line 'auto
- "Controls indenting applied to a recent snippet expansion.
- The following values are possible:
- - `fixed' Indent the snippet to the current column;
- - `auto' Indent each line of the snippet with `indent-according-to-mode'
- Every other value means don't apply any snippet-side indendation
- after expansion (the manual per-line \"$>\" indentation still
- applies)."
- :type '(choice (const :tag "Nothing" nothing)
- (const :tag "Fixed" fixed)
- (const :tag "Auto" auto))
- :group 'yasnippet)
- (defcustom yas/also-auto-indent-first-line nil
- "Non-nil means also auto indent first line according to mode.
- Naturally this is only valid when `yas/indent-line' is `auto'"
- :type 'boolean
- :group 'yasnippet)
- (defcustom yas/snippet-revival t
- "Non-nil means re-activate snippet fields after undo/redo."
- :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"
- "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.
- Otherwise, `yas/next-field-key' just tries to move 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.
- - `call-other-command' means try to temporarily disable YASnippet
- and call the next command bound to `yas/trigger-key'.
- - nil or the symbol `return-nil' mean do nothing. (and
- `yas/expand-returns' nil)
- - 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 "Do nothing" return-nil))
- :group 'yasnippet)
- (make-variable-buffer-local 'yas/fallback-behavior)
- (defcustom yas/choose-keys-first nil
- "If non-nil, prompt for snippet key first, then for template.
- Otherwise prompts for all possible snippet names.
- This affects `yas/insert-snippet' and `yas/visit-snippet-file'."
- :type 'boolean
- :group 'yasnippet)
- (defcustom yas/choose-tables-first nil
- "If non-nil, and multiple eligible snippet tables, prompts user for tables first.
- Otherwise, user chooses between the merging together of all
- eligible tables.
- This affects `yas/insert-snippet', `yas/visit-snippet-file'"
- :type 'boolean
- :group 'yasnippet)
- (defcustom yas/use-menu 'real-modes
- "Display a YASnippet menu in the menu bar.
- When non-nil, submenus for each snippet table will be listed
- under the menu \"Yasnippet\".
- - If set to `real-modes' only submenus whose name more or less
- corresponds to a major mode are listed.
- - If set to `abbreviate', only the current major-mode
- menu and the modes set in `yas/mode-symbol' are listed.
- Any other non-nil value, every submenu is listed."
- :type '(choice (const :tag "Full" t)
- (const :tag "Real modes only" real-modes)
- (const :tag "Abbreviate" abbreviate))
- :group 'yasnippet)
- (defcustom yas/trigger-symbol " =>"
- "The text that will be used in menu to represent the trigger."
- :type 'string
- :group 'yasnippet)
- (defcustom yas/wrap-around-region nil
- "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."
- :type 'boolean
- :group 'yasnippet)
- (defcustom yas/good-grace t
- "If non-nil, don't raise errors in inline elisp evaluation.
- An error string \"[yas] error\" is returned instead."
- :type 'boolean
- :group 'yasnippet)
- (defcustom yas/ignore-filenames-as-triggers nil
- "If non-nil, don't derive tab triggers from filenames.
- This means a snippet without a \"# key:'\ directive wont have a
- tab trigger."
- :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."
- :type 'boolean
- :group 'yasnippet)
- (defface yas/field-highlight-face
- '((((class color) (background light)) (:background "DarkSeaGreen1"))
- (t (:background "DimGrey")))
- "The face used to highlight the currently active field of a snippet"
- :group 'yasnippet)
- (defface yas/field-debug-face
- '()
- "The face used for debugging some overlays normally hidden"
- :group 'yasnippet)
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; 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.")
- (defvar yas/key-syntaxes (list "w" "w_" "w_." "^ ")
- "A list of syntax of a key. This list is tried in the order
- to try to find a key. For example, if the list is '(\"w\" \"w_\").
- And in emacs-lisp-mode, where \"-\" has the syntax of \"_\":
- foo-bar
- will first try \"bar\", if not found, then \"foo-bar\" is tried.")
- (defvar yas/after-exit-snippet-hook
- '()
- "Hooks to run after a snippet exited.
- The hooks will be run in an environment where some variables bound to
- proper values:
- `yas/snippet-beg' : The beginning of the region of the snippet.
- `yas/snippet-end' : Similar to beg.
- Attention: These hooks are not run when exiting nested/stackd snippet expansion!")
- (defvar yas/before-expand-snippet-hook
- '()
- "Hooks to run just before expanding a snippet.")
- (defvar yas/buffer-local-condition
- '(if (and (not (bobp))
- (or (equal 'font-lock-comment-face
- (get-char-property (1- (point))
- 'face))
- (equal 'font-lock-string-face
- (get-char-property (1- (point))
- 'face))))
- '(require-snippet-condition . force-in-comment)
- t)
- "Snippet expanding condition.
- This variable is a lisp form:
- * If it evaluates to nil, no snippets can be expanded.
- * If it evaluates to the a cons (require-snippet-condition
- . REQUIREMENT)
- * Snippets bearing no \"# condition:\" directive are not
- considered
- * Snippets bearing conditions that evaluate to nil (or
- produce an error) won't be onsidered.
- * If the snippet has a condition that evaluates to non-nil
- RESULT:
- * If REQUIREMENT is t, the snippet is considered
- * If REQUIREMENT is `eq' RESULT, the snippet is
- considered
- * Otherwise, the snippet is not considered.
- * If it evaluates to the symbol 'always, all snippets are
- considered for expansion, regardless of any conditions.
- * If it evaluates to t or some other non-nil value
- * Snippet bearing no conditions, or conditions that
- evaluate to non-nil, are considered for expansion.
- * Otherwise, the snippet is not considered.
- Here's an example preventing snippets from being expanded from
- inside comments, in `python-mode' only, with the exception of
- snippets returning the symbol 'force-in-comment in their
- conditions.
- (add-hook 'python-mode-hook
- '(lambda ()
- (setq yas/buffer-local-condition
- '(if (python-in-string/comment)
- '(require-snippet-condition . force-in-comment)
- t))))
- The default value is similar, it filters out potential snippet
- expansions inside comments and string literals, unless the
- snippet itself contains a condition that returns the symbol
- `force-in-comment'.")
- (make-variable-buffer-local 'yas/buffer-local-condition)
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; Internal variables
- (defvar yas/version "0.6.1b")
- (defvar yas/menu-table (make-hash-table)
- "A hash table of MAJOR-MODE symbols to menu keymaps.")
- (defvar yas/active-keybindings nil
- "A list of cons (KEYMAP . KEY) setup from defining snippets.")
- (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.")
- (defconst yas/field-regexp
- "${\\([0-9]+:\\)?\\([^}]*\\)}"
- "A regexp to *almost* recognize a field.")
- (defconst yas/multi-dollar-lisp-expression-regexp
- "$+[ \t\n]*\\(([^)]*)\\)"
- "A regexp to *almost* recognize a \"$(...)\" expression.")
- (defconst yas/backquote-lisp-expression-regexp
- "`\\([^`]*\\)`"
- "A regexp to recognize a \"`lisp-expression`\" expression." )
- (defconst yas/transform-mirror-regexp
- "${\\(?:\\([0-9]+\\):\\)?$\\([ \t\n]*([^}]*\\)"
- "A regexp to *almost* recognize a mirror with a transform.")
- (defconst yas/simple-mirror-regexp
- "$\\([0-9]+\\)"
- "A regexp to recognize a simple mirror.")
- (defvar yas/snippet-id-seed 0
- "Contains the next id for a snippet.")
- (defun yas/snippet-next-id ()
- (let ((id yas/snippet-id-seed))
- (incf yas/snippet-id-seed)
- id))
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; Minor mode stuff
- ;; XXX: `last-buffer-undo-list' is somehow needed in Carbon Emacs for MacOSX
- (defvar last-buffer-undo-list nil)
- (defvar yas/minor-mode-menu nil
- "Holds the YASnippet menu")
- (defun yas/init-minor-keymap ()
- (let ((map (make-sparse-keymap)))
- (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"]
- ["Find snippets..." yas/find-snippets
- :help "Invoke `find-file' in the appropriate snippet directory"]
- "----"
- ("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 \"Real\" modes only" (setq yas/use-menu 'real-modes)
- :help "Show snippet submenus for modes that appear to be real major modes"
- :active t :style radio :selected (eq yas/use-menu 'real-modes)]
- ["Show all modes" (setq yas/use-menu 't)
- :help "Show one snippet submenu for each loaded table"
- :active t :style radio :selected (eq yas/use-menu 't)]
- ["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-prompt
- yas/prompt-functions)))
- :help "Use a normal minibuffer prompt"
- :active t :style radio :selected (eq (car yas/prompt-functions)
- 'yas/completing-prompt-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 eslip in snippets"
- :style toggle :selected yas/good-grace]
- ["Ignore filenames as triggers"
- (setq yas/ignore-filenames-as-triggers
- (not yas/ignore-filenames-as-triggers))
- :help "If non-nil don't derive tab triggers from filenames"
- :style toggle :selected yas/ignore-filenames-as-triggers]
- )
- "----"
- ["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 YASsnippet"]))
- ;; Now for the stuff that has direct keybindings
- ;;
- (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)
- (define-key map "\C-c&\C-f" 'yas/find-snippets)
- map))
- (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)))
- ;;;###autoload
- (define-minor-mode yas/minor-mode
- "Toggle YASnippet mode.
- When YASnippet mode is enabled, the `tas/trigger-key' key expands
- snippets of code depending on the 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
- ;; The indicator for the mode line.
- " yas"
- :group 'yasnippet
- (when yas/minor-mode
- (yas/trigger-key-reload)
- ;; load all snippets definitions unless we still don't have a
- ;; root-directory or some snippets have already been loaded.
- (unless (or (null yas/root-directory)
- (> (hash-table-count yas/snippet-tables) 0))
- (yas/reload-all))))
- (defvar yas/dont-activate #'(lambda ()
- (and yas/root-directory
- (null (yas/get-snippet-tables))))
- "If non-nil don't let `yas/minor-mode-on' active 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.")
- (make-variable-buffer-local 'yas/dont-activate)
- (defun yas/minor-mode-on ()
- "Turn on YASnippet minor mode.
- Do this unless `yas/dont-activate' is t or the function
- `yas/get-snippet-tables' (which see), returns an empty list."
- (interactive)
- (unless (or (and (functionp yas/dont-activate)
- (funcall yas/dont-activate))
- (and (not (functionp yas/dont-activate))
- yas/dont-activate))
- (yas/minor-mode 1)))
- (defun yas/minor-mode-off ()
- "Turn off YASnippet minor mode."
- (interactive)
- (yas/minor-mode -1))
- (define-globalized-minor-mode yas/global-mode yas/minor-mode yas/minor-mode-on
- :group 'yasnippet
- :require 'yasnippet)
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; Major mode stuff
- ;;
- (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)
- (1 font-lock-string-face t))
- ("${\\([0-9]+\\):?"
- (0 font-lock-keyword-face)
- (1 font-lock-warning-face t))
- ("${" font-lock-keyword-face)
- ("$[0-9]+?" font-lock-preprocessor-face)
- ("\\(\\$(\\)" 1 font-lock-preprocessor-face)
- ("}"
- (0 font-lock-keyword-face)))))
- (defun yas/init-major-keymap ()
- (let ((map (make-sparse-keymap)))
- (easy-menu-define nil
- map
- "Menu used when snippet-mode is active."
- (cons "Snippet"
- (mapcar #'(lambda (ent)
- (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")
- (define-derived-mode snippet-mode text-mode "Snippet"
- "A mode for editing yasnippets"
- (set-syntax-table (standard-syntax-table))
- (setq font-lock-defaults '(yas/font-lock-keywords))
- (set (make-local-variable 'require-final-newline) nil)
- (use-local-map snippet-mode-map))
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; Internal structs for template management
- (defstruct (yas/template (:constructor yas/make-template
- (content name condition expand-env file keybinding)))
- "A template for a snippet."
- content
- name
- condition
- expand-env
- file
- keybinding)
- (defvar yas/snippet-tables (make-hash-table)
- "A hash table of MAJOR-MODE symbols to `yas/snippet-table' objects.")
- (defstruct (yas/snippet-table (:constructor yas/make-snippet-table (name)))
- "A table to store snippets for a particular mode.
- Has the following fields:
- `yas/snippet-table-name'
- A symbol normally corresponding to a major mode, but can also be
- a pseudo major-mode to be referenced in `yas/mode-symbol', for
- example.
- `yas/snippet-table-hash'
- A hash table the key is a string (the snippet key) and the
- value is yet another hash of (NAME TEMPLATE), where NAME is the
- snippet name and TEMPLATE is a `yas/template' object name.
- `yas/snippet-table-parents'
- A list of tables considered parents of this table: i.e. when
- searching for expansions they are searched as well."
- name
- (hash (make-hash-table :test 'equal))
- (parents nil))
- (defvar yas/better-guess-for-replacements nil
- "If non-nil `yas/store' better guess snippet replacements.")
- (defun yas/store (table name key template)
- "Store a snippet template in the TABLE."
- ;; This is dones by searching twice:
- ;;
- ;; * Try to get the existing namehash from TABLE using key.
- ;;
- ;; * Try to get the existing namehash from by searching the *whole*
- ;; snippet table for NAME. This is becuase they user might have
- ;; changed the key and that can no longer be used to locate the
- ;; previous `yas/template-structure'.
- ;;
- ;; * If that returns nothing, oh well...
- ;;
- (dolist (existing-namehash (remove nil (list (gethash key (yas/snippet-table-hash table))
- (when yas/better-guess-for-replacements
- (let (a)
- (maphash #'(lambda (key namehash)
- (when (gethash name namehash)
- (setq a namehash)))
- (yas/snippet-table-hash table))
- a)))))
- (let ((existing-template (gethash name existing-namehash)))
- (when existing-template
- ;; Remove the existing keybinding
- (when (yas/template-keybinding existing-template)
- (define-key
- (symbol-value (first (yas/template-keybinding existing-template)))
- (second (yas/template-keybinding existing-template))
- nil)
- (setq yas/active-keybindings
- (delete (yas/template-keybinding existing-template)
- yas/active-keybindings)))
- ;; Remove the (name . template) mapping from existing-namehash.
- (remhash name existing-namehash))))
- ;; Now store the new template independent of the previous steps.
- ;;
- (puthash name
- template
- (or (gethash key
- (yas/snippet-table-hash table))
- (puthash key
- (make-hash-table :test 'equal)
- (yas/snippet-table-hash table)))))
- (defun yas/fetch (table key)
- "Fetch a snippet binding to KEY from TABLE."
- (let* ((keyhash (yas/snippet-table-hash table))
- (namehash (and keyhash (gethash key keyhash))))
- (when namehash
- (yas/filter-templates-by-condition
- (let (alist)
- (maphash #'(lambda (k v)
- (push (cons k v) alist))
- namehash)
- alist)))))
- ;; Filtering/condition logic
- (defun yas/eval-condition (condition)
- (condition-case err
- (save-excursion
- (save-restriction
- (save-match-data
- (eval condition))))
- (error (progn
- (message (format "[yas] error in condition evaluation: %s"
- (error-message-string err)))
- nil))))
- (defun yas/filter-templates-by-condition (templates)
- "Filter the templates using the applicable condition.
- TEMPLATES is a list of cons (NAME . TEMPLATE) where NAME is a
- string and TEMPLATE is a `yas/template' structure.
- This function implements the rules described in
- `yas/buffer-local-condition'. See that variables documentation."
- (let ((requirement (yas/require-template-specific-condition-p)))
- (if (eq requirement 'always)
- templates
- (remove-if-not #'(lambda (pair)
- (yas/template-can-expand-p (yas/template-condition (cdr pair)) requirement))
- templates))))
- (defun yas/require-template-specific-condition-p ()
- "Decides if this buffer requests/requires snippet-specific
- conditions to filter out potential expansions."
- (if (eq 'always yas/buffer-local-condition)
- 'always
- (let ((local-condition (or (and (consp yas/buffer-local-condition)
- (yas/eval-condition yas/buffer-local-condition))
- yas/buffer-local-condition)))
- (when local-condition
- (if (eq local-condition t)
- t
- (and (consp local-condition)
- (eq 'require-snippet-condition (car local-condition))
- (symbolp (cdr local-condition))
- (cdr local-condition)))))))
- (defun yas/template-can-expand-p (condition &optional requirement)
- "Evaluates CONDITION and REQUIREMENT and returns a boolean"
- (let* ((requirement (or requirement
- (yas/require-template-specific-condition-p)))
- (result (or (null condition)
- (yas/eval-condition
- (condition-case err
- (read condition)
- (error (progn
- (message (format "[yas] error reading condition: %s"
- (error-message-string err))))
- nil))))))
- (cond ((eq requirement t)
- result)
- (t
- (eq requirement result)))))
- (defun yas/snippet-table-get-all-parents (table)
- (let ((parents (yas/snippet-table-parents table)))
- (when parents
- (append (copy-list parents)
- (mapcan #'yas/snippet-table-get-all-parents parents)))))
- (defun yas/snippet-table-templates (table)
- (when table
- (let ((acc (list)))
- (maphash #'(lambda (key namehash)
- (maphash #'(lambda (name template)
- (push (cons name template) acc))
- namehash))
- (yas/snippet-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/snippet-table-all-keys (table)
- (when table
- (let ((acc))
- (maphash #'(lambda (key templates)
- (when (yas/filter-templates-by-condition templates)
- (push key acc)))
- (yas/snippet-table-hash table))
- acc)))
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; Internal functions
- (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."
- (or (fboundp mode)
- (find mode yas/known-modes)))
- (defun yas/read-and-eval-string (string)
- ;; TODO: This is a possible optimization point, the expression could
- ;; be stored in cons format instead of string,
- "Evaluate STRING and convert the result to string."
- (let ((retval (catch 'yas/exception
- (condition-case err
- (save-excursion
- (save-restriction
- (save-match-data
- (widen)
- (let ((result (eval (read string))))
- (when result
- (format "%s" result))))))
- (error (if yas/good-grace
- "[yas] elisp error!"
- (error (format "[yas] elisp error: %s"
- (error-message-string err)))))))))
- (when (and (consp retval)
- (eq 'yas/exception (car retval)))
- (error (cdr retval)))
- retval))
- (defvar yas/mode-symbol nil
- "If non-nil, lookup snippets using this instead of `major-mode'.")
- (make-variable-buffer-local 'yas/mode-symbol)
- (defun yas/snippet-table-get-create (mode)
- "Get the snippet table corresponding to MODE.
- Optional DIRECTORY gets recorded as the default directory to
- search for snippet files if the retrieved/created table didn't
- already have such a property."
- (let ((table (gethash mode
- yas/snippet-tables)))
- (unless table
- (setq table (yas/make-snippet-table (symbol-name mode)))
- (puthash mode table yas/snippet-tables))
- table))
- (defun yas/get-snippet-tables (&optional mode-symbol dont-search-parents)
- "Get snippet tables for current buffer.
- Return a list of 'yas/snippet-table' objects indexed by mode.
- The modes are tried in this order: optional MODE-SYMBOL, then
- `yas/mode-symbol', then `major-mode' then, unless
- DONT-SEARCH-PARENTS is non-nil, the guessed parent mode of either
- MODE-SYMBOL or `major-mode'.
- Guessing is done by looking up the MODE-SYMBOL's
- `derived-mode-parent' property, see also `derived-mode-p'."
- (let ((mode-tables
- (mapcar #'(lambda (mode)
- (gethash mode yas/snippet-tables))
- (append (list mode-symbol)
- (if (listp yas/mode-symbol)
- yas/mode-symbol
- (list yas/mode-symbol))
- (list major-mode
- (and (not dont-search-parents)
- (get (or mode-symbol major-mode)
- 'derived-mode-parent))))))
- (all-tables))
- (dolist (table (remove nil mode-tables))
- (push table all-tables)
- (nconc all-tables (yas/snippet-table-get-all-parents table)))
- (remove-duplicates all-tables)))
- (defun yas/menu-keymap-get-create (mode)
- "Get the menu keymap correspondong to MODE."
- (or (gethash mode yas/menu-table)
- (puthash mode (make-sparse-keymap) yas/menu-table)))
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Template-related and snippet loading functions
- (defun yas/parse-template (&optional file)
- "Parse the template in the current buffer.
- Optional FILE is the absolute file name of the file being
- parsed.
- Return a snippet-definition, i.e. a list
- (KEY TEMPLATE NAME CONDITION GROUP VARS FILE KEYBINDING)
- If the buffer contains a line of \"# --\" then the contents
- above this line are ignored. Variables can be set above this
- line through the syntax:
- #name : value
- Here's a list of currently recognized variables:
- * name
- * contributor
- * condition
- * key
- * group
- * expand-env
- #name: #include \"...\"
- # --
- #include \"$1\""
- ;;
- ;;
- (goto-char (point-min))
- (let* ((name (and file
- (file-name-nondirectory file)))
- (key (unless yas/ignore-filenames-as-triggers
- (and name
- (file-name-sans-extension name))))
- template
- bound
- condition
- (group (and file
- (yas/calculate-group file)))
- expand-env
- binding)
- (if (re-search-forward "^# --\n" nil t)
- (progn (setq template
- (buffer-substring-no-properties (point)
- (point-max)))
- (setq bound (point))
- (goto-char (point-min))
- (while (re-search-forward "^# *\\([^ ]+?\\) *: *\\(.*\\)$" bound t)
- (when (string= "name" (match-string-no-properties 1))
- (setq name (match-string-no-properties 2)))
- (when (string= "condition" (match-string-no-properties 1))
- (setq condition (match-string-no-properties 2)))
- (when (string= "group" (match-string-no-properties 1))
- (setq group (match-string-no-properties 2)))
- (when (string= "expand-env" (match-string-no-properties 1))
- (setq expand-env (match-string-no-properties 2)))
- (when (string= "key" (match-string-no-properties 1))
- (setq key (match-string-no-properties 2)))
- (when (string= "binding" (match-string-no-properties 1))
- (setq binding (match-string-no-properties 2)))))
- (setq template
- (buffer-substring-no-properties (point-min) (point-max))))
- (list key template name condition group expand-env file binding)))
- (defun yas/calculate-group (file)
- "Calculate the group for snippet file path FILE."
- (let* ((dominating-dir (locate-dominating-file file
- ".yas-make-groups"))
- (extra-path (and dominating-dir
- (replace-regexp-in-string (concat "^"
- (expand-file-name dominating-dir))
- ""
- (expand-file-name file))))
- (extra-dir (and extra-path
- (file-name-directory extra-path)))
- (group (and extra-dir
- (replace-regexp-in-string "/"
- "."
- (directory-file-name extra-dir)))))
- group))
- ;; (defun yas/glob-files (directory &optional recurse-p append)
- ;; "Returns files under DIRECTORY ignoring dirs and hidden files.
- ;; If RECURSE in non-nil, do that recursively."
- ;; (let (ret
- ;; (default-directory directory))
- ;; (dolist (entry (directory-files "."))
- ;; (cond ((or (string-match "^\\."
- ;; (file-name-nondirectory entry))
- ;; (string-match "~$"
- ;; (file-name-nondirectory entry)))
- ;; nil)
- ;; ((and recurse-p
- ;; (file-directory-p entry))
- ;; (setq ret (nconc ret
- ;; (yas/glob-files (expand-file-name entry)
- ;; recurse-p
- ;; (if append
- ;; (concat append "/" entry)
- ;; entry)))))
- ;; ((file-directory-p entry)
- ;; nil)
- ;; (t
- ;; (push (if append
- ;; (concat append "/" entry)
- ;; entry) ret))))
- ;; ret))
- (defun yas/subdirs (directory &optional file?)
- "Return subdirs or files of DIRECTORY according to FILE?."
- (remove-if (lambda (file)
- (or (string-match "^\\."
- (file-name-nondirectory file))
- (string-match "~$"
- (file-name-nondirectory file))
- (if file?
- (file-directory-p file)
- (not (file-directory-p file)))))
- (directory-files directory t)))
- (defun yas/make-menu-binding (template)
- `(lambda () (interactive) (yas/expand-or-visit-from-menu ,template)))
- (defun yas/expand-or-visit-from-menu (template)
- (if yas/visit-from-menu
- (yas/visit-snippet-file-1 template)
- (let ((where (if mark-active
- (cons (region-beginning) (region-end))
- (cons (point) (point)))))
- (yas/expand-snippet (yas/template-content template)
- (car where)
- (cdr where)))))
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; Popping up for keys and templates
- ;;
- (defun yas/prompt-for-template (templates &optional prompt)
- "Interactively choose a template from the list TEMPLATES.
- TEMPLATES is a list of `yas/template'."
- (when templates
- (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."
- (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)
- (when tables
- (some #'(lambda (fn)
- (funcall fn (or prompt "Choose a snippet table: ")
- tables
- #'yas/snippet-table-name))
- yas/prompt-functions)))
- (defun yas/x-prompt (prompt choices &optional display-fn)
- (when (and window-system choices)
- (let ((keymap (cons 'keymap
- (cons
- prompt
- (mapcar (lambda (choice)
- (list choice
- 'menu-item
- (if display-fn
- (funcall display-fn choice)
- choice)
- t))
- choices)))))
- (when (cdr keymap)
- (car (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)
- keymap))))))
- (defun yas/ido-prompt (prompt choices &optional display-fn)
- (when (and (featurep 'ido)
- ido-mode)
- (let* ((formatted-choices (or (and display-fn
- (mapcar display-fn choices))
- choices))
- (chosen (and formatted-choices
- (ido-completing-read prompt
- formatted-choices
- nil
- 'require-match
- nil
- nil))))
- (when chosen
- (nth (position chosen formatted-choices :test #'string=) choices)))))
- (eval-when-compile (require 'dropdown-list nil t))
- (defun yas/dropdown-promp…