This guide is meant to be usable by both humans and coding agents.
If you want an agent to implement a new language, give it this guide plus a request like:
Add semantic parsing support for
<language>. If the grammar makes call extraction practical, addcall_graphsupport too. Update tests and docs, then runnpm run build,npm run typecheck,npm run lint, andnpm run test:run.
The agent should treat this as a staged task:
- confirm file discovery
- register the language
- add parser support
- add parser tests
- verify
- optionally add call-graph support
- update docs
- re-run verification
This guide removes repo-specific guesswork. It does not replace knowledge of the target language’s tree-sitter grammar.
The implementer still needs to know or look up:
- the grammar crate
- the parser constant
- the node kinds for declarations, comments, and calls
- the grammar license, release history, and compatibility with the repository's tree-sitter runtime
So this guide is a repo wiring guide, not a replacement for the target language's tree-sitter grammar docs.
There are three support levels in this repo:
- File discovery only — files are indexed, but may fall back to line-based chunking
- Semantic parsing — tree-sitter extracts better chunks for search
- Call graph support —
call_graphcan extract callers/callees too
If you are unsure, aim for semantic parsing first. It is much easier to land than full call-graph support.
- the language is registered in
native/src/types.rs - files in that language are discovered by the indexer
native/src/parser.rsuses the correct tree-sitter grammar- semantic chunks are produced for the main declaration types
tests/native.test.tscovers the language- the grammar license is verified and its notice is included in the published package when required
- every new grammar dependency has a locked entry and verified runtime compatibility
- docs are updated if user-facing support claims changed
npm run build,npm run typecheck,npm run lint, andnpm run test:runall pass
- a
native/queries/<language>-calls.scmfile exists native/src/call_extractor.rsroutes the language correctlysrc/indexer/index.tsincludes the language inCALL_GRAPH_LANGUAGESsrc/indexer/call-graph-constants.tsincludes the relevant declaration chunk types inCALL_GRAPH_SYMBOL_CHUNK_TYPEStests/call-graph.test.tscovers the supported call/query forms- full verification passes again
src/config/constants.ts— file extensions included in indexingnative/src/types.rs— language enum and string/extension mappingnative/src/parser.rs— tree-sitter parser wiring, semantic node kinds, comment kindstests/native.test.ts— parser coverage
native/src/call_extractor.rs— call extraction routingnative/queries/<language>-calls.scm— tree-sitter query filesrc/indexer/index.ts— call-graph language allowlist and indexing integrationsrc/indexer/call-graph-constants.ts— shared symbol chunk-type allowlisttests/call-graph.test.ts— call-graph coverage
native/Cargo.toml— grammar crate declarationnative/Cargo.lock— reproducibly resolved version- the repository license or third-party notices file — attribution required by the dependency license
package.json— inclusion of a new notices file in the published package, when applicable
README.md— supported-language claims
- Confirm file discovery in
src/config/constants.ts - Register the language in
native/src/types.rs - Add parser support in
native/src/parser.rs - Add one parser test in
tests/native.test.ts - Run
npm run buildandnpm run test:run - Only then add call-graph support
- Update docs
That order keeps failures easy to localize.
Use this exact sequence:
- Check whether the extensions are already present in
src/config/constants.ts - Add the language to
Language,from_extension(),as_str(), andfrom_string()innative/src/types.rs - Add the grammar crate in
native/Cargo.tomlif needed - Check the license text, release history, registry publication, and tree-sitter ABI compatibility of the grammar
- Update
native/Cargo.lockand distribute required third-party attribution when a grammar crate is added - Add parser selection, comment node kinds, and semantic node kinds in
native/src/parser.rs - Add one parser test in
tests/native.test.ts - Run
npm run buildandnpm run test:run - If call graph is needed, add
native/queries/<language>-calls.scm - Wire the language into
native/src/call_extractor.rs - Add the language to
CALL_GRAPH_LANGUAGESinsrc/indexer/index.ts - Add its declaration chunk types to
src/indexer/call-graph-constants.ts - Add call-graph tests in
tests/call-graph.test.ts - Update
README.mdif supported-language claims changed - Verify the third-party notice with
npm pack --dry-run --ignore-scripts - Run full verification:
npm run build,npm run typecheck,npm run lint,npm run test:run
Add extensions only if they are not already included.
Important: discovery here does not mean semantic parsing is supported.
Add the language to:
Languagefrom_extension()as_str()from_string()
This is the canonical registry.
Add the language in three places:
- parser selection in
parse_file_internal() is_comment_node()is_semantic_node()
Start with narrow, declaration-like nodes:
- functions
- methods
- classes / structs
- interfaces / traits
- enums
- modules / namespaces
Avoid broad container nodes unless the grammar gives you no better option.
If a wrapper such as a template or namespace contains declarations, check whether marking it semantic prevents traversal from reaching the functions or types being sought.
Add a small test proving the main declaration type becomes a chunk.
If the language should support call_graph, do all of this:
Create a query file modeled after the existing ones.
Use the same capture names already expected by native/src/call_extractor.rs:
@callee.name@call@constructor@import.name@import.default@import.namespace
For C-family languages, the extractor also supports conservative exclusion captures:
@excluded.namefor macros and inline function-pointer declarators that must not become direct-call edges@indirect.type,@indirect.variable_type, and@indirect.variablefor connecting an indirect function-type alias to variables declared with that alias
Start small:
- direct calls
- method/member calls
- imports/includes if applicable
Add the language to:
- parser selection
- query-source selection
method_parent_kindsif needed
Add the language to CALL_GRAPH_LANGUAGES and add relevant declaration chunk types to CALL_GRAPH_SYMBOL_CHUNK_TYPES.
Those chunk-type strings in src/indexer/call-graph-constants.ts must match the tree-sitter node kinds that actually become chunks in native/src/parser.rs.
Add focused tests for the constructs your query file supports.
When the grammar is ambiguous, add negative fixtures as well. In particular, verify that declarations, local macros, explicit template calls, and function-pointer invocations do not become ordinary direct calls in the graph.
The extension is included, but the language is not wired in types.rs and parser.rs, so indexing falls back to line-based chunks.
Usually the crate name, parser constant, or grammar version is wrong.
is_semantic_node() is using the wrong node kinds.
is_comment_node() is using the wrong comment node kinds.
The query file, query registration, or query node names do not match the grammar.
Also check that the query capture names match what native/src/call_extractor.rs expects.
The language is missing from CALL_GRAPH_LANGUAGES in src/indexer/index.ts, or its symbol chunk types are missing from CALL_GRAPH_SYMBOL_CHUNK_TYPES in src/indexer/call-graph-constants.ts.
Run:
npm run build
npm run typecheck
npm run lint
npm run test:runFor partial progress while implementing, the minimum useful checkpoint is:
npm run build
npm run test:runPHP is already integrated at all three levels: file discovery, semantic parsing, and call graph extraction. Changes must therefore preserve native/src/types.rs, native/src/parser.rs, native/queries/php-calls.scm, src/indexer/index.ts, src/indexer/call-graph-constants.ts, and their tests together.
A parseFile() content assertion alone does not prove syntax compatibility because Tree-sitter can return chunks while the tree still contains ERROR nodes. PHP grammar tests must also assert !tree.root_node().has_error() in Rust, then verify the chunk names and types exposed through NAPI.
Call graph tests must distinguish an invocation from a first-class callable reference. For example, foo(...) is not a call unless it is used as an operand of the PHP 8.5 pipe operator |>.
The tested matrix and remaining tree-sitter-php 0.24.2 limitations are documented in the README's "Verified PHP 8.x compatibility" section.
Metal uses a dedicated Language::Metal label while reusing the existing C++ grammar. This avoids a fragile new dependency, but still requires Metal-specific semantic nodes, declaration-name extraction, and a call query. Tests must cover short types, nested methods, exact function names under templates, and shader-stage qualifiers; a successfully built tree is not enough because tree-sitter recovery nodes can remain around Metal-specific syntax. Macro-only regions should be checked separately too: in a mixed file, regions not covered by a semantic declaration may remain outside chunks.
If you know how to inspect a tree-sitter grammar, this guide should be enough to add a language in this repo without guessing where the plumbing lives.