]> code.delx.au - gnu-emacs-elpa/blob - yasnippet.el
set customized face. Now user can customize the face.
[gnu-emacs-elpa] / yasnippet.el
1 ;;; yasnippet.el --- Yet another snippet extension for Emacs.
2
3 ;; Copyright 2008 pluskid
4 ;;
5 ;; Author: pluskid <pluskid@gmail.com>
6 ;; Version: 0.2.1
7 ;; X-URL: http://code.google.com/p/yasnippet/
8
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)
12 ;; any later version.
13
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.
18
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.
23
24 ;;; Commentary:
25
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
32 ;; (yas/initialize)
33 ;; (yas/load-directory "~/.emacs.d/snippets")
34 ;;
35 ;; For more information and detailed usage, refer to the project page:
36 ;; http://code.google.com/p/yasnippet/
37
38 (require 'cl)
39
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 \"_\":
47
48 foo-bar
49
50 will first try \"bar\", if not found, then \"foo-bar\" is tried.")
51
52 (defvar yas/root-directory nil
53 "The root directory that stores the snippets for each major modes.")
54
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)
59
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)
65
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)
73
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
83 this variable to t.")
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.")
89
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
99 ;; Internal variables
100 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
101 (defvar yas/version "0.2.1")
102
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]
114 '(menu-item "--"))
115
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"))
125
126 (defconst yas/field-regexp
127 (concat "$\\([0-9]+\\)" "\\|"
128 "${\\(?:\\([0-9]+\\):\\)?\\([^}]*\\)}"))
129
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)
135 id))
136
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.")
146
147
148 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
149 ;; Internal Structs
150 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
151 (defstruct (yas/template (:constructor yas/make-template (content name)))
152 "A template for a snippet."
153 content
154 name)
155 (defstruct (yas/snippet (:constructor yas/make-snippet ()))
156 "A snippet."
157 (groups nil)
158 (tabstops nil) ; tabstops are those groups whose init value is empty
159 (exit-marker nil)
160 (id (yas/snippet-next-id) :read-only t)
161 (overlay nil))
162 (defstruct (yas/group (:constructor yas/make-group (primary-field snippet)))
163 "A group contains a list of field with the same number."
164 primary-field
165 (fields (list primary-field))
166 (next nil)
167 (prev nil)
168 snippet)
169 (defstruct (yas/field
170 (:constructor yas/make-field (overlay number value transform)))
171 "A field in a snippet."
172 overlay
173 number
174 transform
175 value)
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))
179 (parent nil))
180
181 (defun yas/snippet-add-field (snippet field)
182 "Add FIELD to SNIPPET."
183 (let ((group (find field
184 (yas/snippet-groups snippet)
185 :test
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)))))))
191 (if group
192 (yas/group-add-field group field)
193 (push (yas/make-group field snippet)
194 (yas/snippet-groups snippet)))))
195
196 (defun yas/group-value (group)
197 "Get the default value of the field group."
198 (or (yas/field-value
199 (yas/group-primary-field group))
200 ""))
201 (defun yas/group-number (group)
202 "Get the number of the field group."
203 (yas/field-number
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)))
213
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)))
220 (if n1
221 (if n2
222 (< n1 n2)
223 t)
224 (if n2
225 nil
226 (< (overlay-start (yas/field-overlay field1))
227 (overlay-start (yas/field-overlay field2)))))))
228
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)
237 key)))
238 templates))
239 (defun yas/snippet-table-store (table full-key key template)
240 "Store a snippet template in the table."
241 (puthash key
242 (yas/modify-alist (gethash key
243 (yas/snippet-table-hash table))
244 full-key
245 template)
246 (yas/snippet-table-hash table)))
247
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."
257 (or (fboundp mode)
258 (find mode yas/known-modes)))
259
260 (defun yas/eval-string (string)
261 "Evaluate STRING and convert the result to string."
262 (condition-case err
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
269 unmodified."
270 (let ((text value)
271 (transform (yas/field-transform field)))
272 (if transform
273 (yas/eval-string transform)
274 text)))
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)))
280
281 (defun yas/snippet-table (mode)
282 "Get the snippet table corresponding to MODE."
283 (let ((table (gethash mode yas/snippet-tables)))
284 (unless table
285 (setq table (yas/make-snippet-table))
286 (puthash mode table yas/snippet-tables))
287 table))
288 (defsubst yas/current-snippet-table ()
289 "Get the snippet table for current major-mode."
290 (yas/snippet-table major-mode))
291
292 (defun yas/menu-keymap-for-mode (mode)
293 "Get the menu keymap correspondong to MODE."
294 (let ((keymap (gethash mode yas/menu-table)))
295 (unless keymap
296 (setq keymap (make-sparse-keymap))
297 (puthash mode keymap yas/menu-table))
298 keymap))
299
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))
304 (end (point))
305 (syntaxes yas/key-syntaxes)
306 syntax done)
307 (while (and (not done) syntaxes)
308 (setq syntax (car syntaxes))
309 (setq syntaxes (cdr syntaxes))
310 (save-excursion
311 (skip-syntax-backward syntax)
312 (when (yas/snippet-table-fetch
313 (yas/current-snippet-table)
314 (buffer-substring-no-properties (point) end))
315 (setq done t)
316 (setq start (point)))))
317 (list (buffer-substring-no-properties start end)
318 start
319 end)))
320
321 (defun yas/synchronize-fields (field-group)
322 "Update all fields' text according to the primary field."
323 (save-excursion
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)
340 (point))
341 (delete-char original-length))))))))
342
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."
349 (when after?
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)
355 (save-excursion
356 (goto-char 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'
362 event manually."
363 (when (and after?
364 (= length 0)
365 (> end beg)
366 (null (yas/current-snippet-overlay beg))
367 (not (bobp)))
368 (let ((field-overlay (yas/current-snippet-overlay (1- beg))))
369 (if field-overlay
370 (when (= beg (overlay-end field-overlay))
371 (move-overlay field-overlay
372 (overlay-start field-overlay)
373 end)
374 (yas/synchronize-fields (overlay-get field-overlay 'yas/group)))
375 (let ((snippet (yas/snippet-of-current-keymap))
376 (done nil))
377 (if snippet
378 (do* ((tabstops (yas/snippet-tabstops snippet) (cdr tabstops))
379 (tabstop (car tabstops) (car tabstops)))
380 ((or (null tabstops)
381 done))
382 (setq field-overlay (yas/field-overlay
383 (yas/group-primary-field tabstop)))
384 (when (= beg
385 (overlay-start field-overlay))
386 (move-overlay field-overlay beg end)
387 (yas/synchronize-fields tabstop)
388 (setq done t)))))))))
389
390 (defun yas/undo-expand-snippet (start end key snippet)
391 "Undo a snippet expansion. Delete the overlays. This undo can't be
392 redo-ed."
393 (let ((undo (car buffer-undo-list)))
394 (while (null undo)
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)
402 (goto-char start)
403 (delete-char (- end start))
404 (insert key))))
405
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."
409 (goto-char start)
410
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)))
416 (save-restriction
417 (narrow-to-region start start)
418
419 (setq buffer-undo-list t)
420 (insert template)
421
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))
431 (insert indent))))
432
433 ;; Step 2: protect backslash and backquote
434 (yas/replace-all "\\\\" yas/escape-backslash)
435 (yas/replace-all "\\`" yas/escape-backquote)
436
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))
441 t t))
442
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)
448
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)))
455 (transform nil)
456 (value (match-string-no-properties 3)))
457 (when (eq (elt value 0) ?\$)
458 (setq transform (substring value 1))
459 (setq value nil))
460 (if (and number
461 (string= "0" number))
462 (progn
463 (replace-match "")
464 (setf (yas/snippet-exit-marker snippet)
465 (copy-marker (point) t)))
466 (yas/snippet-add-field
467 snippet
468 (yas/make-field
469 (make-overlay (match-beginning 0) (match-end 0))
470 (and number (string-to-number number))
471 value
472 transform)))))
473
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)))))
481 (let ((prev nil))
482 (dolist (group (yas/snippet-groups snippet))
483 (setf (yas/group-prev group) prev)
484 (when prev
485 (setf (yas/group-next prev) group))
486 (setq prev group)))
487
488 ;; Step 7: Create keymap overlay for snippet
489 (let ((overlay (make-overlay (point-min)
490 (point-max)
491 nil
492 nil
493 t)))
494 (overlay-put overlay
495 'modification-hooks
496 yas/keymap-overlay-modification-hooks)
497 (overlay-put overlay
498 'insert-behind-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))
503
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)))
514 (goto-char start)
515 (insert (yas/calculate-field-value field value))
516 (delete-char length)))))
517
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 "\\")
522
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)
536 'face
537 'yas/mirror-highlight-face)))))
538
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)))
543
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
548 ,(point-min)
549 ,(point-max)
550 ,key
551 ,snippet)))
552
553 ;; Step 13: remove the trigger key
554 (widen)
555 (delete-char length)
556
557 (setq buffer-undo-list original-undo-list)
558
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)))
562 (if groups
563 (goto-char (overlay-start
564 (yas/field-overlay
565 (yas/group-primary-field
566 (car groups)))))
567 ;; no need to call exit-snippet, since no overlay created.
568 (yas/exit-snippet snippet)))))))
569
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)))))
581 snippet-overlay))
582
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)))
586 (keymap-snippet nil)
587 (snippet nil))
588 (dolist (overlay (overlays-at point))
589 (setq snippet (overlay-get overlay 'yas/snippet-reference))
590 (when snippet
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)))))
596 keymap-snippet))
597
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))
601 (overlay2 (if (bobp)
602 nil
603 (yas/current-snippet-overlay (- (point) 1)))))
604 (if (null overlay1)
605 overlay2
606 (if (or (null overlay2)
607 (eq (overlay-get overlay1 'yas/snippet)
608 (overlay-get overlay2 'yas/snippet)))
609 overlay1
610 (if (> (yas/snippet-id (overlay-get overlay2 'yas/snippet))
611 (yas/snippet-id (overlay-get overlay1 'yas/snippet)))
612 overlay2
613 overlay1)))))
614
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))))
620 (if target
621 (goto-char (overlay-start
622 (yas/field-overlay
623 (yas/group-primary-field target))))
624 (yas/exit-snippet (yas/group-snippet group)))))
625
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:
631
632 #name : value
633
634 Currently only the \"name\" variable is recognized. Here's
635 an example:
636
637 #name: #include \"...\"
638 # --
639 #include \"$1\""
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)
645 (point-max)))
646 (setq bound (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)))))
651 (setq template
652 (buffer-substring-no-properties (point-min) (point-max))))
653 (list template name)))
654
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))
660 (if file?
661 (file-directory-p file)
662 (not (file-directory-p file)))))
663 (directory-files directory t)))
664
665 (defun yas/make-menu-binding (template)
666 (lexical-let ((template template))
667 (lambda ()
668 (interactive)
669 (yas/expand-snippet (point)
670 (point)
671 template))))
672
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)))
676 (if (null pair)
677 (cons (cons key value)
678 alist)
679 (setcdr pair value)
680 alist)))
681
682 (defun yas/fake-keymap-for-popup (templates)
683 "Create a fake keymap for popup menu usage."
684 (cons 'keymap
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)))
690 templates)))
691
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)
700 (+ (cdr x-y) 20))
701 (selected-window)))
702 t))
703
704 (defun yas/popup-for-template (templates)
705 "Show a popup menu listing templates to let the user select one."
706 (if window-system
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
710 (cdar templates)))
711
712 (defun yas/load-directory-1 (directory &optional parent)
713 "Really do the job of loading snippets from a directory
714 hierarchy."
715 (let ((mode-sym (intern (file-name-nondirectory directory)))
716 (snippets nil))
717 (with-temp-buffer
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))
723 snippets))))
724 (yas/define-snippets mode-sym
725 snippets
726 parent)
727 (dolist (subdir (yas/directory-files directory nil))
728 (yas/load-directory-1 subdir mode-sym))))
729
730 (defun yas/quote-string (string)
731 "Escape and quote STRING.
732 foo\"bar\\! -> \"foo\\\"bar\\\\!\""
733 (concat "\""
734 (replace-regexp-in-string "[\\\"]"
735 "\\\\\\&"
736 string
737 t)
738 "\""))
739
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:
745
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)))
751 (bundle-buffer nil))
752 (with-temp-buffer
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")
765 (insert "'(\n")
766 (dolist (snippet snippets)
767 (insert " ("
768 (yas/quote-string (car snippet))
769 (yas/quote-string (cadr snippet))
770 (if (caddr snippet)
771 (yas/quote-string (caddr snippet))
772 "nil")
773 ")\n"))
774 (insert " )\n")
775 (insert (if parent
776 (concat "'" (symbol-name parent))
777 "nil")
778 ")\n\n"))))
779 (dolist (dir dirs)
780 (dolist (subdir (yas/directory-files dir nil))
781 (yas/load-directory-1 subdir nil))))
782 (insert "(provide '"
783 (file-name-nondirectory
784 (file-name-sans-extension
785 yasnippet-bundle))
786 ")\n")
787 (setq buffer-file-name yasnippet-bundle)
788 (save-buffer))))
789
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'."
797 (interactive)
798 (let ((command (key-binding (kbd "TAB"))))
799 (if (and command
800 (not (eq command 'yas/expand)))
801 (call-interactively command)
802 (call-interactively 'indent-for-tab-command))))
803
804 (defun yas/about ()
805 (interactive)
806 (message (concat "yasnippet (version "
807 yas/version
808 ") -- pluskid <pluskid@gmail.com>")))
809 (defun yas/reload-all ()
810 "Reload all snippets."
811 (interactive)
812 (if yas/root-directory
813 (yas/load-directory-1 yas/root-directory)
814 (call-interactively 'yas/load-directory))
815 (message "done."))
816
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)
829 (message "done.")))
830
831 (defun yas/initialize ()
832 "Do necessary initialization."
833 (global-set-key yas/trigger-key 'yas/expand)
834 (when yas/use-menu
835 (define-key-after
836 (lookup-key global-map [menu-bar])
837 [yasnippet]
838 (cons "YASnippet" yas/menu-keymap)
839 'buffer)))
840
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:
844 (KEY TEMPLATE NAME)
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
849 real mode."
850 (let ((snippet-table (yas/snippet-table mode))
851 (parent-table (if parent-mode
852 (yas/snippet-table parent-mode)
853 nil))
854 (keymap (if yas/use-menu
855 (yas/menu-keymap-for-mode mode)
856 nil)))
857 (when parent-table
858 (setf (yas/snippet-table-parent snippet-table)
859 parent-table)
860 (when yas/use-menu
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)
873 (or name key))))
874 (yas/snippet-table-store snippet-table
875 full-key
876 key
877 template)
878 (when yas/use-menu
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))))))))
883
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))
889 (when yas/use-menu
890 (define-key (yas/menu-keymap-for-mode mode) (vector 'parent-mode)
891 `(menu-item "parent mode"
892 ,(yas/menu-keymap-for-mode parent)))))
893
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))))
900
901
902 (defun yas/expand ()
903 "Expand a snippet."
904 (interactive)
905 (multiple-value-bind (key start end) (yas/current-key)
906 (let ((templates (yas/snippet-table-fetch (yas/current-snippet-table)
907 key)))
908 (if templates
909 (let ((template (if (null (cdr templates)) ; only 1 template
910 (yas/template-content (cdar templates))
911 (yas/popup-for-template templates))))
912 (when template
913 (yas/expand-snippet start end template)))
914 (when yas/trigger-fallback
915 (call-interactively yas/trigger-fallback))))))
916
917 (defun yas/next-field-group ()
918 "Navigate to next field group. If there's none, exit the snippet."
919 (interactive)
920 (let ((overlay (yas/current-overlay-for-navigation)))
921 (if overlay
922 (yas/navigate-group (overlay-get overlay 'yas/group) t)
923 (let ((snippet (yas/snippet-of-current-keymap))
924 (done nil))
925 (if snippet
926 (do* ((tabstops (yas/snippet-tabstops snippet) (cdr tabstops))
927 (tabstop (car tabstops) (car tabstops)))
928 ((or (null tabstops)
929 done)
930 (unless done (message "Not in a snippet field.")))
931 (when (= (point)
932 (overlay-start
933 (yas/field-overlay
934 (yas/group-primary-field tabstop))))
935 (setq done t)
936 (yas/navigate-group tabstop t)))
937 (message "Not in a snippet field."))))))
938
939 (defun yas/prev-field-group ()
940 "Navigate to prev field group. If there's none, exit the snippet."
941 (interactive)
942 (let ((overlay (yas/current-overlay-for-navigation)))
943 (if overlay
944 (yas/navigate-group (overlay-get overlay 'yas/group) nil)
945 (let ((snippet (yas/snippet-of-current-keymap))
946 (done nil))
947 (if snippet
948 (do* ((tabstops (yas/snippet-tabstops snippet) (cdr tabstops))
949 (tabstop (car tabstops) (car tabstops)))
950 ((or (null tabstops)
951 done)
952 (unless done (message "Not in a snippet field.")))
953 (when (= (point)
954 (overlay-start
955 (yas/field-overlay
956 (yas/group-primary-field tabstop))))
957 (setq done t)
958 (yas/navigate-group tabstop nil)))
959 (message "Not in a snippet field."))))))
960
961 (defun yas/exit-snippet (snippet)
962 "Goto exit-marker of SNIPPET and delete the snippet."
963 (interactive)
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)))))
969
970 (provide 'yasnippet)
971
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."
983 (condition-case err
984 ad-do-it
985 (error (message (error-message-string err)))))