为何company的补全有时弹窗给出候选项, 有时又没有.

去年正经入了emacs的坑, 使用doom用到现在, 不断的体验到流水般的顺畅.

有一个问题一直困扰我. 如图, 在默认的config.el中, 也就是elisp模式中, 我录入三个字符后, 然后control-n, company会给出弹窗补全. 但是在python-mode中, 我录入三个字符, control-n, company直接输入了一个词, 没有弹窗. 即使前缀匹配了好几个keywords.

我看到两个mode下的backends都是一样的.

新用户只能发一张图, 所以截屏拼了一下. 上面是company_backends配置. 下左是python中无法弹窗补全的状态, 下右是elist中正常补全弹窗的状态.

为什么在elisp下输入quick会出现弹窗, 而python中输入rec则不会.

能不能让company无论如何都弹窗揭示候选词.

我的doom配置打开了:complete中的company.

这个问题导致在最常用的buffer内关键词补全的时候, 经常control-n超过了正确的词, 从而要删几个字符才能补全正确. 很影响效率.

没用 Doom。是不是由于两个 mode 之中 C-n 补全的函数不一样?你分别在两个 mode 中 M-x describe-key 然后按 C-n 看看?

company-backed 中的company-capf 使用的是 completion-at-point-functions 这个变量来控制具体的自动补全来源的。

elisp中和python中这个 completion-at-point-functions (也叫做capf) 的值是有差别的。

根本问题就是 你的 python中的这个capf 函数可能跟你想的不一样,或者你没有配置对。

具体要看python补全中capf函数具体是哪个。

1 个赞

是不是没有配 lsp

两个c-n都指向evil-complete-next

感谢 guanghui.qu的提醒. 看了一下, 在python-mode中, capf的值为 (python-completion-at-point t) 而elisp-mode中, capf值为 (elisp-completion-at-point tags-completion-at-point-function)

差异应该就来自于此.

不过我最后没有使用这个解决, 因为我发现我期望的是buffer内的关键词补全, doom的默认值给得不太对.

我最后添加了两个修改, 在

此修改完成后需要运行doom sync才会生效.

同时在config.el中加入了一行. (modify-syntax-entry ?_ “w”)

前一个修改是为了把添加dabbrev和files这两个后端. 后一个修改是为了告诉emacs把下划线视为词的一部分.

通过这两个处理之后, 目前已经达到了目的.

感谢热心回复的小伙伴们.

1 个赞

直接在 config.el 里面修改 +lsp-company-backends 就可以吧

因为我是从vim迁移过来的, python的代码补全和vim一样共用了youcompleteme, 为了图省事就没有再配置lsp. 以后可能会试试lsp, 大家都说好, 我曾经尝试了几次但是没有迁过去.

1 个赞

有个诊断命令 M-x company-diag ,可以看到当前正在用的哪个后端,会有一定帮助。

1 个赞

vim/nvim用coc.nvim做补全也挺好的

1 个赞

似乎还是有些问题, 在org-mode中还是不弹窗, 直接补全, 今天新的进展是发现这个行为似乎不是backends的问题, 而是frontends. company-frontends,好像是可以对选项进行过滤, 弹窗应该的应该是company-box这个frontend, 而不弹窗的是(company-pseudo-tooltip-unless-just-one-frontend) 这个frontend.

此时手工设置company-box-mode无效.

而且在org-mode直接补全时会有行提示:

Expansion found in ‘ *org-src-fontification:sh-mode*,

company-pseudo-tooltip-unless-just-one-frontend 这个前端意思就是只有一个候选项就不弹窗

1 个赞

最终解决了. 目前在org-mode和python中, 都如预期达到了目的.

之前帖子中对于autoload的修改不太对, 将capf设置为首选补全应该是有道理的. 根本的问题在于frontends. 刚开始的时候frontend在两个mode中应该是不同的. 特别是 xhcoding提到的just-one-frontend, 可能还有默认的preview-frontend.

最终的话, 在init.el中. 开启

       (company +childframe)   ; the ultimate code completion backend

选项, 使用company-box-frontend, 然后在config.el中设置.

(add-hook 'company-mode-hook 'company-box-mode)
(setq company-frontends '(company-box-frontend))

解决了此问题. 再次感谢朋友的热心回复.

进一步的总结. doom-emacs中使用evil, everywhery.之后默认insert时的C-n绑定到evil-complete-next下. evil-complete-next调用的是evil的补全, 这时是没有弹窗的, 弹窗是company的功能, 弹窗通过minimum-prefix这个变量的最小前缀来触发.

这里容易混淆的就是doom-emacs中在evil-insert-state-map下和弹窗后的company-active-map两个模式下的补全快捷键都是C-n, 所以就容易搞错.

(define-key evil-insert-state-map (kbd "C-n") #'company-complete)

(with-eval-after-load 'company
  (define-key company-active-map (kbd "C-y") #'company-complete-selection)
  (define-key company-active-map (kbd "C-p") #'company-select-previous-or-abort)
  (define-key company-active-map (kbd "C-n") #'company-select-next-or-abort)
  (setq company-selection-wrap-around t)
  (setq company-minimum-prefix-length 3))

这样evil插入时的补全替换为company-complete唤出弹窗, 然后使用c-n c-p循环. 如果只有一条备选就直接插入. 否则在整个环境中循环. 一些补充的后续. 如果有搜到的朋友, 可能有些助益.