X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/8ef771a69698d161f677b4dc050cb1071b3ba4ec..e98815c030f16fa7e372bbf33ad11b0ef336dfc7:/yasnippet.el?ds=sidebyside diff --git a/yasnippet.el b/yasnippet.el index 6fc1f147f..d149fcc84 100644 --- a/yasnippet.el +++ b/yasnippet.el @@ -1,7 +1,10 @@ ;;; yasnippet.el --- Yet another snippet extension for Emacs. +;; Copyright 2008 pluskid +;; ;; Author: pluskid -;; 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 @@ -20,14 +23,24 @@ ;;; 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 \"_\": @@ -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 "") "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 "") '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 "") 'yas/prev-field-group) (define-key yas/keymap (kbd "") '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." @@ -137,8 +184,10 @@ mode will be listed under the menu \"yasnippet\".") (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) @@ -177,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)) @@ -196,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 () @@ -223,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) @@ -245,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 @@ -269,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 @@ -341,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 @@ -353,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) @@ -375,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)) @@ -390,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 @@ -407,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)) @@ -559,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 "))) +(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)) @@ -637,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." @@ -681,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)))))