diff options
| -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}");      }  }  | 
