clojure-ts-mode is an Emacs major mode that provides font-lock (syntax
highlighting), indentation, and navigation support for the
Clojure(Script) programming language, powered by the
tree-sitter-clojure
tree-sitter grammar.
clojure-mode has served us well
for a very long time, but it suffers from a few long-standing
problems, related to
Emacs limitations baked into its design. The introduction of built-in support
for Tree-sitter in Emacs 29 presents a natural opportunity to address many of
them. Enter clojure-ts-mode, which makes use of TreeSitter to provide:
- fast, accurate and more granular font-locking
- fast indentation
- common Emacs functionality like structured navigation,
imenu(an outline of a source buffer), current form inference (used internally by various Emacs modes and utilities), etc
Working with TreeSitter is significantly easier than the legacy Emacs APIs for font-locking and
indentation, which makes it easier to contribute to clojure-ts-mode, and to improve it in general.
Keep in mind that the transition to clojure-ts-mode won't happen overnight for several reasons:
- getting to feature parity with
clojure-modewill take some time - tools that depend on
clojure-modewill need to be updated to work withclojure-ts-mode - we still need to support users of older Emacs versions that don't support Tree-sitter
That's why clojure-ts-mode is being developed independently of clojure-mode
and will one day replace it when the time is right. (e.g. 3 major Emacs version
down the road, so circa Emacs 32)
You can read more about the vision for clojure-ts-mode here.
Warning
This library is still under active development. Breaking changes should be expected.
The currently provided functionality should cover the needs of most Clojure programmers, but you can expect to encounter some bugs and missing functionality here and there.
Those will be addressed over the time, as more and more people use clojure-ts-mode.
For clojure-ts-mode to work, you need Emacs 30+ built with TreeSitter support.
To check if your Emacs supports tree sitter run the following (e.g. by using M-:):
(treesit-available-p)Additionally, you'll need to have Git and some C compiler (cc) installed and available
in your $PATH (or Emacs's exec-path), for clojure-ts-mode to be able to install the required
TreeSitter grammars automatically.
Tip
As the TreeSitter support in Emacs is still fairly new and under active development itself, for optimal results you should use the latest stable Emacs release or even the development version of Emacs. See the "Caveats" section for more on the subject.
Note
That's the recommended way to install clojure-ts-mode.
If you have git and a C compiler (cc) available on your system's PATH,
clojure-ts-mode will install the
grammars
clojure-ts-mode is available on MElPA and NonGNU ELPA. It can be installed with
(package-install 'clojure-ts-mode)Emacs 29 also includes package-vc-install, so you can run
(package-vc-install "https://github.com/clojure-emacs/clojure-ts-mode")to install this package from source.
You can install it by cloning the repository and adding it to your load path.
git clone https://github.com/clojure-emacs/clojure-ts-mode.git(add-to-list 'load-path "~/path/to/clojure-ts-mode/")Once installed, evaluate clojure-ts-mode.el and you should be ready to go.
Note
clojure-ts-mode install the required grammars automatically, so for most
people no manual actions will be required.
clojure-ts-mode makes use of two TreeSitter grammars to work properly:
- The Clojure grammar, mentioned earlier
- markdown_inline, which
will be used for docstrings if available and if
clojure-ts-use-markdown-inlineis enabled.
If you have git and a C compiler (cc) available on your system's PATH,
clojure-ts-mode will install the
grammars when you first open a Clojure file and clojure-ts-ensure-grammars is
set to t (the default).
If clojure-ts-mode fails to automatically install the grammar, you have the
option to install it manually, Please, refer to the installation instructions of
each required grammar and make sure you're install the versions expected. (see
clojure-ts-grammar-recipes for details)
To see a list of available configuration options do M-x customize-group <RET> clojure-ts.
Most configuration changes will require reverting any active clojure-ts-mode buffers.
By default, clojure-ts-mode assumes command over all buffers and file extensions previously associated with clojure-mode (and derived major modes like clojurescript-mode). To disable this remapping, set
(setopt clojure-ts-auto-remap nil)You can also use the commands clojure-ts-activate / clojure-ts-deactivate to interactively change this behavior.
clojure-ts-mode currently supports 2 different indentation strategies:
semantic, the default, which tries to match the indentation ofclojure-modeandcljfmtfixed, a simple indentation strategy outlined by Tonsky in a blog post
Set the var clojure-ts-indent-style to change it.
(setq clojure-ts-indent-style 'fixed)Tip
You can find this article comparing semantic and fixed indentation useful.
The indentation of special forms and macros with bodies is controlled via
clojure-ts-semantic-indent-rules. Nearly all special forms and built-in macros
with bodies have special indentation settings in clojure-ts-mode, which are
aligned with cljfmt indent rules. You can add/alter the indentation settings in
your personal config. Let's assume you want to indent ->> and -> like this:
(->> something
ala
bala
portokala)You can do so by putting the following in your config:
(setopt clojure-ts-semantic-indent-rules '(("->" . ((:block 1)))
("->>" . ((:block 1)))))This means that the body of the ->/->> is after the first argument.
The default set of rules is defined as
clojure-ts--semantic-indent-rules-defaults, any rule can be overridden using
customization option.
Two types of rules are supported: :block and :inner, mirroring those in
cljfmt. When a rule is defined as :block n, n represents the number of
arguments preceding the body. When a rule is defined as :inner n, each form
within the expression's body, nested n levels deep, is indented by two
spaces. These rule definitions fully reflect the cljfmt rules.
For example:
dohas a rule((:block 0)).whenhas a rule((:block 1)).defnandfnhave a rule((:inner 0)).letfnhas a rule((:block 1) (:inner 2 0)).
To highlight entire rich comment expression with the comment font face, set
(setq clojure-ts-comment-macro-font-lock-body t)By default this is nil, so that anything within a comment expression is
highlighted like regular clojure code.
Tip
You can customize the exact level of font-locking via the variables
treesit-font-lock-level (the default value is 3) and
treesit-font-lock-features-list. Check this
section
of the Emacs manual for more details.
By default markdown syntax is highlighted in the docstrings using
markdown_inline grammar. To disable this feature set
(setopt clojure-ts-use-markdown-inline nil)To make forms inside of (comment ...) forms appear as top-level forms for evaluation and navigation, set
(setq clojure-ts-toplevel-inside-comment-form t)To change the maximal line length used by M-x prog-fill-reindent-defun (also
bound to M-q by default) to reformat docstrings and comments it's possible to
customize clojure-ts-fill-paragraph variable (by default set to the value of
Emacs' fill-paragraph value).
Every new line in the docstrings is indented by
clojure-ts-docstring-fill-prefix-width number of spaces (set to 2 by default
which matches the clojure-mode settings).
If you are migrating to clojure-ts-mode note that clojure-mode is still required for cider and clj-refactor packages to work properly.
After installing the package do the following.
- Check the value of
clojure-mode-hookand copy all relevant hooks toclojure-ts-mode-hook.
(add-hook 'clojure-ts-mode-hook #'cider-mode)
(add-hook 'clojure-ts-mode-hook #'enable-paredit-mode)
(add-hook 'clojure-ts-mode-hook #'rainbow-delimiters-mode)
(add-hook 'clojure-ts-mode-hook #'clj-refactor-mode)- Update
.dir-locals.elin all of your Clojure projects to activate directory local variables inclojure-ts-mode.
((clojure-mode
(cider-clojure-cli-aliases . ":test:repl"))
(clojure-ts-mode
(cider-clojure-cli-aliases . ":test:repl")))As the TreeSitter Emacs APIs are new and keep evolving there are some
differences in the behavior of clojure-ts-mode on different Emacs versions.
Here are some notable examples:
- On Emacs 29 the parent mode is
prog-mode, but on Emacs 30+ it's bothprog-modeandclojure-mode(this is very helpful when dealing withderived-mode-pchecks) - Navigation by sexp/lists might work differently on Emacs versions lower than 31. Starting with version 31, Emacs uses TreeSitter 'things' settings, if available, to rebind some commands.
- The indentation of list elements with metadata is inconsistent with other collections. This inconsistency stems from the grammar's interpretation of nearly every definition or function call as a list. Therefore, modifying the indentation for list elements would adversely affect the indentation of numerous other forms.
As of version 0.2.x, the most obvious missing feature are the various
refactoring commands in clojure-mode.
Yes! Preliminary support for clojure-ts-mode was released in CIDER
1.14. Make sure to
grab the latest CIDER from MELPA/GitHub. Note that clojure-mode is still
needed for some APIs that haven't yet been ported to clojure-ts-mode.
For now, when you take care of the keybindings for the CIDER commands you use
and ensure cider-mode is enabled for clojure-ts-mode buffers in your config,
most functionality should already work:
(add-hook 'clojure-ts-mode-hook #'cider-mode)Check out this article for more details.
Currently, there is an open PR adding support for inf-clojure.
Copyright © 2022-2025 Danny Freeman and contributors.
Distributed under the GNU General Public License; type C-h C-c to view it.