feat(code-graph-incremental): language families
What does this MR do and why?
Stacked on !2648 (merged). Languages that resolve against each other now index together: C with C++, Java with Kotlin and Scala, JavaScript with TypeScript. A family is declared in languages.yaml; one run covers all its files, each parsed and rewritten by its own language, resolved in one graph. The two family suites that !2648 (merged) had to skip run again and pass, with no rule-file change. Nothing is wired to the CLI or server yet.
Related Issues
Relates to #1284, original spike !2454 (closed). Design note: tree-dsl-language-families.md (layer 1 of it; the base+overlay rule layout is not part of this).
Testing
cargo nextest run -p integration-tests-codegraph: incremental_suites 177/177 with cpp/cross_language and jvm/cross_language_resolution un-skipped; suites 247/247 unchanged. cargo test -p code-graph-incremental: 52 tests; new: a Kotlin env has Java, Kotlin and Scala as members and parses .java and .kt each with its own grammar while a .py is listed; every family shares one qualified-name separator.
Performance Analysis
No runtime change: nothing calls this crate yet. A standalone language is a one-member family and does exactly what it did before.
- This merge request does not introduce any performance regression. If a performance regression is expected, explain why.
Agent context: long-form analysis, file-by-file walkthroughs, profiler output, alternatives considered
What changes
config/languages.yaml family: c_family | jvm | js_ts on the members
src/treesitter.rs SupportLang::family(), family_members(); pipeline() is gone
src/env.rs Env { lang, lang_id, members, resolve: FamilyResolve, limits, rules: map<SupportLang, LangConfig> }
rules_for(path): the file's own language's rewrite, link and display rules
src/dsl/rules.rs ResolveConfig::merge
src/pipeline/phases.rs Prepare admits family members; Rewrite and Display use rules_for; Resolve uses env.resolve
src/linker.rs link config from rules_for
src/resolver.rs resolve_submodule strips the target path's own extension
integration-tests-codegraph/src/incremental.rs detect_lang accepts one family, rejects a mixNet +105 lines.
Why no "base" language
The design note pairs each family with a base rule file plus overlays. That is not needed for a shared graph. Project-level config differs between members only additively (Java marks java/ as a source root, Kotlin marks kotlin/), so the resolver reads the union of the members' resolve config and the concatenation of their stages. File-level rules stay per language, because a .kt file must be rewritten by the Kotlin rules whatever the family. The base+overlay layout is a rule-corpus refactor for later, if duplication between rule files starts to hurt.
The pipeline() special case for TS/TSX/JS goes away: it was a hard-coded two-member family.
Base branch is michaelusa/code-graph-incremental-languages (!2648 (merged)); retarget to main once that merges.
MR stack
| MR | Contents |
|---|---|
| !2620 (merged) !2627 (merged) !2629 (merged) !2631 (merged) !2633 (merged) !2638 (merged) !2641 (merged) !2644 (merged) | merged: runner through snapshots and reindex |
| !2648 (merged) | fourteen languages and their suites |
| 10 (this) | language families |
| 11 | TypeScript family rules and suites |
| 12 | Rust |
| 13 | orbit index --engine incremental, full index through the existing DuckDB sink |
| 14 | snapshot next to the DuckDB file, changed paths from git, incremental CLI |
| 15 | default flip per language; old pipeline and its fixtures retire |
%{all_commits}