[eglot] Add support for eglot lsp client in emacs
- Update README - Add eglot-specifics to cc, rs, py, hs removing unused lsp-mode packages when eglot is active - Add eglot-specific bindings - Add doctor warnings for debugger +lsp and +peek - Add eglot-backed lookup-handlers - Add flycheck checker using eglot for :checkers syntax users (using flycheck/flycheck#1676 and flycheck/flycheck#1592 discussion). This implementation is based on @marsam code, and uses recent Flycheck development in order to make the code smaller and easier to maintain.
This commit is contained in:
parent
f2a3dee7ff
commit
3e5b7cce3f
18 changed files with 248 additions and 47 deletions
|
@ -89,7 +89,7 @@
|
|||
|
||||
|
||||
(use-package! dap-mode
|
||||
:when (featurep! +lsp)
|
||||
:when (and (featurep! +lsp) (not (featurep! :tools lsp +eglot)))
|
||||
:hook (dap-mode . dap-tooltip-mode)
|
||||
:after lsp-mode
|
||||
:demand t
|
||||
|
|
4
modules/tools/debugger/doctor.el
Normal file
4
modules/tools/debugger/doctor.el
Normal file
|
@ -0,0 +1,4 @@
|
|||
;;; tools/debugger/doctor.el -*- lexical-binding: t; -*-
|
||||
|
||||
(when (and (featurep! +lsp) (featurep! :tools lsp +eglot))
|
||||
(warn! "+lsp flag is not compatible with :tools (lsp +eglot). Choose only one of (eglot or dap-mode) please"))
|
|
@ -56,10 +56,16 @@ As of this writing, this is the state of LSP support in Doom Emacs:
|
|||
** Module Flags
|
||||
+ =+peek= Use =lsp-ui-peek= when looking up definitions and references with
|
||||
functionality from the =:tools lookup= module.
|
||||
+ =+eglot= Use [[https://elpa.gnu.org/packages/eglot.html][Eglot]] instead of [[https://github.com/emacs-lsp/lsp-mode][LSP-mode]] to implement the LSP client in
|
||||
Emacs.
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/emacs-lsp/lsp-mode][lsp-mode]]
|
||||
+ [[https://github.com/emacs-lsp/lsp-ui][lsp-ui]]
|
||||
+ [[https://github.com/emacs-lsp/lsp-ivy][lsp-ivy]]
|
||||
+ [[https://github.com/emacs-lsp/helm-lsp][helm-lsp]]
|
||||
+ [[https://github.com/joaotavora/eglot][eglot]]
|
||||
|
||||
** Hacks
|
||||
+ ~lsp-mode~ has been modified not to automatically install missing LSP servers.
|
||||
This is done to adhere to our "Your system, your rules" mantra, which insist
|
||||
|
@ -75,15 +81,37 @@ You'll find a table that lists available language servers and how to install
|
|||
them [[https://github.com/emacs-lsp/lsp-mode#supported-languages][in the lsp-mode project README]]. The documentation of the module for your
|
||||
targeted language will contain brief instructions as well.
|
||||
|
||||
For eglot users, you can see the list of [[https://github.com/joaotavora/eglot/blob/master/README.md#connecting-to-a-server][default servers supported in the README]].
|
||||
There is also instructions to add another server easily.
|
||||
|
||||
* TODO Features
|
||||
** LSP-powered project search
|
||||
When =:completion ivy= or =:completion helm= is active, LSP is used to search a
|
||||
symbol indexed by the LSP server :
|
||||
Without the =+eglot= flag, and when =:completion ivy= or =:completion helm= is
|
||||
active, LSP is used to search a symbol indexed by the LSP server :
|
||||
| Keybind | Description |
|
||||
|-----------+-------------------------------------|
|
||||
| =SPC c j= | Jump to symbol in current workspace |
|
||||
| =SPC c J= | Jump to symbol in any workspace |
|
||||
** Differences between eglot and lsp-mode
|
||||
Entering the debate about which one to use would be useless. Doom provides an
|
||||
easy way to switch out lsp client implementations so you can test for yourself
|
||||
which one you prefer.
|
||||
|
||||
Mainly, from a code point of view, lsp-mode has a lot of custom code for UI
|
||||
(=lsp-ui-peek=, =lsp-ui-sideline=, ...), while eglot is more barebones with a
|
||||
closer integration with "more basic" emacs packages (=eldoc=, =xref=, ...).
|
||||
|
||||
* TODO Configuration
|
||||
|
||||
* TODO Troubleshooting
|
||||
** My language server is not found
|
||||
Check the entry in the [[../../../docs/faq.org][FAQ]] about "Doom can't find my executables/doesn't inherit
|
||||
the correct ~PATH~"
|
||||
** LSP/Eglot is not started automatically in my buffer
|
||||
Make sure that you added the =+lsp= flag to the language you're using too in
|
||||
your init.el :
|
||||
#+BEGIN_SRC diff
|
||||
:lang
|
||||
-python
|
||||
+(python +lsp)
|
||||
#+END_SRC
|
||||
|
|
9
modules/tools/lsp/autoload/common.el
Normal file
9
modules/tools/lsp/autoload/common.el
Normal file
|
@ -0,0 +1,9 @@
|
|||
;;; tools/lsp/autoload/common.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autodef
|
||||
(defun lsp! ()
|
||||
"Dispatch to call the currently used lsp client entrypoint"
|
||||
(interactive)
|
||||
(if (featurep! +eglot)
|
||||
(eglot-ensure)
|
||||
(lsp-deferred)))
|
19
modules/tools/lsp/autoload/eglot.el
Normal file
19
modules/tools/lsp/autoload/eglot.el
Normal file
|
@ -0,0 +1,19 @@
|
|||
;;; tools/lsp/autoload/eglot.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! +eglot)
|
||||
|
||||
;;;###autodef
|
||||
(defun set-eglot-client! (mode server-call)
|
||||
"Add SERVER-CALL list as a possible lsp server for given major MODE.
|
||||
|
||||
Example : (set-eglot-client! 'python-mode `(,(concat doom-etc-dir \"lsp/mspyls/Microsoft.Python.LanguageServer\")))"
|
||||
(when (featurep! +eglot)
|
||||
(add-to-list 'eglot-server-programs `(,mode . ,server-call))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eglot/documentation-lookup-handler ()
|
||||
"Documentation lookup handler using eglot :document/hover handler.
|
||||
|
||||
Mostly a rewrite of `eglot-help-at-point', which should be used interactively."
|
||||
(interactive)
|
||||
(eglot-help-at-point)
|
||||
(display-buffer eglot--help-buffer))
|
|
@ -1,4 +1,5 @@
|
|||
;;; feature/lsp/autoload.el -*- lexical-binding: t; -*-
|
||||
;;; tools/lsp/autoload/lsp-mode.el -*- lexical-binding: t; -*-
|
||||
;;;###if (not (featurep! +eglot))
|
||||
|
||||
;;;###autodef
|
||||
(defun set-lsp-priority! (client priority)
|
||||
|
@ -9,9 +10,6 @@
|
|||
priority)
|
||||
(error "No LSP client named %S" client)))
|
||||
|
||||
;;;###autodef
|
||||
(defalias 'lsp! #'lsp-deferred)
|
||||
|
||||
;;;###autoload
|
||||
(defun +lsp/uninstall-server (dir)
|
||||
"Delete a LSP server from `lsp-server-install-dir'."
|
|
@ -7,11 +7,34 @@ workspace buffer is closed.
|
|||
This delay prevents premature server shutdown when a user still intends on
|
||||
working on that project after closing the last buffer.")
|
||||
|
||||
;; TODO : set eglot-events-buffer-size to nil in doom-debug-mode
|
||||
|
||||
;;
|
||||
;;; Packages
|
||||
|
||||
(use-package! eglot
|
||||
:when (featurep! +eglot)
|
||||
:init
|
||||
(setq eglot-sync-connect 1
|
||||
eglot-connect-timeout 10
|
||||
eglot-autoshutdown t
|
||||
eglot-send-changes-idle-time 0.5
|
||||
;; NOTE: Do NOT set eglot-auto-display-help-buffer to t.
|
||||
;; With popup-rule! :select t, eglot will steal focus from the source code very often.
|
||||
eglot-auto-display-help-buffer nil)
|
||||
:config
|
||||
(set-popup-rule! "^\\*eglot-help" :size 0.35 :quit t :select t)
|
||||
(when (featurep! :checkers syntax)
|
||||
;; Eager loading which is okay-ish since we want eglot to feed flycheck as soon as possible.
|
||||
(load! "flycheck-eglot.el")
|
||||
(require 'flycheck-eglot))
|
||||
(set-lookup-handlers! 'eglot--managed-mode
|
||||
:documentation #'+eglot/documentation-lookup-handler
|
||||
:definition '(xref-find-definitions :async t)
|
||||
:references '(xref-find-references :async t)))
|
||||
|
||||
(use-package! lsp-mode
|
||||
:unless (featurep! +eglot)
|
||||
:commands lsp-install-server
|
||||
:init
|
||||
(setq lsp-session-file (concat doom-etc-dir "lsp-session"))
|
||||
|
@ -123,7 +146,7 @@ This also logs the resolved project root, if found, so we know where we are."
|
|||
(defun +lsp-init-flycheck-or-flymake-h ()
|
||||
"Set up flycheck-mode or flymake-mode, depending on `lsp-diagnostic-package'."
|
||||
(pcase lsp-diagnostic-package
|
||||
((or :auto 'nil) ; try flycheck, fall back to flymake
|
||||
((or :auto 'nil) ; try flycheck, fall back to flymake
|
||||
(let ((lsp-diagnostic-package
|
||||
(if (require 'flycheck nil t) :flycheck :flymake)))
|
||||
(+lsp-init-flycheck-or-flymake-h)))
|
||||
|
@ -164,6 +187,7 @@ auto-killed (which is a potentially expensive process)."
|
|||
|
||||
|
||||
(use-package! lsp-ui
|
||||
:unless (featurep! +eglot)
|
||||
:hook (lsp-mode . lsp-ui-mode)
|
||||
:config
|
||||
(setq lsp-ui-doc-max-height 8
|
||||
|
@ -185,10 +209,12 @@ auto-killed (which is a potentially expensive process)."
|
|||
|
||||
|
||||
(use-package! helm-lsp
|
||||
:unless (featurep! +eglot)
|
||||
:when (featurep! :completion helm)
|
||||
:commands helm-lsp-workspace-symbol helm-lsp-global-workspace-symbol)
|
||||
|
||||
|
||||
(use-package! lsp-ivy
|
||||
:unless (featurep! +eglot)
|
||||
:when (featurep! :completion ivy)
|
||||
:commands lsp-ivy-workspace-symbol lsp-ivy-global-workspace-symbol)
|
||||
|
|
3
modules/tools/lsp/doctor.el
Normal file
3
modules/tools/lsp/doctor.el
Normal file
|
@ -0,0 +1,3 @@
|
|||
;;; tools/lsp/doctor.el -*- lexical-binding: t; -*-
|
||||
|
||||
(assert! (not (and (featurep! +eglot) (featurep! +peek))) "+eglot and +peek flags are not compatible. Peek uses lsp-mode, while Eglot is another package altogether for LSP.")
|
57
modules/tools/lsp/flycheck-eglot.el
Normal file
57
modules/tools/lsp/flycheck-eglot.el
Normal file
|
@ -0,0 +1,57 @@
|
|||
;;; flycheck-eglot --- Hacky eglot support in flycheck -*- lexical-binding: t; -*-
|
||||
|
||||
(require 'flycheck)
|
||||
|
||||
;;; Code:
|
||||
(defun flycheck-eglot--start (checker callback)
|
||||
"Clean up errors when done.
|
||||
|
||||
CHECKER is the checker (eglot).
|
||||
CALLBACK is the function that we need to call when we are done, on all the errors."
|
||||
(cl-labels
|
||||
((flymake-diag->flycheck-err
|
||||
(diag)
|
||||
(with-current-buffer (flymake--diag-buffer diag)
|
||||
(flycheck-error-new-at-pos
|
||||
(flymake--diag-beg diag)
|
||||
(pcase (flymake--diag-type diag)
|
||||
('eglot-note 'info)
|
||||
('eglot-warning 'warning)
|
||||
('eglot-error 'error)
|
||||
(_ (error "Unknown diagnostic type, %S" diag)))
|
||||
(flymake--diag-text diag)
|
||||
:end-pos (flymake--diag-end diag)
|
||||
:checker checker
|
||||
:buffer (current-buffer)
|
||||
:filename (buffer-file-name)))))
|
||||
;; NOTE: Setting up eglot to automatically create flycheck errors for the buffer.
|
||||
(eglot-flymake-backend (lambda (flymake-diags &rest _)
|
||||
(funcall callback 'finished (mapcar #'flymake-diag->flycheck-err flymake-diags))))
|
||||
;; NOTE: Forcefully trigger a check in the buffer (function name is confusing)
|
||||
(flycheck-buffer)))
|
||||
|
||||
(defun flycheck-eglot--available-p ()
|
||||
(bound-and-true-p eglot--managed-mode))
|
||||
|
||||
(flycheck-define-generic-checker 'eglot
|
||||
"Report `eglot' diagnostics using `flycheck'."
|
||||
:start #'flycheck-eglot--start
|
||||
:predicate #'flycheck-eglot--available-p
|
||||
:modes '(prog-mode text-mode))
|
||||
|
||||
(push 'eglot flycheck-checkers)
|
||||
|
||||
(defun +doom/eglot-prefer-flycheck-h ()
|
||||
(when eglot--managed-mode
|
||||
(when-let ((current-checker (flycheck-get-checker-for-buffer)))
|
||||
(unless (equal current-checker 'eglot)
|
||||
(flycheck-add-next-checker 'eglot current-checker)))
|
||||
(flycheck-add-mode 'eglot major-mode)
|
||||
(flycheck-mode 1)
|
||||
(flymake-mode -1)))
|
||||
|
||||
(add-hook 'eglot--managed-mode-hook #'+doom/eglot-prefer-flycheck-h)
|
||||
|
||||
(provide 'flycheck-eglot)
|
||||
;;; flycheck-eglot.el ends here
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; tools/lsp/packages.el
|
||||
|
||||
(package! lsp-mode :pin "81d62d581b21d847783831e6e5ca9d3c63fe9a4d")
|
||||
(package! lsp-ui :pin "271b47cb33f11915295911f7cf8575f8a82a5e1c")
|
||||
(when (featurep! :completion ivy)
|
||||
(package! lsp-ivy :pin "dce58b5509271bbedb53ba9d0278dcb563a43977"))
|
||||
(when (featurep! :completion helm)
|
||||
(package! helm-lsp :pin "6b5ce182d7c94c62b55b8f7d0c7e643b2c30e560"))
|
||||
(if (featurep! +eglot)
|
||||
(package! eglot :pin "d99a4478a9")
|
||||
(progn
|
||||
(package! lsp-mode :pin "81d62d581b21d847783831e6e5ca9d3c63fe9a4d")
|
||||
(package! lsp-ui :pin "271b47cb33f11915295911f7cf8575f8a82a5e1c")
|
||||
(when (featurep! :completion ivy)
|
||||
(package! lsp-ivy :pin "dce58b5509271bbedb53ba9d0278dcb563a43977"))
|
||||
(when (featurep! :completion helm)
|
||||
(package! helm-lsp :pin "6b5ce182d7c94c62b55b8f7d0c7e643b2c30e560"))))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue