Languages & grammars
A grammar is a registry/<lang>/ directory holding grammar.wasm + tags.scm +
manifest.json. They load at runtime — grove init fetches what your project
needs, or fetch explicitly:
grove languages # what's installed locally
grove fetch # install all grammars from the hosted registry
grove lock # write grove.lock pinning version + wasm sha256
The hosted registry (Entelligentsia/grove-registry)
carries all 27 official tree-sitter grammars (c, cpp, c#, go, java, js, ts, tsx,
python, ruby, rust, php, scala, ocaml, ql, and more); grove fetch installs them.
The grove repo itself ships a small 3-language dev stub. Adding a language is
dropping a registry/<lang>/ directory in — the binary doesn’t change, and that
includes the full surface: the manifest.json carries a profile (container /
function / identifier node kinds) that drives parent grouping, callers’
enclosing-function, and go-to-def, so nothing language-specific is compiled in.
// registry/<lang>/manifest.json
{ "name": "go", "version": "…", "extensions": ["go"],
"profile": {
"function_kinds": ["function_declaration", "method_declaration"],
"containers": [["type_declaration", "name"]],
"identifier_kinds": ["identifier", "field_identifier"]
} }
Build a grammar’s wasm with tree-sitter build --wasm (emits the dylink.0
module the native runtime needs).
Where grammars live
Grammars are a cache — reconstructible from the hosted registry and
content-addressed by grove.lock — so the standard home is the OS-native cache
location. grove resolves the registry root by precedence (first existing wins);
grove registry shows it:
GROVE_REGISTRYenv var — explicit override (CI, tests, air-gapped).<project>/.grove/grammars/— project-vendored grammars (commit them for hermetic / offline builds), found by walking up from the cwd.- OS user cache — the default shared store:
- Linux:
~/.cache/grove/grammars(honors$XDG_CACHE_HOME) - macOS:
~/Library/Caches/grove/grammars - Windows:
%LOCALAPPDATA%\grove/grammars
- Linux:
- dev source tree (
registry/next to this crate) — only in a checkout.
Layout under the root is <lang>/{grammar.wasm, tags.scm, manifest.json}.
Fetching grammars
grove fetch pulls grammars from the hosted registry into the OS cache:
grove fetch # all languages in the catalog
grove fetch python rust # just these
grove fetch python --force # re-download
grove owns the artifacts it serves. Rather than redirecting to upstream URLs
at fetch time, grove ingests each grammar (official release wasm where it exists,
else built from source), normalizes it into the shape grove needs — grammar.wasm
(native dylink.0) + tags.scm + a manifest.json carrying the node-kind
profile — and hosts those bytes content-addressed, recording provenance
(source.repo / source.rev) for auditability. This guarantees the three travel
as one co-versioned unit and that grove.lock always resolves.
The host is the Entelligentsia/grove-registry
repo, split for efficiency: the small text files (index.json, per-language
tags.scm + manifest.json) live in the repo (served via
raw.githubusercontent.com), and the heavy grammar.wasm binaries are GitHub
Release assets (GitHub’s CDN). The catalog’s release_base + per-file asset
fields tell fetch where each file lives. Every file’s sha256 is verified
against the catalog before it’s written (download-verify-then-write, atomically),
so a corrupted or tampered artifact is rejected. Override the host with
GROVE_REGISTRY_URL (self-host, fork, or a local mirror).
Profiles (why some languages do more)
A grammar’s manifest.json profile decides which tools work fully:
- Full profile (~15 languages) — declares
function_kinds/identifier_kinds, socallers(enclosing function),definitionfrom a position, andparentgrouping all work. - Minimal profile (12 languages) — core tools (
outline,symbols,source,check,map) work;callers/definitiondegrade. - css/html/json/regex have no upstream
tags.scm— they stillcheckbut yield no symbols.
Nothing language-specific is compiled into the binary; the profile is data.
Building the registry (maintainer)
grove ingest builds the registry from a curated spec (registry-sources.json):
for each grammar it pulls the official tree-sitter release wasm + the repo’s
tags.scm at a pinned rev, attaches grove’s curated profile/extensions, writes
registry/<lang>/, and regenerates the catalog.
grove ingest # all grammars in registry-sources.json
grove ingest python rust # just these
grove index registry # (re)build index.json with per-file hashes
The spec records identity + provenance + the grove-authored profile; the wasm and
tags come from upstream and the version/source are pinned. grove index then
emits the index.json catalog (per language: version, provenance, content hash of
every served file) — the publish step for registry CI.
Next: Tools · Registry repo →