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