diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 261 |
1 files changed, 74 insertions, 187 deletions
diff --git a/src/main.rs b/src/main.rs index 2a4898d..6e85257 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,10 @@ use std::{path::PathBuf, fs, process::exit}; -use pipeline::Change; -use rowan::{ast::AstNode, NodeOrToken}; +use rowan::ast::AstNode; use clap::{arg, Parser}; +use queries::Query; +use changes::apply_changes; + #[allow(dead_code)] mod queries; #[allow(dead_code)] @@ -12,6 +14,7 @@ mod batchmode; mod util; #[allow(dead_code)] mod pipeline; +mod changes; #[derive(clap::Parser)] @@ -24,39 +27,11 @@ struct Args { batchmode: bool } - -fn parse_nexp(path: &PathBuf) -> anyhow::Result<(String, rnix::Root)> { - let content = fs::read_to_string(path)?; - - let parse = rnix::Root::parse(&content); - if !parse.errors().is_empty() { - anyhow::bail!("error: {:?}", parse.errors()); - } - Ok((content, parse.tree())) -} - - fn main() { let args = Args::parse(); - let parse = queries::parse(&args.query); - if parse.errors.len() != 0 { - eprintln!( - "syntax {}: \n {}", - if parse.errors.len() == 1 { "error" } else { "errors" }, - parse.errors.join(" \n") - ); - exit(1); - } - - let query = match parse.to_query() { - Err(es) => { - queries::print_query_errors(&args.query, es); - exit(1); - }, - Ok(query) => query - }; + let query = parse_query(&args.query); if args.debug { println!("{query:?}"); @@ -65,7 +40,7 @@ fn main() { if args.batchmode { batchmode::batchmode(args.path, query, args.debug); } else { - let (content, nexp) = match parse_nexp(&args.path[0]) { + let (content, nexp) = match parse_nixfile(&args.path[0]) { Err(e) => { eprintln!("could not parse file: {e}"); exit(2); @@ -92,176 +67,88 @@ fn main() { } -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 { - use queries::Operator::*; - match change.kind { - 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; - } - Keep => { - let (before, after) = surrounding_noise(&change.node); - let (before, after) = eat_whitespace(&before, &after); - // let (before, after) = (change.node.text_range().start().into(), change.node.text_range().end().into()); - let (before, after) = (before.text_range().start().into(), after.text_range().end().into()); - if debug { - println!("keeping: {}", &content[before..after]) - }; - ncontent += &content[before..after]; - // last_pos = after; - } - } - } - - ncontent += &content[last_pos..]; +fn parse_nixfile(path: &PathBuf) -> anyhow::Result<(String, rnix::Root)> { + let content = fs::read_to_string(path)?; - ncontent + let tree = parse_nexpr(&content)?; + Ok((content, tree)) } - - -// TODO: does rnix really not have an alias for this? -type NixNodeOrToken = NodeOrToken<rowan::SyntaxNode<rnix::NixLanguage>, rowan::SyntaxToken<rnix::NixLanguage>>; - -/// eat all tokens of the given kind (backwards or forwards) -fn eat_kind(node: &NixNodeOrToken, kind: rnix::SyntaxKind, backwards: bool) -> NixNodeOrToken { - let mut span = node.clone(); - - loop { - span = match if backwards { span.prev_sibling_or_token() } else { span.next_sibling_or_token() } { - Some(NodeOrToken::Token(t)) - if t.kind() == kind - => NodeOrToken::Token(t), - _ => break - }; +fn parse_nexpr(code: &str) -> anyhow::Result<rnix::Root> { + let parse = rnix::Root::parse(&code); + if !parse.errors().is_empty() { + anyhow::bail!("error: {:?}", parse.errors()); } - span + Ok(parse.tree()) } - -/// eat whitespace outwards both ways -fn eat_whitespace(begin: &NixNodeOrToken, end: &NixNodeOrToken) - -> (NixNodeOrToken, NixNodeOrToken) { - let begin = eat_kind(begin, rnix::SyntaxKind::TOKEN_WHITESPACE, true); - let end = eat_kind(end, rnix::SyntaxKind::TOKEN_WHITESPACE, false); - (begin, end) -} - -/// for a node, get all the "syntactic noise" which surrounds it -/// (i.e. brackets, whitespace, etc.) which should be removed if -/// the node itself is removed, except the final bit of whitespace -/// potentially separating it from neighbouring nodes, which should -/// be handled separately to keep formatting nice -fn surrounding_noise(node: &rnix::SyntaxNode) -> (NixNodeOrToken, NixNodeOrToken) { - - fn eat_matching_parens(begin: &NixNodeOrToken, end: &NixNodeOrToken) - -> Option<(NixNodeOrToken, NixNodeOrToken)> { - match (begin.prev_sibling_or_token(), end.next_sibling_or_token()) { - (Some(begin), Some(end)) - if begin.kind() == rnix::SyntaxKind::TOKEN_L_PAREN - && end.kind() == rnix::SyntaxKind::TOKEN_R_PAREN - => Some((begin, end)), - _ => None - } +fn parse_query(querystring: &str) -> Query { + let parse = queries::parse(querystring); + if parse.errors.len() != 0 { + eprintln!( + "syntax {}: \n {}", + if parse.errors.len() == 1 { "error" } else { "errors" }, + parse.errors.join(" \n") + ); + exit(1); } - let (mut begin, mut end): (NixNodeOrToken, NixNodeOrToken) = - (NodeOrToken::Node(node.clone()), NodeOrToken::Node(node.clone())); - - loop { - let (a, b) = eat_whitespace(&begin, &end); - // println!("loop: {begin}, {end}"); - // println!("loop: {a}, {b}"); - // println!("{:?}", (begin.prev_sibling_or_token(), end.next_sibling_or_token())); - if let Some(res) = eat_matching_parens(&a, &b) { - // println!("ate parens!"); - (begin, end) = res; - } else { - break - } - } + let query = match parse.to_query() { + Err(es) => { + queries::print_query_errors(querystring, es); + exit(1); + }, + Ok(query) => query + }; - (begin, end) + query } -fn remove_node(node: &rnix::SyntaxNode) -> (usize, usize) { - let (before, after) = surrounding_noise(node); - - let (mut prev, /*mut*/ next) = - eat_whitespace(&before, &after); - - // println!("{prev:?}, {next:?}"); - if let Some(t) = prev.as_token().map(|t| t.prev_token()).flatten() { - if t.kind() == rnix::SyntaxKind::TOKEN_WHITESPACE { - prev = NodeOrToken::Token(t); - } - }; - // if let Some(t) = next.as_token().map(|t| t.next_token()).flatten() { - // if t.kind() == rnix::SyntaxKind::TOKEN_WHITESPACE { - // next = NodeOrToken::Token(t); - // } - // }; - // println!("{prev:?}, {next:?}"); - - - if prev.kind() == rnix::SyntaxKind::TOKEN_WHITESPACE - && next.kind() == rnix::SyntaxKind::TOKEN_WHITESPACE { - // println!("inside if!"); - if prev.to_string().lines().count() < next.to_string().lines().count() { - let start: usize = (node.text_range().start() - prev.text_range().len()).into(); - let end: usize = node.text_range().end().into(); - (start + previous_indentation(&node).unwrap_or(0) - , end + next_indentation(&node).unwrap_or(0)) - } else { - let start: usize = node.text_range().start().into(); - let end: usize = (node.text_range().end() + next.text_range().len()).into(); - (start, end) - // (start - previous_indentation(&node).unwrap_or(0), - // end - next_indentation(&node).unwrap_or(0)) - } +macro_rules! test_change { + ($name:ident, $query:literal, $before:literal, $after:literal) => { + #[test] + fn $name() { + let nix = $before; + let nexpr = parse_nexpr(&nix).unwrap(); + let query = parse_query($query); + let (changes, _) = query.apply(&nix, nexpr.syntax().clone()).unwrap(); + let changed = apply_changes(&nix, changes, false); + assert_eq!(changed, $after); } - else { - (prev.text_range().start().into(), next.text_range().end().into()) - } -} - -fn indentation_of_string(string: &str) -> Option<usize> { - if string.contains("\n") { - None - } else { - Some(string.lines().last().unwrap().len()) } } -fn previous_indentation(node: &rnix::SyntaxNode) -> Option<usize> { - let whitespace_token = node.prev_sibling_or_token()?; - - if whitespace_token.kind() == rnix::SyntaxKind::TOKEN_WHITESPACE { - indentation_of_string(&whitespace_token.to_string()) - } else { - None - } -} +test_change!( + test_remove_inherit, + ">> Inherit >> mdDoc[remove]", r#" +{lib, ...}: +mkDerivation { + pname = "dings"; + inherit (lib) mdDoc a b c mdDoc; +}"#, + r#" +{lib, ...}: +mkDerivation { + pname = "dings"; + inherit (lib) a b c; +}"# +); + +test_change!( + test_remove_attrpath, + ">> mkDerivation >> pname[remove]", + r#" +{lib, ...}: +mkDerivation { + pname = "dings"; + meta.mainProgram = "blub"; +}"#, + r#" +{lib, ...}: +mkDerivation { + meta.mainProgram = "blub"; +}"# +); -fn next_indentation(node: &rnix::SyntaxNode) -> Option<usize> { - let whitespace_token = node.next_sibling_or_token()?; - if whitespace_token.kind() == rnix::SyntaxKind::TOKEN_WHITESPACE { - indentation_of_string(&whitespace_token.to_string()) - // Some(whitespace_token.to_string().lines().last().unwrap().len()) - } else { - None - } -} |