lsp-bridge -- 速度最快的语法补全插件

突然想到,不知道把 lsp-bridge-record-completion-items 加入到 corfu-auto-commands 有没有作用:

  (defun lsp-bridge-record-completion-items (filepath prefix common items)
    (dolist (buffer (buffer-list))
      (when (string-equal (buffer-file-name buffer) filepath)
        (setq-local lsp-bridge-completion-items items)
        (setq-local lsp-bridge-completion-prefix prefix)
        (setq-local lsp-bridge-completion-common common)
        (message "lsp-bridge-record-completion-items: %s" items)
        )))
  (with-eval-after-load 'corfu
    (add-to-list 'corfu-auto-commands 'lsp-bridge-record-completion-items)
    )

试了一下,corfu 里面的补全项和 lsp-bridge-completion-items 一样。(只测试了 Python)

发现了 corfu 的一个bug, 发了个 PR 上去

2 个赞

我试了上面代码还不行。

把 lsp-bridge 和 corfu 放到一起宣传吧,哈哈哈哈哈。

1 个赞

嗯嗯,可以,必须的,我已经切到corfu有一段时间了

我有时间试一下 volar ,它的 takeover 模式有点麻烦

1 个赞

你这个补丁竟然修复了 corfu 在 Windows 系统上显示图标的一个 bug。不过对 kind-icon 有副作用。 可能还有别的问题。 加了补丁后全部能正常显示,只是后面多了半行。 image

没加补丁的话,后面的一行会被切掉一半。

如果行距调大的话,还会缩放图标,因为补丁改成了自动调整。 image

我这样是有结果的,但是我不确定这种情况下是打字触发的还是 lsp-bridge 触发的

lsp-bridge 现在是文件有变动 0.1s 后向 lsp-server 请求补全。
一个新逻辑不知道会不会更好:capf 补全时候检查当前位置和之前位置是否属于同一个 symbol, 如果是,直接获取 lsp-bridge-completion-items;如果不是,向 lsp-bridge 发送补全请求,lsp-bridge 再找 lsp-server 请求。类似这样:

(defvar lsp-bridge--start nil)
(defun lsp-bridge-capf (&optional arg)
  (let* ((bounds (bounds-of-thing-at-point 'symbol))
         (start (or (car bounds) (point)))
         (end (or (cdr bounds) (point))))
    (if (equal start lsp-bridge--start)
        (list start end
              lsp-bridge-completion-items
              :exclusive 'no)
      (setq lsp-bridge--start start)
      (lsp-bridge-call-async "do_completion" lsp-bridge-filepath (line-number-at-pos) (current-column) (lsp-bridge-char-before)) ;; do_completion 是 py 那边定义的函数,直接调用 `self.completion`。 同时取消 change_file 里面补全部分
      (list start end '())
      )))

这种基于位置的一般都比较脆弱,因为不光有添加,还有删除的逻辑,前后两次如果不是 self-insert-command, 一些高级文本操作有可能会导致 (point) 是相同但是内容不一样的情况。

不建议取巧,这种设计一旦遇到bug了,都不好反馈问题,哈哈哈哈。

好像 windows 下面用不了, enable-debug 和 log 之后看到一堆线程启动退出的 log。暂时不太方便研究,看其他人有没有遇到过。

应该是自动调整的,要不是 frame 内部完全靠各种加加减减,很难算对,而且代码还巨复杂。

1 个赞

Windows 下已经有人成功使用了,更新最新版本以后,看看能否重现问题。

image

corfu 里有这样一种操作,输入corfu-separator(默认是 shift+space),可以用作 orderless分隔符。如图,输入 a[shift+space]string可以继续匹配 a_long_long_string.

但是现有 lsp-bridge 里会破坏这种行为。没太细研究,但是猜测原因可能是 change_file 触发 lsp-bridge 把他当成 a string 去做补全了

以后可以探测 corfu 的这种状态,避免发送 change_file 请求,我先研究一下 corfu 的文档,看看晚上能否把图标给弄了。

lsp 的回包里面的 “kind” 就是图标了:

[{'label': 'a_long_long_string', 'kind': 6, 'data': {'workspacePath': '/Users/lijie/proj/test/main.py', 'filePath': '/Users/lijie/proj/test/main.py', 'position': {'line': 4, 'character': 6}, 'symbolLabel': 'a_long_long_string'}, 'sortText': '09.9999.a_long_long_string'}]

把 lsp-mode 里面的枚举抄过来,然后在 capf 的返回值里面加上 :company-kind 就行了, annotation 也差不多:

 (defun lsp-bridge-capf ()
    (let (
          (bounds (bounds-of-thing-at-point 'symbol)))
      (list (or (car bounds) (point))
              (or (cdr bounds) (point))
            lsp-bridge-completion-items
            :exclusive 'no
            :company-kind
            (lambda (candidate) "Set icon here" (get-text-property 0 'kind candidate))
            :annotation-function
            (lambda (candidate)
              "Extract annotation from CANDIDATE."
              (get-text-property 0 'annotation candidate)))))

还有 :exit-function, 后续展开 snippet 可以放在这里处理

我还没有研究 corfu 的代码,一般都是挂在 pre-command-hook 后面做命令或者按键事件拦截,我的理解,只有是 Emacs 的用户按键命令才会被 corfu-auto-commands 过滤到。

lsp-bridge-record-completion-items 完全是 Python 进程通过 IPC 进行的远程调用,Emacs没法从 pre-command-hook 拦截到。

大佬你测试了吗?感觉我可以直接抄代码了。

这样可以获取,但是需要 lsp-bridge 把 kind 传过来,然后写进 property. 可以参考 eglot-completion-at-point 的实现。

kind 需要在 python 代码里面处理,因为处理返回的 json 代码在那里面,比如放在 lsp-bridge-record-completion-items 调用里面作为参数传回来。现在的 lsp-bridge-record-completion-items 里面只获取了 label 这个属性。