请教一个关于 yasnippet 自动展开设置的问题

本人因为想更快速地记数学笔记,在尝试给 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 下也会有问题,不过排查好久最后发现是我写出问题了哈哈

经过一番排查发现是我自己一开始的函数写挫了 :hot_face: 检查 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 感觉上可能会操作频繁,但实际使用上体验良好 :smile:

为什么需要针对不同模式呢?

比如 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 ,这明显不对。

楼上说的 laas 应该是下面帖子里提到的包