diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 206 |
1 files changed, 149 insertions, 57 deletions
diff --git a/src/main.rs b/src/main.rs index a09f4ef..2bc44ab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use std::{path::PathBuf, fs, process::exit}; -use rowan::{ast::AstNode, TextSize}; -use clap::{arg, command, value_parser}; +use pipeline::{Change, ChangeKind}; +use rowan::{ast::AstNode, TextSize, NodeOrToken}; +use clap::{arg, Parser}; #[allow(dead_code)] mod queries; @@ -9,6 +10,19 @@ mod status_reporter; mod batchmode; #[allow(dead_code)] mod util; +#[allow(dead_code)] +mod pipeline; + + +#[derive(clap::Parser)] +struct Args { + query: String, + path: Vec<PathBuf>, + #[arg(short, long)] + debug: bool, + #[arg(long)] + batchmode: bool +} fn parse_nexp(path: &PathBuf) -> anyhow::Result<(String, rnix::Root)> { @@ -23,24 +37,9 @@ fn parse_nexp(path: &PathBuf) -> anyhow::Result<(String, rnix::Root)> { fn main() { - let matches = command!() - .arg(arg!(--batchmode "run in batch mode") - .required(false) - ) - .arg(arg!([query] "query to run") - .required(true)) - .arg(arg!([file] "file to operate on") - .required(true) - .value_parser(value_parser!(PathBuf)) - ) - .arg(arg!(--edit <operation> "what to do") - .required(false)) - .get_matches(); - - let query_string = matches.get_one::<String>("query").unwrap(); - let files = matches.get_one::<PathBuf>("file").unwrap(); - - let parse = queries::parse(query_string); + let args = Args::parse(); + + let parse = queries::parse(&args.query); if parse.errors.len() != 0 { eprintln!( "syntax {}: \n {}", @@ -50,61 +49,144 @@ fn main() { exit(1); } - let (content, nexp) = match parse_nexp(files) { - Err(e) => { - eprintln!("could not parse file: {e}"); - exit(2); - }, - Ok(exp) => exp - }; - // println!("{nexp:#?}"); - let results = parse.apply(&content, nexp.syntax().clone()).unwrap(); - + if args.batchmode { + batchmode::batchmode(args.path, parse, args.debug); + } else { + let (content, nexp) = match parse_nexp(&args.path[0]) { + Err(e) => { + eprintln!("could not parse file: {e}"); + exit(2); + }, + Ok(exp) => exp + }; + + let (changes, results) = parse.apply(&content, nexp.syntax().clone()).unwrap(); + + if args.debug { + println!("{changes:?}"); + } - if let Some(op) = matches.get_one::<String>("edit") { - match &op[..] { - "remove" => { - let new = remove_nodes(content, &results); - println!("{new}"); + if changes.len() == 0 { + for result in results { + println!("{result}"); } - _ => () + } else { + let changed = apply_changes(&content, changes, args.debug); + + println!("{changed}"); } - } else { - for result in &results { - println!("{result}"); + } +} + + +fn apply_changes(content: &str, mut changes: Vec<Change>, debug: bool) -> String { + let mut last_pos = 0; + let mut ncontent = String::new(); + + changes.sort_by_key(|change| change.node.text_range().start()); + + for change in changes { + match change.kind { + ChangeKind::Remove => { + let (before, after) = remove_node(&change.node); + if last_pos > before { continue } + ncontent += &content[last_pos..before]; + if debug { + println!("removing: {}", &content[before..after]) + } + last_pos = after; + } + ChangeKind::Keep => { + // let (before, after) = surrounding_whitespace(&change.node); + let (before, after) = (change.node.text_range().start().into(), change.node.text_range().end().into()); + if debug { + println!("keeping: {}", &content[before..after]) + }; + ncontent += &content[before..after]; + // last_pos = after; + } } } + ncontent += &content[last_pos..]; + ncontent } -fn remove_nodes(content: String, results: &Vec<rnix::SyntaxNode>) -> String { - - assert!(results.len() == 1); +#[allow(unused)] +fn surrounding_whitespace(node: &rnix::SyntaxNode) -> (usize, usize) { + let before = node + .prev_sibling_or_token() + .filter(|n| n.kind() == rnix::SyntaxKind::TOKEN_WHITESPACE) + .map(|n| n.text_range().start().into()) + .unwrap_or_else(|| node.text_range().start().into()); + let after = node + .next_sibling_or_token() + .filter(|n| n.kind() == rnix::SyntaxKind::TOKEN_WHITESPACE) + .map(|n| n.text_range().end().into()) + .unwrap_or_else(|| node.text_range().end().into()); + + (before, after) +} - let span = &results[0]; - let (before, after) = match (span.prev_sibling_or_token(), span.next_sibling_or_token()) { - (Some(prev), Some(next)) - if prev.kind() == rnix::SyntaxKind::TOKEN_WHITESPACE - && next.kind() == rnix::SyntaxKind::TOKEN_WHITESPACE - => { - if prev.to_string().lines().count() < next.to_string().lines().count() { - (prev.text_range().len(), TextSize::new(0)) - } else { - (TextSize::new(0), next.text_range().len()) +fn remove_node(span: &rnix::SyntaxNode) -> (usize, usize) { + + // TODO: how to remove brackets around the node? + + fn eat_to_bracket(node: &rnix::SyntaxNode, backwards: bool) -> (TextSize, usize) { + let mut span = NodeOrToken::Node(node.clone()); + let mut bracket = span.clone(); + let mut brackets = 0; + loop { + match if backwards { span.prev_sibling_or_token() } else { span.next_sibling_or_token() } { + Some(NodeOrToken::Token(t)) => match t.kind() { + rnix::SyntaxKind::TOKEN_WHITESPACE => span = NodeOrToken::Token(t), + rnix::SyntaxKind::TOKEN_L_PAREN if backwards => { + span = NodeOrToken::Token(t); + bracket = span.clone(); + brackets += 1; + }, + rnix::SyntaxKind::TOKEN_R_PAREN if !backwards => { + span = NodeOrToken::Token(t); + bracket = span.clone(); + brackets += 1; + }, + _ => break, + }, + _ => break, } } - _ => (TextSize::default(),TextSize::default()) + (if backwards { bracket.text_range().start() } else { bracket.text_range().end() }, brackets) + } + + let (before, after) = match (eat_to_bracket(span, true), eat_to_bracket(span, false)) { + ((before, n1), (after, n2)) if n1 == n2 && n1 != 0 => (before.into(), after.into()), + _ => match (span.prev_sibling_or_token(), span.next_sibling_or_token()) { + (Some(prev), Some(next)) + if prev.kind() == rnix::SyntaxKind::TOKEN_WHITESPACE + && next.kind() == rnix::SyntaxKind::TOKEN_WHITESPACE + => { + if prev.to_string().lines().count() < next.to_string().lines().count() { + (Into::<usize>::into(span.text_range().start() - prev.text_range().len()) + previous_indentation(&span).unwrap_or(0) + , Into::<usize>::into(span.text_range().end()) + next_indentation(&span).unwrap_or(0)) + } else { + (Into::<usize>::into(span.text_range().start()) - previous_indentation(&span).unwrap_or(0), + Into::<usize>::into(span.text_range().end() + next.text_range().len()) - next_indentation(&span).unwrap_or(0)) + } + } + _ => (span.text_range().start().into(), span.text_range().end().into()) + } }; - String::new() - + &content[..Into::<usize>::into(span.text_range().start() - before) - previous_indentation(span).unwrap_or(0)] - + &content[(span.text_range().end() + after).into()..] -} + (before, after) + // ( Into::<usize>::into(span.text_range().start()) - before + // , Into::<usize>::into(span.text_range().end()) + after + // ) +} fn previous_indentation(node: &rnix::SyntaxNode) -> Option<usize> { @@ -116,3 +198,13 @@ fn previous_indentation(node: &rnix::SyntaxNode) -> Option<usize> { None } } + +fn next_indentation(node: &rnix::SyntaxNode) -> Option<usize> { + let whitespace_token = node.next_sibling_or_token()?; + + if whitespace_token.kind() == rnix::SyntaxKind::TOKEN_WHITESPACE { + Some(whitespace_token.to_string().lines().last().unwrap().len()) + } else { + None + } +} |