mirror of
https://github.com/joaotavora/yasnippet.git
synced 2025-10-13 21:13:04 +00:00
more document for selecting a snippet
This commit is contained in:
parent
11a716c3b6
commit
62b10948e7
@ -378,6 +378,186 @@ comment to be evaluated to the symbol ``force-in-comment``. Then it
|
|||||||
can be expanded as you expected, while other snippets like ``if``
|
can be expanded as you expected, while other snippets like ``if``
|
||||||
still can't expanded in comment.
|
still can't expanded in comment.
|
||||||
|
|
||||||
|
Multiple snippet with the same key
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
There can be multiple snippet bind to the same key. If you define a
|
||||||
|
snippet with a key that is already used, you'll overwrite the original
|
||||||
|
snippet definition. However, you can add a different *postfix* to the
|
||||||
|
key.
|
||||||
|
|
||||||
|
In general, the *extension* (consider a file name) is *ignored* when
|
||||||
|
defining a snippet. So ``def``, ``def.1`` and ``def.mine`` will all be
|
||||||
|
valid candidates when the key is ``def``.
|
||||||
|
|
||||||
|
When there are multiple candidates, YASnippet will let you select
|
||||||
|
one. The UI for selecting multiple candidate can be
|
||||||
|
customized. There're two variable related:
|
||||||
|
|
||||||
|
* ``yas/window-system-popup-function``: the function used when you
|
||||||
|
have a window system.
|
||||||
|
* ``yas/text-popup-function``: the function used when you don't have a
|
||||||
|
window system, i.e. when you are working in a terminal.
|
||||||
|
|
||||||
|
Currently there're three solution come with YASnippet.
|
||||||
|
|
||||||
|
Popup Menu
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
The function ``yas/x-popup-menu-for-template`` can be used to show a
|
||||||
|
popup menu for you to select. This menu will be part of you native
|
||||||
|
window system widget, which means:
|
||||||
|
|
||||||
|
* It usually looks beautiful. E.g. when you compile Emacs with gtk
|
||||||
|
support, this menu will be rendered with your gtk theme.
|
||||||
|
* Emacs have little control over it. E.g. you can't use ``C-n``,
|
||||||
|
``C-p`` to navigate.
|
||||||
|
* This function can't be used when in a terminal.
|
||||||
|
|
||||||
|
Just select the first one
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This one is originally used in terminal mode. It doesn't let you to
|
||||||
|
choose anything, it just select the first one on behalf of you. So I
|
||||||
|
bet you never want to use this. :p
|
||||||
|
|
||||||
|
Use a dropdown-menu.el
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Originally, only the above two function is available in
|
||||||
|
YASnippet. They are difficult to use -- especially in a
|
||||||
|
terminal. Until later Jaeyoun Chung show me his
|
||||||
|
``dropdown-menu.el``, I say wow! It's wonderful!
|
||||||
|
|
||||||
|
* It works in both window system and terminal.
|
||||||
|
* It is customizable, you can use ``C-n``, ``C-p`` to navigate, ``q``
|
||||||
|
to quite and even press ``6`` as a shortcut to select the 6th
|
||||||
|
candidate.
|
||||||
|
|
||||||
|
So I added ``yas/dropdown-list-popup-for-template`` to support
|
||||||
|
``dropdown-list.el``. And upload ``dropdown-list.el`` to YASnippet
|
||||||
|
hompage for an optional download (since Jaeyoun didn't provide a URL).
|
||||||
|
|
||||||
|
Then finally, in 0.4.0, I included a copy of the content of
|
||||||
|
``dropdown-list.el`` [1]_ in ``yasnippet.el`` and made it the default
|
||||||
|
way for selecting multiple candidates.
|
||||||
|
|
||||||
|
However, the original functions are still there, you can still use this
|
||||||
|
|
||||||
|
.. sourcecode:: common-lisp
|
||||||
|
|
||||||
|
(setq yas/window-system-popup-function
|
||||||
|
'yas/x-popup-menu-for-template)
|
||||||
|
|
||||||
|
if you prefer a *modern* UI. :)
|
||||||
|
|
||||||
|
The Trigger Key
|
||||||
|
---------------
|
||||||
|
|
||||||
|
YASnippet is implemented as a minor-mode (``yas/minor-mode``). The
|
||||||
|
trigger key ``yas/trigger-key`` is defined in ``yas/minor-mode-map``
|
||||||
|
to call ``yas/expand`` to try to expand a snippet.
|
||||||
|
|
||||||
|
The Minor Mode
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
When ``yas/minor-mode`` is enabled, the trigger key will take
|
||||||
|
effect. The default key is ``(kbd "TAB")``, however, you can freely
|
||||||
|
set it to some other key. By default, YASnippet add a hook to
|
||||||
|
``after-change-major-mode-hook`` to enable ``yas/minor-mode`` [2]_ in
|
||||||
|
every buffer. This works fine for most modes, however, some mode
|
||||||
|
doesn't follow the Emacs convention and doens't call this hook. You
|
||||||
|
can either explicitly hook for those mode or just add it to
|
||||||
|
``yas/extra-mode-hooks`` to let YASnippet do it for you:
|
||||||
|
|
||||||
|
.. sourcecode:: common-lisp
|
||||||
|
|
||||||
|
(require 'yasnippet)
|
||||||
|
(add-to-list 'yas/extra-mode-hooks
|
||||||
|
'ruby-mode-hook)
|
||||||
|
(yas/initialize)
|
||||||
|
|
||||||
|
Note that **should** be put after ``(require 'yasnippet)`` and before
|
||||||
|
``(yas/initialize)``. Further more, you may report it to me, I'll add
|
||||||
|
that to the default value.
|
||||||
|
|
||||||
|
The Fallback
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If ``yas/expand`` failed to find any suitable snippet to expand, it
|
||||||
|
will disable the minor mode temporarily and find if there's any other
|
||||||
|
command bind the ``yas/trigger-key``. If found, the command will be
|
||||||
|
called. Usually this works very well -- when there's a snippet, expand
|
||||||
|
it, otherwise, call whatever command originally bind to the trigger
|
||||||
|
key.
|
||||||
|
|
||||||
|
Other way to select a snippet
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
When you use the trigger key (so ``yas/expand``) to expand a snippet,
|
||||||
|
the key for the snippet is deleted before the template for the snippet
|
||||||
|
is inserted.
|
||||||
|
|
||||||
|
However, there're other ways to insert a snippet.
|
||||||
|
|
||||||
|
The Menu
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
YASnippet will setup a menu just after the *Buffers* Menu in the
|
||||||
|
menubar. The snippets for all *real* modes are listed there under the
|
||||||
|
menu. You can select a snippet from the menu to expand it. Since you
|
||||||
|
select manually from the menu, you can expand any snippet. For
|
||||||
|
example, you can expand a snippet defined for ``python-mode`` in a
|
||||||
|
``c-mode`` buffer by selecting it from the menu:
|
||||||
|
|
||||||
|
* Condition system is ignored since you select to expand it
|
||||||
|
explicitly.
|
||||||
|
* There will be no muliple candidates since they are listed in the
|
||||||
|
menu as different items.
|
||||||
|
|
||||||
|
This can be convenient sometimes. However, if you don't like the
|
||||||
|
menubar of Emacs and never use it. You can tell YASnippet don't boring
|
||||||
|
to build a menu by setting ``yas/use-menu`` to nil.
|
||||||
|
|
||||||
|
Another thing to note is that only *real* modes are listed under the
|
||||||
|
menu. As you know, common snippets can be shared by making up a
|
||||||
|
*virtual* parent mode. It's too bad if the menu is floored by those
|
||||||
|
*virtual* modes. So YASnippet only show menus for those *real*
|
||||||
|
modes. But the snippets fo the *virtual* modes can still be accessed
|
||||||
|
through the ``parent`` submenu of some *real* mode.
|
||||||
|
|
||||||
|
YASnippet use a simple way to check whether a mode is *real* or
|
||||||
|
*virtual*: ``(fboundp mode)``. For example, the symbol ``c-mode`` is
|
||||||
|
bound to a function while ``cc-mode`` is not. But this is not enough,
|
||||||
|
some modes aren't part of Emacs, and maybe when initializing
|
||||||
|
YASnippet, those modes haven't been initialized. So YASnippet also
|
||||||
|
maintain a list of known modes (``yas/known-modes``). You can add item
|
||||||
|
to that list if you need.
|
||||||
|
|
||||||
|
Expanding From Elisp Code
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Sometimes you might want to expand a snippet directly by calling a
|
||||||
|
functin from elisp code. You should call ``yas/expand-snippet``
|
||||||
|
instead of ``yas/expand`` in this case.
|
||||||
|
|
||||||
|
As with expanding from the menubar, condition system and multiple
|
||||||
|
candidates won't exists here. In fact, expanding from menubar has the
|
||||||
|
same effect of evaluating the follow code:
|
||||||
|
|
||||||
|
.. sourcecode:: common-lisp
|
||||||
|
|
||||||
|
(yas/expand-snippet (point) (point) template)
|
||||||
|
|
||||||
|
Where ``template`` is the template of a snippet. It is never required
|
||||||
|
to belong to any snippet -- you can even make up it on the fly. The
|
||||||
|
1st and 2nd parameter defines the region to be deleted after YASnippet
|
||||||
|
inserted the template. It is used by ``yas/expand`` to indicate the
|
||||||
|
region of the key. There's usually no need to delete any region when
|
||||||
|
we are expanding a snippet from elisp code, so passing two ``(point)``
|
||||||
|
is fine. Note only ``(point)`` will be fine because the 1st parameter
|
||||||
|
also indicate where to insert and expand the ``template``.
|
||||||
|
|
||||||
The Syntax of the Template
|
The Syntax of the Template
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
@ -391,3 +571,6 @@ The Syntax of the Template
|
|||||||
* ``condition``: The condition of the snippet. This is a piece of
|
* ``condition``: The condition of the snippet. This is a piece of
|
||||||
elisp code. If a snippet has a condition, then it will only be
|
elisp code. If a snippet has a condition, then it will only be
|
||||||
expanded when the condition code evaluate to some non-nil value.
|
expanded when the condition code evaluate to some non-nil value.
|
||||||
|
|
||||||
|
.. [1] With some minor change, mainly for fixing some trivial bugs.
|
||||||
|
.. [2] This is done when you call ``yas/initialize``.
|
||||||
|
185
yasnippet.el
185
yasnippet.el
@ -94,11 +94,11 @@ mode will be listed under the menu \"yasnippet\".")
|
|||||||
(t (:background "gray22")))
|
(t (:background "gray22")))
|
||||||
"The face used to highlight mirror fields of a snippet.")
|
"The face used to highlight mirror fields of a snippet.")
|
||||||
|
|
||||||
(defvar yas/window-system-popup-function #'yas/x-popup-menu-for-template
|
(defvar yas/window-system-popup-function #'yas/dropdown-list-popup-for-template
|
||||||
"When there's multiple candidate for a snippet key. This function
|
"When there's multiple candidate for a snippet key. This function
|
||||||
is called to let user select one of them. `yas/text-popup-function'
|
is called to let user select one of them. `yas/text-popup-function'
|
||||||
is used instead when not in a window system.")
|
is used instead when not in a window system.")
|
||||||
(defvar yas/text-popup-function #'yas/text-popup-for-template
|
(defvar yas/text-popup-function #'yas/dropdown-list-popup-for-template
|
||||||
"When there's multiple candidate for a snippet key. If not in a
|
"When there's multiple candidate for a snippet key. If not in a
|
||||||
window system, this function is called to let user select one of
|
window system, this function is called to let user select one of
|
||||||
them. `yas/window-system-popup-function' is used instead when in
|
them. `yas/window-system-popup-function' is used instead when in
|
||||||
@ -1181,3 +1181,184 @@ handle the end-of-buffer error fired in it by calling
|
|||||||
(condition-case err
|
(condition-case err
|
||||||
ad-do-it
|
ad-do-it
|
||||||
(error (message (error-message-string err)))))
|
(error (message (error-message-string err)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Contents of dropdown-list.el
|
||||||
|
;;
|
||||||
|
;; dropdown-list.el is used by yasnippet to select multiple
|
||||||
|
;; candidate snippets.
|
||||||
|
;;
|
||||||
|
;; This is a slightly modified version of the original
|
||||||
|
;; dropdown-list.el
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
;;; dropdown-list.el --- dropdown menu interface
|
||||||
|
|
||||||
|
;; Copyright (C) 2008 Jaeyoun Chung
|
||||||
|
|
||||||
|
;; Author: jay AT kldp DOT org
|
||||||
|
;; Keywords: convenience
|
||||||
|
;;
|
||||||
|
;; overlay code stolen from company-mode.el
|
||||||
|
;;
|
||||||
|
|
||||||
|
;;; Code:
|
||||||
|
(defface dropdown-list-face
|
||||||
|
'((t :inherit default
|
||||||
|
:background "lightyellow"
|
||||||
|
:foreground "black"))
|
||||||
|
"*Bla."
|
||||||
|
:group 'dropdown-list)
|
||||||
|
|
||||||
|
(defface dropdown-list-selection-face
|
||||||
|
'((t :inherit dropdown-list
|
||||||
|
:background "purple"))
|
||||||
|
"*Bla."
|
||||||
|
:group 'dropdown-list)
|
||||||
|
|
||||||
|
(defvar dropdown-list-overlays nil)
|
||||||
|
|
||||||
|
(defun dropdown-list-hide ()
|
||||||
|
(while dropdown-list-overlays
|
||||||
|
(delete-overlay (pop dropdown-list-overlays))))
|
||||||
|
|
||||||
|
(defun dropdown-list-put-overlay (beg end &optional prop value prop2
|
||||||
|
value2)
|
||||||
|
(let ((ov (make-overlay beg end)))
|
||||||
|
(overlay-put ov 'window t)
|
||||||
|
(when prop
|
||||||
|
(overlay-put ov prop value)
|
||||||
|
(when prop2
|
||||||
|
(overlay-put ov prop2 value2)))
|
||||||
|
ov))
|
||||||
|
|
||||||
|
(defun dropdown-list-line (start replacement &optional no-insert)
|
||||||
|
;; start might be in the middle of a tab, which means we need to hide the
|
||||||
|
;; tab and add spaces
|
||||||
|
(let ((end (+ start (length replacement)))
|
||||||
|
beg-point end-point
|
||||||
|
before-string after-string)
|
||||||
|
(goto-char (point-at-eol))
|
||||||
|
(if (< (current-column) start)
|
||||||
|
(progn (setq before-string
|
||||||
|
(make-string (- start (current-column)) ? ))
|
||||||
|
(setq beg-point (point)))
|
||||||
|
(goto-char (point-at-bol)) ;; Emacs bug, move-to-column is wrong otherwise
|
||||||
|
(move-to-column start)
|
||||||
|
(setq beg-point (point))
|
||||||
|
(when (> (current-column) start)
|
||||||
|
(goto-char (1- (point)))
|
||||||
|
(setq beg-point (point))
|
||||||
|
(setq before-string (make-string (- start (current-column)) ? ))))
|
||||||
|
(move-to-column end)
|
||||||
|
(setq end-point (point))
|
||||||
|
(let ((end-offset (- (current-column) end)))
|
||||||
|
(when (> end-offset 0)
|
||||||
|
(setq after-string (make-string end-offset ?b))))
|
||||||
|
(when no-insert
|
||||||
|
;; prevent inheriting of faces
|
||||||
|
(setq before-string (when before-string
|
||||||
|
(propertize before-string 'face 'default)))
|
||||||
|
(setq after-string (when after-string
|
||||||
|
(propertize after-string 'face 'default))))
|
||||||
|
(let ((string (concat before-string
|
||||||
|
replacement
|
||||||
|
after-string)))
|
||||||
|
(if no-insert
|
||||||
|
string
|
||||||
|
(push (dropdown-list-put-overlay beg-point end-point
|
||||||
|
'invisible t
|
||||||
|
'after-string string)
|
||||||
|
dropdown-list-overlays)))))
|
||||||
|
|
||||||
|
(defun dropdown-list-start-column (display-width)
|
||||||
|
(let ((column (mod (current-column) (window-width)))
|
||||||
|
(width (window-width)))
|
||||||
|
(cond ((<= (+ column display-width) width)
|
||||||
|
column)
|
||||||
|
((> column display-width)
|
||||||
|
(- column display-width))
|
||||||
|
((>= width display-width)
|
||||||
|
(- width display-width))
|
||||||
|
(t
|
||||||
|
nil))))
|
||||||
|
|
||||||
|
(defun dropdown-list-move-to-start-line (candidate-count)
|
||||||
|
(decf candidate-count)
|
||||||
|
(let ((above-line-count (save-excursion (- (vertical-motion (-
|
||||||
|
candidate-count)))))
|
||||||
|
(below-line-count (save-excursion (vertical-motion candidate-count))))
|
||||||
|
(cond ((= below-line-count candidate-count)
|
||||||
|
t)
|
||||||
|
((= above-line-count candidate-count)
|
||||||
|
(vertical-motion (- candidate-count))
|
||||||
|
t)
|
||||||
|
((>= (+ below-line-count above-line-count) candidate-count)
|
||||||
|
(vertical-motion (- (- candidate-count below-line-count)))
|
||||||
|
t)
|
||||||
|
(t
|
||||||
|
nil))))
|
||||||
|
|
||||||
|
(defun dropdown-list-at-point (candidates &optional selidx)
|
||||||
|
(dropdown-list-hide)
|
||||||
|
(let* ((lengths (mapcar #'length candidates))
|
||||||
|
(max-length (apply #'max lengths))
|
||||||
|
(start (dropdown-list-start-column (+ max-length 3)))
|
||||||
|
(i -1)
|
||||||
|
(candidates (mapcar* (lambda (candidate length)
|
||||||
|
(let ((diff (- max-length length)))
|
||||||
|
(propertize
|
||||||
|
(concat (if (> diff 0)
|
||||||
|
(concat candidate (make-string diff ? ))
|
||||||
|
(substring candidate 0 max-length))
|
||||||
|
(format "%3d" (+ 2 i)))
|
||||||
|
'face (if (eql (incf i) selidx)
|
||||||
|
'dropdown-list-selection-face
|
||||||
|
'dropdown-list-face))))
|
||||||
|
candidates lengths)))
|
||||||
|
(save-excursion
|
||||||
|
(and start
|
||||||
|
(dropdown-list-move-to-start-line (length candidates))
|
||||||
|
(loop initially (vertical-motion 0)
|
||||||
|
for candidate in candidates
|
||||||
|
do (dropdown-list-line (+ (current-column) start) candidate)
|
||||||
|
while (/= (vertical-motion 1) 0)
|
||||||
|
finally return t)))))
|
||||||
|
|
||||||
|
(defun dropdown-list (candidates)
|
||||||
|
(let ((selection) (temp-buffer))
|
||||||
|
(save-window-excursion
|
||||||
|
(unwind-protect
|
||||||
|
(let ((candidate-count (length candidates))
|
||||||
|
done key selidx)
|
||||||
|
(while (not done)
|
||||||
|
(unless (dropdown-list-at-point candidates selidx)
|
||||||
|
(switch-to-buffer (setq temp-buffer (get-buffer-create "*selection*")) 'norecord)
|
||||||
|
(delete-other-windows)
|
||||||
|
(delete-region (point-min) (point-max))
|
||||||
|
(insert (make-string (length candidates) ?\n))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(dropdown-list-at-point candidates selidx))
|
||||||
|
(setq key (read-key-sequence ""))
|
||||||
|
(cond ((and (stringp key) (>= (aref key 0) ?1) (<= (aref key 0)
|
||||||
|
(+ ?0 (min 9 candidate-count))))
|
||||||
|
(setq selection (- (aref key 0) ?1)
|
||||||
|
done t))
|
||||||
|
((member key '("" [up]))
|
||||||
|
(setq selidx (mod (+ candidate-count (1- (or selidx 0)))
|
||||||
|
candidate-count)))
|
||||||
|
((member key '("" [down]))
|
||||||
|
(setq selidx (mod (1+ (or selidx -1)) candidate-count)))
|
||||||
|
((member key '("")))
|
||||||
|
((member key '("
|
||||||
|
" [return] ""))
|
||||||
|
(setq selection selidx
|
||||||
|
done t))
|
||||||
|
(t
|
||||||
|
(setq done t)))))
|
||||||
|
(dropdown-list-hide)
|
||||||
|
(and temp-buffer (kill-buffer temp-buffer)))
|
||||||
|
selection)))
|
||||||
|
|
||||||
|
;;; contents dropdown-list.el ends here
|
||||||
|
Loading…
x
Reference in New Issue
Block a user