More on the switch to eglot
Since the switching to eglot I've ended up making a few related changes.
Replacing flycheck with flymake
Since eglot it's written to work with other packages in core, which means it
integrates with flymake. The switch comprised
- Use
:ensure nilto make sureelpacaknows there's nothing to download. - Add a call to
flymake-modetoprog-mode-hook. - Define two functions to toggle showing a list of diagnostics for the current buffer and the project.
- Redefine the relevant keybindings.
The two functions for toggling showing diagnostics look like this
(defun mes/toggle-flymake-buffer-diagnostics ()
(interactive)
(if-let* ((window (get-buffer-window (flymake--diagnostics-buffer-name))))
(save-selected-window (quit-window nil window))
(flymake-show-buffer-diagnostics)))
(defun mes/toggle-flymake-project-diagnostics ()
(interactive)
(if-let* ((window (get-buffer-window (flymake--project-diagnostics-buffer (projectile-project-root)))))
(save-selected-window (quit-window nil window))
(flymake-show-project-diagnostics)))
And the changed keybindings are
| flycheck | flymake |
|---|---|
| flycheck-next-error | flymake-goto-next-error |
| flycheck-previous-error | flymake-goto-prev-error |
| mes/toggle-flycheck-error-list | mes/toggle-flymake-buffer-diagnostics |
| mes/toggle-flycheck-projectile-error-list | mes/toggle-flymake-project-diagnostics |
Using with-eval-after-load instead of :after eglot
When it comes to use-package I keep on being surprised, and after the switch
to elpaca I've found some new surprises. One of them was that using :after
eglot like this
(use-package haskell-ng-mode
:afer eglot
:ensure (:type git
:repo "git@gitlab.com:magus/haskell-ng-mode.git"
:branch "main")
:init
(add-to-list 'major-mode-remap-alist '(haskell-mode . haskell-ng-mode))
(add-to-list 'eglot-server-programs '(haskell-ng-mode "haskell-language-server-wrapper" "--lsp"))
(setq-default eglot-workspace-configuration
(plist-put eglot-workspace-configuration
:haskell
'(:formattingProvider "fourmolu"
:plugin (:stan (:global-on :json-false)))))
...
:hook
(haskell-ng-mode . eglot-ensure)
...)
would delay initialisation until after eglot had been loaded. However, it
turned out that nothing in :init ... seemed to run and upon opening a haskell file
no mode was loaded.
After a bit of thinking and tinkering I got it working by removing :after
eglot and using with-eval-after-load
(use-package haskell-ng-mode
:ensure (:type git
:repo "git@gitlab.com:magus/haskell-ng-mode.git"
:branch "main")
:init
(add-to-list 'major-mode-remap-alist '(haskell-mode . haskell-ng-mode))
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs '(haskell-ng-mode "haskell-language-server-wrapper" "--lsp"))
(setq-default eglot-workspace-configuration
(plist-put eglot-workspace-configuration
:haskell
'(:formattingProvider "fourmolu"
:plugin (:stan (:global-on :json-false))))))
...
:hook
(haskell-ng-mode . eglot-ensure)
...)
That change worked for haskell, and it seemed to work for python too, but after a little while I realised that python needed a bit more attention.
Getting the configuration for Python to work properly
The python setup looked like this
(use-package python
:init
(add-to-list 'major-mode-remap-alist '(python-mode . python-ts-mode))
(with-eval-after-load 'eglot
(assoc-delete-all '(python-mode python-ts-mode) eglot-server-programs)
(add-to-list 'eglot-server-programs
`((python-mode python-ts-mode) . ,(eglot-alternatives
'(("rass" "python") "pylsp")))))
...
:hook (python-ts-mode . eglot-ensure)
...)
and it worked all right, but then I visited the package (using elpaca-visit)
and realised that the downloaded package was all of emacs. That's a bit of
overkill, I'd say.
However, adding :ensure nil didn't have the expected effect of just using the
version that's in core. Instead the whole configuration seemed to never take
effect and again I was back to the situation where I had to jump to
python-ts-mode manually.
The documentation for use-package says that :init is for
Code to run before PACKAGE-NAME has been loaded.
but I'm guessing "before" isn't quite before enough. Then I noticed :preface
with the description
Code to be run before everything except
:disabled; this can be used to define functions for use in:if, or that should be seen by the byte-compiler.
and yes, "before everything" is early enough. The final python configuration looks like this
(use-package python
:ensure nil
:preface
(add-to-list 'major-mode-remap-alist '(python-mode . python-ts-mode))
:init
(with-eval-after-load 'eglot
(assoc-delete-all '(python-mode python-ts-mode) eglot-server-programs)
(add-to-list 'eglot-server-programs
`((python-mode python-ts-mode) . ,(eglot-alternatives
'(("rass" "python") "pylsp")))))
...
:hook (python-ts-mode . eglot-ensure)
...)
Closing remark
I'm still not sure I have the correct intuition about how to use use-package,
but hopefully it's more correct now than before. I have a growing suspicion
that use-package changes behaviour based on the package manager I use. Or
maybe it's just that some package managers make use-package more forgiving of
bad use.