]> code.delx.au - gnu-emacs-elpa/blobdiff - yasnippet.el
Fixed Issue 136.
[gnu-emacs-elpa] / yasnippet.el
index c5771d84939884b8c549eb7de3e02e0a57e7213b..1dbf8c67283a412ea847f3d44323689656e20a15 100644 (file)
@@ -1,12 +1,13 @@
 ;;; 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.1b
+;; Version: 0.7.0
+;; Package-version: 0.7.0
 ;; X-URL: http://code.google.com/p/yasnippet/
-;; Keywords: snippet, textmate
+;; Keywords: convenience, emulation
 ;; URL: http://code.google.com/p/yasnippet/
 ;; EmacsWiki: YaSnippetMode
 
 ;;; Commentary:
 
 ;; Basic steps to setup:
-;; 
+;;
 ;;   1. In your .emacs file:
-;;       (add-to-list 'load-path "/dir/to/yasnippet.el")
+;;        (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)
+;;        (setq yas/snippet-dirs "~/.emacs/snippets")
+;;        (yas/load-directory yas/snippet-dirs)
 ;;   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
 ;;
 ;;   Interesting variables are:
 ;;
-;;       `yas/root-directory'
+;;       `yas/snippet-dirs'
+;;
+;;           The directory where user-created snippets are to be
+;;           stored. Can also be a list of directories. In that case,
+;;           when used for bulk (re)loading of snippets (at startup or
+;;           via `yas/reload-all'), directories appearing earlier in
+;;           the list shadow other dir's snippets. Also, the first
+;;           directory is taken as the default for storing the user's
+;;           new snippets.
 ;;
-;;          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.
+;;           The deprecated `yas/root-directory' aliases this variable
+;;           for backward-compatibility.
 ;;
-;;       `yas/mode-symbol'
+;;       `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
 ;;           list of symbols) that correspond to subdirectories of
-;;           `yas/root-directory' and is used for deciding which
+;;           `yas/snippet-dirs' and is used for deciding which
 ;;           snippets to consider for the active buffer.
 ;;
+;;           Deprecated `yas/mode-symbol' aliases this variable for
+;;           backward-compatibility.
+;;
 ;;   Major commands are:
 ;;
 ;;       M-x yas/expand
 ;;
 ;;       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.
+;;           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
+;;           subdirectory of `yas/snippet-dirs', according to the
 ;;           active major mode (if it exists) like
 ;;           `find-file-other-window'.
 ;;
 ;;       M-x yas/new-snippet
 ;;
 ;;           Lets you create a new snippet file in the correct
-;;           subdirectory of `yas/root-directory', according to the
+;;           subdirectory of `yas/snippet-dirs', according to the
 ;;           active major mode.
 ;;
 ;;       M-x yas/load-snippet-buffer
 ;;
 ;;       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'.
+;;           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'.
+;;
+;;       M-x yas/describe-tables
+;;
+;;           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:
 ;;
 ;;       (require 'dropdown-list)
-;;       (setq 'yas/prompt-functions '(yas/dropdown-prompt
-;;                                    yas/ido-prompt
-;;                                    yas/completing-prompt))
+;;       (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/snippet-dirs' 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)
+(require 'help-mode)
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; User customizable variables
-;;
+\f
+;;; User customizable variables
 
 (defgroup yasnippet nil
   "Yet Another Snippet extension"
   :group 'editing)
 
-(defcustom yas/root-directory nil
-  "Root directory that stores the snippets for each major mode.
+;;;###autoload
+(defcustom yas/snippet-dirs nil
+  "Directory or list of snippet dirs for each major mode.
+
+The directory where user-created snippets are to be stored. Can
+also be a list of directories. In that case, when used for
+bulk (re)loading of snippets (at startup or via
+`yas/reload-all'), directories appearing earlier in the list
+shadow other dir's snippets. Also, the first directory is taken
+as the default for storing the user's new snippets."
+  :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)))))
+(defun yas/snippet-dirs ()
+  (if (listp yas/snippet-dirs) yas/snippet-dirs (list yas/snippet-dirs)))
+(defvaralias 'yas/root-directory 'yas/snippet-dirs)
 
-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'"
+(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.
 
-  :type '(string)
-  :group 'yasnippet)
+These functions are called with the following arguments:
 
-(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."
-  :type 'list
+- 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
@@ -167,9 +225,9 @@ bulk reloading of all snippets using `yas/reload-all'"
 
 The following values are possible:
 
-`fixed' Indent the snippet to the current column;
+`fixed' Indent the snippet to the current column;
 
-`auto' Indent each line of the snippet with `indent-according-to-mode'
+`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
@@ -197,33 +255,61 @@ Naturally this is only valid when `yas/indent-line' is `auto'"
 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"
+  :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'."
-  :type 'string
-  :group 'yasnippet)
+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.
 
-Can also be a list of keys.
-
 Value is a string that is converted to the internal Emacs key
-representation using `read-kbd-macro'."
-  :type 'string
-  :group 'yasnippet)
+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'."
-  :type 'string
-  :group 'yasnippet)
+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.
@@ -236,25 +322,23 @@ field"
 (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
+- `call-other-command' means try to temporarily disable YASnippet
     and call the next command bound to `yas/trigger-key'.
 
-`return-nil' means return do nothing.
+- nil or the symbol `return-nil' mean do nothing. (and
+  `yas/expand-returns' nil)
 
-An entry (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))
+- 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)
 
-(defcustom yas/choose-keys-first t
-  "If non-nil, prompts for key first, then for template if more than one.
+(defcustom yas/choose-keys-first nil
+  "If non-nil, prompt for snippet key first, then for template.
 
-Otherwise prompts for all possible templates 
+Otherwise prompts for all possible snippet names.
 
 This affects `yas/insert-snippet' and `yas/visit-snippet-file'."
   :type 'boolean
@@ -270,13 +354,21 @@ This affects `yas/insert-snippet', `yas/visit-snippet-file'"
   :type 'boolean
   :group 'yasnippet)
 
-(defcustom yas/use-menu t
+(defcustom yas/use-menu 'real-modes
   "Display a YASnippet menu in the menu bar.
 
-When non-nil, snippet templates will be listed under the menu
-\"Yasnippet\". If set to `abbreviate', only the current major-mode
-menu and the modes set in `yas/mode-symbol' are listed."
+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/extra-modes' 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)
 
@@ -300,9 +392,23 @@ 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")))
+  '((t (:inherit 'region)))
   "The face used to highlight the currently active field of a snippet"
   :group 'yasnippet)
 
@@ -311,25 +417,27 @@ An error string \"[yas] error\" is returned instead."
   "The face used for debugging some overlays normally hidden"
   :group 'yasnippet)
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; User semi-customizable variables
-;;
-
-(defvar yas/keymap (make-sparse-keymap)
-  "The keymap active while a snippet expansion is in progress.")
-
+\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))))
+                  (list keys))))
     (dolist (key keys)
       (define-key keymap (read-kbd-macro key) definition))))
 
-(yas/define-some-keys yas/next-field-key yas/keymap 'yas/next-field-or-maybe-expand)
-(yas/define-some-keys yas/prev-field-key yas/keymap 'yas/prev-field)
-(yas/define-some-keys yas/skip-and-clear-key yas/keymap 'yas/skip-and-clear-or-delete-char)
+(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_." "^ ")
+(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 \"_\":
@@ -356,69 +464,73 @@ Attention: These hooks are not run when exiting nested/stackd snippet expansion!
   "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))))
+  '(if (or (fourth (syntax-ppss))
+           (fifth (syntax-ppss)))
        '(require-snippet-condition . force-in-comment)
      t)
-  "Condition to yasnippet local to each buffer.
+  "Snippet expanding condition.
 
-The default value helps filtering out potential snippet
-expansions inside comments and string literals, unless the
-snippet itself contains a condition that returns the symbol
-`force-in-comment'.
-
-    * If yas/buffer-local-condition evaluate to nil, snippet
-      won't be expanded.
-
-    * If it evaluate to the a cons cell where the car is the
-      symbol `require-snippet-condition' and the cdr is a
-      symbol (let's call it \"requirement\"):
-       * If the snippet has no condition, then it won't be
-         expanded.
-       * If the snippet has a condition but it evaluates to nil or
-         error occured during evaluation, it won't be expanded.
-       * If the snippet has a condition that evaluate to
-         non-nil (let's call it \"result\"):
-          * If \"requirement\" is t, the snippet is ready to be
-            expanded.
-          * If \"requirement\" is eq to \"result\", the snippet is ready
-            to be expanded.
-          * Otherwise the snippet won't be expanded.
-
-    * If it evaluates to `always', snippet is unconditionally
-      expanded.
-
-    * If it evaluates to other non-nil value:
-       * If the snippet has no condition, or has a condition that
-         evaluate to non-nil, it is ready to be expanded.
-       * Otherwise, it won't be expanded.
-
-Here's an example:
+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))))")
-(make-variable-buffer-local 'yas/buffer-local-condition)
+                       t))))
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Internal variables
-;; 
+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'.")
 
-(defvar yas/version "0.6.0b")
+\f
+;;; Internal variables
 
-(defvar yas/snippet-tables (make-hash-table)
-  "A hash table of snippet tables corresponding to each major mode.")
+(defvar yas/version "0.7.0")
 
 (defvar yas/menu-table (make-hash-table)
-  "A hash table of menus of corresponding major mode.")
+  "A hash table of MAJOR-MODE symbols to menu keymaps.")
+
+(defun teste ()
+  (interactive)
+  (message "AHAHA!"))
 
 (defvar yas/known-modes
   '(ruby-mode rst-mode markdown-mode)
@@ -441,7 +553,7 @@ Here's an example:
   "A regexp to recognize a \"`lisp-expression`\" expression." )
 
 (defconst yas/transform-mirror-regexp
-  "${\\(?:\\([0-9]+\\):\\)?$\\([^}]*\\)"
+  "${\\(?:\\([0-9]+\\):\\)?$\\([ \t\n]*([^}]*\\)"
   "A regexp to *almost* recognize a mirror with a transform.")
 
 (defconst yas/simple-mirror-regexp
@@ -456,36 +568,198 @@ Here's an example:
     (incf yas/snippet-id-seed)
     id))
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Minor mode stuff
-;;
-;; TODO: XXX: This is somehow needed in Carbon Emacs for MacOSX 
-(defvar last-buffer-undo-list nil)
+\f
+;;; Minor mode stuff
 
-(defvar yas/minor-mode-map (make-sparse-keymap)
-  "The keymap used when function `yas/minor-mode' is active.")
-
-(defvar yas/minor-mode-menu (make-sparse-keymap)
-  "Holds the YASnippet menu. For use with `easy-menu-define'.")
-
-(defun yas/init-keymap-and-menu ()
-  (easy-menu-define yas/minor-mode-menu
-    yas/minor-mode-map
-    "Menu used when YAS/minor-mode is active."
-    (cons "YASnippet"
-         (mapcar #'(lambda (ent)
-                     (when (third ent)
-                       (define-key yas/minor-mode-map (third ent) (second ent)))
-                     (vector (first ent) (second ent) t))
-                 (list (list "--")
-                       (list "Expand trigger" 'yas/expand (when yas/trigger-key (read-kbd-macro yas/trigger-key)))
-                       (list "Insert at point..." 'yas/insert-snippet "\C-c&\C-s")
-                       (list "Visit snippet file..." 'yas/visit-snippet-file "\C-c&\C-v")
-                       (list "Find snippets..." 'yas/find-snippets "\C-c&\C-f")
-                       (list "About" 'yas/about)
-                       (list "Reload-all-snippets" 'yas/reload-all)
-                       (list "Load snippets..." 'yas/load-directory))))))
+;; 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)))
+
+(defvar yas/tables (make-hash-table)
+  "A hash table of MAJOR-MODE symbols to `yas/table' objects.")
+
+(defvar yas/direct-keymaps (list)
+  "Keymap alist supporting direct snippet keybindings.
+
+This variable is is placed `emulation-mode-map-alists'.
+
+Its elements looks like (TABLE-NAME . KEYMAP) and are
+calculated when loading snippets. TABLE-NAME is a variable
+set buffer-locally when entering `yas/minor-mode'. KEYMAP binds
+all defined direct keybindings to the command
+`yas/expand-from-keymap', which acts similarly to `yas/expand'")
+
+(defun yas/direct-keymaps-reload ()
+  "Force reload the direct keybinding for active snippet tables."
+  (interactive)
+  (setq yas/direct-keymaps nil)
+  (maphash #'(lambda (name table)
+               (mapc #'(lambda (table)
+                         (push (cons (intern (format "yas//direct-%s" name))
+                                     (yas/table-direct-keymap table))
+                               yas/direct-keymaps))
+                     (cons table (yas/table-get-all-parents table))))
+           yas/tables))
+
+(defun yas/direct-keymaps-set-vars ()
+  (let ((modes-to-activate (list major-mode))
+        (mode major-mode))
+    (while (setq mode (get mode 'derived-mode-parent))
+      (push mode modes-to-activate))
+    (dolist (mode (yas/extra-modes))
+      (push mode modes-to-activate))
+    (dolist (mode modes-to-activate)
+      (let ((name (intern (format "yas//direct-%s" mode))))
+        (set-default name nil)
+        (set (make-local-variable name) t)))))
+
+(defvar yas/minor-mode-hook nil
+  "Hook run when yas/minor-mode is turned on")
+
+;;;###autoload
 (define-minor-mode yas/minor-mode
   "Toggle YASnippet mode.
 
@@ -504,19 +778,53 @@ Key bindings:
   ;; The indicator for the mode line.
   " yas"
   :group 'yasnippet
-  (when yas/minor-mode
-    ;; when turning on theminor mode, re-read the `yas/trigger-key'
-    ;; if a `yas/minor-mode-map' is already built. Else, call
-    ;; `yas/init-keymap-and-menu' to build it
-    (if (and (cdr yas/minor-mode-map)
-            yas/trigger-key)
-       (define-key yas/minor-mode-map (read-kbd-macro yas/trigger-key) 'yas/expand) 
-      (yas/init-keymap-and-menu))))
+  (cond (yas/minor-mode
+         ;; Reload the trigger key
+         ;;
+         (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/snippet-dirs)
+                     (> (hash-table-count yas/tables) 0))
+           (yas/reload-all))
+         ;; 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
+         ;; index `yas/direct-keymaps'.
+         ;;
+         (add-hook 'emulation-mode-map-alists 'yas/direct-keymaps)
+         (add-hook 'yas/minor-mode-hook 'yas/direct-keymaps-set-vars-runonce 'append))
+        (t
+         ;; Uninstall the direct keymaps.
+         ;;
+         (remove-hook 'emulation-mode-map-alists 'yas/direct-keymaps))))
+
+(defun yas/direct-keymaps-set-vars-runonce ()
+  (yas/direct-keymaps-set-vars)
+  (remove-hook 'yas/minor-mode-hook 'yas/direct-keymaps-set-vars-runonce))
+
+(defvar yas/dont-activate #'(lambda ()
+                              (and yas/snippet-dirs
+                                   (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."
+  "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)
-  (yas/minor-mode 1))
+  (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."
@@ -524,34 +832,50 @@ Key bindings:
   (yas/minor-mode -1))
 
 (define-globalized-minor-mode yas/global-mode yas/minor-mode yas/minor-mode-on
-  :group 'yasnippet)
+  :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)))))
-
-(defvar snippet-mode-map (make-sparse-keymap))
-(define-key snippet-mode-map "\C-c\C-c" 'yas/load-snippet-buffer)
-(define-key snippet-mode-map "\C-c\C-t" 'yas/tryout-snippet)
-
-
-(define-derived-mode snippet-mode text-mode "YASnippet"
+          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))
@@ -559,34 +883,214 @@ Key bindings:
   (use-local-map snippet-mode-map))
 
 
+\f
+;;; Internal structs for template management
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Internal structs for template management
-;; 
-
-(defstruct (yas/template (:constructor yas/make-template
-                                       (content name condition env file)))
+(defstruct (yas/template (:constructor yas/make-blank-template))
   "A template for a snippet."
+  table
+  key
   content
   name
   condition
-  env
-  file)
+  expand-env
+  file
+  keybinding
+  uuid
+  menu-binding-pair
+  group      ;; as dictated by the #group: directive or .yas-make-groups 
+  perm-group ;; as dictated by `yas/define-menu'
+  )
+
+(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))
 
-(defstruct (yas/snippet-table (:constructor yas/make-snippet-table (name)))
-  "A table to store snippets for a perticular mode."
-  name 
+(defstruct (yas/table (:constructor yas/make-snippet-table (name)))
+  "A table to store snippets for a particular mode.
+
+Has the following fields:
+
+`yas/table-name'
+
+  A symbol name normally corresponding to a major mode, but can
+  also be a pseudo major-mode to be referenced in
+  `yas/extra-modes', for example.
+
+`yas/table-hash'
+
+  A hash table (KEY . NAMEHASH), known as the \"keyhash\". KEY is
+  a string or a vector, where the former is the snippet's trigger
+  and the latter means it's a direct keybinding. NAMEHASH is yet
+  another hash of (NAME . TEMPLATE) where NAME is the snippet's
+  name and TEMPLATE is a `yas/template' object.
+
+`yas/table-parents'
+
+  A list of tables considered parents of this table: i.e. when
+  searching for expansions they are searched as well.
+
+`yas/table-direct-keymap'
+
+  A keymap for the snippets in this table that have direct
+  keybindings. This is kept in sync with the keyhash, i.e., all
+  the elements of the keyhash that are vectors appear here as
+  bindings to `yas/expand-from-keymap'.
+
+`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.
+"
+  name
   (hash (make-hash-table :test 'equal))
-  (parents nil))
+  (uuidhash (make-hash-table :test 'equal))
+  (parents nil)
+  (direct-keymap (make-sparse-keymap)))
+
+(defun yas/get-template-by-uuid (mode uuid)
+  "Find the snippet template in MODE by its UUID."
+  (let* ((table (gethash mode yas/tables mode)))
+    (when table
+      (gethash uuid (yas/table-uuidhash table)))))
 
-(defun yas/template-condition-predicate (condition)
+;; Apropos storing/updating, this works with two steps:
+;;
+;; 1. `yas/remove-template-by-uuid' to remove any existing mappings by
+;;    snippet uuid
+;;
+;; 2. `yas/add-template' to add the mappings again:
+;;
+;;    Create or index the entry in TABLES's `yas/table-hash'
+;;    linking KEY to a namehash. That namehash links NAME to
+;;    TEMPLATE, and is also created a new namehash inside that
+;;    entry.
+;;
+(defun yas/remove-template-by-uuid (table uuid)
+  "Remove from TABLE a template identified by UUID."
+  (let ((template (gethash uuid (yas/table-uuidhash table))))
+    (when template
+      (let* ((name                (yas/template-name template))
+             (empty-keys          nil))
+        ;; Remove the name from each of the targeted namehashes
+        ;;
+        (maphash #'(lambda (k v)
+                     (let ((template (gethash name v)))
+                       (when (and template
+                                  (eq uuid (yas/template-uuid template)))
+                         (remhash name v)
+                         (when (zerop (hash-table-count v))
+                           (push k empty-keys)))))
+                 (yas/table-hash table))
+        ;; Remove the namehashed themselves if they've become empty
+        ;;
+        (dolist (key empty-keys)
+          (remhash key (yas/table-hash table)))
+
+        ;; Finally, remove the uuid from the uuidhash
+        ;;
+        (remhash uuid (yas/table-uuidhash table))))))
+
+
+(defun yas/add-template (table template)
+  "Store in TABLE the snippet template TEMPLATE.
+
+KEY can be a string (trigger key) of a vector (direct
+keybinding)."
+  (let ((name (yas/template-name template))
+        (key (yas/template-key template))
+        (keybinding (yas/template-keybinding template))
+        (menu-binding (car (yas/template-menu-binding-pair template))))
+    (dolist (k (remove nil (list key keybinding)))
+      (puthash name
+               template
+               (or (gethash k
+                            (yas/table-hash table))
+                   (puthash k
+                            (make-hash-table :test 'equal)
+                            (yas/table-hash table))))
+      (when (vectorp k)
+        (define-key (yas/table-direct-keymap table) k 'yas/expand-from-keymap)))
+
+    (when menu-binding
+      (setf (getf (cdr menu-binding) :keys)
+            (or (and keybinding (key-description keybinding))
+                (and key (concat key yas/trigger-symbol))))
+      (setcar (cdr menu-binding)
+              name))
+
+    (puthash (yas/template-uuid template) template (yas/table-uuidhash table))))
+
+(defun yas/update-template (snippet-table template)
+  "Add or update TEMPLATE in SNIPPET-TABLE.
+
+Also takes care of adding and updaring to the associated menu."
+  ;; Remove from table by uuid
+  ;;
+  (yas/remove-template-by-uuid snippet-table (yas/template-uuid template))
+  ;; Add to table again
+  ;;
+  (yas/add-template snippet-table template)
+  ;; Take care of the menu
+  ;;
+  (let ((keymap (yas/menu-keymap-get-create snippet-table))
+        (group (yas/template-group template)))
+    (when (and yas/use-menu
+               keymap
+               (not (cdr (yas/template-menu-binding-pair template))))
+      ;; Remove from menu keymap
+      ;;
+      (yas/delete-from-keymap keymap (yas/template-uuid template))
+
+      ;; Add necessary subgroups as necessary.
+      ;; 
+      (dolist (subgroup group)
+        (let ((subgroup-keymap (lookup-key keymap (vector (make-symbol subgroup)))))
+          (unless (and subgroup-keymap
+                       (keymapp subgroup-keymap))
+            (setq subgroup-keymap (make-sparse-keymap))
+            (define-key keymap (vector (make-symbol subgroup))
+              `(menu-item ,subgroup ,subgroup-keymap)))
+            (setq keymap subgroup-keymap)))
+      
+      ;; Add this entry to the keymap
+      ;; 
+      (let ((menu-binding-pair (yas/snippet-menu-binding-pair-get-create template)))
+        (define-key keymap (vector (make-symbol (yas/template-uuid template))) (car menu-binding-pair))))))
+
+(defun yas/fetch (table key)
+  "Fetch templates in TABLE by KEY.
+
+Return a list of cons (NAME . TEMPLATE) where NAME is a
+string and TEMPLATE is a `yas/template' structure."
+  (let* ((keyhash (yas/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)))))
+
+\f
+;;; 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"
+             (message (format "[yas] error in condition evaluation: %s"
                               (error-message-string err)))
              nil))))
 
@@ -594,64 +1098,99 @@ Key bindings:
 (defun yas/filter-templates-by-condition (templates)
   "Filter the templates using the applicable condition.
 
-TEMPLATES is a list of cons (KEY . TEMPLATE) where KEY is a
+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
+        templates
       (remove-if-not #'(lambda (pair)
-                        (let* ((condition (yas/template-condition (cdr pair)))
-                               (result (or (null condition)
-                                           (yas/template-condition-predicate condition))))
-                          (cond ((eq requirement t)
-                                 result)
-                                (t
-                                 (eq requirement result)))))
-                    templates))))
-
-(defun yas/snippet-table-fetch (table key)
-  "Fetch a snippet binding to KEY from TABLE."
-  (when table
-    (yas/filter-templates-by-condition (gethash key (yas/snippet-table-hash table)))))
+                         (yas/template-can-expand-p
+                          (yas/template-condition (cdr pair)) requirement))
+                     templates))))
 
-(defun yas/snippet-table-get-all-parents (table)
-  (let ((parents (yas/snippet-table-parents table))) 
+(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 requirement)
+  "Evaluates CONDITION and REQUIREMENT and returns a boolean"
+  (let* ((result (or (null condition)
+                     (yas/eval-condition condition))))
+    (cond ((eq requirement t)
+           result)
+          (t
+           (eq requirement result)))))
+
+(defun yas/table-get-all-parents (table)
+  "Returns a list of all parent tables of TABLE"
+  (let ((parents (yas/table-parents table)))
     (when parents
-      (append parents
-             (mapcan #'yas/snippet-table-get-all-parents parents)))))
+      (append (copy-list parents)
+              (mapcan #'yas/table-get-all-parents parents)))))
 
-(defun yas/snippet-table-templates (table)
+(defun yas/table-templates (table)
   (when table
-    (let ((acc))
-      (maphash #'(lambda (key templates)
-                  (setq acc (append acc templates)))
-              (yas/snippet-table-hash table))
+    (let ((acc (list)))
+      (maphash #'(lambda (key namehash)
+                   (maphash #'(lambda (name template)
+                                (push (cons name template) acc))
+                            namehash))
+               (yas/table-hash table))
       (yas/filter-templates-by-condition acc))))
 
-(defun yas/snippet-table-all-keys (table)
+(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/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))
+                   (when (yas/filter-templates-by-condition templates)
+                     (push key acc)))
+               (yas/table-hash table))
       acc)))
 
-(defun yas/snippet-table-store (table full-key key template)
-  "Store a snippet template in the TABLE."
-  (puthash key
-           (yas/modify-alist (gethash key
-                                      (yas/snippet-table-hash table))
-                             full-key
-                             template)
-           (yas/snippet-table-hash table)))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Internal functions
-;;
+\f
+;;; Internal functions
 
 (defun yas/real-mode? (mode)
   "Try to find out if MODE is a real mode. The MODE bound to
@@ -662,97 +1201,125 @@ a list of modes like this to help the judgement."
   (or (fboundp mode)
       (find mode yas/known-modes)))
 
-(defun yas/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."
+(defun yas/eval-lisp (form)
+  "Evaluate FORM 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)))))))))
+                  (condition-case err
+                      (save-excursion
+                        (save-restriction
+                          (save-match-data
+                            (widen)
+                            (let ((result (eval form)))
+                              (when result
+                                (format "%s" result))))))
+                    (error (if yas/good-grace
+                               (format "[yas] elisp error! %s" (error-message-string err))
+                             (error (format "[yas] elisp error: %s"
+                                            (error-message-string err)))))))))
     (when (and (consp retval)
-              (eq 'yas/exception (car 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/eval-lisp-no-saves (form)
+  (condition-case err
+      (eval form)
+    (error (if yas/good-grace
+               (format "[yas] elisp error! %s" (error-message-string err))
+             (error (format "[yas] elisp error: %s"
+                            (error-message-string err)))))))
+
+(defun yas/read-lisp (string &optional nil-on-error)
+  "Read STRING as a elisp expression and return it.
 
-(defun yas/snippet-table-get-create (mode)
+In case STRING in an invalid expression and NIL-ON-ERROR is nil,
+return an expression that when evaluated will issue an error."
+  (condition-case err
+      (read string)
+    (error (and (not nil-on-error)
+                `(error (error-message-string err))))))
+
+(defun yas/read-keybinding (keybinding)
+  "Read KEYBINDING as a snippet keybinding, return a vector."
+  (when (and keybinding
+             (not (string-match "keybinding" keybinding)))
+    (condition-case err
+        (let ((keybinding-string (or (and (string-match "\".*\"" keybinding)
+                                          (read keybinding))
+                                     ;; "KEY-DESC" with quotes is deprecated..., but supported
+                                     keybinding)))
+          (read-kbd-macro keybinding-string 'need-vector))
+      (error
+       (message "[yas] warning: keybinding \"%s\" invalid for snippet \"%s\" since %s."
+                keybinding name (error-message-string err))
+       nil))))
+
+(defvar yas/extra-modes nil
+  "If non-nil, also lookup snippets for this/these modes.
+
+Can be a symbol or a list of symbols.
+
+This variable probably makes more sense as buffer-local, so
+ensure your use `make-local-variable' when you set it.")
+(defun yas/extra-modes ()
+  (if (listp yas/extra-modes) yas/extra-modes (list yas/extra-modes)))
+(defvaralias 'yas/mode-symbol 'yas/extra-modes)
+
+(defun yas/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)))
+                        yas/tables)))
     (unless table
       (setq table (yas/make-snippet-table (symbol-name mode)))
-      (puthash mode table yas/snippet-tables))
+      (puthash mode table yas/tables)
+      (aput 'yas/direct-keymaps (intern (format "yas//direct-%s" mode))
+            (yas/table-direct-keymap table)))
     table))
 
 (defun yas/get-snippet-tables (&optional mode-symbol dont-search-parents)
   "Get snippet tables for current buffer.
 
-Return tables in this order: optional MODE-SYMBOL, then
-`yas/mode-symbol', then `major-mode' then, unless
+Return a list of 'yas/table' objects indexed by mode.
+
+The modes are tried in this order: optional MODE-SYMBOL, then
+`yas/extra-modes', then `major-mode' then, unless
 DONT-SEARCH-PARENTS is non-nil, the guessed parent mode of either
-MODE-SYMBOL or `major-mode'."
-  (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)))
+MODE-SYMBOL or `major-mode'.
 
-(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/snippet-table-fetch table (buffer-substring-no-properties start end))) 
-                   (yas/get-snippet-tables)))
-      (if templates
-          (setq done t)
-        (setq start end)))
-    (list templates
-          start
-          end)))
+Guessing is done by looking up the MODE-SYMBOL's
+`derived-mode-parent' property, see also `derived-mode-p'."
+  (let ((mode-tables
+         (remove nil
+                 (mapcar #'(lambda (mode)
+                             (gethash mode yas/tables))
+                 (remove nil (append (list mode-symbol)
+                                     (yas/extra-modes)
+                                     (list major-mode
+                                           (and (not dont-search-parents)
+                                                (get major-mode
+                                                     'derived-mode-parent)))))))))
+    (remove-duplicates 
+     (append mode-tables
+             (mapcan #'yas/table-get-all-parents mode-tables)))))
+
+(defun yas/menu-keymap-get-create (table)
+  "Get or create the main menu keymap correspondong to MODE.
+
+This may very well create a plethora of menu keymaps and arrange
+them in all `yas/menu-table'"
+  (let* ((mode (intern (yas/table-name table)))
+         (menu-keymap (or (gethash mode yas/menu-table)
+                          (puthash mode (make-sparse-keymap) yas/menu-table)))
+        (parents (yas/table-parents table)))
+    (mapc #'yas/menu-keymap-get-create parents)
+    (define-key yas/minor-mode-menu (vector mode)
+        `(menu-item ,(symbol-name mode) ,menu-keymap
+                    :visible (yas/show-menu-p ',mode)))
+    menu-keymap))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Template-related and snippet loading functions
@@ -763,63 +1330,103 @@ the template of a snippet in the current snippet-table."
 Optional FILE is the absolute file name of the file being
 parsed.
 
+Optional GROUP is the group where the template is to go,
+otherwise we attempt to calculate it from FILE.
+
 Return a snippet-definition, i.e. a list
 
- (KEY TEMPLATE NAME CONDITION GROUP ENV)
+ (KEY TEMPLATE NAME CONDITION GROUP VARS FILE KEYBINDING UUID)
 
-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:
+If the buffer contains a line of \"# --\" then the contents above
+this line are ignored. Directives can set most of these with the syntax:
 
-#name : value
+# directive-name : directive-value
 
-Here's a list of currently recognized variables:
+Here's a list of currently recognized directives:
 
+ * type
  * name
  * contributor
  * condition
- * key
  * group
- * env
-
-#name: #include \"...\"
-# --
-#include \"$1\""
+ * key
+ * expand-env
+ * binding
+ * uuid"
   (goto-char (point-min))
-  (let* ((name (and file (file-name-nondirectory file)))
-        (key name)
-        template
-        bound
-        condition
-        group
-        env)
+  (let* ((type 'snippet)
+         (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
+         uuid)
     (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 (read (match-string-no-properties 2))))
-                (when (string= "group" (match-string-no-properties 1))
-                  (setq group (match-string-no-properties 2)))
-                (when (string= "env" (match-string-no-properties 1))
-                  (setq env (match-string-no-properties 2)))
-                (when (string= "key" (match-string-no-properties 1))
-                  (setq key (match-string-no-properties 2)))))
+        (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= "uuid" (match-string-no-properties 1))
+                   (setq uuid (match-string-no-properties 2)))
+                 (when (string= "type" (match-string-no-properties 1))
+                   (setq type (if (string= "command" (match-string-no-properties 2))
+                                  'command
+                                'snippet)))
+                 (when (string= "key" (match-string-no-properties 1))
+                   (setq key (match-string-no-properties 2)))
+                 (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 (yas/read-lisp (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 (yas/read-lisp (match-string-no-properties 2)
+                                                   'nil-on-error)))
+                 (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 env file)))
+            (buffer-substring-no-properties (point-min) (point-max))))
+    (when (eq type 'command)
+      (setq template (yas/read-lisp (concat "(progn" template ")"))))
+    (when group
+      (setq group (split-string group "\\.")))
+    (list key template name condition group expand-env file binding uuid)))
+
+(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/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 "~$"
+                   (string-match "^#.*#$"
+                                 (file-name-nondirectory file))
+                   (string-match "~$"
                                  (file-name-nondirectory file))
                    (if file?
                        (file-directory-p file)
@@ -827,143 +1434,210 @@ Here's a list of currently recognized variables:
              (directory-files directory t)))
 
 (defun yas/make-menu-binding (template)
-  `(lambda () (interactive) (yas/expand-from-menu ,template)))
-
-(defun yas/expand-from-menu (template)
-  (let ((where (if mark-active
-                  (cons (region-beginning) (region-end))
-                (cons (point) (point)))))
-    (yas/expand-snippet (car where)
-                       (cdr where)
-                       (yas/template-content template))))
-
-(defun yas/modify-alist (alist key value)
-  "Modify ALIST to map KEY to VALUE. return the new alist."
-  (let ((pair (assoc key alist)))
-    (if (null pair)
-        (cons (cons key value)
-              alist)
-      (setcdr pair value)
-      alist)))
+  (let ((mode (intern (yas/table-name (yas/template-table template)))))
+    `(lambda () (interactive) (yas/expand-or-visit-from-menu ',mode ,(yas/template-uuid template)))))
+
+(defun yas/expand-or-visit-from-menu (mode uuid)
+  (let* ((table (yas/table-get-create mode))
+         (yas/current-template (and table
+                                    (gethash uuid (yas/table-uuidhash table)))))
+    (when yas/current-template
+      (if yas/visit-from-menu
+          (yas/visit-snippet-file-1 yas/current-template)
+        (let ((where (if mark-active
+                         (cons (region-beginning) (region-end))
+                       (cons (point) (point)))))
+          (yas/expand-snippet (yas/template-content yas/current-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)))
+    (setq templates
+          (sort templates #'(lambda (t1 t2)
+                              (< (length (yas/template-name t1))
+                                 (length (yas/template-name t2))))))
+    (if yas/x-pretty-prompt-templates
+        (yas/x-pretty-prompt-templates "Choose a snippet" templates)
+      (some #'(lambda (fn)
+                (funcall fn (or prompt "Choose a snippet: ")
+                         templates
+                         #'yas/template-name))
+            yas/prompt-functions))))
 
 (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)))
+              (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)))
+              (funcall fn (or prompt "Choose a snippet table: ")
+                       tables
+                       #'yas/table-name))
+          yas/prompt-functions)))
 
 (defun yas/x-prompt (prompt choices &optional display-fn)
+  "Display choices in a x-window prompt."
+  ;; FIXME: HACK: if we notice that one of the objects in choices is
+  ;; actually a `yas/template', defer to `yas/x-prompt-pretty-templates'
+  ;;
+  ;; This would be better implemented by passing CHOICES as a
+  ;; strucutred tree rather than a list. Modifications would go as far
+  ;; up as `yas/all-templates' I think.
+  ;;
   (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))))))
+    (let ((chosen
+           (let (menu d) ;; d for display
+             (dolist (c choices)
+               (setq d (or (and display-fn (funcall display-fn c))
+                           c))
+               (cond ((stringp d)
+                      (push (cons (concat "   " d) c) menu))
+                     ((listp d)
+                      (push (car d) menu))))
+             (setq menu (list prompt (push "title" menu)))
+             (x-popup-menu (if (fboundp 'posn-at-point)
+                               (let ((x-y (posn-x-y (posn-at-point (point)))))
+                                 (list (list (+ (car x-y) 10)
+                                             (+ (cdr x-y) 20))
+                                       (selected-window)))
+                             t)
+                           menu))))
+      (or chosen
+          (keyboard-quit)))))
+
+(defvar yas/x-pretty-prompt-templates nil
+  "If non-nil, attempt to prompt for templates like TextMate.")
+(defun yas/x-pretty-prompt-templates (prompt templates)
+  "Display TEMPLATES, grouping neatly by table name."
+  (let ((props (list))
+        menu
+        more-than-one-table
+        prefix)
+    (dolist (tl templates)
+      (push tl (getf props (intern (yas/table-name (yas/template-table tl))))))
+    (setq more-than-one-table (> (length props) 2))
+    (setq prefix (if more-than-one-table
+                     "   " ""))
+    (dolist (thing props)
+      (cond ((listp thing)
+             (setq menu (nconc (mapcar #'(lambda (tl)
+                                           (cons (concat prefix (yas/template-name tl))
+                                                 tl))
+                                       thing)
+                               menu)))
+            (more-than-one-table
+             (push (symbol-name thing) menu))))
+    (setq menu (nreverse menu))
+    (x-popup-menu (if (fboundp 'posn-at-point)
+                      (let ((x-y (posn-x-y (posn-at-point (point)))))
+                        (list (list (+ (car x-y) 10)
+                                    (+ (cdr x-y) 20))
+                              (selected-window)))
+                    t)
+                  (list prompt (push "title" menu)))))
 
 (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)))))
+             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)
-    (let* ((formatted-choices (or (and display-fn
-                                      (mapcar display-fn choices))
-                                 choices))
-          (chosen (and formatted-choices
-                       (nth (dropdown-list formatted-choices)
-                            choices))))
-      chosen)))
-
-(defun yas/completing-prompt (prompt choices &optional display-fn)
-  (let* ((formatted-choices (or (and display-fn
-                                    (mapcar display-fn choices))
-                               choices))
-        (chosen (and formatted-choices
-                     (completing-read prompt
-                                      formatted-choices
-                                      nil
-                                      'require-match
-                                      nil
-                                      nil))))
+    (let (formatted-choices
+          filtered-choices
+          chosen
+          d)
+      (dolist (choice choices)
+        (setq d (or (and display-fn (funcall display-fn choice))
+                      choice))
+        (when (stringp d)
+          (push d formatted-choices)
+          (push choice filtered-choices)))
+      (setq chosen (and formatted-choices
+                        (nth (dropdown-list formatted-choices)
+                             filtered-choices))))))
+
+(defun yas/completing-prompt (prompt choices &optional display-fn completion-fn)
+  (let (formatted-choices
+        filtered-choices
+        chosen
+        d
+        (completion-fn (or completion-fn
+                           #'completing-read)))
+    (dolist (choice choices)
+      (setq d (or (and display-fn (funcall display-fn choice))
+                    choice))
+      (when (stringp d)
+        (push d formatted-choices)
+        (push choice filtered-choices)))
+    (setq chosen (and formatted-choices
+                      (funcall completion-fn prompt
+                               formatted-choices
+                               nil
+                               'require-match
+                               nil
+                               nil)))
     (when chosen
-      (nth (position chosen formatted-choices :test #'string=) choices))))
+      (nth (position chosen formatted-choices :test #'string=) filtered-choices))))
 
 (defun yas/no-prompt (prompt choices &optional display-fn)
   (first choices))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Loading snippets from files
-;; 
-(defun yas/load-directory-1 (directory &optional parents no-hierarchy-parents)
+;;
+(defun yas/load-directory-1 (directory &optional mode-sym parents)
   "Recursively load snippet templates from DIRECTORY."
 
-  (let* ((major-mode-and-parents (yas/compute-major-mode-and-parents (concat directory "/dummy")
-                                                                    nil
-                                                                    no-hierarchy-parents))
-        (mode-sym (car major-mode-and-parents))
-        (parents (rest major-mode-and-parents))
-        (snippet-defs nil))
-    (with-temp-buffer
-      (dolist (file (yas/subdirs directory 'no-subdirs-just-files))
-        (when (file-readable-p file)
-          (insert-file-contents file nil nil nil t)
-          (push (yas/parse-template file)
-               snippet-defs))))
-    (yas/define-snippets mode-sym
-                         snippet-defs
-                         parents)
-    (dolist (subdir (yas/subdirs directory))
-      (yas/load-directory-1 subdir (list mode-sym)))))
+  ;; Load .yas-setup.el files wherever we find them
+  ;;
+  (let ((file (concat directory "/" ".yas-setup")))
+    (when (or (file-readable-p (concat file ".el"))
+              (file-readable-p (concat file ".elc")))
+      (load file)))
+
+  ;;
+  ;;
+  (unless (file-exists-p (concat directory "/" ".yas-skip"))
+    (let* ((major-mode-and-parents (if mode-sym
+                                       (cons mode-sym parents)
+                                     (yas/compute-major-mode-and-parents (concat directory
+                                                                                 "/dummy"))))
+           (yas/ignore-filenames-as-triggers
+            (or yas/ignore-filenames-as-triggers
+                (file-exists-p (concat directory "/"
+                                       ".yas-ignore-filenames-as-triggers"))))
+           (snippet-defs nil))
+      ;; load the snippet files
+      ;;
+      (with-temp-buffer
+        (dolist (file (yas/subdirs directory 'no-subdirs-just-files))
+          (when (file-readable-p file)
+            (insert-file-contents file nil nil nil t)
+            (push (yas/parse-template file)
+                  snippet-defs))))
+      (when snippet-defs
+        (yas/define-snippets (car major-mode-and-parents)
+                             snippet-defs
+                             (cdr major-mode-and-parents)))
+      ;; now recurse to a lower level
+      ;;
+      (dolist (subdir (yas/subdirs directory))
+        (yas/load-directory-1 subdir
+                              (car major-mode-and-parents)
+                              (cdr major-mode-and-parents))))))
 
 (defun yas/load-directory (directory)
   "Load snippet definition from a directory hierarchy.
@@ -975,47 +1649,59 @@ 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)
+  (unless yas/snippet-dirs
+    (setq yas/snippet-dirs directory))
   (dolist (dir (yas/subdirs directory))
-    (yas/load-directory-1 dir nil 'no-hierarchy-parents))
+    (yas/load-directory-1 dir))
   (when (interactive-p)
-    (message "done.")))
+    (message "[yas] Loaded snippets from %s." directory)))
 
-(defun yas/reload-all ()
-  "Reload all snippets and rebuild the YASnippet menu. "
+(defun yas/load-snippet-dirs ()
+  "Reload the directories listed in `yas/snippet-dirs' or
+   prompt the user to select one."
+  (if yas/snippet-dirs
+      (dolist (directory (reverse (yas/snippet-dirs)))
+        (yas/load-directory directory))
+    (call-interactively 'yas/load-directory)))
 
-  (interactive)
-  (let ((restore-global-mode nil)
-       (restore-minor-mode nil))
-    (setq yas/snippet-tables (make-hash-table))
+(defun yas/reload-all (&optional reset-root-directory)
+  "Reload all snippets and rebuild the YASnippet menu. "
+  (interactive "P")
+  ;; Turn off global modes and minor modes, save their state though
+  ;;
+  (let ((restore-global-mode (prog1 yas/global-mode
+                               (yas/global-mode -1)))
+        (restore-minor-mode (prog1 yas/minor-mode
+                              (yas/minor-mode -1))))
+    ;; Empty all snippet tables and all menu tables
+    ;;
+    (setq yas/tables (make-hash-table))
     (setq yas/menu-table (make-hash-table))
-    (setf (cdr yas/minor-mode-menu) nil)
-    (setf (cdr yas/minor-mode-map) nil)
-    (when yas/global-mode
-      (yas/global-mode -1)
-      (setq restore-global-mode t))
-
-    (when yas/minor-mode
-      (yas/minor-mode -1)
-      (setq restore-minor-mode t))
 
-    (yas/init-keymap-and-menu)
-
-    (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))
+    ;; Init the `yas/minor-mode-map', taking care not to break the
+    ;; menu....
+    ;;
+    (setf (cdr yas/minor-mode-map)
+          (cdr (yas/init-minor-keymap)))
 
+    (when reset-root-directory
+      (setq yas/snippet-dirs nil))
 
+    ;; Reload the directories listed in `yas/snippet-dirs' or prompt
+    ;; the user to select one.
+    ;;
+    (yas/load-snippet-dirs)
+    ;; Reload the direct keybindings
+    ;;
+    (yas/direct-keymaps-reload)
+    ;; Restore the mode configuration
+    ;;
     (when restore-minor-mode
       (yas/minor-mode 1))
-
     (when restore-global-mode
       (yas/global-mode 1))
 
-    (message "done.")))
+    (message "[yas] Reloading everything... Done.")))
 
 (defun yas/quote-string (string)
   "Escape and quote STRING.
@@ -1036,106 +1722,130 @@ foo\"bar\\! -> \"foo\\\"bar\\\\!\""
 (defun yas/compile-bundle
   (&optional yasnippet yasnippet-bundle snippet-roots code dropdown)
   "Compile snippets in SNIPPET-ROOTS to a single bundle file.
+
+YASNIPPET is the yasnippet.el file path.
+
+YASNIPPET-BUNDLE is the output file of the compile result.
+
 SNIPPET-ROOTS is a list of root directories that contains the
-snippets definition. YASNIPPET is the yasnippet.el file
-path. YASNIPPET-BUNDLE is the output file of the compile
-result. CODE is the code you would like to used to initialize
-yasnippet. Last optional argument DROPDOWN is the filename of the
-dropdown-list.el library.
+snippets definition.
 
-Here's the default value for all the parameters:
+CODE is the code to be placed at the end of the generated file
+and that can initialize the YASnippet bundle.
 
- (yas/compile-bundle \"yasnippet.el\"
-                     \"./yasnippet-bundle.el\"
-                     '(\"snippets\")
-                     \"(yas/initialize)\")
+Last optional argument DROPDOWN is the filename of the
+dropdown-list.el library.
 
-..
+Here's the default value for all the parameters:
 
+  (yas/compile-bundle \"yasnippet.el\"
+                      \"yasnippet-bundle.el\"
+                      \"snippets\")
+                      \"(yas/initialize-bundle)
+                        ### autoload
+                        (require 'yasnippet-bundle)`\"
+                      \"dropdown-list.el\")
 "
-  (when (null yasnippet)
-    (setq yasnippet "yasnippet.el"))
-  (when (null yasnippet-bundle)
-    (setq yasnippet-bundle "./yasnippet-bundle.el"))
-  (when (null snippet-roots)
-    (setq snippet-roots '("snippets")))
-  (when (null dropdown)
-    (setq dropdown "dropdown-list.el"))
-  (when (null code)
-    (setq code (concat "(yas/initialize-bundle)"
-                      "\n;;;###autoload" ; break through so that won't
-                      "(require 'yasnippet-bundle)"))) ; be treated as magic comment
-
-  (let ((dirs (or (and (listp snippet-roots) snippet-roots)
-                  (list snippet-roots)))
-        (bundle-buffer nil))
-    (with-temp-buffer
-      (setq bundle-buffer (current-buffer))
+  (interactive (concat "ffind the yasnippet.el file: \nFTarget bundle file: "
+                       "\nDSnippet directory to bundle: \nMExtra code? \nfdropdown-library: "))
+
+  (let* ((yasnippet (or yasnippet
+                        "yasnippet.el"))
+         (yasnippet-bundle (or yasnippet-bundle
+                               "./yasnippet-bundle.el"))
+         (snippet-roots (or snippet-roots
+                            "snippets"))
+         (dropdown (or dropdown
+                       "dropdown-list.el"))
+         (code (or (and code
+                        (condition-case err (read code) (error nil))
+                        code)
+                   (concat "(yas/initialize-bundle)"
+                           "\n;;;###autoload" ; break through so that won't
+                           "(require 'yasnippet-bundle)")))
+         (dirs (or (and (listp snippet-roots) snippet-roots)
+                   (list snippet-roots)))
+         (bundle-buffer nil))
+    (with-temp-file yasnippet-bundle
       (insert ";;; yasnippet-bundle.el --- "
               "Yet another snippet extension (Auto compiled bundle)\n")
       (insert-file-contents yasnippet)
       (goto-char (point-max))
       (insert "\n")
       (when dropdown
-       (insert-file-contents dropdown))
+        (insert-file-contents dropdown))
       (goto-char (point-max))
       (insert ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
       (insert ";;;;      Auto-generated code         ;;;;\n")
       (insert ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
       (insert "(defun yas/initialize-bundle ()\n"
-              "  \"Initialize YASnippet and load snippets in the bundle.\""
-              "  (yas/global-mode 1)\n")
+              "  \"Initialize YASnippet and load snippets in the bundle.\"")
       (flet ((yas/define-snippets
-             (mode snippets &optional parent-or-parents)
-             (with-current-buffer bundle-buffer
-               (insert ";;; snippets for " (symbol-name mode) "\n")
-               (insert "(yas/define-snippets '" (symbol-name mode) "\n")
-               (insert "'(\n")
-               (dolist (snippet snippets)
-                 (insert "  ("
-                         (yas/quote-string (car snippet))
-                         " "
-                         (yas/quote-string (nth 1 snippet))
-                         " "
-                         (if (nth 2 snippet)
-                             (yas/quote-string (nth 2 snippet))
-                           "nil")
-                         " "
-                         (if (nth 3 snippet)
-                             (format "'%s" (nth 3 snippet))
-                           "nil")
-                         " "
-                         (if (nth 4 snippet)
-                             (yas/quote-string (nth 4 snippet))
-                           "nil")
-                         ")\n"))
-               (insert "  )\n")
-               (insert (if parent-or-parents
-                           (format "'%s" parent-or-parents)
-                         "nil")
-                       ;; (if directory
-                       ;;     (concat "\"" directory "\"")
-                       ;;   "nil")
-                       ")\n\n"))))
+              (mode snippets &optional parent-or-parents)
+              (insert ";;; snippets for " (symbol-name mode) "\n")
+              (let ((literal-snippets (list)))
+                (dolist (snippet snippets)
+                  (let ((key                    (first   snippet))
+                        (template-content       (second  snippet))
+                        (name                   (third   snippet))
+                        (condition              (fourth  snippet))
+                        (group                  (fifth   snippet))
+                        (expand-env             (sixth   snippet))
+                        (file                   nil) ;; (seventh snippet)) ;; omit on purpose
+                        (binding                (eighth  snippet))
+                        (uuid                    (ninth   snippet)))
+                    (push `(,key
+                            ,template-content
+                            ,name
+                            ,condition
+                            ,group
+                            ,expand-env
+                            ,file
+                            ,binding
+                            ,uuid)
+                          literal-snippets)))
+                (insert (pp-to-string `(yas/define-snippets ',mode ',literal-snippets ',parent-or-parents)))
+                (insert "\n\n"))))
         (dolist (dir dirs)
           (dolist (subdir (yas/subdirs dir))
-            (yas/load-directory-1 subdir nil 'no-hierarchy-parents))))
-
+            (let ((file (concat subdir "/.yas-setup.el")))
+              (when (file-readable-p file)
+                (insert ";; Supporting elisp for subdir " (file-name-nondirectory subdir) "\n\n")
+                (goto-char (+ (point)
+                              (second (insert-file-contents file))))))
+            (yas/load-directory-1 subdir nil))))
+
+      (insert (pp-to-string `(yas/global-mode 1)))
       (insert ")\n\n" code "\n")
-      (insert "(provide '"
-              (file-name-nondirectory
-               (file-name-sans-extension
-                yasnippet-bundle))
-              ")\n")
+
+      ;; bundle-specific provide and value for yas/dont-activate
+      (let ((bundle-feature-name (file-name-nondirectory
+                                  (file-name-sans-extension
+                                   yasnippet-bundle))))
+        (insert (pp-to-string `(set-default 'yas/dont-activate
+                                            #'(lambda ()
+                                                (and (or yas/snippet-dirs
+                                                         (featurep ',(make-symbol bundle-feature-name)))
+                                                     (null (yas/get-snippet-tables)))))))
+        (insert (pp-to-string `(provide ',(make-symbol bundle-feature-name)))))
+
       (insert ";;; "
               (file-name-nondirectory yasnippet-bundle)
-              " ends here\n")
-      (setq buffer-file-name yasnippet-bundle)
-      (save-buffer))))
+              " ends here\n"))))
+
+(defun yas/compile-textmate-bundle ()
+  (interactive)
+  (yas/compile-bundle "yasnippet.el"
+                      "./yasnippet-textmate-bundle.el"
+                      "extras/imported/"
+                      (concat "(yas/initialize-bundle)"
+                              "\n;;;###autoload" ; break through so that won't
+                              "(require 'yasnippet-textmate-bundle)")
+                      "dropdown-list.el"))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Some user level functions
-;;; 
+;;;
 
 (defun yas/about ()
   (interactive)
@@ -1144,127 +1854,212 @@ Here's the default value for all the parameters:
                    ") -- pluskid <pluskid@gmail.com>/joaotavora <joaotavora@gmail.com>")))
 
 (defun yas/define-snippets (mode snippets &optional parent-mode)
-  "Define snippets for MODE.  SNIPPETS is a list of
-snippet definitions, each taking the following form:
+  "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.
 
- (KEY TEMPLATE NAME CONDITION GROUP)
+TEMPLATE might be a lisp form or a string, depending on whether
+this is a snippet or a snippet-command.
 
-NAME, CONDITION or GROUP may be omitted.
+CONDITION, EXPAND-ENV and KEYBINDING are lisp forms, they have
+been `yas/read-lisp'-ed and will eventually be
+`yas/eval-lisp'-ed.
 
-Optional PARENT-MODE can be used to specify the parent modes of
+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.
+
+Optional PARENT-MODE can be used to specify the parent tables of
 MODE. It can be a mode symbol of a list of mode symbols. It does
-not need to be a real mode.
-
-That is, when looking a snippet in MODE failed, it can refer to
-its parent modes."
-  (let ((snippet-table (yas/snippet-table-get-create mode))
-        (parent-tables (mapcar #'yas/snippet-table-get-create
-                              (if (listp parent-mode)
-                                  parent-mode
-                                (list parent-mode))))
-        (keymap (if yas/use-menu
-                    (yas/menu-keymap-get-create mode)
-                  nil)))
-    (when parent-tables
-      (setf (yas/snippet-table-parents snippet-table)
-            parent-tables)
-      (when yas/use-menu
-       (let ((parent-menu-syms-and-names
-              (if (listp parent-mode)
-                  (mapcar #'(lambda (sym)
-                              (cons sym (concat "parent mode - " (symbol-name sym))))
-                          parent-mode)
-                '((parent-mode . "parent mode")))))
-         (mapc #'(lambda (sym-and-name)
-                   (define-key keymap (vector (intern (replace-regexp-in-string " " "_" (cdr sym-and-name))))
-                     (list 'menu-item (cdr sym-and-name)
-                           (yas/menu-keymap-get-create (car sym-and-name)))))
-               (reverse parent-menu-syms-and-names)))))
-    (when (and yas/use-menu
-               (yas/real-mode? mode))
-      (define-key yas/minor-mode-menu (vector mode)
-        `(menu-item ,(symbol-name mode) ,keymap
-                   :visible (yas/show-menu-p ',mode))))
+not need to be a real mode."
+  ;; X) `snippet-table' is created or retrieved for MODE, same goes
+  ;;    for the list of snippet tables `parent-tables'.
+  ;;
+  (let ((snippet-table (yas/table-get-create mode))
+        (parent-tables (mapcar #'yas/table-get-create
+                               (if (listp parent-mode)
+                                   parent-mode
+                                 (list parent-mode))))
+        (template nil))
+    ;; X) Connect `snippet-table' with `parent-tables'.
+    ;;
+    ;; TODO: this should be a remove-duplicates of the concatenation
+    ;; of `snippet-table's existings parents with the new parents...
+    ;;
+    (dolist (parent parent-tables)
+      (unless (find parent (yas/table-parents snippet-table))
+        (push parent
+              (yas/table-parents snippet-table))))
+
+    ;; X) Now, iterate for evey snippet def list
+    ;;
     (dolist (snippet snippets)
-      (let* ((full-key (car snippet))
-             (key (file-name-sans-extension full-key))
-             (name (or (nth 2 snippet) (file-name-extension full-key)))
-             (condition (nth 3 snippet))
-             (group (nth 4 snippet))
-            (template (yas/make-template (nth 1 snippet)
-                                          (or name key)
-                                          condition
-                                         (nth 5 snippet)
-                                         (nth 6 snippet))))
-        (yas/snippet-table-store snippet-table
-                                 full-key
-                                 key
-                                 template)
-        (when yas/use-menu
-          (let ((group-keymap keymap))
-           ;; delete this entry from another group if already exists
-           ;; in some other group. An entry is considered as existing
-           ;; in another group if its name string-matches.
-           (yas/delete-from-keymap group-keymap name)
-  
-           ;; ... then add this entry to the correct group
-            (when (and (not (null group))
-                       (not (string= "" group)))
-              (dolist (subgroup (mapcar #'make-symbol
-                                        (split-string group "\\.")))
-                (let ((subgroup-keymap (lookup-key group-keymap
-                                                   (vector subgroup))))
-                  (when (null subgroup-keymap)
-                    (setq subgroup-keymap (make-sparse-keymap))
-                    (define-key group-keymap (vector subgroup)
-                      `(menu-item ,(symbol-name subgroup)
-                                  ,subgroup-keymap)))
-                  (setq group-keymap subgroup-keymap))))
-            (define-key group-keymap (vector (make-symbol full-key))
-              `(menu-item ,(yas/template-name template)
-                          ,(yas/make-menu-binding template)
-                          :keys ,(concat key yas/trigger-symbol)))))))))
+      (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 (or (car snippet)
+                  (unless yas/ignore-filenames-as-triggers
+                    (and file
+                         (file-name-sans-extension (file-name-nondirectory file))))))
+         (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/snippet-menu-binding-pair-get-create (template &optional type)
+  "Get TEMPLATE's menu binding or assign it a new one."
+  (or (yas/template-menu-binding-pair 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))
+                                ,(yas/make-menu-binding template)
+                                :keys ,nil)
+                    type)))))
 
 (defun yas/show-menu-p (mode)
-  (message "what")
-  (or (not (eq yas/use-menu 'abbreviate)) 
-      (find mode (cons major-mode
-                      (if (listp yas/mode-symbol)
-                          yas/mode-symbol
-                        (list yas/mode-symbol))))))
-
-(defun yas/delete-from-keymap (keymap name)
-  "Recursively delete items name NAME from KEYMAP and its submenus.
-
-Skip any submenus named \"parent mode\""
+  (cond ((eq yas/use-menu 'abbreviate)
+         (find mode
+               (mapcar #'(lambda (table)
+                           (intern (yas/table-name table)))
+                       (yas/get-snippet-tables))))
+        ((eq yas/use-menu 'real-modes)
+         (yas/real-mode? mode))
+        (t
+         t)))
+
+(defun yas/delete-from-keymap (keymap uuid)
+  "Recursively delete items with UUID from KEYMAP and its submenus."
+
+  ;; XXX: This used to skip any submenus named \"parent mode\"
+  ;; 
   ;; First of all, recursively enter submenus, i.e. the tree is
   ;; searched depth first so that stale submenus can be found in the
   ;; higher passes.
-  ;; 
-  (mapc #'(lambda (item)
-           (when (and (keymapp (fourth item))
-                      (stringp (third item))
-                      (not (string-match "parent mode" (third item))))
-             (yas/delete-from-keymap (fourth item) name)))
-       (rest keymap))
   ;;
-  (when (keymapp keymap)
-    (let ((pos-in-keymap))
-      (while (setq pos-in-keymap (position-if #'(lambda (item)
-                                                 (and (listp item)
-                                                      (or
-                                                       ;; the menu item we want to delete
-                                                       (and (eq 'menu-item (second item))
-                                                            (third item)
-                                                            (and (string= (third item) name)))
-                                                       ;; a stale subgroup
-                                                       (and (keymapp (fourth item))
-                                                            (not (and (stringp (third item))
-                                                                      (string-match "parent mode" (third item))))
-                                                            (null (rest (fourth item)))))))
-                                             keymap))
-       (setf (nthcdr pos-in-keymap keymap)
-             (nthcdr (+ 1 pos-in-keymap) keymap))))))
+  (mapc #'(lambda (item)
+            (when (and (listp (cdr item))
+                       (keymapp (third (cdr item))))
+              (yas/delete-from-keymap (third (cdr item)) uuid)))
+        (rest keymap))
+  ;; Set the uuid entry to nil
+  ;; 
+  (define-key keymap (vector (make-symbol uuid)) nil)
+  ;; Destructively modify keymap
+  ;; 
+  (setcdr keymap (delete-if #'(lambda (item)
+                                (or (null (cdr item))
+                                    (and (keymapp (third (cdr item)))
+                                         (null (cdr (third (cdr item)))))))
+                            (rest keymap))))
+
+(defun yas/define-menu (mode menu omit-items)
+  "Define a snippet menu for MODE according to MENU, ommitting 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
+  permanent, i.e. it will never move 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
+  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.
+"
+  (let* ((table (yas/table-get-create mode))
+         (hash (yas/table-uuidhash table)))
+    (yas/define-menu-1 table
+                       (yas/menu-keymap-get-create table)
+                       menu
+                       hash)
+    (dolist (uuid omit-items)
+      (let ((template (or (gethash uuid hash)
+                          (yas/populate-template (puthash uuid
+                                                          (yas/make-blank-template)
+                                                          hash)
+                                                 :table table
+                                                 :uuid uuid))))
+        (setf (yas/template-menu-binding-pair template) (cons nil :none))))))
+
+(defun yas/define-menu-1 (table keymap menu uuidhash &optional group-list)
+  (dolist (e (reverse menu))
+    (cond ((eq (first e) 'yas/item)
+           (let ((template (or (gethash (second e) uuidhash)
+                               (yas/populate-template (puthash (second e)
+                                                               (yas/make-blank-template)
+                                                               uuidhash)
+                                                      :table table
+                                                      :perm-group group-list
+                                                      :uuid (second e)))))
+             (define-key keymap (vector (make-symbol (second e)))
+               (car (yas/snippet-menu-binding-pair-get-create template :stay)))))
+          ((eq (first e) 'yas/submenu)
+           (let ((subkeymap (make-sparse-keymap)))
+             (define-key keymap (vector (make-symbol(second e)))
+               `(menu-item ,(second e) ,subkeymap))
+             (yas/define-menu-1 table
+                                subkeymap
+                                (third e)
+                                uuidhash
+                                (append group-list (list (second e))))))
+          ((eq (first e) 'yas/separator)
+           (define-key keymap (vector (gensym))
+             '(menu-item "----")))
+          (t
+           (message "[yas] don't know anything about menu entry %s" (first e))))))
 
 (defun yas/define (mode key template &optional name condition group)
   "Define a snippet.  Expanding KEY into TEMPLATE.
@@ -1285,66 +2080,132 @@ will only be expanded when the condition evaluated to non-nil."
     (undo 1)
     nil))
 
-(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 (yas/template-condition-predicate
-                           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/expand ()
+
+;;; Apropos condition-cache:
+;;;
+;;;
+;;;
+;;;
+(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
+expansion conditions.
+
+It doesn't make any sense to call FUNC programatically."
+  `(defun ,func () ,(if (and doc
+                             (stringp doc))
+                        (concat doc
+"\n\nFor use in snippets' conditions. Within each
+snippet-expansion routine like `yas/expand', computes actual
+value for the first time then always returns a cached value.")
+                      (setq body (cons doc body))
+                      nil)
+     (let ((timestamp-and-value (get ',func 'yas/condition-cache)))
+       (if (equal (car timestamp-and-value) yas/condition-cache-timestamp)
+           (cdr timestamp-and-value)
+         (let ((new-value (progn
+                            ,@body
+                            )))
+           (put ',func 'yas/condition-cache (cons yas/condition-cache-timestamp new-value))
+           new-value)))))
+
+(defalias 'yas/expand 'yas/expand-from-trigger-key)
+(defun yas/expand-from-trigger-key (&optional field)
   "Expand a snippet before point.
 
 If no snippet expansion is possible, fall back to the behaviour
-defined in `yas/fallback-behavior'"
-  (interactive)
-  (yas/expand-1))
+defined in `yas/fallback-behavior'.
 
-(defun yas/expand-1 (&optional field)
-  "Actually fo the work for `yas/expand'"
+Optional argument FIELD is for non-interactive use and is an
+object satisfying `yas/field-p' to restrict the expansion to."
+  (interactive)
+  (setq yas/condition-cache-timestamp (current-time))
   (multiple-value-bind (templates start end) (if field
-                                                (save-restriction
-                                                  (narrow-to-region (yas/field-start field) (yas/field-end field))
-                                                  (yas/current-key))
-                                              (yas/current-key))
+                                                 (save-restriction
+                                                   (narrow-to-region (yas/field-start field)
+                                                                     (yas/field-end field))
+                                                   (yas/current-key))
+                                               (yas/current-key))
     (if templates
-       (let ((template (or (and (rest templates) ;; more than one
-                                (yas/prompt-for-template (mapcar #'cdr templates)))
-                           (cdar templates))))
-         (when template
-           (yas/expand-snippet start
-                               end
-                               (yas/template-content template)
-                               (yas/template-env template))))
-      (cond ((eq yas/fallback-behavior 'return-nil)
-            ;; return nil
-            nil)
-           ((eq yas/fallback-behavior 'call-other-command)
-            (let* ((yas/minor-mode nil)
-                   (command (key-binding (read-kbd-macro yas/trigger-key))))
-              (when (commandp command)
-                (setq this-command command)
-                (call-interactively command))))
-           ((and (listp yas/fallback-behavior)
-                 (cdr yas/fallback-behavior)
-                 (eq 'apply (car yas/fallback-behavior)))
-            (if (cddr yas/fallback-behavior)
-                (apply (cadr yas/fallback-behavior)
-                       (cddr yas/fallback-behavior))
-              (when (commandp (cadr yas/fallback-behavior))
-                (setq this-command (cadr yas/fallback-behavior))
-                (call-interactively (cadr yas/fallback-behavior)))))
-           (t
-            ;; also return nil if all the other fallbacks have failed
-            nil)))))
+        (yas/expand-or-prompt-for-template templates start end)
+      (yas/fallback 'trigger-key))))
+
+(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* ((vec (this-command-keys-vector))
+         (templates (mapcan #'(lambda (table)
+                                (yas/fetch table vec))
+                            (yas/get-snippet-tables))))
+    (if templates
+        (yas/expand-or-prompt-for-template templates)
+      (let ((yas/fallback-behavior 'call-other-command))
+        (yas/fallback)))))
+
+(defun yas/expand-or-prompt-for-template (templates &optional start end)
+  "Expand one of TEMPLATES from START to END.
+
+Prompt the user if TEMPLATES has more than one element, else
+expand immediately. Common gateway for
+`yas/expand-from-trigger-key' and `yas/expand-from-keymap'."
+  (let ((yas/current-template (or (and (rest templates) ;; more than one
+                                       (yas/prompt-for-template (mapcar #'cdr templates)))
+                                  (cdar templates))))
+    (when yas/current-template
+      (yas/expand-snippet (yas/template-content yas/current-template)
+                          start
+                          end
+                          (yas/template-expand-env yas/current-template)))))
+
+(defun yas/fallback (&optional from-trigger-key-p)
+  "Fallback after expansion has failed.
+
+Common gateway for `yas/expand-from-trigger-key' and
+`yas/expand-from-keymap'."
+  (cond ((eq yas/fallback-behavior 'return-nil)
+         ;; 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))))
+        ((and (listp yas/fallback-behavior)
+              (cdr yas/fallback-behavior)
+              (eq 'apply (car yas/fallback-behavior)))
+         (if (cddr yas/fallback-behavior)
+             (apply (cadr yas/fallback-behavior)
+                    (cddr yas/fallback-behavior))
+           (when (commandp (cadr yas/fallback-behavior))
+             (setq this-command (cadr yas/fallback-behavior))
+             (call-interactively (cadr yas/fallback-behavior)))))
+        (t
+         ;; also return nil if all the other fallbacks have failed
+         nil)))
+
+
+\f
+;;; Snippet development
 
 (defun yas/all-templates (tables)
   "Return all snippet tables applicable for the current buffer.
@@ -1354,14 +2215,15 @@ Honours `yas/choose-tables-first', `yas/choose-keys-first' and
   (when yas/choose-tables-first
     (setq tables (list (yas/prompt-for-table tables))))
   (mapcar #'cdr
-         (if yas/choose-keys-first
-             (let ((key (yas/prompt-for-keys
-                         (mapcan #'yas/snippet-table-all-keys tables))))
-               (when key
-                 (mapcan #'(lambda (table)
-                             (yas/snippet-table-fetch table key))
-                         tables)))
-           (mapcan #'yas/snippet-table-templates tables))))
+          (if yas/choose-keys-first
+              (let ((key (yas/prompt-for-keys
+                          (mapcan #'yas/table-all-keys tables))))
+                (when key
+                  (mapcan #'(lambda (table)
+                              (yas/fetch table key))
+                          tables)))
+            (remove-duplicates (mapcan #'yas/table-templates tables)
+                               :test #'equal))))
 
 (defun yas/insert-snippet (&optional no-condition)
   "Choose a snippet to expand, pop-up a list of choices according
@@ -1370,22 +2232,23 @@ to `yas/prompt-function'.
 With prefix argument NO-CONDITION, bypass filtering of snippets
 by condition."
   (interactive "P")
+  (setq yas/condition-cache-timestamp (current-time))
   (let* ((yas/buffer-local-condition (or (and no-condition
-                                             'always)
-                                        yas/buffer-local-condition))
-        (templates (yas/all-templates (yas/get-snippet-tables)))
-        (template (and templates
-                       (or (and (rest templates) ;; more than one template for same key
-                                (yas/prompt-for-template templates))
-                           (car templates))))
-        (where (if mark-active
-                   (cons (region-beginning) (region-end))
-                 (cons (point) (point)))))
-    (if template
-       (yas/expand-snippet (car where)
-                           (cdr where)
-                           (yas/template-content template)
-                           (yas/template-env template))
+                                              'always)
+                                         yas/buffer-local-condition))
+         (templates (yas/all-templates (yas/get-snippet-tables)))
+         (yas/current-template (and templates
+                                    (or (and (rest templates) ;; more than one template for same key
+                                             (yas/prompt-for-template templates))
+                                        (car templates))))
+         (where (if mark-active
+                    (cons (region-beginning) (region-end))
+                  (cons (point) (point)))))
+    (if yas/current-template
+        (yas/expand-snippet (yas/template-content yas/current-template)
+                            (car where)
+                            (cdr where)
+                            (yas/template-expand-env yas/current-template))
       (message "[yas] No snippets can be inserted here!"))))
 
 (defun yas/visit-snippet-file ()
@@ -1395,176 +2258,425 @@ Only success if selected snippet was loaded from a file.  Put the
 visited file in `snippet-mode'."
   (interactive)
   (let* ((yas/buffer-local-condition 'always)
-        (templates (yas/all-templates (yas/get-snippet-tables)))
-        (template (and templates
-                       (or (and (rest templates) ;; more than one template for same key
-                                (yas/prompt-for-template templates
-                                                         "Choose a snippet template to edit: "))
-                           (car templates)))))
+         (templates (yas/all-templates (yas/get-snippet-tables)))
+         (template (and templates
+                        (or (and (rest templates) ;; more than one template for same key
+                                 (yas/prompt-for-template templates
+                                                          "Choose a snippet template to edit: "))
+                            (car templates)))))
 
     (when template
-      (let ((file (yas/template-file template)))
-       (cond ((and file (file-exists-p file))
-              (find-file-other-window file)
-              (snippet-mode))
-             (file
-              (message "Original file %s no longer exists!" file))
-             (t
-              (message "This snippet was not loaded from a file!")))))))
-
-(defun yas/guess-snippet-directory ()
-  "Try to guess suitable directories based on `major-mode' and
-also the current active tables."
-  (let ((main-dir (or (and (listp yas/root-directory)
-                          (first yas/root-directory))
-                     yas/root-directory
-                     "~/.emacs.d/snippets"))
-       (mode major-mode)
-       (options))
-    ;; Lookup main mode and add that to the options
-    ;; 
-    (push (format "%s/%s" main-dir mode) options)
-    ;; Next lookup the main active table
-    ;; 
-    (let ((active-tables (first (yas/get-snippet-tables)))
-         (other-path-alternative main-dir))
-      (when active-tables
-       (setq active-tables (cons active-tables
-                                 (yas/snippet-table-get-all-parents active-tables))))
-      (dolist (table (reverse active-tables))
-       (setq other-path-alternative
-             (concat other-path-alternative "/" (yas/snippet-table-name table))))
-      (push other-path-alternative options))
-    ;; Finally add to the options the guessed parent of major-mode
-    ;;  (this is almost never works out)
-    (when (get mode 'derived-mode-parent)
-      (push (format "%s/%s" main-dir (get mode 'derived-mode-parent)) options))
-    (reverse options)))
-
-(defun yas/new-snippet (&optional same-window)
-  "Create a new snippet in guessed current mode's directory."
-  (interactive)
-  (yas/find-snippets same-window
-                    (read-from-minibuffer "Enter snippet name: ")))
-  
+      (yas/visit-snippet-file-1 template))))
+
+(defun yas/visit-snippet-file-1 (template)
+  (let ((file (yas/template-file template)))
+    (cond ((and file (file-readable-p file))
+           (find-file-other-window file)
+           (snippet-mode)
+           (setq yas/editing-template template))
+          (file
+           (message "Original file %s no longer exists!" file))
+          (t
+           (switch-to-buffer (format "*%s*"(yas/template-name template)))
+           (let ((type 'snippet))
+             (when (listp (yas/template-content template))
+               (insert (format "# type: command\n"))
+               (setq type 'command))
+             (insert (format "# key: %s\n" (yas/template-key template)))
+             (insert (format "# name: %s\n" (yas/template-name template)))
+             (when (yas/template-keybinding template)
+               (insert (format "# binding: %s\n" (yas/template-keybinding template))))
+             (when (yas/template-expand-env template)
+               (insert (format "# expand-env: %s\n" (yas/template-expand-env template))))
+             (when (yas/template-condition template)
+               (insert (format "# condition: %s\n" (yas/template-condition template))))
+             (insert "# --\n")
+             (insert (if (eq type 'command)
+                         (pp-to-string (yas/template-content template))
+                       (yas/template-content template))))
+           (snippet-mode)
+           (setq yas/editing-template template)))))
+
+(defun yas/guess-snippet-directories-1 (table)
+  "Guesses possible snippet subdirectories for TABLE."
+  (cons (yas/table-name table)
+        (mapcan #'(lambda (parent)
+                    (yas/guess-snippet-directories-1
+                     parent))
+                (yas/table-parents table))))
+
+(defun yas/guess-snippet-directories (&optional table)
+  "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
+`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
+                   "/+$" ""
+                   (or (first (or (yas/snippet-dirs)
+                                  (setq yas/snippet-dirs '("~/.emacs.d/snippets")))))))
+        (tables (or (and table
+                         (list table))
+                    (yas/get-snippet-tables))))
+    ;; HACK! the snippet table created here is actually registered!
+    ;;
+    (unless (or table (gethash major-mode yas/tables))
+      (push (yas/table-get-create major-mode)
+            tables))
+
+    (mapcar #'(lambda (table)
+                (cons table
+                      (mapcar #'(lambda (subdir)
+                                  (concat main-dir "/" subdir))
+                              (yas/guess-snippet-directories-1 table))))
+            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."
+  (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))
+          (error "[yas] %s is not writable." candidate))
+        (if (y-or-n-p (format "Guessed directory (%s) for%s%s table \"%s\" does not exist! Create? "
+                              candidate
+                              (if (gethash (intern (yas/table-name (car table-and-dirs)))
+                                           yas/tables)
+                                  ""
+                                " brand new")
+                              (or main-table-string
+                                  "")
+                              (yas/table-name (car table-and-dirs))))
+            (progn
+              (make-directory candidate 'also-make-parents)
+              ;; create the .yas-parents file here...
+              candidate)))))
+
+(defun yas/new-snippet (&optional choose-instead-of-guess)
+  ""
+  (interactive "P")
+  (let ((guessed-directories (yas/guess-snippet-directories)))
+
+    (switch-to-buffer "*new snippet*")
+    (erase-buffer)
+    (set (make-local-variable 'yas/editing-template) nil)
+    (snippet-mode)
+    (set (make-local-variable 'yas/guessed-modes) (mapcar #'(lambda (d)
+                                                              (intern (yas/table-name (car d))))
+                                                          guessed-directories))
+    (unless (and choose-instead-of-guess
+                 (not (y-or-n-p "Insert a snippet with useful headers? ")))
+      (yas/expand-snippet "\
+# -*- mode: snippet -*-
+# name: $1
+# key: $2${3:
+# binding: ${4:direct-keybinding}}${5:
+# expand-env: ((${6:some-var} ${7:some-value}))}${8:
+# type: command}
+# --
+$0"))))
 
-(defun yas/find-snippets (&optional same-window file-name)
-  "Look for user snippets in guessed current mode's directory.
+(defun yas/find-snippets (&optional same-window )
+  "Find snippet file in guessed current mode's directory.
 
 Calls `find-file' interactively in the guessed directory.
 
 With prefix arg SAME-WINDOW opens the buffer in the same window.
 
-With optional FILE-NAME, finds the file directly, i.e. `find-file' is
-called non-interactively.
-
 Because snippets can be loaded from many different locations,
 this has to guess the correct directory using
-`yas/guess-directory', which returns a list of options. If any
-one of these exists, it is taken and `find-file' is called there,
-otherwise, proposes to create the first option returned by
-`yas/guess-directory'."
+`yas/guess-snippet-directories', which returns a list of
+options.
+
+If any one of these exists, it is taken and `find-file' is called
+there, otherwise, proposes to create the first option returned by
+`yas/guess-snippet-directories'."
   (interactive "P")
-  (let* ((guessed-directories (yas/guess-snippet-directory))
-        (target-directory (first (remove-if-not #'file-exists-p guessed-directories)))
-        (buffer))
-    
-    (unless target-directory
-      (when (y-or-n-p (format "Guessed directory (%s) does not exist! Create? " (first guessed-directories)))
-       (setq target-directory (first guessed-directories))
-       (make-directory target-directory 'also-make-parents)))
-
-    (when target-directory
-      (let ((default-directory target-directory))
-       (setq buffer (if file-name
-                        (if same-window
-                            (find-file file-name)
-                          (find-file-other-window file-name))
-                      (call-interactively (if same-window
-                                              'find-file
-                                            'find-file-other-window))))
-       (when buffer
-         (save-excursion
-           (set-buffer buffer)
-           (when (eq major-mode 'fundamental-mode)
-             (snippet-mode))))))))
-
-(defun yas/compute-major-mode-and-parents (file &optional prompt-if-failed no-hierarchy-parents)
+  (let* ((guessed-directories (yas/guess-snippet-directories))
+         (chosen)
+         (buffer))
+    (setq chosen (yas/make-directory-maybe (first guessed-directories) " main"))
+    (unless chosen
+      (if (y-or-n-p (format "Continue guessing for other active tables %s? "
+                            (mapcar #'(lambda (table-and-dirs)
+                                        (yas/table-name (car table-and-dirs)))
+                                    (rest guessed-directories))))
+          (setq chosen (some #'yas/make-directory-maybe
+                             (rest guessed-directories)))))
+    (unless chosen
+      (when (y-or-n-p "Having trouble... go to snippet root dir? ")
+        (setq chosen (first (yas/snippet-dirs)))))
+    (if chosen
+        (let ((default-directory chosen))
+          (setq buffer (call-interactively (if same-window
+                                               'find-file
+                                             'find-file-other-window)))
+          (when buffer
+            (save-excursion
+              (set-buffer buffer)
+              (when (eq major-mode 'fundamental-mode)
+                (snippet-mode)))))
+      (message "Could not guess snippet dir!"))))
+
+(defun yas/compute-major-mode-and-parents (file &optional prompt-if-failed)
   (let* ((file-dir (and file
-                       (directory-file-name (file-name-directory file))))
-        (major-mode-name (and file-dir
-                              (file-name-nondirectory file-dir)))
-        (parent-file-dir (and file-dir
-                              (directory-file-name (file-name-directory file-dir))))
-        (parent-mode-name (and parent-file-dir
-                               (not no-hierarchy-parents)
-                               (file-name-nondirectory parent-file-dir)))
-        (major-mode-sym (or (and major-mode-name
-                                 (intern major-mode-name))
-                            (when prompt-if-failed
-                              (read-from-minibuffer "[yas] Cannot auto-detect major mode! Enter a major mode: "))))
-        (parent-mode-sym (and parent-mode-name
-                              (intern parent-mode-name)))
-        (extra-parents-file-name (concat file-dir "/.yas-parents"))
-        (more-parents (when (file-readable-p extra-parents-file-name)
-                        (mapcar #'intern
-                                (split-string
-                                 (with-temp-buffer
-                                   (insert-file extra-parents-file-name)
-                                   (buffer-substring-no-properties (point-min)
-                                                                   (point-max))))))))
+                        (directory-file-name (or (some #'(lambda (special)
+                                                           (locate-dominating-file file special))
+                                                       '(".yas-setup.el"
+                                                         ".yas-make-groups"
+                                                         ".yas-parents"))
+                                                 (directory-file-name (file-name-directory file))))))
+         (parents-file-name (concat file-dir "/.yas-parents"))
+         (major-mode-name (and file-dir
+                               (file-name-nondirectory file-dir)))
+         (major-mode-sym (or (and major-mode-name
+                                  (intern major-mode-name))
+                             (when prompt-if-failed
+                               (read-from-minibuffer
+                                "[yas] Cannot auto-detect major mode! Enter a major mode: "))))
+         (parents (when (file-readable-p parents-file-name)
+                         (mapcar #'intern
+                                 (split-string
+                                  (with-temp-buffer
+                                    (insert-file-contents parents-file-name)
+                                    (buffer-substring-no-properties (point-min)
+                                                                    (point-max))))))))
     (when major-mode-sym
-      (remove nil (append (list major-mode-sym parent-mode-sym)
-                         more-parents)))))
+      (cons major-mode-sym parents))))
+
+(defvar yas/editing-template nil
+  "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")
-  (if buffer-file-name
-      (let ((major-mode-and-parent (yas/compute-major-mode-and-parents buffer-file-name)))
-       (if major-mode-and-parent
-           (let* ((parsed (yas/parse-template buffer-file-name))
-                  (name (and parsed
-                             (third parsed))))
-             (when name
-               (yas/define-snippets (car major-mode-and-parent)
-                                    (list parsed)
-                                    (cdr major-mode-and-parent))
-               (when (and (buffer-modified-p)
-                          (y-or-n-p "Save snippet? "))
-                 (save-buffer))
-               (if kill
-                   (quit-window kill)
-                 (message "[yas] Snippet \"%s\" loaded for %s." name (car major-mode-and-parent)))))
-         (message "[yas] Cannot load snippet for unknown major mode")))
-    (message "Save the buffer as a file first!")))
+  (let ((yas/ignore-filenames-as-triggers
+         (or yas/ignore-filenames-as-triggers
+             (and buffer-file-name
+                  (locate-dominating-file
+                   buffer-file-name
+                   ".yas-ignore-filenames-as-triggers")))))
+    (cond
+     ;;  We have `yas/editing-template', this buffer's
+     ;;  content comes from a template which is already loaded and
+     ;;  neatly positioned,...
+     ;;
+     (yas/editing-template
+      (yas/define-snippets-1 (yas/parse-template (yas/template-file yas/editing-template)) 
+                             (yas/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))))))))
+        (set (make-local-variable 'yas/editing-template) 
+             (yas/define-snippets-1 (yas/parse-template buffer-file-name)
+                                    table))))))
+  ;; Now, offer to save this shit
+  ;;
+  ;; 1) if `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).
+  ;;
+  ;; 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] 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))
+  (message "[yas] Snippet \"%s\" loaded for %s."
+           (yas/template-name yas/editing-template)
+           (yas/table-name (yas/template-table yas/editing-template))))
+
 
 (defun yas/tryout-snippet (&optional debug)
   "Test current buffers's snippet template in other buffer."
   (interactive "P")
   (let* ((major-mode-and-parent (yas/compute-major-mode-and-parents buffer-file-name))
-        (parsed (and major-mode-and-parent
-                     (fboundp (car major-mode-and-parent))
-                     (yas/parse-template (symbol-name (car major-mode-and-parent)))))
-        (template (and parsed
-                       (yas/make-template (second parsed) (third parsed) nil (sixth parsed) nil))))
-    (cond (template
-          (let ((buffer-name (format "*YAS TEST: %s*" (yas/template-name template))))
-            (set-buffer (switch-to-buffer buffer-name))
-            (erase-buffer)
-            (setq buffer-undo-list nil)
-            (funcall (car major-mode-and-parent))
-            (yas/expand-snippet (point-min) (point-max) (yas/template-content template) (yas/template-env template))
-            (when debug
-              (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local))))
-         (t
-          (message "[yas] Cannot test snippet for unknown major mode"))))) 
+         (parsed (yas/parse-template))
+         (test-mode (or (and (car major-mode-and-parent)
+                             (fboundp (car major-mode-and-parent))
+                             (car major-mode-and-parent))
+                        (first yas/guessed-modes)
+                        (intern (read-from-minibuffer "[yas] please input a mode: "))))
+         (yas/current-template
+          (and parsed
+               (fboundp test-mode)
+               (yas/populate-template (yas/make-blank-template)
+                                      :table       nil ;; no tables for ephemeral snippets
+                                      :key         (first parsed)
+                                      :content     (second parsed)
+                                      :name        (third parsed)
+                                      :expand-env  (sixth parsed)))))
+    (cond (yas/current-template
+           (let ((buffer-name (format "*testing snippet: %s*" (yas/template-name yas/current-template))))
+             (set-buffer (switch-to-buffer buffer-name))
+             (erase-buffer)
+             (setq buffer-undo-list nil)
+             (condition-case nil (funcall test-mode) (error nil))
+             (yas/expand-snippet (yas/template-content yas/current-template)
+                                 (point-min)
+                                 (point-max)
+                                 (yas/template-expand-env yas/current-template))
+             (when (and debug
+                        (require 'yasnippet-debug nil t))
+               (add-hook 'post-command-hook 'yas/debug-snippet-vars 't 'local))))
+          (t
+           (message "[yas] Cannot test snippet for unknown major mode")))))
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun yas/template-fine-group (template)
+  (car (last (or (yas/template-group template)
+                 (yas/template-perm-group template)))))
+
+(defun yas/describe-tables (&optional choose)
+  "Display snippets for each table."
+  (interactive "P")
+  (let* ((by-name-hash (and choose
+                            (y-or-n-p "Show by namehash? ")))
+         (buffer (get-buffer-create "*YASnippet tables*"))
+         (active-tables (yas/get-snippet-tables))
+         (remain-tables (let ((all))
+                          (maphash #'(lambda (k v)
+                                       (unless (find v active-tables)
+                                         (push v all)))
+                                   yas/tables)
+                          all))
+         (table-lists (list active-tables remain-tables))
+         (original-buffer (current-buffer))
+         (continue t)
+         (yas/condition-cache-timestamp (current-time)))
+    (with-current-buffer buffer
+      (setq buffer-read-only nil)
+      (erase-buffer)
+      (cond ((not by-name-hash)
+             (insert "YASnippet tables: \n")
+             (while (and table-lists
+                         continue)
+               (dolist (table (car table-lists))
+                 (yas/describe-pretty-table table))
+               (setq table-lists (cdr table-lists))
+               (when table-lists
+                 (yas/create-snippet-xrefs)
+                 (display-buffer buffer)
+                 (setq continue (and choose (y-or-n-p "Show also non-active tables? ")))))
+             (yas/create-snippet-xrefs)
+             (help-mode)
+             (goto-char 1))
+            (t
+             (insert "\n\nYASnippet tables by NAMEHASH: \n")
+             (dolist (table (append active-tables remain-tables))
+               (insert (format "\nSnippet table `%s':\n\n" (yas/table-name table)))
+               (let ((keys))
+                 (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)
+                                                  (push k names))
+                                              (gethash key (yas/table-hash table)))
+                                     names))))))))
+      (goto-char 1)
+      (setq buffer-read-only t))
+    (display-buffer buffer)))
+
+(defun yas/describe-pretty-table (table)
+  (insert (format "\nSnippet table `%s'"
+                  (yas/table-name table)))
+  (if (yas/table-parents table)
+      (insert (format " parents: %s\n"
+                      (mapcar #'yas/table-name
+                              (yas/table-parents table))))
+    (insert "\n"))
+  (insert (make-string 100 ?-) "\n")
+  (insert "group                   state name                                    key             binding\n")
+  (let ((groups-alist (list))
+        group)
+    (maphash #'(lambda (k v)
+                 (setq group (or (yas/template-fine-group v)
+                                 "(top level)"))
+                 (when (yas/template-name v)
+                   
+                   (aput 'groups-alist group (cons v (aget groups-alist group)))))
+             (yas/table-uuidhash table))
+    (dolist (group-and-templates groups-alist)
+      (when (rest groups-alist)
+        (setq group (truncate-string-to-width (car group-and-templates) 25 0 ?  "..."))
+        (insert (make-string 100 ?-) "\n")
+        (dolist (p (cdr group-and-templates))
+          (let ((name (truncate-string-to-width (propertize (format "\\\\snippet `%s'" (yas/template-name p))
+                                                            'yasnippet p)
+                                                50 0 ? "..."))
+                (group (prog1 group 
+                         (setq group (make-string (length group) ? ))))
+                (condition-string (let ((condition (yas/template-condition p)))
+                                    (if condition
+                                        (with-current-buffer original-buffer
+                                          (if (yas/eval-condition condition)
+                                              "(y)"
+                                            "(s)"))
+                                      "(a)"))))
+            (insert group " ")
+            (insert condition-string " ")
+            (insert name 
+                    (if (string-match "\\.\\.\\.$" name)
+                        "'"
+                      " ")
+                    " ")
+            (insert (truncate-string-to-width (or (yas/template-key p) "")
+                                              15 0 ?  "...") " ")
+            (insert (truncate-string-to-width (key-description (yas/template-keybinding p))
+                                              15 0 ?  "...") " ")
+            (insert "\n")))))))
+
+
+
+
+\f
 ;;; User convenience functions, for using in snippet definitions
-;;;
 
 (defvar yas/modified-p nil
   "Non-nil if field has been modified by user or transformation.")
@@ -1589,19 +2701,19 @@ If found, the content of subexp group SUBEXP (default 0) is
 (defun yas/choose-value (possibilities)
   "Prompt for a string in the list POSSIBILITIES and return it."
   (unless (or yas/moving-away-p
-             yas/modified-p)
+              yas/modified-p)
     (some #'(lambda (fn)
-             (funcall fn "Choose: " possibilities))
-         yas/prompt-functions)))
+              (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)
+              yas/modified-p)
     (let ((key (read-key-sequence "")))
       (when (stringp key)
-       (or (cdr (find key alist :key #'car :test #'string=))
-           key)))))
+        (or (cdr (find key alist :key #'car :test #'string=))
+            key)))))
 
 (defun yas/throw (text)
   "Throw a yas/exception with TEXT as the reason."
@@ -1615,18 +2727,45 @@ Otherwise throw exception."
     (yas/throw (format "[yas] field only allows %s" possibilities))))
 
 (defun yas/field-value (number)
+  "A primary field transformation..."
   (let* ((snippet (car (yas/snippets-at-point)))
-        (field (and snippet
-                    (yas/snippet-find-field snippet number))))
+         (field (and snippet
+                     (yas/snippet-find-field snippet number))))
     (when field
       (yas/field-text-for-display field))))
 
-(defun yas/oni (text oni-regexp)
-  "Runs ruby to parse TEXT with Oniguruma regexp ONI-REGEXP."
-  (shell-command-to-string (format "ruby -e 'print \"%s\".gsub(\"a\",\"b\")'" "aha"))) 
-  
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun yas/text ()
+  "Return `yas/text' if that exists and is non-empty, else nil."
+  (if (and yas/text
+           (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/get-field-once (number &optional transform-fn)
+  (unless yas/modified-p
+    (if transform-fn
+        (funcall transform-fn (yas/field-value number))
+      (yas/field-value number))))
+
+(defun yas/default-from-field (number)
+  (unless yas/modified-p
+    (yas/field-value number)))
+
+(defun yas/inside-string ()
+  (equal 'font-lock-string-face (get-char-property (1- (point)) 'face)))
+
+(defun yas/unimplemented ()
+  (if yas/current-template
+      (if (y-or-n-p "This snippet is unimplemented. Visit the snippet definition? ")
+          (yas/visit-snippet-file-1 yas/current-template))
+    (message "No implementation.")))
+
+\f
 ;;; Snippet expansion and field management
 
 (defvar yas/active-field-overlay nil
@@ -1635,6 +2774,9 @@ Otherwise throw exception."
 (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")
+
 (defvar yas/deleted-text nil
   "The text deleted in the last snippet expansion.")
 
@@ -1676,6 +2818,7 @@ Otherwise throw exception."
   "A mirror."
   start end
   (transform nil)
+  parent-field
   next)
 
 (defstruct (yas/exit (:constructor yas/make-exit (marker)))
@@ -1686,19 +2829,20 @@ Otherwise throw exception."
   "Calculate the value of the field/mirror. If there's a transform
 for this field, apply it. Otherwise, returned nil."
   (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)
-                       (yas/mirror-transform field-or-mirror)
-                     (yas/field-transform field-or-mirror)))
-        (start-point (if (yas/mirror-p field-or-mirror)
-                         (yas/mirror-start field-or-mirror)
-                       (yas/field-start field-or-mirror)))
-        (transformed (and transform
-                          (save-excursion
-                            (goto-char start-point)
-                            (yas/eval-string transform)))))
+         (text yas/text)
+         (yas/modified-p (yas/field-modified-p field))
+         (yas/moving-away-p nil)
+         (transform (if (yas/mirror-p field-or-mirror)
+                        (yas/mirror-transform field-or-mirror)
+                      (yas/field-transform field-or-mirror)))
+         (start-point (if (yas/mirror-p field-or-mirror)
+                          (yas/mirror-start field-or-mirror)
+                        (yas/field-start field-or-mirror)))
+         (transformed (and transform
+                           (save-excursion
+                             (goto-char start-point)
+                             (let ((ret (yas/eval-lisp transform)))
+                               (or ret ""))))))
     transformed))
 
 (defsubst yas/replace-all (from to &optional text)
@@ -1713,15 +2857,14 @@ With optional string TEXT do it in that string."
 
 (defun yas/snippet-find-field (snippet number)
   (find-if #'(lambda (field)
-              (eq number (yas/field-number field)))
-          (yas/snippet-fields snippet)))
+               (eq number (yas/field-number field)))
+           (yas/snippet-fields snippet)))
 
 (defun yas/snippet-sort-fields (snippet)
   "Sort the fields of SNIPPET in navigation order."
   (setf (yas/snippet-fields snippet)
-       (sort (yas/snippet-fields snippet)
-             '(lambda (field1 field2)
-                (yas/snippet-field-compare field1 field2)))))
+        (sort (yas/snippet-fields snippet)
+              #'yas/snippet-field-compare)))
 
 (defun yas/snippet-field-compare (field1 field2)
   "Compare two fields. The field with a number is sorted first.
@@ -1731,10 +2874,11 @@ have, compare through the field's start point"
         (n2 (yas/field-number field2)))
     (if n1
         (if n2
-            (< n1 n2)
-          t)
+            (or (zerop n2) (and (not (zerop n1))
+                                (< n1 n2)))
+          (not (zerop n1)))
       (if n2
-          nil
+          (zerop n2)
         (< (yas/field-start field1)
            (yas/field-start field2))))))
 
@@ -1742,18 +2886,18 @@ have, compare through the field's start point"
   "Guess if SNIPPET's FIELD should be skipped."
   (and (zerop (- (yas/field-start field) (yas/field-end field)))
        (or (yas/field-parent-field field)
-          (and (eq field (car (last (yas/snippet-fields snippet))))
-               (= (yas/field-start field) (overlay-end (yas/snippet-control-overlay snippet)))))))
+           (and (eq field (car (last (yas/snippet-fields snippet))))
+                (= (yas/field-start field) (overlay-end (yas/snippet-control-overlay snippet)))))))
 
 (defun yas/snippets-at-point (&optional all-snippets)
   "Return a sorted list of snippets at point, most recently
 inserted first."
   (sort
    (remove nil (remove-duplicates (mapcar #'(lambda (ov)
-                                             (overlay-get ov 'yas/snippet))
-                                         (if all-snippets
-                                             (overlays-in (point-min) (point-max))
-                                           (overlays-at (point))))))
+                                              (overlay-get ov 'yas/snippet))
+                                          (if all-snippets
+                                              (overlays-in (point-min) (point-max))
+                                            (overlays-at (point))))))
    #'(lambda (s1 s2)
        (<= (yas/snippet-id s2) (yas/snippet-id s1)))))
 
@@ -1763,10 +2907,10 @@ delegate to `yas/next-field'."
   (interactive)
   (if yas/triggers-in-field
       (let ((yas/fallback-behavior 'return-nil)
-           (active-field (overlay-get yas/active-field-overlay 'yas/field)))
-       (when active-field
-         (unless (yas/expand-1 active-field)
-           (yas/next-field))))
+            (active-field (overlay-get yas/active-field-overlay 'yas/field)))
+        (when active-field
+          (unless (yas/expand-from-trigger-key active-field)
+            (yas/next-field))))
     (yas/next-field)))
 
 (defun yas/next-field (&optional arg)
@@ -1775,24 +2919,24 @@ delegate to `yas/next-field'."
   (let* ((arg (or arg
                   1))
          (snippet (first (yas/snippets-at-point)))
-        (active-field (overlay-get yas/active-field-overlay 'yas/field))
+         (active-field (overlay-get yas/active-field-overlay 'yas/field))
          (live-fields (remove-if #'(lambda (field)
-                                    (and (not (eq field active-field))
-                                         (yas/field-probably-deleted-p snippet field)))
-                                (yas/snippet-fields snippet)))
-        (active-field-pos (position active-field live-fields))
-        (target-pos (and active-field-pos (+ arg active-field-pos)))
-        (target-field (nth target-pos live-fields)))
+                                     (and (not (eq field active-field))
+                                          (yas/field-probably-deleted-p snippet field)))
+                                 (yas/snippet-fields snippet)))
+         (active-field-pos (position active-field live-fields))
+         (target-pos (and active-field-pos (+ arg active-field-pos)))
+         (target-field (nth target-pos live-fields)))
     ;; First check if we're moving out of a field with a transform
-    ;; 
+    ;;
     (when (and active-field
-              (yas/field-transform active-field))
+               (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-string (yas/field-transform active-field))))
+             (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))))
     ;; Now actually move...
     (cond ((>= target-pos (length live-fields))
            (yas/exit-snippet snippet))
@@ -1814,75 +2958,48 @@ Also create some protection overlays"
   (setf (yas/snippet-active-field snippet) field)
   (yas/place-overlays snippet field)
   (overlay-put yas/active-field-overlay 'yas/field field)
-  ;;; primary field transform: first call to snippet transform
-  (unless (yas/field-modified-p field)
-    (if (yas/field-update-display field snippet)
-       (let ((inhibit-modification-hooks t))
-         (yas/update-mirrors snippet))
-      (setf (yas/field-modified-p field) nil))))
+  (let ((number (yas/field-number field)))
+    (if (and number (zerop number))
+        (progn
+          (set-mark (yas/field-end field))
+          (setf (yas/snippet-force-exit snippet) t))
+      ;; primary field transform: first call to snippet transform
+      (unless (yas/field-modified-p field)
+        (if (yas/field-update-display field snippet)
+            (let ((inhibit-modification-hooks t))
+              (yas/update-mirrors snippet))
+          (setf (yas/field-modified-p field) nil))))))
 
 (defun yas/prev-field ()
   "Navigate to prev field.  If there's none, exit the snippet."
   (interactive)
   (yas/next-field -1))
 
+(defun yas/abort-snippet (&optional snippet)
+  (interactive)
+  (let ((snippet (or snippet
+                     (car (yas/snippets-at-point)))))
+    (when snippet
+      (setf (yas/snippet-force-exit snippet) t))))
+
 (defun yas/exit-snippet (snippet)
   "Goto exit-marker of SNIPPET."
   (interactive)
   (setf (yas/snippet-force-exit snippet) t)
   (goto-char (if (yas/snippet-exit snippet)
-                (yas/exit-marker (yas/snippet-exit snippet))
-              (overlay-end (yas/snippet-control-overlay snippet)))))
+                 (yas/exit-marker (yas/snippet-exit snippet))
+               (overlay-end (yas/snippet-control-overlay snippet)))))
 
-;;; Apropos markers-to-points:
-;;;
-;;; This was found useful for performance reasons, so that an
-;;; excessive number of live markers aren't kept around in the
-;;; `buffer-undo-list'. However, in `markers-to-points', 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 nil has to be reused.
-;;;
-;;; This shouldn't bring horrible problems with undo/redo, but it
-;;; you never know
-;;;
+(defun yas/exit-all-snippets ()
+  "Exit all snippets."
+  (interactive)
+  (mapc #'(lambda (snippet)
+            (yas/exit-snippet snippet)
+            (yas/check-commit-snippet))
+        (yas/snippets-at-point)))
 
-(defun yas/markers-to-points (snippet)
-  "Convert all markers in SNIPPET to a cons (POINT . MARKER)
-where POINT is the original position of the marker and MARKER is
-the original marker object with the position set to nil."
-  (dolist (field (yas/snippet-fields snippet))
-    (let ((start (marker-position (yas/field-start field)))
-         (end (marker-position (yas/field-end field))))
-      (set-marker (yas/field-start field) nil)
-      (set-marker (yas/field-end field) nil)
-      (setf (yas/field-start field) (cons start (yas/field-start field)))
-      (setf (yas/field-end field) (cons end (yas/field-end field))))
-    (dolist (mirror (yas/field-mirrors field))
-      (let ((start (marker-position (yas/mirror-start mirror)))
-           (end (marker-position (yas/mirror-end mirror))))
-       (set-marker (yas/mirror-start mirror) nil)
-       (set-marker (yas/mirror-end mirror) nil)
-       (setf (yas/mirror-start mirror) (cons start (yas/mirror-start mirror)))
-       (setf (yas/mirror-end mirror) (cons end (yas/mirror-end mirror))))))
-  (let ((snippet-exit (yas/snippet-exit snippet)))
-    (when snippet-exit 
-      (let ((exit (marker-position (yas/exit-marker snippet-exit))))
-       (set-marker (yas/exit-marker snippet-exit) nil)
-       (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'."
-  (dolist (field (yas/snippet-fields snippet))
-    (setf (yas/field-start field) (set-marker (cdr (yas/field-start field)) (car (yas/field-start field))))
-    (setf (yas/field-end field) (set-marker (cdr (yas/field-end field)) (car (yas/field-end field))))
-    (dolist (mirror (yas/field-mirrors field))
-      (setf (yas/mirror-start mirror) (set-marker (cdr (yas/mirror-start mirror)) (car (yas/mirror-start mirror))))
-      (setf (yas/mirror-end mirror) (set-marker (cdr (yas/mirror-end mirror)) (car (yas/mirror-end mirror))))))
-  (let ((snippet-exit (yas/snippet-exit snippet)))
-    (when snippet-exit
-      (setf (yas/exit-marker snippet-exit) (set-marker (cdr (yas/exit-marker snippet-exit)) (car (yas/exit-marker snippet-exit)))))))
+\f
+;;; Some low level snippet-routines
 
 (defun yas/commit-snippet (snippet &optional no-hooks)
   "Commit SNIPPET, but leave point as it is.  This renders the
@@ -1894,8 +3011,8 @@ exiting the snippet.
 NO-HOOKS means don't run the `yas/after-exit-snippet-hook' hooks."
 
   (let ((control-overlay (yas/snippet-control-overlay snippet))
-       yas/snippet-beg
-       yas/snippet-end)
+        yas/snippet-beg
+        yas/snippet-end)
     ;;
     ;; Save the end of the moribund snippet in case we need to revive it
     ;; its original expansion.
@@ -1908,9 +3025,9 @@ NO-HOOKS means don't run the `yas/after-exit-snippet-hook' hooks."
 
     (let ((inhibit-modification-hooks t))
       (when yas/active-field-overlay
-       (delete-overlay yas/active-field-overlay))
+        (delete-overlay yas/active-field-overlay))
       (when yas/field-protection-overlays
-       (mapc #'delete-overlay yas/field-protection-overlays)))
+        (mapc #'delete-overlay yas/field-protection-overlays)))
 
     ;; stacked expansion: if the original expansion took place from a
     ;; field, make sure we advance it here at least to
@@ -1918,7 +3035,7 @@ NO-HOOKS means don't run the `yas/after-exit-snippet-hook' hooks."
     ;;
     (let ((previous-field (yas/snippet-previous-active-field snippet)))
       (when (and yas/snippet-end previous-field)
-       (yas/advance-end-maybe previous-field yas/snippet-end)))
+        (yas/advance-end-maybe previous-field yas/snippet-end)))
 
     ;; Convert all markers to points,
     ;;
@@ -1927,12 +3044,12 @@ NO-HOOKS means don't run the `yas/after-exit-snippet-hook' hooks."
     ;; Take care of snippet revival
     ;;
     (if yas/snippet-revival
-       (push `(apply yas/snippet-revive ,yas/snippet-beg ,yas/snippet-end ,snippet)
-             buffer-undo-list)
+        (push `(apply yas/snippet-revive ,yas/snippet-beg ,yas/snippet-end ,snippet)
+              buffer-undo-list)
       ;; Dismember the snippet... this is useful if we get called
       ;; again from `yas/take-care-of-redo'....
       (setf (yas/snippet-fields snippet) nil))
-    
+
     ;; XXX: `yas/after-exit-snippet-hook' should be run with
     ;; `yas/snippet-beg' and `yas/snippet-end' bound. That might not
     ;; be the case if the main overlay had somehow already
@@ -1940,43 +3057,97 @@ NO-HOOKS means don't run the `yas/after-exit-snippet-hook' hooks."
     ;; up...
     ;;
     (unless no-hooks (run-hooks 'yas/after-exit-snippet-hook)))
-  
+
   (message "[yas] snippet exited."))
 
 (defun yas/check-commit-snippet ()
   "Checks 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))
+         (snippets-left snippets))
     (dolist (snippet snippets)
       (let ((active-field (yas/snippet-active-field snippet)))
-       (cond ((or (prog1 (yas/snippet-force-exit snippet)
-                    (setf (yas/snippet-force-exit snippet) nil))
-                  (not (and active-field (yas/field-contains-point-p active-field))))
-              (setq snippets-left (delete snippet snippets-left))
-              (yas/commit-snippet snippet snippets-left))
-             ((and active-field
-                   (or (not yas/active-field-overlay)
-                       (not (overlay-buffer yas/active-field-overlay))))
-              ;;
-              ;; stacked expansion: this case is mainly for recent
-              ;; snippet exits that place us back int the field of
-              ;; another snippet
-              ;;
-              (save-excursion
-                (yas/move-to-field snippet active-field)
-                (yas/update-mirrors snippet)))
-             (t
-              nil))))
+        (cond ((or (prog1 (yas/snippet-force-exit snippet)
+                     (setf (yas/snippet-force-exit snippet) nil))
+                   (not (and active-field (yas/field-contains-point-p active-field))))
+               (setq snippets-left (delete snippet snippets-left))
+               (yas/commit-snippet snippet snippets-left))
+              ((and active-field
+                    (or (not yas/active-field-overlay)
+                        (not (overlay-buffer yas/active-field-overlay))))
+               ;;
+               ;; stacked expansion: this case is mainly for recent
+               ;; snippet exits that place us back int the field of
+               ;; another snippet
+               ;;
+               (save-excursion
+                 (yas/move-to-field snippet active-field)
+                 (yas/update-mirrors snippet)))
+              (t
+               nil))))
     (unless snippets-left
       (remove-hook 'post-command-hook 'yas/post-command-handler 'local)
       (remove-hook 'pre-command-hook 'yas/pre-command-handler 'local))))
 
+;; Apropos markers-to-points:
+;;
+;; This was found useful for performance reasons, so that an
+;; excessive number of live markers aren't kept around in the
+;; `buffer-undo-list'. However, in `markers-to-points', 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 nil has to be reused.
+;;
+;; This shouldn't bring horrible problems with undo/redo, but it
+;; you never know
+;;
+(defun yas/markers-to-points (snippet)
+  "Convert all markers in SNIPPET to a cons (POINT . MARKER)
+where POINT is the original position of the marker and MARKER is
+the original marker object with the position set to nil."
+  (dolist (field (yas/snippet-fields snippet))
+    (let ((start (marker-position (yas/field-start field)))
+          (end (marker-position (yas/field-end field))))
+      (set-marker (yas/field-start field) nil)
+      (set-marker (yas/field-end field) nil)
+      (setf (yas/field-start field) (cons start (yas/field-start field)))
+      (setf (yas/field-end field) (cons end (yas/field-end field))))
+    (dolist (mirror (yas/field-mirrors field))
+      (let ((start (marker-position (yas/mirror-start mirror)))
+            (end (marker-position (yas/mirror-end mirror))))
+        (set-marker (yas/mirror-start mirror) nil)
+        (set-marker (yas/mirror-end mirror) nil)
+        (setf (yas/mirror-start mirror) (cons start (yas/mirror-start mirror)))
+        (setf (yas/mirror-end mirror) (cons end (yas/mirror-end mirror))))))
+  (let ((snippet-exit (yas/snippet-exit snippet)))
+    (when snippet-exit
+      (let ((exit (marker-position (yas/exit-marker snippet-exit))))
+        (set-marker (yas/exit-marker snippet-exit) nil)
+        (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'."
+  (dolist (field (yas/snippet-fields snippet))
+    (setf (yas/field-start field) (set-marker (cdr (yas/field-start field))
+                                              (car (yas/field-start field))))
+    (setf (yas/field-end field) (set-marker (cdr (yas/field-end field))
+                                            (car (yas/field-end field))))
+    (dolist (mirror (yas/field-mirrors field))
+      (setf (yas/mirror-start mirror) (set-marker (cdr (yas/mirror-start mirror))
+                                                  (car (yas/mirror-start mirror))))
+      (setf (yas/mirror-end mirror) (set-marker (cdr (yas/mirror-end mirror))
+                                                (car (yas/mirror-end mirror))))))
+  (let ((snippet-exit (yas/snippet-exit snippet)))
+    (when snippet-exit
+      (setf (yas/exit-marker snippet-exit) (set-marker (cdr (yas/exit-marker snippet-exit))
+                                                       (car (yas/exit-marker snippet-exit)))))))
+
 (defun yas/field-contains-point-p (field &optional point)
   (let ((point (or point
-                  (point))))
+                   (point))))
     (and (>= point (yas/field-start field))
-        (<= point (yas/field-end field)))))
+         (<= point (yas/field-end field)))))
 
 (defun yas/field-text-for-display (field)
   "Return the propertized display text for field FIELD.  "
@@ -2006,40 +3177,58 @@ holds the keymap."
 Otherwise deletes a character normally by calling `delete-char'."
   (interactive)
   (let ((field (or field
-                  (and yas/active-field-overlay
-                       (overlay-buffer yas/active-field-overlay)
-                       (overlay-get yas/active-field-overlay 'yas/field)))))
+                   (and yas/active-field-overlay
+                        (overlay-buffer yas/active-field-overlay)
+                        (overlay-get yas/active-field-overlay 'yas/field)))))
     (cond ((and field
-               (not (yas/field-modified-p field))
-               (eq (point) (marker-position (yas/field-start field))))
-          (yas/skip-and-clear field)
-          (yas/next-field 1))
-         (t
-          (call-interactively 'delete-char)))))
+                (not (yas/field-modified-p field))
+                (eq (point) (marker-position (yas/field-start field))))
+           (yas/skip-and-clear field)
+           (yas/next-field 1))
+          (t
+           (call-interactively 'delete-char)))))
 
 (defun yas/skip-and-clear (field)
   "Deletes the region of FIELD and sets it modified state to t"
-  (setf (yas/field-modified-p field) t)
+  ;; Just before skipping-and-clearing the field, mark its children
+  ;; fields as modified, too. If the childen have mirrors-in-fields
+  ;; this prevents them from updating erroneously (we're skipping and
+  ;; deleting!).
+  ;;
+  (yas/mark-this-and-children-modified field)
   (delete-region (yas/field-start field) (yas/field-end field)))
 
+(defun yas/mark-this-and-children-modified (field)
+  (setf (yas/field-modified-p field) t)
+  (let ((fom (yas/field-next field)))
+    (while (and fom
+                (yas/fom-parent-field fom))
+      (when (and (eq (yas/fom-parent-field fom) field)
+                 (yas/field-p fom))
+        (yas/mark-this-and-children-modified fom))
+      (setq fom (yas/fom-next fom)))))
+
 (defun yas/make-move-active-field-overlay (snippet field)
   "Place the active field overlay in SNIPPET's FIELD.
 
 Move the overlay, or create it if it does not exit."
   (if (and yas/active-field-overlay
-          (overlay-buffer yas/active-field-overlay))
+           (overlay-buffer yas/active-field-overlay))
       (move-overlay yas/active-field-overlay
-                   (yas/field-start field)
-                   (yas/field-end field))
+                    (yas/field-start field)
+                    (yas/field-end field))
     (setq yas/active-field-overlay
-         (make-overlay (yas/field-start field)
-                       (yas/field-end field)
-                       nil nil t))
+          (make-overlay (yas/field-start field)
+                        (yas/field-end field)
+                        nil nil t))
+    (overlay-put yas/active-field-overlay 'priority 100)
     (overlay-put yas/active-field-overlay 'face 'yas/field-highlight-face)
     (overlay-put yas/active-field-overlay 'yas/snippet snippet)
     (overlay-put yas/active-field-overlay 'modification-hooks '(yas/on-field-overlay-modification))
-    (overlay-put yas/active-field-overlay 'insert-in-front-hooks '(yas/on-field-overlay-modification))
-    (overlay-put yas/active-field-overlay 'insert-behind-hooks '(yas/on-field-overlay-modification))))
+    (overlay-put yas/active-field-overlay 'insert-in-front-hooks
+                 '(yas/on-field-overlay-modification))
+    (overlay-put yas/active-field-overlay 'insert-behind-hooks
+                 '(yas/on-field-overlay-modification))))
 
 (defun yas/on-field-overlay-modification (overlay after? beg end &optional length)
   "Clears the field and updates mirrors, conditionally.
@@ -2048,182 +3237,221 @@ Only clears the field if it hasn't been modified and it point it
 at field start. This hook doesn't do anything if an undo is in
 progress."
   (unless (yas/undo-in-progress)
-    (let ((field (overlay-get yas/active-field-overlay 'yas/field)))
+    (let* ((field (overlay-get yas/active-field-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))
-            ;;; primary field transform: normal calls to expression
-            (let ((saved-point (point)))
-              (yas/field-update-display field (car (yas/snippets-at-point)))
-              (goto-char saved-point))
-            (yas/update-mirrors (car (yas/snippets-at-point))))
-           (field
-            (when (and (not after?)
-                       (not (yas/field-modified-p field))
-                       (eq (point) (if (markerp (yas/field-start field))
-                                       (marker-position (yas/field-start field))
-                                     (yas/field-start field))))
-              (yas/skip-and-clear field))
-            (setf (yas/field-modified-p field) t))))))
-
+             (yas/advance-end-maybe field (overlay-end overlay))
+             (let ((saved-point (point)))
+               (yas/field-update-display field (car (yas/snippets-at-point)))
+               (goto-char saved-point))
+             (yas/update-mirrors (car (yas/snippets-at-point))))
+            (field
+             (when (and (not after?)
+                        (not (yas/field-modified-p field))
+                        (eq (point) (if (markerp (yas/field-start field))
+                                        (marker-position (yas/field-start field))
+                                      (yas/field-start field))))
+               (yas/skip-and-clear field))
+             (setf (yas/field-modified-p field) t))))))
+\f
 ;;; Apropos protection overlays:
-;;;
-;;; These exist for nasty users who will try to delete parts of the
-;;; snippet outside the active field. Actual protection happens in
-;;; `yas/on-protection-overlay-modification'.
-;;;
-;;; Currently this signals an error which inhibits the command. For
-;;; commands that move point (like `kill-line'), point is restored in
-;;; the `yas/post-command-handler' using a global
-;;; `yas/protection-violation' variable.
-;;;
-;;; Alternatively, I've experimented with an implementation that
-;;; commits the snippet before actually calling `this-command'
-;;; interactively, and then signals an eror, which is ignored. but
-;;; blocks all other million modification hooks. This presented some
-;;; problems with stacked expansion.
-;;;
+;;
+;; These exist for nasty users who will try to delete parts of the
+;; snippet outside the active field. Actual protection happens in
+;; `yas/on-protection-overlay-modification'.
+;;
+;; Currently this signals an error which inhibits the command. For
+;; commands that move point (like `kill-line'), point is restored in
+;; the `yas/post-command-handler' using a global
+;; `yas/protection-violation' variable.
+;;
+;; Alternatively, I've experimented with an implementation that
+;; commits the snippet before actually calling `this-command'
+;; interactively, and then signals an eror, which is ignored. but
+;; blocks all other million modification hooks. This presented some
+;; problems with stacked expansion.
+;;
 
 (defun yas/make-move-field-protection-overlays (snippet field)
   "Place protection overlays surrounding SNIPPET's FIELD.
 
 Move the overlays, or create them if they do not exit."
   (let ((start (yas/field-start field))
-       (end (yas/field-end field)))
+        (end (yas/field-end field)))
     ;; First check if the (1+ end) is contained in the buffer,
     ;; otherwise we'll have to do a bit of cheating and silently
     ;; insert a newline. the `(1+ (buffer-size))' should prevent this
     ;; when using stacked expansion
-    ;; 
+    ;;
     (when (< (buffer-size) end)
       (save-excursion
-       (let ((inhibit-modification-hooks t))
-         (goto-char (point-max))
-         (newline))))
+        (let ((inhibit-modification-hooks t))
+          (goto-char (point-max))
+          (newline))))
     ;; go on to normal overlay creation/moving
-    ;; 
+    ;;
     (cond ((and yas/field-protection-overlays
-               (every #'overlay-buffer yas/field-protection-overlays))
-          (move-overlay (first yas/field-protection-overlays) (1- start) start)
-          (move-overlay (second yas/field-protection-overlays) end (1+ end)))
-         (t
-          (setq yas/field-protection-overlays
-                (list (make-overlay (1- start) start nil t nil)
-                      (make-overlay end (1+ end) nil t nil)))
-          (dolist (ov yas/field-protection-overlays)
-            (overlay-put ov 'face 'yas/field-debug-face)
-            (overlay-put ov 'yas/snippet snippet)
-            ;; (overlay-put ov 'evaporate t)
-            (overlay-put ov 'modification-hooks '(yas/on-protection-overlay-modification)))))))
+                (every #'overlay-buffer yas/field-protection-overlays))
+           (move-overlay (first yas/field-protection-overlays) (1- start) start)
+           (move-overlay (second yas/field-protection-overlays) end (1+ end)))
+          (t
+           (setq yas/field-protection-overlays
+                 (list (make-overlay (1- start) start nil t nil)
+                       (make-overlay end (1+ end) nil t nil)))
+           (dolist (ov yas/field-protection-overlays)
+             (overlay-put ov 'face 'yas/field-debug-face)
+             (overlay-put ov 'yas/snippet snippet)
+             ;; (overlay-put ov 'evaporate t)
+             (overlay-put ov 'modification-hooks '(yas/on-protection-overlay-modification)))))))
 
 (defvar yas/protection-violation nil
   "When non-nil, signals attempts to erronesly exit or modify the snippet.
 
 Functions in the `post-command-hook', for example
-`yas/post-command-handler' can check it and reset its value to nil. The variables value is the point where the violation originated")
+`yas/post-command-handler' can check it and reset its value to
+nil. The variables value is the point where the violation
+originated")
 
 (defun yas/on-protection-overlay-modification (overlay after? beg end &optional length)
   "Signals a snippet violation, then issues error.
 
 The error should be ignored in `debug-ignored-errors'"
   (cond ((not (or after?
-                 (yas/undo-in-progress)))
-        (setq yas/protection-violation (point))
-        (error "Exit the snippet first!"))))
+                  (yas/undo-in-progress)))
+         (setq yas/protection-violation (point))
+         (error "Exit the snippet first!"))))
 
 (add-to-list 'debug-ignored-errors "^Exit the snippet first!$")
 
+\f
 ;;; Apropos stacked expansion:
-;;;
-;;; the parent snippet does not run its fields modification hooks
-;;; (`yas/on-field-overlay-modification' and
-;;; `yas/on-protection-overlay-modification') while the child snippet
-;;; is active. This means, among other things, that the mirrors of the
-;;; parent snippet are not updated, this only happening when one exits
-;;; the child snippet.
-;;;
-;;; Unfortunately, this also puts some ugly (and not fully-tested)
-;;; bits of code in `yas/expand-snippet' and
-;;; `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
-;;; 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
-;;; running. This would mean a lot of overlay modification hooks
-;;; running, but if managed correctly (including overlay priorities)
-;;; they should account for all situations...
-;;;
+;;
+;; the parent snippet does not run its fields modification hooks
+;; (`yas/on-field-overlay-modification' and
+;; `yas/on-protection-overlay-modification') while the child snippet
+;; is active. This means, among other things, that the mirrors of the
+;; parent snippet are not updated, this only happening when one exits
+;; the child snippet.
+;;
+;; Unfortunately, this also puts some ugly (and not fully-tested)
+;; bits of code in `yas/expand-snippet' and
+;; `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
+;; 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
+;; running. This would mean a lot of overlay modification hooks
+;; running, but if managed correctly (including overlay priorities)
+;; they should account for all situations...
+;;
+
+(defun yas/expand-snippet (content &optional start end expand-env)
+  "Expand snippet CONTENT at current point.
 
-(defun yas/expand-snippet (start end template &optional snippet-vars)
-  "Expand snippet at current point. Text between START and END
-will be deleted before inserting template."
+Text between START and END will be deleted before inserting
+template. EXPAND-ENV is are let-style variable to value bindings
+considered when expanding the snippet."
   (run-hooks 'yas/before-expand-snippet-hook)
-  (goto-char start)
 
-  ;; stacked expansion: shoosh the overlay modification hooks
-  ;; 
-  (let ((key (buffer-substring-no-properties start end))
-       (inhibit-modification-hooks t)
-       (column (current-column))
-       snippet)
+  ;; If a region is active, set `yas/selected-text'
+  (setq yas/selected-text
+        (when mark-active
+          (prog1 (buffer-substring-no-properties (region-beginning)
+                                                 (region-end))
+            (unless start (setq start (region-beginning))
+                    (unless end (setq end (region-end)))))))
 
-    ;; Delete the trigger key, this *does* get undo-recorded.
-    ;;
-    (delete-region start end)
-    
-    ;; Narrow the region down to the template, shoosh the
-    ;; `buffer-undo-list', and create the snippet, the new snippet
-    ;; updates its mirrors once, so we are left with some plain text.
-    ;; The undo action for deleting this plain text will get recorded
-    ;; at the end of this function.
-    (save-restriction
-      (narrow-to-region start start)
-      (let ((buffer-undo-list t))
-       ;; snippet creation might evaluate users elisp, which
-       ;; might generate errors, so we have to be ready to catch
-       ;; them mostly to make the undo information
-       ;;
-       (setq yas/start-column (save-restriction (widen) (current-column)))
-       (insert template)
-       (setq yas/deleted-text key)
-       (setq yas/selected-text (when mark-active key))
-       (setq snippet
-             (if snippet-vars
-                 (eval `(let ,(read snippet-vars)
-                          (yas/snippet-create (point-min) (point-max))))
-               (yas/snippet-create (point-min) (point-max))))))
-
-    ;; stacked-expansion: This checks for stacked expansion, save the
-    ;; `yas/previous-active-field' and advance its boudary.
-    ;;
-    (let ((existing-field (and yas/active-field-overlay
-                              (overlay-buffer yas/active-field-overlay)
-                              (overlay-get yas/active-field-overlay 'yas/field))))
-      (when existing-field
-       (setf (yas/snippet-previous-active-field snippet) existing-field)
-       (yas/advance-end-maybe existing-field (overlay-end yas/active-field-overlay))))
-    
-    ;; Exit the snippet immediately if no fields
-    ;;
-    (unless (yas/snippet-fields snippet)
-      (yas/exit-snippet snippet))
-    
-    ;; Push two undo actions: the deletion of the inserted contents of
-    ;; the new snippet (whitout the "key") followed by an apply of
-    ;; `yas/take-care-of-redo' on the newly inserted snippet boundaries
-    ;; 
-    (let ((start (overlay-start (yas/snippet-control-overlay snippet)))
-         (end (overlay-end (yas/snippet-control-overlay snippet))))
-      (push (cons start end) buffer-undo-list)
-      (push `(apply yas/take-care-of-redo ,start ,end ,snippet)
-           buffer-undo-list))
-    ;; Now, move to the first field
+  (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)))
+        (column (current-column))
+        snippet)
+    ;; Delete the region to delete, this *does* get undo-recorded.
     ;;
-    (let ((first-field (car (yas/snippet-fields snippet))))
-      (when first-field
-       (yas/move-to-field snippet first-field))))
-  (message "[yas] snippet expanded."))
+    (when (and to-delete
+               (> end start))
+      (delete-region start end)
+      (setq yas/deleted-text to-delete))
+
+    (cond ((listp content)
+           ;; x) This is a snippet-command
+           ;;
+           (yas/eval-lisp-no-saves content))
+          (t
+           ;; x) This is a snippet-snippet :-)
+           ;;
+           ;;    Narrow the region down to the content, shoosh the
+           ;;    `buffer-undo-list', and create the snippet, the new
+           ;;    snippet updates its mirrors once, so we are left with
+           ;;    some plain text.  The undo action for deleting this
+           ;;    plain text will get recorded at the end.
+           ;;
+           ;;    stacked expansion: also shoosh the overlay modification hooks
+           (save-restriction
+             (narrow-to-region start start)
+             (let ((inhibit-modification-hooks t)
+                   (buffer-undo-list t))
+               ;; snippet creation might evaluate users elisp, which
+               ;; might generate errors, so we have to be ready to catch
+               ;; them mostly to make the undo information
+               ;;
+               (setq yas/start-column (save-restriction (widen) (current-column)))
+
+               (setq snippet
+                     (if expand-env
+                         (eval `(let ,expand-env
+                                  (insert content)
+                                  (yas/snippet-create (point-min) (point-max))))
+                       (insert content)
+                       (yas/snippet-create (point-min) (point-max))))))
+
+           ;; stacked-expansion: This checks for stacked expansion, save the
+           ;; `yas/previous-active-field' and advance its boudary.
+           ;;
+           (let ((existing-field (and yas/active-field-overlay
+                                      (overlay-buffer yas/active-field-overlay)
+                                      (overlay-get yas/active-field-overlay 'yas/field))))
+             (when existing-field
+               (setf (yas/snippet-previous-active-field snippet) existing-field)
+               (yas/advance-end-maybe existing-field (overlay-end yas/active-field-overlay))))
+
+           ;; Exit the snippet immediately if no fields
+           ;;
+           (unless (yas/snippet-fields snippet)
+             (yas/exit-snippet snippet))
+
+           ;; Push two undo actions: the deletion of the inserted contents of
+           ;; the new snippet (without the "key") followed by an apply of
+           ;; `yas/take-care-of-redo' on the newly inserted snippet boundaries
+           ;;
+           ;; A small exception, if `yas/also-auto-indent-first-line'
+           ;; is t and `yas/indent' decides to indent the line to a
+           ;; point before the actual expansion point, undo would be
+           ;; messed up. We call the early point "newstart"".  case,
+           ;; and attempt to fix undo.
+           ;;
+           (let ((newstart (overlay-start (yas/snippet-control-overlay snippet)))
+                 (end (overlay-end (yas/snippet-control-overlay snippet))))
+             (when (< newstart start)
+               (push (cons (make-string (- start newstart) ? ) newstart) buffer-undo-list))
+             (push (cons newstart end) buffer-undo-list)
+             (push `(apply yas/take-care-of-redo ,start ,end ,snippet)
+                   buffer-undo-list))
+           ;; Now, schedule a move to the first field
+           ;;
+           (let ((first-field (car (yas/snippet-fields snippet))))
+             (when first-field
+               (sit-for 0) ;; fix issue 125
+               (yas/move-to-field snippet first-field)))
+           (message "[yas] snippet expanded.")
+           t))))
 
 (defun yas/take-care-of-redo (beg end snippet)
   "Commits SNIPPET, which in turn pushes an undo action for
@@ -2252,18 +3480,18 @@ After revival, push the `yas/take-care-of-redo' in the
   ;; try to revive the whole thing...
   ;;
   (let ((target-field (or (yas/snippet-active-field snippet)
-                         (car (yas/snippet-fields snippet)))))
+                          (car (yas/snippet-fields snippet)))))
     (when target-field
       (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay snippet beg end))
       (overlay-put (yas/snippet-control-overlay snippet) 'yas/snippet snippet)
-   
+
       (yas/move-to-field snippet target-field)
 
       (add-hook 'post-command-hook 'yas/post-command-handler nil t)
       (add-hook 'pre-command-hook 'yas/pre-command-handler t t)
-  
+
       (push `(apply yas/take-care-of-redo ,beg ,end ,snippet)
-           buffer-undo-list))))
+            buffer-undo-list))))
 
 (defun yas/snippet-create (begin end)
   "Creates a snippet from an template inserted between BEGIN and END.
@@ -2275,12 +3503,10 @@ Returns the newly created snippet."
 
     ;; Sort and link each field
     (yas/snippet-sort-fields snippet)
-    
-    ;; Update the mirrors for the first time
-    (yas/update-mirrors snippet)
 
     ;; Create keymap overlay for snippet
-    (setf (yas/snippet-control-overlay snippet) (yas/make-control-overlay snippet (point-min) (point-max)))
+    (setf (yas/snippet-control-overlay snippet)
+          (yas/make-control-overlay snippet (point-min) (point-max)))
 
     ;; Move to end
     (goto-char (point-max))
@@ -2288,49 +3514,59 @@ Returns the newly created snippet."
     ;; Setup hooks
     (add-hook 'post-command-hook 'yas/post-command-handler nil t)
     (add-hook 'pre-command-hook 'yas/pre-command-handler t t)
-    
-    snippet))
 
-;;; apropos adjacencies: Once the $-constructs bits like "$n" and
-;;; "${:n" are deleted in the recently expanded snippet, we might
-;;; actually have many fields, mirrors (and the snippet exit) in the
-;;; very same position in the buffer. Therefore we need to single-link
-;;; the fields-or-mirrors-or-exit, which I have called "fom",
-;;; according to their original positions in the buffer.
-;;;
-;;; Then we have operation `yas/advance-end-maybe' and
-;;; `yas/advance-start-maybe', which conditionally push the starts and
-;;; ends of these foms down the chain.
-;;;
-;;; This allows for like the printf with the magic ",":
-;;;
-;;;   printf ("${1:%s}\\n"${1:$(if (string-match "%" text) "," "\);")}  \
-;;;   $2${1:$(if (string-match "%" text) "\);" "")}$0
-;;;
+    snippet))
 
+\f
+;;; Apropos adjacencies and "fom's":
+;;
+;; Once the $-constructs bits like "$n" and "${:n" are deleted in the
+;; recently expanded snippet, we might actually have many fields,
+;; mirrors (and the snippet exit) in the very same position in the
+;; buffer. Therefore we need to single-link the
+;; fields-or-mirrors-or-exit, which I have called "fom", according to
+;; their original positions in the buffer.
+;;
+;; Then we have operation `yas/advance-end-maybe' and
+;; `yas/advance-start-maybe', which conditionally push the starts and
+;; ends of these foms down the chain.
+;;
+;; This allows for like the printf with the magic ",":
+;;
+;;   printf ("${1:%s}\\n"${1:$(if (string-match "%" text) "," "\);")}  \
+;;   $2${1:$(if (string-match "%" text) "\);" "")}$0
+;;
 (defun yas/fom-start (fom)
   (cond ((yas/field-p fom)
-        (yas/field-start fom))
-       ((yas/mirror-p fom)
-        (yas/mirror-start fom))
-       (t
-        (yas/exit-marker fom))))
+         (yas/field-start fom))
+        ((yas/mirror-p fom)
+         (yas/mirror-start fom))
+        (t
+         (yas/exit-marker fom))))
 
 (defun yas/fom-end (fom)
   (cond ((yas/field-p fom)
-        (yas/field-end fom))
-       ((yas/mirror-p fom)
-        (yas/mirror-end fom))
-       (t
-        (yas/exit-marker fom))))
+         (yas/field-end fom))
+        ((yas/mirror-p fom)
+         (yas/mirror-end fom))
+        (t
+         (yas/exit-marker fom))))
 
 (defun yas/fom-next (fom)
   (cond ((yas/field-p fom)
-        (yas/field-next fom))
-       ((yas/mirror-p fom)
-        (yas/mirror-next fom))
-       (t
-        (yas/exit-next fom))))
+         (yas/field-next fom))
+        ((yas/mirror-p fom)
+         (yas/mirror-next fom))
+        (t
+         (yas/exit-next fom))))
+
+(defun yas/fom-parent-field (fom)
+  (cond ((yas/field-p fom)
+         (yas/field-parent-field fom))
+        ((yas/mirror-p fom)
+         (yas/mirror-parent-field fom))
+        (t
+         nil)))
 
 (defun yas/calculate-adjacencies (snippet)
   "Calculate adjacencies for fields or mirrors of SNIPPET.
@@ -2338,29 +3574,48 @@ Returns the newly created 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)
-                              (> (yas/fom-start fom2) (yas/fom-start fom1)))
-        (yas/link-foms (fom1 fom2)
-                       (yas/fom-set-next-fom fom1 fom2)))
-    ;; make some yas/field, yas/mirror and yas/exit soup 
+                               (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)))
+    ;; make some yas/field, yas/mirror and yas/exit soup
     (let ((soup))
       (when (yas/snippet-exit snippet)
-       (push (yas/snippet-exit snippet) soup))
+        (push (yas/snippet-exit snippet) soup))
       (dolist (field (yas/snippet-fields snippet))
-       (push field soup)
-       (dolist (mirror (yas/field-mirrors field))
-         (push mirror soup)))
+        (push field soup)
+        (dolist (mirror (yas/field-mirrors field))
+          (push mirror soup)))
       (setq soup
-           (sort soup
-                 #'yas/compare-fom-begs))
+            (sort soup
+                  #'yas/compare-fom-begs))
       (when soup
-       (reduce #'yas/link-foms soup)))))
+        (reduce #'yas/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
+deleted."
+  (let ((min (point-min))
+        (max (point-max)))
+    (dolist (field (yas/snippet-fields snippet))
+      (when (and (<= (yas/field-start field) (yas/mirror-start mirror))
+                 (<= (yas/mirror-end mirror) (yas/field-end field))
+               (< min (yas/field-start field))
+               (< (yas/field-end field) max))
+          (setq min (yas/field-start field)
+                max (yas/field-end field))
+          (setf (yas/mirror-parent-field mirror) field)))))
 
 (defun yas/advance-end-maybe (fom newend)
   "Maybe advance FOM's end to NEWEND if it needs it.
@@ -2370,13 +3625,21 @@ If it does, also:
 * call `yas/advance-start-maybe' on FOM's next fom.
 
 * in case FOM is field call `yas/advance-end-maybe' on its parent
-  field"
-  (when (and fom (< (yas/fom-end fom) newend))
-    (set-marker (yas/fom-end fom) newend)
-    (yas/advance-start-maybe (yas/fom-next fom) newend)
-    (if (and (yas/field-p fom)
-            (yas/field-parent-field fom))
-       (yas/advance-end-maybe (yas/field-parent-field fom) newend))))
+  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.
+
+"
+  (cond ((and fom (< (yas/fom-end fom) newend))
+         (set-marker (yas/fom-end fom) newend)
+         (yas/advance-start-maybe (yas/fom-next fom) newend)
+         (let ((parent (yas/fom-parent-field fom)))
+           (when parent
+             (yas/advance-end-maybe parent newend))))
+        ((yas/exit-p fom)
+         (yas/advance-start-maybe (yas/fom-next fom) newend))))
 
 (defun yas/advance-start-maybe (fom newstart)
   "Maybe advance FOM's start to NEWSTART if it needs it.
@@ -2386,43 +3649,56 @@ If it does, also call `yas/advance-end-maybe' on FOM."
     (set-marker (yas/fom-start fom) newstart)
     (yas/advance-end-maybe fom newstart)))
 
+(defun yas/advance-end-of-parents-maybe (field newend)
+  "Like `yas/advance-end-maybe' but for parents."
+  (when (and field
+             (< (yas/field-end field) newend))
+    (set-marker (yas/field-end field) newend)
+    (yas/advance-end-of-parents-maybe (yas/field-parent-field field) newend)))
+
+(defvar yas/dollar-regions nil
+  "When expanding the snippet the \"parse-create\" functions add
+  cons cells to this var")
+
 (defun yas/snippet-parse-create (snippet)
   "Parse a recently inserted snippet template, creating all
 necessary fields, mirrors and exit points.
 
 Meant to be called in a narrowed buffer, does various passes"
-  (let ((parse-start (point))
-       (dollar-regions (list 'reg)))
-    ;; protect quote and backquote escapes
-    ;; 
-    (yas/protect-escapes nil '(?` ?'))
+  (let ((parse-start (point)))
+    ;; Reset the yas/dollar-regions
+    ;;
+    (setq yas/dollar-regions nil)
+    ;; protect escaped quote, backquotes and backslashes
+    ;;
+    (yas/protect-escapes nil '(?\\ ?` ?'))
     ;; replace all backquoted expressions
     ;;
     (goto-char parse-start)
     (yas/replace-backquotes)
-    ;; protect escapes again since previous stepds might have
-    ;; generated more characters needing escapinge
+    ;; protect escapes again since previous steps might have generated
+    ;; more characters needing escaping
     ;;
     (goto-char parse-start)
     (yas/protect-escapes)
     ;; parse fields with {}
-    ;; 
+    ;;
     (goto-char parse-start)
-    (yas/field-parse-create snippet dollar-regions)
+    (yas/field-parse-create snippet)
     ;; parse simple mirrors and fields
-    ;; 
+    ;;
     (goto-char parse-start)
-    (yas/simple-mirror-parse-create snippet dollar-regions)
+    (yas/simple-mirror-parse-create snippet)
     ;; parse mirror transforms
     ;;
     (goto-char parse-start)
-    (yas/transform-mirror-parse-create snippet dollar-regions)
+    (yas/transform-mirror-parse-create snippet)
     ;; calculate adjacencies of fields and mirrors
     ;;
     (yas/calculate-adjacencies snippet)
     ;; Delete $-constructs
     ;;
-    (yas/delete-regions (copy-list (rest dollar-regions)))
+    (yas/delete-regions yas/dollar-regions)
     ;; restore escapes
     ;;
     (goto-char parse-start)
@@ -2435,60 +3711,68 @@ Meant to be called in a narrowed buffer, does various passes"
     (goto-char parse-start)
     (yas/indent snippet)))
 
+(defun yas/indent-according-to-mode (snippet-markers)
+  "Indent current line according to mode, preserving
+SNIPPET-MARKERS."
+  ;;; Apropos indenting problems....
+  ;;
+  ;; `indent-according-to-mode' uses whatever `indent-line-function'
+  ;; is available. Some implementations of these functions delete text
+  ;; before they insert. If there happens to be a marker just after
+  ;; the text being deleted, the insertion actually happens after the
+  ;; marker, which misplaces it.
+  ;;
+  ;; This would also happen if we had used overlays with the
+  ;; `front-advance' property set to nil.
+  ;;
+  ;; This is why I have these `trouble-markers', they are the ones at
+  ;; they are the ones at the first non-whitespace char at the line
+  ;; (i.e. at `yas/real-line-beginning'. After indentation takes place
+  ;; we should be at the correct to restore them to. All other
+  ;; non-trouble-markers have been *pushed* and don't need special
+  ;; attention.
+  ;;
+  (goto-char (yas/real-line-beginning))
+  (let ((trouble-markers (remove-if-not #'(lambda (marker)
+                                            (= marker (point)))
+                                        snippet-markers)))
+    (save-restriction
+      (widen)
+      (condition-case err
+          (indent-according-to-mode)
+        (error (message "[yas] warning: yas/indent-according-to-mode habing problems running %s" indent-line-function)
+               nil)))
+    (mapc #'(lambda (marker)
+              (set-marker marker (point)))
+          trouble-markers)))
+
 (defun yas/indent (snippet)
-  (save-excursion
-    (while (re-search-forward "$>" nil t)
-      (delete-region (match-beginning 0) (match-end 0))
-      (when (not (eq yas/indent-line 'auto))
-       (indent-according-to-mode))))
-  (save-excursion
-    (cond ((eq yas/indent-line 'fixed)
-          (let* ((indent (if indent-tabs-mode
-                             (concat (make-string (/ column tab-width) ?\t)
-                                     (make-string (% column tab-width) ?\ ))
-                           (make-string (current-column) ?\ ))))
-            (goto-char (point-min))
-            (while (and (zerop (forward-line))
-                        (= (current-column) 0))
-              (insert indent))))
-         ((eq yas/indent-line 'auto)
-          (let ((end (set-marker (make-marker) (point-max)))
-                (indent-first-line-p yas/also-auto-indent-first-line)
-                (snippet-markers (yas/collect-snippet-markers snippet)))
-            (save-restriction
-              (widen)
-              ;; XXX: Here seems to be the indent problem:
-              ;;
-              ;; `indent-according-to-mode' uses whatever
-              ;; `indent-line-function' is available. Some
-              ;; implementations of these functions delete text
-              ;; before they insert. If there happens to be a marker
-              ;; just after the text being deleted, the insertion
-              ;; actually happens after the marker, which misplaces
-              ;; it.
-              ;;
-              ;; This would also happen if we had used overlays with
-              ;; the `front-advance' property set to nil.
-              ;;
-              (while (and (zerop (if indent-first-line-p
-                                     (prog1
-                                         (forward-line 0)
-                                       (setq indent-first-line-p nil))
-                                   (forward-line 1)))
-                          (not (eobp))
-                          (<= (point) end))
-                (goto-char (yas/real-line-beginning))
-                (let ((trouble-markers (remove-if-not #'(lambda (marker)
-                                                          (= marker (point)))
-                                                      snippet-markers)))
-                      (indent-according-to-mode)
-                      (mapc #'(lambda (marker)
-                                (set-marker marker (point)))
-                            trouble-markers)
-                  (indent-according-to-mode)))
-              (set-marker end nil))))
-         (t
-          nil))))
+  (let ((snippet-markers (yas/collect-snippet-markers snippet)))
+    ;; Look for those $>
+    (save-excursion
+      (while (re-search-forward "$>" nil t)
+        (delete-region (match-beginning 0) (match-end 0))
+        (when (not (eq yas/indent-line 'auto))
+          (yas/indent-according-to-mode snippet-markers))))
+    ;; Now do stuff for 'fixed and 'auto
+    (save-excursion
+      (cond ((eq yas/indent-line 'fixed)
+             (while (and (zerop (forward-line))
+                         (zerop (current-column)))
+               (indent-to-column column)))
+            ((eq yas/indent-line 'auto)
+             (let ((end (set-marker (make-marker) (point-max)))
+                   (indent-first-line-p yas/also-auto-indent-first-line))
+               (while (and (zerop (if indent-first-line-p
+                                      (prog1
+                                          (forward-line 0)
+                                        (setq indent-first-line-p nil))
+                                    (forward-line 1)))
+                           (not (eobp))
+                           (<= (point) end))
+                 (yas/indent-according-to-mode snippet-markers))))
+            (t
+             nil)))))
 
 (defun yas/collect-snippet-markers (snippet)
   "Make a list of all the markers used by SNIPPET."
@@ -2497,12 +3781,12 @@ Meant to be called in a narrowed buffer, does various passes"
       (push (yas/field-start field) markers)
       (push (yas/field-end field) markers)
       (dolist (mirror (yas/field-mirrors field))
-       (push (yas/mirror-start mirror) markers)
-       (push (yas/mirror-end mirror) markers)))
+        (push (yas/mirror-start mirror) markers)
+        (push (yas/mirror-end mirror) markers)))
     (let ((snippet-exit (yas/snippet-exit snippet)))
       (when (and snippet-exit
-                (marker-buffer (yas/exit-marker snippet-exit)))
-       (push (yas/exit-marker snippet-exit) markers)))
+                 (marker-buffer (yas/exit-marker snippet-exit)))
+        (push (yas/exit-marker snippet-exit) markers)))
     markers))
 
 (defun yas/real-line-beginning ()
@@ -2522,42 +3806,42 @@ Meant to be called in a narrowed buffer, does various passes"
 
 With optional string TEXT do it in string instead of buffer."
   (let ((changed-text text)
-       (text-provided-p text))
+        (text-provided-p text))
     (mapc #'(lambda (escaped)
-             (setq changed-text
-                   (yas/replace-all (concat "\\" (char-to-string escaped))
-                                    (yas/escape-string escaped)
-                                    (when text-provided-p changed-text))))
-         (or escaped yas/escaped-characters))
+              (setq changed-text
+                    (yas/replace-all (concat "\\" (char-to-string escaped))
+                                     (yas/escape-string escaped)
+                                     (when text-provided-p changed-text))))
+          (or escaped yas/escaped-characters))
     changed-text))
-  
+
 (defun yas/restore-escapes (&optional text escaped)
   "Restore all escaped characters from their numeric ASCII value.
 
 With optional string TEXT do it in string instead of the buffer."
   (let ((changed-text text)
-       (text-provided-p text))
+        (text-provided-p text))
     (mapc #'(lambda (escaped)
-             (setq changed-text
-                   (yas/replace-all (yas/escape-string escaped)
-                                    (char-to-string escaped)
-                                    (when text-provided-p changed-text))))
-         (or escaped yas/escaped-characters))
+              (setq changed-text
+                    (yas/replace-all (yas/escape-string escaped)
+                                     (char-to-string escaped)
+                                     (when text-provided-p changed-text))))
+          (or escaped yas/escaped-characters))
     changed-text))
 
 (defun yas/replace-backquotes ()
   "Replace all the \"`(lisp-expression)`\"-style expression
   with their evaluated value"
   (while (re-search-forward yas/backquote-lisp-expression-regexp nil t)
-  (let ((transformed (yas/eval-string (yas/restore-escapes (match-string 1)))))
-    (goto-char (match-end 0))
-    (when transformed (insert transformed))
-    (delete-region (match-beginning 0) (match-end 0)))))
+    (let ((transformed (yas/eval-lisp (yas/read-lisp (yas/restore-escapes (match-string 1))))))
+      (goto-char (match-end 0))
+      (when transformed (insert transformed))
+      (delete-region (match-beginning 0) (match-end 0)))))
 
 (defun yas/scan-sexps (from count)
   (condition-case err
       (with-syntax-table (standard-syntax-table)
-       (scan-sexps from count))
+        (scan-sexps from count))
     (error
      nil)))
 
@@ -2567,318 +3851,430 @@ With optional string TEXT do it in string instead of the buffer."
     (set-marker-insertion-type marker nil)
     marker))
 
-(defun yas/add-to-list (l e)
-  (setf (cdr l)
-       (cons e (cdr l))))
-
-(defun yas/field-parse-create (snippet dollar-regions &optional parent-field)
-  "Parse most field expression, except for the simple one \"$n\".
+(defun yas/field-parse-create (snippet &optional parent-field)
+  "Parse most field expressions, 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;
+  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.
 
 When multiple expressions are found, only the last one counts."
+  ;;
   (save-excursion
-  (while (re-search-forward yas/field-regexp nil t)
-    (let* ((real-match-end-0 (yas/scan-sexps (1+ (match-beginning 0)) 1))
-          (number (and (match-string-no-properties 1)
-                       (string-to-number (match-string-no-properties 1))))
-          (brand-new-field (and real-match-end-0
-                                (not (save-match-data
-                                       (eq (string-match "$[ \t\n]*(" (match-string-no-properties 2)) 0)))
-                                (not (and number (zerop number)))
-                                (yas/make-field number
-                                                (yas/make-marker (match-beginning 2))
-                                                (yas/make-marker (1- real-match-end-0))
-                                                parent-field))))
-      (when brand-new-field
-       (yas/add-to-list dollar-regions
-                        (cons (1- real-match-end-0) real-match-end-0))
-       (yas/add-to-list dollar-regions
-                        (cons (match-beginning 0) (match-beginning 2))) 
-       (push brand-new-field (yas/snippet-fields snippet))
-       (save-excursion
-         (save-restriction
-           (narrow-to-region (yas/field-start brand-new-field) (yas/field-end brand-new-field))
-           (goto-char (point-min))
-           (yas/field-parse-create snippet dollar-regions brand-new-field)))))))
+    (while (re-search-forward yas/field-regexp nil t)
+      (let* ((real-match-end-0 (yas/scan-sexps (1+ (match-beginning 0)) 1))
+             (number (and (match-string-no-properties 1)
+                          (string-to-number (match-string-no-properties 1))))
+             (brand-new-field (and real-match-end-0
+                                   ;; break if on "$(" immediately
+                                   ;; after the ":", this will be
+                                   ;; caught as a mirror with
+                                   ;; transform later.
+                                   (not (save-match-data
+                                          (eq (string-match "$[ \t\n]*("
+                                                            (match-string-no-properties 2)) 0)))
+                                   ;; allow ${0: some exit text}
+                                   ;; (not (and number (zerop number)))
+                                   (yas/make-field number
+                                                   (yas/make-marker (match-beginning 2))
+                                                   (yas/make-marker (1- real-match-end-0))
+                                                   parent-field))))
+        (when brand-new-field
+          (goto-char real-match-end-0)
+          (push (cons (1- real-match-end-0) real-match-end-0)
+                yas/dollar-regions)
+          (push (cons (match-beginning 0) (match-beginning 2))
+                yas/dollar-regions)
+          (push brand-new-field (yas/snippet-fields snippet))
+          (save-excursion
+            (save-restriction
+              (narrow-to-region (yas/field-start brand-new-field) (yas/field-end brand-new-field))
+              (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
+  ;; primary field transformations
+  ;;
   (when parent-field
-  (save-excursion
-    (while (re-search-forward yas/multi-dollar-lisp-expression-regexp nil t)
-      (let* ((real-match-end-1 (yas/scan-sexps (match-beginning 1) 1)))
-       (when real-match-end-1
-         (let ((lisp-expression-string (buffer-substring-no-properties (match-beginning 1) real-match-end-1)))
-           (setf (yas/field-transform parent-field) (yas/restore-escapes lisp-expression-string)))
-         (yas/add-to-list dollar-regions
-                          (cons (match-beginning 0) real-match-end-1))))))))
-
-(defun yas/transform-mirror-parse-create (snippet dollar-regions)
+    (save-excursion
+      (while (re-search-forward yas/multi-dollar-lisp-expression-regexp nil t)
+        (let* ((real-match-end-1 (yas/scan-sexps (match-beginning 1) 1)))
+          ;; commit the primary field transformation if:
+          ;;
+          ;; 1. we don't find it in yas/dollar-regions (a subnested
+          ;; field) might have already caught it.
+          ;;
+          ;; 2. we really make sure we have either two '$' or some
+          ;; text and a '$' after the colon ':'. This is a FIXME: work
+          ;; my regular expressions and end these ugly hacks.
+          ;;
+          (when (and real-match-end-1
+                     (not (member (cons (match-beginning 0)
+                                        real-match-end-1)
+                                  yas/dollar-regions))
+                     (not (eq ?:
+                              (char-before (1- (match-beginning 1))))))
+            (let ((lisp-expression-string (buffer-substring-no-properties (match-beginning 1)
+                                                                          real-match-end-1)))
+              (setf (yas/field-transform parent-field)
+                    (yas/read-lisp (yas/restore-escapes lisp-expression-string))))
+            (push (cons (match-beginning 0) real-match-end-1)
+                  yas/dollar-regions)))))))
+
+(defun yas/transform-mirror-parse-create (snippet)
   "Parse the \"${n:$(lisp-expression)}\" mirror transformations."
   (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)))
-        (field (and number
-                    (not (zerop number))
-                    (yas/snippet-find-field snippet number))))
-    (when (and real-match-end-0
-              field)
-      (push (yas/make-mirror (yas/make-marker (match-beginning 0))
-                            (yas/make-marker (match-beginning 0))
-                            (yas/restore-escapes (buffer-substring-no-properties (match-beginning 2)
-                                                                                 (1- real-match-end-0))))
-           (yas/field-mirrors field))
-      (yas/add-to-list dollar-regions
-                      (cons (match-beginning 0) real-match-end-0))))))
-
-(defun yas/simple-mirror-parse-create (snippet dollar-regions)
-  "Parse the simple \"$n\" mirrors and the exit-marker."
+    (let* ((real-match-end-0 (yas/scan-sexps (1+ (match-beginning 0)) 1))
+           (number (string-to-number (match-string-no-properties 1)))
+           (field (and number
+                       (not (zerop number))
+                       (yas/snippet-find-field snippet number)))
+           (brand-new-mirror
+            (and real-match-end-0
+                 field
+                 (yas/make-mirror (yas/make-marker (match-beginning 0))
+                                  (yas/make-marker (match-beginning 0))
+                                  (yas/read-lisp
+                                   (yas/restore-escapes
+                                    (buffer-substring-no-properties (match-beginning 2)
+                                                                    (1- real-match-end-0))))))))
+      (when brand-new-mirror
+        (push brand-new-mirror
+              (yas/field-mirrors field))
+        (yas/calculate-mirrors-in-fields snippet brand-new-mirror)
+        (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."
   (while (re-search-forward yas/simple-mirror-regexp nil t)
-  (let ((number (string-to-number (match-string-no-properties 1))))
-    (cond ((zerop number)
-            
-          (setf (yas/snippet-exit snippet)
-                (yas/make-exit (yas/make-marker (match-end 0)))) 
-          (save-excursion
-            (goto-char (match-beginning 0))
-            (when (and yas/wrap-around-region yas/selected-text)
-              (insert yas/selected-text))
-            (yas/add-to-list dollar-regions
-                             (cons (point) (yas/exit-marker (yas/snippet-exit snippet))))))
-         (t
-          (let ((field (yas/snippet-find-field snippet number)))
-            (if field
-                (push (yas/make-mirror (yas/make-marker (match-beginning 0))
-                                       (yas/make-marker (match-beginning 0))
-                                       nil)
-                      (yas/field-mirrors field))
-              (push (yas/make-field number
-                                    (yas/make-marker (match-beginning 0))
-                                    (yas/make-marker (match-beginning 0))
-                                    nil)
-                    (yas/snippet-fields snippet))))
-          (yas/add-to-list dollar-regions
-                           (cons (match-beginning 0) (match-end 0))))))))
+    (let ((number (string-to-number (match-string-no-properties 1))))
+      (cond ((zerop number)
+
+             (setf (yas/snippet-exit snippet)
+                   (yas/make-exit (yas/make-marker (match-end 0))))
+             (save-excursion
+               (goto-char (match-beginning 0))
+               (when yas/wrap-around-region
+                 (cond (yas/selected-text
+                        (insert yas/selected-text))
+                       ((and (eq yas/wrap-around-region 'cua)
+                             cua-mode
+                             (get-register ?0))
+                        (insert (prog1 (get-register ?0)
+                                  (set-register ?0 nil))))))
+               (push (cons (point) (yas/exit-marker (yas/snippet-exit snippet)))
+                     yas/dollar-regions)))
+            (t
+             (let ((field (yas/snippet-find-field snippet number)))
+               (if field
+                   (let ((brand-new-mirror (yas/make-mirror
+                                            (yas/make-marker (match-beginning 0))
+                                            (yas/make-marker (match-beginning 0))
+                                            nil)))
+                     (push brand-new-mirror
+                           (yas/field-mirrors field))
+                     (yas/calculate-mirrors-in-fields snippet brand-new-mirror))
+                 (push (yas/make-field number
+                                       (yas/make-marker (match-beginning 0))
+                                       (yas/make-marker (match-beginning 0))
+                                       nil)
+                       (yas/snippet-fields snippet))))
+             (push (cons (match-beginning 0) (match-end 0))
+                   yas/dollar-regions))))))
 
 (defun yas/delete-regions (regions)
   "Sort disjuct REGIONS by start point, then delete from the back."
   (mapc #'(lambda (reg)
-           (delete-region (car reg) (cdr reg)))
-       (sort regions
-             #'(lambda (r1 r2)
-                 (>= (car r1) (car r2))))))
+            (delete-region (car reg) (cdr reg)))
+        (sort regions
+              #'(lambda (r1 r2)
+                  (>= (car r1) (car r2))))))
 
 (defun yas/update-mirrors (snippet)
   "Updates all the mirrors of SNIPPET."
   (save-excursion
-  (dolist (field (yas/snippet-fields snippet))
-    (dolist (mirror (yas/field-mirrors field))
-      ;; stacked expansion: I added an `inhibit-modification-hooks'
-      ;; here, for safety, may need to remove if we the mechanism is
-      ;; altered.
-      ;; 
-      (let ((inhibit-modification-hooks t))
-       (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)))))))
+    (let* ((fields (copy-list (yas/snippet-fields snippet)))
+           (field (car fields)))
+      (while field
+        (dolist (mirror (yas/field-mirrors field))
+          ;; stacked expansion: I added an `inhibit-modification-hooks'
+          ;; here, for safety, may need to remove if we the mechanism is
+          ;; altered.
+          ;;
+          (let ((inhibit-modification-hooks t)
+                (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))))))
 
 (defun yas/mirror-update-display (mirror field)
   "Update MIRROR according to FIELD (and mirror transform)."
-  (let ((reflection (or (yas/apply-transform mirror field)
-                       (yas/field-text-for-display field))))
-  (when (and reflection
-            (not (string= reflection (buffer-substring-no-properties (yas/mirror-start mirror) (yas/mirror-end mirror)))))
-    (goto-char (yas/mirror-start mirror))
-    (insert reflection)
-    (if (> (yas/mirror-end mirror) (point))
-       (delete-region (point) (yas/mirror-end mirror))
-      (set-marker (yas/mirror-end mirror) (point))
-      (yas/advance-start-maybe (yas/mirror-next mirror) (point))))))
+
+  (let* ((mirror-parent-field (yas/mirror-parent-field mirror))
+         (reflection (and (not (and mirror-parent-field
+                                    (yas/field-modified-p mirror-parent-field)))
+                          (or (yas/apply-transform mirror field)
+                              (yas/field-text-for-display field)))))
+    (when (and reflection
+               (not (string= reflection (buffer-substring-no-properties (yas/mirror-start mirror)
+                                                                        (yas/mirror-end mirror)))))
+      (goto-char (yas/mirror-start mirror))
+      (insert reflection)
+      (if (> (yas/mirror-end mirror) (point))
+          (delete-region (point) (yas/mirror-end mirror))
+        (set-marker (yas/mirror-end mirror) (point))
+        (yas/advance-start-maybe (yas/mirror-next mirror) (point))
+        ;; 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"
   (when (yas/field-transform field)
-  (let ((inhibit-modification-hooks t)
-       (transformed (yas/apply-transform field field))
-       (point (point)))
-    (when (and transformed
-              (not (string= transformed (buffer-substring-no-properties (yas/field-start field) (yas/field-end field)))))
-      (setf (yas/field-modified-p field) t)
-      (goto-char (yas/field-start field))
-      (insert transformed)
-      (if (> (yas/field-end field) (point))
-         (delete-region (point) (yas/field-end field))
-       (set-marker (yas/field-end field) (point))
-       (yas/advance-start-maybe (yas/field-next field) (point)))
-      t))))
+    (let ((inhibit-modification-hooks t)
+          (transformed (yas/apply-transform field field))
+          (point (point)))
+      (when (and transformed
+                 (not (string= transformed (buffer-substring-no-properties (yas/field-start field)
+                                                                           (yas/field-end field)))))
+        (setf (yas/field-modified-p field) t)
+        (goto-char (yas/field-start field))
+        (insert transformed)
+        (if (> (yas/field-end field) (point))
+            (delete-region (point) (yas/field-end field))
+          (set-marker (yas/field-end field) (point))
+          (yas/advance-start-maybe (yas/field-next field) (point)))
+        t))))
+
+\f
+;;; Pre- and post-command hooks:
+
+(defvar yas/post-command-runonce-actions nil
+  "List of actions to run once  `post-command-hook'.
+
+Each element of this list looks like (FN . ARGS) where FN is
+called with ARGS as its arguments after the currently executing
+snippet command.
+
+After all actions have been run, this list is emptied, and after
+that the rest of `yas/post-command-handler' runs.")
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Pre- and post-command hooks
-;; 
 (defun yas/pre-command-handler () )
 
 (defun yas/post-command-handler ()
   "Handles various yasnippet conditions after each command."
+  (when yas/post-command-runonce-actions
+    (condition-case err
+        (mapc #'(lambda (fn-and-args)
+                  (apply (car fn-and-args)
+                         (cdr fn-and-args)))
+              yas/post-command-runonce-actions)
+      (error (message "[yas] problem running `yas/post-command-runonce-actions'!")))
+    (setq yas/post-command-runonce-actions nil))
   (cond (yas/protection-violation
-        (goto-char yas/protection-violation)
-        (setq yas/protection-violation nil))
-       ((eq 'undo this-command)
-        ;;
-        ;; After undo revival the correct field is sometimes not
-        ;; restored correctly, this condition handles that
-        ;;
-        (let* ((snippet (car (yas/snippets-at-point)))
-               (target-field (and snippet
-                                  (find-if-not #'(lambda (field)
-                                                   (yas/field-probably-deleted-p snippet field))
-                                               (remove nil
-                                                       (cons (yas/snippet-active-field snippet)
-                                                             (yas/snippet-fields snippet)))))))
-          (when target-field
-            (yas/move-to-field snippet target-field))))
-       ((not (yas/undo-in-progress))
-        ;; When not in an undo, check if we must commit the snippet (use exited it).
-        (yas/check-commit-snippet))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Debug functions.  Use (or change) at will whenever needed.
-;;
-;; some useful debug code for looking up snippet tables
-;;
-;; (insert (pp
-;; (let ((shit))
-;;   (maphash #'(lambda (k v)
-;;            (push k shit))
-;;        (yas/snippet-table-hash (gethash 'ruby-mode yas/snippet-tables)))
-;;   shit)))
-;;
-
-(defun yas/debug-some-vars ()
-  "Debug snippets, fields, mirrors and the `buffer-undo-list'."
-  (interactive)
-  (with-output-to-temp-buffer "*YASnippet trace*"
-    (princ "Interesting YASnippet vars: \n\n")
-
-    (princ (format "\nPost command hook: %s\n" post-command-hook))
-    (princ (format "\nPre  command hook: %s\n" pre-command-hook))
-
-    (princ (format "%s live snippets in total\n" (length (yas/snippets-at-point (quote all-snippets)))))
-    (princ (format "%s overlays in buffer:\n\n" (length (overlays-in (point-min) (point-max)))))
-    (princ (format "%s live snippets at point:\n\n" (length (yas/snippets-at-point))))
-    
-    
-    (dolist (snippet (yas/snippets-at-point))
-      (princ (format "\tsid: %d control overlay from %d to %d\n"
-                    (yas/snippet-id snippet)
-                    (overlay-start (yas/snippet-control-overlay snippet))
-                    (overlay-end (yas/snippet-control-overlay snippet))))
-      (princ (format "\tactive field: %d from %s to %s covering \"%s\"\n"
-                    (yas/field-number (yas/snippet-active-field snippet))
-                    (marker-position (yas/field-start (yas/snippet-active-field snippet)))
-                    (marker-position (yas/field-end (yas/snippet-active-field snippet)))
-                    (buffer-substring-no-properties (yas/field-start (yas/snippet-active-field snippet)) (yas/field-end (yas/snippet-active-field snippet)))))
-      (when (yas/snippet-exit snippet)
-       (princ (format "\tsnippet-exit: at %s next: %s\n"
-                      (yas/exit-marker (yas/snippet-exit snippet))
-                      (yas/exit-next (yas/snippet-exit snippet)))))
-      (dolist (field (yas/snippet-fields snippet))
-       (princ (format "\tfield: %d from %s to %s covering \"%s\" next: %s\n"
-                      (yas/field-number field)
-                      (marker-position (yas/field-start field))
-                      (marker-position (yas/field-end field))
-                      (buffer-substring-no-properties (yas/field-start field) (yas/field-end field))
-                      (yas/debug-format-fom-concise (yas/field-next field))))
-       (dolist (mirror (yas/field-mirrors field))
-         (princ (format "\t\tmirror: from %s to %s covering \"%s\" next: %s\n"
-                        (marker-position (yas/mirror-start mirror))
-                        (marker-position (yas/mirror-end mirror))
-                        (buffer-substring-no-properties (yas/mirror-start mirror) (yas/mirror-end mirror))
-                        (yas/debug-format-fom-concise (yas/mirror-next mirror)))))))
-
-    (princ (format "\nUndo is %s and point-max is %s.\n"
-                  (if (eq buffer-undo-list t)
-                      "DISABLED"
-                    "ENABLED")
-                  (point-max)))
-    (unless (eq buffer-undo-list t)
-      (princ (format "Undpolist has %s elements. First 10 elements follow:\n" (length buffer-undo-list)))
-      (let ((first-ten (subseq buffer-undo-list 0 19)))
-       (dolist (undo-elem first-ten)
-         (princ (format "%2s:  %s\n" (position undo-elem first-ten) (truncate-string-to-width (format "%s" undo-elem) 70))))))))
-
-(defun yas/debug-format-fom-concise (fom)
-  (when fom
-    (cond ((yas/field-p fom)
-          (format "field %d from %d to %d"
-                  (yas/field-number fom)
-                  (marker-position (yas/field-start fom))
-                  (marker-position (yas/field-end fom))))
-         ((yas/mirror-p fom)
-          (format "mirror from %d to %d"
-                  (marker-position (yas/mirror-start fom))
-                  (marker-position (yas/mirror-end fom))))
-         (t
-          (format "snippet exit at %d"
-                  (marker-position (yas/fom-start fom)))))))
-         
-
-(defun yas/exterminate-package ()
-  (interactive)
-  (yas/global-mode -1)
-  (yas/minor-mode -1)
-  (mapatoms #'(lambda (atom)
-                (when (string-match "yas/" (symbol-name atom))
-                  (unintern atom)))))
-
-(defun yas/debug-test (&optional quiet)
-  (interactive "P")
-  (yas/load-directory (or (and (listp yas/root-directory)
-                              (first yas/root-directory))
-                         yas/root-directory
-                         "~/Source/yasnippet/snippets/"))
-  (set-buffer (switch-to-buffer "*YAS TEST*"))
-  (mapc #'yas/commit-snippet (yas/snippets-at-point 'all-snippets))
-  (erase-buffer)
-  (setq buffer-undo-list nil)
-  (setq undo-in-progress nil)
-  (snippet-mode)
-  (yas/minor-mode 1)
-  (let ((abbrev))
-  (setq abbrev "$f")
-  (insert abbrev))
-  (unless quiet
-    (add-hook 'post-command-hook 'yas/debug-some-vars 't 'local)))
-
-(provide 'yasnippet)
+         (goto-char yas/protection-violation)
+         (setq yas/protection-violation nil))
+        ((eq 'undo this-command)
+         ;;
+         ;; After undo revival the correct field is sometimes not
+         ;; restored correctly, this condition handles that
+         ;;
+         (let* ((snippet (car (yas/snippets-at-point)))
+                (target-field (and snippet
+                                   (find-if-not #'(lambda (field)
+                                                    (yas/field-probably-deleted-p snippet field))
+                                                (remove nil
+                                                        (cons (yas/snippet-active-field snippet)
+                                                              (yas/snippet-fields snippet)))))))
+           (when target-field
+             (yas/move-to-field snippet target-field))))
+        ((not (yas/undo-in-progress))
+         ;; When not in an undo, check if we must commit the snippet
+         ;; (user exited it).
+         (yas/check-commit-snippet))))
+\f
+;;; Fancy docs:
+
+(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'."
+  (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))))
+                  (or (and fallback
+                           (format " call command `%s'." (pp-to-string fallback)))
+                      " do nothing.")))
+               ((eq yas/fallback-behavior 'return-nil)
+                ", do nothing.")
+               (t
+                ", defer to `yas/fallback-behaviour' :-)"))))
+    (concat "Expand a snippet before point. If no snippet
+expansion is possible,"
+            fallback-description
+            "\n\nOptional argument FIELD is for non-interactive use and is an
+object satisfying `yas/field-p' to restrict the expansion to.")))
+
+(put 'yas/expand-from-keymap  'function-documentation '(yas/expand-from-keymap-doc))
+(defun yas/expand-from-keymap-doc ()
+  "A doc synthethizer 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)
+            (let* ((vec (this-single-command-keys))
+                   (templates (mapcan #'(lambda (table)
+                                          (yas/fetch table vec))
+                                      (yas/get-snippet-tables)))
+                   (yas/direct-keymaps nil)
+                   (fallback (key-binding vec)))
+              (concat "In this case, "
+                      (when templates
+                        (concat "these snippets are bound to this key:\n"
+                                (yas/template-pretty-list templates)
+                                "\n\nIf none of these expands, "))
+                      (or (and fallback
+                               (format "fallback `%s' will be called." (pp-to-string fallback)))
+                          "no fallback keybinding is called."))))))
+
+(defun yas/template-pretty-list (templates)
+  (let ((acc)
+        (yas/buffer-local-condition 'always))
+    (dolist (plate templates)
+      (setq acc (concat acc "\n*) "
+                        (propertize (concat "\\\\snippet `" (car plate) "'")
+                                    'yasnippet (cdr plate)))))
+    acc))
+
+(define-button-type 'help-snippet-def
+  :supertype 'help-xref
+  'help-function (lambda (template) (yas/visit-snippet-file-1 template))
+  'help-echo (purecopy "mouse-2, RET: find snippets's definition"))
+
+(defun yas/snippet-description-finish-runonce ()
+  "Final adjustments for the help buffer when snippets are concerned."
+  (yas/create-snippet-xrefs)
+  (remove-hook 'temp-buffer-show-hook 'yas/snippet-description-finish-runonce))
+
+(defun yas/create-snippet-xrefs ()
+  (save-excursion
+    (goto-char (point-min))
+    (while (search-forward-regexp "\\\\\\\\snippet[ \s\t]+`\\([^']+\\)'" nil t)
+      (let ((template (get-text-property (match-beginning 1)
+                                         'yasnippet)))
+        (when template
+          (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
+;;; Some hacks:
+;; `locate-dominating-file' is added for compatibility in emacs < 23
+(unless (or (eq emacs-major-version 23)
+            (fboundp 'locate-dominating-file))
+  (defvar locate-dominating-stop-dir-regexp
+    "\\`\\(?:[\\/][\\/][^\\/]+[\\/]\\|/\\(?:net\\|afs\\|\\.\\.\\.\\)/\\)\\'"
+    "Regexp of directory names which stop the search in `locate-dominating-file'.
+Any directory whose name matches this regexp will be treated like
+a kind of root directory by `locate-dominating-file' which will stop its search
+when it bumps into it.
+The default regexp prevents fruitless and time-consuming attempts to find
+special files in directories in which filenames are interpreted as hostnames,
+or mount points potentially requiring authentication as a different user.")
+
+  (defun locate-dominating-file (file name)
+    "Look up the directory hierarchy from FILE for a file named NAME.
+Stop at the first parent directory containing a file NAME,
+and return the directory.  Return nil if not found."
+    ;; We used to use the above locate-dominating-files code, but the
+    ;; directory-files call is very costly, so we're much better off doing
+    ;; multiple calls using the code in here.
+    ;;
+    ;; Represent /home/luser/foo as ~/foo so that we don't try to look for
+    ;; `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)
+                      ;; FIXME: Disabled this heuristic because it is sometimes
+                      ;; inappropriate.
+                      ;; As a heuristic, we stop looking up the hierarchy of
+                      ;; directories as soon as we find a directory belonging
+                      ;; to another user.  This should save us from looking in
+                      ;; things like /net and /afs.  This assumes that all the
+                      ;; files inside a project belong to the same user.
+                      ;; (let ((prev-user user))
+                      ;;   (setq user (nth 2 (file-attributes file)))
+                      ;;   (and prev-user (not (equal user prev-user))))
+                      (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
+                                       (directory-file-name file))))
+               (setq file nil))))
+      root)))
+
+;; `c-neutralize-syntax-in-CPP` sometimes fires "End of Buffer" error
+;; (when it execute forward-char) and interrupt the after change
+;; hook. Thus prevent the insert-behind hook of yasnippet to be
+;; invoked. Here's a way to reproduce it:
+
+;; # open a *new* Emacs.
+;; # load yasnippet.
+;; # open a *new* .cpp file.
+;; # input "inc" and press TAB to expand the snippet.
+;; # select the `#include <...>` snippet.
+;; # type inside `<>`
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Monkey patching for other functions that's causing
-;; problems to yasnippet. For details on why I patch
-;; those functions, refer to
-;;   http://code.google.com/p/yasnippet/wiki/MonkeyPatching
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 (defadvice c-neutralize-syntax-in-CPP
   (around yas-mp/c-neutralize-syntax-in-CPP activate)
   "Adviced `c-neutralize-syntax-in-CPP' to properly
 handle the end-of-buffer error fired in it by calling
 `forward-char' at the end of buffer."
   (condition-case err
-  ad-do-it
-  (error (message (error-message-string err)))))
+      ad-do-it
+    (error (message (error-message-string err)))))
 
 ;; disable c-electric-* serial command in YAS fields
 (add-hook 'c-mode-common-hook
           '(lambda ()
-         (make-variable-buffer-local 'yas/keymap)
-         (dolist (k '(":" ">" ";" "<" "{" "}"))
-           (define-key yas/keymap
-             k 'self-insert-command))))
+             (dolist (k '(":" ">" ";" "<" "{" "}"))
+               (define-key (symbol-value (make-local-variable 'yas/keymap))
+                 k 'self-insert-command))))
 
+(provide 'yasnippet)
 
 ;;; yasnippet.el ends here