]> code.delx.au - gnu-emacs-elpa/blobdiff - yasnippet.el
Support expanding a snippet inside another snippet (only if not inside a field)
[gnu-emacs-elpa] / yasnippet.el
index 6d3b7c31da247b4acce5fbe48d5e3d9855a38e92..d149fcc84e8a544e26d4386deb2e60fce05908f5 100644 (file)
@@ -1,7 +1,10 @@
 ;;; yasnippet.el --- Yet another snippet extension for Emacs.
 
+;; Copyright 2008 pluskid
+;; 
 ;; Author: pluskid <pluskid@gmail.com>
-;; Version: 0.1
+;; Version: 0.2.1
+;; X-URL: http://code.google.com/p/yasnippet/
 
 ;; This file is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
 
 ;;; Commentary:
 
-;; Nothing.
+;; Basic steps to setup:
+;;   1. Place `yasnippet.el' in your `load-path'.
+;;   2. In your .emacs file:
+;;        (require 'yasnippet)
+;;   3. Place the `snippets' directory somewhere. E.g: ~/.emacs.d/snippets
+;;   4. In your .emacs file
+;;        (yas/initialize)
+;;        (yas/load-directory "~/.emacs.d/snippets")
+;;
+;; For more information and detailed usage, refer to the project page:
+;;      http://code.google.com/p/yasnippet/
 
 (require 'cl)
 
@@ -36,33 +49,56 @@ foo-bar
 
 will first try \"bar\", if not found, then \"foo-bar\" is tried.")
 
+(defvar yas/root-directory nil
+  "The root directory that stores the snippets for each major modes.")
+
 (defvar yas/indent-line t
   "Each (except the 1st) line of the snippet template is indented to
 current column if this variable is non-`nil'.")
 (make-variable-buffer-local 'yas/indent-line)
 
-(defvar yas/trigger-key (kbd "TAB")
+(defvar yas/trigger-key (kbd "<tab>")
   "The key to bind as a trigger of snippet.")
-(defvar yas/trigger-fallback 'indent-according-to-mode
+(defvar yas/trigger-fallback 'yas/default-trigger-fallback
   "The fallback command to call when there's no snippet to expand.")
 (make-variable-buffer-local 'yas/trigger-fallback)
 
 (defvar yas/keymap (make-sparse-keymap)
   "The keymap of snippet.")
+(define-key yas/keymap (kbd "<tab>") 'yas/next-field-group)
 (define-key yas/keymap (kbd "TAB") 'yas/next-field-group)
 (define-key yas/keymap (kbd "S-TAB") 'yas/prev-field-group)
 (define-key yas/keymap (kbd "<S-iso-lefttab>") 'yas/prev-field-group)
 (define-key yas/keymap (kbd "<S-tab>") 'yas/prev-field-group)
 
+(defvar yas/show-all-modes-in-menu nil
+  "Currently yasnippet only all \"real modes\" to menubar. For
+example, you define snippets for \"cc-mode\" and make it the
+parent of `c-mode', `c++-mode' and `java-mode'. There's really
+no such mode like \"cc-mode\". So we don't show it in the yasnippet
+menu to avoid the menu becoming too big with strange modes. The
+snippets defined for \"cc-mode\" can still be accessed from
+menu-bar->c-mode->parent (or c++-mode, java-mode, all are ok).
+However, if you really like to show all modes in the menu, set
+this variable to t.")
 (defvar yas/use-menu t
   "If this is set to `t', all snippet template of the current
 mode will be listed under the menu \"yasnippet\".")
 (defvar yas/trigger-symbol " =>"
   "The text that will be used in menu to represent the trigger.")
+
+(defface yas/field-highlight-face
+  '((((class color) (background light)) (:background "DarkSeaGreen2"))
+    (t (:background "DimGrey")))
+  "The face used to highlight a field of snippet.")
+(defface yas/mirror-highlight-face
+  '((((class color) (background light)) (:background "LightYellow2"))
+    (t (:background "gray22")))
+  "The face used to highlight mirror fields of a snippet.")
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Internal variables
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(defvar yas/version "0.1")
+(defvar yas/version "0.2.1")
 
 (defvar yas/snippet-tables (make-hash-table)
   "A hash table of snippet tables corresponding to each major-mode.")
@@ -72,9 +108,14 @@ mode will be listed under the menu \"yasnippet\".")
 ;; empty menu will cause problems, so we insert some items
 (define-key yas/menu-keymap [yas/about]
   '(menu-item "About" yas/about))
+(define-key yas/menu-keymap [yas/reload]
+  '(menu-item "Reload all snippets" yas/reload-all))
 (define-key yas/menu-keymap [yas/separator]
   '(menu-item "--"))
 
+(defvar yas/known-modes
+  '(ruby-mode rst-mode)
+  "A list of mode which is well known but not part of emacs.")
 (defconst yas/escape-backslash
   (concat "YASESCAPE" "BACKSLASH" "PROTECTGUARD"))
 (defconst yas/escape-dollar
@@ -83,8 +124,8 @@ mode will be listed under the menu \"yasnippet\".")
   (concat "YASESCAPE" "BACKQUOTE" "PROTECTGUARD"))
 
 (defconst yas/field-regexp
-  (concat "$\\(?1:[0-9]+\\)" "\\|"
-         "${\\(?:\\(?1:[0-9]+\\):\\)?\\(?2:[^}]*\\)}"))
+  (concat "$\\([0-9]+\\)" "\\|"
+         "${\\(?:\\([0-9]+\\):\\)?\\([^}]*\\)}"))
 
 (defvar yas/snippet-id-seed 0
   "Contains the next id for a snippet")
@@ -99,9 +140,9 @@ mode will be listed under the menu \"yasnippet\".")
 (defvar yas/overlay-insert-in-front-hooks
   (list 'yas/overlay-insert-in-front-hook)
   "The list of hooks of the overlay inserted in front event.")
-(defvar yas/overlay-insert-behind-hooks
-  (list 'yas/overlay-insert-behind-hook)
-  "The list of hooks of the overlay inserted behind event.")
+(defvar yas/keymap-overlay-modification-hooks
+  (list 'yas/overlay-maybe-insert-behind-hook)
+  "The list of hooks of the big keymap overlay modification event.")
 
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -125,11 +166,17 @@ mode will be listed under the menu \"yasnippet\".")
   (next nil)
   (prev nil)
   snippet)
-(defstruct (yas/field (:constructor yas/make-field (overlay number value)))
+(defstruct (yas/field 
+           (:constructor yas/make-field (overlay number value transform)))
   "A field in a snippet."
   overlay
   number
+  transform
   value)
+(defstruct (yas/snippet-table (:constructor yas/make-snippet-table ()))
+  "A table to store snippets for a perticular mode."
+  (hash (make-hash-table :test 'equal))
+  (parent nil))
 
 (defun yas/snippet-add-field (snippet field)
   "Add FIELD to SNIPPET."
@@ -138,6 +185,7 @@ mode will be listed under the menu \"yasnippet\".")
                     :test
                     '(lambda (field group)
                        (and (not (null (yas/field-number field)))
+                            (not (null (yas/group-number group)))
                             (= (yas/field-number field)
                                (yas/group-number group)))))))
     (if group
@@ -178,15 +226,52 @@ have, compare through the start point of the overlay."
        (< (overlay-start (yas/field-overlay field1))
           (overlay-start (yas/field-overlay field2)))))))
 
+(defun yas/snippet-table-fetch (table key)
+  "Fetch a snippet binding to KEY from TABLE. If not found,
+fetch from parent if any."
+  (let ((templates (gethash key (yas/snippet-table-hash table))))
+    (when (and (null templates)
+              (not (null (yas/snippet-table-parent table))))
+      (setq templates (yas/snippet-table-fetch
+                      (yas/snippet-table-parent table)
+                      key)))
+    templates))
+(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
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun yas/real-mode? (mode)
+  "Try to find out if MODE is a real mode. The MODE bound to
+a function (like `c-mode') is considered real mode. Other well
+known mode like `ruby-mode' which is not part of Emacs might
+not bound to a function until it is loaded. So yasnippet keeps
+a list of modes like this to help the judgement."
+  (or (fboundp mode)
+      (find mode yas/known-modes)))
+
 (defun yas/eval-string (string)
   "Evaluate STRING and convert the result to string."
   (condition-case err
       (format "%s" (eval (read string)))
     (error (format "(error in elisp evaluation: %s)" 
                   (error-message-string err)))))
+(defun yas/calculate-field-value (field value)
+  "Calculate the value of the field. If there's a transform
+for this field, apply it. Otherwise, the value is returned
+unmodified."
+  (let ((text value)
+       (transform (yas/field-transform field)))
+    (if transform
+       (yas/eval-string transform)
+      text)))
 (defsubst yas/replace-all (from to)
   "Replace all occurance from FROM to TO."
   (goto-char (point-min))
@@ -197,7 +282,7 @@ have, compare through the start point of the overlay."
   "Get the snippet table corresponding to MODE."
   (let ((table (gethash mode yas/snippet-tables)))
     (unless table
-      (setq table (make-hash-table :test 'equal))
+      (setq table (yas/make-snippet-table))
       (puthash mode table yas/snippet-tables))
     table))
 (defsubst yas/current-snippet-table ()
@@ -224,8 +309,9 @@ the template of a snippet in the current snippet-table."
       (setq syntaxes (cdr syntaxes))
       (save-excursion
        (skip-syntax-backward syntax)
-       (when (gethash (buffer-substring-no-properties (point) end)
-                      (yas/current-snippet-table))
+       (when (yas/snippet-table-fetch
+              (yas/current-snippet-table)
+              (buffer-substring-no-properties (point) end))
          (setq done t)
          (setq start (point)))))
     (list (buffer-substring-no-properties start end)
@@ -246,7 +332,7 @@ the template of a snippet in the current snippet-table."
                                   (overlay-start field-overlay))))
          (unless (eq field-overlay primary-overlay)
            (goto-char (overlay-start field-overlay))
-           (insert text)
+           (insert (yas/calculate-field-value field text))
            (if (= (overlay-start field-overlay)
                   (overlay-end field-overlay))
                (move-overlay field-overlay
@@ -270,14 +356,36 @@ the template of a snippet in the current snippet-table."
            (goto-char end)
            (delete-char (- (overlay-end overlay) end)))))
      (yas/synchronize-fields field-group))))
-(defun yas/overlay-insert-behind-hook (overlay after? beg end &optional length)
-  "Hook for snippet overlay when text is inserted just behind a snippet field."
+(defun yas/overlay-maybe-insert-behind-hook (overlay after? beg end &optional length)
+  "Insert behind hook sometimes doesn't get called. I don't know why.
+So I add modification hook in the big overlay and try to detect `insert-behind'
+event manually."
   (when (and after?
-            (null (yas/current-snippet-overlay beg))) ; not inside another field
-    (move-overlay overlay
-                 (overlay-start overlay)
-                 end)
-    (yas/synchronize-fields (overlay-get overlay 'yas/group))))
+            (= length 0)
+            (> end beg)
+            (null (yas/current-snippet-overlay beg))
+            (not (bobp)))
+    (let ((field-overlay (yas/current-snippet-overlay (1- beg))))
+      (if field-overlay
+         (when (= beg (overlay-end field-overlay))
+           (move-overlay field-overlay
+                         (overlay-start field-overlay)
+                         end)
+           (yas/synchronize-fields (overlay-get field-overlay 'yas/group)))
+       (let ((snippet (yas/snippet-of-current-keymap))
+             (done nil))
+         (if snippet
+             (do* ((tabstops (yas/snippet-tabstops snippet) (cdr tabstops))
+                   (tabstop (car tabstops) (car tabstops)))
+                 ((or (null tabstops)
+                      done))
+               (setq field-overlay (yas/field-overlay 
+                                    (yas/group-primary-field tabstop)))
+               (when (= beg
+                        (overlay-start field-overlay))
+                 (move-overlay field-overlay beg end)
+                 (yas/synchronize-fields tabstop)
+                 (setq done t)))))))))
 
 (defun yas/undo-expand-snippet (start end key snippet)
   "Undo a snippet expansion. Delete the overlays. This undo can't be
@@ -342,7 +450,13 @@ will be deleted before inserting template."
        ;; Step 5: Create fields
        (goto-char (point-min))
        (while (re-search-forward yas/field-regexp nil t)
-         (let ((number (match-string-no-properties 1)))
+         (let ((number (or (match-string-no-properties 1)
+                           (match-string-no-properties 2)))
+               (transform nil)
+               (value (match-string-no-properties 3)))
+           (when (eq (elt value 0) ?\$)
+             (setq transform (substring value 1))
+             (setq value nil))
            (if (and number
                     (string= "0" number))
                (progn
@@ -354,7 +468,8 @@ will be deleted before inserting template."
               (yas/make-field
                (make-overlay (match-beginning 0) (match-end 0))
                (and number (string-to-number number))
-               (match-string-no-properties 2))))))
+               value
+               transform)))))
 
        ;; Step 6: Sort and link each field group
        (setf (yas/snippet-groups snippet)
@@ -376,6 +491,12 @@ will be deleted before inserting template."
                                     nil
                                     nil
                                     t)))
+         (overlay-put overlay 
+                      'modification-hooks
+                      yas/keymap-overlay-modification-hooks)
+         (overlay-put overlay 
+                      'insert-behind-hooks
+                      yas/keymap-overlay-modification-hooks)
          (overlay-put overlay 'keymap yas/keymap)
          (overlay-put overlay 'yas/snippet-reference snippet)
          (setf (yas/snippet-overlay snippet) overlay))
@@ -391,7 +512,7 @@ will be deleted before inserting template."
                     (end (overlay-end overlay))
                     (length (- end start)))
                (goto-char start)
-               (insert value)
+               (insert (yas/calculate-field-value field value))
                (delete-char length)))))
 
        ;; Step 9: restore all escape characters
@@ -408,11 +529,12 @@ will be deleted before inserting template."
            (overlay-put overlay 'yas/modified? nil)
            (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks)
            (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks)
-           (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks)
+           (overlay-put overlay 'face 'yas/field-highlight-face)
            (dolist (field (yas/group-fields group))
-             (overlay-put (yas/field-overlay field)
-                          'face 
-                          'highlight))))
+             (unless (equal overlay (yas/field-overlay field))
+               (overlay-put (yas/field-overlay field)
+                            'face 
+                            'yas/mirror-highlight-face)))))
 
        ;; Step 11: move to end and make sure exit-marker exist
        (goto-char (point-max))
@@ -560,66 +682,230 @@ an example:
 (defun yas/fake-keymap-for-popup (templates)
   "Create a fake keymap for popup menu usage."
   (cons 'keymap 
-       (cons "Select a template:"
-             (mapcar (lambda (pair)
-                       (let* ((template (cdr pair))
-                              (name (yas/template-name template))
-                              (content (yas/template-content template)))
-                         (list content 'menu-item name t)))
-                     templates))))
-
+       (mapcar (lambda (pair)
+                 (let* ((template (cdr pair))
+                        (name (yas/template-name template))
+                        (content (yas/template-content template)))
+                   (list content 'menu-item name t)))
+               templates)))
+
+(defun yas/point-to-coord (&optional point)
+  "Get the xoffset/yoffset information of POINT.
+If POINT is not given, default is to current point.
+If `posn-at-point' is not available (like in Emacs 21.3),
+t is returned simply."
+  (if (fboundp 'posn-at-point)
+      (let ((x-y (posn-x-y (posn-at-point (or point (point))))))
+       (list (list (+ (car x-y) 10)
+                   (+ (cdr x-y) 20))
+             (selected-window)))
+    t))
 (defun yas/popup-for-template (templates)
   "Show a popup menu listing templates to let the user select one."
   (if window-system
-      (car (x-popup-menu t (yas/fake-keymap-for-popup templates)))
+      (car (x-popup-menu (yas/point-to-coord) 
+                        (yas/fake-keymap-for-popup templates)))
     ;; no window system, simply select the first one
     (cdar templates)))
 
+(defun yas/load-directory-1 (directory &optional parent)
+  "Really do the job of loading snippets from a directory 
+hierarchy."
+  (let ((mode-sym (intern (file-name-nondirectory directory)))
+       (snippets nil))
+    (with-temp-buffer
+      (dolist (file (yas/directory-files directory t))
+       (when (file-readable-p file)
+         (insert-file-contents file nil nil nil t)
+         (push (cons (file-name-nondirectory file)
+                     (yas/parse-template))
+               snippets))))
+    (yas/define-snippets mode-sym
+                        snippets
+                        parent)
+    (dolist (subdir (yas/directory-files directory nil))
+      (yas/load-directory-1 subdir mode-sym))))
+
+(defun yas/quote-string (string)
+  "Escape and quote STRING.
+foo\"bar\\! -> \"foo\\\"bar\\\\!\""
+  (concat "\""
+         (replace-regexp-in-string "[\\\"]"
+                                   "\\\\\\&"
+                                   string
+                                   t)
+         "\""))
+
+(defun yas/compile-bundle (yasnippet yasnippet-bundle snippet-roots)
+  "Compile snippets in SNIPPET-ROOTS to a single bundle file.
+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. Here's an example:
+
+ (yas/compile-bundle \"~/.emacs.d/plugins/yasnippet/yasnippet.el\"
+                     \"~/.emacs.d/plugins/yasnippet-bundle.el\"
+                     '(\"~/.emacs.d/plugins/yasnippet/snippets\"))"
+  (let ((dirs (or (and (listp snippet-roots) snippet-roots)
+                 (list snippet-roots)))
+       (bundle-buffer nil))
+    (with-temp-buffer
+      (setq bundle-buffer (current-buffer))
+      (insert-file-contents yasnippet)
+      (goto-char (point-max))
+      (insert ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
+      (insert ";;;;      Auto-generated code         ;;;;\n")
+      (insert ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
+      (insert "(yas/initialize)\n")
+      (flet ((yas/define-snippets 
+             (mode snippets &optional parent)
+             (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 (cadr snippet))
+                         (if (caddr snippet)
+                             (yas/quote-string (caddr snippet))
+                           "nil")
+                         ")\n"))
+               (insert "  )\n")
+               (insert (if parent
+                           (concat "'" (symbol-name parent))
+                         "nil")
+                       ")\n\n"))))
+           (dolist (dir dirs)
+             (dolist (subdir (yas/directory-files dir nil))
+               (yas/load-directory-1 subdir nil))))
+      (insert "(provide '"
+             (file-name-nondirectory
+              (file-name-sans-extension
+               yasnippet-bundle))
+             ")\n")
+      (setq buffer-file-name yasnippet-bundle)
+      (save-buffer))))
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; User level functions
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defun yas/default-trigger-fallback ()
+  "Default fallback when a snippet expansion failed.
+It looks key binding for TAB. If found, execute it. If not found.
+Run `indent-for-tab-command'."
+  (interactive)
+  (let ((command (key-binding (kbd "TAB"))))
+    (if (and command
+            (not (eq command 'yas/expand))
+            (not (eq command 'yas/next-field-group)))
+       (call-interactively command)
+      (call-interactively 'indent-for-tab-command))))
+
 (defun yas/about ()
   (interactive)
   (message (concat "yasnippet (version "
                   yas/version
                   ") -- pluskid <pluskid@gmail.com>")))
+(defun yas/reload-all ()
+  "Reload all snippets."
+  (interactive)
+  (if yas/root-directory
+      (yas/load-directory-1 yas/root-directory)
+    (call-interactively 'yas/load-directory))
+  (message "done."))
+
+(defun yas/load-directory (directory)
+  "Load snippet definition from a directory hierarchy.
+Below the top-level directory, each directory is a mode
+name. And under each subdirectory, each file is a definition
+of a snippet. The file name is the trigger key and the
+content of the file is the template."
+  (interactive "DSelect the root directory: ")
+  (unless yas/root-directory
+    (setq yas/root-directory directory))
+  (dolist (dir (yas/directory-files directory nil))
+    (yas/load-directory-1 dir))
+  (when (interactive-p)
+    (message "done.")))
+
 (defun yas/initialize ()
   "Do necessary initialization."
   (global-set-key yas/trigger-key 'yas/expand)
   (when yas/use-menu
-    (define-key-after global-map 
-      [menu-bar yasnippet]
+    (define-key-after 
+      (lookup-key global-map [menu-bar])
+      [yasnippet]
       (cons "YASnippet" yas/menu-keymap)
       'buffer)))
 
+(defun yas/define-snippets (mode snippets &optional parent-mode)
+  "Define snippets for MODE. SNIPPETS is a list of
+snippet definition, of the following form:
+ (KEY TEMPLATE NAME)
+or the NAME may be omitted. The optional 3rd parameter
+can be used to specify the parent mode of MODE. That is,
+when looking a snippet in MODE failed, it can refer to
+its parent mode. The PARENT-MODE may not need to be a 
+real mode."
+  (let ((snippet-table (yas/snippet-table mode))
+       (parent-table (if parent-mode
+                         (yas/snippet-table parent-mode)
+                       nil))
+       (keymap (if yas/use-menu
+                   (yas/menu-keymap-for-mode mode)
+                 nil)))
+    (when parent-table
+      (setf (yas/snippet-table-parent snippet-table)
+           parent-table)
+      (when yas/use-menu
+       (define-key keymap (vector 'parent-mode)
+         `(menu-item "parent mode"
+                     ,(yas/menu-keymap-for-mode parent-mode)))))
+    (when (and yas/use-menu
+              (yas/real-mode? mode))
+      (define-key yas/menu-keymap (vector mode)
+       `(menu-item ,(symbol-name mode) ,keymap)))
+    (dolist (snippet snippets)
+      (let* ((full-key (car snippet))
+            (key (file-name-sans-extension full-key))
+            (name (caddr snippet))
+            (template (yas/make-template (cadr snippet)
+                                         (or name key))))
+       (yas/snippet-table-store snippet-table
+                                full-key
+                                key
+                                template)
+       (when yas/use-menu
+         (define-key keymap (vector (make-symbol full-key))
+           `(menu-item ,(yas/template-name template)
+                       ,(yas/make-menu-binding (yas/template-content template))
+                       :keys ,(concat key yas/trigger-symbol))))))))
+
+(defun yas/set-mode-parent (mode parent)
+  "Set parent mode of MODE to PARENT."
+  (setf (yas/snippet-table-parent
+        (yas/snippet-table mode))
+       (yas/snippet-table parent))
+  (when yas/use-menu
+    (define-key (yas/menu-keymap-for-mode mode) (vector 'parent-mode)
+      `(menu-item "parent mode"
+                 ,(yas/menu-keymap-for-mode parent)))))
+
 (defun yas/define (mode key template &optional name)
   "Define a snippet. Expanding KEY into TEMPLATE.
 NAME is a description to this template. Also update
 the menu if `yas/use-menu' is `t'."
-  (let* ((full-key key)
-        (key (file-name-sans-extension full-key))
-        (template (yas/make-template template (or name key)))
-        (snippet-table (yas/snippet-table mode)))
-    (puthash key
-            (yas/modify-alist (gethash key snippet-table)
-                              full-key
-                              template)
-            snippet-table)
-    (when yas/use-menu
-      (let ((keymap (yas/menu-keymap-for-mode mode)))
-       (define-key yas/menu-keymap (vector mode) 
-         `(menu-item ,(symbol-name mode) ,keymap))
-       (define-key keymap (vector (make-symbol full-key))
-         `(menu-item ,(yas/template-name template)
-                     ,(yas/make-menu-binding (yas/template-content template))
-                     :keys ,(concat key yas/trigger-symbol)))))))
+  (yas/define-snippets mode
+                      (list (list key template name))))
+    
 
 (defun yas/expand ()
-  "Expand a snippet. When a snippet is expanded, t is returned,
-otherwise, nil returned."
+  "Expand a snippet."
   (interactive)
   (multiple-value-bind (key start end) (yas/current-key)
-    (let ((templates (gethash key (yas/current-snippet-table))))
+    (let ((templates (yas/snippet-table-fetch (yas/current-snippet-table)
+                                             key)))
       (if templates
          (let ((template (if (null (cdr templates)) ; only 1 template
                              (yas/template-content (cdar templates))
@@ -638,18 +924,18 @@ otherwise, nil returned."
       (let ((snippet (yas/snippet-of-current-keymap))
            (done nil))
        (if snippet
-         (do* ((tabstops (yas/snippet-tabstops snippet) (cdr tabstops))
-               (tabstop (car tabstops) (car tabstops)))
-             ((or (null tabstops)
-                  done)
-              (unless done (message "Not in a snippet field.")))
-           (when (= (point)
-                    (overlay-start
-                     (yas/field-overlay
-                      (yas/group-primary-field tabstop))))
-             (setq done t)
-             (yas/navigate-group tabstop t)))
-         (message "Not in a snippet field."))))))
+           (do* ((tabstops (yas/snippet-tabstops snippet) (cdr tabstops))
+                 (tabstop (car tabstops) (car tabstops)))
+               ((or (null tabstops)
+                    done)
+                (unless done (call-interactively 'yas/expand)))
+             (when (= (point)
+                      (overlay-start
+                       (yas/field-overlay
+                        (yas/group-primary-field tabstop))))
+               (setq done t)
+               (yas/navigate-group tabstop t)))
+         (call-interactively 'yas/expand))))))
 
 (defun yas/prev-field-group ()
   "Navigate to prev field group. If there's none, exit the snippet."
@@ -682,22 +968,19 @@ otherwise, nil returned."
     (dolist (field (yas/group-fields group))
       (delete-overlay (yas/field-overlay field)))))
 
-(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."
-  (with-temp-buffer
-    (dolist (mode (yas/directory-files directory nil))
-      (let ((mode-sym (intern (file-name-nondirectory mode))))
-       (dolist (file (yas/directory-files mode t))
-         (when (file-readable-p file)
-           (insert-file-contents file nil nil nil t)
-           (multiple-value-bind 
-               (key template name)
-               (cons (file-name-nondirectory file)
-                     (yas/parse-template))
-             (yas/define mode-sym key template name))))))))
-
 (provide 'yasnippet)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 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)))))