elisp-tree-sitter binds tree-sitter to Emacs through a Rust dynamic module
Emacs Lisp bindings for tree-sitter
At a glance
- What is it?
- elisp-tree-sitter gives Emacs a tree-sitter parser so that packages can work on code structurally rather than by regular expression, and it does so with a Rust dynamic module plus Lisp bindings and a set of language grammars. Its main competitive point is uncomfortable for the project itself, because the README tells Emacs 29 users to use the built-in integration instead.
- Who is it for?
- elisp-tree-sitter fits a developer on Emacs 25 through 28 who wants structural parsing for highlighting, folding, structural editing or imenu indexing, and who is prepared to build a dynamic module. Skip it on Emacs 29 and later, because the README itself points you at the built-in integration, and skip it if you only want to write packages in Lisp without touching Rust.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 55 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 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Structural parsing for packages that do not know the language
The README states the purpose plainly. This is an Emacs Lisp binding for tree-sitter, described as an incremental parsing library, and it requires Emacs 25.1 or above built with dynamic module support.
What that buys is a parser that produces a syntax tree, incrementally, so an editor can ask what a node is rather than guessing from a regular expression. The README frames the project as the foundation for a new breed of Emacs packages that understand code structurally, and it lists four examples.
Faster, fine-grained code highlighting is first, because a parser knows which characters are inside a string literal or a comment. More flexible code folding is second, since the foldable units are the tree's nodes rather than the indentation heuristic. Third comes structural editing, like Paredit or even better, for non-Lisp code, which is the case Emacs has never had a good answer for. Fourth is more informative indexing for imenu, which today falls back on regular expressions and therefore mis-describes many languages.
Those four are the reason to care, and they are the reason the project is worth reading even where it is no longer the recommended option.
The licence is MIT with the text in the `LICENSE` file, the primary language is Emacs Lisp, and the default branch is `master`. The release history shows 0.19.1 and 0.19.3 both on 2025-12-22 and 0.19.4 on 2026-01-16, with the last push on 2026-09-01, so the project is still being worked on rather than abandoned.
The README tells Emacs 29 users to use something else
The first line of the README after the badges is the most important sentence in the document. For Emacs 29 and later, it says to use the built-in integration instead of this package, and it links the Emacs development mailing list thread where that integration was announced.
That is worth dwelling on, because it changes how you should read everything else here. This is not a project that is losing to a competitor. It is a project that won, in the sense that its approach was adopted upstream, and the upstream version is now the supported path for new users.
The alternatives section says the same thing in a different way, listing tree-sitter in Emacs core as the future, and a separate project, a binding through C instead of Rust, as dormant.
So the honest evaluation of elisp-tree-sitter today has two branches. For a user on Emacs 29 or later, the answer is the built-in integration and this repository is a reference. For a user on Emacs 25.1 through 28, or for someone working on the binding itself, or for someone who needs the specific features this implementation has, it remains a working option, and the README is candid about that rather than quietly hoping nobody notices.
The dynamic module requirement is the other practical boundary. This package needs an Emacs built with module support, which narrows the set of Emacs builds it will run on, and the Windows note in the development instructions, that you should use Powershell rather than Bash or cmd.exe, is a small sign of how much of the toolchain assumes a Unix-like shell.
The development setup splits into three layers
The README separates hacking on the project into layers, and the split tells you where each kind of change belongs.
The base setup applies to working on the package at all. Clone the repository with the `--recursive` flag, add three of its directories to the load path, which are `core/`, `lisp/` and `langs/`, install the Eask package management tooling, and run `bin/setup`.
If you only want to work on the high level features, which are the ones written in Lisp, you make your changes in the `.el` files, add tests to `tree-sitter-tests.el`, and run them with `bin/test`. That is the fastest loop in the project and it needs neither Rust nor Node.
Building the grammars is the next layer, and it is where the tree-sitter toolchain comes in. NodeJS is needed to generate the grammar code from the JavaScript DSL, with volta recommended as the tool to manage NodeJS versions, and you need the tree-sitter CLI tool, which the README gives as a pinned version.
# For yarn user
yarn global add [email protected]
# For npm user
npm install -g [email protected]With that installed, you compile the grammar you want.
langs/script/compile rustThe last layer is the dynamic module itself, which needs the Rust toolchain, and the Makefile exposes the same operations as named targets so you can read what each one does.
build: ## Build the bindings
@./bin/buildwatch: ## Continuous testing (requires cargo-watch)
@./bin/test watchGrammars are fetched on demand, and the Makefile says so
There is a target in the Makefile that explains how grammars reach your machine, and it is a per language pattern rather than a fixed list.
ensure/%: ## Download grammar for a given language
@./bin/ensure-lang $*The target takes the language name as a wildcard, calls a script that downloads that grammar, and then prints the two lines of Lisp you need in your session to load it, which are a require of `tree-sitter` and a call to `tree-sitter-require` with the language name. The build target does the same thing for the load path, printing the `add-to-list` calls for the `lisp` and `langs` directories.
The printing is a small design decision with a real effect. These are generated commands that depend on where your checkout lives, so the Makefile cannot write them into a file for you, and echoing them is the honest way to hand over a path-dependent instruction. A `help` target lists everything, using the same convention of a comment on each target.
The repository layout supports the same split. There is a `core/` directory, which is the part that carries the dynamic module, a `lisp/` directory for the bindings and the higher level features, a `langs` entry for the grammars, a `bin/` directory with the scripts the Makefile calls, a `tests/` directory, a `doc/` directory for the documentation source, an `Eask` file for the package dependencies, a `Makefile`, a `CHANGELOG.md` and a `.gitmodules` file, which is the submodule mechanism behind the recursive clone.
One more practical detail from the README: to test against a different version of Emacs you set the `EMACS` environment variable, with the example given as running the tests against a snap-installed Emacs.
Where language work belongs, and who to ask
The contribution section is short and draws a line that is worth reading before you file anything.
Contributions are welcomed, the README says, and points at the issue list for ideas or at creating a new issue to describe an improvement. But the second sentence narrows it: for language-specific issues and features, you should check out the separate tree-sitter-langs repository instead.
That is a sensible division, and it mirrors the structure of the code. The grammars are a large, mostly mechanical body of work generated from the tree-sitter DSLs, with a steady stream of upstream language changes, and the Emacs binding is the part that needs deep knowledge of how Emacs packages, dynamic modules and the load path work. Splitting the two means the language work does not have to wait for someone who understands the Emacs side.
The closing note about showing respect and empathy towards others, with both technical empathy and general empathy described as highly valued, is the kind of sentence that says more about a project's culture than its test suite does.
The result is a project that is small in scope, careful in its boundaries, and explicit about where it stands. It found its way into the Emacs core, and this repository documents the version of the idea that came before that, along with the code to run it on the Emacs versions that predate it.
Editorial conclusion
elisp-tree-sitter fits a developer on Emacs 25 through 28 who wants structural parsing for highlighting, folding, structural editing or imenu indexing, and who is prepared to build a dynamic module. Skip it on Emacs 29 and later, because the README itself points you at the built-in integration, and skip it if you only want to write packages in Lisp without touching Rust. Verify first which Emacs version you are on, that your platform has dynamic module support, and that the grammar you need is available before you plan an editor package on top of it.
Frequently asked questions
Should I use elisp-tree-sitter on a recent Emacs?
No. The README states that for Emacs 29 and later you should use the built-in integration instead of this package, and its alternatives section calls tree-sitter in Emacs core the future. This package is the option for Emacs 25.1 through 28, and for working on the binding itself.
What does elisp-tree-sitter require to build the grammars?
NodeJS to generate the grammar code from the JavaScript DSL, with volta recommended as the Node manager, and the tree-sitter CLI tool, which the README installs as `[email protected]` through yarn or npm. Once those are in place you compile a language with `langs/script/compile rust`.
How do I set up elisp-tree-sitter for development?
Clone the repository with the `--recursive` flag, add `core/`, `lisp/` and `langs/` to the load path, install the eask tooling, and run `bin/setup`. To work only on the Lisp level, change the `.el` files, add tests to `tree-sitter-tests.el` and run them with `bin/test`.
Where should I report a language-specific problem in elisp-tree-sitter?
The README says that for language-specific issues and features you should use the separate tree-sitter-langs repository instead, and it asks people to check the issue list or open a new issue for anything else. The project also asks contributors to show respect and empathy towards others.
How do I test elisp-tree-sitter against a different Emacs version?
You set the `EMACS` environment variable before running the tests, with the README giving the example of pointing it at a snap-installed Emacs. The Makefile wraps the same operations in the `build`, `test` and `watch` targets, and the continuous mode needs cargo-watch.
Official sources
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.
[](https://hysenlabs.com/projects/emacs-tree-sitter-elisp-tree-sitter)Was this useful?