Open-source project
renzmann/treesit-auto avatar
renzmann/treesit-auto

treesit-auto makes Emacs 29 switch to tree-sitter modes and compile the grammar if needed

Automatic installation, usage, and fallback for tree-sitter major modes in Emacs 29

526 stars40 forksEmacs LispGPL-3.0

At a glance

What is it?
treesit-auto is an Emacs package that closes the gap Emacs 29 left open: it remaps major modes to their tree-sitter equivalents when a grammar is installed, compiles one on demand when it is not, and falls back to the original major mode when installation is refused. Every part of that behaviour is configurable, and the whole package is one Elisp file.
Who is it for?
Adopt treesit-auto if you use Emacs 29 or later and want tree-sitter modes without maintaining a list of grammar installations yourself, because the package installs grammars on demand and falls back cleanly when a grammar is missing. Do not adopt it on Emacs 28 or older, since the package states it works with Emacs 29 and up, where treesit.el and treesit-install-language-grammar arrive.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 53 days ago.
What is it written in?
Mainly Emacs Lisp, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The gap in Emacs 29 that this package fills

The package explains the problem in a paragraph, and the reason it exists is worth reading closely.

Emacs 29, while featuring treesit.el and a convenient treesit-install-language-grammar, will not feature an intelligent way to choose between a default mode, such as python-mode, and its tree-sitter enhanced version, python-ts-mode. This package attempts to remedy that by adjusting the major-mode-remap-alist and treesit-language-source-alist variables in order to get the described behaviour.

That is the whole gap. Emacs 29 shipped tree-sitter support, but it shipped it as a set of tools you drive yourself: you install a grammar, you edit auto-mode-alist, you decide which mode wins. The package takes those three manual steps and folds them into one activation.

The author is Robb Enzmann and the description in the repository header is precise: automatic installation, usage, and fallback for tree-sitter major modes in Emacs 29. The README's own summary is the same: automatically install and use tree-sitter major modes in Emacs 29+. If the tree-sitter version can't be used, fall back to the original major mode.

The primary language is Emacs Lisp, the licence is GPL-3.0, the default branch is main, and the last push was on 2026-08-31. The recent releases are v1.0.9 and v1.0.8, both from 2026-02-10, and v1.0.5 from 2024-04-01.

The repository is small in a way that tells you the scope. There is treesit-auto.el at the root, which is the whole package, plus check_treesit_abi.py, a Python script that is presumably a check on the tree-sitter ABI, and the usual files. There is a CONTRIBUTING.org rather than a Markdown file, which is the Emacs convention, and a README.org, so the documentation is written in Org mode.

The four behaviours, in the order the package applies them

The README lays out the decision procedure as five numbered cases, and following them in order is the fastest way to understand what the package will actually do to your buffer.

The first case is the happy path. If the grammar is installed, then switch to the appropriate tree-sitter mode: assuming we open a Python buffer and the Python tree-sitter grammar is installed, then Emacs will use python-ts-mode instead of python-mode.

The second case is the one that makes the package worth having. If the grammar is NOT installed and treesit-auto-install is non-nil, then upon activating any major mode that has a corresponding tree-sitter mode, the grammar will be downloaded and compiled using treesit-install-language-grammar, and Emacs will then activate the tree-sitter major mode for that buffer. The prompt setting is like t, except a message will be displayed in the echo area asking for a yes/no response before attempting the installation.

The author's own example: if I visit a Python file and didn't already have the grammar installed, I wind up with an installed grammar and a buffer using python-ts-mode.

The third case is the fallback. If the grammar is not installed and treesit-auto-install is nil, then it will try to fall back to another major mode. Most languages will have a fallback mode specified, such as python-ts-mode falling back to python-mode. And there is a way to check what the fallback will be, by looking at the recipe for that language:

emacs-lisp
(treesit-auto-recipe-remap (alist-get 'python treesit-auto-lang-recipe-alist))
    ⇒ python-mode

The fourth case is the optional step that fixes a specific gap. If the grammar is installed but there is no fallback mode, you can optionally use the treesit-auto-add-to-auto-mode-alist function to ensure that your installed tree-sitter languages have their corresponding ...-ts-mode added to auto-mode-alist, so that Emacs opens the buffer in that mode rather than the default Fundamental mode. The README's example: supposing we don't have typescript-mode installed, then even if we do have typescript-ts-mode installed along with the typescript grammar compiled in ~/.emacs.d/tree-sitter/, Emacs still won't use typescript-ts-mode unless you also added the typescript-ts-mode entry for the ts extension to auto-mode-alist.

The fifth case is the general one. If the grammar is not installed, treesit-auto-install is nil, and no fallback mode is specified in the language recipe, then you still gain the benefit of quickly installing grammars through treesit-install-language-grammar without having to build the recipe interactively, but treesit-auto will make no attempt to switch away from the tree-sitter mode.

That last case is the subtle one, because it explains a behaviour people find confusing: the package does not force tree-sitter modes, it only removes the reasons not to use them.

Installing it, and the configuration the author actually uses

treesit-auto is available from MELPA, and the README walks through the setup in the standard order. After following MELPA's own setup, you can use your preferred package manager. If the default is package.el, simply run M-x package-refresh-contents and then:

emacs-lisp
M-x package-install RET treesit-auto

If you want a local clone of the repository rather than just a copy of the source, you might instead use package-vc-install:

emacs-lisp
M-x package-vc-install RET https://github.com/renzmann/treesit-auto.git

Then, in your Emacs configuration file, ~/.emacs.d/init.el:

emacs-lisp
  (use-package treesit-auto
    :config
    (global-treesit-auto-mode))

For most users, the README says this will be enough.

The author's own configuration adds two things:

emacs-lisp
  (use-package treesit-auto
    :custom
    (treesit-auto-install 'prompt)
    :config
    (treesit-auto-add-to-auto-mode-alist 'all)
    (global-treesit-auto-mode))

The first is the install policy, set to prompt rather than the default, so that a grammar is only compiled after a yes or no in the echo area. The second is the auto-mode-alist fix from case four, applied to all languages rather than per language.

There is also a convenience function, M-x treesit-auto-install-all, which will install all or a selected subset of the maintained and compatible grammars. That is the bulk path for someone setting up a new machine: install the package, run the function once, and the grammars are there for every language in the recipe list.

The naming convention for the file path matters in one place. The example recipes reference ~/.emacs.d/tree-sitter/ as the location where a compiled grammar lives, and the package installation puts the grammars there, so that path is what tree-sitter itself expects.

One ordering constraint is stated explicitly and is easy to get wrong. If you have modified treesit-language-source-alist through setq, then it is recommended to put any configuration of this package AFTER that setq. The package adjusts treesit-language-source-alist itself, so a later setq of that variable would undo the adjustment, and the symptom would be that tree-sitter modes silently stop being selected.

Where treesit-auto is the wrong tool

Three limits, and the first is about the requirement it imposes on you.

The first is that a tree-sitter mode needs a compiled grammar, and compiling one needs a working toolchain. The package handles downloading and compiling through treesit-install-language-grammar, but it cannot remove the need for a C compiler and the tree-sitter CLI on the machine. So the package removes a configuration chore and replaces it with an environment prerequisite, which is a good trade on a developer machine and a poor one on a locked-down one.

The second is that the behaviour is Emacs-version-bound in a specific way. The package works with Emacs 29 and up, and the reason is that treesit.el and treesit-install-language-grammar arrived there. On Emacs 28 or older there is nothing to abstract, and the package is simply the wrong tool. For an organisation still standardising on 28, that is a hard constraint rather than a preference.

The third is that the fallback story is a per-language table and you inherit it. The README says most languages will have a fallback mode specified, and the example shows how to check the one for a language. But the table is maintained by the package author, and a fallback you did not choose is a fallback you did not choose. If your workflow depends on a particular mode for a particular language, the fallback for that language becomes something you have to verify rather than something you can rely on.

The alternative approach is to do the mapping by hand, which is what the package's own README describes as the pre-Emacs-29 approach: set auto-mode-alist yourself, and set major-mode-remap-alist to make a tree-sitter mode take precedence over its plain counterpart. That is two setq forms and a list of languages, and it has the advantage that nothing happens behind your back. The difference in approach is that the manual version asks you to maintain a list that grows as you install more grammars, while treesit-auto derives the mapping from what is actually installed, which is why it also solves the reverse problem of a tree-sitter mode existing with no grammar behind it.

A third option is to not use tree-sitter modes at all, which is a legitimate position. The tree-sitter major modes give better syntactic awareness, but the standard modes are predictable, and in an Emacs configuration shared across a team, agreeing on which mode opens which file is itself a feature that a per-machine automatic mapping can complicate.

The recipe list, the language filter and the ABI check script

Three details in the repository explain how the package keeps itself out of the way, and each is worth knowing about before you change the configuration.

The first is the recipe list. The behaviour for each language lives in a recipe, and the README refers to treesit-auto-recipe-list and treesit-auto-lang-recipe-alist. That structure is what makes the fallback modes per-language data rather than hard-coded logic, and it is why the fourth case in the decision procedure can even exist: the recipe knows the fallback for a language, or knows that there is none.

The second is the language filter. You can globally alter the behavior of treesit-auto to only consider a specific set of languages by setting the treesit-auto-langs list to a set of language symbols. By default, this list includes every possible language that treesit-auto supports, so you can use M-x describe-variable RET treesit-auto-langs to see what the options are. Setting it manually looks like this:

emacs-lisp
  (setq treesit-auto-langs '(python rust go))

After that, treesit-auto features will only ever affect Python, Rust and Go files, running treesit-auto-install-all will only install these three grammars, and no automatic prompting or installation will occur for the others. The README's description of that last point is cut off in the text available here, but the effect is clear from the two sentences before it.

This filter is more useful than it first appears. The package decides whether to install a grammar by asking whether the language has a corresponding tree-sitter mode, and a language can have a tree-sitter mode in Emacs without having a maintained grammar available. Narrowing the list to the languages you actually work in is the cheapest way to stop the package from doing something you did not expect on a file type you rarely open.

The third is check_treesit_abi.py, the Python script in the repository root next to the Elisp file. Its name says what it does: it checks the tree-sitter ABI. The ABI is the version number that the tree-sitter runtime and the generated parsers have to agree on, and a mismatch between them produces a grammar that loads and then misbehaves. A check script in the repository is the project acknowledging that this is a real failure mode, and it is worth running after a tree-sitter upgrade rather than discovering the problem through a broken syntax highlighting.

The repository also has a .bumpversion.cfg, so the version number is managed by bumpversion rather than by hand, which is why the release tags v1.0.8 and v1.0.9 landed minutes apart in February 2026 without a corresponding code change between them.

Editorial conclusion

Adopt treesit-auto if you use Emacs 29 or later and want tree-sitter modes without maintaining a list of grammar installations yourself, because the package installs grammars on demand and falls back cleanly when a grammar is missing. Do not adopt it on Emacs 28 or older, since the package states it works with Emacs 29 and up, where treesit.el and treesit-install-language-grammar arrive. Verify first that you have a C compiler and the tree-sitter CLI available, because installing a grammar means compiling it, that the fallback modes in the recipe list match what you want for each language, since a wrong fallback silently changes your editing experience, and that you put any treesit-auto configuration after your own treesit-language-source-alist changes, which the README explicitly recommends.

Frequently asked questions

What does treesit-auto do in Emacs?

It automatically switches to a name-ts-mode when the grammar for that name is installed, installs a grammar on demand before opening a compatible file, and falls back to the original major mode when the grammar is missing or installation is refused. It does this by adjusting major-mode-remap-alist and treesit-language-source-alist.

Which Emacs versions does treesit-auto support?

Emacs 29 and later. The package exists because Emacs 29 shipped treesit.el and treesit-install-language-grammar without an intelligent way to choose between a default mode and its tree-sitter enhanced version.

How do I install treesit-auto?

It is available from MELPA. With package.el, run M-x package-refresh-contents and then M-x package-install RET treesit-auto. For a local clone instead of a copy of the source, use M-x package-vc-install RET https://github.com/renzmann/treesit-auto.git.

What is the difference between treesit-auto-install being t and prompt?

Both install a missing grammar automatically, but prompt is like t except that a message is displayed in the echo area asking for a yes or no response before the installation is attempted. When the value is nil, no installation happens and the package falls back to another major mode.

What does treesit-auto-add-to-auto-mode-alist do?

It ensures your installed tree-sitter languages have their corresponding ...-ts-mode added to auto-mode-alist, so Emacs opens a buffer in that mode rather than the default Fundamental mode. It is needed because a tree-sitter mode alone is not enough when the plain mode is absent.

How can I check which fallback mode treesit-auto will use for a language?

Look at the recipe for that language with (treesit-auto-recipe-remap (alist-get 'python treesit-auto-lang-recipe-alist)), which returns the fallback mode, python-mode in the example. You can also restrict which languages the package considers with the treesit-auto-langs variable.

Official sources

  1. Issues
  2. License: GPL-3.0
  3. README
  4. Releases
  5. renzmann/treesit-auto on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/renzmann-treesit-auto.svg)](https://hysenlabs.com/projects/renzmann-treesit-auto)

Was this useful?