diff options
author | stuebinm | 2024-04-12 17:04:55 +0200 |
---|---|---|
committer | stuebinm | 2024-04-12 17:04:55 +0200 |
commit | 9e32c201789e4d0a119c4d486138f2f5edd1e168 (patch) | |
tree | 857fdb6a71376945267ef360ba72d10c6983e0db /src/main.rs | |
parent | 422dbd5209fddac75412d8e7de0137f732c7dbc4 (diff) |
rework handling of whitespace around deletions
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 138 |
1 files changed, 75 insertions, 63 deletions
diff --git a/src/main.rs b/src/main.rs index 2bc44ab..89ed1a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use std::{path::PathBuf, fs, process::exit}; use pipeline::{Change, ChangeKind}; -use rowan::{ast::AstNode, TextSize, NodeOrToken}; +use rnix::SyntaxNode; +use rowan::{ast::AstNode, TextSize, NodeOrToken, TextRange}; use clap::{arg, Parser}; #[allow(dead_code)] @@ -115,77 +116,88 @@ fn apply_changes(content: &str, mut changes: Vec<Change>, debug: bool) -> String ncontent } -#[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) + + +// 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 + }; + } + span } -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, - } +/// 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 } - (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()) + let (mut begin, mut end): (NixNodeOrToken, NixNodeOrToken) = + (NodeOrToken::Node(node.clone()), NodeOrToken::Node(node.clone())); + + loop { + let (a, b) = eat_whitespace(&begin, &end); + if let Some(res) = eat_matching_parens(&a, &b) { + (begin, end) = res; + } else { + break } - }; + } + + (begin, end) +} - (before, after) - // ( Into::<usize>::into(span.text_range().start()) - before - // , Into::<usize>::into(span.text_range().end()) + after - // ) +fn remove_node(node: &rnix::SyntaxNode) -> (usize, usize) { + let (before, after) = surrounding_noise(node); + + let (prev, next) = + eat_whitespace(&before, &after); + + 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(node.text_range().start() - prev.text_range().len()) + previous_indentation(&node).unwrap_or(0) + , Into::<usize>::into(node.text_range().end()) + next_indentation(&node).unwrap_or(0)) + } else { + (Into::<usize>::into(node.text_range().start()) - previous_indentation(&node).unwrap_or(0), + Into::<usize>::into(node.text_range().end() + next.text_range().len()) - next_indentation(&node).unwrap_or(0)) + } + } + else { + (prev.text_range().start().into(), next.text_range().end().into()) + } } |