I'm learning Elisp and I find Doom to be a really interesting implementation of Emacs. I really like the idea of having a set of core functions/vars/etc. I've been looking through a lot of the code to understand it, and maybe at some point figure out how to refactor a few things to create my own sort of personal twist on Doom-Emacs.
The LSP-modes for Emacs are amazing, and I think it'd be interesting to figure out how to make use of them in Doom-Emacs. Although I'm not entirely sure how to implement the base LSP packages (lsp-mode, lsp-ui, company-lsp). My questions are
Should a language-server module be under the lang, tool, or be it's own module?
How would one go about loading the configuration for each language-server for each available language, ex. how will lsp-python catch the default configurations for lsp-mode and such?
Should there be any autoloads?
Does this Doom-Emacs config.el code seem sane?
;;; lang/language-server/config.el -*- lexical-binding: t; -*-
(def-package! lsp-mode
:after (:any cc-mode
c-mode
c++-mode
objc-mode
python
haskell-mode
rust-mode
caml-mode
vue-mode
css-mode
scss-mode
sass-mode
less-mode
stylus-mode)
:config
(setq lsp-enable-eldoc t)
(setq lsp-enable-completion-at-point t))
(def-package! lsp-ui
:requires lsp-mode
:config
(with-eval-after-load "lsp-mode"
(setq lsp-ui-flycheck-enable t)
(map! :map lsp-ui-mode-map
[remap xref-find-definitions] . lsp-ui-peek-find-definitions
[remap xref-find-references] . lsp-ui-peek-find-references)
:hook
(lsp-mode . lsp-ui-mode))
(def-package! company-lsp
:after (:all lsp-mode lsp-ui)
:config
(set! :company-backend lsp-mode 'company-lsp)
(setq company-lsp-async t)
(setq company-lsp-enable-snippet t))
;;; EOF
The only issue I see is :requires lsp-mode under lsp-ui.
iirc, it means use-package will only load this package if lsp-mode has been required at the moment this use-package forms, which won't be the case since lsp-mode is lazy-loaded. I'd suggest changing that to :after lsp-mode.
Otherwise, it looks fine to me.
Actually, this needs more thought. I'll formulate a more in-depth response once I get home this evening.
I'm torn between a self-contained :lang language-server mega-module with components for each language (controlled via module flags, e.g. :lang (language-server +python +rust ...)) -- which would be mutually exclusive to the default modules, or allowing :lang language-server be foundational and let the individual language modules detect it and adjust accordingly. i.e.
(if (featurep! :lang language-server +python)
;; LSP config
;; vanilla config
)
Most of the emacs-lsp packages don't need much configuration.
Some things to note is that the lsp-css isn't in MELPA, so you'd need to do a
(package! lsp-css
:recipe (:fetcher github
:repo "emacs-lsp/lsp-css"))
and that only works with CSS, SCSS, SASS, and LESS files.
lsp-rust and lsp-haskell are the most finicky: the Rust-Language-Server really only works consistently with the stable toolchain, so the only configuration with that would be having
(setq lsp-rust-rls-command '("rustup" "run" "stable" "rls"))
somewhere. The Haskell-IDE-Engine with lsp-haskell gripes if there's not a Hoogle doc somewhere in a project folder.
Everything else works fine, all you need is a :hook (some-prog-mode . lsp-prog-enable) in a def-package! statement. It's more about the external dependencies.
So since that's the case, it wouldn't really be difficult to do either one of the ways that you mentioned of configuring Doom-Emacs to allow for language-server support
I'm torn between a self-contained :lang language-server mega-module with components for each language (controlled via module flags, e.g. :lang (language-server +python +rust ...)) -- which would be mutually exclusive to the default modules, or allowing :lang language-server be foundational and let the individual language modules detect it and adjust accordingly. i.e.
Just my two cents, but I would think the latter, i.e. having the language-server mode be foundational, and let the language specific modules be responsible for hooking into it when supported, is the best way to go. This follows the way I expect modularity to function in general, and with things like featurep!, etc., there really isn't any reason _not_ to push language-specific hooks for LSP into the lang modules as far as I can tell.
I'm also interested in LSP support in doom-emacs. Is there any work being done on this?
I would love to see LSP support baked in, although I personally think that the UI of lsp-ui is awful. I can't stand the way it injects text right into the file, rather than an overlay or minibuffer, it ends up looking messy and cluttered. If this gets included into Doom I hope there's an easy way to disable that part.
Rudimentary LSP support was added in cb923ea. Adding the +lsp flag to :lang modules that support it is enough to enable it. It still needs to be documented, but let me know if you have any suggestions on how to improve it.
tools/lsp's readme now outlines module support. I'll soon incorporate setup instructions into their respective modules, but for the time being I'll consider this issue resolved.
Note: it'd be better to read tool/lsp's readme in Emacs, as Doom's org-mode will highlight 404 links differently (easier to eyeball which modules have documentation).
Most helpful comment
Rudimentary LSP support was added in cb923ea. Adding the
+lspflag to :lang modules that support it is enough to enable it. It still needs to be documented, but let me know if you have any suggestions on how to improve it.