最近一段时间喜欢上了在 Corfu 中使用 TAB-and-Go 的按键方式,corfu-map 中通过 TAB和Shift-TAB 进行候选的上下浏览,并自动预览。
不过当 Corfu 遇上 YASnippet 时,TAB 就被 yasnippet 抢占了。不知道有没有更加方便的方法进行设置,实现以下功能:
- 当没有 corfu 候选框时,TAB 按键由
yas-map
占用。
- 当 corfu 弹出候选框时,TAB 就交给
corfu-map
- 当 corfu 候选框关闭后,TAB 交回给
yas-map
目前我能想到的方案是通过 advice-add 实现,功能是正常的,但觉得写的有点丑,也许大佬们有更好的方案?
(advice-add #'corfu--make-frame :around
(defun +corfu--make-frame-a (oldfun &rest args)
(setq yas-keymap-disable-hook t)
(apply oldfun args)))
(advice-add #'corfu--popup-hide :around
(defun +corfu--popup-hide-a (oldfun &rest args)
(setq yas-keymap-disable-hook nil)
(apply oldfun args)))
这是我的 corfu 和 yasnippet 配置:
(use-package corfu
:custom
(corfu-cycle t)
(corfu-auto t)
(corfu-auto-prefix 1)
(corfu-auto-delay 0.1)
(corfu-preselect 'prompt)
:bind (:map corfu-map
([tab] . corfu-next)
([backtab] . corfu-previous)
("S-<return>" . corfu-insert)
("RET" . nil))
:hook (eshell-mode . (lambda () (setq-local corfu-auto nil)))
:init
(global-corfu-mode))
(use-package yasnippet
:diminish yas-minor-mode
:hook (after-init . yas-global-mode))
(use-package yasnippet-snippets)
感谢 @kigo64 的指点,目前我的 YASnippet 使用下面的设置,已经能够完美在 Corfu 中配合 tng (TAB-and-Go) 按键模式使用了。
3 个赞
比较好奇有没有 yas
的快捷键在 corfu
中补全的方法。
不好意思,我实在没看懂你说的是什么意思。
yas 的快捷键是指的哪些命令?yas-expand
?
是我没写清楚,应该是: snippet 的 key 进行补全 。 如 org-mode下 # key: dit_
可以直接配合 corfu
进行补全。
你意思是把 yasnippet 的key加入 corfu 的候选列表中?
是的, 这个功能实现的话,两者冲突的概率应该就不大了。
你说的这个是可以实现的。最近有人写了 GitHub - elken/cape-yasnippet
结合 cape 就可以。比如下面的配置:
(defun yas-setup-capf ()
(setq-local completion-at-point-functions
(cons #'cape-yasnippet
completion-at-point-functions)))
(add-hook 'prog-mode-hook 'yas-setup-capf)
(add-hook 'text-mode-hook 'yas-setup-capf)
如果使用 eglot的话,还可以加上这个设置:
(defun my/eglot-capf ()
(setq-local completion-at-point-functions
(list (cape-super-capf
#'cape-yasnippet
#'eglot-completion-at-point))))
(add-hook 'eglot-managed-mode-hook #'my/eglot-capf)
javascript-ts-mode 中开启 eglot 的效果如下图:
我个人不喜欢把 snippet 放到补全列表中,比较喜欢手动用 TAB 触发。
但我的问题不是触发 yasnippet,是 yasnippet 展开后会抢占 TAB。
1 个赞
我用corfu同类插件company时把yasnippet从company-backends去掉了.用M-y
触发yasnippet. 我的习惯是yasnippet用来输入略长一点的代码端.
用你的配置无法重现问题。
- 运行:
$ emacsq.sh -P use-package,yasnippet,corfu --eval "\
(progn
(use-package corfu
:custom
(corfu-cycle t)
(corfu-auto t)
(corfu-auto-prefix 1)
(corfu-auto-delay 0.1)
(corfu-preselect 'prompt)
:bind (:map corfu-map
([tab] . corfu-next)
([backtab] . corfu-previous)
(\"S-<return>\" . corfu-insert)
(\"RET\" . nil))
:hook (eshell-mode . (lambda () (setq-local corfu-auto nil)))
:init
(global-corfu-mode))
(use-package yasnippet
:diminish yas-minor-mode
:hook (after-init . yas-global-mode))
;; +++
(add-hook 'emacs-startup-hook
(lambda ()
(switch-to-buffer \"*scratch*\")
(yas-minor-mode 1)
(yas-define-snippets
'emacs-lisp-mode
'((\"glo\" \"yasnippet$0\" \"snippet\" nil nil nil nil nil nil))))))"
- 输入
glo
- 等待补全菜单出现
- 按
TAB
- 结果
- 期望:展开
glo
- 实际:执行
corfu-next
在还没触发 yasnippet 展开时,这个结果我是能接受的。
是我描述的不清楚,应该是在 yasnippet 展开后和 corfu 有冲突。重现步骤如下:
- 除了安装 corfu 和 yasnippet,还应该安装一个 yasnippet-snippets
- 在 scratch buffer 输入
lam
- 等待补全菜单出现
- 按
TAB
,执行的是 corfu-next
,可以接受。
- 按 C-q 取消,或者直接按 yas-expand 对应的其他按键。
- 按
TAB
,光标在yasnippet 的编辑区跳转。
- 输入字符 a,等待补全菜单出现
- 按
TAB
- 结果:
- 期望:执行
corfu-next
- 实际:跳转到下一个 yasnippet 编辑区。
我也是打算单独绑定一个按键用于 yas-expand,目前尝试用 M-*, 因为我用的原生按键, M-y 被占用了。
这样应该可以?简单测试了一下,没有 corfu-frame 的时候,可以正常跳到下一个位置;弹出 corfu-frame 后,按 tab 执行了 corfu-next
(defun my-corfu-frame-visible-h ()
(and (frame-live-p corfu--frame) (frame-visible-p corfu--frame)))
(add-hook 'yas-keymap-disable-hook #'my-corfu-frame-visible-h)
3 个赞
简单来说你的问题是希望在补全列表出现的时候用tab来循环选择是吧?
这个我从没用过,一开始是C-n C-p上下选择的,后来跟abo-abo抄了“列表出现的时候按1 2 3来选择某项(alt-1输入真的1)”这个做法。(见abo-abo的ora-company-number
)基于这个,你的问题可以:
- 也用1 2 3来选择对应项
- 把1和2绑定到next/prev(alt-1输入真的1)
是的,我不想用 C-n C-p ,因为不想按太多的 Ctrl。用 TAB 感觉更流畅。
感谢大佬,这就是我想要实现的。这样我就不用 advice-add
了
我基于你的代码做了下简化,(frame-live-p corfu--frame)
好像也没必要检查,只要检查弹出框是否可见就好了。
下面的配置就可以正常工作了。
(use-package yasnippet
:diminish yas-minor-mode
:bind ("M-*" . yas-expand)
:custom (yas-keymap-disable-hook (lambda () (frame-visible-p corfu--frame)))
:hook (after-init . yas-global-mode))
为了保险起见,还是 (frame-live-p corfu--frame)
检查一下,否则万一什么地方把窗口销毁了,或者窗口从来没显示过,变成 nil 就会出错。
1 个赞
确实像你说的,如果是刚启动 Emacs,没有弹出过 corfu 窗口,直接通过 M-x yas-insert-snippet
插入 sneppet 后,TAB 就没法跳到下一个选区了。
当存在补全框的时候按 C-y 会触发展开 yas, 当不存在补全框的时候就是按 tab 展开。一直都这么用,已经用习惯了。然后存在补全框的时候 tab 就是常规的 tng 模式。
这样能省一个按键。
不过我用习惯了原生按键,C-y 是最常用的粘贴键,对我来说很难适应。
其实展开yasnippet 我更喜欢用 consult-yasnippet,可以快速查询 snippet 的名称和按键,还能实时预览。
类似的包,还有 ivy-yasnippet
更多的时候是使用 eglot 时,lsp-server 就会提供 snippet,补全时就会自动用 YASnippet 进行展开。