;;; 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)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; User customizable variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(defvar yas/key-syntaxes (list "w" "w_")
+(defvar yas/key-syntaxes (list "w" "w_" "w_." "^ ")
"A list of syntax of a key. This list is tried in the order
to try to find a key. For example, if the list is '(\"w\" \"w_\").
And in emacs-lisp-mode, where \"-\" has the syntax of \"_\":
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.")
;; 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
(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")
(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.")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(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."
(yas/snippet-groups snippet)
:test
'(lambda (field group)
- (= (yas/field-number field)
- (yas/group-number group))))))
+ (and (not (null (yas/field-number field)))
+ (not (null (yas/group-number group)))
+ (= (yas/field-number field)
+ (yas/group-number group)))))))
(if group
(yas/group-add-field group field)
(push (yas/make-group field snippet)
(< (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))
"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 ()
(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)
(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
(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
;; 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
(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)
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))
(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
(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))
(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))
(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."
(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)))))