问题情景:
json-mode继承自js-mode,这造成一个严重问题。
在开启了js的lsp后,再次进入的json的buffer,会被默认开启js的lsp,出现混乱。
关键的,我不用(也不想用)json的lsp,只能耗费了一些时间才找到解决方法。
核心代码:
(defvar my/eglot-language-ignore-modes nil)
(defun my/eglot-current-server (orig-fn)
(if (derived-mode-p my/eglot-language-ignore-modes)
(setq eglot--cached-server nil)
(funcall orig-fn)))
(advice-add 'eglot-current-server :around #'my/eglot-current-server)
使用方法:
(add-to-list 'my/eglot-language-ignore-modes 'json-mode)
1 个赞
感觉你这种方式还是会启动lsp服务,但是不会被当前buffer连接,或许可以试试我一直在用的,只允许指定的major-mode或者minor-mode激活eglot,虽然比较麻烦一些
(defvar maple-lsp-major-modes
'(python-mode go-mode dart-mode html-mode css-mode js-mode web-mode vue-mode typescript-mode))
(defvar maple-lsp-minor-modes
'(:not magit-blob-mode))
(defmacro maple-lsp-with(&rest body)
"Start lsp server and execute BODY within special major modes or minor modes."
(declare (indent 0) (debug t))
`(when (and (memq major-mode maple-lsp-major-modes)
(cl-loop for mode in maple-lsp-minor-modes
if (and (not (keywordp mode)) (boundp mode) (symbol-value mode))
return (not (eq (car maple-lsp-minor-modes) :not))
finally return t))
,@body))
(defun eglot-ensure@override ()
(let ((buffer (current-buffer)))
(cl-labels
((maybe-connect
()
(eglot--when-live-buffer buffer
(remove-hook 'post-command-hook #'maybe-connect t)
(unless eglot--managed-mode
(maple-lsp-with
(apply #'eglot--connect (eglot--guess-contact)))))))
(when buffer-file-name
(add-hook 'post-command-hook #'maybe-connect 'append t)))))
(advice-add 'eglot-ensure :override 'eglot-ensure@override)
是比较烦,我今天突然发现进入 json-mode/json-ts-mode eglot 突然自动开启了
hook eglot-ensure 是无用的。真正起作用的是下面这条:
(add-hook 'after-change-major-mode-hook #'eglot--maybe-activate-editing-mode)
这造成启动一个lsp服务后,后续的buffer变更都可能会触发问题。
因为启动了一个js的lsp,而json继承自js。
lsp-bridge 吧, 丝滑, 没有这些奇奇怪怪的问题。
真正的入口就是 eglot-ensure
,如果你使用的是
(add-hook 'js-mode-hook 'eglot-ensure)
来激活eglot,因为json-mode继承自js-mode,所以eglot-ensure
也会在json-mode中执行,另外,js-mode和json-mode的lsp服务是不一样的,所以你即使启动了一个js的lsp服务,打开json文件还是会启动另一个json的lsp服务
eglot启动lsp服务后会把当前major-mode加入到eglot–major-modes这个变量
(setf (eglot--major-modes server) (eglot--ensure-list managed-modes))
同样的你所使用的方法里的eglot-current-server
也是通过查找major-mode和这个变量获取当前的lsp服务,并不会继续向上查找继承的major-mode
(cl-find major-mode
(gethash (eglot--current-project) eglot--servers-by-project)
:key #'eglot--major-modes
:test #'memq)
至于你所说的hook eglot-ensure不起作用不知道是啥情况,但我简单测试后是可以的
(defun eglot-ensure@around(oldfunc &rest args)
(unless (derived-mode-p 'json-mode)
(apply oldfunc args)))
(advice-add 'eglot-ensure :around 'eglot-ensure@around)
我的方法之所以复杂,是因为除了部分major-mode外,我还需要排除一些minor-mode,这些mode是否激活无法直接hook eglot-ensure,必须在eglot--when-live-buffer
内才能判断
我是尽量选择在其他方法调用前尽快跳出,避免过多的方法调用。
但的确有一个副作用,eglot-current-server 本身的调用比较频繁。
这个不应该 eglot 本身做处理么,是否可以提个 bug 给他
算不上bug吧。
1)它的原则应该是:子mode也能使用父mode的lsp。只是出现了,json这种纯文本的mode,居然是继承子程序语言mode这种特殊情况。
2)如果启用了json的lsp,也会避免出现这种场景,只是我自己不喜欢文本类的mode还使用lsp这种重量级后端而已。