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