1 ;;; yasnippet.el --- Yet another snippet extension for Emacs.
3 ;; Copyright 2008 pluskid
5 ;; Author: pluskid <pluskid@gmail.com>
7 ;; X-URL: http://code.google.com/p/yasnippet/
9 ;; This file is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; This file is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING. If not, write to
21 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
26 ;; Basic steps to setup:
27 ;; 1. Place `yasnippet.el' in your `load-path'.
28 ;; 2. In your .emacs file:
29 ;; (require 'yasnippet)
30 ;; 3. Place the `snippets' directory somewhere. E.g: ~/.emacs.d/snippets
31 ;; 4. In your .emacs file
33 ;; (yas/load-directory "~/.emacs.d/snippets")
35 ;; For more information and detailed usage, refer to the project page:
36 ;; http://code.google.com/p/yasnippet/
40 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
41 ;; User customizable variables
42 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
43 (defvar yas/key-syntaxes (list "w" "w_" "w_." "^ ")
44 "A list of syntax of a key. This list is tried in the order
45 to try to find a key. For example, if the list is '(\"w\" \"w_\").
46 And in emacs-lisp-mode, where \"-\" has the syntax of \"_\":
50 will first try \"bar\", if not found, then \"foo-bar\" is tried.")
52 (defvar yas/root-directory nil
53 "The root directory that stores the snippets for each major modes.")
55 (defvar yas/indent-line t
56 "Each (except the 1st) line of the snippet template is indented to
57 current column if this variable is non-`nil'.")
58 (make-variable-buffer-local 'yas/indent-line)
60 (defvar yas/trigger-key (kbd "<tab>")
61 "The key to bind as a trigger of snippet.")
62 (defvar yas/trigger-fallback 'yas/default-trigger-fallback
63 "The fallback command to call when there's no snippet to expand.")
64 (make-variable-buffer-local 'yas/trigger-fallback)
66 (defvar yas/keymap (make-sparse-keymap)
67 "The keymap of snippet.")
68 (define-key yas/keymap (kbd "<tab>") 'yas/next-field-group)
69 (define-key yas/keymap (kbd "TAB") 'yas/next-field-group)
70 (define-key yas/keymap (kbd "S-TAB") 'yas/prev-field-group)
71 (define-key yas/keymap (kbd "<S-iso-lefttab>") 'yas/prev-field-group)
72 (define-key yas/keymap (kbd "<S-tab>") 'yas/prev-field-group)
74 (defvar yas/show-all-modes-in-menu nil
75 "Currently yasnippet only all \"real modes\" to menubar. For
76 example, you define snippets for \"cc-mode\" and make it the
77 parent of `c-mode', `c++-mode' and `java-mode'. There's really
78 no such mode like \"cc-mode\". So we don't show it in the yasnippet
79 menu to avoid the menu becoming too big with strange modes. The
80 snippets defined for \"cc-mode\" can still be accessed from
81 menu-bar->c-mode->parent (or c++-mode, java-mode, all are ok).
82 However, if you really like to show all modes in the menu, set
84 (defvar yas/use-menu t
85 "If this is set to `t', all snippet template of the current
86 mode will be listed under the menu \"yasnippet\".")
87 (defvar yas/trigger-symbol " =>"
88 "The text that will be used in menu to represent the trigger.")
90 (defface yas/field-highlight-face
91 '((((class color) (background light)) (:background "DarkSeaGreen2"))
92 (t (:background "DimGrey")))
93 "The face used to highlight a field of snippet.")
94 (defface yas/mirror-highlight-face
95 '((((class color) (background light)) (:background "LightYellow2"))
96 (t (:background "gray22")))
97 "The face used to highlight mirror fields of a snippet.")
98 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
100 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
101 (defvar yas/version "0.2.1")
103 (defvar yas/snippet-tables (make-hash-table)
104 "A hash table of snippet tables corresponding to each major-mode.")
105 (defvar yas/menu-table (make-hash-table)
106 "A hash table of menus of corresponding major-mode.")
107 (defvar yas/menu-keymap (make-sparse-keymap "YASnippet"))
108 ;; empty menu will cause problems, so we insert some items
109 (define-key yas/menu-keymap [yas/about]
110 '(menu-item "About" yas/about))
111 (define-key yas/menu-keymap [yas/reload]
112 '(menu-item "Reload all snippets" yas/reload-all))
113 (define-key yas/menu-keymap [yas/separator]
116 (defvar yas/known-modes
117 '(ruby-mode rst-mode)
118 "A list of mode which is well known but not part of emacs.")
119 (defconst yas/escape-backslash
120 (concat "YASESCAPE" "BACKSLASH" "PROTECTGUARD"))
121 (defconst yas/escape-dollar
122 (concat "YASESCAPE" "DOLLAR" "PROTECTGUARD"))
123 (defconst yas/escape-backquote
124 (concat "YASESCAPE" "BACKQUOTE" "PROTECTGUARD"))
126 (defconst yas/field-regexp
127 (concat "$\\([0-9]+\\)" "\\|"
128 "${\\(?:\\([0-9]+\\):\\)?\\([^}]*\\)}"))
130 (defvar yas/snippet-id-seed 0
131 "Contains the next id for a snippet")
132 (defun yas/snippet-next-id ()
133 (let ((id yas/snippet-id-seed))
134 (incf yas/snippet-id-seed)
137 (defvar yas/overlay-modification-hooks
138 (list 'yas/overlay-modification-hook)
139 "The list of hooks to the overlay modification event.")
140 (defvar yas/overlay-insert-in-front-hooks
141 (list 'yas/overlay-insert-in-front-hook)
142 "The list of hooks of the overlay inserted in front event.")
143 (defvar yas/keymap-overlay-modification-hooks
144 (list 'yas/overlay-maybe-insert-behind-hook)
145 "The list of hooks of the big keymap overlay modification event.")
148 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
150 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
151 (defstruct (yas/template (:constructor yas/make-template (content name)))
152 "A template for a snippet."
155 (defstruct (yas/snippet (:constructor yas/make-snippet ()))
158 (tabstops nil) ; tabstops are those groups whose init value is empty
160 (id (yas/snippet-next-id) :read-only t)
162 (defstruct (yas/group (:constructor yas/make-group (primary-field snippet)))
163 "A group contains a list of field with the same number."
165 (fields (list primary-field))
169 (defstruct (yas/field
170 (:constructor yas/make-field (overlay number value transform)))
171 "A field in a snippet."
176 (defstruct (yas/snippet-table (:constructor yas/make-snippet-table ()))
177 "A table to store snippets for a perticular mode."
178 (hash (make-hash-table :test 'equal))
181 (defun yas/snippet-add-field (snippet field)
182 "Add FIELD to SNIPPET."
183 (let ((group (find field
184 (yas/snippet-groups snippet)
186 '(lambda (field group)
187 (and (not (null (yas/field-number field)))
188 (not (null (yas/group-number group)))
189 (= (yas/field-number field)
190 (yas/group-number group)))))))
192 (yas/group-add-field group field)
193 (push (yas/make-group field snippet)
194 (yas/snippet-groups snippet)))))
196 (defun yas/group-value (group)
197 "Get the default value of the field group."
199 (yas/group-primary-field group))
201 (defun yas/group-number (group)
202 "Get the number of the field group."
204 (yas/group-primary-field group)))
205 (defun yas/group-add-field (group field)
206 "Add a field to the field group. If the value of the primary
207 field is nil and that of the field is not nil, the field is set
208 as the primary field of the group."
209 (push field (yas/group-fields group))
210 (when (and (null (yas/field-value (yas/group-primary-field group)))
211 (yas/field-value field))
212 (setf (yas/group-primary-field group) field)))
214 (defun yas/snippet-field-compare (field1 field2)
215 "Compare two fields. The field with a number is sorted first.
216 If they both have a number, compare through the number. If neither
217 have, compare through the start point of the overlay."
218 (let ((n1 (yas/field-number field1))
219 (n2 (yas/field-number field2)))
226 (< (overlay-start (yas/field-overlay field1))
227 (overlay-start (yas/field-overlay field2)))))))
229 (defun yas/snippet-table-fetch (table key)
230 "Fetch a snippet binding to KEY from TABLE. If not found,
231 fetch from parent if any."
232 (let ((templates (gethash key (yas/snippet-table-hash table))))
233 (when (and (null templates)
234 (not (null (yas/snippet-table-parent table))))
235 (setq templates (yas/snippet-table-fetch
236 (yas/snippet-table-parent table)
239 (defun yas/snippet-table-store (table full-key key template)
240 "Store a snippet template in the table."
242 (yas/modify-alist (gethash key
243 (yas/snippet-table-hash table))
246 (yas/snippet-table-hash table)))
248 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
249 ;; Internal functions
250 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
251 (defun yas/real-mode? (mode)
252 "Try to find out if MODE is a real mode. The MODE bound to
253 a function (like `c-mode') is considered real mode. Other well
254 known mode like `ruby-mode' which is not part of Emacs might
255 not bound to a function until it is loaded. So yasnippet keeps
256 a list of modes like this to help the judgement."
258 (find mode yas/known-modes)))
260 (defun yas/eval-string (string)
261 "Evaluate STRING and convert the result to string."
263 (format "%s" (eval (read string)))
264 (error (format "(error in elisp evaluation: %s)"
265 (error-message-string err)))))
266 (defun yas/calculate-field-value (field value)
267 "Calculate the value of the field. If there's a transform
268 for this field, apply it. Otherwise, the value is returned
271 (transform (yas/field-transform field)))
273 (yas/eval-string transform)
275 (defsubst yas/replace-all (from to)
276 "Replace all occurance from FROM to TO."
277 (goto-char (point-min))
278 (while (search-forward from nil t)
279 (replace-match to t t)))
281 (defun yas/snippet-table (mode)
282 "Get the snippet table corresponding to MODE."
283 (let ((table (gethash mode yas/snippet-tables)))
285 (setq table (yas/make-snippet-table))
286 (puthash mode table yas/snippet-tables))
288 (defsubst yas/current-snippet-table ()
289 "Get the snippet table for current major-mode."
290 (yas/snippet-table major-mode))
292 (defun yas/menu-keymap-for-mode (mode)
293 "Get the menu keymap correspondong to MODE."
294 (let ((keymap (gethash mode yas/menu-table)))
296 (setq keymap (make-sparse-keymap))
297 (puthash mode keymap yas/menu-table))
300 (defun yas/current-key ()
301 "Get the key under current position. A key is used to find
302 the template of a snippet in the current snippet-table."
303 (let ((start (point))
305 (syntaxes yas/key-syntaxes)
307 (while (and (not done) syntaxes)
308 (setq syntax (car syntaxes))
309 (setq syntaxes (cdr syntaxes))
311 (skip-syntax-backward syntax)
312 (when (yas/snippet-table-fetch
313 (yas/current-snippet-table)
314 (buffer-substring-no-properties (point) end))
316 (setq start (point)))))
317 (list (buffer-substring-no-properties start end)
321 (defun yas/synchronize-fields (field-group)
322 "Update all fields' text according to the primary field."
324 (let* ((inhibit-modification-hooks t)
325 (primary (yas/group-primary-field field-group))
326 (primary-overlay (yas/field-overlay primary))
327 (text (buffer-substring-no-properties (overlay-start primary-overlay)
328 (overlay-end primary-overlay))))
329 (dolist (field (yas/group-fields field-group))
330 (let* ((field-overlay (yas/field-overlay field))
331 (original-length (- (overlay-end field-overlay)
332 (overlay-start field-overlay))))
333 (unless (eq field-overlay primary-overlay)
334 (goto-char (overlay-start field-overlay))
335 (insert (yas/calculate-field-value field text))
336 (if (= (overlay-start field-overlay)
337 (overlay-end field-overlay))
338 (move-overlay field-overlay
339 (overlay-start field-overlay)
341 (delete-char original-length))))))))
343 (defun yas/overlay-modification-hook (overlay after? beg end &optional length)
344 "Modification hook for snippet field overlay."
345 (when (and after? (not undo-in-progress))
346 (yas/synchronize-fields (overlay-get overlay 'yas/group))))
347 (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length)
348 "Hook for snippet overlay when text is inserted in front of a snippet field."
350 (let ((field-group (overlay-get overlay 'yas/group))
351 (inhibit-modification-hooks t))
352 (when (not (overlay-get overlay 'yas/modified?))
353 (overlay-put overlay 'yas/modified? t)
354 (when (> (overlay-end overlay) end)
357 (delete-char (- (overlay-end overlay) end)))))
358 (yas/synchronize-fields field-group))))
359 (defun yas/overlay-maybe-insert-behind-hook (overlay after? beg end &optional length)
360 "Insert behind hook sometimes doesn't get called. I don't know why.
361 So I add modification hook in the big overlay and try to detect `insert-behind'
366 (null (yas/current-snippet-overlay beg))
368 (let ((field-overlay (yas/current-snippet-overlay (1- beg))))
370 (when (= beg (overlay-end field-overlay))
371 (move-overlay field-overlay
372 (overlay-start field-overlay)
374 (yas/synchronize-fields (overlay-get field-overlay 'yas/group)))
375 (let ((snippet (yas/snippet-of-current-keymap))
378 (do* ((tabstops (yas/snippet-tabstops snippet) (cdr tabstops))
379 (tabstop (car tabstops) (car tabstops)))
382 (setq field-overlay (yas/field-overlay
383 (yas/group-primary-field tabstop)))
385 (overlay-start field-overlay))
386 (move-overlay field-overlay beg end)
387 (yas/synchronize-fields tabstop)
388 (setq done t)))))))))
390 (defun yas/undo-expand-snippet (start end key snippet)
391 "Undo a snippet expansion. Delete the overlays. This undo can't be
393 (let ((undo (car buffer-undo-list)))
395 (setq buffer-undo-list (cdr buffer-undo-list))
396 (setq undo (car buffer-undo-list)))
397 ;; Remove this undo operation record
398 (setq buffer-undo-list (cdr buffer-undo-list))
399 (let ((inhibit-modification-hooks t)
400 (buffer-undo-list t))
401 (yas/exit-snippet snippet)
403 (delete-char (- end start))
406 (defun yas/expand-snippet (start end template)
407 "Expand snippet at current point. Text between START and END
408 will be deleted before inserting template."
411 (let ((key (buffer-substring-no-properties start end))
412 (original-undo-list buffer-undo-list)
413 (inhibit-modification-hooks t)
414 (length (- end start))
415 (column (current-column)))
417 (narrow-to-region start start)
419 (setq buffer-undo-list t)
422 ;; Step 1: do necessary indent
423 (when yas/indent-line
424 (let* ((indent (if indent-tabs-mode
425 (concat (make-string (/ column tab-width) ?\t)
426 (make-string (% column tab-width) ?\ ))
427 (make-string column ?\ ))))
428 (goto-char (point-min))
429 (while (and (zerop (forward-line))
430 (= (current-column) 0))
433 ;; Step 2: protect backslash and backquote
434 (yas/replace-all "\\\\" yas/escape-backslash)
435 (yas/replace-all "\\`" yas/escape-backquote)
437 ;; Step 3: evaluate all backquotes
438 (goto-char (point-min))
439 (while (re-search-forward "`\\([^`]*\\)`" nil t)
440 (replace-match (yas/eval-string (match-string-no-properties 1))
443 ;; Step 4: protect all escapes, including backslash and backquot
444 ;; which may be produced in Step 3
445 (yas/replace-all "\\\\" yas/escape-backslash)
446 (yas/replace-all "\\`" yas/escape-backquote)
447 (yas/replace-all "\\$" yas/escape-dollar)
449 (let ((snippet (yas/make-snippet)))
450 ;; Step 5: Create fields
451 (goto-char (point-min))
452 (while (re-search-forward yas/field-regexp nil t)
453 (let ((number (or (match-string-no-properties 1)
454 (match-string-no-properties 2)))
456 (value (match-string-no-properties 3)))
457 (when (eq (elt value 0) ?\$)
458 (setq transform (substring value 1))
461 (string= "0" number))
464 (setf (yas/snippet-exit-marker snippet)
465 (copy-marker (point) t)))
466 (yas/snippet-add-field
469 (make-overlay (match-beginning 0) (match-end 0))
470 (and number (string-to-number number))
474 ;; Step 6: Sort and link each field group
475 (setf (yas/snippet-groups snippet)
476 (sort (yas/snippet-groups snippet)
477 '(lambda (group1 group2)
478 (yas/snippet-field-compare
479 (yas/group-primary-field group1)
480 (yas/group-primary-field group2)))))
482 (dolist (group (yas/snippet-groups snippet))
483 (setf (yas/group-prev group) prev)
485 (setf (yas/group-next prev) group))
488 ;; Step 7: Create keymap overlay for snippet
489 (let ((overlay (make-overlay (point-min)
496 yas/keymap-overlay-modification-hooks)
499 yas/keymap-overlay-modification-hooks)
500 (overlay-put overlay 'keymap yas/keymap)
501 (overlay-put overlay 'yas/snippet-reference snippet)
502 (setf (yas/snippet-overlay snippet) overlay))
504 ;; Step 8: Replace fields with default values
505 (dolist (group (yas/snippet-groups snippet))
506 (let ((value (yas/group-value group)))
507 (when (string= "" value)
508 (push group (yas/snippet-tabstops snippet)))
509 (dolist (field (yas/group-fields group))
510 (let* ((overlay (yas/field-overlay field))
511 (start (overlay-start overlay))
512 (end (overlay-end overlay))
513 (length (- end start)))
515 (insert (yas/calculate-field-value field value))
516 (delete-char length)))))
518 ;; Step 9: restore all escape characters
519 (yas/replace-all yas/escape-dollar "$")
520 (yas/replace-all yas/escape-backquote "`")
521 (yas/replace-all yas/escape-backslash "\\")
523 ;; Step 10: Set up properties of overlays
524 (dolist (group (yas/snippet-groups snippet))
525 (let ((overlay (yas/field-overlay
526 (yas/group-primary-field group))))
527 (overlay-put overlay 'yas/snippet snippet)
528 (overlay-put overlay 'yas/group group)
529 (overlay-put overlay 'yas/modified? nil)
530 (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks)
531 (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks)
532 (overlay-put overlay 'face 'yas/field-highlight-face)
533 (dolist (field (yas/group-fields group))
534 (unless (equal overlay (yas/field-overlay field))
535 (overlay-put (yas/field-overlay field)
537 'yas/mirror-highlight-face)))))
539 ;; Step 11: move to end and make sure exit-marker exist
540 (goto-char (point-max))
541 (unless (yas/snippet-exit-marker snippet)
542 (setf (yas/snippet-exit-marker snippet) (copy-marker (point) t)))
544 ;; Step 12: Construct undo information
545 (unless (eq original-undo-list t)
546 (add-to-list 'original-undo-list
547 `(apply yas/undo-expand-snippet
553 ;; Step 13: remove the trigger key
557 (setq buffer-undo-list original-undo-list)
559 ;; Step 14: place the cursor at a proper place
560 (let ((groups (yas/snippet-groups snippet))
561 (exit-marker (yas/snippet-exit-marker snippet)))
563 (goto-char (overlay-start
565 (yas/group-primary-field
567 ;; no need to call exit-snippet, since no overlay created.
568 (yas/exit-snippet snippet)))))))
570 (defun yas/current-snippet-overlay (&optional point)
571 "Get the most proper overlay which is belongs to a snippet."
572 (let ((point (or point (point)))
573 (snippet-overlay nil))
574 (dolist (overlay (overlays-at point))
575 (when (overlay-get overlay 'yas/snippet)
576 (if (null snippet-overlay)
577 (setq snippet-overlay overlay)
578 (when (> (yas/snippet-id (overlay-get overlay 'yas/snippet))
579 (yas/snippet-id (overlay-get snippet-overlay 'yas/snippet)))
580 (setq snippet-overlay overlay)))))
583 (defun yas/snippet-of-current-keymap (&optional point)
584 "Get the snippet holding the snippet keymap under POINT."
585 (let ((point (or point (point)))
588 (dolist (overlay (overlays-at point))
589 (setq snippet (overlay-get overlay 'yas/snippet-reference))
591 (if (null keymap-snippet)
592 (setq keymap-snippet snippet)
593 (when (> (yas/snippet-id snippet)
594 (yas/snippet-id keymap-snippet))
595 (setq keymap-snippet snippet)))))
598 (defun yas/current-overlay-for-navigation ()
599 "Get current overlay for navigation. Might be overlay at current or previous point."
600 (let ((overlay1 (yas/current-snippet-overlay))
603 (yas/current-snippet-overlay (- (point) 1)))))
606 (if (or (null overlay2)
607 (eq (overlay-get overlay1 'yas/snippet)
608 (overlay-get overlay2 'yas/snippet)))
610 (if (> (yas/snippet-id (overlay-get overlay2 'yas/snippet))
611 (yas/snippet-id (overlay-get overlay1 'yas/snippet)))
615 (defun yas/navigate-group (group next?)
616 "Go to next of previous field group. Exit snippet if none."
617 (let ((target (if next?
618 (yas/group-next group)
619 (yas/group-prev group))))
621 (goto-char (overlay-start
623 (yas/group-primary-field target))))
624 (yas/exit-snippet (yas/group-snippet group)))))
626 (defun yas/parse-template ()
627 "Parse the template in the current buffer.
628 If the buffer contains a line of \"# --\" then the contents
629 above this line are ignored. Variables can be set above this
630 line through the syntax:
634 Currently only the \"name\" variable is recognized. Here's
637 #name: #include \"...\"
640 (goto-char (point-min))
641 (let (template name bound)
642 (if (re-search-forward "^# --\n" nil t)
643 (progn (setq template
644 (buffer-substring-no-properties (point)
647 (goto-char (point-min))
648 (while (re-search-forward "^#\\([^ ]+\\) *: *\\(.*\\)$" bound t)
649 (when (string= "name" (match-string-no-properties 1))
650 (setq name (match-string-no-properties 2)))))
652 (buffer-substring-no-properties (point-min) (point-max))))
653 (list template name)))
655 (defun yas/directory-files (directory file?)
656 "Return directory files or subdirectories in full path."
657 (remove-if (lambda (file)
658 (or (string-match "^\\."
659 (file-name-nondirectory file))
661 (file-directory-p file)
662 (not (file-directory-p file)))))
663 (directory-files directory t)))
665 (defun yas/make-menu-binding (template)
666 (lexical-let ((template template))
669 (yas/expand-snippet (point)
673 (defun yas/modify-alist (alist key value)
674 "Modify ALIST to map KEY to VALUE. return the new alist."
675 (let ((pair (assoc key alist)))
677 (cons (cons key value)
682 (defun yas/fake-keymap-for-popup (templates)
683 "Create a fake keymap for popup menu usage."
685 (mapcar (lambda (pair)
686 (let* ((template (cdr pair))
687 (name (yas/template-name template))
688 (content (yas/template-content template)))
689 (list content 'menu-item name t)))
692 (defun yas/point-to-coord (&optional point)
693 "Get the xoffset/yoffset information of POINT.
694 If POINT is not given, default is to current point.
695 If `posn-at-point' is not available (like in Emacs 21.3),
696 t is returned simply."
697 (if (fboundp 'posn-at-point)
698 (let ((x-y (posn-x-y (posn-at-point (or point (point))))))
699 (list (list (+ (car x-y) 10)
704 (defun yas/popup-for-template (templates)
705 "Show a popup menu listing templates to let the user select one."
707 (car (x-popup-menu (yas/point-to-coord)
708 (yas/fake-keymap-for-popup templates)))
709 ;; no window system, simply select the first one
712 (defun yas/load-directory-1 (directory &optional parent)
713 "Really do the job of loading snippets from a directory
715 (let ((mode-sym (intern (file-name-nondirectory directory)))
718 (dolist (file (yas/directory-files directory t))
719 (when (file-readable-p file)
720 (insert-file-contents file nil nil nil t)
721 (push (cons (file-name-nondirectory file)
722 (yas/parse-template))
724 (yas/define-snippets mode-sym
727 (dolist (subdir (yas/directory-files directory nil))
728 (yas/load-directory-1 subdir mode-sym))))
730 (defun yas/quote-string (string)
731 "Escape and quote STRING.
732 foo\"bar\\! -> \"foo\\\"bar\\\\!\""
734 (replace-regexp-in-string "[\\\"]"
740 (defun yas/compile-bundle (yasnippet yasnippet-bundle snippet-roots)
741 "Compile snippets in SNIPPET-ROOTS to a single bundle file.
742 SNIPPET-ROOTS is a list of root directories that contains the snippets
743 definition. YASNIPPET is the yasnippet.el file path. YASNIPPET-BUNDLE
744 is the output file of the compile result. Here's an example:
746 (yas/compile-bundle \"~/.emacs.d/plugins/yasnippet/yasnippet.el\"
747 \"~/.emacs.d/plugins/yasnippet-bundle.el\"
748 '(\"~/.emacs.d/plugins/yasnippet/snippets\"))"
749 (let ((dirs (or (and (listp snippet-roots) snippet-roots)
750 (list snippet-roots)))
753 (setq bundle-buffer (current-buffer))
754 (insert-file-contents yasnippet)
755 (goto-char (point-max))
756 (insert ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
757 (insert ";;;; Auto-generated code ;;;;\n")
758 (insert ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
759 (insert "(yas/initialize)\n")
760 (flet ((yas/define-snippets
761 (mode snippets &optional parent)
762 (with-current-buffer bundle-buffer
763 (insert ";;; snippets for " (symbol-name mode) "\n")
764 (insert "(yas/define-snippets '" (symbol-name mode) "\n")
766 (dolist (snippet snippets)
768 (yas/quote-string (car snippet))
769 (yas/quote-string (cadr snippet))
771 (yas/quote-string (caddr snippet))
776 (concat "'" (symbol-name parent))
780 (dolist (subdir (yas/directory-files dir nil))
781 (yas/load-directory-1 subdir nil))))
783 (file-name-nondirectory
784 (file-name-sans-extension
787 (setq buffer-file-name yasnippet-bundle)
790 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
791 ;; User level functions
792 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
793 (defun yas/default-trigger-fallback ()
794 "Default fallback when a snippet expansion failed.
795 It looks key binding for TAB. If found, execute it. If not found.
796 Run `indent-for-tab-command'."
798 (let ((command (key-binding (kbd "TAB"))))
800 (not (eq command 'yas/expand)))
801 (call-interactively command)
802 (call-interactively 'indent-for-tab-command))))
806 (message (concat "yasnippet (version "
808 ") -- pluskid <pluskid@gmail.com>")))
809 (defun yas/reload-all ()
810 "Reload all snippets."
812 (if yas/root-directory
813 (yas/load-directory-1 yas/root-directory)
814 (call-interactively 'yas/load-directory))
817 (defun yas/load-directory (directory)
818 "Load snippet definition from a directory hierarchy.
819 Below the top-level directory, each directory is a mode
820 name. And under each subdirectory, each file is a definition
821 of a snippet. The file name is the trigger key and the
822 content of the file is the template."
823 (interactive "DSelect the root directory: ")
824 (unless yas/root-directory
825 (setq yas/root-directory directory))
826 (dolist (dir (yas/directory-files directory nil))
827 (yas/load-directory-1 dir))
828 (when (interactive-p)
831 (defun yas/initialize ()
832 "Do necessary initialization."
833 (global-set-key yas/trigger-key 'yas/expand)
836 (lookup-key global-map [menu-bar])
838 (cons "YASnippet" yas/menu-keymap)
841 (defun yas/define-snippets (mode snippets &optional parent-mode)
842 "Define snippets for MODE. SNIPPETS is a list of
843 snippet definition, of the following form:
845 or the NAME may be omitted. The optional 3rd parameter
846 can be used to specify the parent mode of MODE. That is,
847 when looking a snippet in MODE failed, it can refer to
848 its parent mode. The PARENT-MODE may not need to be a
850 (let ((snippet-table (yas/snippet-table mode))
851 (parent-table (if parent-mode
852 (yas/snippet-table parent-mode)
854 (keymap (if yas/use-menu
855 (yas/menu-keymap-for-mode mode)
858 (setf (yas/snippet-table-parent snippet-table)
861 (define-key keymap (vector 'parent-mode)
862 `(menu-item "parent mode"
863 ,(yas/menu-keymap-for-mode parent-mode)))))
864 (when (and yas/use-menu
865 (yas/real-mode? mode))
866 (define-key yas/menu-keymap (vector mode)
867 `(menu-item ,(symbol-name mode) ,keymap)))
868 (dolist (snippet snippets)
869 (let* ((full-key (car snippet))
870 (key (file-name-sans-extension full-key))
871 (name (caddr snippet))
872 (template (yas/make-template (cadr snippet)
874 (yas/snippet-table-store snippet-table
879 (define-key keymap (vector (make-symbol full-key))
880 `(menu-item ,(yas/template-name template)
881 ,(yas/make-menu-binding (yas/template-content template))
882 :keys ,(concat key yas/trigger-symbol))))))))
884 (defun yas/set-mode-parent (mode parent)
885 "Set parent mode of MODE to PARENT."
886 (setf (yas/snippet-table-parent
887 (yas/snippet-table mode))
888 (yas/snippet-table parent))
890 (define-key (yas/menu-keymap-for-mode mode) (vector 'parent-mode)
891 `(menu-item "parent mode"
892 ,(yas/menu-keymap-for-mode parent)))))
894 (defun yas/define (mode key template &optional name)
895 "Define a snippet. Expanding KEY into TEMPLATE.
896 NAME is a description to this template. Also update
897 the menu if `yas/use-menu' is `t'."
898 (yas/define-snippets mode
899 (list (list key template name))))
905 (multiple-value-bind (key start end) (yas/current-key)
906 (let ((templates (yas/snippet-table-fetch (yas/current-snippet-table)
909 (let ((template (if (null (cdr templates)) ; only 1 template
910 (yas/template-content (cdar templates))
911 (yas/popup-for-template templates))))
913 (yas/expand-snippet start end template)))
914 (when yas/trigger-fallback
915 (call-interactively yas/trigger-fallback))))))
917 (defun yas/next-field-group ()
918 "Navigate to next field group. If there's none, exit the snippet."
920 (let ((overlay (yas/current-overlay-for-navigation)))
922 (yas/navigate-group (overlay-get overlay 'yas/group) t)
923 (let ((snippet (yas/snippet-of-current-keymap))
926 (do* ((tabstops (yas/snippet-tabstops snippet) (cdr tabstops))
927 (tabstop (car tabstops) (car tabstops)))
930 (unless done (message "Not in a snippet field.")))
934 (yas/group-primary-field tabstop))))
936 (yas/navigate-group tabstop t)))
937 (message "Not in a snippet field."))))))
939 (defun yas/prev-field-group ()
940 "Navigate to prev field group. If there's none, exit the snippet."
942 (let ((overlay (yas/current-overlay-for-navigation)))
944 (yas/navigate-group (overlay-get overlay 'yas/group) nil)
945 (let ((snippet (yas/snippet-of-current-keymap))
948 (do* ((tabstops (yas/snippet-tabstops snippet) (cdr tabstops))
949 (tabstop (car tabstops) (car tabstops)))
952 (unless done (message "Not in a snippet field.")))
956 (yas/group-primary-field tabstop))))
958 (yas/navigate-group tabstop nil)))
959 (message "Not in a snippet field."))))))
961 (defun yas/exit-snippet (snippet)
962 "Goto exit-marker of SNIPPET and delete the snippet."
964 (goto-char (yas/snippet-exit-marker snippet))
965 (delete-overlay (yas/snippet-overlay snippet))
966 (dolist (group (yas/snippet-groups snippet))
967 (dolist (field (yas/group-fields group))
968 (delete-overlay (yas/field-overlay field)))))
972 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
973 ;; Monkey patching for other functions that's causing
974 ;; problems to yasnippet. For details on why I patch
975 ;; those functions, refer to
976 ;; http://code.google.com/p/yasnippet/wiki/MonkeyPatching
977 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
978 (defadvice c-neutralize-syntax-in-CPP
979 (around yas-mp/c-neutralize-syntax-in-CPP activate)
980 "Adviced `c-neutralize-syntax-in-CPP' to properly
981 handle the end-of-buffer error fired in it by calling
982 `forward-char' at the end of buffer."
985 (error (message (error-message-string err)))))