]> code.delx.au - gnu-emacs-elpa/blob - yasnippet.el
no need to lookup local keymap: if there's one, yas/expand shouldn't be executed.
[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.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 "S-TAB") 'yas/prev-field-group)
70 (define-key yas/keymap (kbd "<S-iso-lefttab>") 'yas/prev-field-group)
71 (define-key yas/keymap (kbd "<S-tab>") 'yas/prev-field-group)
72
73 (defvar yas/use-menu t
74 "If this is set to `t', all snippet template of the current
75 mode will be listed under the menu \"yasnippet\".")
76 (defvar yas/trigger-symbol " =>"
77 "The text that will be used in menu to represent the trigger.")
78 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
79 ;; Internal variables
80 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
81 (defvar yas/version "0.1")
82
83 (defvar yas/snippet-tables (make-hash-table)
84 "A hash table of snippet tables corresponding to each major-mode.")
85 (defvar yas/menu-table (make-hash-table)
86 "A hash table of menus of corresponding major-mode.")
87 (defvar yas/menu-keymap (make-sparse-keymap "YASnippet"))
88 ;; empty menu will cause problems, so we insert some items
89 (define-key yas/menu-keymap [yas/about]
90 '(menu-item "About" yas/about))
91 (define-key yas/menu-keymap [yas/reload]
92 '(menu-item "Reload all snippets" yas/reload-all))
93 (define-key yas/menu-keymap [yas/separator]
94 '(menu-item "--"))
95
96 (defconst yas/escape-backslash
97 (concat "YASESCAPE" "BACKSLASH" "PROTECTGUARD"))
98 (defconst yas/escape-dollar
99 (concat "YASESCAPE" "DOLLAR" "PROTECTGUARD"))
100 (defconst yas/escape-backquote
101 (concat "YASESCAPE" "BACKQUOTE" "PROTECTGUARD"))
102
103 (defconst yas/field-regexp
104 (concat "$\\([0-9]+\\)" "\\|"
105 "${\\(?:\\([0-9]+\\):\\)?\\([^}]*\\)}"))
106
107 (defvar yas/snippet-id-seed 0
108 "Contains the next id for a snippet")
109 (defun yas/snippet-next-id ()
110 (let ((id yas/snippet-id-seed))
111 (incf yas/snippet-id-seed)
112 id))
113
114 (defvar yas/overlay-modification-hooks
115 (list 'yas/overlay-modification-hook)
116 "The list of hooks to the overlay modification event.")
117 (defvar yas/overlay-insert-in-front-hooks
118 (list 'yas/overlay-insert-in-front-hook)
119 "The list of hooks of the overlay inserted in front event.")
120 (defvar yas/overlay-insert-behind-hooks
121 (list 'yas/overlay-insert-behind-hook)
122 "The list of hooks of the overlay inserted behind event.")
123
124
125 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
126 ;; Internal Structs
127 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
128 (defstruct (yas/template (:constructor yas/make-template (content name)))
129 "A template for a snippet."
130 content
131 name)
132 (defstruct (yas/snippet (:constructor yas/make-snippet ()))
133 "A snippet."
134 (groups nil)
135 (tabstops nil) ; tabstops are those groups whose init value is empty
136 (exit-marker nil)
137 (id (yas/snippet-next-id) :read-only t)
138 (overlay nil))
139 (defstruct (yas/group (:constructor yas/make-group (primary-field snippet)))
140 "A group contains a list of field with the same number."
141 primary-field
142 (fields (list primary-field))
143 (next nil)
144 (prev nil)
145 snippet)
146 (defstruct (yas/field (:constructor yas/make-field (overlay number value)))
147 "A field in a snippet."
148 overlay
149 number
150 value)
151
152 (defun yas/snippet-add-field (snippet field)
153 "Add FIELD to SNIPPET."
154 (let ((group (find field
155 (yas/snippet-groups snippet)
156 :test
157 '(lambda (field group)
158 (and (not (null (yas/field-number field)))
159 (= (yas/field-number field)
160 (yas/group-number group)))))))
161 (if group
162 (yas/group-add-field group field)
163 (push (yas/make-group field snippet)
164 (yas/snippet-groups snippet)))))
165
166 (defun yas/group-value (group)
167 "Get the default value of the field group."
168 (or (yas/field-value
169 (yas/group-primary-field group))
170 ""))
171 (defun yas/group-number (group)
172 "Get the number of the field group."
173 (yas/field-number
174 (yas/group-primary-field group)))
175 (defun yas/group-add-field (group field)
176 "Add a field to the field group. If the value of the primary
177 field is nil and that of the field is not nil, the field is set
178 as the primary field of the group."
179 (push field (yas/group-fields group))
180 (when (and (null (yas/field-value (yas/group-primary-field group)))
181 (yas/field-value field))
182 (setf (yas/group-primary-field group) field)))
183
184 (defun yas/snippet-field-compare (field1 field2)
185 "Compare two fields. The field with a number is sorted first.
186 If they both have a number, compare through the number. If neither
187 have, compare through the start point of the overlay."
188 (let ((n1 (yas/field-number field1))
189 (n2 (yas/field-number field2)))
190 (if n1
191 (if n2
192 (< n1 n2)
193 t)
194 (if n2
195 nil
196 (< (overlay-start (yas/field-overlay field1))
197 (overlay-start (yas/field-overlay field2)))))))
198
199 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
200 ;; Internal functions
201 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
202 (defun yas/eval-string (string)
203 "Evaluate STRING and convert the result to string."
204 (condition-case err
205 (format "%s" (eval (read string)))
206 (error (format "(error in elisp evaluation: %s)"
207 (error-message-string err)))))
208 (defsubst yas/replace-all (from to)
209 "Replace all occurance from FROM to TO."
210 (goto-char (point-min))
211 (while (search-forward from nil t)
212 (replace-match to t t)))
213
214 (defun yas/snippet-table (mode)
215 "Get the snippet table corresponding to MODE."
216 (let ((table (gethash mode yas/snippet-tables)))
217 (unless table
218 (setq table (make-hash-table :test 'equal))
219 (puthash mode table yas/snippet-tables))
220 table))
221 (defsubst yas/current-snippet-table ()
222 "Get the snippet table for current major-mode."
223 (yas/snippet-table major-mode))
224
225 (defun yas/menu-keymap-for-mode (mode)
226 "Get the menu keymap correspondong to MODE."
227 (let ((keymap (gethash mode yas/menu-table)))
228 (unless keymap
229 (setq keymap (make-sparse-keymap))
230 (puthash mode keymap yas/menu-table))
231 keymap))
232
233 (defun yas/current-key ()
234 "Get the key under current position. A key is used to find
235 the template of a snippet in the current snippet-table."
236 (let ((start (point))
237 (end (point))
238 (syntaxes yas/key-syntaxes)
239 syntax done)
240 (while (and (not done) syntaxes)
241 (setq syntax (car syntaxes))
242 (setq syntaxes (cdr syntaxes))
243 (save-excursion
244 (skip-syntax-backward syntax)
245 (when (gethash (buffer-substring-no-properties (point) end)
246 (yas/current-snippet-table))
247 (setq done t)
248 (setq start (point)))))
249 (list (buffer-substring-no-properties start end)
250 start
251 end)))
252
253 (defun yas/synchronize-fields (field-group)
254 "Update all fields' text according to the primary field."
255 (save-excursion
256 (let* ((inhibit-modification-hooks t)
257 (primary (yas/group-primary-field field-group))
258 (primary-overlay (yas/field-overlay primary))
259 (text (buffer-substring-no-properties (overlay-start primary-overlay)
260 (overlay-end primary-overlay))))
261 (dolist (field (yas/group-fields field-group))
262 (let* ((field-overlay (yas/field-overlay field))
263 (original-length (- (overlay-end field-overlay)
264 (overlay-start field-overlay))))
265 (unless (eq field-overlay primary-overlay)
266 (goto-char (overlay-start field-overlay))
267 (insert text)
268 (if (= (overlay-start field-overlay)
269 (overlay-end field-overlay))
270 (move-overlay field-overlay
271 (overlay-start field-overlay)
272 (point))
273 (delete-char original-length))))))))
274
275 (defun yas/overlay-modification-hook (overlay after? beg end &optional length)
276 "Modification hook for snippet field overlay."
277 (when (and after? (not undo-in-progress))
278 (yas/synchronize-fields (overlay-get overlay 'yas/group))))
279 (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length)
280 "Hook for snippet overlay when text is inserted in front of a snippet field."
281 (when after?
282 (let ((field-group (overlay-get overlay 'yas/group))
283 (inhibit-modification-hooks t))
284 (when (not (overlay-get overlay 'yas/modified?))
285 (overlay-put overlay 'yas/modified? t)
286 (when (> (overlay-end overlay) end)
287 (save-excursion
288 (goto-char end)
289 (delete-char (- (overlay-end overlay) end)))))
290 (yas/synchronize-fields field-group))))
291 (defun yas/overlay-insert-behind-hook (overlay after? beg end &optional length)
292 "Hook for snippet overlay when text is inserted just behind a snippet field."
293 (when (and after?
294 (null (yas/current-snippet-overlay beg))) ; not inside another field
295 (move-overlay overlay
296 (overlay-start overlay)
297 end)
298 (yas/synchronize-fields (overlay-get overlay 'yas/group))))
299
300 (defun yas/undo-expand-snippet (start end key snippet)
301 "Undo a snippet expansion. Delete the overlays. This undo can't be
302 redo-ed."
303 (let ((undo (car buffer-undo-list)))
304 (while (null undo)
305 (setq buffer-undo-list (cdr buffer-undo-list))
306 (setq undo (car buffer-undo-list)))
307 ;; Remove this undo operation record
308 (setq buffer-undo-list (cdr buffer-undo-list))
309 (let ((inhibit-modification-hooks t)
310 (buffer-undo-list t))
311 (yas/exit-snippet snippet)
312 (goto-char start)
313 (delete-char (- end start))
314 (insert key))))
315
316 (defun yas/expand-snippet (start end template)
317 "Expand snippet at current point. Text between START and END
318 will be deleted before inserting template."
319 (goto-char start)
320
321 (let ((key (buffer-substring-no-properties start end))
322 (original-undo-list buffer-undo-list)
323 (inhibit-modification-hooks t)
324 (length (- end start))
325 (column (current-column)))
326 (save-restriction
327 (narrow-to-region start start)
328
329 (setq buffer-undo-list t)
330 (insert template)
331
332 ;; Step 1: do necessary indent
333 (when yas/indent-line
334 (let* ((indent (if indent-tabs-mode
335 (concat (make-string (/ column tab-width) ?\t)
336 (make-string (% column tab-width) ?\ ))
337 (make-string column ?\ ))))
338 (goto-char (point-min))
339 (while (and (zerop (forward-line))
340 (= (current-column) 0))
341 (insert indent))))
342
343 ;; Step 2: protect backslash and backquote
344 (yas/replace-all "\\\\" yas/escape-backslash)
345 (yas/replace-all "\\`" yas/escape-backquote)
346
347 ;; Step 3: evaluate all backquotes
348 (goto-char (point-min))
349 (while (re-search-forward "`\\([^`]*\\)`" nil t)
350 (replace-match (yas/eval-string (match-string-no-properties 1))
351 t t))
352
353 ;; Step 4: protect all escapes, including backslash and backquot
354 ;; which may be produced in Step 3
355 (yas/replace-all "\\\\" yas/escape-backslash)
356 (yas/replace-all "\\`" yas/escape-backquote)
357 (yas/replace-all "\\$" yas/escape-dollar)
358
359 (let ((snippet (yas/make-snippet)))
360 ;; Step 5: Create fields
361 (goto-char (point-min))
362 (while (re-search-forward yas/field-regexp nil t)
363 (let ((number (or (match-string-no-properties 1)
364 (match-string-no-properties 2)))
365 (value (match-string-no-properties 3)))
366 (if (and number
367 (string= "0" number))
368 (progn
369 (replace-match "")
370 (setf (yas/snippet-exit-marker snippet)
371 (copy-marker (point) t)))
372 (yas/snippet-add-field
373 snippet
374 (yas/make-field
375 (make-overlay (match-beginning 0) (match-end 0))
376 (and number (string-to-number number))
377 value)))))
378
379 ;; Step 6: Sort and link each field group
380 (setf (yas/snippet-groups snippet)
381 (sort (yas/snippet-groups snippet)
382 '(lambda (group1 group2)
383 (yas/snippet-field-compare
384 (yas/group-primary-field group1)
385 (yas/group-primary-field group2)))))
386 (let ((prev nil))
387 (dolist (group (yas/snippet-groups snippet))
388 (setf (yas/group-prev group) prev)
389 (when prev
390 (setf (yas/group-next prev) group))
391 (setq prev group)))
392
393 ;; Step 7: Create keymap overlay for snippet
394 (let ((overlay (make-overlay (point-min)
395 (point-max)
396 nil
397 nil
398 t)))
399 (overlay-put overlay 'keymap yas/keymap)
400 (overlay-put overlay 'yas/snippet-reference snippet)
401 (setf (yas/snippet-overlay snippet) overlay))
402
403 ;; Step 8: Replace fields with default values
404 (dolist (group (yas/snippet-groups snippet))
405 (let ((value (yas/group-value group)))
406 (when (string= "" value)
407 (push group (yas/snippet-tabstops snippet)))
408 (dolist (field (yas/group-fields group))
409 (let* ((overlay (yas/field-overlay field))
410 (start (overlay-start overlay))
411 (end (overlay-end overlay))
412 (length (- end start)))
413 (goto-char start)
414 (insert value)
415 (delete-char length)))))
416
417 ;; Step 9: restore all escape characters
418 (yas/replace-all yas/escape-dollar "$")
419 (yas/replace-all yas/escape-backquote "`")
420 (yas/replace-all yas/escape-backslash "\\")
421
422 ;; Step 10: Set up properties of overlays
423 (dolist (group (yas/snippet-groups snippet))
424 (let ((overlay (yas/field-overlay
425 (yas/group-primary-field group))))
426 (overlay-put overlay 'yas/snippet snippet)
427 (overlay-put overlay 'yas/group group)
428 (overlay-put overlay 'yas/modified? nil)
429 (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks)
430 (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks)
431 (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks)
432 (dolist (field (yas/group-fields group))
433 (overlay-put (yas/field-overlay field)
434 'face
435 'highlight))))
436
437 ;; Step 11: move to end and make sure exit-marker exist
438 (goto-char (point-max))
439 (unless (yas/snippet-exit-marker snippet)
440 (setf (yas/snippet-exit-marker snippet) (copy-marker (point) t)))
441
442 ;; Step 12: Construct undo information
443 (unless (eq original-undo-list t)
444 (add-to-list 'original-undo-list
445 `(apply yas/undo-expand-snippet
446 ,(point-min)
447 ,(point-max)
448 ,key
449 ,snippet)))
450
451 ;; Step 13: remove the trigger key
452 (widen)
453 (delete-char length)
454
455 (setq buffer-undo-list original-undo-list)
456
457 ;; Step 14: place the cursor at a proper place
458 (let ((groups (yas/snippet-groups snippet))
459 (exit-marker (yas/snippet-exit-marker snippet)))
460 (if groups
461 (goto-char (overlay-start
462 (yas/field-overlay
463 (yas/group-primary-field
464 (car groups)))))
465 ;; no need to call exit-snippet, since no overlay created.
466 (yas/exit-snippet snippet)))))))
467
468 (defun yas/current-snippet-overlay (&optional point)
469 "Get the most proper overlay which is belongs to a snippet."
470 (let ((point (or point (point)))
471 (snippet-overlay nil))
472 (dolist (overlay (overlays-at point))
473 (when (overlay-get overlay 'yas/snippet)
474 (if (null snippet-overlay)
475 (setq snippet-overlay overlay)
476 (when (> (yas/snippet-id (overlay-get overlay 'yas/snippet))
477 (yas/snippet-id (overlay-get snippet-overlay 'yas/snippet)))
478 (setq snippet-overlay overlay)))))
479 snippet-overlay))
480
481 (defun yas/snippet-of-current-keymap (&optional point)
482 "Get the snippet holding the snippet keymap under POINT."
483 (let ((point (or point (point)))
484 (keymap-snippet nil)
485 (snippet nil))
486 (dolist (overlay (overlays-at point))
487 (setq snippet (overlay-get overlay 'yas/snippet-reference))
488 (when snippet
489 (if (null keymap-snippet)
490 (setq keymap-snippet snippet)
491 (when (> (yas/snippet-id snippet)
492 (yas/snippet-id keymap-snippet))
493 (setq keymap-snippet snippet)))))
494 keymap-snippet))
495
496 (defun yas/current-overlay-for-navigation ()
497 "Get current overlay for navigation. Might be overlay at current or previous point."
498 (let ((overlay1 (yas/current-snippet-overlay))
499 (overlay2 (if (bobp)
500 nil
501 (yas/current-snippet-overlay (- (point) 1)))))
502 (if (null overlay1)
503 overlay2
504 (if (or (null overlay2)
505 (eq (overlay-get overlay1 'yas/snippet)
506 (overlay-get overlay2 'yas/snippet)))
507 overlay1
508 (if (> (yas/snippet-id (overlay-get overlay2 'yas/snippet))
509 (yas/snippet-id (overlay-get overlay1 'yas/snippet)))
510 overlay2
511 overlay1)))))
512
513 (defun yas/navigate-group (group next?)
514 "Go to next of previous field group. Exit snippet if none."
515 (let ((target (if next?
516 (yas/group-next group)
517 (yas/group-prev group))))
518 (if target
519 (goto-char (overlay-start
520 (yas/field-overlay
521 (yas/group-primary-field target))))
522 (yas/exit-snippet (yas/group-snippet group)))))
523
524 (defun yas/parse-template ()
525 "Parse the template in the current buffer.
526 If the buffer contains a line of \"# --\" then the contents
527 above this line are ignored. Variables can be set above this
528 line through the syntax:
529
530 #name : value
531
532 Currently only the \"name\" variable is recognized. Here's
533 an example:
534
535 #name: #include \"...\"
536 # --
537 #include \"$1\""
538 (goto-char (point-min))
539 (let (template name bound)
540 (if (re-search-forward "^# --\n" nil t)
541 (progn (setq template
542 (buffer-substring-no-properties (point)
543 (point-max)))
544 (setq bound (point))
545 (goto-char (point-min))
546 (while (re-search-forward "^#\\([^ ]+\\) *: *\\(.*\\)$" bound t)
547 (when (string= "name" (match-string-no-properties 1))
548 (setq name (match-string-no-properties 2)))))
549 (setq template
550 (buffer-substring-no-properties (point-min) (point-max))))
551 (list template name)))
552
553 (defun yas/directory-files (directory file?)
554 "Return directory files or subdirectories in full path."
555 (remove-if (lambda (file)
556 (or (string-match "^\\."
557 (file-name-nondirectory file))
558 (if file?
559 (file-directory-p file)
560 (not (file-directory-p file)))))
561 (directory-files directory t)))
562
563 (defun yas/make-menu-binding (template)
564 (lexical-let ((template template))
565 (lambda ()
566 (interactive)
567 (yas/expand-snippet (point)
568 (point)
569 template))))
570
571 (defun yas/modify-alist (alist key value)
572 "Modify ALIST to map KEY to VALUE. return the new alist."
573 (let ((pair (assoc key alist)))
574 (if (null pair)
575 (cons (cons key value)
576 alist)
577 (setcdr pair value)
578 alist)))
579
580 (defun yas/fake-keymap-for-popup (templates)
581 "Create a fake keymap for popup menu usage."
582 (cons 'keymap
583 (mapcar (lambda (pair)
584 (let* ((template (cdr pair))
585 (name (yas/template-name template))
586 (content (yas/template-content template)))
587 (list content 'menu-item name t)))
588 templates)))
589
590 (defun yas/point-to-coord (&optional point)
591 "Get the xoffset/yoffset information of POINT.
592 If POINT is not given, default is to current point.
593 If `posn-at-point' is not available (like in Emacs 21.3),
594 t is returned simply."
595 (if (boundp 'posn-at-point)
596 (let ((x-y (posn-x-y (posn-at-point (or point (point))))))
597 (list (list (+ (car x-y) 10)
598 (+ (cdr x-y) 20))
599 (selected-window)))
600 t))
601
602 (defun yas/popup-for-template (templates)
603 "Show a popup menu listing templates to let the user select one."
604 (if window-system
605 (car (x-popup-menu (yas/point-to-coord) (yas/fake-keymap-for-popup templates)))
606 ;; no window system, simply select the first one
607 (cdar templates)))
608
609 (defun yas/load-directory-1 (directory)
610 "Really do the job of loading snippets from a directory
611 hierarchy."
612 (with-temp-buffer
613 (dolist (mode (yas/directory-files directory nil))
614 (let ((mode-sym (intern (file-name-nondirectory mode))))
615 (dolist (file (yas/directory-files mode t))
616 (when (file-readable-p file)
617 (insert-file-contents file nil nil nil t)
618 (multiple-value-bind
619 (key template name)
620 (cons (file-name-nondirectory file)
621 (yas/parse-template))
622 (yas/define mode-sym key template name))))))))
623
624 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
625 ;; User level functions
626 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
627 (defun yas/default-trigger-fallback ()
628 "Default fallback when a snippet expansion failed.
629 It looks key binding for TAB. If found, execute it. If not found.
630 Run `indent-for-tab-command'."
631 (interactive)
632 (let ((command (key-binding (kbd "TAB"))))
633 (if command
634 (call-interactively command)
635 (call-interactively 'indent-for-tab-command))))
636
637 (defun yas/about ()
638 (interactive)
639 (message (concat "yasnippet (version "
640 yas/version
641 ") -- pluskid <pluskid@gmail.com>")))
642 (defun yas/reload-all ()
643 "Reload all snippets."
644 (interactive)
645 (if yas/root-directory
646 (yas/load-directory-1 yas/root-directory)
647 (call-interactively 'yas/load-directory))
648 (message "done."))
649
650 (defun yas/load-directory (directory)
651 "Load snippet definition from a directory hierarchy.
652 Below the top-level directory, each directory is a mode
653 name. And under each subdirectory, each file is a definition
654 of a snippet. The file name is the trigger key and the
655 content of the file is the template."
656 (interactive "DSelect the root directory: ")
657 (unless yas/root-directory
658 (setq yas/root-directory directory))
659 (yas/load-directory-1 directory)
660 (when (interactive-p)
661 (message "done.")))
662
663 (defun yas/initialize ()
664 "Do necessary initialization."
665 (global-set-key yas/trigger-key 'yas/expand)
666 (when yas/use-menu
667 (define-key-after
668 (lookup-key global-map [menu-bar])
669 [yasnippet]
670 (cons "YASnippet" yas/menu-keymap)
671 'buffer)))
672
673 (defun yas/define (mode key template &optional name)
674 "Define a snippet. Expanding KEY into TEMPLATE.
675 NAME is a description to this template. Also update
676 the menu if `yas/use-menu' is `t'."
677 (let* ((full-key key)
678 (key (file-name-sans-extension full-key))
679 (template (yas/make-template template (or name key)))
680 (snippet-table (yas/snippet-table mode)))
681 (puthash key
682 (yas/modify-alist (gethash key snippet-table)
683 full-key
684 template)
685 snippet-table)
686 (when yas/use-menu
687 (let ((keymap (yas/menu-keymap-for-mode mode)))
688 (define-key yas/menu-keymap (vector mode)
689 `(menu-item ,(symbol-name mode) ,keymap))
690 (define-key keymap (vector (make-symbol full-key))
691 `(menu-item ,(yas/template-name template)
692 ,(yas/make-menu-binding (yas/template-content template))
693 :keys ,(concat key yas/trigger-symbol)))))))
694
695 (defun yas/expand ()
696 "Expand a snippet."
697 (interactive)
698 (multiple-value-bind (key start end) (yas/current-key)
699 (let ((templates (gethash key (yas/current-snippet-table))))
700 (if templates
701 (let ((template (if (null (cdr templates)) ; only 1 template
702 (yas/template-content (cdar templates))
703 (yas/popup-for-template templates))))
704 (when template
705 (yas/expand-snippet start end template)))
706 (when yas/trigger-fallback
707 (call-interactively yas/trigger-fallback))))))
708
709 (defun yas/next-field-group ()
710 "Navigate to next field group. If there's none, exit the snippet."
711 (interactive)
712 (let ((overlay (yas/current-overlay-for-navigation)))
713 (if overlay
714 (yas/navigate-group (overlay-get overlay 'yas/group) t)
715 (let ((snippet (yas/snippet-of-current-keymap))
716 (done nil))
717 (if snippet
718 (do* ((tabstops (yas/snippet-tabstops snippet) (cdr tabstops))
719 (tabstop (car tabstops) (car tabstops)))
720 ((or (null tabstops)
721 done)
722 (unless done (message "Not in a snippet field.")))
723 (when (= (point)
724 (overlay-start
725 (yas/field-overlay
726 (yas/group-primary-field tabstop))))
727 (setq done t)
728 (yas/navigate-group tabstop t)))
729 (message "Not in a snippet field."))))))
730
731 (defun yas/prev-field-group ()
732 "Navigate to prev field group. If there's none, exit the snippet."
733 (interactive)
734 (let ((overlay (yas/current-overlay-for-navigation)))
735 (if overlay
736 (yas/navigate-group (overlay-get overlay 'yas/group) nil)
737 (let ((snippet (yas/snippet-of-current-keymap))
738 (done nil))
739 (if snippet
740 (do* ((tabstops (yas/snippet-tabstops snippet) (cdr tabstops))
741 (tabstop (car tabstops) (car tabstops)))
742 ((or (null tabstops)
743 done)
744 (unless done (message "Not in a snippet field.")))
745 (when (= (point)
746 (overlay-start
747 (yas/field-overlay
748 (yas/group-primary-field tabstop))))
749 (setq done t)
750 (yas/navigate-group tabstop nil)))
751 (message "Not in a snippet field."))))))
752
753 (defun yas/exit-snippet (snippet)
754 "Goto exit-marker of SNIPPET and delete the snippet."
755 (interactive)
756 (goto-char (yas/snippet-exit-marker snippet))
757 (delete-overlay (yas/snippet-overlay snippet))
758 (dolist (group (yas/snippet-groups snippet))
759 (dolist (field (yas/group-fields group))
760 (delete-overlay (yas/field-overlay field)))))
761
762
763 (provide 'yasnippet)