Rethink lsp defaults

+ Allow LSP to prompt to install servers. All this machinary just adds
more confusion for beginners, and at least LSP asks for your permission
before it does it.
+ Reverts lsp-enable-file-watchers and lsp-enable-indentation to their
default (enabled), hopefully to help lsp-java, lsp-dart, and lsp-clojure
users, for whom file-watchers seems to be necessary.
+ Apply GC/IPC optimizations globally, to ensure their reach. By only
setting them buffer-locally we don't have a guarantee that subprocesses
will be affected when the lsp buffer isn't focused.

Closes #3989

Co-authored-by: Eric Dallo <ercdll1337@gmail.com>
This commit is contained in:
Henrik Lissner 2020-10-11 18:13:59 -04:00
parent db07304c71
commit 22b6eaed03
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
4 changed files with 51 additions and 90 deletions

View file

@ -2,7 +2,7 @@
(use-package! eglot (use-package! eglot
:commands eglot eglot-ensure :commands eglot eglot-ensure
:hook (eglot-managed-mode . +lsp-init-optimizations-h) :hook (eglot-managed-mode . +lsp-optimization-mode)
:init :init
(setq eglot-sync-connect 1 (setq eglot-sync-connect 1
eglot-connect-timeout 10 eglot-connect-timeout 10
@ -34,11 +34,13 @@ server getting expensively restarted when reverting buffers."
(letf! (defun eglot-shutdown (server) (letf! (defun eglot-shutdown (server)
(if (or (null +lsp-defer-shutdown) (if (or (null +lsp-defer-shutdown)
(eq +lsp-defer-shutdown 0)) (eq +lsp-defer-shutdown 0))
(funcall eglot-shutdown server) (prog1 (funcall eglot-shutdown server)
(+lsp-optimization-mode -1))
(run-at-time (run-at-time
(if (numberp +lsp-defer-shutdown) +lsp-defer-shutdown 3) (if (numberp +lsp-defer-shutdown) +lsp-defer-shutdown 3)
nil (lambda (server) nil (lambda (server)
(unless (eglot--managed-buffers server) (unless (eglot--managed-buffers server)
(funcall eglot-shutdown server))) (prog1 (funcall eglot-shutdown server)
(+lsp-optimization-mode -1))))
server))) server)))
(funcall orig-fn server)))) (funcall orig-fn server))))

View file

@ -4,13 +4,6 @@
"The backends to prepend to `company-backends' in `lsp-mode' buffers. "The backends to prepend to `company-backends' in `lsp-mode' buffers.
Can be a list of backends; accepts any value `company-backends' accepts.") Can be a list of backends; accepts any value `company-backends' accepts.")
(defvar +lsp-auto-install-servers nil
"If non-nil, automatically install LSP servers.
`lsp-mode' will prompt you to install an LSP server when you enter a major mode
with LSP support but no available server. I believe changes to your system
should be a deliberate act (as is flipping this variable).")
;; ;;
;;; Packages ;;; Packages
@ -18,30 +11,27 @@ should be a deliberate act (as is flipping this variable).")
(use-package! lsp-mode (use-package! lsp-mode
:commands lsp-install-server :commands lsp-install-server
:init :init
(setq lsp-session-file (concat doom-etc-dir "lsp-session")) ;; Don't touch ~/.emacs.d, which could be purged without warning
;; For `lsp-clients' (setq lsp-session-file (concat doom-etc-dir "lsp-session")
(setq lsp-server-install-dir (concat doom-etc-dir "lsp/")) lsp-server-install-dir (concat doom-etc-dir "lsp/"))
;; Don't auto-kill LSP server after last workspace buffer is killed, because I ;; Don't auto-kill LSP server after last workspace buffer is killed, because I
;; will do it for you, after `+lsp-defer-shutdown' seconds. ;; will do it for you, after `+lsp-defer-shutdown' seconds.
(setq lsp-keep-workspace-alive nil) (setq lsp-keep-workspace-alive nil)
;; Let doom bind the lsp keymap.
(when (featurep! :config default +bindings)
(setq lsp-keymap-prefix nil))
;; NOTE I tweak LSP's defaults in order to make its more expensive or imposing ;; NOTE I tweak LSP's defaults in order to make its more expensive or imposing
;; features opt-in. Some servers implement these poorly and, in most ;; features opt-in. Some servers implement these poorly and, in most
;; cases, it's safer to rely on Emacs' native mechanisms (eldoc vs ;; cases, it's safer to rely on Emacs' native mechanisms (eldoc vs
;; lsp-ui-doc, open in popup vs sideline, etc). ;; lsp-ui-doc, open in popup vs sideline, etc).
;; Disable features that have great potential to be slow. ;; Disable features that have great potential to be slow.
(setq lsp-enable-file-watchers nil (setq lsp-enable-folding nil
lsp-enable-folding nil
lsp-enable-text-document-color nil) lsp-enable-text-document-color nil)
;; Reduce unexpected modifications to code
(setq lsp-enable-on-type-formatting nil)
;; Disable features that modify our code without our permission. ;; Let doom bind the lsp keymap.
(setq lsp-enable-indentation nil (when (featurep! :config default +bindings)
lsp-enable-on-type-formatting nil) (setq lsp-keymap-prefix nil))
:config :config
(pushnew! doom-debug-variables 'lsp-log-io 'lsp-print-performance) (pushnew! doom-debug-variables 'lsp-log-io 'lsp-print-performance)
@ -62,43 +52,6 @@ should be a deliberate act (as is flipping this variable).")
:type-definition #'lsp-find-type-definition :type-definition #'lsp-find-type-definition
:references #'lsp-find-references) :references #'lsp-find-references)
(when lsp-auto-configure
(mapc (lambda (package) (require package nil t))
(cl-remove-if #'featurep lsp-client-packages)))
(defadvice! +lsp--dont-auto-install-servers-a (orig-fn &rest args)
"Replace auto-install behavior with warning and support indirect buffers."
:around #'lsp
(if +lsp-auto-install-servers
(apply orig-fn args)
(letf! ((buffer-file-name
;; Add support for indirect buffers (org src or capture buffers)
(or buffer-file-name
(buffer-file-name (buffer-base-buffer))))
;; Already loaded them. No need to do so again.
(lsp-client-packages nil)
;; `lsp' is normally eager to automatically install LSP servers, or
;; prompting to do so, but (in my opinion) server installation
;; should be a deliberate act by the end-user:
(defun lsp--completing-read (msg clients &rest _)
(lsp--warn (concat msg "%s\n"
" Use `M-x lsp-install-server' to install a supported server, or read "
"https://emacs-lsp.github.io/lsp-mode/page/languages for more.")
(mapcar #'lsp--client-server-id clients))
(throw 'not-installed nil)))
(catch 'not-installed
(apply orig-fn args)))))
(defadvice! +lsp--respect-user-defined-checkers-a (orig-fn &rest args)
"Set up flycheck-mode or flymake-mode, depending on `lsp-diagnostic-package'."
:around #'lsp-diagnostics--flycheck-enable
(if flycheck-checker
;; Respect file/dir/explicit user-defined `flycheck-checker'.
(let ((old-checker flycheck-checker))
(apply orig-fn args)
(setq-local flycheck-checker old-checker))
(apply orig-fn args)))
(add-hook! 'lsp-mode-hook (add-hook! 'lsp-mode-hook
(defun +lsp-display-guessed-project-root-h () (defun +lsp-display-guessed-project-root-h ()
"Log what LSP things is the root of the current project." "Log what LSP things is the root of the current project."
@ -107,7 +60,7 @@ should be a deliberate act (as is flipping this variable).")
(if-let (root (lsp--calculate-root (lsp-session) path)) (if-let (root (lsp--calculate-root (lsp-session) path))
(lsp--info "Guessed project root is %s" (abbreviate-file-name root)) (lsp--info "Guessed project root is %s" (abbreviate-file-name root))
(lsp--info "Could not guess project root.")))) (lsp--info "Could not guess project root."))))
#'+lsp-init-optimizations-h) #'+lsp-optimization-mode)
(add-hook! 'lsp-completion-mode-hook (add-hook! 'lsp-completion-mode-hook
(defun +lsp-init-company-backends-h () (defun +lsp-init-company-backends-h ()
@ -128,7 +81,8 @@ server getting expensively restarted when reverting buffers."
restart restart
(null +lsp-defer-shutdown) (null +lsp-defer-shutdown)
(= +lsp-defer-shutdown 0)) (= +lsp-defer-shutdown 0))
(funcall orig-fn restart) (prog1 (funcall orig-fn restart)
(+lsp-optimization-mode -1))
(when (timerp +lsp--deferred-shutdown-timer) (when (timerp +lsp--deferred-shutdown-timer)
(cancel-timer +lsp--deferred-shutdown-timer)) (cancel-timer +lsp--deferred-shutdown-timer))
(setq +lsp--deferred-shutdown-timer (setq +lsp--deferred-shutdown-timer
@ -137,7 +91,8 @@ server getting expensively restarted when reverting buffers."
nil (lambda (workspace) nil (lambda (workspace)
(let ((lsp--cur-workspace workspace)) (let ((lsp--cur-workspace workspace))
(unless (lsp--workspace-buffers lsp--cur-workspace) (unless (lsp--workspace-buffers lsp--cur-workspace)
(funcall orig-fn)))) (funcall orig-fn)
(+lsp-optimization-mode -1))))
lsp--cur-workspace))))) lsp--cur-workspace)))))

View file

@ -7,7 +7,6 @@
- [[#description][Description]] - [[#description][Description]]
- [[#module-flags][Module Flags]] - [[#module-flags][Module Flags]]
- [[#plugins][Plugins]] - [[#plugins][Plugins]]
- [[#hacks][Hacks]]
- [[#prerequisites][Prerequisites]] - [[#prerequisites][Prerequisites]]
- [[#features][Features]] - [[#features][Features]]
- [[#lsp-powered-project-search][LSP-powered project search]] - [[#lsp-powered-project-search][LSP-powered project search]]
@ -70,23 +69,17 @@ As of this writing, this is the state of LSP support in Doom Emacs:
+ [[https://github.com/emacs-lsp/helm-lsp][helm-lsp]] + [[https://github.com/emacs-lsp/helm-lsp][helm-lsp]]
+ [[https://github.com/joaotavora/eglot][eglot]] + [[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
that it is better etiquette to let the user decide when their development
environment is modified. Use ~M-x lsp-install-server~ to install LSP servers
manually.
* Prerequisites * Prerequisites
This module has no direct prerequisites, but major-modes require you to install This module has no direct prerequisites, but different languages will need
language servers. different language servers, which =lsp-mode= will prompt you to auto-install.
=eglot= will not.
You'll find a table that lists available language servers and how to install A table that lists available language servers and how to install them can be
them [[https://github.com/emacs-lsp/lsp-mode#supported-languages][in the lsp-mode project README]]. The documentation of the module for your found [[https://emacs-lsp.github.io/lsp-mode/page/languages/][on the lsp-mode project README]]. The documentation of the module for your
targeted language will contain brief instructions as well. 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]]. For eglot users, a list of [[https://github.com/joaotavora/eglot/blob/master/README.md#connecting-to-a-server][default servers supported is on Eglot's README]],
There is also instructions to add another server easily. including instructions to register your own.
* TODO Features * TODO Features
** LSP-powered project search ** LSP-powered project search

View file

@ -12,20 +12,31 @@ killing and opening many LSP/eglot-powered buffers.")
;; ;;
;;; Common ;;; Common
(defun +lsp-init-optimizations-h () (defvar +lsp--default-read-process-output-max nil)
"Deploys universal optimizations for `lsp-mode' and `eglot'." (defvar +lsp--default-gcmh-high-cons-threshold nil)
(when (or (bound-and-true-p eglot--managed-mode)
(bound-and-true-p lsp-mode)) (define-minor-mode +lsp-optimization-mode
;; `read-process-output-max' is only available on recent development "Deploys universal GC and IPC optimizations for `lsp-mode' and `eglot'."
;; builds of Emacs 27 and above. :global t
(setq-local read-process-output-max (* 1024 1024)) :init-value nil
;; REVIEW LSP causes a lot of allocations, with or without Emacs 27+'s (if +lsp-optimization-mode
;; native JSON library, so we up the GC threshold to stave off (progn
;; GC-induced slowdowns/freezes. Doom uses `gcmh' to enforce its GC (setq +lsp--default-read-process-output-max
;; strategy, so we modify its variables rather than (default-value 'read-process-output-max)
;; `gc-cons-threshold' directly. +lsp--default-gcmh-high-cons-threshold
(setq-local gcmh-high-cons-threshold (* 2 (default-value 'gcmh-high-cons-threshold))) (default-value 'gcmh-high-cons-threshold))
(gcmh-set-high-threshold))) ;; `read-process-output-max' is only available on recent development
;; builds of Emacs 27 and above.
(setq-default 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. Doom uses `gcmh' to enforce its
;; GC strategy, so we modify its variables rather than
;; `gc-cons-threshold' directly.
(setq-default gcmh-high-cons-threshold (* 2 +lsp--default-gcmh-high-cons-threshold)))
(setq-default read-process-output-max +lsp--default-read-process-output-max
gcmh-high-cons-threshold +lsp--default-gcmh-high-cons-threshold))
(gcmh-set-high-threshold))
;; ;;