问题背景
tab-and-go 模式,即在显示候选词时,按“下一个候选词”键(经常是tab)时,同时选择和输入下一个候选词。corfu 默认就打开了这个功能,只是没设置 tab 的快捷键,它的开关是 corfu-preview-current。 corfu 在它的文档中讲述了其 tab-and-go 模式的注意点:
请注意,进一步的输入不会展开 snippet 或 template,······ 为了强制展开 snippet ,请使用 RET 确认选择候选词。
这个描述与我习惯的,nvim 中的模式一致。但是 corfu 在实际使用时的行为,却不是这样。
问题描述
当我将 corfu 与 eglot 一起使用时,选择候选词(方法名等)后,继续输入括号时,snippet 竟然自动展开了。


一开始我认为这是什么新的 feature,但在阅读了一些 issue 和源码后,发现并不是,这就是一个 bug。并且不是什么无法解决的 bug,是拒绝修改的 bug。
问题源于 corfu 的作者与 eglot 的作者对于 capf 中 :exit-function 的 exact 状态理解的不同。
corfu 的作者认为 exact 就是什么都不做,保持当前的状态。然而在 eglot 的 eglot-completion-at-point 方法中,excat 和 finish(正常的结束的状态,corfu 中为按回车)在 :exit-function 中视为同一个状态,干同一件事,也就是会自动展开 snippet。
具体参见这个 Issue 的二楼:
解决方案
修改 corfu.el
中 corfu--prepare
方法最后一行
(corfu--insert 'exact)
为
(corfu--insert nil)
并且推荐设置 corfu-on-exact-match 为 nil 或者 quit,来让自己输入完整候选词后不自动展开
(setq corfu-on-exact-match nil)
这样处理后 corfu 的行为就与文档中描述的一致了。
或者,如果不需要 eglot 的 snippet 功能,也可以直接关闭
(setq eglot-stay-out-of '(yasnippet))
4 个赞
这个确实坑。
我也用的 corfu Tab-and-Go + eglot,在 python 3.11 + pyright 时,输入完 __init__
按空格,就会自动展开,设置了 (setq eglot-stay-out-of '(yasnippet))
也还是会展开。不知道是哪个包的问题?
展开前:
展开后:
(use-package corfu
:custom
(corfu-cycle t)
(corfu-auto t)
(corfu-auto-prefix 1)
(corfu-auto-delay 0.1)
(corfu-preselect 'prompt)
(corfu-on-exact-match nil)
:bind (:map corfu-map
([tab] . corfu-next)
([backtab] . corfu-previous)
("S-<return>" . corfu-insert)
("RET" . nil)
([remap move-end-of-line] . nil))
:hook (eshell-mode . (lambda () (setq-local corfu-auto nil)))
:init
(global-corfu-mode)
(corfu-popupinfo-mode))
PS: Company 也是同样。
试了一下,确实如此。
__init__
展开的这一段应该不是 snippet,所以设置了 stay-out-of yasnippet 也无效。
但原理上是一样的,corfu 发送 exact 信号,eglot 调用 :exit-function 帮你补全了这一段不需要的代码 
这种展开即使不用 tng,选中后按回车也会感觉挺迷惑的。。。
研究这种问题费时费力啊,最简单的解决方法还是自己手打__init__
了 
补充:
开 VSCode 试了一下,展开来也是一样的,那这就不是 Emacs 这几个插件的锅了
我之前试图讨论问过两边,失败了,主要我对 lsp 一窍不通
仔细看了下讨论,感觉主要是服务器端的问题。
我在 vscode 上也试过了,是同样的情况。不折腾这个了,要输入 __init__
的时候全部手打就好了。
主要是 pyright 的维护者也很固执,很难交流,万人血书加上忽略下划线开头的变量到现在还是东扯西扯2333
实在不喜欢就换后端😄。
python 除了 pyright,还有好2个在维护的 language server,哪个更好点?
-
python-lsp-server, Python 实现,目前Spyder IDE 团队和社区还在积极维护中。
-
jedi-language-server ,不知道有什么特点。