Tree-sitter in Guix

I’m impartial to syntax highlighting in code since it can be a source of distraction more often than not, however, I tend to use the favored major mode for the language I’m writing in Emacs paired with eglot. Lately though, this has become a pain point when working with all-things JavaScript on large files. My quick solution was to forego eglot and web-mode, which improved the editing speed tremendously.

It looks like the latter is a common reason for the slowdown when working with TSX files. I figured this is a good reason to finally get tree-sitter in my Emacs setup working as I’ve failed previously.

Tree-sitter is a parser generator tool that can build a concrete syntax tree for a source file and efficiently update the tree during edits. Syntax highlighting is one of those features that can leverage this tool. Code navigation is another, though we’ll only talk about syntax highlighting.

Syntax highlighting tools usually rely on custom regular expressions, so using Tree-sitter is faster and has better highlighting since it’s more context-aware.

Installation

I’ve had trouble installing the language grammars manually before on my Guix system, so I figured it’s better to install the packages from Guix.

I use Guix Profiles to group package installations together e.g., a default profile, an Emacs profile, a Common Lisp profile etc. and make them available on login.

In this case I’m adding the relevant packages to my default profile:

scheme code
;; guix-default-manifest.scm
;; default packages but example only includes Tree-sitter ones
(specifications->manifest
 '("tree-sitter"
   "tree-sitter-bash"
   "tree-sitter-css"
   "tree-sitter-dockerfile"
   "tree-sitter-go"
   "tree-sitter-html"
   "tree-sitter-javascript"
   "tree-sitter-json"
   "tree-sitter-python"
   "tree-sitter-typescript"))

Then I run:

bash code
guix package --manifest=/home/boogs/.package-lists/guix-default-manifest.scm --profile=/home/boogs/.guix-extra-profiles/default/default

to update my default profile to include these packages.

Emacs configuration

Now we’ve installed the tool and the language grammars. Next, we need to tweak our Emacs configuration slightly to know where to find the language grammars.

elisp code
(setq treesit-extra-load-path '("~/.guix-extra-profiles/default/default/lib/tree-sitter"))

(add-to-list 'auto-mode-alist '("\\.js\\'" . js-ts-mode))
(add-to-list 'auto-mode-alist '("\\.json\\'" . json-ts-mode))
(add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode))
(add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode))
(add-to-list 'auto-mode-alist '("\\.html\\'" . html-ts-mode))
(add-to-list 'auto-mode-alist '("\\.css\\'" . css-ts-mode))
(add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode))

The load path may vary depending on how you install the packages. If you install them to the current profile, then the path starts with "~/.guix-profile/lib/tree-sitter".

Conclusion

With Tree-sitter set up via Guix, editing large JavaScript, TypeScript, or TSX files in Emacs should feel snappier, thanks to efficient parsing and context-aware highlighting. This setup not only resolves slowdowns from modes like web-mode but also opens the door to advanced features like structural editing.

If you encounter issues (e.g., missing grammars or incompatible Emacs versions), check the Tree-sitter GitHub repository or the Guix Codeberg repository. Happy hacking!