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``
|
||||
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
|
||||
==========================
|
||||
|
||||
@ -391,3 +571,6 @@ The Syntax of the Template
|
||||
* ``condition``: The condition of the snippet. This is a piece of
|
||||
elisp code. If a snippet has a condition, then it will only be
|
||||
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")))
|
||||
"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
|
||||
is called to let user select one of them. `yas/text-popup-function'
|
||||
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
|
||||
window system, this function is called to let user select one of
|
||||
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
|
||||
ad-do-it
|
||||
(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