16 Nov 2023

Using the golang mode shipped with Emacs

A few weeks ago I wanted to try out tree-sitter and switched a few of the modes I use for coding to their -ts-mode variants. Based on the excellent How to Get Started with Tree-Sitter I added bits like this to the setup I have for coding modes:1

(use-package X-mode
  :init
  (add-to-list 'treesit-language-source-alist '(X "https://github.com/tree-sitter/tree-sitter-X"))
  ;; (treesit-install-language-grammar 'X)
  (add-to-list 'major-mode-remap-alist '(X-mode . X-ts-mode))
  ;; ...
  )

I then manually evaluated the expression that's commented out to download and compile the tree-sitter grammar. It's a rather small change, it works, and I can switch over language by language. I swapped a couple of languages to the tree-sitter modes like this, including golang. The only mode that I noticed changes in was golang, in particular my adding of gofmt-before-save to before-save-hook had stopped having any effect.

What I hadn't realised was that the go-mode I was using didn't ship with Emacs and that when I switched to go-ts-mode I switched to one that was. It turns out that gofmt-before-save is hard-wired to work only in go-mode, something others have noticed.

I don't feel like waiting for go-mode to fix that though, especially not when there's a perfectly fine golang mode shipping with Emacs now, and not when emacs-reformatter make it so easy to define formatters (as I've written about before).

My golang setup, sans keybindings, now looks like this:2

(use-package go-ts-mode
  :hook
  (go-ts-mode . lsp-deferred)
  (go-ts-mode . go-format-on-save-mode)
  :init
  (add-to-list 'treesit-language-source-alist '(go "https://github.com/tree-sitter/tree-sitter-go"))
  (add-to-list 'treesit-language-source-alist '(gomod "https://github.com/camdencheek/tree-sitter-go-mod"))
  ;; (dolist (lang '(go gomod)) (treesit-install-language-grammar lang))
  (add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode))
  (add-to-list 'auto-mode-alist '("/go\\.mod\\'" . go-mod-ts-mode))
  :config
  (reformatter-define go-format
    :program "goimports"
    :args '("/dev/stdin"))
  :general
  ;; ...
  )

So far I'm happy with the built-in go-ts-mode and I've got to say that using a minor mode for the format-on-save functionality is more elegant than adding a function to before-save-hook (something that go-mode may get through this PR).

Footnotes:

1

There were a few more things that I needed to modify. As the tree-sitter modes are completely separate from the non-tree-sitter modes things like hooks and keybindings in the modes' keymaps.

2

The full file is here.

Tags: emacs tree-sitter
Comment here.