Corfu和lsp一起工作时不支持snippet

2022-02-04-16-09-02 最近看到了company新的替代方案corfu,试了一下,在Windows上和lsp搭配的表现确实不错,但是好像不支持lsp中的snippet,求教各位大佬有没有什么解决方案。 这是与lsp相关的所有配置:

(use-package lsp-mode
  :straight t
  :custom
  (lsp-completion-provider :none)
  :init
  (defun my/orderless-dispatch-flex-first (_pattern index _total)
    (and (eq index 0) 'orderless-flex))
  (defun my/lsp-mode-setup-completion ()
    (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
          '(orderless))  (setq lsp-mode t))
  (add-hook 'orderless-style-dispatchers #'my/orderless-dispatch-flex-first nil 'local)
  (setq-local completion-at-point-functions (list (lsp-completion-at-point)))
  :hook
  ((lsp-completion-mode . my/lsp-mode-setup-completion)
   (python-mode . (lambda ()
                         (require 'lsp-pyright)
                         (lsp)))

这是与corfu有关的所有配置:

(use-package corfu
  :straight t
  :bind (:map corfu-map
	      ("TAB" . 'corfu-insert)
	      ("<backtab>" . 'corfu-complete)
              ("RET" . 'newline))
  :custom
  (corfu-auto t)                 ;; Enable auto completion
  (corfu-quit-at-boundary t)     ;; Automatically quit at word boundary
  (corfu-quit-no-match t)        ;; Automatically quit if there is no match
  (corfu-preview-current nil)    ;; Disable current candidate preview
  :hook ((prog-mode . corfu-mode)
         (shell-mode . corfu-mode)
         (eshell-mode . (lambda ()
			  (setq-local corfu-quit-at-boundary t
				      corfu-quit-no-match t
				      corfu-auto nil)
			  (corfu-mode))))
  :init
  (corfu-global-mode))

在录制GIF时,yas-minor-mode已确认打开。

顺带一提,与lsp有关的代码从这里: Home · minad/corfu Wiki · GitHub 获得。

我用 Corfu+Eglot,是支持snippet的。

估计是你 lsp-mode 的配置问题

可以贴一下你的eglot配置吗?多谢! :wink:

eglot 我基本都没怎么设置

(use-package eglot
  :defer t
  :bind (:map eglot-mode-map
              ("C-c l a" . eglot-code-actions)
              ("C-c l r" . eglot-rename)
              ("C-c l f" . eglot-format)
              ("C-c l d" . eldoc))
  :hook (eglot-managed-mode . (lambda () (flymake-mode -1)))
  :config
  (setq read-process-output-max (* 1024 1024))
  (push :documentHighlightProvider eglot-ignored-server-capabilities)
  (setq eldoc-echo-area-use-multiline-p nil))

注释掉lsp后使用你的配置,还是没有snippet呀。 请问有没有snippet相关的配置,比如yasnippet?

你安装 yasnippet 和 yasnippet-snippets了吗?你装上这两个包应该就可以了。 这是我的配置:

;; YASnippet
(use-package yasnippet
  :diminish yas-minor-mode
  :hook ((prog-mode org-mode) . yas-minor-mode)
  :bind (("C-c y i" . yas-insert-snippet)
         ("C-c y f" . yas-visit-snippet-file)
         ("C-c y n" . yas-new-snippet)
         ("C-c y t" . yas-tryout-snippet)
         ("C-c y l" . yas-describe-tables)
         ("C-c y g" . yas-global-mode)
         ("C-c y m" . yas-minor-mode)
         ("C-c y r" . yas-reload-all)
         ("C-c y x" . yas-expand)
         :map yas-keymap
         ("C-i" . yas-next-field-or-maybe-expand))
  :config
  (yas-reload-all))

(use-package yasnippet-snippets
  :defer t
  :after yasnippet)

大概明白了,你是用corfu补全代码,yasnippet展开snippet(C-i),是吗?

我这边看了一下,snippet是由clangd提供的。我是想可不可以tab补全后直接选中($0),然后再键入呢?

不是的,我就是直接按的回车(corfu-insert)接受补全,然后就会进行snippet的展开,光标就会落在 $0 的位置。 这个功能是 eglot 支持的,跟补全的前端无关(不管你是用 company,corfu,即使是只用默认的completion-at-point 都是可以的)

这是 eglot 官方的展示和解释:GitHub - joaotavora/eglot: A client for Language Server Protocol servers

我估计你的问题是其他配置引起的冲突,因为你 eglot 都没法使用 snippet。建议你用一个干净的配置测试:只安装 lsp-mode + corfu 或者 eglot + corfu 试试。

多谢!找到原因了,是因为我将[tab]绑定成了corfu-complete

corfu-completeimage

corfu-insertimage

以前一直没有理解这两个之间的差别,而我又想[enter]直接换行,所以就绑错了。 :joy: