more powerful condition system

This commit is contained in:
Zhang Chiyuan 2008-03-20 13:24:39 +00:00
parent 21c98d141a
commit 67dc9d7518
2 changed files with 139 additions and 17 deletions

View File

@ -261,6 +261,122 @@ customization code instead of the default ``(yas/initialize)``. For
example, you can set ``yas/trigger-key`` to ``(kbd "SPC")`` here if example, you can set ``yas/trigger-key`` to ``(kbd "SPC")`` here if
you like. you like.
yas/define
~~~~~~~~~~
The basic syntax for ``yas/define`` is
.. sourcecode:: common-lisp
(yas/define mode key template &optional name condition)
This is only a syntax sugar for
.. sourcecode:: common-lisp
(yas/define-snippets mode
(list (list key template name condition)))
The strategy to select a snippet
================================
When user press the ``yas/trigger-key``, YASnippet try to find a
proper snippet to expand. The strategy to find such a snippet is
explained here.
Finding the key
---------------
YASnippet search from current point backward trying to find the
snippet to be expanded. The default searching strategy is quite
powerful. For example, in ``c-mode``, ``"bar"``, ``"foo_bar"``,
``"#foo_bar"`` can all be recognized as a template key. Further more,
the searching is in that order. In other words, if ``"bar"`` is found
to be a key to some *valid* snippet, then ``"foo_bar"`` and
``"#foobar"`` won't be searched.
However, this strategy can also be customized easily from the
``yas/key-syntaxes`` variable. It is a list of syntax rules, the
default value is ``("w" "w_" "w_." "^ ")``. Which means search the
following thing until found one:
* a word.
* a symbol. In lisp, ``-`` and ``?`` can all be part of a symbol.
* a sequence of characters of either word, symbol or punctuation.
* a sequence of characters of non-whitespace characters.
But you'd better keep the default value unless you understand what
Emacs's syntax rule mean.
The condition system
--------------------
I write forked snippet.el to make the smart-snippet.el. I call it
*smart*-snippet because a condition can be attached to a snippet. This
is really a good idea. However, writing condition for a snippet
usually needs good elisp and Emacs knowledge, so it is strange to many
user.
Later I write YASnippet and persuade people to use it instead of
smart-snippet.el. However, some user still love smart-snippet because
it is smart. So I make YASnippet smart. Even smarter than
smart-snippet.el. :p
Consider this scenario: you are an old Emacs hacker. You like the
abbrev-way and set ``yas/trigger-key`` to ``(kbd "SPC")``. However,
you don't want ``if`` to be expanded as a snippet when you are typing
in a comment block or a string (e.g. in ``python-mode``).
It's OK, just specify the condition for ``if`` to be ``(not
(python-in-string/comment))``. But how about ``while``, ``for``,
etc. ? Writing the same condition for all the snippets is just
boring. So YASnippet introduce a buffer local variable
``yas/buffer-local-condition``. You can set this variable to ``(not
(python-in-string/comment))`` in ``python-mode-hook``. There's no way
to do this in smart-snippet.el!
Then, what if you really want some snippet even in comment? This is
also possible! But let's stop telling the story and look at the rules:
* If ``yas/buffer-local-condition`` evaluate to nil, snippet won't be
expanded.
* If it evaluate to the a cons cell where the ``car`` is the symbol
``require-snippet-condition`` and the ``cdr`` is a symbol (let's
call it ``requirement``):
* If the snippet has no condition, then it won't be expanded.
* If the snippet has a condition but evaluate to nil or error
occured during evaluation, it won't be expanded.
* If the snippet has a condition that evaluate to non-nil (let's
call it ``result``):
* If ``requirement`` is ``t``, the snippet is ready to be
expanded.
* If ``requirement`` is ``eq`` to ``result``, the snippet is ready
to be expanded.
* Otherwise the snippet won't be expanded.
* If it evaluate to other non-nil value:
* If the snippet has no condition, or has a condition that evaluate
to non-nil, it is ready to be expanded.
* Otherwise, it won't be expanded.
So set ``yas/buffer-local-condition`` like this
.. sourcecode:: common-lisp
(add-hook 'python-mode-hook
'(lambda ()
(setq yas/buffer-local-condition
'(if (python-in-string/comment)
'(require-snippet-condition . force-in-comment)
t))))
And specify the condition for a snippet that you're going to expand in
comment to be evaluated to the symbol ``force-in-comment``. Then it
can be expanded as you expected.
The Syntax of the Template The Syntax of the Template
========================== ==========================

View File

@ -124,11 +124,11 @@ proper values:
(defvar yas/buffer-local-condition t (defvar yas/buffer-local-condition t
"Condition to yasnippet local to each buffer. "Condition to yasnippet local to each buffer.
If this eval to nil, no snippet can be expanded. If this eval to nil, no snippet can be expanded. If this eval to
If this eval to 'require-snippet-condition, then a snippet can be expanded the symbol require-snippet-condition, then a snippet can be
if and only if it has a condition attached and that condition eval to non-nil. expanded if and only if it has a condition attached and that
Otherwise, if a snippet has no condition or its conditin eval to non-nil, it condition eval to non-nil. Otherwise, if a snippet has no
will be expanded. condition or its conditin eval to non-nil, it will be expanded.
Here's an example: Here's an example:
@ -319,13 +319,17 @@ have, compare through the start point of the overlay."
* If the template has no condition, it is kept. * If the template has no condition, it is kept.
* If the template's condition eval to non-nil, it is kept. * If the template's condition eval to non-nil, it is kept.
* Otherwise (eval error or eval to nil) it is filtered." * Otherwise (eval error or eval to nil) it is filtered."
(remove-if-not '(lambda (pair) (remove-if '(lambda (pair)
(let ((condition (yas/template-condition (cdr pair)))) (let ((condition (yas/template-condition (cdr pair))))
(if (null condition) (if (null condition)
(if yas/require-template-condition (if yas/require-template-condition
nil t
t) nil)
(yas/template-condition-predicate condition)))) (let ((result
(yas/template-condition-predicate condition)))
(if (eq yas/require-template-condition t)
result
(not (eq result yas/require-template-condition)))))))
templates)) templates))
(defun yas/snippet-table-fetch (table key) (defun yas/snippet-table-fetch (table key)
@ -1059,9 +1063,11 @@ when the condition evaluated to non-nil."
(let ((local-condition (yas/template-condition-predicate (let ((local-condition (yas/template-condition-predicate
yas/buffer-local-condition))) yas/buffer-local-condition)))
(if local-condition (if local-condition
(let ((yas/require-template-condition (if (eq local-condition (let ((yas/require-template-condition
'require-snippet-condition) (if (and (consp local-condition)
t (eq 'require-snippet-condition (car local-condition))
(symbolp (cdr local-condition)))
(cdr local-condition)
nil))) nil)))
(multiple-value-bind (key start end) (yas/current-key) (multiple-value-bind (key start end) (yas/current-key)
(let ((templates (yas/snippet-table-fetch (yas/current-snippet-table) (let ((templates (yas/snippet-table-fetch (yas/current-snippet-table)