[已解决] 如何正确地添加一个 acm (lsp-bridge) 的补全后端?

目的: 用 acm 来代替之前配置中的 company 来作为 SLY 的补全提示.

参考 @manateelazycat 大佬在论坛中 completion buffer有没有可能像lsp-bridge、company那种方式显示? 的回复, 发现要修改的内容比较多 (?)

这里给出一个穷人实现, 不清楚是否可靠.

省流版: 核心代码是 acm-update-candidates-append-sly-results, 用 (advice-add 'acm-update-candidates :around ...) 的方式给原本的函数行为进行一个 “修正”.

问题: 空格没法触发 acm-hide (?) , 不清楚是不是只改了一个函数的缘故.

sly

;; use acm as SLY completion front end
;; currently is only for patching

;; from https://github.com/joaotavora/sly/blob/ba40c8f054ec3b7040a6c36a1ef3e9596b936421/lib/sly-completion.el#L323C9-L335C36
;; and see `acm-icon-alist' for the icon mapping
;; from https://github.com/manateelazycat/lsp-bridge/blob/49b5497243873b1bddea09a4a988e3573ed7cc3e/acm/acm-icon.el#L94

(defun acm-backend-sly-candidate-type (candidate)
  (pcase (get-text-property 0 'sly--classification candidate)
    ("fn"             "function")
    ("generic-fn"     "method")
    ("generic-fn,cla" "method")
    ("cla,type"       "class")
    ("cla"            "class")
    ("special-op"     "operator")
    ("type"           "class")
    ("constant"       "constant")
    ("var"            "variable")
    ("pak"            "package")
    ("pak,constant"   "package")
    ("macro"          "macro")
    (_                "unknown")))

(defun acm-backend-sly-candidates (keyword)
  (mapcar (lambda (candidate)
	    (let ((type (acm-backend-sly-candidate-type candidate)))
	      (list :key          candidate
		    :icon         type
		    :label        candidate
		    :displayLabel candidate
		    :annotation   (capitalize candidate)
		    :backend      "sly")))
	  (ignore-errors
	    (car (funcall sly-complete-symbol-function keyword)))))

;; poor man's lisp doc

(defun acm-backend-sly-candidate-doc (candidate)
  (when (sly-connected-p)
    (let ((type (plist-get candidate :icon))
	  (key  (plist-get candidate :key)))
      (pcase type
	((or "function" "method" "operator")
	 (sly-eval `(slynk:describe-function ,key)))
	(_ (sly-eval `(slynk:describe-symbol ,key)))))))

(defun acm-update-candidates-append-sly-results (fn)
  (if (and (derived-mode-p 'lisp-mode)
	   (sly-connected-p))
      (let* ((keyword (acm-get-input-prefix))
	     (sly-res (acm-backend-sly-candidates keyword)))
	(append sly-res (funcall fn)))
    (funcall fn)))
(advice-add 'acm-update-candidates
	    :around #'acm-update-candidates-append-sly-results)

(add-hook 'lisp-mode-hook      #'lsp-bridge-mode)
(add-hook 'sly-mrepl-mode-hook #'lsp-bridge-mode)

方便发补丁吗?我们在github上讨论吧,最近忙,论坛容易忘记。

OK, 已经提交了 PR 了. (感谢大佬)

今天晚上抽空看了一下代码, 有一个问题:

这个 sly 是不是同时支持capf接口? 如果支持的话, 你其实把 lisp-mode 和 sly-mrepl-mode 加入 acm-backend-capf-mode-list 就可以自动做补全, 不用新写一个 acm-backend-sly 后端。

空格没有触发 acm-hide 的问题, 其实是你的 acm-backend-sly 需要实现一个 acm-backend-sly-clean 的 clean 接口函数, acm-backend-sly-clean 函数的目的是acm菜单隐藏时清空 acm 对应后端的缓存数据, 可以参考其他 acm 后端的 clean 函数实现。一般实现clean接口也需要使用 acm-with-cache-candidates 宏, 搜索一下 acm-with-cache-candidates 就知道怎么用的了。

1 个赞

刚刚用最新的lsp-bridge,加上这个配置试了一下

(setq acm-enable-capf t)
(add-to-list 'acm-backend-capf-mode-list 'lisp-mode)

有点问题,因为 sly 返回的 collection 是 function 而不是数据,会报错,需要改一下,等我周末找时间提一下 PR

diff --git a/acm/acm-backend-capf.el b/acm/acm-backend-capf.el
index e2d475a..ddfd91d 100644
--- a/acm/acm-backend-capf.el
+++ b/acm/acm-backend-capf.el
@@ -142,8 +142,10 @@
                          (lambda ()
                            ;; We're still in the same completion field.
                            (let ((newstart (car-safe (funcall hookfun))))
-                             (and newstart (= newstart start))))))
-                   collection
+                             (and newstart (= newstart start)))))
+                        (initial (buffer-substring-no-properties start end)))
+                   (completion-all-completions initial collection nil (length initial))
                    )))))))

 (provide 'acm-backend-capf)

感谢感谢,zsbd

感谢指点, (字数补丁)

@manateelazycat 猫大,这个效果怎么样。原本的 sly 还提供了 annotation,但是刚刚改的那一版没有,我又修改了一下,我现在给拼上去了

diff --git a/acm/acm-backend-capf.el b/acm/acm-backend-capf.el
index e2d475a..e5866ba 100644
--- a/acm/acm-backend-capf.el
+++ b/acm/acm-backend-capf.el
@@ -125,25 +125,30 @@
 (defun acm-backend-capf-candiates (keyword)
   (when (member major-mode acm-backend-capf-mode-list)
     (let ((res (run-hook-wrapped 'completion-at-point-functions
-                                 #'completion--capf-wrapper 'all)))
+                                 #'completion--capf-wrapper 'all))
+          annotation-function)
       (mapcar (lambda (candidate)
                 (list :key candidate
                       :icon "capf"
                       :label candidate
                       :displayLabel candidate
-                      :annotation "CAPF"
+                      :annotation (if annotation-function
+                                      (concat (funcall annotation-function candidate) " CAPF")
+                                    "CAPF")
                       :backend "capf"))
               (pcase res
                 (`(,_ . ,(and (pred functionp) f)) (funcall f))
                 (`(,hookfun . (,start ,end ,collection . ,plist))
                  (unless (markerp start) (setq start (copy-marker start)))
+                 (setq annotation-function (plist-get plist :annotation-function))
                  (let* ((completion-extra-properties plist)
                         (completion-in-region-mode-predicate
                          (lambda ()
                            ;; We're still in the same completion field.
                            (let ((newstart (car-safe (funcall hookfun))))
-                             (and newstart (= newstart start))))))
-                   collection
+                             (and newstart (= newstart start)))))
+                        (initial (buffer-substring-no-properties start end)))
+                   (completion-all-completions initial collection nil (length initial))
                    )))))))

 (provide 'acm-backend-capf)
1 个赞

大佬,github发个补丁吧,谢谢

已提 PR,猫大有时间看看

感谢补丁,这个百分比是sly提供的吗?

是的,它的 annotation 里面有百分比

这个补丁还有点问题, 我已经在 github 中评价了。

补丁已经合并了, 感谢感谢!