From 14656ba4f59a5b67cff5faf6d1b530ac60170ca7 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Sat, 3 Jan 2026 08:50:31 -0300 Subject: [PATCH] feat: add inline source support for tree command --- crates/plotnik-cli/src/cli.rs | 8 ++++-- crates/plotnik-cli/src/commands/tree.rs | 34 +++++++++++++++++++------ crates/plotnik-cli/src/main.rs | 6 +++-- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/crates/plotnik-cli/src/cli.rs b/crates/plotnik-cli/src/cli.rs index c62a7fbc..2d171f6c 100644 --- a/crates/plotnik-cli/src/cli.rs +++ b/crates/plotnik-cli/src/cli.rs @@ -34,11 +34,15 @@ pub enum Command { #[command(after_help = r#"EXAMPLES: plotnik tree app.ts plotnik tree app.ts --raw - plotnik tree app.ts --spans"#)] + plotnik tree -s 'let x = 1' -l javascript"#)] Tree { /// Source file to parse (use "-" for stdin) #[arg(value_name = "SOURCE")] - source: PathBuf, + source_path: Option, + + /// Inline source text + #[arg(short = 's', long = "source", value_name = "TEXT")] + source_text: Option, /// Language (inferred from extension if not specified) #[arg(short = 'l', long, value_name = "LANG")] diff --git a/crates/plotnik-cli/src/commands/tree.rs b/crates/plotnik-cli/src/commands/tree.rs index c646dc90..34b055d5 100644 --- a/crates/plotnik-cli/src/commands/tree.rs +++ b/crates/plotnik-cli/src/commands/tree.rs @@ -6,15 +6,28 @@ use arborium_tree_sitter as tree_sitter; use plotnik_langs::Lang; pub struct TreeArgs { - pub source_path: PathBuf, + pub source_path: Option, + pub source_text: Option, pub lang: Option, pub raw: bool, pub spans: bool, } pub fn run(args: TreeArgs) { - let lang = resolve_lang(&args.lang, &args.source_path); - let source = load_source(&args.source_path); + let source = match (&args.source_text, &args.source_path) { + (Some(text), None) => text.clone(), + (None, Some(path)) => load_source(path), + (Some(_), Some(_)) => { + eprintln!("error: cannot use both --source and positional SOURCE"); + std::process::exit(1); + } + (None, None) => { + eprintln!("error: source required (positional or --source)"); + std::process::exit(1); + } + }; + + let lang = resolve_lang(&args.lang, args.source_path.as_deref(), args.source_text.is_some()); let tree = lang.parse(&source); print!("{}", dump_tree(&tree, &source, args.raw, args.spans)); } @@ -33,7 +46,7 @@ fn load_source(path: &PathBuf) -> String { }) } -fn resolve_lang(lang: &Option, source_path: &Path) -> Lang { +fn resolve_lang(lang: &Option, source_path: Option<&Path>, is_inline: bool) -> Lang { if let Some(name) = lang { return plotnik_langs::from_name(name).unwrap_or_else(|| { eprintln!("error: unknown language: {}", name); @@ -41,19 +54,24 @@ fn resolve_lang(lang: &Option, source_path: &Path) -> Lang { }); } - if source_path.as_os_str() != "-" - && let Some(ext) = source_path.extension().and_then(|e| e.to_str()) + if let Some(path) = source_path + && path.as_os_str() != "-" + && let Some(ext) = path.extension().and_then(|e| e.to_str()) { return plotnik_langs::from_ext(ext).unwrap_or_else(|| { eprintln!( - "error: cannot infer language from extension '.{}', use --lang", + "error: cannot infer language from extension '.{}', use -l/--lang", ext ); std::process::exit(1); }); } - eprintln!("error: --lang is required (cannot infer from stdin)"); + if is_inline { + eprintln!("error: -l/--lang is required when using inline source"); + } else { + eprintln!("error: -l/--lang is required (cannot infer from stdin)"); + } std::process::exit(1); } diff --git a/crates/plotnik-cli/src/main.rs b/crates/plotnik-cli/src/main.rs index b4ece4eb..761f24bb 100644 --- a/crates/plotnik-cli/src/main.rs +++ b/crates/plotnik-cli/src/main.rs @@ -13,13 +13,15 @@ fn main() { match cli.command { Command::Tree { - source, + source_path, + source_text, lang, raw, spans, } => { commands::tree::run(TreeArgs { - source_path: source, + source_path, + source_text, lang, raw, spans,