* typed a lot of cleanup code, probably many many bugs

* at least most of the customization group is here I think
* still got a lot to do for the minor mode (including making it global)
* TODO: write wrap regions
* TODO: write per-snippet variable overrides
* goto sleep
This commit is contained in:
capitaomorte 2009-07-13 22:19:41 +00:00
parent 0622075ba9
commit bc7acbaf99

View File

@ -2,8 +2,8 @@
;; Copyright 2008 pluskid ;; Copyright 2008 pluskid
;; ;;
;; Author: pluskid <pluskid@gmail.com> ;; Authors: pluskid <pluskid@gmail.com>, joaotavora <joaotavora@gmail.com>
;; Version: 0.5.6 XXX: Change this ;; Version: 0.6.0 XXX: Change this
;; X-URL: http://code.google.com/p/yasnippet/ ;; X-URL: http://code.google.com/p/yasnippet/
;; This file is free software; you can redistribute it and/or modify ;; This file is free software; you can redistribute it and/or modify
@ -39,10 +39,179 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; User customizable variables ;; User customizable variables
;;
(defgroup yasnippet nil
"Yet Another Snippet extension"
:group 'editing)
(defcustom yas/root-directory nil
"The (list of) root directory that stores the snippets for each
major mode."
:type '(string)
:group 'yasnippet)
(defcustom yas/prompt-functions '(yas/x-prompt
yas/ido-prompt
yas/dropdown-prompt
yas/completing-prompt
yas/no-prompt)
"List of functions to prompt the user for keys, templates and
other values interactively."
:type 'list
:group 'yasnippet)
(defcustom yas/indent-line 'auto
"Controls indenting applied to a recent snippet expansion.
The following values are possible:
`nothing' Don't apply any indendation after expansion;
`fixed' Indent the snippet to the current column;
`auto' Indent each line of the snippet with `indent-according-to-mode'"
:type '(choice (const :tag "Nothing" nothing)
(const :tag "Fixed" always)
(const :tag "Auto" auto))
:group 'yasnippet)
(defcustom yas/snippet-revival t
"Non-nil means re-activate snippet fields after an when undoing
an exit from an active snippet or redoing a snippet expansion"
:type 'boolean
:group 'yasnippet)
(defcustom yas/trigger-key "<tab>"
"The key to bind as a trigger of snippet when `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)
(defcustom yas/next-field-key "<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'. "
:type 'string
:group 'yasnippet)
(defcustom yas/prev-field-key "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'. "
:type 'string
:group 'yasnippet)
(defcustom yas/clear-field-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'. "
:type 'string
:group 'yasnippet)
(defcustom yas/triggers-in-field t
"If non-nil, allow `yas/next-field-key' to trigger a stacked
snippet expansion.
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.
The fall back behavior of YASnippet when it can't find a snippet
to expand.
`call-other-command' means try to temporarily disable
YASnippet and call other command bound to `yas/trigger-key'.
`return-nil' means return do nothing."
:type '(choice (const :tag "Call previous command" 'call-other-command)
(const :tag "Do nothing" 'return-nil))
:group 'yasnippet)
(defcustom yas/choose-keys-first t
"If non-nil, `yas/choose-snippet' prompts for key, then for template.
Otherwise `yas/choose-snippet' prompts for all possible
templates and inserts the selected one."
:type 'boolean
:group 'yasnippet)
(defcustom yas/use-menu t
"Display a YASnippet menu in the menu bar.
If this is set to `t', all snippet template of the current
mode will be listed under the menu \"yasnippet\"."
:type 'boolean
:group 'yasnippet)
(defcustom yas/trigger-symbol " =>"
"The text that will be used in menu to represent the trigger."
:type 'string
:group 'yasnippet)
(defcustom yas/show-all-modes-in-menu nil
"Display \"artificial\" major modes in menu bar as well.
Currently, YASnippet only all \"real modes\" to menubar. For
example, you define snippets for \"cc-mode\" and make it the
parent of `c-mode', `c++-mode' and `java-mode'. There's really
no such mode like \"cc-mode\". So we don't show it in the yasnippet
menu to avoid the menu becoming too big with strange modes. The
snippets defined for \"cc-mode\" can still be accessed from
menu-bar->c-mode->parent (or c++-mode, java-mode, all are ok).
However, if you really like to show all modes in the menu, set
this variable to t."
:type 'boolean
:group 'yasnippet)
(defcustom yas/wrap-around-region t
"If non-nil, wrap selected region in the snippet being
expanded.
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 nil
"If non-nil, don't raise errors in inline elisp evaluation.
The erorr string is instead returned."
: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
'((((class color) (background light)) (:background "tomato"))
(t (:background "tomato")))
"The face used for debugging some overlays normally hidden"
:group 'yasnippet)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar yas/dont-activate nil ;; User semi-customizable variables
"If set to t, don't activate yas/minor-mode automatically.") ;;
(make-variable-buffer-local 'yas/dont-activate)
(defvar yas/keymap (make-sparse-keymap)
"The keymap of snippet.")
(eval-when-compile
(define-key yas/keymap (read-kbd-macro yas/next-field-key) 'yas/next-field-or-maybe-expand)
(define-key yas/keymap (read-kbd-macro yas/prev-field-key) 'yas/prev-field)
(define-key yas/keymap (read-kbd-macro yas/clear-field-key) 'yas/clear-field-or-delete-char))
(defvar yas/key-syntaxes (list "w" "w_" "w_." "^ ") (defvar yas/key-syntaxes (list "w" "w_" "w_." "^ ")
"A list of syntax of a key. This list is tried in the order "A list of syntax of a key. This list is tried in the order
@ -53,96 +222,22 @@ foo-bar
will first try \"bar\", if not found, then \"foo-bar\" is tried.") will first try \"bar\", if not found, then \"foo-bar\" is tried.")
(defvar yas/root-directory nil
"The (list of) root directory that stores the snippets for each
major modes.")
(defvar yas/indent-line t
"Each (except the 1st) line of the snippet template is indented to
current column if this variable is non-`nil'.")
(make-variable-buffer-local 'yas/indent-line)
(defvar yas/trigger-key (kbd "<tab>")
"The key to bind as a trigger of snippet.")
(defvar yas/next-field-key (kbd "<tab>")
"The key to navigate to next field.")
(defvar yas/clear-field-key (kbd "C-d")
"The key to clear the currently active field.")
(defvar yas/keymap (make-sparse-keymap)
"The keymap of snippet.")
(define-key yas/keymap yas/next-field-key 'yas/next-field)
(define-key yas/keymap yas/clear-field-key 'yas/clear-field-or-delete-char)
(define-key yas/keymap (kbd "S-TAB") 'yas/prev-field)
(define-key yas/keymap (kbd "<deletechar>") 'yas/prev-field)
(define-key yas/keymap (kbd "<S-iso-lefttab>") 'yas/prev-field)
(define-key yas/keymap (kbd "<S-tab>") 'yas/prev-field)
(define-key yas/keymap (kbd "<backtab>") 'yas/prev-field)
(defvar yas/show-all-modes-in-menu nil
"Currently yasnippet only all \"real modes\" to menubar. For
example, you define snippets for \"cc-mode\" and make it the
parent of `c-mode', `c++-mode' and `java-mode'. There's really
no such mode like \"cc-mode\". So we don't show it in the yasnippet
menu to avoid the menu becoming too big with strange modes. The
snippets defined for \"cc-mode\" can still be accessed from
menu-bar->c-mode->parent (or c++-mode, java-mode, all are ok).
However, if you really like to show all modes in the menu, set
this variable to t.")
(defvar yas/use-menu t
"If this is set to `t', all snippet template of the current
mode will be listed under the menu \"yasnippet\".")
(defvar yas/trigger-symbol " =>"
"The text that will be used in menu to represent the trigger.")
(defvar yas/good-grace nil
"If non-nil, don't raise errors in inline elisp evaluation,
return the error string instead.")
(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")
(defface yas/mirror-highlight-face
'((((class color) (background light)) (:background "Dodgerblue"))
(t (:background "DimGrey")))
"The face used to highlight a mirror of a snippet")
(defface yas/field-debug-face
'((((class color) (background light)) (:background "tomato"))
(t (:background "tomato")))
"The face used for debugging")
(defvar yas/popup-functions
'( yas/x-popup
yas/ido-popup
yas/dropdown-popup
yas/completing-popup
yas/no-popup ))
(defvar yas/extra-mode-hooks
'()
"A list of mode-hook that should be hooked to enable yas/minor-mode.
Most modes need no special consideration. Some mode (like `ruby-mode')
doesn't call `after-change-major-mode-hook' need to be hooked explicitly.")
(mapc '(lambda (x)
(add-to-list 'yas/extra-mode-hooks
x))
'(ruby-mode-hook actionscript-mode-hook ox-mode-hook python-mode-hook))
(defvar yas/after-exit-snippet-hook (defvar yas/after-exit-snippet-hook
'() '()
"Hooks to run after a snippet exited. "Hooks to run after a snippet exited.
The hooks will be run in an environment where some variables bound to The hooks will be run in an environment where some variables bound to
proper values: proper values:
* yas/snippet-beg : The beginning of the region of the snippet.
* yas/snippet-end : Similar to beg.") `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 (defvar yas/before-expand-snippet-hook
'() '()
"Hooks to run after a before expanding a snippet.") "Hooks to run just before expanding a snippet.")
(defvar yas/buffer-local-condition (defvar yas/buffer-local-condition
'(if (and (not (bobp)) '(if (and (not (bobp))
@ -187,37 +282,38 @@ Here's an example:
'(require-snippet-condition . force-in-comment) '(require-snippet-condition . force-in-comment)
t))))") t))))")
(defvar yas/fallback-behavior 'call-other-command
"The fall back behavior of YASnippet when it can't find a snippet
to expand.
* 'call-other-command means try to temporarily disable
YASnippet and call other command bound to `yas/trigger-key'.
* 'return-nil means return nil.")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Internal variables ;; Internal variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;
(defvar yas/version "0.5.6-nested-placeholders") (defvar yas/version "0.5.6-nested-placeholders")
(defvar yas/snippet-tables (make-hash-table) (defvar yas/snippet-tables (make-hash-table)
"A hash table of snippet tables corresponding to each major-mode.") "A hash table of snippet tables corresponding to each major-mode.")
(defvar yas/menu-table (make-hash-table) (defvar yas/menu-table (make-hash-table)
"A hash table of menus of corresponding major-mode.") "A hash table of menus of corresponding major-mode.")
(defvar yas/menu-keymap (make-sparse-keymap "YASnippet")) (defvar yas/menu-keymap (make-sparse-keymap "YASnippet"))
;; empty menu will cause problems, so we insert some items ;; empty menu will cause problems, so we insert some items
(define-key yas/menu-keymap [yas/about]
'(menu-item "About" yas/about)) (eval-when-compile
(define-key yas/menu-keymap [yas/reload] (define-key yas/menu-keymap [yas/about]
'(menu-item "Reload all snippets" yas/reload-all)) '(menu-item "About" yas/about))
(define-key yas/menu-keymap [yas/load]
'(menu-item "Load snippets..." yas/load-directory)) (define-key yas/menu-keymap [yas/reload]
(define-key yas/menu-keymap [yas/separator] '(menu-item "Reload all snippets" yas/reload-all))
'(menu-item "--"))
(define-key yas/menu-keymap [yas/load]
'(menu-item "Load snippets..." yas/load-directory))
(define-key yas/menu-keymap [yas/separator]
'(menu-item "--")))
(defvar yas/known-modes (defvar yas/known-modes
'(ruby-mode rst-mode markdown-mode) '(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 yas/escaped-characters
'(?\\ ?` ?$ ?} ) '(?\\ ?` ?$ ?} )
"A list of characters which *might* need to be escaped in "A list of characters which *might* need to be escaped in
@ -245,6 +341,7 @@ snippet templates")
(defvar yas/snippet-id-seed 0 (defvar yas/snippet-id-seed 0
"Contains the next id for a snippet.") "Contains the next id for a snippet.")
(defun yas/snippet-next-id () (defun yas/snippet-next-id ()
(let ((id yas/snippet-id-seed)) (let ((id yas/snippet-id-seed))
(incf yas/snippet-id-seed) (incf yas/snippet-id-seed)
@ -252,13 +349,11 @@ snippet templates")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; YASnippet minor mode ;; YASnippet minor mode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;
(defvar yas/minor-mode-map (make-sparse-keymap) (defvar yas/minor-mode-map (make-sparse-keymap)
"The keymap of yas/minor-mode") "The keymap of yas/minor-mode")
(defvar yas/minor-mode-on-hook nil
"Hook to call when yas/minor-mode is on.")
(defvar yas/minor-mode-off-hook nil
"Hook to call when yas/minor-mode is off.")
(define-minor-mode yas/minor-mode (define-minor-mode yas/minor-mode
"Toggle YASnippet mode. "Toggle YASnippet mode.
With no argument, this command toggles the mode. With no argument, this command toggles the mode.
@ -274,25 +369,22 @@ You can customize the key through `yas/trigger-key'."
;; The indicator for the mode line. ;; The indicator for the mode line.
" yas" " yas"
:group 'editing :group 'editing
(define-key yas/minor-mode-map yas/trigger-key 'yas/expand)) (define-key yas/minor-mode-map (read-kbd-macro yas/trigger-key) 'yas/expand))
(defun yas/minor-mode-auto-on ()
"Turn on YASnippet minor mode unless `yas/dont-activate' is
set to t."
(unless yas/dont-activate
(yas/minor-mode-on)))
(defun yas/minor-mode-on () (defun yas/minor-mode-on ()
"Turn on YASnippet minor mode." "Turn on YASnippet minor mode."
(interactive) (interactive)
(yas/minor-mode 1)) (yas/minor-mode 1))
(defun yas/minor-mode-off () (defun yas/minor-mode-off ()
"Turn off YASnippet minor mode." "Turn off YASnippet minor mode."
(interactive) (interactive)
(yas/minor-mode -1)) (yas/minor-mode -1))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Internal Structs ;; Internal structs for template management
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;
(defstruct (yas/template (:constructor yas/make-template (defstruct (yas/template (:constructor yas/make-template
(content name condition))) (content name condition)))
"A template for a snippet." "A template for a snippet."
@ -381,7 +473,8 @@ fetch from parent if any."
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Internal functions ;; Internal functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;
(defun yas/ensure-minor-mode-priority () (defun yas/ensure-minor-mode-priority ()
"Ensure that the key binding of yas/minor-mode takes priority." "Ensure that the key binding of yas/minor-mode takes priority."
(unless (eq 'yas/minor-mode (unless (eq 'yas/minor-mode
@ -530,25 +623,26 @@ Here's a list of currently recognized variables:
(setcdr pair value) (setcdr pair value)
alist))) alist)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Popping up for keys and templates ;; Popping up for keys and templates
;; ;;
(defun yas/popup-for-template-content (templates) (defun yas/prompt-for-template-content (templates)
"Interactively choose a template's content from the list "Interactively choose a template's content from the list
TEMPLATES." TEMPLATES."
(let ((template (some #'(lambda (fn) (let ((template (some #'(lambda (fn)
(funcall fn "Choose a snippet: " templates #'(lambda (template) (funcall fn "Choose a snippet: " templates #'(lambda (template)
(yas/template-name template)))) (yas/template-name template))))
yas/popup-functions))) yas/prompt-functions)))
(when template (when template
(yas/template-content template)))) (yas/template-content template))))
(defun yas/popup-for-keys (keys) (defun yas/prompt-for-keys (keys)
"Interactively choose a template key from the list KEYS." "Interactively choose a template key from the list KEYS."
(some #'(lambda (fn) (some #'(lambda (fn)
(funcall fn "Choose a snippet key: " keys)) (funcall fn "Choose a snippet key: " keys))
yas/popup-functions)) yas/prompt-functions))
(defun yas/x-popup (prompt choices &optional display-fn) (defun yas/x-prompt (prompt choices &optional display-fn)
(when window-system (when window-system
(let ((keymap (cons 'keymap (let ((keymap (cons 'keymap
(cons (cons
@ -570,8 +664,7 @@ TEMPLATES."
t) t)
keymap)))))) keymap))))))
(defun yas/ido-prompt (prompt choices &optional display-fn)
(defun yas/ido-popup (prompt choices &optional display-fn)
(when (featurep 'ido) (when (featurep 'ido)
(let* ((formatted-choices (or (and display-fn (let* ((formatted-choices (or (and display-fn
(mapcar display-fn choices)) (mapcar display-fn choices))
@ -586,19 +679,21 @@ TEMPLATES."
(when chosen (when chosen
(nth (position chosen formatted-choices) choices))))) (nth (position chosen formatted-choices) choices)))))
(defun yas/dropdown-popup (prompt choices &optional display-fn) (defun yas/dropdown-prompt (prompt choices &optional display-fn)
(when (featurep 'dropdown-list) (when (featurep 'dropdown-list)
)) ))
(defun yas/completing-popup (prompt choices &optional display-fn) (defun yas/completing-prompt (prompt choices &optional display-fn)
) )
(defun yas/no-popup (prompt choices &optional display-fn) (defun yas/no-prompt (prompt choices &optional display-fn)
) )
;; Loading snippets ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Loading snippets from files
;; ;;
(defun yas/load-directory-1 (directory &optional parent) (defun yas/load-directory-1 (directory &optional parent)
"Really do the job of loading snippets from a directory "Really do the job of loading snippets from a directory
hierarchy." hierarchy."
(let ((mode-sym (intern (file-name-nondirectory directory))) (let ((mode-sym (intern (file-name-nondirectory directory)))
@ -617,6 +712,32 @@ hierarchy."
(dolist (subdir (yas/directory-files directory nil)) (dolist (subdir (yas/directory-files directory nil))
(yas/load-directory-1 subdir mode-sym)))) (yas/load-directory-1 subdir mode-sym))))
(defun yas/load-directory (directory)
"Load snippet definition from a directory hierarchy.
Below the top-level directory, each directory is a mode
name. And under each subdirectory, each file is a definition
of a snippet. The file name is the trigger key and the
content of the file is the template."
(interactive "DSelect the root directory: ")
(unless (file-directory-p directory)
(error "Error %s not a directory" directory))
(add-to-list 'yas/root-directory directory)
(dolist (dir (yas/directory-files directory nil))
(yas/load-directory-1 dir))
(when (interactive-p)
(message "done.")))
(defun yas/reload-all ()
"Reload all snippets."
(interactive)
(if yas/root-directory
(if (listp yas/root-directory)
(dolist (directory yas/root-directory)
(yas/load-directory directory))
(yas/load-directory yas/root-directory))
(call-interactively 'yas/load-directory))
(message "done."))
(defun yas/quote-string (string) (defun yas/quote-string (string)
"Escape and quote STRING. "Escape and quote STRING.
foo\"bar\\! -> \"foo\\\"bar\\\\!\"" foo\"bar\\! -> \"foo\\\"bar\\\\!\""
@ -698,6 +819,7 @@ all the parameters:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; User level functions ;;; User level functions
;;;
(defun yas/about () (defun yas/about ()
(interactive) (interactive)
@ -705,39 +827,8 @@ all the parameters:
yas/version yas/version
") -- pluskid <pluskid@gmail.com>"))) ") -- pluskid <pluskid@gmail.com>")))
(defun yas/reload-all ()
"Reload all snippets."
(interactive)
(if yas/root-directory
(if (listp yas/root-directory)
(dolist (directory yas/root-directory)
(yas/load-directory directory))
(yas/load-directory yas/root-directory))
(call-interactively 'yas/load-directory))
(message "done."))
(defun yas/load-directory (directory)
"Load snippet definition from a directory hierarchy.
Below the top-level directory, each directory is a mode
name. And under each subdirectory, each file is a definition
of a snippet. The file name is the trigger key and the
content of the file is the template."
(interactive "DSelect the root directory: ")
(unless (file-directory-p directory)
(error "Error %s not a directory" directory))
(add-to-list 'yas/root-directory directory)
(dolist (dir (yas/directory-files directory nil))
(yas/load-directory-1 dir))
(when (interactive-p)
(message "done.")))
(defun yas/initialize () (defun yas/initialize ()
"Do necessary initialization." "Do necessary initialization. When turning on `yas/minor-mode'"
(add-hook 'after-change-major-mode-hook
'yas/minor-mode-auto-on)
(dolist (hook yas/extra-mode-hooks)
(add-hook hook
'yas/minor-mode-auto-on))
(add-hook 'yas/minor-mode-on-hook (add-hook 'yas/minor-mode-on-hook
'yas/ensure-minor-mode-priority) 'yas/ensure-minor-mode-priority)
(when yas/use-menu (when yas/use-menu
@ -841,7 +932,9 @@ when the condition evaluated to non-nil."
(interactive) (interactive)
(multiple-value-bind (templates start end) (yas/current-key) (multiple-value-bind (templates start end) (yas/current-key)
(if templates (if templates
(let ((template-content (yas/popup-for-template-content templates))) (let ((template-content (or (and (rest templates) ;; more than one
(mapcar #'cdr (yas/prompt-for-template-content templates)))
(yas/template-content (cdar templates)))))
(when template-content (when template-content
(yas/expand-snippet start end template-content))) (yas/expand-snippet start end template-content)))
(if (eq yas/fallback-behavior 'return-nil) (if (eq yas/fallback-behavior 'return-nil)
@ -851,25 +944,19 @@ when the condition evaluated to non-nil."
(when (commandp command) (when (commandp command)
(call-interactively command))))))) (call-interactively command)))))))
(defvar yas/complete-for-keys t (defun yas/choose-snippet ()
"If non-nil, `yas/completing-expand' prompts for key, then for template.
Otherwise 'yas/completing-expand' prompts for all possible
templates and inserts the selected one.")
(defun yas/completing-expand ()
"Choose a snippet to expand, pop-up a list of choices according "Choose a snippet to expand, pop-up a list of choices according
to `yas/popup-function'." to `yas/prompt-function'."
(interactive) (interactive)
(let* ((templates (mapcar #'cdr (let* ((templates (mapcar #'cdr
(if yas/complete-for-keys (if yas/choose-keys-first
(let ((key (yas/popup-for-keys (yas/snippet-table-all-keys (yas/current-snippet-table))))) (let ((key (yas/prompt-for-keys (yas/snippet-table-all-keys (yas/current-snippet-table)))))
(when key (when key
(yas/snippet-table-fetch (yas/current-snippet-table) key 'no-condition))) (yas/snippet-table-fetch (yas/current-snippet-table) key 'no-condition)))
(yas/snippet-table-all-templates (yas/current-snippet-table))))) (yas/snippet-table-all-templates (yas/current-snippet-table)))))
(template-content (and templates (template-content (and templates
(or (and (cdr templates) (or (and (rest templates) ;; more than one template for same key
(yas/popup-for-template-content templates)) (yas/prompt-for-template-content templates))
(yas/template-content (car templates))))) (yas/template-content (car templates)))))
(where (if mark-active (where (if mark-active
(cons (region-beginning) (region-end)) (cons (region-beginning) (region-end))
@ -878,7 +965,27 @@ to `yas/popup-function'."
(yas/expand-snippet (car where) (cdr where) template-content)))) (yas/expand-snippet (car where) (cdr where) template-content))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Snippet expansion and field managment ;;; User conveniente functions, for using in snippet definitions
;;;
(defun yas/choose-value (&rest possibilities)
(some #'(lambda (fn)
(funcall fn "Choose: " possibilities))
yas/prompt-functions))
(defun yas/verify-value (&rest possibilities)
(when (and yas/moving-away (notany #'(lambda (pos) (string= pos yas/text)) possibilities))
(error "field only allows " possibilities)))
(defun yas/field-value (number)
(let ((snippet (car (yas/snippets-at-point)))
(field (and snippet
(yas/snippet-find-field snippet number))))
(when field
(yas/field-text-for-display field))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Snippet expansion and field management
(defvar yas/active-field-overlay nil (defvar yas/active-field-overlay nil
"Overlays the currently active field") "Overlays the currently active field")
@ -889,9 +996,14 @@ to `yas/popup-function'."
(defvar yas/deleted-text nil (defvar yas/deleted-text nil
"The text deleted in the last snippet expansion") "The text deleted in the last snippet expansion")
(make-variable-buffer-local 'yas/active-field-overlay) (defvar yas/selection-text nil
(make-variable-buffer-local 'yas/field-protection-overlays) "The previously selected region deleted in the last snippet
(make-variable-buffer-local 'yas/deleted-text) expansion")
(eval-when-compile
(make-variable-buffer-local 'yas/active-field-overlay)
(make-variable-buffer-local 'yas/field-protection-overlays)
(make-variable-buffer-local 'yas/deleted-text))
(defstruct (yas/snippet (:constructor yas/make-snippet ())) (defstruct (yas/snippet (:constructor yas/make-snippet ()))
"A snippet. "A snippet.
@ -937,7 +1049,6 @@ for this field, apply it. Otherwise, returned nil."
(yas/eval-string transform)))) (yas/eval-string transform))))
transformed)) transformed))
(defsubst yas/replace-all (from to) (defsubst yas/replace-all (from to)
"Replace all occurance from FROM to TO." "Replace all occurance from FROM to TO."
(goto-char (point-min)) (goto-char (point-min))
@ -981,6 +1092,16 @@ inserted first."
#'(lambda (s1 s2) #'(lambda (s1 s2)
(<= (yas/snippet-id s2) (yas/snippet-id s1))))) (<= (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'."
(interactive)
(if yas/triggers-in-field
(let ((yas/fallback-behavior 'return-nil))
(unless (yas/expand)
(yas/next-field)))
(yas/next-field)))
(defun yas/next-field (&optional arg) (defun yas/next-field (&optional arg)
"Navigate to next field. If there's none, exit the snippet." "Navigate to next field. If there's none, exit the snippet."
(interactive) (interactive)
@ -1037,16 +1158,19 @@ up the snippet does not delete it!"
(yas/snippet-exit snippet) (yas/snippet-exit snippet)
(overlay-end (yas/snippet-control-overlay snippet))))) (overlay-end (yas/snippet-control-overlay snippet)))))
;;; Apropos markers-to-points: This can be useful for performance reasons, so ;;; Apropos markers-to-points:
;;; that an excessive number of live markers arent kept aroung in the ;;;
;;; `buffer-undo-list'. In `markers-to-points', the set-to-nil ;;; This was ground useful for performance
;;; markers can't simply be discarded and replaced with fresh ones in ;;; reasons, so that an excessive number of live markers arent kept
;;; `points-to-markers'. The original marker that was just set to nilhas to be ;;; aroung in the `buffer-undo-list'. However, in `markers-to-points',
;;; reused. ;;; the set-to-nil markers can't simply be discarded and replaced with
;;; fresh ones in `points-to-markers'. The original marker that was
;;; just set to nilhas to be reused.
;;; ;;;
;;; This shouldn't bring horrible problems with undo/redo, but it ;;; This shouldn't bring horrible problems with undo/redo, but it
;;; would be one of the the first thing I'd remove if I was debugging that... ;;; would be one of the the first thing I'd remove if I was debugging that...
;;; ;;;
(defun yas/markers-to-points (snippet) (defun yas/markers-to-points (snippet)
"Convert all markers in SNIPPET to a cons (POINT . MARKER) "Convert all markers in SNIPPET to a cons (POINT . MARKER)
where POINT is the original position of the marker and MARKER is where POINT is the original position of the marker and MARKER is
@ -1121,8 +1245,9 @@ exiting the snippet."
;; Push an action for snippet revival ;; Push an action for snippet revival
;; ;;
(push `(apply yas/snippet-revive ,yas/snippet-beg ,yas/snippet-end ,snippet) (when yas/snippet-revival
buffer-undo-list) (push `(apply yas/snippet-revive ,yas/snippet-beg ,yas/snippet-end ,snippet)
buffer-undo-list))
;; XXX: `yas/after-exit-snippet-hook' should be run with ;; XXX: `yas/after-exit-snippet-hook' should be run with
;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not
@ -1130,7 +1255,9 @@ exiting the snippet."
;; disappeared, which sometimes happens when the snippet's messed ;; disappeared, which sometimes happens when the snippet's messed
;; up... ;; up...
;; ;;
(unless no-hooks (run-hooks 'yas/after-exit-snippet-hook)))) (unless no-hooks (run-hooks 'yas/after-exit-snippet-hook)))
(message "[yas] snippet exited."))
(defun yas/check-commit-snippet () (defun yas/check-commit-snippet ()
"Checks if point exited the currently active field of the "Checks if point exited the currently active field of the
@ -1281,8 +1408,7 @@ progress."
(yas/clear-field field)) (yas/clear-field field))
(setf (yas/field-modified-p field) t)))))) (setf (yas/field-modified-p field) t))))))
;;; ;;; Apropos protection overlays:
;;; Apropos protection overlays:...
;;; ;;;
;;; These exist for nasty users who will try to delete parts of the ;;; These exist for nasty users who will try to delete parts of the
;;; snippet outside the active field. Actual protection happens in ;;; snippet outside the active field. Actual protection happens in
@ -1299,6 +1425,7 @@ progress."
;;; blocks all other million modification hooks. This presented some ;;; blocks all other million modification hooks. This presented some
;;; problems with stacked expansion. ;;; problems with stacked expansion.
;;; ;;;
(defun yas/make-move-field-protection-overlays (snippet field) (defun yas/make-move-field-protection-overlays (snippet field)
"Place protection overlays surrounding SNIPPET's FIELD. "Place protection overlays surrounding SNIPPET's FIELD.
@ -1337,7 +1464,6 @@ Move the overlays, or create them if they do not exit."
Functions in the `post-command-hook', for example Functions in the `post-command-hook', for example
`yas/post-command-handler' can check it and reset its value to nil. The variables value is the point where the violation originated") `yas/post-command-handler' can check it and reset its value to nil. The variables value is the point where the violation originated")
(defun yas/on-protection-overlay-modification (overlay after? beg end &optional length) (defun yas/on-protection-overlay-modification (overlay after? beg end &optional length)
"Signals a snippet violation, then issues error. "Signals a snippet violation, then issues error.
@ -1346,10 +1472,10 @@ The error should be ignored in `debug-ignored-errors'"
(yas/undo-in-progress))) (yas/undo-in-progress)))
(setq yas/protection-violation (point)) (setq yas/protection-violation (point))
(error "Exit the snippet first!")))) (error "Exit the snippet first!"))))
(add-to-list 'debug-ignored-errors "^Exit the snippet first!$") (add-to-list 'debug-ignored-errors "^Exit the snippet first!$")
;;; ;;; Apropos stacked expansion:
;;; Apropos stacked expansion:...
;;; ;;;
;;; the parent snippet does not run its fields modification hooks ;;; the parent snippet does not run its fields modification hooks
;;; (`yas/on-field-overlay-modification' and ;;; (`yas/on-field-overlay-modification' and
@ -1367,9 +1493,11 @@ The error should be ignored in `debug-ignored-errors'"
;;; maybe the correct implementation is to make the globals ;;; maybe the correct implementation is to make the globals
;;; `yas/active-field-overlay' and `yas/field-protection-overlays' be ;;; `yas/active-field-overlay' and `yas/field-protection-overlays' be
;;; snippet-local and be active even while the child snippet is ;;; snippet-local and be active even while the child snippet is
;;; running. This is a whole lot of hooks running, but they should ;;; running. This would mean a lot of overlay modification hooks
;;; account for all ;;; running, but if managed correctly (including overlay priorities)
;;; ;;; they should account for all situations...
;;;
(defun yas/expand-snippet (start end template) (defun yas/expand-snippet (start end template)
"Expand snippet at current point. Text between START and END "Expand snippet at current point. Text between START and END
will be deleted before inserting template." will be deleted before inserting template."
@ -1398,6 +1526,7 @@ will be deleted before inserting template."
;; ;;
(insert template) (insert template)
(setq yas/deleted-text key) (setq yas/deleted-text key)
(setq yas/selected-text (if mark-active key ""))
(setq snippet (yas/snippet-create (point-min) (point-max)))) (setq snippet (yas/snippet-create (point-min) (point-max))))
(error (error
(push (cons (point-min) (point-max)) buffer-undo-list) (push (cons (point-min) (point-max)) buffer-undo-list)
@ -1432,7 +1561,8 @@ will be deleted before inserting template."
(end (overlay-end (yas/snippet-control-overlay snippet)))) (end (overlay-end (yas/snippet-control-overlay snippet))))
(push (cons start end) buffer-undo-list) (push (cons start end) buffer-undo-list)
(push `(apply yas/take-care-of-redo ,start ,end ,snippet) (push `(apply yas/take-care-of-redo ,start ,end ,snippet)
buffer-undo-list)))) buffer-undo-list)))
(message "[yas] snippet expanded."))
(defun yas/take-care-of-redo (beg end snippet) (defun yas/take-care-of-redo (beg end snippet)
"Commits SNIPPET, which in turn pushes an undo action for "Commits SNIPPET, which in turn pushes an undo action for
@ -1515,13 +1645,18 @@ necessary fields, mirrors and exit points.
Meant to be called in a narrowed buffer, does various passes" Meant to be called in a narrowed buffer, does various passes"
(let ((parse-start (point))) (let ((parse-start (point)))
;; protect escapes ;; protect backquote escapes
;; ;;
(yas/protect-escapes) (yas/protect-escapes '(?`))
;; replace all backquoted expressions ;; replace all backquoted expressions
;; ;;
(goto-char parse-start) (goto-char parse-start)
(yas/replace-backquotes) (yas/replace-backquotes)
;; protect escapes again since previous stepds might have
;; generated more characters needing escapinge
;;
(goto-char parse-start)
(yas/protect-escapes)
;; parse fields ;; parse fields
;; ;;
(goto-char parse-start) (goto-char parse-start)
@ -1540,7 +1675,6 @@ Meant to be called in a narrowed buffer, does various passes"
(yas/restore-escapes) (yas/restore-escapes)
;; indent the best we can ;; indent the best we can
;; ;;
)) ))
@ -1548,12 +1682,12 @@ Meant to be called in a narrowed buffer, does various passes"
(defun yas/escape-string (escaped) (defun yas/escape-string (escaped)
(concat "YASESCAPE" (format "%d" escaped) "PROTECTGUARD")) (concat "YASESCAPE" (format "%d" escaped) "PROTECTGUARD"))
(defun yas/protect-escapes () (defun yas/protect-escapes (&optional escaped)
"Protect all escaped characters with their numeric ASCII value." "Protect all escaped characters with their numeric ASCII value."
(mapc #'(lambda (escaped) (mapc #'(lambda (escaped)
(yas/replace-all (concat "\\" (char-to-string escaped)) (yas/replace-all (concat "\\" (char-to-string escaped))
(yas/escape-string escaped))) (yas/escape-string escaped)))
yas/escaped-characters)) (or escaped yas/escaped-characters)))
(defun yas/restore-escapes () (defun yas/restore-escapes ()
"Restore all escaped characters from their numeric ASCII value." "Restore all escaped characters from their numeric ASCII value."
@ -1691,18 +1825,9 @@ When multiple expressions are found, only the last one counts."
(set-marker (yas/field-end field) (point))) (set-marker (yas/field-end field) (point)))
t)))) t))))
;; User convenience functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun yas/choose (&rest possibilities)
(ido-completing-read "Choose: " possibilities nil nil nil nil (car possibilities)))
(defun yas/verify (&rest possibilities)
(when (and yas/moving-away (notany #'(lambda (pos) (string= pos yas/text)) possibilities))
(error "hey don't move away just now")))
;; Debug functions. Use (or change) at will whenever needed. ;; Debug functions. Use (or change) at will whenever needed.
;; ;;
(defun yas/debug-some-vars () (defun yas/debug-some-vars ()
(interactive) (interactive)
@ -1733,7 +1858,6 @@ When multiple expressions are found, only the last one counts."
(dolist (undo-elem first-ten) (dolist (undo-elem first-ten)
(princ (format "%2s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 70)))))))) (princ (format "%2s: %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 70))))))))
(defun yas/exterminate-package () (defun yas/exterminate-package ()
(interactive) (interactive)
(yas/minor-mode -1) (yas/minor-mode -1)
@ -1761,7 +1885,6 @@ When multiple expressions are found, only the last one counts."
(when quiet (when quiet
(add-hook 'post-command-hook 'yas/debug-some-vars 't 'local)) (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local))
) )
(provide 'yasnippet) (provide 'yasnippet)