]> code.delx.au - gnu-emacs-elpa/blob - yasnippet.el
I didn't realized that filter is a function from rst.el instead of Emacs build-in...
[gnu-emacs-elpa] / yasnippet.el
1 ;;; yasnippet.el --- Yet another snippet extension for Emacs.
2
3 ;; Author: pluskid <pluskid@gmail.com>
4 ;; Version: 0.1
5
6 ;; This file is free software; you can redistribute it and/or modify
7 ;; it under the terms of the GNU General Public License as published by
8 ;; the Free Software Foundation; either version 2, or (at your option)
9 ;; any later version.
10
11 ;; This file is distributed in the hope that it will be useful,
12 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;; GNU General Public License for more details.
15
16 ;; You should have received a copy of the GNU General Public License
17 ;; along with GNU Emacs; see the file COPYING. If not, write to
18 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 ;; Boston, MA 02111-1307, USA.
20
21 ;;; Commentary:
22
23 ;; Nothing.
24
25 (require 'cl)
26
27 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
28 ;; User customizable variables
29 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
30 (defvar yas/key-syntax "w"
31 "Syntax of a key. This is used to determine the current key being
32 expanded.")
33
34 (defvar yas/indent-line t
35 "Each (except the 1st) line of the snippet template is indented to
36 current column if this variable is non-`nil'.")
37 (make-variable-buffer-local 'yas/indent-line)
38
39 (defvar yas/trigger-key (kbd "TAB")
40 "The key to bind as a trigger of snippet.")
41 (defvar yas/trigger-fallback 'indent-according-to-mode
42 "The fallback command to call when there's no snippet to expand.")
43 (make-variable-buffer-local 'yas/trigger-fallback)
44
45 (defvar yas/keymap (make-sparse-keymap)
46 "The keymap of snippet.")
47 (define-key yas/keymap (kbd "TAB") 'yas/next-field-group)
48 (define-key yas/keymap (kbd "S-TAB") 'yas/prev-field-group)
49 (define-key yas/keymap (kbd "<S-iso-lefttab>") 'yas/prev-field-group)
50 (define-key yas/keymap (kbd "<S-tab>") 'yas/prev-field-group)
51
52 (defvar yas/use-menu t
53 "If this is set to `t', all snippet template of the current
54 mode will be listed under the menu \"yasnippet\".")
55 (defvar yas/trigger-symbol " =>"
56 "The text that will be used in menu to represent the trigger.")
57 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
58 ;; Internal variables
59 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
60 (defvar yas/version "0.1")
61
62 (defvar yas/snippet-tables (make-hash-table)
63 "A hash table of snippet tables corresponding to each major-mode.")
64 (defvar yas/menu-table (make-hash-table)
65 "A hash table of menus of corresponding major-mode.")
66 (defvar yas/menu-keymap (make-sparse-keymap))
67 ;; empty menu will cause problems, so we insert some items
68 (define-key yas/menu-keymap [yas/about]
69 '(menu-item "About" yas/about))
70 (define-key yas/menu-keymap [yas/separator]
71 '(menu-item "--"))
72
73 (defconst yas/escape-backslash
74 (concat "YASESCAPE" "BACKSLASH" "PROTECTGUARD"))
75 (defconst yas/escape-dollar
76 (concat "YASESCAPE" "DOLLAR" "PROTECTGUARD"))
77 (defconst yas/escape-backquote
78 (concat "YASESCAPE" "BACKQUOTE" "PROTECTGUARD"))
79
80 (defconst yas/field-regexp
81 (concat "$\\(?1:[0-9]+\\)" "\\|"
82 "${\\(?:\\(?1:[0-9]+\\):\\)?\\(?2:[^}]*\\)}"))
83
84 (defvar yas/snippet-id-seed 0
85 "Contains the next id for a snippet")
86 (defun yas/snippet-next-id ()
87 (let ((id yas/snippet-id-seed))
88 (incf yas/snippet-id-seed)
89 id))
90
91 (defvar yas/overlay-modification-hooks
92 (list 'yas/overlay-modification-hook)
93 "The list of hooks to the overlay modification event.")
94 (defvar yas/overlay-insert-in-front-hooks
95 (list 'yas/overlay-insert-in-front-hook)
96 "The list of hooks of the overlay inserted in front event.")
97 (defvar yas/overlay-insert-behind-hooks
98 (list 'yas/overlay-insert-behind-hook)
99 "The list of hooks of the overlay inserted behind event.")
100
101
102 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
103 ;; Internal Structs
104 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
105 (defstruct (yas/template (:constructor yas/make-template (content name)))
106 "A template for a snippet."
107 content
108 name)
109 (defstruct (yas/snippet (:constructor yas/make-snippet ()))
110 "A snippet."
111 (groups nil)
112 (tabstops nil) ; tabstops are those groups whose init value is empty
113 (exit-marker nil)
114 (id (yas/snippet-next-id) :read-only t)
115 (overlay nil))
116 (defstruct (yas/group (:constructor yas/make-group (primary-field snippet)))
117 "A group contains a list of field with the same number."
118 primary-field
119 (fields (list primary-field))
120 (next nil)
121 (prev nil)
122 snippet)
123 (defstruct (yas/field (:constructor yas/make-field (overlay number value)))
124 "A field in a snippet."
125 overlay
126 number
127 value)
128
129 (defun yas/snippet-add-field (snippet field)
130 "Add FIELD to SNIPPET."
131 (let ((group (find field
132 (yas/snippet-groups snippet)
133 :test
134 '(lambda (field group)
135 (= (yas/field-number field)
136 (yas/group-number group))))))
137 (if group
138 (yas/group-add-field group field)
139 (push (yas/make-group field snippet)
140 (yas/snippet-groups snippet)))))
141
142 (defun yas/group-value (group)
143 "Get the default value of the field group."
144 (or (yas/field-value
145 (yas/group-primary-field group))
146 ""))
147 (defun yas/group-number (group)
148 "Get the number of the field group."
149 (yas/field-number
150 (yas/group-primary-field group)))
151 (defun yas/group-add-field (group field)
152 "Add a field to the field group. If the value of the primary
153 field is nil and that of the field is not nil, the field is set
154 as the primary field of the group."
155 (push field (yas/group-fields group))
156 (when (and (null (yas/field-value (yas/group-primary-field group)))
157 (yas/field-value field))
158 (setf (yas/group-primary-field group) field)))
159
160 (defun yas/snippet-field-compare (field1 field2)
161 "Compare two fields. The field with a number is sorted first.
162 If they both have a number, compare through the number. If neither
163 have, compare through the start point of the overlay."
164 (let ((n1 (yas/field-number field1))
165 (n2 (yas/field-number field2)))
166 (if n1
167 (if n2
168 (< n1 n2)
169 t)
170 (if n2
171 nil
172 (< (overlay-start (yas/field-overlay field1))
173 (overlay-start (yas/field-overlay field2)))))))
174
175 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
176 ;; Internal functions
177 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
178 (defun yas/eval-string (string)
179 "Evaluate STRING and convert the result to string."
180 (condition-case err
181 (format "%s" (eval (read string)))
182 (error (format "(error in elisp evaluation: %s)"
183 (error-message-string err)))))
184 (defsubst yas/replace-all (from to)
185 "Replace all occurance from FROM to TO."
186 (goto-char (point-min))
187 (while (search-forward from nil t)
188 (replace-match to t t)))
189
190 (defun yas/snippet-table (mode)
191 "Get the snippet table corresponding to MODE."
192 (let ((table (gethash mode yas/snippet-tables)))
193 (unless table
194 (setq table (make-hash-table :test 'equal))
195 (puthash mode table yas/snippet-tables))
196 table))
197 (defsubst yas/current-snippet-table ()
198 "Get the snippet table for current major-mode."
199 (yas/snippet-table major-mode))
200
201 (defun yas/menu-keymap-for-mode (mode)
202 "Get the menu keymap correspondong to MODE."
203 (let ((keymap (gethash mode yas/menu-table)))
204 (unless keymap
205 (setq table (make-sparse-keymap))
206 (puthash mode keymap yas/menu-table))
207 table))
208
209 (defsubst yas/template (key snippet-table)
210 "Get template for KEY in SNIPPET-TABLE."
211 (gethash key snippet-table))
212
213 (defun yas/current-key ()
214 "Get the key under current position. A key is used to find
215 the template of a snippet in the current snippet-table."
216 (let ((start (point))
217 (end (point)))
218 (save-excursion
219 (skip-syntax-backward yas/key-syntax)
220 (setq start (point))
221 (list (buffer-substring-no-properties start end)
222 start
223 end))))
224
225 (defun yas/synchronize-fields (field-group)
226 "Update all fields' text according to the primary field."
227 (save-excursion
228 (let* ((inhibit-modification-hooks t)
229 (primary (yas/group-primary-field field-group))
230 (primary-overlay (yas/field-overlay primary))
231 (text (buffer-substring-no-properties (overlay-start primary-overlay)
232 (overlay-end primary-overlay))))
233 (dolist (field (yas/group-fields field-group))
234 (let* ((field-overlay (yas/field-overlay field))
235 (original-length (- (overlay-end field-overlay)
236 (overlay-start field-overlay))))
237 (unless (eq field-overlay primary-overlay)
238 (goto-char (overlay-start field-overlay))
239 (insert text)
240 (if (= (overlay-start field-overlay)
241 (overlay-end field-overlay))
242 (move-overlay field-overlay
243 (overlay-start field-overlay)
244 (point))
245 (delete-char original-length))))))))
246
247 (defun yas/overlay-modification-hook (overlay after? beg end &optional length)
248 "Modification hook for snippet field overlay."
249 (when (and after? (not undo-in-progress))
250 (yas/synchronize-fields (overlay-get overlay 'yas/group))))
251 (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length)
252 "Hook for snippet overlay when text is inserted in front of a snippet field."
253 (when after?
254 (let ((field-group (overlay-get overlay 'yas/group))
255 (inhibit-modification-hooks t))
256 (when (not (overlay-get overlay 'yas/modified?))
257 (overlay-put overlay 'yas/modified? t)
258 (when (> (overlay-end overlay) end)
259 (save-excursion
260 (goto-char end)
261 (delete-char (- (overlay-end overlay) end)))))
262 (yas/synchronize-fields field-group))))
263 (defun yas/overlay-insert-behind-hook (overlay after? beg end &optional length)
264 "Hook for snippet overlay when text is inserted just behind a snippet field."
265 (when (and after?
266 (null (yas/current-snippet-overlay beg))) ; not inside another field
267 (move-overlay overlay
268 (overlay-start overlay)
269 end)
270 (yas/synchronize-fields (overlay-get overlay 'yas/group))))
271
272 (defun yas/undo-expand-snippet (start end key snippet)
273 "Undo a snippet expansion. Delete the overlays. This undo can't be
274 redo-ed."
275 (let ((undo (car buffer-undo-list)))
276 (while (null undo)
277 (setq buffer-undo-list (cdr buffer-undo-list))
278 (setq undo (car buffer-undo-list)))
279 ;; Remove this undo operation record
280 (setq buffer-undo-list (cdr buffer-undo-list))
281 (let ((inhibit-modification-hooks t)
282 (buffer-undo-list t))
283 (yas/exit-snippet snippet)
284 (goto-char start)
285 (delete-char (- end start))
286 (insert key))))
287
288 (defun yas/expand-snippet (start end template)
289 "Expand snippet at current point. Text between START and END
290 will be deleted before inserting template."
291 (goto-char start)
292
293 (let ((key (buffer-substring-no-properties start end))
294 (original-undo-list buffer-undo-list)
295 (inhibit-modification-hooks t)
296 (length (- end start))
297 (column (current-column)))
298 (save-restriction
299 (narrow-to-region start start)
300
301 (setq buffer-undo-list t)
302 (insert template)
303
304 ;; Step 1: do necessary indent
305 (when yas/indent-line
306 (let* ((indent (if indent-tabs-mode
307 (concat (make-string (/ column tab-width) ?\t)
308 (make-string (% column tab-width) ?\ ))
309 (make-string column ?\ ))))
310 (goto-char (point-min))
311 (while (and (zerop (forward-line))
312 (= (current-column) 0))
313 (insert indent))))
314
315 ;; Step 2: protect backslash and backquote
316 (yas/replace-all "\\\\" yas/escape-backslash)
317 (yas/replace-all "\\`" yas/escape-backquote)
318
319 ;; Step 3: evaluate all backquotes
320 (goto-char (point-min))
321 (while (re-search-forward "`\\([^`]*\\)`" nil t)
322 (replace-match (yas/eval-string (match-string-no-properties 1))
323 t t))
324
325 ;; Step 4: protect all escapes, including backslash and backquot
326 ;; which may be produced in Step 3
327 (yas/replace-all "\\\\" yas/escape-backslash)
328 (yas/replace-all "\\`" yas/escape-backquote)
329 (yas/replace-all "\\$" yas/escape-dollar)
330
331 (let ((snippet (yas/make-snippet)))
332 ;; Step 5: Create fields
333 (goto-char (point-min))
334 (while (re-search-forward yas/field-regexp nil t)
335 (let ((number (match-string-no-properties 1)))
336 (if (and number
337 (string= "0" number))
338 (progn
339 (replace-match "")
340 (setf (yas/snippet-exit-marker snippet)
341 (copy-marker (point) t)))
342 (yas/snippet-add-field
343 snippet
344 (yas/make-field
345 (make-overlay (match-beginning 0) (match-end 0))
346 (and number (string-to-number number))
347 (match-string-no-properties 2))))))
348
349 ;; Step 6: Sort and link each field group
350 (setf (yas/snippet-groups snippet)
351 (sort (yas/snippet-groups snippet)
352 '(lambda (group1 group2)
353 (yas/snippet-field-compare
354 (yas/group-primary-field group1)
355 (yas/group-primary-field group2)))))
356 (let ((prev nil))
357 (dolist (group (yas/snippet-groups snippet))
358 (setf (yas/group-prev group) prev)
359 (when prev
360 (setf (yas/group-next prev) group))
361 (setq prev group)))
362
363 ;; Step 7: Create keymap overlay for snippet
364 (let ((overlay (make-overlay (point-min)
365 (point-max)
366 nil
367 nil
368 t)))
369 (overlay-put overlay 'keymap yas/keymap)
370 (overlay-put overlay 'yas/snippet-reference snippet)
371 (setf (yas/snippet-overlay snippet) overlay))
372
373 ;; Step 8: Replace fields with default values
374 (dolist (group (yas/snippet-groups snippet))
375 (let ((value (yas/group-value group)))
376 (when (string= "" value)
377 (push group (yas/snippet-tabstops snippet)))
378 (dolist (field (yas/group-fields group))
379 (let* ((overlay (yas/field-overlay field))
380 (start (overlay-start overlay))
381 (end (overlay-end overlay))
382 (length (- end start)))
383 (goto-char start)
384 (insert value)
385 (delete-char length)))))
386
387 ;; Step 9: restore all escape characters
388 (yas/replace-all yas/escape-dollar "$")
389 (yas/replace-all yas/escape-backquote "`")
390 (yas/replace-all yas/escape-backslash "\\")
391
392 ;; Step 10: Set up properties of overlays
393 (dolist (group (yas/snippet-groups snippet))
394 (let ((overlay (yas/field-overlay
395 (yas/group-primary-field group))))
396 (overlay-put overlay 'yas/snippet snippet)
397 (overlay-put overlay 'yas/group group)
398 (overlay-put overlay 'yas/modified? nil)
399 (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks)
400 (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks)
401 (overlay-put overlay 'insert-behind-hooks yas/overlay-insert-behind-hooks)
402 (dolist (field (yas/group-fields group))
403 (overlay-put (yas/field-overlay field)
404 'face
405 'highlight))))
406
407 ;; Step 11: move to end and make sure exit-marker exist
408 (goto-char (point-max))
409 (unless (yas/snippet-exit-marker snippet)
410 (setf (yas/snippet-exit-marker snippet) (copy-marker (point) t)))
411
412 ;; Step 12: Construct undo information
413 (unless (eq original-undo-list t)
414 (add-to-list 'original-undo-list
415 `(apply yas/undo-expand-snippet
416 ,(point-min)
417 ,(point-max)
418 ,key
419 ,snippet)))
420
421 ;; Step 13: remove the trigger key
422 (widen)
423 (delete-char length)
424
425 ;; Step 14: place the cursor at a proper place
426 (let ((groups (yas/snippet-groups snippet))
427 (exit-marker (yas/snippet-exit-marker snippet)))
428 (if groups
429 (goto-char (overlay-start
430 (yas/field-overlay
431 (yas/group-primary-field
432 (car groups)))))
433 ;; no need to call exit-snippet, since no overlay created.
434 (goto-char exit-marker)))
435
436 (setq buffer-undo-list original-undo-list)))))
437
438 (defun yas/current-snippet-overlay (&optional point)
439 "Get the most proper overlay which is belongs to a snippet."
440 (let ((point (or point (point)))
441 (snippet-overlay nil))
442 (dolist (overlay (overlays-at point))
443 (when (overlay-get overlay 'yas/snippet)
444 (if (null snippet-overlay)
445 (setq snippet-overlay overlay)
446 (when (> (yas/snippet-id (overlay-get overlay 'yas/snippet))
447 (yas/snippet-id (overlay-get snippet-overlay 'yas/snippet)))
448 (setq snippet-overlay overlay)))))
449 snippet-overlay))
450
451 (defun yas/snippet-of-current-keymap (&optional point)
452 "Get the snippet holding the snippet keymap under POINT."
453 (let ((point (or point (point)))
454 (keymap-snippet nil)
455 (snippet nil))
456 (dolist (overlay (overlays-at point))
457 (setq snippet (overlay-get overlay 'yas/snippet-reference))
458 (when snippet
459 (if (null keymap-snippet)
460 (setq keymap-snippet snippet)
461 (when (> (yas/snippet-id snippet)
462 (yas/snippet-id keymap-snippet))
463 (setq keymap-snippet snippet)))))
464 keymap-snippet))
465
466 (defun yas/current-overlay-for-navigation ()
467 "Get current overlay for navigation. Might be overlay at current or previous point."
468 (let ((overlay1 (yas/current-snippet-overlay))
469 (overlay2 (if (bobp)
470 nil
471 (yas/current-snippet-overlay (- (point) 1)))))
472 (if (null overlay1)
473 overlay2
474 (if (or (null overlay2)
475 (eq (overlay-get overlay1 'yas/snippet)
476 (overlay-get overlay2 'yas/snippet)))
477 overlay1
478 (if (> (yas/snippet-id (overlay-get overlay2 'yas/snippet))
479 (yas/snippet-id (overlay-get overlay1 'yas/snippet)))
480 overlay2
481 overlay1)))))
482
483 (defun yas/navigate-group (group next?)
484 "Go to next of previous field group. Exit snippet if none."
485 (let ((target (if next?
486 (yas/group-next group)
487 (yas/group-prev group))))
488 (if target
489 (goto-char (overlay-start
490 (yas/field-overlay
491 (yas/group-primary-field target))))
492 (yas/exit-snippet (yas/group-snippet group)))))
493
494 (defun yas/parse-template ()
495 "Parse the template in the current buffer.
496 If the buffer contains a line of \"# --\" then the contents
497 above this line are ignored. Variables can be set above this
498 line through the syntax:
499
500 #name : value
501
502 Currently only the \"name\" variable is recognized. Here's
503 an example:
504
505 #name: #include \"...\"
506 # --
507 #include \"$1\""
508 (goto-char (point-min))
509 (let (template name bound)
510 (if (re-search-forward "^# --\n" nil t)
511 (progn (setq template
512 (buffer-substring-no-properties (point)
513 (point-max)))
514 (setq bound (point))
515 (goto-char (point-min))
516 (while (re-search-forward "^#\\([^ ]+\\) *: *\\(.*\\)$" bound t)
517 (when (string= "name" (match-string-no-properties 1))
518 (setq name (match-string-no-properties 2)))))
519 (setq template
520 (buffer-substring-no-properties (point-min) (point-max))))
521 (list template name)))
522
523 (defun yas/directory-files (directory file?)
524 "Return directory files or subdirectories in full path."
525 (remove-if (lambda (file)
526 (and (not (string-match "/\\.[^/]*$" file))
527 (if file?
528 (not (file-directory-p file))
529 (file-directory-p file))))
530 (directory-files directory t)))
531
532 (defun yas/make-menu-binding (template)
533 (lexical-let ((template template))
534 (lambda ()
535 (interactive)
536 (yas/expand-snippet (point)
537 (point)
538 template))))
539
540 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
541 ;; User level functions
542 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
543 (defun yas/about ()
544 (interactive)
545 (message (concat "yasnippet (version "
546 yas/version
547 ") -- pluskid <pluskid@gmail.com>")))
548 (defun yas/initialize ()
549 "Do necessary initialization."
550 (global-set-key yas/trigger-key 'yas/expand)
551 (when yas/use-menu
552 (define-key-after global-map
553 [menu-bar yasnippet]
554 (cons "YASnippet" yas/menu-keymap)
555 'buffer)))
556
557 (defun yas/define (mode key template &optional name)
558 "Define a snippet. Expanding KEY into TEMPLATE.
559 NAME is a description to this template. Also update
560 the menu if `yas/use-menu' is `t'."
561 (let ((template (yas/make-template template (or name key))))
562 (puthash key
563 template
564 (yas/snippet-table mode))
565 (when yas/use-menu
566 (let ((keymap (yas/menu-keymap-for-mode mode)))
567 (define-key yas/menu-keymap (vector mode)
568 `(menu-item ,(symbol-name mode) ,keymap))
569 (define-key keymap (vector (make-symbol key))
570 `(menu-item ,(yas/template-name template)
571 ,(yas/make-menu-binding (yas/template-content template))
572 :keys ,(concat key yas/trigger-symbol)))))))
573
574 (defun yas/expand ()
575 "Expand a snippet. When a snippet is expanded, t is returned,
576 otherwise, nil returned."
577 (interactive)
578 (multiple-value-bind (key start end) (yas/current-key)
579 (let ((template (yas/template key (yas/current-snippet-table))))
580 (if template
581 (yas/expand-snippet start end (yas/template-content template))
582 (when yas/trigger-fallback
583 (call-interactively yas/trigger-fallback))))))
584
585 (defun yas/next-field-group ()
586 "Navigate to next field group. If there's none, exit the snippet."
587 (interactive)
588 (let ((overlay (yas/current-overlay-for-navigation)))
589 (if overlay
590 (yas/navigate-group (overlay-get overlay 'yas/group) t)
591 (let ((snippet (yas/snippet-of-current-keymap))
592 (done nil))
593 (if snippet
594 (do* ((tabstops (yas/snippet-tabstops snippet) (cdr tabstops))
595 (tabstop (car tabstops) (car tabstops)))
596 ((or (null tabstops)
597 done)
598 (unless done (message "Not in a snippet field.")))
599 (when (= (point)
600 (overlay-start
601 (yas/field-overlay
602 (yas/group-primary-field tabstop))))
603 (setq done t)
604 (yas/navigate-group tabstop t)))
605 (message "Not in a snippet field."))))))
606
607 (defun yas/prev-field-group ()
608 "Navigate to prev field group. If there's none, exit the snippet."
609 (interactive)
610 (let ((overlay (yas/current-overlay-for-navigation)))
611 (if overlay
612 (yas/navigate-group (overlay-get overlay 'yas/group) nil)
613 (let ((snippet (yas/snippet-of-current-keymap))
614 (done nil))
615 (if snippet
616 (do* ((tabstops (yas/snippet-tabstops snippet) (cdr tabstops))
617 (tabstop (car tabstops) (car tabstops)))
618 ((or (null tabstops)
619 done)
620 (unless done (message "Not in a snippet field.")))
621 (when (= (point)
622 (overlay-start
623 (yas/field-overlay
624 (yas/group-primary-field tabstop))))
625 (setq done t)
626 (yas/navigate-group tabstop nil)))
627 (message "Not in a snippet field."))))))
628
629 (defun yas/exit-snippet (snippet)
630 "Goto exit-marker of SNIPPET and delete the snippet."
631 (interactive)
632 (goto-char (yas/snippet-exit-marker snippet))
633 (delete-overlay (yas/snippet-overlay snippet))
634 (dolist (group (yas/snippet-groups snippet))
635 (dolist (field (yas/group-fields group))
636 (delete-overlay (yas/field-overlay field)))))
637
638 (defun yas/load-directory (directory)
639 "Load snippet definition from a directory hierarchy.
640 Below the top-level directory, each directory is a mode
641 name. And under each subdirectory, each file is a definition
642 of a snippet. The file name is the trigger key and the
643 content of the file is the template."
644 (with-temp-buffer
645 (dolist (mode (yas/directory-files directory nil))
646 (let* ((mode-sym (intern (file-name-nondirectory mode)))
647 (snippet-table (yas/snippet-table mode-sym))
648 (keymap (if yas/use-menu
649 (yas/menu-keymap-for-mode mode-sym)
650 nil)))
651 (dolist (key (yas/directory-files mode t))
652 (when (file-readable-p key)
653 (insert-file-contents key nil nil nil t)
654 (multiple-value-bind
655 (key template name)
656 (cons (file-name-sans-extension
657 (file-name-nondirectory key))
658 (yas/parse-template))
659 (puthash key
660 (yas/make-template
661 template
662 (or name key))
663 snippet-table)
664 (when yas/use-menu
665 (define-key yas/menu-keymap (vector mode-sym)
666 `(menu-item ,(symbol-name mode-sym) ,keymap))
667 (define-key keymap (vector (make-symbol key))
668 `(menu-item ,(or name key)
669 ,(yas/make-menu-binding template)
670 :keys ,(concat key yas/trigger-symbol)))))))))))
671
672 (provide 'yasnippet)