本人因为想更快速地记数学笔记,在尝试给 yasnippet 配置自动展开。我的目的是,只在使用 org-mode 时,在数学公式环境中,才对一部分设置了自动展开的 snippet 进行展开。目前实现这个目的的方法是,对 post-command-hook
添加一个 hook,在里面判断:1. 触发 hook 的命令是否是 org-self-insert-command
,从而只在 org-mode 下展开,也避免意外触发展开的问题(比如退格到光标处恰好是触发词时意外展开);2. 当前环境是否是数学环境;3. snippet 是否设置了自动展开。2 和 3 是通过配合设置 snippet 的 condition 属性为 # condition: (and (texmathp) auto)
实现的。代码如下:
(defun my-yas-try-expanding-auto-snippets ()
(when (and (boundp 'yas-minor-mode) yas-minor-mode)
(let ((yas-buffer-local-condition
'(if (eq this-command 'org-self-insert-command)
'(require-snippet-condition . auto)
nil)))
(yas-expand))))
(add-hook 'post-command-hook 'my-yas-try-expanding-auto-snippets)
这里涉及到 yasnippet 的条件展开机制,简单说就是在执行 yas-expand
时会 eval yas-buffer-local-condition
,然后根据结果通过一个规则去判断是否展开,具体见yasnippet 文档。
我的问题是,如此配置之后,我的目的虽然实现了,但出现了别的环境下意外自动展开的问题。比如,doom 自带的一些 snippet 明明没有设置 'auto
这个 condition,输入时也不处于 org-mode 下,因此这时 yas-buffer-local-condition
本应为 nil
(从而根本不应该展开任何 snippet),但只要光标到触发词的位置,就会直接自动展开。一个例子是:
# -*- mode: snippet -*-
# name: mode
# key: mode
# uuid: mode
# condition: (= (line-number-at-pos) 1)
# --
`comment-start`-*- mode: ${1:mode} -*-`comment-end`
这个 snippet 就会触发这个情况,搞得我百思不得其解,如果有人能回答我的问题的话就太感谢了
1 个赞
你要确定这到底是 Doom 配置造成的还是 yasnippet 自己的原因,在 emacs -Q 下也会自动展开吗?
虽然解决不了这个问题,但是如果为了写数学笔记,建议使用laas
谢谢提醒,一开始在 emacs -Q 下也会有问题,不过排查好久最后发现是我写出问题了哈哈
经过一番排查发现是我自己一开始的函数写挫了 检查 this-command
是否是输入命令应该在函数的一开始而不是 yas-buffer-local-condition
内部,这个似乎是因为 yas 做条件检查的时候限定 yas-buffer-local-condition
是某几种形式之一,所以把 if
一类也放进去以后求值会出错导致 yas 自己最后求出的不是预期的 nil
而是 t
,从而会把所有设置了条件的 snippet 自动展开。经过修改后无问题的代码如下:
(defun my-yas-try-expanding-auto-snippets (specific-self-insert-command)
(lambda ()
(when (and (eq this-command specific-self-insert-command) (boundp 'yas-minor-mode) yas-minor-mode)
(let ((yas-buffer-local-condition ''(require-snippet-condition . auto)))
(yas-expand)))))
(add-hook 'post-command-hook (my-yas-try-expanding-auto-snippets 'org-self-insert-command))
这个代码同时还可以针对不同的模式进行设置,避免了原来硬编码成适用 org-mode 的问题。虽然对 post-command-hook
进行 hook 感觉上可能会操作频繁,但实际使用上体验良好
比如 org-mode 下输入时调用的不是 self-insert-command
而是 org-self-insert-command
, 这时候只去判断前一个就会有问题;然后可能有别的模式也有这种情况,所以应该还是要区分一下的
1 个赞
我也是用这种方法来设置自动展开的,不过由于设置的 key 都比较冷门,到是没遇到过这种情况,经你提醒确实需要加个判断。
哈哈哈,因为我刚开始练习熟悉 snippets 的时候总是打错,退格时就发现了意外触发的问题。然后去 github 发现有人和我有一样的问题,在那个评论提供的思路帮助下解决了
我看了下怎么感觉是个 bug ?
snippet 要不要展开由 yas--template-can-expand-p
决定,
(defun yas--template-can-expand-p (condition requirement)
"Evaluate CONDITION and REQUIREMENT and return a boolean."
(let* ((result (or (null condition)
(yas--eval-condition condition))))
(cond ((eq requirement t)
result)
(t
(eq requirement result)))))
但如果 condition
为 (= (line-number-at-pos) 1)
即 nil
且 requirement
(由 yas-buffer-local-condition
决定) 也为 nil
,那么最终结果是 t
,这明显不对。