06 Sep 2026

Emacs salmagundi, 2026-09-06

Compilation commands by project type

I while ago I added a minor mode, haskell-ng-project-mode, to my Haskell mode. The idea I had was that I could use it to add keybindings for running various tools (via compile), e.g. I bound cabal build and cabal test , p b and , p t respectively. Then I hooked in the minor mode in my haskell-ng-mode. Soon I realised it'd be nice to have those shortcuts available in dired too, so I hooked it in there as well. When I later wanted to re-run the tests after modifying a JSON file that was part of a golden test I realised that this wasn't a very nice solution at all.

After looking around a bit I found emacs-multi-compile. I removed the minor mode and add this configuration instead

(add-to-list 'multi-compile-alist
             '((haskell-ng-is-project) . (("cabal build" "cabal build -j --semaphore" (project-root (project-current)))
                                          ("cabal test" "cabal test" (project-root (project-current)))
                                          ("fourmolu" "fourmolu -i $(fd .hs$)" (project-root (project-current))))))

I've had some vague thoughts that it'd be rather easy to write something slightly more custom, but emacs-multi-compile works very well for me so I'm very happy with it.

Jumping between implementation and test, again

I wrote about this a while ago. At the time I came up with a way of bending ff-find-other-file to my will, but soon after that I ran into a case where there may be 2 "other files" – one of the Haskell projects at work put unit tests in files ending in Spec.hs and property tests in filed ending with Prop.hs. Unfortunately this is a situation that ff-find-other-file can't handle.

I ended up hacking together consult-kith to deal with it. The configuration to handle two kinds of test files looks like this

(setq-local consult-kith-alist `((,(rx (seq "Spec.hs" eol)) (".hs"))
                                 (,(rx (seq "Prop.hs" eol)) (".hs"))
                                 (,(rx (seq ".hs" eol)) ("Prop.hs" "Spec.hs")))
            consult-kith-search-directories '("src" "test"))

The project has since settled on only using Spec.hs for tests, but I'm sticking to consult-kith for now.

Tags: emacs
13 Aug 2026

Emacs salmagundi, 2026-08-13

Here's a few minor tweaks I've made to my Emacs setup recently.

Limiting enabled tree-sitter modes

After reading Rahul's post Emacs 31 Is Around the Corner: The Changes I'm Already Daily Driving I simplified my setup and turned on all the tree-sitter modes

(treesit-enabled-modes t)

I regretted that as soon as I opened a YAML file though. It turns out the yaml-ts-mode is rather unusable.1 I switched back to yaml-mode and adjusted the value of treesit-enabled-modes to include what I have use for (the change):

(treesit-enabled-modes '(bash-ts-mode
                         c++-ts-mode
                         c-or-c++-ts-mode
                         c-ts-mode
                         dockerfile-ts-mode
                         go-mod-ts-mode
                         go-ts-mode
                         go-work-ts-mode
                         json-ts-mode
                         python-ts-mode
                         rust-ts-mode
                         typescript-ts-mode))

The customization of treesit-enabled-modes contains a list of all the supported modes.

I hope someone will take pity on yaml-ts-mode and make it usable soon.

Tree-sitter grammar for Cabal files

When I began tinkering with my own treesitter-based mode for Haskell I failed to find a tree-sitter grammar for Cabal files so I wrote a rather rough one myself. My hope was that it could serve as a base, or motivation, for someone else to write a proper one. It took more than 2 years, but now it's finally happened!

I've switched my mode to use Curtis's grammar and I'll be archiving mine. (The change.)

Hello editable xref, good bye wgrep

Rahul's post also mentions that editable xref buffers. I had the same itch to have editable xref buffers, and I'd added a function to turn them into grep buffers to be able to edit them. I've now switched to using the built-in support, and removed the function.

At the same time I also decided to remove wgrep and instead use the builtin function for editing grep buffers.

Footnotes:

1

A mode for an indentation-sensitive language really ought to at least handle indentation!

Tags: emacs haskell
04 May 2026

Jumping to errors in Evil

Recently I realised that it'd be really nice if jumping to errors would store the previous location in the Evil jump list. These definitions do just that

(evil-define-motion mes/evil-goto-next-error (count)
  :jump t
  (unless (bound-and-true-p flymake-mode) (signal 'search-failed nil))
  (flymake-goto-next-error count))
(evil-define-motion mes/evil-goto-prev-error (count)
  :jump t
  (unless (bound-and-true-p flymake-mode) (signal 'search-failed nil))
  (flymake-goto-prev-error count))

and for now I've bound them to C-j and C-k (because that's what evil-collection does)

(general-def flymake-mode-map
  :states 'normal
  "C-j" 'mes/evil-goto-next-error
  "C-k" 'mes/evil-goto-prev-error)

This makes it easier to make a change, fix the errors caused by the change and then return to where I was.

Tags: emacs evil
02 May 2026

Follow-up on switching to eglot

Jan G sent me a two-part comment.

Part one

I was under the impression that when using elpaca you needed to disable use-package, and that when using elpaca-use-package, you were redefining the macro. I’m not 100% sure about this, but the documentation has an example of use-package and how it actually expands to an elpaca command.

I wouldn't know. All I can say is that it would be nice if package managers that hook into, or completely redefines use-package, would document if they deviate from the behaviour of "vanilla use-package" in some way.

Part two

Given that, use-package’s documentation is always going to be a little off, since elpaca is doing everything async. The only way I’ve found to reliably manage some dependencies is to use the elpaca-after-init hook, so they don’t even try to run until elpaca is finished loading everything.

I'd say it sometimes seems like the documentation for use-package is a little off for use-package itself 🙂

The README for Elpaca says that

Add configuration which relies on after-init-hook, emacs-startup-hook, etc to elpaca-after-init-hook so it runs after Elpaca has activated all queued packages.

but that seems like a very big hammer and as I understand it I'd have to move the whole :init block for python-mode into the hook in that case. Playing around with the various blocks for use-package isn't too time consuming and I think it's a good first thing to try.

Tags: emacs
02 May 2026

Secrets when connecting to DBs

I should have dealt with comments I got to my posts on how I deal with secrets in my work notes, here, and here. Better late than never though, I hope.

Comment from Stefano R

The first one is a link to post titled How I use :dbconnection in org files. It describes a nice way of setting sql-connection-alist based on the contents of a file, in his case ~/.pgppass.

Comment from Harald J

The other starts with a function for searching ~/.authinfo.gpg for entries of the form

machine <host>/<dbname> login <username> password <password> port <port>

and then setting sql-password-search-wallet-function and sql-password-wallet to tell sql-mode to use it

(defun my/sql-auth-source-search-wallet (wallet product user server database port)
  "Read auth source WALLET to locate the USER secret.
Sets `auth-sources' to WALLET and uses `auth-source-search' to locate the entry.
The DATABASE and SERVER are concatenated with a slash between them as the
host key."
  (when-let (results (auth-source-search :host (concat server "/" database)
                                         :user user
                                         :port (number-to-string port)))
    (when (and (= (length results) 1)
               (plist-member (car results) :secret))
      (plist-get (car results) :secret))))

(setq sql-password-search-wallet-function #'my/sql-auth-source-search-wallet)
(setq sql-password-wallet "~/.authinfo.gpg")

The value for sql-connection-alist is then as normal

(setq sql-connection-alist
  '((some-dbname (sql-product 'oracle)
                 (sql-port 1521)
                 (sql-server ...)
                 ...))

and the blocks in orgmode looks like this

SRC sql-mode :product oracle :dbconnection i3v1e-ro :results raw
SELECT to_char(sysdate, 'YYYY-MM-DD HH24:ii:ss') AS today,
       to_char(sysdate + 1, 'YYYY-MM-DD HH24:ii:ss') AS tomorrow
FROM dual;
SRC

Thoughts

Both of these feel closer to the intent of sql-mode in a way. I'll have to try using sql-connection-alist at some point.

Tags: emacs
Other posts