| Crates.io | arbol |
| lib.rs | arbol |
| version | 0.1.0 |
| created_at | 2025-12-04 05:44:36.438495+00 |
| updated_at | 2025-12-04 05:44:36.438495+00 |
| description | Tree-sitter rust AST query tool |
| homepage | |
| repository | https://github.com/joaommartins/arbol |
| max_upload_size | |
| id | 1965956 |
| size | 78,735 |
A small utility (library + CLI) that walks a Rust workspace / crate, parses each .rs file with Tree‑sitter (v0.25), and lets you:
It focuses on being: fast, embeddable, and predictable (deterministic output ordering).
rayontarget/, generated/)--skip-dir <path> (relative or absolute)--include-tests)--context)--max-depth)--with-source)From crates.io:
cargo install --locked arbol
From the repo:
cargo install --locked --git https://github.com/joaommartins/arbol
After install you can run as a normal binary:
arbol --help
Dump a shallow CST (installed binary assumed):
arbol dump-json --max-depth 2 > ast.json
Run an inline query and emit JSON:
arbol query --expr '(function_item name: (identifier) @fn.name)' --json > fns.json
Use a query file with line context:
arbol query --query-file examples/functions.scm --context
Include tests / benches:
arbol query --include-tests --expr '(macro_invocation macro: (identifier) @macro.name)'
Skip specific directories (repeat --skip-dir or pass multiple):
arbol query \
--skip-dir target \
--skip-dir openapi/generated \
--expr '(trait_item name: (type_identifier) @trait.name)' --json
Verbose tracing:
arbol dump-json --verbose --max-depth 1
Subcommands:
Dump a per‑file JSON listing of nodes (optionally including node source text):
Flags:
--with-source include short node snippets (<= 240 bytes)--max-depth <n> limit traversal depth (0 = only root)--output <path> write to file instead of stdoutRun a raw Tree‑sitter query across all discovered Rust files.
Provide exactly one of:
--query-file <file.scm>--expr '<inline s-expression>'Optional flags:
--context include the full source line for each capture--json emit structured JSON instead of plain grouped textGlobal flags:
--include-tests also scan tests/ & benches/--skip-dir <path> repeatable; omit any paths under these directories--verbose enable tracing subscriber--root <path> (default .) – directory to scan (should contain a Cargo.toml or nested crates)--markdown-help emit Markdown help to stdout (or to file with --help-output)--help-output <path> path to write Markdown help (implies --markdown-help)[{
"path": "src/lib.rs",
"root_kind": "source_file",
"nodes": [
{
"kind": "function_item",
"start_byte": 120,
"end_byte": 260,
"start_line": 10,
"end_line": 18,
"child_count": 5,
"text": "fn foo() {}" // present only with --with-source and short spans
}
]
}]
[{
"crate_path": "utilities/arbol",
"captures": [
{
"crate_path": "utilities/arbol",
"file": "src/lib.rs",
"line": 42,
"column": 5,
"name": "fn.name",
"text": "rust_language",
"line_text": "pub fn rust_language() -> Language {" // only with --context
}
]
}]
Queries are standard Tree‑sitter S‑expressions. Example: capture all public function names:
((function_item
(visibility_modifier) @vis
name: (identifier) @fn.name))
Capture trait names:
((trait_item name: (type_identifier) @trait.name))
You can combine them in one file; all captures are flattened then grouped by crate.
--max-depth for structural overviewsjq for quick ad‑hoc exploration: ... DumpJson | jq '.[] | .path, .nodes[0]'--context then re‑run when refiningMIT
Small focused improvements welcome: