Remove project & _list_projects, I don't use them anymore.
1 ;;; yasnippet.el --- Yet another snippet extension for Emacs.
3 ;; Copyright 2008 pluskid
5 ;; Author: pluskid <pluskid@gmail.com>
7 ;; X-URL: http://code.google.com/p/yasnippet/
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)
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.
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.
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
33 ;; (yas/load-directory "~/.emacs.d/snippets")
35 ;; For more information and detailed usage, refer to the project page:
36 ;; http://code.google.com/p/yasnippet/
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 \"_\":
50 will first try \"bar\", if not found, then \"foo-bar\" is tried.")
52 (defvar yas/root-directory nil
53 "The (list of) root directory that stores the snippets for each
56 (defvar yas/indent-line t
57 "Each (except the 1st) line of the snippet template is indented to
58 current column if this variable is non-`nil'.")
59 (make-variable-buffer-local 'yas/indent-line)
61 (defvar yas/trigger-key (kbd "TAB")
62 "The key to bind as a trigger of snippet.")
63 (defvar yas/next-field-key (kbd "TAB")
64 "The key to navigate to next field.")
66 (defvar yas/keymap (make-sparse-keymap)
67 "The keymap of snippet.")
68 (define-key yas/keymap yas/next-field-key 'yas/next-field-group)
69 (define-key yas/keymap (kbd "S-TAB") 'yas/prev-field-group)
70 (define-key yas/keymap (kbd "<S-iso-lefttab>") 'yas/prev-field-group)
71 (define-key yas/keymap (kbd "<S-tab>") 'yas/prev-field-group)
72 (define-key yas/keymap (kbd "<backtab>") 'yas/prev-field-group)
74 (defvar yas/show-all-modes-in-menu nil
75 "Currently yasnippet only all \"real modes\" to menubar. For
76 example, you define snippets for \"cc-mode\" and make it the
77 parent of `c-mode', `c++-mode' and `java-mode'. There's really
78 no such mode like \"cc-mode\". So we don't show it in the yasnippet
79 menu to avoid the menu becoming too big with strange modes. The
80 snippets defined for \"cc-mode\" can still be accessed from
81 menu-bar->c-mode->parent (or c++-mode, java-mode, all are ok).
82 However, if you really like to show all modes in the menu, set
84 (defvar yas/use-menu t
85 "If this is set to `t', all snippet template of the current
86 mode will be listed under the menu \"yasnippet\".")
87 (defvar yas/trigger-symbol " =>"
88 "The text that will be used in menu to represent the trigger.")
90 (defface yas/field-highlight-face
91 '((((class color) (background light)) (:background "DarkSeaGreen2"))
92 (t (:background "DimGrey")))
93 "The face used to highlight a field of snippet.")
94 (defface yas/mirror-highlight-face
95 '((((class color) (background light)) (:background "LightYellow2"))
96 (t (:background "gray22")))
97 "The face used to highlight mirror fields of a snippet.")
99 (defvar yas/window-system-popup-function #'yas/dropdown-list-popup-for-template
100 "When there's multiple candidate for a snippet key. This function
101 is called to let user select one of them. `yas/text-popup-function'
102 is used instead when not in a window system.")
103 (defvar yas/text-popup-function #'yas/dropdown-list-popup-for-template
104 "When there's multiple candidate for a snippet key. If not in a
105 window system, this function is called to let user select one of
106 them. `yas/window-system-popup-function' is used instead when in
109 (defvar yas/extra-mode-hooks
110 '(ruby-mode-hook actionscript-mode-hook)
111 "A list of mode-hook that should be hooked to enable yas/minor-mode.
112 Most modes need no special consideration. Some mode (like ruby-mode)
113 doesn't call `after-change-major-mode-hook' need to be hooked explicitly.")
115 (defvar yas/after-exit-snippet-hook
117 "Hooks to run after a snippet exited.
118 The hooks will be run in an environment where some variables bound to
120 * yas/snippet-beg : The beginning of the region of the snippet.
121 * yas/snippet-end : Similar to beg.")
123 (defvar yas/before-expand-snippet-hook
125 "Hooks to run after a before expanding a snippet.")
127 (defvar yas/buffer-local-condition
128 '(if (and (not (bobp))
129 (or (equal "font-lock-comment-face"
130 (get-char-property (1- (point))
132 (equal "font-lock-string-face"
133 (get-char-property (1- (point))
135 '(require-snippet-condition . force-in-comment)
137 "Condition to yasnippet local to each buffer.
139 * If yas/buffer-local-condition evaluate to nil, snippet
142 * If it evaluate to the a cons cell where the car is the
143 symbol require-snippet-condition and the cdr is a
144 symbol (let's call it requirement):
145 * If the snippet has no condition, then it won't be
147 * If the snippet has a condition but evaluate to nil or
148 error occured during evaluation, it won't be expanded.
149 * If the snippet has a condition that evaluate to
150 non-nil (let's call it result):
151 * If requirement is t, the snippet is ready to be
153 * If requirement is eq to result, the snippet is ready
155 * Otherwise the snippet won't be expanded.
156 * If it evaluate to other non-nil value:
157 * If the snippet has no condition, or has a condition that
158 evaluate to non-nil, it is ready to be expanded.
159 * Otherwise, it won't be expanded.
163 (add-hook 'python-mode-hook
165 (setq yas/buffer-local-condition
166 '(if (python-in-string/comment)
167 '(require-snippet-condition . force-in-comment)
170 (defvar yas/fallback-behavior 'call-other-command
171 "The fall back behavior of YASnippet when it can't find a snippet
174 * 'call-other-command means try to temporarily disable
175 YASnippet and call other command bound to `yas/trigger-key'.
176 * 'return-nil means return nil.")
178 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
179 ;; Internal variables
180 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
181 (defvar yas/version "0.5.2")
183 (defvar yas/snippet-tables (make-hash-table)
184 "A hash table of snippet tables corresponding to each major-mode.")
185 (defvar yas/menu-table (make-hash-table)
186 "A hash table of menus of corresponding major-mode.")
187 (defvar yas/menu-keymap (make-sparse-keymap "YASnippet"))
188 ;; empty menu will cause problems, so we insert some items
189 (define-key yas/menu-keymap [yas/about]
190 '(menu-item "About" yas/about))
191 (define-key yas/menu-keymap [yas/reload]
192 '(menu-item "Reload all snippets" yas/reload-all))
193 (define-key yas/menu-keymap [yas/load]
194 '(menu-item "Load snippets..." yas/load-directory))
195 (define-key yas/menu-keymap [yas/separator]
198 (defvar yas/known-modes
199 '(ruby-mode rst-mode)
200 "A list of mode which is well known but not part of emacs.")
201 (defconst yas/escape-backslash
202 (concat "YASESCAPE" "BACKSLASH" "PROTECTGUARD"))
203 (defconst yas/escape-dollar
204 (concat "YASESCAPE" "DOLLAR" "PROTECTGUARD"))
205 (defconst yas/escape-backquote
206 (concat "YASESCAPE" "BACKQUOTE" "PROTECTGUARD"))
208 (defconst yas/field-regexp
209 (concat "$\\([0-9]+\\)" "\\|"
210 "${\\(?:\\([0-9]+\\):\\)?\\([^}]*\\)}"))
212 (defvar yas/snippet-id-seed 0
213 "Contains the next id for a snippet")
214 (defun yas/snippet-next-id ()
215 (let ((id yas/snippet-id-seed))
216 (incf yas/snippet-id-seed)
219 (defvar yas/overlay-modification-hooks
220 (list 'yas/overlay-modification-hook)
221 "The list of hooks to the overlay modification event.")
222 (defvar yas/overlay-insert-in-front-hooks
223 (list 'yas/overlay-insert-in-front-hook)
224 "The list of hooks of the overlay inserted in front event.")
225 (defvar yas/keymap-overlay-modification-hooks
226 (list 'yas/overlay-maybe-insert-behind-hook)
227 "The list of hooks of the big keymap overlay modification event.")
229 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
230 ;; YASnippet minor mode
231 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
232 (defvar yas/minor-mode-map (make-sparse-keymap)
233 "The keymap of yas/minor-mode")
234 (defvar yas/minor-mode-on-hook nil
235 "Hook to call when yas/minor-mode is on.")
236 (defvar yas/minor-mode-off-hook nil
237 "Hook to call when yas/minor-mode is off.")
238 (define-minor-mode yas/minor-mode
239 "Toggle YASnippet mode.
240 With no argument, this command toggles the mode.
241 positive prefix argument turns on the mode.
242 Negative prefix argument turns off the mode.
244 When YASnippet mode is enabled, the TAB key
245 expands snippets of code depending on the mode.
247 You can customize the key through `yas/trigger-key'."
248 ;; The initial value.
250 ;; The indicator for the mode line.
253 (define-key yas/minor-mode-map yas/trigger-key 'yas/expand))
255 (defun yas/minor-mode-on ()
256 "Turn on YASnippet minor mode."
259 (defun yas/minor-mode-off ()
260 "Turn off YASnippet minor mode."
265 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
267 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
268 (defstruct (yas/template (:constructor yas/make-template
269 (content name condition)))
270 "A template for a snippet."
274 (defstruct (yas/snippet (:constructor yas/make-snippet ()))
278 (id (yas/snippet-next-id) :read-only t)
280 (defstruct (yas/group (:constructor yas/make-group (primary-field snippet)))
281 "A group contains a list of field with the same number."
283 (fields (list primary-field))
287 (defstruct (yas/field
288 (:constructor yas/make-field (overlay number value transform)))
289 "A field in a snippet."
294 (defstruct (yas/snippet-table (:constructor yas/make-snippet-table ()))
295 "A table to store snippets for a perticular mode."
296 (hash (make-hash-table :test 'equal))
299 (defun yas/snippet-valid? (snippet)
300 "See if snippet is valid (ie. still alive)."
301 (and (not (null snippet))
302 (not (null (yas/snippet-overlay snippet)))
303 (not (null (overlay-start (yas/snippet-overlay snippet))))))
305 (defun yas/snippet-add-field (snippet field)
306 "Add FIELD to SNIPPET."
307 (let ((group (find field
308 (yas/snippet-groups snippet)
310 '(lambda (field group)
311 (and (not (null (yas/field-number field)))
312 (not (null (yas/group-number group)))
313 (= (yas/field-number field)
314 (yas/group-number group)))))))
316 (yas/group-add-field group field)
317 (push (yas/make-group field snippet)
318 (yas/snippet-groups snippet)))))
320 (defun yas/group-value (group)
321 "Get the default value of the field group."
323 (yas/group-primary-field group))
325 (defun yas/group-number (group)
326 "Get the number of the field group."
328 (yas/group-primary-field group)))
329 (defun yas/group-add-field (group field)
330 "Add a field to the field group. If the value of the primary
331 field is nil and that of the field is not nil, the field is set
332 as the primary field of the group."
333 (push field (yas/group-fields group))
334 (when (and (null (yas/field-value (yas/group-primary-field group)))
335 (yas/field-value field))
336 (setf (yas/group-primary-field group) field)))
338 (defun yas/snippet-field-compare (field1 field2)
339 "Compare two fields. The field with a number is sorted first.
340 If they both have a number, compare through the number. If neither
341 have, compare through the start point of the overlay."
342 (let ((n1 (yas/field-number field1))
343 (n2 (yas/field-number field2)))
350 (< (overlay-start (yas/field-overlay field1))
351 (overlay-start (yas/field-overlay field2)))))))
353 (defun yas/template-condition-predicate (condition)
360 (message (format "[yas]error in condition evaluation: %s"
361 (error-message-string err)))
364 (defun yas/filter-templates-by-condition (templates)
365 "Filter the templates using the condition. The rules are:
367 * If the template has no condition, it is kept.
368 * If the template's condition eval to non-nil, it is kept.
369 * Otherwise (eval error or eval to nil) it is filtered."
370 (remove-if '(lambda (pair)
371 (let ((condition (yas/template-condition (cdr pair))))
373 (if yas/require-template-condition
377 (yas/template-condition-predicate condition)))
378 (if (eq yas/require-template-condition t)
380 (not (eq result yas/require-template-condition)))))))
383 (defun yas/snippet-table-fetch (table key)
384 "Fetch a snippet binding to KEY from TABLE. If not found,
385 fetch from parent if any."
386 (let ((templates (yas/filter-templates-by-condition
387 (gethash key (yas/snippet-table-hash table)))))
388 (when (and (null templates)
389 (not (null (yas/snippet-table-parent table))))
390 (setq templates (yas/snippet-table-fetch
391 (yas/snippet-table-parent table)
394 (defun yas/snippet-table-store (table full-key key template)
395 "Store a snippet template in the table."
397 (yas/modify-alist (gethash key
398 (yas/snippet-table-hash table))
401 (yas/snippet-table-hash table)))
403 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
404 ;; Internal functions
405 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
406 (defun yas/ensure-minor-mode-priority ()
407 "Ensure that the key binding of yas/minor-mode takes priority."
408 (unless (eq 'yas/minor-mode
409 (caar minor-mode-map-alist))
410 (setq minor-mode-map-alist
412 (cons 'yas/minor-mode yas/minor-mode-map)
413 (assq-delete-all 'yas/minor-mode
414 minor-mode-map-alist)))))
416 (defun yas/real-mode? (mode)
417 "Try to find out if MODE is a real mode. The MODE bound to
418 a function (like `c-mode') is considered real mode. Other well
419 known mode like `ruby-mode' which is not part of Emacs might
420 not bound to a function until it is loaded. So yasnippet keeps
421 a list of modes like this to help the judgement."
423 (find mode yas/known-modes)))
425 (defun yas/eval-string (string)
426 "Evaluate STRING and convert the result to string."
431 (format "%s" (eval (read string))))))
432 (error (format "(error in elisp evaluation: %s)"
433 (error-message-string err)))))
434 (defun yas/calculate-field-value (field value)
435 "Calculate the value of the field. If there's a transform
436 for this field, apply it. Otherwise, the value is returned
439 (transform (yas/field-transform field)))
441 (yas/eval-string transform)
443 (defsubst yas/replace-all (from to)
444 "Replace all occurance from FROM to TO."
445 (goto-char (point-min))
446 (while (search-forward from nil t)
447 (replace-match to t t)))
449 (defun yas/snippet-table (mode)
450 "Get the snippet table corresponding to MODE."
451 (let ((table (gethash mode yas/snippet-tables)))
453 (setq table (yas/make-snippet-table))
454 (puthash mode table yas/snippet-tables))
456 (defsubst yas/current-snippet-table ()
457 "Get the snippet table for current major-mode."
458 (yas/snippet-table major-mode))
460 (defun yas/menu-keymap-for-mode (mode)
461 "Get the menu keymap correspondong to MODE."
462 (let ((keymap (gethash mode yas/menu-table)))
464 (setq keymap (make-sparse-keymap))
465 (puthash mode keymap yas/menu-table))
468 (defun yas/current-key ()
469 "Get the key under current position. A key is used to find
470 the template of a snippet in the current snippet-table."
471 (let ((start (point))
473 (syntaxes yas/key-syntaxes)
474 syntax done templates)
475 (while (and (not done) syntaxes)
476 (setq syntax (car syntaxes))
477 (setq syntaxes (cdr syntaxes))
479 (skip-syntax-backward syntax)
480 (setq start (point)))
482 (yas/snippet-table-fetch
483 (yas/current-snippet-table)
484 (buffer-substring-no-properties start end)))
492 (defun yas/synchronize-fields (field-group)
493 "Update all fields' text according to the primary field."
494 (when (yas/snippet-valid? (yas/group-snippet field-group))
496 (let* ((inhibit-modification-hooks t)
497 (primary (yas/group-primary-field field-group))
498 (primary-overlay (yas/field-overlay primary))
499 (text (buffer-substring-no-properties (overlay-start primary-overlay)
500 (overlay-end primary-overlay))))
501 (dolist (field (yas/group-fields field-group))
502 (let* ((field-overlay (yas/field-overlay field))
503 (original-length (- (overlay-end field-overlay)
504 (overlay-start field-overlay))))
505 (unless (eq field-overlay primary-overlay)
506 (goto-char (overlay-start field-overlay))
507 (insert (yas/calculate-field-value field text))
508 (if (= (overlay-start field-overlay)
509 (overlay-end field-overlay))
510 (move-overlay field-overlay
511 (overlay-start field-overlay)
513 (delete-char original-length)))))))))
515 (defun yas/overlay-modification-hook (overlay after? beg end &optional length)
516 "Modification hook for snippet field overlay."
517 (when (and after? (not undo-in-progress))
518 (yas/synchronize-fields (overlay-get overlay 'yas/group))))
519 (defun yas/overlay-insert-in-front-hook (overlay after? beg end &optional length)
520 "Hook for snippet overlay when text is inserted in front of a snippet field."
522 (let ((field-group (overlay-get overlay 'yas/group))
523 (inhibit-modification-hooks t))
524 (when (not (overlay-get overlay 'yas/modified?))
525 (overlay-put overlay 'yas/modified? t)
526 (when (> (overlay-end overlay) end)
529 (delete-char (- (overlay-end overlay) end)))))
530 (yas/synchronize-fields field-group))))
531 (defun yas/overlay-maybe-insert-behind-hook (overlay after? beg end &optional length)
532 "Insert behind hook sometimes doesn't get called. I don't know why.
533 So I add modification hook in the big overlay and try to detect `insert-behind'
536 (cond ((and (= beg end)
538 (= (overlay-start overlay)
539 (overlay-end overlay)))
540 (yas/exit-snippet (overlay-get overlay 'yas/snippet-reference)))
543 (null (yas/current-snippet-overlay beg))
545 (let ((field-overlay (yas/current-snippet-overlay (1- beg))))
547 (when (= beg (overlay-end field-overlay))
548 (move-overlay field-overlay
549 (overlay-start field-overlay)
551 (yas/synchronize-fields (overlay-get field-overlay 'yas/group)))
552 (let ((snippet (yas/snippet-of-current-keymap))
555 (do* ((groups (yas/snippet-groups snippet) (cdr groups))
556 (group (car groups) (car groups)))
559 (setq field-overlay (yas/field-overlay
560 (yas/group-primary-field group)))
561 (when (and (= (overlay-start field-overlay)
562 (overlay-end field-overlay))
564 (overlay-start field-overlay)))
565 (move-overlay field-overlay beg end)
566 (yas/synchronize-fields group)
567 (setq done t)))))))))))
569 (defun yas/undo-expand-snippet (start end key snippet)
570 "Undo a snippet expansion. Delete the overlays. This undo can't be
572 (let ((undo (car buffer-undo-list)))
574 (setq buffer-undo-list (cdr buffer-undo-list))
575 (setq undo (car buffer-undo-list)))
576 ;; Remove this undo operation record
577 (setq buffer-undo-list (cdr buffer-undo-list))
578 (let ((inhibit-modification-hooks t)
579 (buffer-undo-list t))
580 (yas/exit-snippet snippet)
582 (delete-char (- end start))
585 (defun yas/expand-snippet (start end template)
586 "Expand snippet at current point. Text between START and END
587 will be deleted before inserting template."
588 (run-hooks 'yas/before-expand-snippet-hook)
592 (let ((key (buffer-substring-no-properties start end))
593 (original-undo-list buffer-undo-list)
594 (inhibit-modification-hooks t)
595 (length (- end start))
596 (column (current-column)))
598 (narrow-to-region start start)
600 (setq buffer-undo-list t)
603 ;; Step 1: do necessary indent
604 (when yas/indent-line
605 (let* ((indent (if indent-tabs-mode
606 (concat (make-string (/ column tab-width) ?\t)
607 (make-string (% column tab-width) ?\ ))
608 (make-string column ?\ ))))
609 (goto-char (point-min))
610 (while (and (zerop (forward-line))
611 (= (current-column) 0))
614 ;; Step 2: protect backslash and backquote
615 (yas/replace-all "\\\\" yas/escape-backslash)
616 (yas/replace-all "\\`" yas/escape-backquote)
618 ;; Step 3: evaluate all backquotes
619 (goto-char (point-min))
620 (while (re-search-forward "`\\([^`]*\\)`" nil t)
621 (replace-match (yas/eval-string (match-string-no-properties 1))
624 ;; Step 4: protect all escapes, including backslash and backquot
625 ;; which may be produced in Step 3
626 (yas/replace-all "\\\\" yas/escape-backslash)
627 (yas/replace-all "\\`" yas/escape-backquote)
628 (yas/replace-all "\\$" yas/escape-dollar)
630 (let ((snippet (yas/make-snippet)))
631 ;; Step 5: Create fields
632 (goto-char (point-min))
633 (while (re-search-forward yas/field-regexp nil t)
634 (let ((number (or (match-string-no-properties 1)
635 (match-string-no-properties 2)))
637 (value (match-string-no-properties 3)))
638 (when (eq (elt value 0) ?\$)
639 (setq transform (substring value 1))
642 (string= "0" number))
645 (setf (yas/snippet-exit-marker snippet)
646 (copy-marker (point) t)))
647 (yas/snippet-add-field
650 (make-overlay (match-beginning 0) (match-end 0))
651 (and number (string-to-number number))
655 ;; Step 6: Sort and link each field group
656 (setf (yas/snippet-groups snippet)
657 (sort (yas/snippet-groups snippet)
658 '(lambda (group1 group2)
659 (yas/snippet-field-compare
660 (yas/group-primary-field group1)
661 (yas/group-primary-field group2)))))
663 (dolist (group (yas/snippet-groups snippet))
664 (setf (yas/group-prev group) prev)
666 (setf (yas/group-next prev) group))
669 ;; Step 7: Create keymap overlay for snippet
670 (let ((overlay (make-overlay (point-min)
677 yas/keymap-overlay-modification-hooks)
680 yas/keymap-overlay-modification-hooks)
681 (overlay-put overlay 'keymap yas/keymap)
682 (overlay-put overlay 'yas/snippet-reference snippet)
683 (setf (yas/snippet-overlay snippet) overlay))
685 ;; Step 8: Replace fields with default values
686 (dolist (group (yas/snippet-groups snippet))
687 (let ((value (yas/group-value group)))
688 (dolist (field (yas/group-fields group))
689 (let* ((overlay (yas/field-overlay field))
690 (start (overlay-start overlay))
691 (end (overlay-end overlay))
692 (length (- end start)))
694 (insert (yas/calculate-field-value field value))
695 (delete-char length)))))
697 ;; Step 9: restore all escape characters
698 (yas/replace-all yas/escape-dollar "$")
699 (yas/replace-all yas/escape-backquote "`")
700 (yas/replace-all yas/escape-backslash "\\")
702 ;; Step 10: Set up properties of overlays
703 (dolist (group (yas/snippet-groups snippet))
704 (let ((overlay (yas/field-overlay
705 (yas/group-primary-field group))))
706 (overlay-put overlay 'yas/snippet snippet)
707 (overlay-put overlay 'yas/group group)
708 (overlay-put overlay 'yas/modified? nil)
709 (overlay-put overlay 'modification-hooks yas/overlay-modification-hooks)
710 (overlay-put overlay 'insert-in-front-hooks yas/overlay-insert-in-front-hooks)
711 (overlay-put overlay 'face 'yas/field-highlight-face)
712 (dolist (field (yas/group-fields group))
713 (unless (equal overlay (yas/field-overlay field))
714 (overlay-put (yas/field-overlay field)
716 'yas/mirror-highlight-face)))))
718 ;; Step 11: move to end and make sure exit-marker exist
719 (goto-char (point-max))
720 (unless (yas/snippet-exit-marker snippet)
721 (setf (yas/snippet-exit-marker snippet) (copy-marker (point) t)))
723 ;; Step 12: Construct undo information
724 (unless (eq original-undo-list t)
725 (add-to-list 'original-undo-list
726 `(apply yas/undo-expand-snippet
732 ;; Step 13: remove the trigger key
736 (setq buffer-undo-list original-undo-list)
738 ;; Step 14: place the cursor at a proper place
739 (let ((groups (yas/snippet-groups snippet))
740 (exit-marker (yas/snippet-exit-marker snippet)))
742 (goto-char (overlay-start
744 (yas/group-primary-field
746 ;; no need to call exit-snippet, since no overlay created.
747 (yas/exit-snippet snippet)))))))
749 (defun yas/current-snippet-overlay (&optional point)
750 "Get the most proper overlay which is belongs to a snippet."
751 (let ((point (or point (point)))
752 (snippet-overlay nil))
753 (dolist (overlay (overlays-at point))
754 (when (overlay-get overlay 'yas/snippet)
755 (if (null snippet-overlay)
756 (setq snippet-overlay overlay)
757 (when (> (yas/snippet-id (overlay-get overlay 'yas/snippet))
758 (yas/snippet-id (overlay-get snippet-overlay 'yas/snippet)))
759 (setq snippet-overlay overlay)))))
762 (defun yas/snippet-of-current-keymap (&optional point)
763 "Get the snippet holding the snippet keymap under POINT."
764 (let ((point (or point (point)))
767 (dolist (overlay (overlays-at point))
768 (setq snippet (overlay-get overlay 'yas/snippet-reference))
770 (if (null keymap-snippet)
771 (setq keymap-snippet snippet)
772 (when (> (yas/snippet-id snippet)
773 (yas/snippet-id keymap-snippet))
774 (setq keymap-snippet snippet)))))
777 (defun yas/current-overlay-for-navigation ()
778 "Get current overlay for navigation. Might be overlay at current or previous point."
779 (let ((overlay1 (yas/current-snippet-overlay))
782 (yas/current-snippet-overlay (- (point) 1)))))
785 (if (or (null overlay2)
786 (eq (overlay-get overlay1 'yas/snippet)
787 (overlay-get overlay2 'yas/snippet)))
789 (if (> (yas/snippet-id (overlay-get overlay2 'yas/snippet))
790 (yas/snippet-id (overlay-get overlay1 'yas/snippet)))
794 (defun yas/navigate-group (group next?)
795 "Go to next of previous field group. Exit snippet if none."
796 (let ((target (if next?
797 (yas/group-next group)
798 (yas/group-prev group))))
800 (goto-char (overlay-start
802 (yas/group-primary-field target))))
803 (yas/exit-snippet (yas/group-snippet group)))))
805 (defun yas/parse-template ()
806 "Parse the template in the current buffer.
807 If the buffer contains a line of \"# --\" then the contents
808 above this line are ignored. Variables can be set above this
809 line through the syntax:
813 Here's a list of currently recognized variables:
819 #name: #include \"...\"
822 (goto-char (point-min))
823 (let (template name bound condition)
824 (if (re-search-forward "^# --\n" nil t)
825 (progn (setq template
826 (buffer-substring-no-properties (point)
829 (goto-char (point-min))
830 (while (re-search-forward "^#\\([^ ]+\\) *: *\\(.*\\)$" bound t)
831 (when (string= "name" (match-string-no-properties 1))
832 (setq name (match-string-no-properties 2)))
833 (when (string= "condition" (match-string-no-properties 1))
834 (setq condition (read (match-string-no-properties 2))))))
836 (buffer-substring-no-properties (point-min) (point-max))))
837 (list template name condition)))
839 (defun yas/directory-files (directory file?)
840 "Return directory files or subdirectories in full path."
841 (remove-if (lambda (file)
842 (or (string-match "^\\."
843 (file-name-nondirectory file))
845 (file-directory-p file)
846 (not (file-directory-p file)))))
847 (directory-files directory t)))
849 (defun yas/make-menu-binding (template)
850 (lexical-let ((template template))
853 (yas/expand-snippet (point)
857 (defun yas/modify-alist (alist key value)
858 "Modify ALIST to map KEY to VALUE. return the new alist."
859 (let ((pair (assoc key alist)))
861 (cons (cons key value)
866 (defun yas/fake-keymap-for-popup (templates)
867 "Create a fake keymap for popup menu usage."
869 (mapcar (lambda (pair)
870 (let* ((template (cdr pair))
871 (name (yas/template-name template))
872 (content (yas/template-content template)))
873 (list content 'menu-item name t)))
876 (defun yas/point-to-coord (&optional point)
877 "Get the xoffset/yoffset information of POINT.
878 If POINT is not given, default is to current point.
879 If `posn-at-point' is not available (like in Emacs 21.3),
880 t is returned simply."
881 (if (fboundp 'posn-at-point)
882 (let ((x-y (posn-x-y (posn-at-point (or point (point))))))
883 (list (list (+ (car x-y) 10)
888 (defun yas/x-popup-menu-for-template (templates)
889 "Show a popup menu listing templates to let the user select one."
890 (car (x-popup-menu (yas/point-to-coord)
891 (yas/fake-keymap-for-popup templates))))
892 (defun yas/text-popup-for-template (templates)
893 "Can't display popup menu in text mode. Just select the first one."
894 (yas/template-content (cdar templates)))
895 (defun yas/dropdown-list-popup-for-template (templates)
896 "Use dropdown-list.el to popup for templates. Better than the
897 default \"select first\" behavior of `yas/text-popup-for-template'.
898 You can also use this in window-system.
900 NOTE: You need to download and install dropdown-list.el to use this."
901 (if (fboundp 'dropdown-list)
902 (let ((n (dropdown-list (mapcar (lambda (i)
907 (yas/template-content
908 (cdr (nth n templates)))
910 (error "Please download and install dropdown-list.el to use this")))
912 (defun yas/popup-for-template (templates)
914 (funcall yas/window-system-popup-function templates)
915 (funcall yas/text-popup-function templates)))
917 (defun yas/load-directory-1 (directory &optional parent)
918 "Really do the job of loading snippets from a directory
920 (let ((mode-sym (intern (file-name-nondirectory directory)))
923 (dolist (file (yas/directory-files directory t))
924 (when (file-readable-p file)
925 (insert-file-contents file nil nil nil t)
926 (push (cons (file-name-nondirectory file)
927 (yas/parse-template))
929 (yas/define-snippets mode-sym
932 (dolist (subdir (yas/directory-files directory nil))
933 (yas/load-directory-1 subdir mode-sym))))
935 (defun yas/quote-string (string)
936 "Escape and quote STRING.
937 foo\"bar\\! -> \"foo\\\"bar\\\\!\""
939 (replace-regexp-in-string "[\\\"]"
945 (defun yas/compile-bundle
946 (&optional yasnippet yasnippet-bundle snippet-roots code)
947 "Compile snippets in SNIPPET-ROOTS to a single bundle file.
948 SNIPPET-ROOTS is a list of root directories that contains the snippets
949 definition. YASNIPPET is the yasnippet.el file path. YASNIPPET-BUNDLE
950 is the output file of the compile result. CODE is the code you would
951 like to used to initialize yasnippet. Here's the default value for
954 (yas/compile-bundle \"yasnippet.el\"
955 \"./yasnippet-bundle.el\"
957 \"(yas/initialize)\")"
958 (when (null yasnippet)
959 (setq yasnippet "yasnippet.el"))
960 (when (null yasnippet-bundle)
961 (setq yasnippet-bundle "./yasnippet-bundle.el"))
962 (when (null snippet-roots)
963 (setq snippet-roots '("snippets")))
965 (setq code "(yas/initialize)"))
967 (let ((dirs (or (and (listp snippet-roots) snippet-roots)
968 (list snippet-roots)))
971 (setq bundle-buffer (current-buffer))
972 (insert-file-contents yasnippet)
973 (goto-char (point-max))
974 (insert ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
975 (insert ";;;; Auto-generated code ;;;;\n")
976 (insert ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n")
978 (flet ((yas/define-snippets
979 (mode snippets &optional parent)
980 (with-current-buffer bundle-buffer
981 (insert ";;; snippets for " (symbol-name mode) "\n")
982 (insert "(yas/define-snippets '" (symbol-name mode) "\n")
984 (dolist (snippet snippets)
986 (yas/quote-string (car snippet))
988 (yas/quote-string (cadr snippet))
991 (yas/quote-string (caddr snippet))
995 (format "'%s" (nth 3 snippet))
1000 (concat "'" (symbol-name parent))
1004 (dolist (subdir (yas/directory-files dir nil))
1005 (yas/load-directory-1 subdir nil))))
1006 (insert "(provide '"
1007 (file-name-nondirectory
1008 (file-name-sans-extension
1011 (setq buffer-file-name yasnippet-bundle)
1014 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1015 ;; User level functions
1016 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1019 (message (concat "yasnippet (version "
1021 ") -- pluskid <pluskid@gmail.com>")))
1022 (defun yas/reload-all ()
1023 "Reload all snippets."
1025 (if yas/root-directory
1026 (if (listp yas/root-directory)
1027 (dolist (directory yas/root-directory)
1028 (yas/load-directory directory))
1029 (yas/load-directory yas/root-directory))
1030 (call-interactively 'yas/load-directory))
1033 (defun yas/load-directory (directory)
1034 "Load snippet definition from a directory hierarchy.
1035 Below the top-level directory, each directory is a mode
1036 name. And under each subdirectory, each file is a definition
1037 of a snippet. The file name is the trigger key and the
1038 content of the file is the template."
1039 (interactive "DSelect the root directory: ")
1040 (when (and (interactive-p)
1041 (file-directory-p directory))
1042 (add-to-list 'yas/root-directory directory))
1043 (dolist (dir (yas/directory-files directory nil))
1044 (yas/load-directory-1 dir))
1045 (when (interactive-p)
1048 (defun yas/initialize ()
1049 "Do necessary initialization."
1050 (add-hook 'after-change-major-mode-hook
1052 (dolist (hook yas/extra-mode-hooks)
1054 'yas/minor-mode-on))
1055 (add-hook 'yas/minor-mode-on-hook
1056 'yas/ensure-minor-mode-priority)
1059 (lookup-key global-map [menu-bar])
1061 (cons "YASnippet" yas/menu-keymap)
1064 (defun yas/define-snippets (mode snippets &optional parent-mode)
1065 "Define snippets for MODE. SNIPPETS is a list of
1066 snippet definition, of the following form:
1068 (KEY TEMPLATE NAME CONDITION)
1070 or the NAME and CONDITION may be omitted. The optional 3rd
1071 parameter can be used to specify the parent mode of MODE. That
1072 is, when looking a snippet in MODE failed, it can refer to its
1073 parent mode. The PARENT-MODE may not need to be a real mode."
1074 (let ((snippet-table (yas/snippet-table mode))
1075 (parent-table (if parent-mode
1076 (yas/snippet-table parent-mode)
1078 (keymap (if yas/use-menu
1079 (yas/menu-keymap-for-mode mode)
1082 (setf (yas/snippet-table-parent snippet-table)
1085 (define-key keymap (vector 'parent-mode)
1086 `(menu-item "parent mode"
1087 ,(yas/menu-keymap-for-mode parent-mode)))))
1088 (when (and yas/use-menu
1089 (yas/real-mode? mode))
1090 (define-key yas/menu-keymap (vector mode)
1091 `(menu-item ,(symbol-name mode) ,keymap)))
1092 (dolist (snippet snippets)
1093 (let* ((full-key (car snippet))
1094 (key (file-name-sans-extension full-key))
1095 (name (caddr snippet))
1096 (condition (nth 3 snippet))
1097 (template (yas/make-template (cadr snippet)
1100 (yas/snippet-table-store snippet-table
1105 (define-key keymap (vector (make-symbol full-key))
1106 `(menu-item ,(yas/template-name template)
1107 ,(yas/make-menu-binding (yas/template-content template))
1108 :keys ,(concat key yas/trigger-symbol))))))))
1110 (defun yas/set-mode-parent (mode parent)
1111 "Set parent mode of MODE to PARENT."
1112 (setf (yas/snippet-table-parent
1113 (yas/snippet-table mode))
1114 (yas/snippet-table parent))
1116 (define-key (yas/menu-keymap-for-mode mode) (vector 'parent-mode)
1117 `(menu-item "parent mode"
1118 ,(yas/menu-keymap-for-mode parent)))))
1120 (defun yas/define (mode key template &optional name condition)
1121 "Define a snippet. Expanding KEY into TEMPLATE.
1122 NAME is a description to this template. Also update
1123 the menu if `yas/use-menu' is `t'. CONDITION is the
1124 condition attached to this snippet. If you attach a
1125 condition to a snippet, then it will only be expanded
1126 when the condition evaluated to non-nil."
1127 (yas/define-snippets mode
1128 (list (list key template name condition))))
1131 (defun yas/hippie-try-expand (first-time?)
1132 "Integrate with hippie expand. Just put this function in
1133 `hippie-expand-try-functions-list'."
1134 (if (not first-time?)
1135 (let ((yas/fallback-behavior 'return-nil))
1137 (when (and (null (car buffer-undo-list))
1139 (car (cadr buffer-undo-list)))
1140 (eq 'yas/undo-expand-snippet
1141 (cadr (cadr buffer-undo-list))))
1145 (defun yas/expand ()
1148 (let ((local-condition (yas/template-condition-predicate
1149 yas/buffer-local-condition)))
1151 (let ((yas/require-template-condition
1152 (if (and (consp local-condition)
1153 (eq 'require-snippet-condition (car local-condition))
1154 (symbolp (cdr local-condition)))
1155 (cdr local-condition)
1157 (multiple-value-bind (templates start end) (yas/current-key)
1159 (let ((template (if (null (cdr templates)) ; only 1 template
1160 (yas/template-content (cdar templates))
1161 (yas/popup-for-template templates))))
1163 (progn (yas/expand-snippet start end template)
1164 'expanded) ; expanded successfully
1165 'interruptted)) ; interrupted by user
1166 (if (eq yas/fallback-behavior 'return-nil)
1168 (let* ((yas/minor-mode nil)
1169 (command (key-binding yas/trigger-key)))
1170 (when (commandp command)
1171 (call-interactively command))))))))))
1173 (defun yas/next-field-group ()
1174 "Navigate to next field group. If there's none, exit the snippet."
1176 (let ((overlay (yas/current-overlay-for-navigation)))
1178 (yas/navigate-group (overlay-get overlay 'yas/group) t)
1179 (let ((snippet (yas/snippet-of-current-keymap))
1182 (do* ((groups (yas/snippet-groups snippet) (cdr groups))
1183 (group (car groups) (car groups)))
1187 (let* ((overlay (yas/snippet-overlay snippet))
1188 (keymap (overlay-get overlay 'keymap))
1190 (overlay-put overlay 'keymap nil)
1191 (overlay-put overlay 'yas/snippet-reference nil)
1192 (setq command (key-binding yas/next-field-key))
1193 (when (commandp command)
1194 (call-interactively command))
1195 (overlay-put overlay 'keymap keymap)
1196 (overlay-put overlay 'yas/snippet-reference snippet))))
1200 (yas/group-primary-field group))))
1202 (yas/navigate-group group t))))))))
1204 (defun yas/prev-field-group ()
1205 "Navigate to prev field group. If there's none, exit the snippet."
1207 (let ((overlay (yas/current-overlay-for-navigation)))
1209 (yas/navigate-group (overlay-get overlay 'yas/group) nil)
1210 (let ((snippet (yas/snippet-of-current-keymap))
1213 (do* ((groups (yas/snippet-groups snippet) (cdr groups))
1214 (group (car groups) (car groups)))
1217 (unless done (message "Not in a snippet field.")))
1221 (yas/group-primary-field group))))
1223 (yas/navigate-group group nil)))
1224 (message "Not in a snippet field."))))))
1226 (defun yas/exit-snippet (snippet)
1227 "Goto exit-marker of SNIPPET and delete the snippet."
1229 (let ((overlay (yas/snippet-overlay snippet)))
1230 (let ((yas/snippet-beg (overlay-start overlay))
1231 (yas/snippet-end (overlay-end overlay)))
1232 (goto-char (yas/snippet-exit-marker snippet))
1233 (delete-overlay overlay)
1234 (dolist (group (yas/snippet-groups snippet))
1235 (dolist (field (yas/group-fields group))
1236 (delete-overlay (yas/field-overlay field))))
1238 (run-hooks 'yas/after-exit-snippet-hook))))
1240 (provide 'yasnippet)
1242 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1243 ;; Monkey patching for other functions that's causing
1244 ;; problems to yasnippet. For details on why I patch
1245 ;; those functions, refer to
1246 ;; http://code.google.com/p/yasnippet/wiki/MonkeyPatching
1247 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1248 (defadvice c-neutralize-syntax-in-CPP
1249 (around yas-mp/c-neutralize-syntax-in-CPP activate)
1250 "Adviced `c-neutralize-syntax-in-CPP' to properly
1251 handle the end-of-buffer error fired in it by calling
1252 `forward-char' at the end of buffer."
1255 (error (message (error-message-string err)))))
1258 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1259 ;; Contents of dropdown-list.el
1261 ;; dropdown-list.el is used by yasnippet to select multiple
1262 ;; candidate snippets.
1264 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1265 ;;; dropdown-list.el --- Drop-down menu interface
1267 ;; Filename: dropdown-list.el
1268 ;; Description: Drop-down menu interface
1269 ;; Author: Jaeyoun Chung [jay.chung@gmail.com]
1271 ;; Copyright (C) 2008 Jaeyoun Chung
1272 ;; Created: Sun Mar 16 11:20:45 2008 (Pacific Daylight Time)
1274 ;; Last-Updated: Sun Mar 16 12:19:49 2008 (Pacific Daylight Time)
1277 ;; URL: http://www.emacswiki.org/cgi-bin/wiki/dropdown-list.el
1278 ;; Keywords: convenience menu
1279 ;; Compatibility: GNU Emacs 21.x, GNU Emacs 22.x
1281 ;; Features that might be required by this library:
1285 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1289 ;; According to Jaeyoun Chung, "overlay code stolen from company-mode.el."
1291 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1295 ;; 2008/03/16 dadams
1296 ;; Clean-up - e.g. use char-to-string for control chars removed by email posting.
1297 ;; Moved example usage code (define-key*, command-selector) inside the library.
1298 ;; Require cl.el at byte-compile time.
1299 ;; Added GPL statement.
1300 ;; 2008/01/06 Jaeyoun Chung
1301 ;; Posted to gnu-emacs-sources@gnu.org at 9:10 p.m.
1303 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1305 ;; This program is free software; you can redistribute it and/or
1306 ;; modify it under the terms of the GNU General Public License as
1307 ;; published by the Free Software Foundation; either version 3, or
1308 ;; (at your option) any later version.
1310 ;; This program is distributed in the hope that it will be useful,
1311 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
1312 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1313 ;; General Public License for more details.
1315 ;; You should have received a copy of the GNU General Public License
1316 ;; along with this program; see the file COPYING. If not, write to
1317 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
1318 ;; Floor, Boston, MA 02110-1301, USA.
1320 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1324 (eval-when-compile (require 'cl)) ;; decf, fourth, incf, loop, mapcar*
1326 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1328 (defface dropdown-list-face
1329 '((t :inherit default :background "lightyellow" :foreground "black"))
1330 "*Bla." :group 'dropdown-list)
1332 (defface dropdown-list-selection-face
1333 '((t :inherit dropdown-list :background "purple"))
1334 "*Bla." :group 'dropdown-list)
1336 (defvar dropdown-list-overlays nil)
1338 (defun dropdown-list-hide ()
1339 (while dropdown-list-overlays
1340 (delete-overlay (pop dropdown-list-overlays))))
1342 (defun dropdown-list-put-overlay (beg end &optional prop value prop2 value2)
1343 (let ((ov (make-overlay beg end)))
1344 (overlay-put ov 'window t)
1346 (overlay-put ov prop value)
1347 (when prop2 (overlay-put ov prop2 value2)))
1350 (defun dropdown-list-line (start replacement &optional no-insert)
1351 ;; start might be in the middle of a tab, which means we need to hide the
1352 ;; tab and add spaces
1353 (let ((end (+ start (length replacement)))
1355 before-string after-string)
1356 (goto-char (point-at-eol))
1357 (if (< (current-column) start)
1358 (progn (setq before-string (make-string (- start (current-column)) ? ))
1359 (setq beg-point (point)))
1360 (goto-char (point-at-bol)) ;; Emacs bug, move-to-column is wrong otherwise
1361 (move-to-column start)
1362 (setq beg-point (point))
1363 (when (> (current-column) start)
1364 (goto-char (1- (point)))
1365 (setq beg-point (point))
1366 (setq before-string (make-string (- start (current-column)) ? ))))
1367 (move-to-column end)
1368 (setq end-point (point))
1369 (let ((end-offset (- (current-column) end)))
1370 (when (> end-offset 0) (setq after-string (make-string end-offset ?b))))
1372 ;; prevent inheriting of faces
1373 (setq before-string (when before-string (propertize before-string 'face 'default)))
1374 (setq after-string (when after-string (propertize after-string 'face 'default))))
1375 (let ((string (concat before-string replacement after-string)))
1378 (push (dropdown-list-put-overlay beg-point end-point 'invisible t
1379 'after-string string)
1380 dropdown-list-overlays)))))
1382 (defun dropdown-list-start-column (display-width)
1383 (let ((column (mod (current-column) (window-width)))
1384 (width (window-width)))
1385 (cond ((<= (+ column display-width) width) column)
1386 ((> column display-width) (- column display-width))
1387 ((>= width display-width) (- width display-width))
1390 (defun dropdown-list-move-to-start-line (candidate-count)
1391 (decf candidate-count)
1392 (let ((above-line-count (save-excursion (- (vertical-motion (- candidate-count)))))
1393 (below-line-count (save-excursion (vertical-motion candidate-count))))
1394 (cond ((= below-line-count candidate-count)
1396 ((= above-line-count candidate-count)
1397 (vertical-motion (- candidate-count))
1399 ((>= (+ below-line-count above-line-count) candidate-count)
1400 (vertical-motion (- (- candidate-count below-line-count)))
1404 (defun dropdown-list-at-point (candidates &optional selidx)
1405 (dropdown-list-hide)
1406 (let* ((lengths (mapcar #'length candidates))
1407 (max-length (apply #'max lengths))
1408 (start (dropdown-list-start-column (+ max-length 3)))
1410 (candidates (mapcar* (lambda (candidate length)
1411 (let ((diff (- max-length length)))
1413 (concat (if (> diff 0)
1414 (concat candidate (make-string diff ? ))
1415 (substring candidate 0 max-length))
1416 (format "%3d" (+ 2 i)))
1417 'face (if (eql (incf i) selidx)
1418 'dropdown-list-selection-face
1419 'dropdown-list-face))))
1424 (dropdown-list-move-to-start-line (length candidates))
1425 (loop initially (vertical-motion 0)
1426 for candidate in candidates
1427 do (dropdown-list-line (+ (current-column) start) candidate)
1428 while (/= (vertical-motion 1) 0)
1429 finally return t)))))
1431 (defun dropdown-list (candidates)
1434 (save-window-excursion
1436 (let ((candidate-count (length candidates))
1439 (unless (dropdown-list-at-point candidates selidx)
1440 (switch-to-buffer (setq temp-buffer (get-buffer-create "*selection*"))
1442 (delete-other-windows)
1443 (delete-region (point-min) (point-max))
1444 (insert (make-string (length candidates) ?\n))
1445 (goto-char (point-min))
1446 (dropdown-list-at-point candidates selidx))
1447 (setq key (read-key-sequence ""))
1448 (cond ((and (stringp key)
1449 (>= (aref key 0) ?1)
1450 (<= (aref key 0) (+ ?0 (min 9 candidate-count))))
1451 (setq selection (- (aref key 0) ?1)
1453 ((member key `(,(char-to-string ?\C-p) [up]))
1454 (setq selidx (mod (+ candidate-count (1- (or selidx 0)))
1456 ((member key `(,(char-to-string ?\C-n) [down]))
1457 (setq selidx (mod (1+ (or selidx -1)) candidate-count)))
1458 ((member key `(,(char-to-string ?\f))))
1459 ((member key `(,(char-to-string ?\r) [return]))
1460 (setq selection selidx
1462 (t (setq done t)))))
1463 (dropdown-list-hide)
1464 (and temp-buffer (kill-buffer temp-buffer)))
1466 ;; (message "your selection => %d: %s" selection (nth selection candidates))
1470 (defun define-key* (keymap key command)
1471 "Add COMMAND to the multiple-command binding of KEY in KEYMAP.
1472 Use multiple times to bind different COMMANDs to the same KEY."
1473 (define-key keymap key (combine-command command (lookup-key keymap key))))
1475 (defun combine-command (command defs)
1476 "$$$$$ FIXME - no doc string"
1477 (cond ((null defs) command)
1479 (eq 'lambda (car defs))
1481 (listp (fourth defs))
1482 (eq 'command-selector (car (fourth defs))))
1483 (unless (member `',command (cdr (fourth defs)))
1484 (setcdr (fourth defs) (nconc (cdr (fourth defs)) `(',command))))
1487 `(lambda () (interactive) (command-selector ',defs ',command)))))
1489 (defvar command-selector-last-command nil "$$$$$ FIXME - no doc string")
1491 (defun command-selector (&rest candidates)
1492 "$$$$$ FIXME - no doc string"
1493 (if (and (eq last-command this-command) command-selector-last-command)
1494 (call-interactively command-selector-last-command)
1495 (let* ((candidate-strings
1496 (mapcar (lambda (candidate)
1497 (format "%s" (if (symbolp candidate)
1499 (let ((s (format "%s" candidate)))
1500 (if (>= (length s) 7)
1501 (concat (substring s 0 7) "...")
1504 (selection (dropdown-list candidate-strings)))
1506 (let ((cmd (nth selection candidates)))
1507 (call-interactively cmd)
1508 (setq command-selector-last-command cmd))))))
1510 ;;;;;;;;;;;;;;;;;;;;
1512 (provide 'dropdown-list)
1514 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1515 ;;; dropdown-list.el ends here