company-backends 添加第三方backend的一个helper函数设置

我的 company-backends 的默认值是:

(setq-default company-backends
                `((company-capf         ; `completion-at-point-functions'
                   ,@(if (and (featurep 'company-tabnine)
                              (or (derived-mode-p 'c-mode)
                                  (derived-mode-p 'c++-mode)))
                         (list 'company-tabnine))
                   ;; :with company-semantic
                   ;; company-gtags company-etags
                   :separate company-yasnippet
                   :separate company-tempo  ; tempo: flexible template insertion
                   :separate company-keywords
                   :separate company-abbrev)
                  company-dabbrev-code
                  company-files))

我写了一个helper函数,用于给major mode hook来添加第三方的backend,以便于和我的company-backends默认值能够group起来。 这是helper函数代码:

(defun my-company-add-backend-locally (backend)
    "Add a backend in my custom way.

\\(my-company-add-backend-locally 'company-robe\\)"
    (make-local-variable 'company-backends)
    (unless (eq (if (listp (car company-backends))
                    (caar company-backends)
                  (car company-backends))
                backend)
      (setf (car company-backends) ; <----- need improve here.
            (cons backend (cons ':with (car company-backends))))))

这个是我改了好几次后的版本,之前是用 add-to-list 还用过其他办法。现在改成 setf 了。但是 setf 有个问题,就是会不断的叠加修改效果,尽管我设置了 company-backends buffer local variable. 还是没用。 不知道各位大神有什么好办法么?

还是用回老办法了,cons拼装。

(defun my-company-add-backend-locally (backend)
    "Add a backend in my custom way.

\\(my-company-add-backend-locally 'company-robe\\)"
    (make-local-variable 'company-backends)
    (unless (eq (if (listp (car company-backends))
                    (caar company-backends)
                  (car company-backends))
                backend)
      (cons (cons backend (cons ':with (car company-backends)))
            (cdr company-backends))))

把输入值和期望的输出值提供一下,有点没明白你的意思哦

比如 company-backends 的默认值是:

((company-capf :separate company-yasnippet :separate company-tempo :separate company-keywords :separate company-abbrev)
 company-dabbrev-code company-files)

当执行 (my-company-add-backend-locally 'company-elisp) 之后,我希望变成:

((company-elisp :with company-capf :separate company-yasnippet :separate company-tempo :separate company-keywords :separate company-abbrev)
 company-dabbrev-code company-files)

但是这里面要判断 company-backends 里是否是 car 是否是 list, 以及 car list 里面是否第一个是 company-capf 。这样的。 具体代码逻辑 my-company-add-backend-locally 函数里写了。 主要是用了 setf 后, company-backends 会叠加,比如我进入 ruby-mode, 变成:

((company-robe :with company-capf ....) ...)

再次进入其他mode,比如 emacs-lisp-mode 后,变成了:

((company-elisp :with company-robe :with company-capf ....) ...)

这样不断叠加了。

你说的再次进入 ruby-mode 是通过 M-x ruby-mode 这样进入的吗 我试过了前面的代码,不同 buffer 里面代码是有效的 make-local-variable 只对当前 buffer 有效

通过打开一个 ruby buffer, 这个函数是从:

(add-hook 'ruby-mode-hook #'(lambda () (my-company-add-backend-locally 'company-robe)))

这种形式。

我去读 setf 的docstring,没太看懂。我以为应该是不会对 buffer-local variable 产生 全局影响的。

我觉得是 hook 有问题,如果不加 Hook, 在各个模式 手动 执行 (my-company-add-backend-locally 'company-robe) 是没有问题的 看了下 add-hook 的文档, 最后有个local的参数, – Function: add-hook hook function &optional depth local 等下吃饭时间再试试

按道理应该不会啊,我 my-company-add-backend-locally 是设置了 make-buffer-local 的。 setf 应该修改的是 buffer local variable 才对啊。我现在修改了所有 这个hook,加上了 (add-hook ... nil 'local) 参数。试试。

我试了,修改后还是不行。

你应该自定义一个变量,比如

(setq my-company-backends
      `((company-capf         ; `completion-at-point-functions'
         ,@(if (and (featurep 'company-tabnine)
                    (or (derived-mode-p 'c-mode)
                        (derived-mode-p 'c++-mode)))
               (list 'company-tabnine))
         ;; :with company-semantic
         ;; company-gtags company-etags
         :separate company-yasnippet
         :separate company-tempo  ; tempo: flexible template insertion
         :separate company-keywords
         :separate company-abbrev)
        company-dabbrev-code
        company-files))

然后设置company-backends的默认值为这个变量,这样你就可以直接操作my-company-backends,不用再判断company-backends里的值

(defun my-company-add-backend-locally (backend)
  (let* ((a (car my-company-backends))
         (b (car a))
         (c (cdr a)))
    (make-local-variable 'company-backends)
    (setq company-backends
          (append
           (list (append (list backend :with b) c))
           (cdr my-company-backends)))))

(my-company-add-backend-locally 'company-lsp)
(my-company-add-backend-locally 'company-web)

自定义一个变量还是有一个问题,那就是很多mode其实就是 company-capf 有效。所以我用的是 setq-default. 另外有的mode需要 my-company-add-backend-locally 两次,添加不同的 backend,比如latex下面,有很多 company backend 。当然,其实可以直接 copy 这个 default 变量出一个临时新变量也是ok的。不过原先旧的方案我用的是 cons 拼接方式。用的挺好的,就是忽然想到用 setf 结果出问题了。