Chuyển từ lsp-mode sang Eglot trong GNU Emacs
(utcc.utoronto.ca)- Tổng hợp trải nghiệm di chuyển thực tế khi thay thế hoàn toàn môi trường LSP dựa trên lsp-mode vốn đang hoạt động tốt bằng Eglot, giải pháp LSP tích hợp sẵn của GNU Emacs
- So với lsp-mode, Eglot cung cấp giao diện tối giản và yên tĩnh hơn, với cấu trúc tích hợp cùng các gói ngoài như Corfu, Consult, Flycheck thông qua API Emacs Lisp tiêu chuẩn
- Phần lớn công việc chuyển đổi không nằm ở cấu hình Eglot mà tốn vào việc tìm hiểu, cấu hình và thử sai với các gói phụ trợ
- Tùy theo từng máy chủ LSP như pylsp, gopls mà cách cấu hình workspace khác nhau, và với cấu hình theo dự án thì cần dùng
.dir-locals.el - Vì Eglot được tích hợp sẵn trong GNU Emacs, về lâu dài đây là lựa chọn đáng để người dùng Emacs cân nhắc chuyển sang
Động lực chuyển đổi và ấn tượng tổng thể
- Không có lý do quá mạnh mẽ nào; sau khi chuyển sang Corfu, tác giả dùng thử Eglot rồi tiếp tục luôn
- lsp-mode + lsp-ui tạo ra giao diện bận rộn (busy) với nhiều thông tin hiển thị đồng thời, nên tác giả chuyển sang để có trải nghiệm LSP yên tĩnh hơn
- Eglot tối giản hơn lsp-mode, nhưng để có trải nghiệm đầy đủ thì vẫn cần bổ sung tính năng bằng các gói phụ
- Kết quả nhìn chung là hài lòng, và trong chế độ Go và Python các tính năng như 'tự động hoàn thành theo common prefix' hoạt động tốt hơn
- Dù vẫn có thể tinh chỉnh thêm cấu hình của lsp-ui, việc chuyển sang Eglot đã giải quyết mọi vấn đề cùng lúc
Tích hợp với các gói phụ trợ
- Corfu hoạt động cho tự động hoàn thành với nguyên cấu hình cũ, không cần thiết lập riêng
- Để có phần xem trước kiểu lsp-ui trong cross-reference, cần kết nối gói consult và đặt
xref-show-xrefs-functionthànhconsult-xref - Sau khi cân nhắc giữa Flycheck và Flymake, tác giả chọn Flycheck
- Flymake tích hợp với Eglot tốt hơn, nhưng nhìn chung tác giả vẫn thích Flycheck hơn
- Vì Eglot tự động bật
flymake-mode, cần thêmflymakevàoeglot-stay-out-ofđể vô hiệu hóa - Do global mode của flycheck-eglot không hoạt động ổn định, tác giả dùng cách tự thiết lập hook
Thiết lập key binding
- Eglot không cung cấp key binding mặc định, nên cần tự cấu hình
- Ví dụ các binding đang dùng hiện tại:
C-c r→eglot-rename,C-c o→eglot-code-action-organize-importsC-c h→eldoc,C-c a→eglot-code-actions,C-c q→eglot-code-action-quickfixC-M-<mouse-2>→eglot-code-actions-at-mouse(binding chuột để vượt qua giới hạn tích hợp của flycheck-eglot)
- Tác giả cố ý không gán
eglot-format— trong Go đã dùng gofmt của go-mode - Khi đặt
eglot-extend-to-xrefthànht, sau khi nhảy tới mục bên ngoài có thể dùng M-? để tìm các chỗ sử dụng khác trong dự án
Tự động khởi chạy máy chủ LSP
- Tài liệu chính thức của Eglot khuyến nghị khởi chạy thủ công, nhưng tác giả cấu hình để chỉ tự động khởi chạy với file cục bộ
- Định nghĩa hàm
eglot-ensure-local-onlyđể kiểm tra file từ xa bằngfile-remote-p, rồi mới gọieglot-ensure - Hạn chế của
eglot-ensure: khi một ngôn ngữ có nhiều máy chủ LSP (ví dụ Python có pylsp và ruff), nó chỉ tự chọn máy chủ mặc định; nếu muốn đổi thì phải dừng máy chủ hiện tại rồi gọieglotthủ công - Muốn chạy đồng thời nhiều máy chủ LSP thì có thể dùng trình multiplex như rassumfrassum
Khả năng truy cập Code Actions
- Dù máy chủ LSP cung cấp nhiều code actions, trong Eglot việc truy cập chúng vẫn chưa thật tiện (lsp-mode cũng tương tự)
- Máy chủ LSP chỉ trả về code actions khi có yêu cầu và chúng phụ thuộc vào vị trí cụ thể
- Eglot không có chức năng lọc trong danh sách code action dài do máy chủ gửi về, khiến danh sách trong cả Go và Python trở nên rối rắm
Cấu hình workspace cho pylsp
- Với pylsp (Python LSP server), để vô hiệu hóa các linter dựa trên style trong chẩn đoán thường trực thì cần dùng eglot-workspace-configuration
- lsp-mode cung cấp các điều khiển tiện lợi để tắt từng công cụ chẩn đoán riêng lẻ (như mccabe), còn trong Eglot thì phải tự viết workspace configuration theo định dạng JSON
- Ví dụ: vô hiệu hóa mccabe, pylint, mypy, pycodestyle bằng
:enabled :json-false - Các khóa liên quan đến mypy bị dùng lẫn giữa hai tên
pylsp_mypyvàmypy, đây là chi tiết trong phần triển khai nội bộ của pylsp - Bắt buộc phải dùng
setq-default;setqsẽ không hoạt động
Cấu hình theo dự án và .dir-locals.el
- Eglot không có cách tiện lợi để tạm thời đặt tham số máy chủ LSP theo từng dự án
- Nếu cần cấu hình riêng, cách dễ nhất là viết đúng định dạng trong file
.dir-locals.el - Với gopls (Go) và pylsp (Python), cấu trúc cấu hình hoàn toàn khác nhau, nên cần tự tìm hiểu riêng cho từng máy chủ LSP
- Muốn thay đổi cấu hình khi đang chạy thì cần tự viết hàm chuyên dụng, trong đó định nghĩa class mới bằng
dir-locals-set-class-variables, rồi gọidir-locals-set-directory-classvàeglot-signal-didChangeConfiguration - Vì Eglot chạy một máy chủ LSP cho toàn bộ dự án (cây thư mục), nên không thể cấu hình LSP theo file hay buffer, mà bắt buộc phải áp dụng theo dự án
- Nếu đặt
eglot-workspace-configurationtheo cách thông thường thì nó sẽ trở thành biến cục bộ theo buffer, nên hầu như vô dụng
Trải nghiệm Flymake vs Flycheck
- Flymake tích hợp tốt hơn với Eglot, có thể hiển thị trực tiếp đề xuất sửa lỗi từ máy chủ LSP (quickfix code action) trong menu bật lên của nút chuột 2 trên ghi chú chẩn đoán
- Flycheck chỉ đánh dấu lỗi, và có hạn chế là phải kích hoạt riêng các LSP code action
- Ban đầu tác giả chuyển sang Flymake, nhưng vì Flycheck có những điểm tốt hơn nên giữ lại cấu hình cho cả hai bên
- Tác giả của flycheck-eglot đã đề xuất một cách lách vấn đề này, nên tác giả quay lại với Flycheck
- So với Flymake, Flycheck có bộ checker lớn hơn và chuyển đổi giữa các checker cũng dễ hơn
- Tùy chọn 'hiển thị chẩn đoán ở cuối dòng' của Flymake là điểm đáng tiếc
- flycheck-inline chỉ hiển thị cảnh báo tại vị trí hiện tại, và khi cuộn không thể hiện toàn bộ cảnh báo
- Sideline + sideline-flycheck cũng có cùng hạn chế, nhưng trải nghiệm UI tốt hơn
2 bình luận
https://web.archive.org/web/20260513001754/…
Ý kiến trên Lobste.rs
Tùy ngôn ngữ, lời khuyên tự động khởi động Eglot có thể cực kỳ tệ hại. Nhiều máy chủ LSP không an toàn để dùng với mã không đáng tin cậy, và chỉ cần mở file trong một dự án Rust hoặc Elixir do kẻ tấn công kiểm soát cũng có thể khiến máy bị xâm nhập
Trừ khi đó là ngôn ngữ có máy chủ LSP được biết là an toàn, nên tránh tự động kích hoạt LSP. Nguồn: https://rust-analyzer.github.io/book/security.html
git statuscũng có thể là bề mặt tấn công: https://github.com/justinsteven/advisories/…Khác biệt là ở ví dụ trên, bản thân kho chứa phải có exploit, còn với LSP thì vấn đề cũng có thể phát sinh từ phía dependency. Dù vậy, nếu đã quen bật LSP theo thói quen thì có vẻ khó tránh việc trở nên chai lì trước các cảnh báo
rust-analyzer4GB chạy hàng phút, và tạo ra thư mụctarget/debug/hơn 1GBcargo buildcoi như cũng đã xong rồi. Tất nhiên, giữa tự động nạp LSP và lệnh do người dùng chủ động chạy vẫn có khác biệt lớn, nhưng trong sử dụng thực tế có khi khác biệt lại nhỏ hơn tưởng tượnglsp-mode, rồi thêm dự án vào danh sách cho phép. Nếu đã có hook “chạy tự động” thì chỉ cần khoảng 10 dòng vớiread-from-minibufferđể hỏi trước “bạn có tin cậy thư mục này không?”, và dùng thứ nhưprojectileđể lấy thư mục gốc thì có thể đạt được phần lớn lợi ích bảo mậtCấu hình của tôi dùng danh sách cho phép của
lsp-modenhưng xóa nó sau mỗi phiên, để mỗi lần mở lại Emacs thì phải đồng ý lại theo từng dự án. Hình như ban đầu tôi làm vậy vì hiệu năng, và có lúclsp-modekhởi chạy nhiều tiến trình trước cả khi mở một dự án cụ thể. Rủi ro bảo mật là có thật, nhưng để tạo ra một workflow ổn thì không quá khóĐiều khó chịu nhất ở Eglot là nó không phơi bày phần lớn lệnh dưới dạng hàm, mà định nghĩa chúng trên các giao diện Emacs như
xref. Khi có cả backendxrefcủaCIDERlẫnclojure-lspnhư trong Clojure, tôi thường thích phíaCIDERhơn vì nó biết trạng thái runtime thực của đoạn mã đã nạpPhân tích tĩnh của
clojure-lspđặc biệt dễ lệch đồng bộ, nhất là trong workflow REPL từ xa. Vớilsp-mode, bạn vẫn có thể gọi trực tiếp các chức năng như đi tới định nghĩa dưới dạng lệnh mà vẫn tiếp tục dùngxref, nhưng trong Eglot thì việc loại riêng một backendxrefcụ thể khá phiền. Một số lệnh khác có tronglsp-modecũng thiếu ở Eglot, dù thực ra chúng có thể được cung cấp qua các điểm tích hợp Emacs tương tự nhưxrefTôi đã thử
lsp-modemột lần, nhưng quá nhiều popup và thông báo gây rối nên xóa ngay. Eglot cho trải nghiệm LSP yên tĩnh hơn nhiềuCứ bật đó, đến khi sẵn sàng thì dùng tính năng. Cách ~cks tiếp cận từ hướng ngược lại và nhắc đến nhiều mẹo cùng lựa chọn thay thế khá thú vị
lsp-modevới khá nhiều tính năng đã tắt đi nên giao diện cũng khá yên tĩnh. Tôi từng định chuyển sang Eglot nhưng lúc đó có vẻ nó thiếu các tích hợp mà tôi muốn nên không thử thêm nữaĐiều tôi thực sự muốn tìm là một máy chủ LSP có thể xử lý kho mã rất lớn. Đây thường là giới hạn lớn nhất, và tôi từng nghĩ có lẽ phải tự làm thứ gì đó lập chỉ mục mã nguồn gần như một lần rồi tái sử dụng cho nhiều thao tác kiểu LSP, nhưng lại lo mình đang định đun sôi thêm một đại dương nữa
Trong các client LSP cho Emacs, Eglot và
lsp-modelà nổi tiếng nhất, nhưng cũng có các lựa chọn khác như lspce và lsp-bridgeTôi đã dùng Eglot hài lòng nhiều năm, nhưng nó có một giới hạn thiết kế có thể sẽ thành vấn đề lớn hơn về sau. Nó giả định mỗi buffer chỉ có một client, điều này hợp lý vào thời điểm Eglot ra đời, nhưng giờ ngày càng thường gặp các trường hợp muốn chạy nhiều máy chủ LSP trong một buffer. [Khuyến nghị] hiện tại là dùng một chương trình riêng làm bộ ghép kênh LSP
Tôi đã chuyển từ
lsp-modesang Eglot cho Python cách đây 4 ngày và khá hài lòngTôi đã công khai phiên bản tối thiểu của cấu hình hiện tại ở đây: https://discuss.afpy.org/t/configuration-emacs-minimale-en-2026/3001
Tuy vậy vẫn có chút bất tiện về hoàn thành. Ví dụ, khi nhấn
foo<tab><tab>, đôi khibasedpyrightlại tự import thứ gì đó kỳ quặc dù có symbol phù hợp ngay trong scope hiện tại, và tôi vẫn chưa tìm ra cách chỉ hoàn thành đến chuỗi chung dài nhất. Ngoài ra thì khá ổnTôi ghen tị với những người biến Emacs thành một IDE hiện đại. Tôi dùng phím tắt Emacs, nhưng dù bỏ ra 6–8 tiếng tôi vẫn không làm được để Emacs hoạt động như IDE
Trước đây tôi từng cố thiết lập theo FB Flow mà mình dùng thay TypeScript trong môi trường phát triển Linux và FreeBSD rồi bỏ cuộc, và cuối tuần trước lại bỏ cuộc khi cố dựng một môi trường Python đầy đủ tính năng trên Windows kèm tree-sitter. Có quá nhiều thứ phải cấu hình, lại còn phải tải riêng quá nhiều DLL như parser tree-sitter, nên để biến nó thành một IDE tử tế thì cần quá nhiều thứ. Giờ tôi không còn muốn đầu tư thời gian nữa, nhưng thỉnh thoảng gõ
emacs -nwở bất kỳ terminal nào và có ngay môi trường soạn thảo quen thuộc vẫn rất thíchfido-vertical-mode,which-key-mode,global-completion-preview-mode,yasnippet,eglot-ensure,basedpyrightlà đã đủ để bắt đầuNếu
basedpyrightcó trong PATH thì bạn vẫn có hoàn thành và tô sáng cú pháp tử tế mà không cần ngữ pháp tree-sitter. Đây là bản rút gọn tối thiểu từ cấu hình đầy đủ của tôi, còn cấu hình đầy đủ ở my full configdoom emacs. Việc cấu hình rất dễ và ngay ở trạng thái mặc định thì hầu hết mọi thứ đã hoạt động tốt. Nếu không thíchevil-modethì cũng có thể quay lại phím tắt Emacs