Reusing an existing tree-sitter Tree
A common pain point is that callers who already drive
tree-sitter for syntax highlighting, code folding, or queries
end up parsing every file twice: once for their own tree, once
inside the metric walker. The parse seam lets you
hand big-code-analysis an already-parsed tree_sitter::Tree and
get the same FuncSpace back without re-parsing.
Use
Ast::from_tree_sitter. It adopts a caller-builttree_sitter::Treeand lets you run the metric walker more than once against the same parse (differentMetricsOptions::with_onlyselections, custom tree-sitter walks interleaved with metrics,Ast::opsfor operator/operand extraction, etc.). See Parse once, run metrics many times. It carries an explicitname: Option<String>rather than deriving the top-levelFuncSpace::namefrom a path via lossy UTF-8 conversion.
When to use this
Use the parse seam if you:
- Already keep a
tree_sitter::Treeper open buffer (editor, LSP, language server, custom static-analysis pipeline) and want to reuse that parse for metrics rather than paying the byte-based cost again. - Want to run multiple passes (metrics + AST dump + custom analysis) against one parse result.
- Intend to pin
tree-sitteron your side without taking a separate dependency from this library. The re-exportedbig_code_analysis::tree_sittermodule is the same crate we link against, so the types agree by definition.
Use the byte-based entry point analyze (with a
Source) if you do not already have a tree — it
constructs the parser internally and owns the parse end to end.
Working example
use big_code_analysis::{analyze, tree_sitter, Ast, LANG, MetricsOptions, Source}; let source_code = "fn main() { if true { 1 } else { 2 }; }"; let source = source_code.as_bytes().to_vec(); // Step 1: build a tree with the *re-exported* tree-sitter crate. // Using `big_code_analysis::tree_sitter` (rather than a direct // `tree-sitter` dependency on your side) guarantees the version // matches the one the metric walker was compiled against. let mut parser = tree_sitter::Parser::new(); parser .set_language( &LANG::Rust.tree_sitter_language().expect("rust feature enabled"), ) .expect("rust grammar pinned to a compatible version"); let tree = parser .parse(&source, None) .expect("parser has a language set"); // Step 2: adopt the tree with an explicit display name. let from_tree = Ast::from_tree_sitter( LANG::Rust, tree, source.clone(), Some("foo.rs".to_owned()), ) .expect("rust feature enabled") .metrics(MetricsOptions::default()) .expect("non-empty input"); // Step 3 (optional): confirm the values match the byte-based path. let from_bytes = analyze( Source::new(LANG::Rust, &source).with_name(Some("foo.rs".to_owned())), MetricsOptions::default(), ) .expect("non-empty input"); assert_eq!( from_tree.metrics.cyclomatic.cyclomatic_sum(), from_bytes.metrics.cyclomatic.cyclomatic_sum(), );
The same shape works for any LANG variant — pass the
matching grammar to tree_sitter::Parser::set_language (via
LANG::tree_sitter_language) and the metric
walker will produce the same FuncSpace it would have produced
from bytes.
The single tree-adoption seam
Ast::from_tree_sitter is the entry point for tree reuse —
it dispatches on a LANG at runtime and hides the parser plumbing
entirely. The former lower-level path (the generic Parser<T> /
ParserTrait and the per-language *Parser / *Code tag types)
is now crate-private (pub(crate)) and is no longer part of the
public surface; see STABILITY.md. Library consumers should
adopt a tree through Ast::from_tree_sitter, which does not expose
any per-language tag types or trait bounds.
Out of scope
- Incremental re-computation. Applying a
tree_sitter::InputEditand re-querying only the changed spans is not supported yet — the metric walker still walks the entire tree on every call. The parse seam is the first step; making the walker itself incremental is a follow-up. - Promoting all of
Node'spub(crate)traversal methods.Nodestill exposes its innertree_sitter::Nodethrough the public.0field for ad-hoc traversal; the wrapper helpers remain crate-private.