2019-02-21 16:08:27 -05:00
|
|
|
;;; tools/lsp/config.el -*- lexical-binding: t; -*-
|
|
|
|
|
2019-08-15 14:59:53 -04:00
|
|
|
(defvar +lsp-company-backend 'company-lsp
|
2020-02-29 19:08:55 -05:00
|
|
|
"What backend to use for lsp-driven autocompletion.
|
|
|
|
|
|
|
|
This can be overridden by `+lsp-capf-blacklist'.
|
|
|
|
|
|
|
|
While `company-capf' does not require the `company-lsp' package and should offer
|
|
|
|
better performance, it has been integrated into lsp only recently and as of
|
|
|
|
02/25/2020 is known to cause issues with some language servers. If you wish to
|
|
|
|
use `company-capf' in general but fall back to `company-lsp' for specific
|
|
|
|
language servers, set `+lsp-company-backend' to `company-capf' and add the
|
2020-02-23 16:37:16 +01:00
|
|
|
excluded servers' identifiers to `+lsp-capf-blacklist'.")
|
2019-08-15 14:59:53 -04:00
|
|
|
|
2020-02-23 16:37:16 +01:00
|
|
|
(defvar +lsp-capf-blacklist '(ts-ls gopls)
|
|
|
|
"Language servers listed here will always use the `company-lsp' backend,
|
|
|
|
irrespective of what `+lsp-company-backend' is set to.")
|
2019-08-15 14:59:53 -04:00
|
|
|
|
2019-10-21 19:01:06 -04:00
|
|
|
;;
|
|
|
|
;;; Packages
|
|
|
|
|
|
|
|
(use-package! lsp-mode
|
2020-01-10 04:42:54 -05:00
|
|
|
:commands lsp-install-server
|
2019-10-21 19:01:06 -04:00
|
|
|
:init
|
|
|
|
(setq lsp-session-file (concat doom-etc-dir "lsp-session"))
|
|
|
|
;; Don't prompt the user for the project root every time we open a new
|
|
|
|
;; lsp-worthy file, instead, try to guess it with projectile.
|
|
|
|
(setq lsp-auto-guess-root t)
|
|
|
|
;; Auto-kill LSP server once you've killed the last buffer associated with its
|
|
|
|
;; project.
|
|
|
|
(setq lsp-keep-workspace-alive nil)
|
2020-02-29 19:08:55 -05:00
|
|
|
;; 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-groovy-server-install-dir (concat lsp-server-install-dir "lsp-groovy/")
|
|
|
|
lsp-intelephense-storage-path (concat doom-cache-dir "lsp-intelephense/"))
|
|
|
|
|
|
|
|
:config
|
|
|
|
(set-lookup-handlers! 'lsp-mode :async t
|
|
|
|
:documentation #'lsp-describe-thing-at-point
|
|
|
|
:definition #'lsp-find-definition
|
|
|
|
:references #'lsp-find-references)
|
|
|
|
|
|
|
|
(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)))
|
|
|
|
(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))))))
|
|
|
|
|
2020-02-23 16:37:16 +01:00
|
|
|
(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)
|
|
|
|
(let ((preferred-backend +lsp-company-backend))
|
|
|
|
(lsp-foreach-workspace
|
|
|
|
(when (memq (lsp--client-server-id (lsp--workspace-client lsp--cur-workspace))
|
|
|
|
+lsp-capf-blacklist)
|
|
|
|
(setq preferred-backend 'company-lsp)))
|
|
|
|
(if (eq 'company-capf preferred-backend)
|
|
|
|
;; use capf backend
|
|
|
|
(progn
|
|
|
|
(setq-local lsp-enable-completion-at-point t)
|
|
|
|
(setq-local lsp-prefer-capf t)
|
|
|
|
(setq-local company-backends
|
|
|
|
(cons 'company-capf (remq 'company-capf company-backends))))
|
|
|
|
;; use company-lsp backend (may need to be loaded first)
|
|
|
|
(require 'company-lsp)
|
|
|
|
(setq-local lsp-enable-completion-at-point nil)
|
|
|
|
(setq-local lsp-prefer-capf nil)
|
|
|
|
(setq-local company-backends
|
|
|
|
(cons 'company-lsp (remq 'company-capf company-backends)))
|
|
|
|
(setq-default company-lsp-cache-candidates 'auto))
|
|
|
|
(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'."
|
2020-02-29 19:08:55 -05:00
|
|
|
(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)))))))
|
2019-04-16 20:21:13 -04:00
|
|
|
|
2019-10-21 19:01:57 -04:00
|
|
|
(defvar +lsp--deferred-shutdown-timer nil)
|
2020-01-23 01:44:43 -05:00
|
|
|
(defadvice! +lsp-defer-server-shutdown-a (orig-fn &optional restart)
|
2019-10-21 19:01:57 -04:00
|
|
|
"Defer server shutdown for a few seconds.
|
|
|
|
This gives the user a chance to open other project files before the server is
|
2020-02-29 19:08:55 -05:00
|
|
|
auto-killed (which is a potentially expensive process)."
|
2019-10-21 19:01:57 -04:00
|
|
|
:around #'lsp--shutdown-workspace
|
2019-11-02 15:50:02 -04:00
|
|
|
(if (or lsp-keep-workspace-alive
|
2020-01-23 01:44:43 -05:00
|
|
|
restart)
|
2019-10-21 19:01:57 -04:00
|
|
|
(funcall orig-fn)
|
|
|
|
(when (timerp +lsp--deferred-shutdown-timer)
|
|
|
|
(cancel-timer +lsp--deferred-shutdown-timer))
|
|
|
|
(setq +lsp--deferred-shutdown-timer
|
|
|
|
(run-at-time
|
|
|
|
3 nil (lambda (workspace)
|
|
|
|
(let ((lsp--cur-workspace workspace))
|
2019-10-29 10:44:25 -04:00
|
|
|
(unless (lsp--workspace-buffers lsp--cur-workspace)
|
2019-10-21 19:01:57 -04:00
|
|
|
(funcall orig-fn))))
|
|
|
|
lsp--cur-workspace))))
|
|
|
|
|
2019-11-10 05:05:42 -05:00
|
|
|
(defadvice! +lsp-prompt-if-no-project-a (session file-name)
|
2019-11-08 00:02:50 -05:00
|
|
|
"Prompt for the project root only if no project was found."
|
2019-11-10 05:05:42 -05:00
|
|
|
:after-until #'lsp--calculate-root
|
|
|
|
(cond ((not lsp-auto-guess-root)
|
|
|
|
nil)
|
|
|
|
((cl-find-if (lambda (dir)
|
|
|
|
(and (lsp--files-same-host dir file-name)
|
|
|
|
(file-in-directory-p file-name dir)))
|
|
|
|
(lsp-session-folders-blacklist session))
|
|
|
|
nil)
|
|
|
|
((lsp--find-root-interactively session))))
|
2019-10-21 18:29:11 -04:00
|
|
|
|
2019-04-16 20:21:13 -04:00
|
|
|
;; Don't prompt to restart LSP servers while quitting Emacs
|
|
|
|
(add-hook! 'kill-emacs-hook (setq lsp-restart 'ignore)))
|
2019-02-28 05:01:40 -05:00
|
|
|
|
2019-02-24 13:49:12 -05:00
|
|
|
|
2019-07-23 12:44:03 +02:00
|
|
|
(use-package! lsp-ui
|
2019-02-21 16:08:27 -05:00
|
|
|
:hook (lsp-mode . lsp-ui-mode)
|
|
|
|
:config
|
2020-02-23 16:37:16 +01:00
|
|
|
(setq lsp-ui-doc-max-height 8
|
2019-02-21 16:08:27 -05:00
|
|
|
lsp-ui-doc-max-width 35
|
2019-05-17 02:05:33 -04:00
|
|
|
lsp-ui-sideline-ignore-duplicate t
|
2019-07-22 00:43:50 +02:00
|
|
|
;; lsp-ui-doc is redundant with and more invasive than
|
2019-05-17 02:05:33 -04:00
|
|
|
;; `+lookup/documentation'
|
2019-07-22 00:43:50 +02:00
|
|
|
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).
|
2019-09-26 14:26:08 -04:00
|
|
|
lsp-ui-sideline-show-hover nil)
|
2019-04-16 20:21:13 -04:00
|
|
|
|
2020-02-23 16:37:16 +01:00
|
|
|
(when (featurep! +peek)
|
|
|
|
(set-lookup-handlers! 'lsp-ui-mode :async t
|
|
|
|
:definition 'lsp-ui-peek-find-definitions
|
|
|
|
:references 'lsp-ui-peek-find-references)))
|
2019-12-20 00:49:27 +01:00
|
|
|
|
2020-01-10 04:42:54 -05:00
|
|
|
|
2019-12-20 00:49:27 +01:00
|
|
|
(use-package! helm-lsp
|
|
|
|
:when (featurep! :completion helm)
|
|
|
|
:commands helm-lsp-workspace-symbol helm-lsp-global-workspace-symbol)
|
|
|
|
|
2020-01-10 04:42:54 -05:00
|
|
|
|
2019-12-20 00:49:27 +01:00
|
|
|
(use-package! lsp-ivy
|
|
|
|
:when (featurep! :completion ivy)
|
|
|
|
:commands lsp-ivy-workspace-symbol lsp-ivy-global-workspace-symbol)
|