Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ Tree-sitter: `((a) (b))` — Plotnik: `{(a) (b)}`. The #1 syntax error.
```
crates/
plotnik-cli/ # CLI tool
src/commands/ # Subcommands (check, dump, exec, infer, trace, tree, langs)
src/commands/ # Subcommands (ast, check, dump, exec, infer, trace, langs)
plotnik-core/ # Common code (Interner, Symbol)
plotnik-lib/ # Plotnik as library
src/
Expand All @@ -172,22 +172,23 @@ Run: `cargo run -p plotnik-cli -- <command>`

| Command | Purpose |
| ------- | ----------------------------- |
| `tree` | Explore tree-sitter AST |
| `ast` | Show AST of query and/or source |
| `check` | Validate query |
| `dump` | Show compiled bytecode |
| `infer` | Generate TypeScript types |
| `exec` | Execute query, output JSON |
| `trace` | Trace execution for debugging |
| `langs` | List supported languages |

## tree
## ast

Explore a source file's tree-sitter AST.
Show AST of query and/or source file.

```sh
cargo run -p plotnik-cli -- tree app.ts
cargo run -p plotnik-cli -- tree app.ts --raw
cargo run -p plotnik-cli -- tree app.ts --spans
cargo run -p plotnik-cli -- ast query.ptk # query AST
cargo run -p plotnik-cli -- ast app.ts # source AST (tree-sitter)
cargo run -p plotnik-cli -- ast query.ptk app.ts # both ASTs
cargo run -p plotnik-cli -- ast query.ptk app.ts --raw # CST / include anonymous nodes
```

## check
Expand Down Expand Up @@ -275,6 +276,7 @@ cargo run -p plotnik-cli -- langs
- Early exit (`return`, `continue`, `break`) over deep nesting
- Comments for seniors, not juniors
- Rust 2024 `let` chains: `if let Some(x) = a && let Some(y) = b { ... }`
- Never claim "all tests pass" — CI verifies this

## Lifetime Conventions

Expand Down
8 changes: 0 additions & 8 deletions crates/plotnik-cli/src/cli/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,6 @@ pub fn raw_arg() -> Arg {
.help("Include anonymous nodes (literals, punctuation)")
}

/// Show source positions (--spans).
pub fn spans_arg() -> Arg {
Arg::new("spans")
.long("spans")
.action(ArgAction::SetTrue)
.help("Show source positions")
}

/// Treat warnings as errors (--strict).
pub fn strict_arg() -> Arg {
Arg::new("strict")
Expand Down
62 changes: 44 additions & 18 deletions crates/plotnik-cli/src/cli/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,18 @@ fn with_hidden_trace_args(cmd: Command) -> Command {
.arg(fuel_arg().hide(true))
}

/// Add hidden AST args (for commands that don't show AST).
fn with_hidden_ast_args(cmd: Command) -> Command {
cmd.arg(raw_arg().hide(true))
}

/// Build the complete CLI with all subcommands.
pub fn build_cli() -> Command {
Command::new("plotnik")
.about("Query language for tree-sitter AST with type inference")
.subcommand_required(true)
.arg_required_else_help(true)
.subcommand(tree_command())
.subcommand(ast_command())
.subcommand(check_command())
.subcommand(dump_command())
.subcommand(infer_command())
Expand All @@ -51,26 +56,39 @@ pub fn build_cli() -> Command {
.subcommand(langs_command())
}

/// Explore a source file's tree-sitter AST.
pub fn tree_command() -> Command {
Command::new("tree")
.about("Explore a source file's tree-sitter AST")
/// Show AST of query and/or source file.
///
/// Accepts all runtime flags for unified CLI experience.
/// Shows query AST when query is provided, source AST when source is provided.
pub fn ast_command() -> Command {
let cmd = Command::new("ast")
.about("Show AST of query and/or source file")
.override_usage(
"\
plotnik tree <SOURCE>
plotnik tree -s <TEXT> -l <LANG>",
plotnik ast <QUERY> [SOURCE]
plotnik ast -q <TEXT> [SOURCE]
plotnik ast <SOURCE>
plotnik ast -s <TEXT> -l <LANG>",
)
.after_help(
r#"EXAMPLES:
plotnik tree app.ts # source file
plotnik tree app.ts --raw # include anonymous nodes
plotnik tree -s 'let x = 1' -l js # inline source"#,
plotnik ast query.ptk # query AST
plotnik ast app.ts # source AST (tree-sitter)
plotnik ast query.ptk app.ts # both ASTs
plotnik ast query.ptk app.ts --raw # CST / include anonymous nodes
plotnik ast -q '(id) @x' # inline query AST
plotnik ast -s 'let x = 1' -l js # inline source AST"#,
)
.arg(query_path_arg())
.arg(source_path_arg())
.arg(query_text_arg())
.arg(source_text_arg())
.arg(lang_arg())
.arg(raw_arg())
.arg(spans_arg())
.arg(color_arg());

// Hidden unified flags
with_hidden_trace_args(with_hidden_exec_args(cmd))
}

/// Validate a query.
Expand Down Expand Up @@ -100,7 +118,9 @@ pub fn check_command() -> Command {
.arg(color_arg());

// Hidden unified flags
with_hidden_trace_args(with_hidden_exec_args(with_hidden_source_args(cmd)))
with_hidden_ast_args(with_hidden_trace_args(with_hidden_exec_args(
with_hidden_source_args(cmd),
)))
}

/// Show compiled bytecode.
Expand Down Expand Up @@ -128,7 +148,9 @@ pub fn dump_command() -> Command {
.arg(color_arg());

// Hidden unified flags
with_hidden_trace_args(with_hidden_exec_args(with_hidden_source_args(cmd)))
with_hidden_ast_args(with_hidden_trace_args(with_hidden_exec_args(
with_hidden_source_args(cmd),
)))
}

/// Generate type definitions from a query.
Expand Down Expand Up @@ -163,7 +185,9 @@ NOTE: Use --verbose-nodes to match `exec --verbose-nodes` output shape."#,
.arg(color_arg());

// Hidden unified flags (use partial exec args since --verbose-nodes is visible)
with_hidden_trace_args(with_hidden_exec_args_partial(with_hidden_source_args(cmd)))
with_hidden_ast_args(with_hidden_trace_args(with_hidden_exec_args_partial(
with_hidden_source_args(cmd),
)))
}

/// Execute a query against source code and output JSON.
Expand Down Expand Up @@ -196,7 +220,7 @@ pub fn exec_command() -> Command {
.arg(entry_arg());

// Hidden unified flags
with_hidden_trace_args(cmd)
with_hidden_ast_args(with_hidden_trace_args(cmd))
}

/// Trace query execution for debugging.
Expand Down Expand Up @@ -229,9 +253,11 @@ pub fn trace_command() -> Command {
.arg(fuel_arg());

// Hidden unified flags (exec output flags only - entry is visible for trace)
cmd.arg(compact_arg().hide(true))
.arg(verbose_nodes_arg().hide(true))
.arg(check_arg().hide(true))
with_hidden_ast_args(
cmd.arg(compact_arg().hide(true))
.arg(verbose_nodes_arg().hide(true))
.arg(check_arg().hide(true)),
)
}

/// List supported languages.
Expand Down
33 changes: 24 additions & 9 deletions crates/plotnik-cli/src/cli/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,56 @@ use std::path::PathBuf;
use clap::ArgMatches;

use super::ColorChoice;
use crate::commands::ast::AstArgs;
use crate::commands::check::CheckArgs;
use crate::commands::dump::DumpArgs;
use crate::commands::exec::ExecArgs;
use crate::commands::infer::InferArgs;
use crate::commands::trace::TraceArgs;
use crate::commands::tree::TreeArgs;

pub struct TreeParams {
pub struct AstParams {
pub query_path: Option<PathBuf>,
pub query_text: Option<String>,
pub source_path: Option<PathBuf>,
pub source_text: Option<String>,
pub lang: Option<String>,
pub raw: bool,
pub spans: bool,
pub color: ColorChoice,
}

impl TreeParams {
impl AstParams {
pub fn from_matches(m: &ArgMatches) -> Self {
let query_path = m.get_one::<PathBuf>("query_path").cloned();
let query_text = m.get_one::<String>("query_text").cloned();
let source_path = m.get_one::<PathBuf>("source_path").cloned();

// Positional shifting: when -q is used with a single positional,
// shift it from query_path to source_path.
let (query_path, source_path) =
shift_positional_to_source(query_text.is_some(), query_path, source_path);

Self {
source_path: m.get_one::<PathBuf>("source_path").cloned(),
query_path,
query_text,
source_path,
source_text: m.get_one::<String>("source_text").cloned(),
lang: m.get_one::<String>("lang").cloned(),
raw: m.get_flag("raw"),
spans: m.get_flag("spans"),
color: parse_color(m),
}
}
}

impl From<TreeParams> for TreeArgs {
fn from(p: TreeParams) -> Self {
impl From<AstParams> for AstArgs {
fn from(p: AstParams) -> Self {
Self {
query_path: p.query_path,
query_text: p.query_text,
source_path: p.source_path,
source_text: p.source_text,
lang: p.lang,
raw: p.raw,
spans: p.spans,
color: p.color.should_colorize(),
}
}
}
Expand Down
Loading