summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs206
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
+ }
+}