如何让company-lsp与company-dabbrev-code愉快地工作?

elpy有一个补全比较好的地方就是,在jedi没有返回结果的时候,会去获取dabbrev的结果:elpy-company-backend

                 (cond
                  ;; The backend returned something
                  (result
                   (elpy-company--cache-completions arg result))
                  ;; Nothing from the backend, try dabbrev-code.
                  ((> (length arg) company-minimum-prefix-length)
                   (elpy--sort-and-strip-duplicates
                    (company-dabbrev-code 'candidates arg)))
                  ;; Well, ok, let's go meh.
                  (t
                   nil))

我把backend设成(company-lsp company-dabbrev-code) 这样子是没办法自动触发company-dabbrev-code的。

把backend设成((company-lsp company-dabbrev-code))((company-lsp :with company-dabbrev-code))这样,补全的时候有轻微卡顿,并且结果也没有合并,重复选项非常多,排序也怪怪的。

我期待的是和elpy一样的结果,就是当lsp完全没有结果返回时才使用company-dabbrev-code。但是(company-lsp company-dabbrev-code)这样为什么不会自动触发company-dabbrev-code?输入一个lsp不会有结果的词,company-diag也显示的是Used backend: company-lsp。对这个词手动company-dabbrev-code是有结果的。

是否必须通过像(company-dabbrev-code 'candidates arg)这样注入代码到company-lsp才能达到效果?

我用ccls也遇到了这个问题,我自己的方案是,默认使用company-dabbrev-code,这样速度比较快,company-lsp绑定了一个快捷键 (Ctrl+Tab),在需要的时候手动触发,目前使用没啥障碍,速度也没什么影响

多个backend和group的backends从来就没搞彻底懂过。。company-backends下面那一大段文档读了,实际多个backends使用时触发还是像谜的一样,难怪issue里那么多人也不懂backends的相互关系。。

我是想把它搞得稍微智能一点点。。因为不太喜欢手动触发。。哪怕需要配置。。曾经用过一位大神的hippie expand的配置,效果非常好,但是因为要手动触发,渐渐就不用了。。隔壁贴说模糊匹配之类的,我已经不敢指望了。。:sob:

'(A B)这样的时候不重复还是可以做到的吧,是不是因为lsp有自动snippet之类的所以没有把相同的项识别出来?那大概需要自己手动把两个backend合并一下,感觉很难……

就是手动合并两个后端的补全list

应该不难

对于lsp与dabbrev这种主力的backend,我是比较倾向于当一个backend完全没有结果的时候才使用下一个。不然的话排序也是个问题。所以elpy也是直接在callback里做(使用另一个backend)这件事。

我有个地方不太明白,输入一个不存在的prefix,例如:“haha”,company-lsp里返回的prefix是"haha",但此时其实它不能补全,为什么没办法把控制交给下一个backend?是因为它是个async的backend的缘故?company-backends的文档里说要返回nil才把控制交给下一个backend。

我看了一些别的一些backend也会有这样的,这样的意思是不是要求每一个company-backends里面的元素都是一“类”backend?因为不同“类”才会使触发prefix不一样(比如company-files不在字符串里会自动不补全)。否则,prefix不是nil,但又不能补全,company-backends里塞一大堆那有什么用。。

文档里说Only one backend is used at a time。好奇为什么没法做到一个backend没结果了,就按顺序触发下一个,哪怕此时prefix不是nil

如果一个后端没有补全结果,compnay就会按顺序启用下一个。这个跟prefix返回的结果没关系。

似乎并不会。例如在一个elisp buffer里如果company-backends的值为(company-elisp company-dabbrev-code),文件内容为:

(defun hahahaha ()
  )

haha*

此时光标在*处的话,是不会触发company-dabbrev-code的。因为文档里说当一个backend调用prefix时返回nil,才会触发下一个。但此时company-elisp返回的prefix是haha,所以不会触发下一个。

也就是说“应不应该补全”和“能不能补全”不太一样。但“不能补全”时如果prefix不是nil的话,应该是不会触发下一个的。

Returning nil from this command passes control to the next backend. The function should return stop if it should complete but cannot (e.g. when in the middle of a symbol).

之前遇到同样问题, 并提了一个issue: For a backend group, if the first backend gives candinates, how to discard candinates from the second backend? · Issue #806 · company-mode/company-mode · GitHub

我的想法跟LZ稍微不同, 我是想让两个后端同时开工, 这样(理论上)响应速度快, 如果第一个backend有返回结果, 就把第二个的丢弃, 用第一个, 如果第一个没有返回结果, 就用第二个的.

company目前不支持这样, 不过issue里面最后提供的方法也很接近想要的效果, 可以试试.

thread 还没有搞明白

就想用 concurrency 了?

lsp补全的工作大部分在外部的lsp服务器, 所以可以并行. company目前就相当于并行的, 因为backend很多都是异步, 只需要提供上面提到的丢弃的功能即可.

:separate那种方法,结果不会合并。。好多重复

你可以看下那个issue的跟帖, 里面就是在讨论这个问题, 最后有一个方法还可以

就是那个:separate的方法,结果不会合并~

里面本来也没说会合并啊, 最后的效果是让补全项按backend的顺序显示, 比如company-lsp的补全项显示在前面, 另外一个的显示在后面, 这样保证了你优先看到company-lsp的补全项

看来只好这样…

这个问题有办法了吗,company-lsp为什么会在没结果的时候阻止后面的backends触发啊?

这个问题的影响不算非常大,因为company-lsp会给出当前scope可用的变量、函数等符号,基本算是包括了company-dabbrev-code的功能,只剩关键字之类的没法补全。

可以在补全结果增加一个后端标识符,选候选时可以输入后端标识符列出该后端的匹配项,参考vscode不知道行不行