A tree-sitter grammar for the Nix expression language.
This is a numtide fork of nix-community/tree-sitter-nix, kept moving while upstream is stalled. New bug reports and PRs should be filed here. The grammar itself is fully compatible with upstream consumers (ABI 15, tree-sitter ≥ 0.26).
- Grammar —
grammar.js+ a custom C scanner for strings / paths / interpolation edge cases (src/scanner.c). - Queries —
queries/highlights.scm,injections.scm,locals.scm,tags.scm,indents.scm, all used by editors that consume tree-sitter. - Language bindings under
bindings/:c— header + pkg-config templatego— cgo wrappernode— N-API bindings + TypeScript declarationspython— CPython module with bundled queriesrust— crate exposingLANGUAGEconstswift— SwiftPM targetzig— Zig module +build.zigocaml— OCaml library + opam package (hand-written; seebindings/ocaml/README.md)
inputs.tree-sitter-nix.url = "github:numtide/tree-sitter-nix";[dependencies]
tree-sitter-nix = { git = "https://github.com/numtide/tree-sitter-nix" }
tree-sitter = ">=0.26"let mut parser = tree_sitter::Parser::new();
parser.set_language(&tree_sitter_nix::LANGUAGE.into())?;
let tree = parser.parse("{ a = 1; }", None).unwrap();npm install github:numtide/tree-sitter-nixconst Parser = require("tree-sitter");
const Nix = require("tree-sitter-nix");
const parser = new Parser();
parser.setLanguage(Nix);pip install git+https://github.com/numtide/tree-sitter-niximport tree_sitter_nix
from tree_sitter import Language, Parser
parser = Parser(Language(tree_sitter_nix.language()))
tree = parser.parse(b"{ a = 1; }")
# Queries are also bundled:
# tree_sitter_nix.HIGHLIGHTS_QUERY, .INJECTIONS_QUERY, .LOCALS_QUERY,
# .TAGS_QUERY, .INDENTS_QUERYThe grammar is automatically picked up by:
- Neovim via nvim-treesitter
(
:TSInstall nix). Our queries follow nvim-treesitter capture conventions. - Helix —
helix-editor/helixbundles the grammar. For the fork, clone into your Helix runtime'sgrammars/directory or use Nix overlays pinning this flake. - Emacs via tree-sitter-langs.
- Zed, GitHub code navigation, nixd / nil language servers — all consume the C bindings and queries directly.
Everything is managed by the Nix flake:
nix developThat gives you tree-sitter, node, cargo, formatters, and
editorconfig-checker.
# Regenerate parser after editing grammar.js
tree-sitter generate --abi 15
# Run the test suite (corpus + highlight tests)
tree-sitter test
# Run all flake checks (build, generated-diff, treefmt, bindings tests)
nix flake check
# Format everything
nix fmtAfter editing grammar.js:
- Run
tree-sitter generate --abi 15to regeneratesrc/parser.c,src/grammar.json,src/node-types.json. - Run
tree-sitter test. Add corpus cases undercorpus/for new constructs. - When in doubt about whether a construct is valid Nix, check against
the real C++ parser:
nix-instantiate --parse -E '<expression>'.
queries/*.scm files are used at load time by tree-sitter consumers.
Tests in test/highlight/basic.nix exercise highlights.scm via
tree-sitter test.
PRs welcome. Low-friction workflow:
- Fork (or branch if you have push access), make changes, run
tree-sitter testandnix flake checklocally. - Open a PR; CI runs the flake matrix across Linux (x86_64/aarch64) and macOS (x86_64/aarch64).
- Small fixes and dependency bumps auto-merge on green; larger changes get a human review.
Releases are tagged vX.Y.Z on this repo. Consumers using the git URL
will pick up changes on next flake update. Published-registry
strategy:
- Nix flake — ready to consume immediately.
- crates.io / PyPI / npm — planned under a numtide-scoped name (existing packages under the bare name are controlled by upstream). Tracking in #14.
Original grammar by Charles Strahan and maintainers of nix-community/tree-sitter-nix. This fork preserves commit authorship on all harvested contributions where possible.
MIT. See LICENSE.