5 releases
Uses new Rust 2024
| 0.3.2 | Jan 16, 2026 |
|---|---|
| 0.3.1 | Jan 15, 2026 |
| 0.3.0 | Jan 15, 2026 |
| 0.2.1 | Jan 13, 2026 |
| 0.2.0 | Jan 7, 2026 |
#83 in Development tools
245KB
5.5K
SLoC
Plotnik
A type-safe query language for Tree-sitter.
Powered by the arborium grammar collection.
⚠️ BETA: NOT FOR PRODUCTION USE ⚠️
Tree-sitter gives you the syntax tree. Extracting structured data from it still means writing imperative navigation code, null checks, and maintaining type definitions by hand. Plotnik makes extraction declarative: write a pattern, get typed data. The query is the type definition.
Features
- Static type inference from query structure
- Named expressions for composition and reuse
- Recursion for nested structures
- Tagged unions (discriminated unions)
- TypeScript type generation
- CLI:
execfor matches,inferfor types,ast/trace/dumpfor debug - Full validation against grammar (reject queries that can never match)
- Compile-time queries via proc-macro
- WASM
- LSP, editor extensions
Installation
cargo install plotnik
By default, 15 common languages are included. To add specific languages:
cargo install plotnik --features lang-ruby,lang-elixir
Or with all 80+ languages:
cargo install plotnik --features all-languages
Example
Extract function signatures from Rust. Type references itself to handle nested generics like Option<Vec<String>>.
query.ptk:
Type = [
Simple: [(type_identifier) (primitive_type)] @name :: string
Generic: (generic_type
type: (type_identifier) @name :: string
type_arguments: (type_arguments (Type)* @args))
]
Func = (function_item
name: (identifier) @name :: string
parameters: (parameters
(parameter
pattern: (identifier) @param :: string
type: (Type) @type
)* @params))
Funcs = (source_file (Func)* @funcs)
lib.rs:
fn get(key: Option<Vec<String>>) {}
fn set(key: String, val: i32) {}
Plotnik infers TypeScript types from the query structure. Type is recursive: args: Type[].
❯ plotnik infer query.ptk --lang rust
export type Type =
| { $tag: "Simple"; $data: { name: string } }
| { $tag: "Generic"; $data: { name: string; args: Type[] } };
export interface Func {
name: string;
params: { param: string; type: Type }[];
}
export interface Funcs {
funcs: Func[];
}
Run the query against lib.rs to extract structured JSON:
❯ plotnik exec query.ptk lib.rs
{
"funcs": [
{
"name": "get",
"params": [{
"param": "key",
"type": {
"$tag": "Generic",
"$data": {
"name": "Option",
"args": [{
"$tag": "Generic",
"$data": {
"name": "Vec",
"args": [{ "$tag": "Simple", "$data": { "name": "String" } }]
}
}]
}
}
}]
},
{
"name": "set",
"params": [
{ "param": "key", "type": { "$tag": "Simple", "$data": { "name": "String" } } },
{ "param": "val", "type": { "$tag": "Simple", "$data": { "name": "i32" } } }
]
}
]
}
Why
Pattern matching over syntax trees is powerful, but tree-sitter queries produce flat capture lists. You still need to assemble the results, handle missing captures, and define types by hand. Plotnik closes this gap: the query describes structure, the engine guarantees it.
Documentation
Acknowledgments
Max Brunsfeld created Tree-sitter; Amaan Qureshi and other contributors maintain the parser ecosystem that makes this project possible.
License
This project is licensed under the Apache License (Version 2.0).
Dependencies
~8–120MB
~3.5M SLoC