]> code.delx.au - gnu-emacs-elpa/blobdiff - yasnippet.el
bugfix: fix #245
[gnu-emacs-elpa] / yasnippet.el
index d7920652e8ccdc75d36121da832eb1e62ff01fd3..305cae913aabd4e25c1360f91054fde2aef6cf96 100644 (file)
@@ -6,9 +6,9 @@
 ;; Authors: pluskid <pluskid@gmail.com>, joaotavora <joaotavora@gmail.com>
 ;; Version: 0.7.0
 ;; Package-version: 0.7.0
-;; X-URL: http://code.google.com/p/yasnippet/
+;; X-URL: http://github.com/capitaomorte/yasnippet
 ;; Keywords: convenience, emulation
-;; URL: http://code.google.com/p/yasnippet/
+;; URL: http://github.com/capitaomorte/yasnippet
 ;; EmacsWiki: YaSnippetMode
 
 ;; This file is free software; you can redistribute it and/or modify
 
 ;; Basic steps to setup:
 ;;
-;;   1. In your .emacs file:
-;;        (add-to-list 'load-path "/dir/to/yasnippet.el")
-;;        (require 'yasnippet)
-;;   2. Place the `snippets' directory somewhere.  E.g: ~/.emacs.d/snippets
-;;   3. In your .emacs file
-;;        (setq yas/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
-;;        M-x yas/global-mode
+;;    (add-to-list 'load-path
+;;                 "~/.emacs.d/plugins/yasnippet")
+;;    (require 'yasnippet)
+;;    (yas/global-mode 1)
 ;;
-;;   Steps 4. and 5. are optional, you don't have to use the minor
-;;   mode to use YASnippet.
 ;;
 ;;   Interesting variables are:
 ;;
 ;;   `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/
+;;      http://github.com/capitaomorte/yasnippet
 
 ;;; Code:
 
 (require 'cl)
-(require 'assoc)
 (require 'easymenu)
 (require 'help-mode)
 
@@ -190,6 +180,7 @@ as the default for storing the user's new snippets."
                          (equal old new))
                (yas/reload-all)))))
 (defun yas/snippet-dirs ()
+  "Returns `yas/snippet-dirs' (which see) as a list."
   (if (listp yas/snippet-dirs) yas/snippet-dirs (list yas/snippet-dirs)))
 (defvaralias 'yas/root-directory 'yas/snippet-dirs)
 
@@ -489,7 +480,8 @@ Attention: These hooks are not run when exiting nested/stackd snippet expansion!
      t)
   "Snippet expanding condition.
 
-This variable is a lisp form:
+This variable is a lisp form which is evaluated everytime a
+snippet expansion is attemped:
 
     * If it evaluates to nil, no snippets can be expanded.
 
@@ -552,7 +544,7 @@ snippet itself contains a condition that returns the symbol
   "A list of mode which is well known but not part of emacs.")
 
 (defvar yas/escaped-characters
-  '(?\\ ?` ?' ?$ ?} ?{ ?\( ?\))
+  '(?\\ ?` ?\" ?' ?$ ?} ?{ ?\( ?\))
   "List of characters which *might* need to be escaped.")
 
 (defconst yas/field-regexp
@@ -729,42 +721,55 @@ With optional UNBIND-KEY, try to unbind that key from
     (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.")
+  "A hash table of mode symbols to `yas/table' objects.")
+
+(defvar yas/parents (make-hash-table)
+  "A hash table of mode symbols do lists of direct parent mode symbols.
+
+This list is populated when reading the \".yas-parents\" files
+found when traversing snippet directories with
+`yas/load-directory'.
+
+There might be additionalal parenting information stored in the
+`derived-mode-parent' property of some mode symbols, but that is
+not recorded here.")
 
 (defvar yas/direct-keymaps (list)
   "Keymap alist supporting direct snippet keybindings.
 
-This variable is is placed `emulation-mode-map-alists'.
+This variable is is placed in `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'")
+Its elements looks like (TABLE-NAME . KEYMAP). They're
+instantiated on `yas/reload-all' but KEYMAP is added to only when
+loading snippets. `yas//direct-TABLE-NAME' is then a variable set
+buffer-locally when entering `yas/minor-mode'. KEYMAP binds all
+defined direct keybindings to the command
+`yas/expand-from-keymap' which then which snippet to 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))))
+               (push (cons (intern (format "yas//direct-%s" name))
+                           (yas/table-direct-keymap table))
+                     yas/direct-keymaps))
            yas/tables))
 
-(defun yas/direct-keymaps-set-vars ()
+(defun yas/modes-to-activate ()
+  "Compute list of mode symbols that are active for `yas/expand'
+and friends."
   (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)))))
+    (remove-duplicates
+     (append modes-to-activate
+             (mapcan #'(lambda (mode)
+                         (yas/all-parents mode))
+                     modes-to-activate)))))
 
 (defvar yas/minor-mode-hook nil
   "Hook run when yas/minor-mode is turned on")
@@ -773,7 +778,7 @@ all defined direct keybindings to the command
 (define-minor-mode yas/minor-mode
   "Toggle YASnippet mode.
 
-When YASnippet mode is enabled, the `tas/trigger-key' key expands
+When YASnippet mode is enabled, the `yas/trigger-key' key expands
 snippets of code depending on the mode.
 
 With no argument, this command toggles the mode.
@@ -801,16 +806,21 @@ Key bindings:
          ;;
          (add-hook 'emulation-mode-map-alists 'yas/direct-keymaps)
          (add-hook 'post-command-hook 'yas/post-command-handler nil t)
-         (add-hook 'yas/minor-mode-hook 'yas/direct-keymaps-set-vars-runonce 'append))
+         ;; Set the `yas//direct-%s' vars for direct keymap expansion
+         ;;
+         (dolist (mode (yas/modes-to-activate))
+           (let ((name (intern (format "yas//direct-%s" mode))))
+             (set-default name nil)
+             (set (make-local-variable name) t)))
+         ;; Perform JIT loads
+         ;;
+         (yas/load-pending-jits))
         (t
          ;; Uninstall the direct keymaps and the post-command hook
          ;;
          (remove-hook 'post-command-hook 'yas/post-command-handler t)
          (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 nil
   "If non-nil don't let `yas/minor-mode-on' active yas for this buffer.
@@ -839,10 +849,15 @@ Do this unless `yas/dont-activate' is truish "
   :group 'yasnippet
   :require 'yasnippet)
 
-(add-hook 'yas/global-mode-hook 'yas/reload-all-maybe)
-(defun yas/reload-all-maybe ()
-  (if yas/global-mode
-      (yas/reload-all)))
+(defadvice yas/global-mode (before yas/reload-with-jit (arg) activate)
+  (cond ((and arg
+              (numberp arg)
+              (> arg 1))
+         ;; explicitly enabling
+         (yas/reload-all))
+        ((not yas/global-mode)
+         ;; toggling
+         (yas/reload-all))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Major mode stuff
@@ -886,10 +901,10 @@ Do this unless `yas/dont-activate' is truish "
 
 (define-derived-mode snippet-mode text-mode "Snippet"
   "A mode for editing yasnippets"
-  (set-syntax-table (standard-syntax-table))
   (setq font-lock-defaults '(yas/font-lock-keywords))
   (set (make-local-variable 'require-final-newline) nil)
-  (use-local-map snippet-mode-map))
+  (set (make-local-variable 'comment-start) "#")
+  (set (make-local-variable 'comment-start-skip) "#+[\t ]*"))
 
 
 \f
@@ -897,7 +912,6 @@ Do this unless `yas/dont-activate' is truish "
 
 (defstruct (yas/template (:constructor yas/make-blank-template))
   "A template for a snippet."
-  table
   key
   content
   name
@@ -907,8 +921,9 @@ Do this unless `yas/dont-activate' is truish "
   keybinding
   uuid
   menu-binding-pair
-  group      ;; as dictated by the #group: directive or .yas-make-groups 
+  group      ;; as dictated by the #group: directive or .yas-make-groups
   perm-group ;; as dictated by `yas/define-menu'
+  table
   )
 
 (defun yas/populate-template (template &rest args)
@@ -941,11 +956,6 @@ Has the following fields:
   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
@@ -1007,7 +1017,6 @@ Has the following fields:
         ;;
         (remhash uuid (yas/table-uuidhash table))))))
 
-
 (defun yas/add-template (table template)
   "Store in TABLE the snippet template TEMPLATE.
 
@@ -1016,7 +1025,7 @@ 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))))
+        (menu-binding-pair (yas/snippet-menu-binding-pair-get-create template)))
     (dolist (k (remove nil (list key keybinding)))
       (puthash name
                template
@@ -1028,28 +1037,27 @@ keybinding)."
       (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))
+    ;; Update trigger & keybinding in the menu-binding pair
+    ;;
+    (setf (getf (cdr (car menu-binding-pair)) :keys)
+          (or (and keybinding (key-description keybinding))
+              (and key (concat key yas/trigger-symbol))))
 
     (puthash (yas/template-uuid template) template (yas/table-uuidhash table))))
 
-(defun yas/update-template (snippet-table template)
-  "Add or update TEMPLATE in SNIPPET-TABLE.
+(defun yas/update-template (table template)
+  "Add or update TEMPLATE in TABLE.
 
-Also takes care of adding and updaring to the associated menu."
+Also takes care of adding and updating to the associated menu."
   ;; Remove from table by uuid
   ;;
-  (yas/remove-template-by-uuid snippet-table (yas/template-uuid template))
+  (yas/remove-template-by-uuid table (yas/template-uuid template))
   ;; Add to table again
   ;;
-  (yas/add-template snippet-table template)
+  (yas/add-template table template)
   ;; Take care of the menu
   ;;
-  (let ((keymap (yas/menu-keymap-get-create snippet-table))
+  (let ((keymap (yas/menu-keymap-get-create table))
         (group (yas/template-group template)))
     (when (and yas/use-menu
                keymap
@@ -1059,7 +1067,7 @@ Also takes care of adding and updaring to the associated menu."
       (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
@@ -1068,9 +1076,9 @@ Also takes care of adding and updaring to the associated menu."
             (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))))))
 
@@ -1101,8 +1109,7 @@ string and TEMPLATE is a `yas/template' structure."
           (save-match-data
             (eval condition))))
     (error (progn
-             (message (format "[yas] error in condition evaluation: %s"
-                              (error-message-string err)))
+             (yas/message 1 "Error in condition evaluation: %s" (error-message-string err))
              nil))))
 
 
@@ -1147,12 +1154,11 @@ conditions to filter out potential expansions."
           (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 (copy-list parents)
-              (mapcan #'yas/table-get-all-parents parents)))))
+(defun yas/all-parents (mode)
+  "Returns a list of all parent modes of MODE"
+  (let ((parents (gethash mode yas/parents)))
+    (append parents
+            (mapcan #'yas/all-parents parents))))
 
 (defun yas/table-templates (table)
   (when table
@@ -1224,8 +1230,8 @@ a list of modes like this to help the judgement."
                               (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"
+                               (yas/format "elisp error! %s" (error-message-string err))
+                             (error (yas/format "elisp error: %s"
                                             (error-message-string err)))))))))
     (when (and (consp retval)
                (eq 'yas/exception (car retval)))
@@ -1236,8 +1242,8 @@ a list of modes like this to help the judgement."
   (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"
+               (yas/format "elisp error! %s" (error-message-string err))
+             (error (yas/format "elisp error: %s"
                             (error-message-string err)))))))
 
 (defun yas/read-lisp (string &optional nil-on-error)
@@ -1248,7 +1254,7 @@ 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))))))
+                `(error (error-message-string ,err))))))
 
 (defun yas/read-keybinding (keybinding)
   "Read KEYBINDING as a snippet keybinding, return a vector."
@@ -1260,7 +1266,7 @@ return an expression that when evaluated will issue an error."
                        (read-kbd-macro keybinding 'need-vector))))
           res)
       (error
-       (message "[yas] warning: keybinding \"%s\" invalid since %s."
+       (yas/message 3 "warning: keybinding \"%s\" invalid since %s."
                 keybinding (error-message-string err))
        nil))))
 
@@ -1276,45 +1282,26 @@ ensure your use `make-local-variable' when you set it.")
 (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."
+  "Get or create the snippet table corresponding to MODE."
   (let ((table (gethash mode
                         yas/tables)))
     (unless table
       (setq table (yas/make-snippet-table (symbol-name mode)))
       (puthash mode table yas/tables)
-      (aput 'yas/direct-keymaps (intern (format "yas//direct-%s" mode))
-            (yas/table-direct-keymap table)))
+      (push (cons (intern (format "yas//direct-%s" mode))
+                  (yas/table-direct-keymap table))
+            yas/direct-keymaps))
     table))
 
-(defun yas/get-snippet-tables (&optional mode-symbol dont-search-parents)
+(defun yas/get-snippet-tables ()
   "Get snippet tables for current buffer.
 
-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'.
-
-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)))))
+Return a list of `yas/table' objects. The list of modes to
+consider is returned by `yas/modes-to-activate'"
+  (remove nil
+          (mapcar #'(lambda (mode-name)
+                      (gethash mode-name yas/tables))
+                  (yas/modes-to-activate))))
 
 (defun yas/menu-keymap-get-create (table)
   "Get or create the main menu keymap correspondong to MODE.
@@ -1404,6 +1391,8 @@ Here's a list of currently recognized directives:
                    (setq binding (match-string-no-properties 2)))))
       (setq template
             (buffer-substring-no-properties (point-min) (point-max))))
+    (unless (or key binding)
+      (setq key (and file (file-name-nondirectory file))))
     (when (eq type 'command)
       (setq template (yas/read-lisp (concat "(progn" template ")"))))
     (when group
@@ -1427,8 +1416,8 @@ Here's a list of currently recognized directives:
                                                (directory-file-name extra-dir)))))
     group))
 
-(defun yas/subdirs (directory &optional file?)
-  "Return subdirs or files of DIRECTORY according to FILE?."
+(defun yas/subdirs (directory &optional filep)
+  "Return subdirs or files of DIRECTORY according to FILEP."
   (remove-if (lambda (file)
                (or (string-match "^\\."
                                  (file-name-nondirectory file))
@@ -1436,7 +1425,7 @@ Here's a list of currently recognized directives:
                                  (file-name-nondirectory file))
                    (string-match "~$"
                                  (file-name-nondirectory file))
-                   (if file?
+                   (if filep
                        (file-directory-p file)
                      (not (file-directory-p file)))))
              (directory-files directory t)))
@@ -1463,6 +1452,10 @@ Here's a list of currently recognized directives:
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Popping up for keys and templates
 ;;
+(defvar yas/x-pretty-prompt-templates nil
+  "If non-nil, attempt to prompt for templates like TextMate.")
+
+
 (defun yas/prompt-for-template (templates &optional prompt)
   "Interactively choose a template from the list TEMPLATES.
 
@@ -1525,26 +1518,27 @@ TEMPLATES is a list of `yas/template'."
       (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 ((pretty-alist (list))
+  (let ((organized (make-hash-table :test #'equal))
         menu
         more-than-one-table
         prefix)
     (dolist (tl templates)
-      (aput 'pretty-alist (yas/template-table tl) (cons tl (aget pretty-alist (yas/template-table tl)))))
-    (setq more-than-one-table (> (length pretty-alist) 1))
+      (puthash (yas/template-table tl)
+               (cons tl
+                     (gethash (yas/template-table tl) organized))
+               organized))
+    (setq more-than-one-table (> (hash-table-count organized) 1))
     (setq prefix (if more-than-one-table
                      "   " ""))
-    (dolist (table-and-templates pretty-alist)
-      (when (cdr table-and-templates)
-        (if more-than-one-table
-            (push (yas/table-name (car table-and-templates)) menu))
-        (dolist (template (cdr table-and-templates))
-          (push (cons (concat prefix (yas/template-name template))
-                      template) menu))))
+    (if more-than-one-table
+        (maphash #'(lambda (table templates)
+                     (push (yas/table-name table) menu)
+                     (dolist (tl templates)
+                       (push (cons (concat prefix (yas/template-name tl)) tl) menu))) organized)
+      (setq menu (mapcar #'(lambda (tl) (cons (concat prefix (yas/template-name tl)) tl)) templates)))
+
     (setq menu (nreverse menu))
     (or (x-popup-menu (if (fboundp 'posn-at-point)
                           (let ((x-y (posn-x-y (posn-at-point (point)))))
@@ -1556,7 +1550,7 @@ TEMPLATES is a list of `yas/template'."
         (keyboard-quit))))
 
 (defun yas/ido-prompt (prompt choices &optional display-fn)
-  (when (featurep 'ido)
+  (when (fboundp 'ido-completing-read)
     (yas/completing-prompt prompt choices display-fn #'ido-completing-read)))
 
 (eval-when-compile (require 'dropdown-list nil t))
@@ -1607,96 +1601,138 @@ TEMPLATES is a list of `yas/template'."
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Loading snippets from files
 ;;
-(defun yas/load-directory-1 (directory &optional mode-sym parents)
-  "Recursively load snippet templates from DIRECTORY."
+(defun yas/load-yas-setup-file (file)
+  (load file 'noerror))
 
-  ;; 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)))
+(defun yas/load-directory (top-level-dir &optional use-jit)
+  "Load snippets in directory hierarchy TOP-LEVEL-DIR.
 
-  ;;
-  ;;
-  (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"))))
-           (default-directory directory)
-           (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 (or snippet-defs
-                (cdr major-mode-and-parents))
-        (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.
-
-Below the top-level directory, each directory is a mode
-name.  And under each subdirectory, each file is a definition
-of a snippet.  The file name is the trigger key and the
-content of the file is the template."
+Below TOP-LEVEL-DIR each directory should be a mode name.
+
+Optional USE-JIT use jit-loading of snippets."
   (interactive "DSelect the root directory: ")
-  (unless (file-directory-p directory)
-    (error "%s is not a directory" directory))
   (unless yas/snippet-dirs
-    (setq yas/snippet-dirs directory))
-  (dolist (dir (yas/subdirs directory))
-    (yas/load-directory-1 dir))
+    (setq yas/snippet-dirs top-level-dir))
+  (dolist (dir (yas/subdirs top-level-dir))
+    (let* ((major-mode-and-parents (yas/compute-major-mode-and-parents
+                                    (concat dir "/dummy")))
+           (mode-sym (car major-mode-and-parents))
+           (parents (cdr major-mode-and-parents)))
+      (yas/define-parents mode-sym parents)
+      (let ((form `(yas/load-directory-1 ,dir
+                                         ',mode-sym
+                                         ',parents)))
+        (if use-jit
+            (yas/schedule-jit mode-sym form)
+            (eval form)))))
   (when (interactive-p)
-    (message "[yas] Loaded snippets from %s." directory)))
+    (yas/message 3 "Loaded snippets from %s." top-level-dir)))
+
+(defun yas/load-directory-1 (directory mode-sym parents &optional no-compiled-snippets)
+  "Recursively load snippet templates from DIRECTORY."
+  (unless (file-exists-p (concat directory "/" ".yas-skip"))
+    (if (and (not no-compiled-snippets)
+             (load (expand-file-name ".yas-compiled-snippets" directory) 'noerror (<= yas/verbosity 2)))
+        (yas/message 2 "Loading much faster .yas-compiled-snippets from %s" directory)
+      (yas/load-directory-2 directory mode-sym))))
 
-(defun yas/load-snippet-dirs ()
+(defun yas/load-directory-2 (directory mode-sym)
+  ;; Load .yas-setup.el files wherever we find them
+  ;;
+  (yas/load-yas-setup-file (expand-file-name ".yas-setup" directory))
+  (let* ((default-directory directory)
+         (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 mode-sym
+                           snippet-defs))
+    ;; now recurse to a lower level
+    ;;
+    (dolist (subdir (yas/subdirs directory))
+      (yas/load-directory-2 subdir
+                            mode-sym))))
+
+(defun yas/load-snippet-dirs (&optional nojit)
   "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)))
-
-(defun yas/reload-all (&optional interactive)
-  "Reload all snippets and rebuild the YASnippet menu. "
+  (let (errors)
+    (if yas/snippet-dirs
+        (dolist (directory (reverse (yas/snippet-dirs)))
+          (cond ((file-directory-p directory)
+                 (yas/load-directory directory (not nojit))
+                 (yas/message 3 "Loaded %s" directory))
+                (t
+                 (push (yas/message 0 "Check your `yas/snippet-dirs': %s is not a directory" directory) errors))))
+      (call-interactively 'yas/load-directory))
+    errors))
+
+(defun yas/reload-all (&optional no-jit)
+  "Reload all snippets and rebuild the YASnippet menu.
+
+Behaviour is affected by `yas/no-jit', which see."
   (interactive "p")
-  (let ((errors))
-    ;; Empty all snippet tables and all menu tables
-    ;;
-    (setq yas/tables (make-hash-table))
-    (setq yas/menu-table (make-hash-table))
+  (catch 'abort
+    (let ((errors)
+          (snippet-editing-buffers
+           (remove-if-not #'(lambda (buffer)
+                              (with-current-buffer buffer yas/editing-template))
+                          (buffer-list))))
+      ;; Warn if there are buffers visiting snippets, since reloading will break
+      ;; any on-line editing of those buffers.
+      ;;
+      (if snippet-editing-buffers
+          (if (y-or-n-p "Some buffers editing live snippets, close them and proceed with reload?")
+              (mapcar #'kill-buffer snippet-editing-buffers)
+            (yas/message 1 "Aborted reload...")
+            (throw 'abort nil)))
 
-    ;; Init the `yas/minor-mode-map', taking care not to break the
-    ;; menu....
-    ;;
-    (setf (cdr yas/minor-mode-map)
-          (cdr (yas/init-minor-keymap)))
+      ;; Empty all snippet tables, parenting info and all menu tables
+      ;;
+      (setq yas/tables (make-hash-table))
+      (setq yas/parents (make-hash-table))
+      (setq yas/menu-table (make-hash-table))
 
-    ;; Reload the directories listed in `yas/snippet-dirs' or prompt
-    ;; the user to select one.
-    ;;
-    (condition-case oops
-        (yas/load-snippet-dirs)
-      (error (push oops errors)
-             (message "[yas] Check your `yas/snippet-dirs': %s" (second oops))))
-    ;; Reload the direct keybindings
-    ;;
-    (yas/direct-keymaps-reload)
-    (message "[yas] Reloaded everything...%s." (if errors " (some errors, check *Messages*)" ""))))
+      ;; Cancel all pending 'yas/scheduled-jit-loads'
+      ;;
+      (setq yas/scheduled-jit-loads (make-hash-table))
+
+      ;; Init the `yas/minor-mode-map', taking care not to break the
+      ;; menu....
+      ;;
+      (setf (cdr yas/minor-mode-map)
+            (cdr (yas/init-minor-keymap)))
+
+      ;; Reload the directories listed in `yas/snippet-dirs' or prompt
+      ;; the user to select one.
+      ;;
+      (setq errors (yas/load-snippet-dirs no-jit))
+      ;; Reload the direct keybindings
+      ;;
+      (yas/direct-keymaps-reload)
+      ;; Reload the trigger-key (shoudn't be needed, but see issue #237)
+      ;;
+      (yas/trigger-key-reload)
+
+      (yas/message 3 "Reloaded everything...%s." (if errors " (some errors, check *Messages*)" "")))))
+
+(defun yas/load-pending-jits ()
+  (when yas/minor-mode
+    (dolist (mode (yas/modes-to-activate))
+      (let ((forms (gethash mode yas/scheduled-jit-loads)))
+        (dolist (form forms)
+          (yas/message  3 "Loading snippets for %s, just in time: %s!" mode form)
+          (eval form))
+        (remhash mode yas/scheduled-jit-loads)))))
+
+;; (when (<= emacs-major-version 22)
+;;   (add-hook 'after-change-major-mode-hook 'yas/load-pending-jits))
 
 (defun yas/quote-string (string)
   "Escape and quote STRING.
@@ -1708,139 +1744,80 @@ foo\"bar\\! -> \"foo\\\"bar\\\\!\""
                                     t)
           "\""))
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; Yasnippet Bundle
+;;; Snippet compilation function
 
 (defun yas/initialize ()
   "For backward compatibility, enable `yas/minor-mode' globally"
   (yas/global-mode 1))
 
-(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.
+(defun yas/compile-directory (top-level-dir)
+  "Create .yas-compiled-snippets.el files under subdirs of TOP-LEVEL-DIR.
+
+This works by stubbing a few functions, then calling
+`yas/load-directory'."
+  (interactive "DTop level snippet directory?")
+  (flet ((yas/load-yas-setup-file
+          (file)
+          (let ((elfile (concat file ".el")))
+            (when (file-exists-p elfile)
+              (insert ";;; .yas-setup.el support file if any:\n;;;\n")
+              (insert-file-contents elfile)
+              (end-of-buffer)
+              )))
+         (yas/define-snippets
+          (mode snippets)
+          (insert ";;; Snippet definitions:\n;;;\n")
+          (let ((literal-snippets (list))
+                (print-length nil))
+            (dolist (snippet snippets)
+              (let ((key                    (first   snippet))
+                    (template-content       (second  snippet))
+                    (name                   (third   snippet))
+                    (condition              (fourth  snippet))
+                    (group                  (fifth   snippet))
+                    (expand-env             (sixth   snippet))
+                    (file                   nil) ;; (seventh snippet)) ;; omit on purpose
+                    (binding                (eighth  snippet))
+                    (uuid                    (ninth   snippet)))
+                (push `(,key
+                        ,template-content
+                        ,name
+                        ,condition
+                        ,group
+                        ,expand-env
+                        ,file
+                        ,binding
+                        ,uuid)
+                      literal-snippets)))
+            (insert (pp-to-string `(yas/define-snippets ',mode ',literal-snippets)))
+            (insert "\n\n")))
+         (yas/load-directory-1
+          (dir mode parents &rest ignore)
+          (let ((output-file (concat (file-name-as-directory dir) ".yas-compiled-snippets.el")))
+            (with-temp-file output-file
+              (insert (format ";;; Compiled snippets and support files for `%s'\n" mode))
+              (yas/load-directory-2 dir mode)
+              (insert (format ";;; Do not edit! File generated at %s\n" (current-time-string)))))))
+    (yas/load-directory top-level-dir nil)))
+
+(defun yas/recompile-all ()
+  "Compile every dir in `yas/snippet-dirs'."
+  (interactive)
+  (mapc #'yas/compile-directory (yas/snippet-dirs)))
 
-SNIPPET-ROOTS is a list of root directories that contains the
-snippets definition.
 
-CODE is the code to be placed at the end of the generated file
-and that can initialize the YASnippet bundle.
+;;; JIT loading
+;;;
 
-Last optional argument DROPDOWN is the filename of the
-dropdown-list.el library.
+(defvar yas/scheduled-jit-loads (make-hash-table)
+  "Alist of mode-symbols to forms to be evaled when `yas/minor-mode' kicks in.")
 
-Here's the default value for all the parameters:
+(defun yas/schedule-jit (mode form)
+  (puthash mode
+           (cons form
+                 (gethash mode yas/scheduled-jit-loads))
+           yas/scheduled-jit-loads))
 
-  (yas/compile-bundle \"yasnippet.el\"
-                      \"yasnippet-bundle.el\"
-                      \"snippets\")
-                      \"(yas/initialize-bundle)
-                        ### autoload
-                        (require 'yasnippet-bundle)`\"
-                      \"dropdown-list.el\")
-"
-  (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))
-      (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.\"")
-      (flet ((yas/define-snippets
-              (mode snippets &optional parent-or-parents)
-              (insert ";;; snippets for " (symbol-name mode) ", subdir " (file-name-nondirectory (replace-regexp-in-string "/$" "" default-directory)) "\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))
-            (let ((file (concat subdir "/.yas-setup.el")))
-              (when (file-readable-p file)
-                (insert "\n;; Supporting elisp for subdir " (file-name-nondirectory subdir) "\n\n")
-                (with-temp-buffer
-                  (insert-file-contents file)
-                  (replace-regexp "^;;.*$" "" nil (point-min) (point-max))
-                  (replace-regexp "^[\s\t]*\n\\([\s\t]*\n\\)+" "\n" nil (point-min) (point-max))
-                  (kill-region (point-min) (point-max)))
-                (yank)))
-            (yas/load-directory-1 subdir nil))))
-
-      (insert (pp-to-string `(yas/global-mode 1)))
-      (insert ")\n\n" code "\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"))))
-
-(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
@@ -1852,7 +1829,14 @@ Here's the default value for all the parameters:
                    yas/version
                    ") -- pluskid <pluskid@gmail.com>/joaotavora <joaotavora@gmail.com>")))
 
-(defun yas/define-snippets (mode snippets &optional parent-mode)
+(defun yas/define-parents (mode parents)
+  "Add PARENTS to the list of MODE's parents"
+  (puthash mode-sym (remove-duplicates
+                     (append parents
+                             (gethash mode-sym yas/parents)))
+           yas/parents))
+
+(defun yas/define-snippets (mode snippets)
   "Define SNIPPETS for MODE.
 
 SNIPPETS is a list of snippet definitions, each taking the
@@ -1878,32 +1862,9 @@ 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."
-  ;; X) `snippet-table' is created or retrieved for MODE, same goes
-  ;;    for the list of snippet tables `parent-tables'.
-  ;;
+the current buffers contents."
   (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)
       (setq template (yas/define-snippets-1 snippet
                                             snippet-table)))
@@ -1950,7 +1911,10 @@ not need to be a real mode."
     template))
 
 (defun yas/snippet-menu-binding-pair-get-create (template &optional type)
-  "Get TEMPLATE's menu binding or assign it a new one."
+  "Get TEMPLATE's menu binding or assign it a new one.
+
+TYPE may be `:stay', signalling this menu binding should be
+static in the menu."
   (or (yas/template-menu-binding-pair template)
       (let ((key (yas/template-key template))
             (keybinding (yas/template-keybinding template)))
@@ -1976,7 +1940,7 @@ not need to be a real mode."
   "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.
@@ -1987,10 +1951,10 @@ not need to be a real mode."
               (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)))
@@ -2004,7 +1968,7 @@ 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.
+  permanent, i.e. it will never move (be reordered) in the menu.
 
 - (yas/separator) : Creates a separator
 
@@ -2055,7 +2019,7 @@ ommited from MODE's menu, even if they're manually loaded.
            (define-key keymap (vector (gensym))
              '(menu-item "----")))
           (t
-           (message "[yas] don't know anything about menu entry %s" (first e))))))
+           (yas/message 3 "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.
@@ -2139,7 +2103,10 @@ object satisfying `yas/field-p' to restrict the expansion to."
 If expansion fails, execute the previous binding for this key"
   (interactive)
   (setq yas/condition-cache-timestamp (current-time))
-  (let* ((vec (this-command-keys-vector))
+  (let* ((yas/prefix current-prefix-arg)
+         (vec (subseq (this-command-keys-vector) (if current-prefix-arg
+                                                     universal-argument-num-events
+                                                   0)))
          (templates (mapcan #'(lambda (table)
                                 (yas/fetch table vec))
                             (yas/get-snippet-tables))))
@@ -2251,7 +2218,7 @@ by condition."
                             (car where)
                             (cdr where)
                             (yas/template-expand-env yas/current-template))
-      (message "[yas] No snippets can be inserted here!"))))
+      (yas/message 3 "No snippets can be inserted here!"))))
 
 (defun yas/visit-snippet-file ()
   "Choose a snippet to edit, selection like `yas/insert-snippet'.
@@ -2340,7 +2307,7 @@ where snippets of table might exist."
   (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))
+          (error (yas/format "%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)))
@@ -2355,8 +2322,11 @@ where snippets of table might exist."
               ;; create the .yas-parents file here...
               candidate)))))
 
-(defun yas/new-snippet (&optional choose-instead-of-guess)
-  ""
+(defun yas/new-snippet (&optional no-template)
+  "Pops a new buffer for writing a snippet.
+
+Expands a snippet-writing snippet, unless the optional prefix arg
+NO-TEMPLATE is non-nil."
   (interactive "P")
   (let ((guessed-directories (yas/guess-snippet-directories)))
 
@@ -2364,12 +2334,11 @@ where snippets of table might exist."
     (erase-buffer)
     (kill-all-local-variables)
     (snippet-mode)
+    (yas/minor-mode 1)
     (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 "\
+    (unless no-template (yas/expand-snippet "\
 # -*- mode: snippet -*-
 # name: $1
 # key: $2${3:
@@ -2421,7 +2390,13 @@ there, otherwise, proposes to create the first option returned by
                 (snippet-mode)))))
       (message "Could not guess snippet dir!"))))
 
-(defun yas/compute-major-mode-and-parents (file &optional prompt-if-failed)
+(defun yas/compute-major-mode-and-parents (file)
+  "Given FILE, find the nearest snippet directory for a given
+mode, then return a list (MODE-SYM PARENTS), the mode's symbol and a list
+representing one or more of the mode's parents.
+
+Note that MODE-SYM need not be the symbol of a real major mode,
+neither do the elements of PARENTS."
   (let* ((file-dir (and file
                         (directory-file-name (or (some #'(lambda (special)
                                                            (locate-dominating-file file special))
@@ -2433,10 +2408,7 @@ there, otherwise, proposes to create the first option returned by
          (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: "))))
+                                  (intern major-mode-name))))
          (parents (when (file-readable-p parents-file-name)
                          (mapcar #'intern
                                  (split-string
@@ -2467,7 +2439,7 @@ With optional prefix argument KILL quit the window and buffer."
    ;;  neatly positioned,...
    ;;
    (yas/editing-template
-    (yas/define-snippets-1 (yas/parse-template (yas/template-file yas/editing-template)) 
+    (yas/define-snippets-1 (yas/parse-template (yas/template-file yas/editing-template))
                            (yas/template-table yas/editing-template)))
    ;; Try to use `yas/guessed-modes'. If we don't have that use the
    ;; value from `yas/compute-major-mode-and-parents'
@@ -2491,7 +2463,7 @@ With optional prefix argument KILL quit the window and buffer."
                              nil
                              (if (first yas/guessed-modes)
                                  (symbol-name (first yas/guessed-modes))))))))
-      (set (make-local-variable 'yas/editing-template) 
+      (set (make-local-variable 'yas/editing-template)
            (yas/define-snippets-1 (yas/parse-template buffer-file-name)
                                   table)))))
   ;; Now, offer to save this shit iff:
@@ -2508,8 +2480,8 @@ With optional prefix argument KILL quit the window and buffer."
                  (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? ")
+
+    (when (y-or-n-p (yas/format "Looks like a library or new snippet. Save to new file? "))
       (let* ((option (first (yas/guess-snippet-directories (yas/template-table yas/editing-template))))
              (chosen (and option
                           (yas/make-directory-maybe option))))
@@ -2523,7 +2495,7 @@ With optional prefix argument KILL quit the window and buffer."
             (setf (yas/template-file yas/editing-template) buffer-file-name))))))
   (when kill
     (quit-window kill))
-  (message "[yas] Snippet \"%s\" loaded for %s."
+  (yas/message 3 "Snippet \"%s\" loaded for %s."
            (yas/template-name yas/editing-template)
            (yas/table-name (yas/template-table yas/editing-template))))
 
@@ -2537,7 +2509,7 @@ With optional prefix argument KILL quit the window and buffer."
                              (fboundp (car major-mode-and-parent))
                              (car major-mode-and-parent))
                         (first yas/guessed-modes)
-                        (intern (read-from-minibuffer "[yas] please input a mode: "))))
+                        (intern (read-from-minibuffer (yas/format "Please input a mode: ")))))
          (yas/current-template
           (and parsed
                (fboundp test-mode)
@@ -2553,6 +2525,7 @@ With optional prefix argument KILL quit the window and buffer."
              (switch-to-buffer (get-buffer-create buffer-name))
              (setq buffer-undo-list nil)
              (condition-case nil (funcall test-mode) (error nil))
+             (setq buffer-read-only nil)
              (yas/expand-snippet (yas/template-content yas/current-template)
                                  (point-min)
                                  (point-max)
@@ -2561,7 +2534,7 @@ With optional prefix argument KILL quit the window and buffer."
                         (require 'yasnippet-debug nil t))
                (add-hook 'post-command-hook 'yas/debug-snippet-vars nil t))))
           (t
-           (message "[yas] Cannot test snippet for unknown major mode")))))
+           (yas/message 3 "Cannot test snippet for unknown major mode")))))
 
 (defun yas/template-fine-group (template)
   (car (last (or (yas/template-group template)
@@ -2630,47 +2603,46 @@ With optional prefix argument KILL quit the window and buffer."
     (insert "\n"))
   (insert (make-string 100 ?-) "\n")
   (insert "group                   state name                                    key             binding\n")
-  (let ((groups-alist (list))
-        group)
+  (let ((groups-hash (make-hash-table :test #'equal)))
     (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)))))
+                 (let ((group (or (yas/template-fine-group v)
+                                  "(top level)")))
+                   (when (yas/template-name v)
+                     (puthash group
+                              (cons v (gethash group groups-hash))
+                              groups-hash))))
              (yas/table-uuidhash table))
-    (dolist (group-and-templates groups-alist)
-      (when (rest group-and-templates)
-        (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 (and condition
-                                             original-buffer)
-                                        (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")))))))
-
-
+    (maphash
+     #'(lambda (group templates)
+         (setq group (truncate-string-to-width group 25 0 ?  "..."))
+         (insert (make-string 100 ?-) "\n")
+         (dolist (p 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 (and condition
+                                              original-buffer)
+                                         (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"))))
+     groups-hash)))
 
 
 \f
@@ -2722,7 +2694,7 @@ If found, the content of subexp group SUBEXP (default 0) is
 
 Otherwise throw exception."
   (when (and yas/moving-away-p (notany #'(lambda (pos) (string= pos yas/text)) possibilities))
-    (yas/throw (format "[yas] field only allows %s" possibilities))))
+    (yas/throw (yas/format "Field only allows %s" possibilities))))
 
 (defun yas/field-value (number)
   "Get the string for field with NUMBER.
@@ -2888,13 +2860,24 @@ have, compare through the field's start point"
 
 (defun yas/field-probably-deleted-p (snippet field)
   "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)))))
-       ;; the field numbered 0, just before the exit marker, should
-       ;; never be skipped
-       (not (zerop (yas/field-number field)))))
+  (and
+   ;; field must be zero lentgh
+   ;;
+   (zerop (- (yas/field-start field) (yas/field-end field)))
+   ;; skip if:
+   (or
+    ;;  1) is a nested field and it's been modified
+    ;;
+    (and (yas/field-parent-field field)
+         (yas/field-modified-p field))
+    ;;  2) ends just before the snippet end
+    ;;
+    (and (eq field (car (last (yas/snippet-fields snippet))))
+         (= (yas/field-start field) (overlay-end (yas/snippet-control-overlay snippet)))))
+   ;; the field numbered 0, just before the exit marker, should
+   ;; never be skipped
+   ;;
+   (not (zerop (yas/field-number field)))))
 
 (defun yas/snippets-at-point (&optional all-snippets)
   "Return a sorted list of snippets at point, most recently
@@ -2965,7 +2948,7 @@ Also create some protection overlays"
   (yas/place-overlays snippet field)
   (overlay-put yas/active-field-overlay 'yas/field field)
   (let ((number (yas/field-number field)))
-    ;; check for the special ${0: ...} field 
+    ;; check for the special ${0: ...} field
     (if (and number (zerop number))
         (progn
           (set-mark (yas/field-end field))
@@ -3007,7 +2990,7 @@ Also create some protection overlays"
   (mapc #'(lambda (snippet)
             (yas/exit-snippet snippet)
             (yas/check-commit-snippet))
-        (yas/snippets-at-point)))
+        (yas/snippets-at-point 'all-snippets)))
 
 \f
 ;;; Some low level snippet-routines
@@ -3018,16 +3001,14 @@ Also create some protection overlays"
   `(let ((yas/inhibit-overlay-hooks t))
      (progn ,@body)))
 
+(defvar yas/snippet-beg nil "Beginning position of the last snippet commited.")
+(defvar yas/snippet-end nil "End position of the last snippet commited.")
+
 (defun yas/commit-snippet (snippet)
   "Commit SNIPPET, but leave point as it is.  This renders the
-snippet as ordinary text.
-
-Return a buffer position where the point should be placed if
-exiting the snippet."
+snippet as ordinary text."
 
-  (let ((control-overlay (yas/snippet-control-overlay snippet))
-        yas/snippet-beg
-        yas/snippet-end)
+  (let ((control-overlay (yas/snippet-control-overlay snippet)))
     ;;
     ;; Save the end of the moribund snippet in case we need to revive it
     ;; its original expansion.
@@ -3065,13 +3046,13 @@ exiting the snippet."
       ;; again from `yas/take-care-of-redo'....
       (setf (yas/snippet-fields snippet) nil)))
 
-  (message "[yas] snippet %s exited." (yas/snippet-id snippet)))
+  (yas/message 3 "Snippet %s exited." (yas/snippet-id snippet)))
 
 (defun yas/safely-run-hooks (hook-var)
   (condition-case error
       (run-hooks hook-var)
     (error
-     (message "[yas] %s error: %s" hook-var (error-message-string error)))))
+     (yas/message 3 "%s error: %s" hook-var (error-message-string error)))))
 
 
 (defun yas/check-commit-snippet ()
@@ -3082,7 +3063,7 @@ snippet, if so cleans up the whole snippet up."
          (snippet-exit-transform))
     (dolist (snippet snippets)
       (let ((active-field (yas/snippet-active-field snippet)))
-        (setq snippet-exit-transform (yas/snippet-force-exit snippet)) 
+        (setq snippet-exit-transform (yas/snippet-force-exit snippet))
         (cond ((or snippet-exit-transform
                    (not (and active-field (yas/field-contains-point-p active-field))))
                (setq snippets-left (delete snippet snippets-left))
@@ -3426,7 +3407,7 @@ considered when expanding the snippet."
                (yas/inhibit-overlay-hooks
                  (setq snippet
                        (if expand-env
-                           (eval `(let ,expand-env
+                           (eval `(let* ,expand-env
                                     (insert content)
                                     (yas/snippet-create (point-min) (point-max))))
                          (insert content)
@@ -3470,7 +3451,7 @@ considered when expanding the snippet."
              (when first-field
                (sit-for 0) ;; fix issue 125
                (yas/move-to-field snippet first-field)))
-           (message "[yas] snippet expanded.")
+           (yas/message 3 "snippet expanded.")
            t))))
 
 (defun yas/take-care-of-redo (beg end snippet)
@@ -3685,7 +3666,7 @@ Meant to be called in a narrowed buffer, does various passes"
     (setq yas/dollar-regions nil)
     ;; protect escaped quote, backquotes and backslashes
     ;;
-    (yas/protect-escapes nil '(?\\ ?` ?'))
+    (yas/protect-escapes nil `(?\\ ?` ?'))
     ;; replace all backquoted expressions
     ;;
     (goto-char parse-start)
@@ -3754,7 +3735,7 @@ SNIPPET-MARKERS."
       (widen)
       (condition-case err
           (indent-according-to-mode)
-        (error (message "[yas] warning: yas/indent-according-to-mode habing problems running %s" indent-line-function)
+        (error (yas/message 3 "Warning: `yas/indent-according-to-mode' having problems running %s" indent-line-function)
                nil)))
     (mapc #'(lambda (marker)
               (set-marker marker (point)))
@@ -4097,7 +4078,7 @@ that the rest of `yas/post-command-handler' runs.")
                   (apply (car fn-and-args)
                          (cdr fn-and-args)))
               yas/post-command-runonce-actions)
-      (error (message "[yas] problem running `yas/post-command-runonce-actions'!")))
+      (error (yas/message 3 "Problem running `yas/post-command-runonce-actions'!")))
     (setq yas/post-command-runonce-actions nil))
   (cond (yas/protection-violation
          (goto-char yas/protection-violation)
@@ -4204,13 +4185,25 @@ Remaining args as in `yas/expand-snippet'."
                                     (gethash uuid (yas/table-uuidhash table)))))
     (when yas/current-template
       (yas/expand-snippet (yas/template-content yas/current-template)))))
+\f
+;;; Utils
+;;;
+
+(defvar yas/verbosity 4
+  "Log level for `yas/message' 4 means trace most anything, 0 means nothing.")
+(defun yas/message (level message &rest args)
+  (when (> yas/verbosity level)
+    (message (apply #'yas/format message args))))
+
+(defun yas/format (format-control &rest format-args)
+  (apply #'format (concat "[yas] " format-control) format-args))
 
 \f
 ;;; Some hacks:
-;;; 
-;; `locate-dominating-file' 
+;;;
+;; `locate-dominating-file'
 ;; `region-active-p'
-;; 
+;;
 ;; added for compatibility in emacs < 23
 (unless (>= emacs-major-version 23)
   (unless (fboundp 'region-active-p)