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