使用 eglot 代替 lsp-mode
LSP 是当前使用最广泛的一套协议,用于给文本编辑器提供类似 IDE 的功能,比如:自动补全、定义跳转等。对于 Emacs 来说,主要有两个实现:
- emacs-lsp/lsp-mode,主打功能丰富
- joaotavora/eglot,主打小巧精悍
笔者本人在使用 lsp-mode 多年后转到了 eglot,主要觉得 lsp 太占用内存,有很多华而不实的功能,导致使用时能明显感到卡顿。
下面是笔者在使用 lsp-mode 几天后,执行 memory-report 后的数据(完整版):
Largest Variables 1.8 GiB lsp-clients 1.8 GiB lsp--session 1.8 GiB lsp--last-active-workspaces 4.6 MiB package-archive-contents 4.5 MiB elfeed-db Largest Buffers 3.6 GiB database.rs 3.6 GiB source.rs 3.6 GiB history-backup/main.rs 1.8 GiB datafusion-5.0.0/src/logical_plan/expr.rs 1.8 GiB prometheus.rs 1.8 GiB libsqlite3-sys-0.23.2/src/error.rs
可以看到,占内存最高的变量都与 lsp-mode 有关,而且在用 lsp-mode 进行 Rust 开发时,能明显感到卡顿,根本不敢用 rust-analyzer 来进行补全,之前笔者都是用 tabnine 来进行 Rust 代码的补全,只用 lsp 来进行『查找定义』。
在替换成 eglot 后,内存使用就没有这么夸张了,用 rust-analyzer 进行补全时,之前的卡顿感没有了,和在 VSCode 中的体验无异。而且 eglot 的依赖很少,会尽量复用 Emacs 内置的模块,比如采用 flymake,而不是 flycheck,也有相关 issue 讨论如何用在 eglot 中使用 flycheck:
笔者使用 eglot 的配置如下,主要进行了下面几点的改进:
- eldoc 高度限制为 3 行,太大了影响阅读代码
- 修改高亮『当前变量』的字体,默认的不是很明显
- 修改
eldoc-documentation-functions,支持直接在 minibuffer 中显示 flymake 错误信息 - 修改 rust-analyzer 启动参数,开启所有 features,这样可以分析那些只在特定 feature 下开启的文件
- 增加 Rust 宏展开的命令,lsp-mode 默认支持,这里给出了 eglot 的实现
1(use-package eglot
2 :defer t
3 :commands (eglot-ensure my/rust-expand-macro)
4 :hook (eglot-managed-mode . my/eglot-hook)
5 :config
6 (progn
7 (defun my/eglot-hook ()
8 ;; Show flymake diagnostics first.
9 ;; https://github.com/joaotavora/eglot/discussions/898#discussioncomment-2609402
10 (setq eldoc-documentation-functions
11 (cons #'flymake-eldoc-function
12 (remove #'flymake-eldoc-function eldoc-documentation-functions))))
13
14 (setq eldoc-echo-area-use-multiline-p 3
15 eldoc-echo-area-display-truncation-message nil)
16 (set-face-attribute 'eglot-highlight-symbol-face nil
17 :background "#b3d7ff")
18
19 ;; https://github.com/joaotavora/eglot/pull/901
20 ;; 2022-05-28 后支持
21 (add-to-list 'eglot-server-programs `(rust-mode . ("rust-analyzer"
22 :initializationOptions (:cargo (:features "all")))))
23 (defun my/rust-expand-macro ()
24 "Expand macro at point, same as `lsp-rust-analyzer-expand-macro'.
25https://rust-analyzer.github.io/manual.html#expand-macro-recursively"
26 (interactive)
27 (jsonrpc-async-request
28 (eglot--current-server-or-lose)
29 :rust-analyzer/expandMacro (eglot--TextDocumentPositionParams)
30 :error-fn (lambda (msg) (error "Macro expand failed, msg:%s." msg))
31 :success-fn
32 (lambda (expanded-macro)
33 (cl-destructuring-bind (name format expansion result) expanded-macro
34 (let* ((pr (eglot--current-project))
35 (buf (get-buffer-create (format "*rust macro expansion %s*" (project-root pr)))))
36 (with-current-buffer buf
37 (let ((inhibit-read-only t))
38 (erase-buffer)
39 (insert result)
40 (rust-mode)))
41 (switch-to-buffer-other-window buf))))))
42 ))
最后,读者可以根据自身需求,通过添加 hook 的方式来自动打开 eglot:
1(add-hook 'rust-mode-hook 'eglot-ensure)收听方式

反馈
- 对节目有想法或发现内容错误?欢迎来信交流️