jsmestad / treesit-auto

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

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

treesit-auto

GNU Emacs

https://melpa.org/packages/treesit-auto-badge.svg https://stable.melpa.org/packages/treesit-auto-badge.svg

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.

Features

Each of these behaviors are configurable and documented under the “Configuration” section. By activating global-treesit-auto-mode, Emacs will:

  • Automatically switch to <name>-ts-mode when the grammar for <name> is installed
  • Stick with <name>-mode if the grammar isn’t installed
  • (Optional) automatically install a grammar before opening a compatible file type

There is also a convenience function M-x treesit-auto-install-all, which will install all of the maintained and compatible grammars.

Installation

treesit-auto is available from MELPA. After following their setup, you can use your preferred package manager. If that’s the default package.el, simply M-x package-refresh-contents and then

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

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

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

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

There are some nifty things you might want to enable, though, which are covered in the “Configuration” section below.

What this package does

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 following behavior:

1. If the grammar is installed, then switch to the appropriate tree-sitter mode:

In this case, 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.

2. The grammar is NOT installed and treesit-auto-install is non-nil:

When the grammar is not installed and treesit-auto-install is t, 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. Emacs will then activate the tree-sitter major mode for that buffer.

prompt is like t, except a message will be displayed in the echo area asking for a yes/no response before attempting the installation.

As an example for both cases: 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.

Otherwise, when treesit-auto-install is nil, it will try to fall back to another major mode as described in the following two rules.

3. If the grammar is NOT installed, and the user has specified a fallback

The customizable variable treesit-auto-fallback-alist lets you pick the fallback modes by name. For instance, if we apply this:

(add-to-list 'treesit-auto-fallback-alist '(toml-ts-mode . conf-toml-mode))

Then, when the TOML grammar is missing, Emacs will use conf-toml-mode, instead of trying to fall back to toml-mode.

4. All other cases…

This is the most general case, where the grammar is not installed, treesit-auto-install is nil, no fallback mode is specified in treesit-auto-fallback-alist, and an similarly named base mode exists. Here, treesit-auto will switch to the non-tree-sitter major mode sharing a common prefix.

For example, if the Go tree-sitter grammar is not installed, but we have installed go-mode, then Emacs will use that instead of go-ts-mode, since they share the same go- prefix.

Configuration

If you have modified treesit-language-source-alist through setq, then it is recommended to put any configuration of this package AFTER that setq.

Automatically install grammars if they are missing

The treesit-auto-install variable controls whether a grammar should be installed automatically when activating a major mode compatible with tree-sitter.

  1. nil, the default, means treesit-auto won’t try to install anything, and will rely on the fallback logic outlined above
  2. t means treesit-auto should always try to clone and install a grammar when missing
  3. prompt will cause a yes/no prompt to appear in the minibuffer before attempting installation
(setq treesit-auto-install 'prompt)

Then, supposing I don’t have libtree-sitter-python.so (or its mac/Windows equivalent) under ~/.emacs.d/tree-sitter (or anywhere else in treesit-extra-load-path), visiting a Python file or calling M-x python-ts-mode will generate this prompt:

Tree-sitter grammar for python is missing.  Would you like to install it from https://github.com/tree-sitter/tree-sitter-python? (yes/no)

Responding with “yes” will use treesit-install-langauge-grammar to go fetch and compile the missing grammar.

The other function that respects this variable is treesit-auto-install-all. When treesit-auto-install is t, using M-x treesit-auto-install-all will skip all prompts. Otherwise, it will ask before attempting the installation.

When major mode names don’t match

Not all default major modes make sense to bump up to a similar tree-sitter mode. For example, when I open a .sh file, my intent is nearly always to use it with Bash. This is not the case for everyone, though, so by default this package will not replace sh-mode with bash-ts-mode. If you do want such a remap, simply include a line like this before calling treesit-auto-apply-remap:

(add-to-list 'treesit-auto-fallback-alist '(bash-ts-mode . sh-mode))

Keep track of your hooks

This package does not modify any of your major mode hooks. That is, if you have functions in python-mode-hook, but not in python-ts-mode-hook, then your hook from python-mode will not be applied, assuming python-ts-mode is what gets loaded. For major modes in which this is a concern, the current recommendation is to address this as part of your configuration.

(setq rust-ts-mode-hook rust-mode-hook)

Some modes have a shared base, such as python-ts-mode and python-mode both deriving from python-base-mode. For these languages, you can opt to hook into python-base-mode-hook instead of explicitly setting the tree-sitter mode’s hook.

Full example

This is how I configure treesit-auto for my own personal use.

(use-package treesit-auto
  :demand t
  :config
  (add-to-list 'treesit-auto-fallback-alist '(bash-ts-mode . sh-mode))
  (setq treesit-auto-install 'prompt)
  (global-treesit-auto-mode))

Caveats

This package is, admittedly, a hack. treesit.el provides an excellent foundation to incremental source code parsing for Emacs 29, and over time that foundation will expand into an improved core editing experience. With that in mind, I fully expect this package to eventually be obsolesced by the default options in Emacs 30 and beyond. That does not preclude us from adding a few quality of life improvements to Emacs 29, though, and so it still seems prudent to have this plugin available in the meantime.

Contributing

Bug reports, feature requests, and contributions are most welcome. Even though this is a small project, there is always room for improvement. I also appreciate “nitpicky” contributions, such as formatting, conventions, variable naming, code simplification, and improvements to language in documentation.

Issues are tracked on GitHub, which is also where patches and pull requests should be submitted.

About

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

License:GNU General Public License v3.0


Languages

Language:Emacs Lisp 100.0%