Merge pull request #3020 from gagbo/feature/eglot-support
Add support for eglot as LSP client implementation
This commit is contained in:
commit
75b6e11f56
24 changed files with 454 additions and 235 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"))
|
19
modules/tools/lsp/+eglot.el
Normal file
19
modules/tools/lsp/+eglot.el
Normal file
|
@ -0,0 +1,19 @@
|
|||
;;; tools/lsp/+eglot.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package! eglot
|
||||
:commands (eglot-ensure 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)
|
||||
(after! flycheck
|
||||
(load! "autoload/flycheck-eglot")))
|
||||
(set-lookup-handlers! 'eglot--managed-mode
|
||||
:documentation #'+eglot/documentation-lookup-handler))
|
183
modules/tools/lsp/+lsp.el
Normal file
183
modules/tools/lsp/+lsp.el
Normal file
|
@ -0,0 +1,183 @@
|
|||
;;; tools/lsp/+lsp.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package! lsp-mode
|
||||
:commands lsp-install-server
|
||||
:init
|
||||
(setq lsp-session-file (concat doom-etc-dir "lsp-session"))
|
||||
;; Auto-kill LSP server after last workspace buffer is killed.
|
||||
(setq lsp-keep-workspace-alive nil)
|
||||
;; Let `flycheck-check-syntax-automatically' determine this.
|
||||
(setq lsp-flycheck-live-reporting nil)
|
||||
;; For `lsp-clients'
|
||||
(setq lsp-server-install-dir (concat doom-etc-dir "lsp/")
|
||||
lsp-intelephense-storage-path (concat doom-cache-dir "lsp-intelephense/"))
|
||||
|
||||
(when (featurep! :config default +bindings)
|
||||
;; Let doom bind the lsp keymap.
|
||||
(setq lsp-keymap-prefix nil))
|
||||
|
||||
;; Disable LSP's superfluous, expensive and/or debatably unnecessary features.
|
||||
;; Some servers implement these poorly. Better to just rely on Emacs' native
|
||||
;; mechanisms and make these opt-in.
|
||||
(setq lsp-enable-folding nil
|
||||
;; HACK Fix #2911, until it is resolved upstream. Links come in
|
||||
;; asynchronously from the server, but lsp makes no effort to
|
||||
;; "select" the original buffer before laying them down, so they
|
||||
;; could be rendered in the wrong buffer (like the minibuffer).
|
||||
lsp-enable-links nil
|
||||
;; Potentially slow
|
||||
lsp-enable-file-watchers nil
|
||||
lsp-enable-text-document-color nil
|
||||
lsp-enable-semantic-highlighting nil
|
||||
;; Don't modify our code without our permission
|
||||
lsp-enable-indentation nil
|
||||
lsp-enable-on-type-formatting nil
|
||||
;; capf is the preferred completion mechanism for lsp-mode now
|
||||
lsp-prefer-capf t)
|
||||
|
||||
:config
|
||||
(set-popup-rule! "^\\*lsp-help" :size 0.35 :quit t :select t)
|
||||
(set-lookup-handlers! 'lsp-mode :async t
|
||||
:documentation #'lsp-describe-thing-at-point
|
||||
:definition #'lsp-find-definition
|
||||
:implementations #'lsp-find-implementation
|
||||
:type-definition #'lsp-find-type-definition
|
||||
:references #'lsp-find-references)
|
||||
|
||||
;; TODO Lazy load these. They don't need to be loaded all at once unless the
|
||||
;; user uses `lsp-install-server'.
|
||||
(when lsp-auto-configure
|
||||
(mapc (lambda (package) (require package nil t))
|
||||
lsp-client-packages))
|
||||
|
||||
(defadvice! +lsp-init-a (&optional arg)
|
||||
"Enable `lsp-mode' in the current buffer.
|
||||
|
||||
Meant to gimp `lsp', which is too eager about installing LSP servers, or
|
||||
prompting to do so, or complaining about no LSP servers, or initializing
|
||||
lsp-ui-mode, company, yasnippet and flycheck. We want LSP to work only if the
|
||||
server is present, and for server installation to be a deliberate act by the
|
||||
end-user. Also, setting up these other packages are handled by their respective
|
||||
modules.
|
||||
|
||||
Also see:
|
||||
+ `+lsp-init-company-h' (on `lsp-mode-hook')
|
||||
+ `+lsp-init-flycheck-or-flymake-h' (on `lsp-mode-hook')
|
||||
|
||||
This also logs the resolved project root, if found, so we know where we are."
|
||||
:override #'lsp
|
||||
(interactive "P")
|
||||
(and (buffer-file-name (buffer-base-buffer))
|
||||
(require 'lsp-mode nil t)
|
||||
(setq-local
|
||||
lsp--buffer-workspaces
|
||||
(or (lsp--try-open-in-library-workspace)
|
||||
(lsp--try-project-root-workspaces
|
||||
(equal arg '(4))
|
||||
(and arg (not (equal arg 1))))))
|
||||
;; read-process-output-max is only available on recent
|
||||
;; development builds of Emacs 27 and above
|
||||
(or (not (boundp 'read-process-output-max))
|
||||
(setq-local read-process-output-max (* 1024 1024)))
|
||||
;; REVIEW LSP causes a lot of allocations, with or without Emacs 27+'s
|
||||
;; native JSON library, so we up the GC threshold to stave off
|
||||
;; GC-induced slowdowns/freezes.
|
||||
(setq-local gcmh-high-cons-threshold (* 2 gcmh-high-cons-threshold))
|
||||
(prog1 (lsp-mode 1)
|
||||
(setq-local lsp-buffer-uri (lsp--buffer-uri))
|
||||
;; Announce what project root we're using, for diagnostic purposes
|
||||
(if-let (root (lsp--calculate-root (lsp-session) (buffer-file-name)))
|
||||
(lsp--info "Guessed project root is %s" (abbreviate-file-name root))
|
||||
(lsp--info "Could not guess project root."))
|
||||
(lsp--info "Connected to %s."
|
||||
(apply #'concat
|
||||
(mapcar
|
||||
(lambda (it) (format "[%s]" (lsp--workspace-print it)))
|
||||
lsp--buffer-workspaces))))))
|
||||
|
||||
(when (featurep! :config default +bindings)
|
||||
(dolist (leader-key (list doom-leader-key doom-leader-alt-key))
|
||||
(let ((lsp-keymap-prefix (concat leader-key " c l")))
|
||||
(lsp-enable-which-key-integration))))
|
||||
|
||||
(add-hook! 'lsp-mode-hook
|
||||
(defun +lsp-init-company-h ()
|
||||
(if (not (bound-and-true-p company-mode))
|
||||
(add-hook 'company-mode-hook #'+lsp-init-company-h t t)
|
||||
;; Ensure `company-capf' is at the front of `company-backends'
|
||||
(setq-local company-backends
|
||||
(cons 'company-capf
|
||||
(remq 'company-capf company-backends)))
|
||||
(remove-hook 'company-mode-hook #'+lsp-init-company-h t)))
|
||||
(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
|
||||
(let ((lsp-diagnostic-package
|
||||
(if (require 'flycheck nil t) :flycheck :flymake)))
|
||||
(+lsp-init-flycheck-or-flymake-h)))
|
||||
((or :flymake 't)
|
||||
(lsp--flymake-setup))
|
||||
(:flycheck
|
||||
(let ((old-checker flycheck-checker))
|
||||
(lsp-flycheck-enable)
|
||||
;; Ensure file/dir local `flycheck-checker' is respected
|
||||
(when old-checker
|
||||
(setq-local flycheck-checker old-checker)
|
||||
(kill-local-variable 'flycheck-check-syntax-automatically)))))))
|
||||
|
||||
(defvar +lsp--deferred-shutdown-timer nil)
|
||||
(defadvice! +lsp-defer-server-shutdown-a (orig-fn &optional restart)
|
||||
"Defer server shutdown for a few seconds.
|
||||
This gives the user a chance to open other project files before the server is
|
||||
auto-killed (which is a potentially expensive process)."
|
||||
:around #'lsp--shutdown-workspace
|
||||
(if (or lsp-keep-workspace-alive
|
||||
restart
|
||||
(null +lsp-defer-shutdown)
|
||||
(= +lsp-defer-shutdown 0))
|
||||
(funcall orig-fn restart)
|
||||
(when (timerp +lsp--deferred-shutdown-timer)
|
||||
(cancel-timer +lsp--deferred-shutdown-timer))
|
||||
(setq +lsp--deferred-shutdown-timer
|
||||
(run-at-time
|
||||
(if (numberp +lsp-defer-shutdown) +lsp-defer-shutdown 3)
|
||||
nil (lambda (workspace)
|
||||
(let ((lsp--cur-workspace workspace))
|
||||
(unless (lsp--workspace-buffers lsp--cur-workspace)
|
||||
(funcall orig-fn))))
|
||||
lsp--cur-workspace))))
|
||||
|
||||
;; Don't prompt to restart LSP servers while quitting Emacs
|
||||
(add-hook! 'kill-emacs-hook (setq lsp-restart 'ignore)))
|
||||
|
||||
|
||||
(use-package! lsp-ui
|
||||
:hook (lsp-mode . lsp-ui-mode)
|
||||
:config
|
||||
(setq lsp-ui-doc-max-height 8
|
||||
lsp-ui-doc-max-width 35
|
||||
lsp-ui-sideline-ignore-duplicate t
|
||||
;; lsp-ui-doc is redundant with and more invasive than
|
||||
;; `+lookup/documentation'
|
||||
lsp-ui-doc-enable nil
|
||||
;; Don't show symbol definitions in the sideline. They are pretty noisy,
|
||||
;; and there is a bug preventing Flycheck errors from being shown (the
|
||||
;; errors flash briefly and then disappear).
|
||||
lsp-ui-sideline-show-hover nil)
|
||||
|
||||
(when (featurep! +peek)
|
||||
(set-lookup-handlers! 'lsp-ui-mode :async t
|
||||
:definition 'lsp-ui-peek-find-definitions
|
||||
:implementations 'lsp-ui-peek-find-implementation
|
||||
:references 'lsp-ui-peek-find-references)))
|
||||
|
||||
|
||||
(use-package! helm-lsp
|
||||
:when (featurep! :completion helm)
|
||||
:commands helm-lsp-workspace-symbol helm-lsp-global-workspace-symbol)
|
||||
|
||||
|
||||
(use-package! lsp-ivy
|
||||
:when (featurep! :completion ivy)
|
||||
:commands lsp-ivy-workspace-symbol lsp-ivy-global-workspace-symbol)
|
|
@ -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\")))"
|
||||
(after! 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))
|
56
modules/tools/lsp/autoload/flycheck-eglot.el
Normal file
56
modules/tools/lsp/autoload/flycheck-eglot.el
Normal file
|
@ -0,0 +1,56 @@
|
|||
;;; flycheck-eglot --- Hacky eglot support in flycheck -*- lexical-binding: t; -*-
|
||||
|
||||
;;; 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)
|
||||
|
||||
;;; flycheck-eglot.el ends here
|
|
@ -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,188 +7,7 @@ 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.")
|
||||
|
||||
|
||||
;;
|
||||
;;; Packages
|
||||
|
||||
(use-package! lsp-mode
|
||||
:commands lsp-install-server
|
||||
:init
|
||||
(setq lsp-session-file (concat doom-etc-dir "lsp-session"))
|
||||
;; Auto-kill LSP server after last workspace buffer is killed.
|
||||
(setq lsp-keep-workspace-alive nil)
|
||||
;; Let `flycheck-check-syntax-automatically' determine this.
|
||||
(setq lsp-flycheck-live-reporting nil)
|
||||
;; For `lsp-clients'
|
||||
(setq lsp-server-install-dir (concat doom-etc-dir "lsp/")
|
||||
lsp-intelephense-storage-path (concat doom-cache-dir "lsp-intelephense/"))
|
||||
|
||||
(when (featurep! :config default +bindings)
|
||||
;; Let doom bind the lsp keymap.
|
||||
(setq lsp-keymap-prefix nil))
|
||||
|
||||
;; Disable LSP's superfluous, expensive and/or debatably unnecessary features.
|
||||
;; Some servers implement these poorly. Better to just rely on Emacs' native
|
||||
;; mechanisms and make these opt-in.
|
||||
(setq lsp-enable-folding nil
|
||||
;; HACK Fix #2911, until it is resolved upstream. Links come in
|
||||
;; asynchronously from the server, but lsp makes no effort to
|
||||
;; "select" the original buffer before laying them down, so they
|
||||
;; could be rendered in the wrong buffer (like the minibuffer).
|
||||
lsp-enable-links nil
|
||||
;; Potentially slow
|
||||
lsp-enable-file-watchers nil
|
||||
lsp-enable-text-document-color nil
|
||||
lsp-enable-semantic-highlighting nil
|
||||
;; Don't modify our code without our permission
|
||||
lsp-enable-indentation nil
|
||||
lsp-enable-on-type-formatting nil
|
||||
;; capf is the preferred completion mechanism for lsp-mode now
|
||||
lsp-prefer-capf t)
|
||||
|
||||
:config
|
||||
(set-popup-rule! "^\\*lsp-help" :size 0.35 :quit t :select t)
|
||||
(set-lookup-handlers! 'lsp-mode :async t
|
||||
:documentation #'lsp-describe-thing-at-point
|
||||
:definition #'lsp-find-definition
|
||||
:implementations #'lsp-find-implementation
|
||||
:type-definition #'lsp-find-type-definition
|
||||
:references #'lsp-find-references)
|
||||
|
||||
;; TODO Lazy load these. They don't need to be loaded all at once unless the
|
||||
;; user uses `lsp-install-server'.
|
||||
(when lsp-auto-configure
|
||||
(mapc (lambda (package) (require package nil t))
|
||||
lsp-client-packages))
|
||||
|
||||
(defadvice! +lsp-init-a (&optional arg)
|
||||
"Enable `lsp-mode' in the current buffer.
|
||||
|
||||
Meant to gimp `lsp', which is too eager about installing LSP servers, or
|
||||
prompting to do so, or complaining about no LSP servers, or initializing
|
||||
lsp-ui-mode, company, yasnippet and flycheck. We want LSP to work only if the
|
||||
server is present, and for server installation to be a deliberate act by the
|
||||
end-user. Also, setting up these other packages are handled by their respective
|
||||
modules.
|
||||
|
||||
Also see:
|
||||
+ `+lsp-init-company-h' (on `lsp-mode-hook')
|
||||
+ `+lsp-init-flycheck-or-flymake-h' (on `lsp-mode-hook')
|
||||
|
||||
This also logs the resolved project root, if found, so we know where we are."
|
||||
:override #'lsp
|
||||
(interactive "P")
|
||||
(and (buffer-file-name (buffer-base-buffer))
|
||||
(require 'lsp-mode nil t)
|
||||
(setq-local
|
||||
lsp--buffer-workspaces
|
||||
(or (lsp--try-open-in-library-workspace)
|
||||
(lsp--try-project-root-workspaces
|
||||
(equal arg '(4))
|
||||
(and arg (not (equal arg 1))))))
|
||||
;; read-process-output-max is only available on recent
|
||||
;; development builds of Emacs 27 and above
|
||||
(or (not (boundp 'read-process-output-max))
|
||||
(setq-local read-process-output-max (* 1024 1024)))
|
||||
;; REVIEW LSP causes a lot of allocations, with or without Emacs 27+'s
|
||||
;; native JSON library, so we up the GC threshold to stave off
|
||||
;; GC-induced slowdowns/freezes.
|
||||
(setq-local gcmh-high-cons-threshold (* 2 gcmh-high-cons-threshold))
|
||||
(prog1 (lsp-mode 1)
|
||||
(setq-local lsp-buffer-uri (lsp--buffer-uri))
|
||||
;; Announce what project root we're using, for diagnostic purposes
|
||||
(if-let (root (lsp--calculate-root (lsp-session) (buffer-file-name)))
|
||||
(lsp--info "Guessed project root is %s" (abbreviate-file-name root))
|
||||
(lsp--info "Could not guess project root."))
|
||||
(lsp--info "Connected to %s."
|
||||
(apply #'concat
|
||||
(mapcar
|
||||
(lambda (it) (format "[%s]" (lsp--workspace-print it)))
|
||||
lsp--buffer-workspaces))))))
|
||||
|
||||
(when (featurep! :config default +bindings)
|
||||
(dolist (leader-key (list doom-leader-key doom-leader-alt-key))
|
||||
(let ((lsp-keymap-prefix (concat leader-key " c l")))
|
||||
(lsp-enable-which-key-integration))))
|
||||
|
||||
(add-hook! 'lsp-mode-hook
|
||||
(defun +lsp-init-company-h ()
|
||||
(if (not (bound-and-true-p company-mode))
|
||||
(add-hook 'company-mode-hook #'+lsp-init-company-h t t)
|
||||
;; Ensure `company-capf' is at the front of `company-backends'
|
||||
(setq-local company-backends
|
||||
(cons 'company-capf
|
||||
(remq 'company-capf company-backends)))
|
||||
(remove-hook 'company-mode-hook #'+lsp-init-company-h t)))
|
||||
(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
|
||||
(let ((lsp-diagnostic-package
|
||||
(if (require 'flycheck nil t) :flycheck :flymake)))
|
||||
(+lsp-init-flycheck-or-flymake-h)))
|
||||
((or :flymake 't)
|
||||
(lsp--flymake-setup))
|
||||
(:flycheck
|
||||
(let ((old-checker flycheck-checker))
|
||||
(lsp-flycheck-enable)
|
||||
;; Ensure file/dir local `flycheck-checker' is respected
|
||||
(when old-checker
|
||||
(setq-local flycheck-checker old-checker)
|
||||
(kill-local-variable 'flycheck-check-syntax-automatically)))))))
|
||||
|
||||
(defvar +lsp--deferred-shutdown-timer nil)
|
||||
(defadvice! +lsp-defer-server-shutdown-a (orig-fn &optional restart)
|
||||
"Defer server shutdown for a few seconds.
|
||||
This gives the user a chance to open other project files before the server is
|
||||
auto-killed (which is a potentially expensive process)."
|
||||
:around #'lsp--shutdown-workspace
|
||||
(if (or lsp-keep-workspace-alive
|
||||
restart
|
||||
(null +lsp-defer-shutdown)
|
||||
(= +lsp-defer-shutdown 0))
|
||||
(funcall orig-fn restart)
|
||||
(when (timerp +lsp--deferred-shutdown-timer)
|
||||
(cancel-timer +lsp--deferred-shutdown-timer))
|
||||
(setq +lsp--deferred-shutdown-timer
|
||||
(run-at-time
|
||||
(if (numberp +lsp-defer-shutdown) +lsp-defer-shutdown 3)
|
||||
nil (lambda (workspace)
|
||||
(let ((lsp--cur-workspace workspace))
|
||||
(unless (lsp--workspace-buffers lsp--cur-workspace)
|
||||
(funcall orig-fn))))
|
||||
lsp--cur-workspace))))
|
||||
|
||||
;; Don't prompt to restart LSP servers while quitting Emacs
|
||||
(add-hook! 'kill-emacs-hook (setq lsp-restart 'ignore)))
|
||||
|
||||
|
||||
(use-package! lsp-ui
|
||||
:hook (lsp-mode . lsp-ui-mode)
|
||||
:config
|
||||
(setq lsp-ui-doc-max-height 8
|
||||
lsp-ui-doc-max-width 35
|
||||
lsp-ui-sideline-ignore-duplicate t
|
||||
;; lsp-ui-doc is redundant with and more invasive than
|
||||
;; `+lookup/documentation'
|
||||
lsp-ui-doc-enable nil
|
||||
;; Don't show symbol definitions in the sideline. They are pretty noisy,
|
||||
;; and there is a bug preventing Flycheck errors from being shown (the
|
||||
;; errors flash briefly and then disappear).
|
||||
lsp-ui-sideline-show-hover nil)
|
||||
|
||||
(when (featurep! +peek)
|
||||
(set-lookup-handlers! 'lsp-ui-mode :async t
|
||||
:definition 'lsp-ui-peek-find-definitions
|
||||
:implementations 'lsp-ui-peek-find-implementation
|
||||
:references 'lsp-ui-peek-find-references)))
|
||||
|
||||
|
||||
(use-package! helm-lsp
|
||||
:when (featurep! :completion helm)
|
||||
:commands helm-lsp-workspace-symbol helm-lsp-global-workspace-symbol)
|
||||
|
||||
|
||||
(use-package! lsp-ivy
|
||||
:when (featurep! :completion ivy)
|
||||
:commands lsp-ivy-workspace-symbol lsp-ivy-global-workspace-symbol)
|
||||
;; TODO : set eglot-events-buffer-size to nil in doom-debug-mode
|
||||
(if (featurep! +eglot)
|
||||
(load! "+eglot")
|
||||
(load! "+lsp"))
|
||||
|
|
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.")
|
|
@ -1,9 +1,11 @@
|
|||
;; -*- 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")
|
||||
(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