;;; tools/lsp/config.el -*- lexical-binding: t; -*- (defvar +lsp-company-backend 'company-lsp "What backend to prepend to `company-backends' when `lsp-mode' is active. This can be a single company backend or a list thereof. It can be anything `company-backends' will accept.") ;; ;;; Packages (use-package! lsp-mode :defer t :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) ;; For `lsp-clients' (setq lsp-fsharp-server-install-dir (concat doom-etc-dir "lsp-fsharp/") lsp-groovy-server-install-dir (concat doom-etc-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) (defvar +lsp--deferred-shutdown-timer nil) (defadvice! +lsp-defer-server-shutdown-a (orig-fn) "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 usually an expensive process)." :around #'lsp--shutdown-workspace (if (or lsp-keep-workspace-alive (eq (lsp--workspace-shutdown-action lsp--cur-workspace) 'restart)) (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)) (unless (lsp--workspace-buffers lsp--cur-workspace) (funcall orig-fn)))) lsp--cur-workspace)))) (defadvice! +lsp-prompt-if-no-project-a (session file-name) "Prompt for the project root only if no project was found." :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)))) (defadvice! +lsp-init-a (&optional arg) "Enable `lsp-mode' in the current buffer. Meant to be a lighter alternative to `lsp', which is too eager about initializing lsp-ui-mode, company, yasnippet and flycheck. Instead, these have been moved out to their respective modules, or these hooks: + `+lsp-init-company-h' (on `lsp-mode-hook') + `+lsp-init-ui-flycheck-or-flymake-h' (on `lsp-ui-mode-hook') Also logs the resolved project root, if found." :override #'lsp (interactive "P") (require 'lsp-mode) (when lsp-auto-configure (require 'lsp-clients)) (and (buffer-file-name) (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)))))) (prog1 (lsp-mode 1) ;; 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)))))) ;; 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) :init (add-hook! 'lsp-ui-mode-hook (defun +lsp-init-ui-flycheck-or-flymake-h () "Sets up flymake-mode or flycheck-mode, depending on `lsp-prefer-flymake'." (cond ((eq :none lsp-prefer-flymake)) (lsp-prefer-flymake (lsp--flymake-setup)) ((require 'flycheck nil t) (require 'lsp-ui-flycheck) (lsp-ui-flycheck-enable t))))) :config (setq lsp-prefer-flymake nil 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) (set-lookup-handlers! 'lsp-ui-mode :async t :definition 'lsp-ui-peek-find-definitions :references 'lsp-ui-peek-find-references)) (use-package! company-lsp :when (featurep! :completion company) :defer t :init ;; Make sure that `company-capf' is disabled since it is incompatible with ;; `company-lsp' (see lsp-mode#884) (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) (setq-local company-backends (cons +lsp-company-backend (remq 'company-capf company-backends))) (remove-hook 'company-mode-hook #'+lsp-init-company-h t)))) :config (setq company-lsp-cache-candidates 'auto)) ;; cache candidates for better performance