use rowan::NodeOrToken; use crate::pipeline::Change; pub fn apply_changes(content: &str, mut changes: Vec, 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 crate::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..]; ncontent } // TODO: does rnix really not have an alias for this? type NixNodeOrToken = NodeOrToken, rowan::SyntaxToken>; /// 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 }; } span } /// 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 } } 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 } } (begin, end) } 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)) } } else { (prev.text_range().start().into(), next.text_range().end().into()) } } fn indentation_of_string(string: &str) -> Option { if string.contains("\n") { None } else { Some(string.lines().last().unwrap().len()) } } fn previous_indentation(node: &rnix::SyntaxNode) -> Option { 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 } } fn next_indentation(node: &rnix::SyntaxNode) -> Option { 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 } }