diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/changes.rs | 45 | ||||
-rw-r--r-- | src/main.rs | 77 |
2 files changed, 103 insertions, 19 deletions
diff --git a/src/changes.rs b/src/changes.rs index 5099364..986e9c4 100644 --- a/src/changes.rs +++ b/src/changes.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use rowan::NodeOrToken; use crate::pipeline::Change; @@ -174,3 +176,46 @@ fn next_indentation(node: &rnix::SyntaxNode) -> Option<usize> { None } } + +/// give line & column of a position marker in the given string +fn line_at_textpos(content: &str, pos: rowan::TextSize) -> Option<(usize, usize)> { + let pos: usize = pos.into(); + content + .split("\n") + .scan(0usize, |acc,line| { + let start = *acc; + let end = line.len() + 1 + start; + *acc = end; + Some((start, end)) + }) + .enumerate() + .find(|(_, (_, end))| *end > pos) + .map(|(line, (start, _))| (line + 1, pos + 1 - start)) +} + +pub fn format_pos(path: &PathBuf, content: &str, pos1: rowan::TextSize) -> String { + let (line1, col1) = line_at_textpos(content, pos1).unwrap(); + format!("{path:?}:{line1}:{col1}") +} + +pub fn format_range(path: &PathBuf, content: &str, range: rowan::TextRange) -> String { + let (line1, col1) = line_at_textpos(content, range.start()).unwrap(); + let (line2, col2) = line_at_textpos(content, range.end()).unwrap(); + format!("{}:{line1}:{col1}-{line2}:{col2}", path.to_string_lossy()) +} + +pub fn format_range_json(path: &PathBuf, content: &str, range: rowan::TextRange) -> serde_json::Value { + let (line1, col1) = line_at_textpos(content, range.start()).unwrap(); + let (line2, col2) = line_at_textpos(content, range.end()).unwrap(); + serde_json::json!({ + "file": path, + "start": { + "line": line1, + "column": col1 + }, + "end": { + "line": line2, + "column": col2 + } + }) +} diff --git a/src/main.rs b/src/main.rs index 6e85257..bd448ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,9 @@ use rowan::ast::AstNode; use clap::{arg, Parser}; use queries::Query; -use changes::apply_changes; +use changes::{apply_changes, format_range}; + +use crate::changes::format_range_json; #[allow(dead_code)] mod queries; @@ -24,7 +26,11 @@ struct Args { #[arg(short, long)] debug: bool, #[arg(long)] - batchmode: bool + batchmode: bool, + #[arg(long)] + print_positions: bool, + #[arg(long)] + json: bool } fn main() { @@ -38,31 +44,64 @@ fn main() { } if args.batchmode { + macro_rules! warn_arg { + ($arg: ident, $text:literal) => { if args.$arg { + eprintln!("Warning: option {} is ignored in batch mode.", $text); + }} + } + + warn_arg!(json, "--json"); + warn_arg!(print_positions, "--print-positions"); + batchmode::batchmode(args.path, query, args.debug); } else { - let (content, nexp) = match parse_nixfile(&args.path[0]) { - Err(e) => { - eprintln!("could not parse file: {e}"); - exit(2); - }, - Ok(exp) => exp - }; - - let (changes, results) = query.apply(&content, nexp.syntax().clone()).unwrap(); - - if args.debug { - println!("{changes:?}"); + for path in &args.path { + handle_file(path, &args, &query) } + } +} + +fn handle_file(path: &PathBuf, args: &Args, query: &Query) { + let (content, nexp) = match parse_nixfile(path) { + Err(e) => { + eprintln!("could not parse file: {e}"); + exit(2); + }, + Ok(exp) => exp + }; - if changes.len() == 0 { + let (changes, results) = query.apply(&content, nexp.syntax().clone()).unwrap(); + + if args.debug { + println!("{changes:?}"); + } + + if changes.len() == 0 { + if args.print_positions { + if args.json { + let json = results + .iter() + .map(|result| + format_range_json(path, &content, result.text_range())) + .collect::<Vec<_>>(); + println!("{}", serde_json::to_string(&json).unwrap()); + } else { + for result in results { + println!( + "{}", + format_range(path, &content, result.text_range()) + ); + } + } + } else { for result in results { println!("{result}"); } - } else { - let changed = apply_changes(&content, changes, args.debug); - - println!("{changed}"); } + } else { + let changed = apply_changes(&content, changes, args.debug); + + println!("{changed}"); } } |