summaryrefslogtreecommitdiff
path: root/src/changes.rs
diff options
context:
space:
mode:
authorstuebinm2024-04-17 15:30:39 +0200
committerstuebinm2024-04-17 15:30:39 +0200
commit93d72079a86849e0453c9130c73e1702e3d66f69 (patch)
tree7a473d7c33fa9f89a649a40b7fbdc03475681c9d /src/changes.rs
parentcf418ce76679f019834e4b6ff2cbafc6d40c180e (diff)
add simple tests
Diffstat (limited to 'src/changes.rs')
-rw-r--r--src/changes.rs176
1 files changed, 176 insertions, 0 deletions
diff --git a/src/changes.rs b/src/changes.rs
new file mode 100644
index 0000000..5099364
--- /dev/null
+++ b/src/changes.rs
@@ -0,0 +1,176 @@
+use rowan::NodeOrToken;
+use crate::pipeline::Change;
+
+pub 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 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::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
+}
+
+
+/// 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<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
+ }
+}
+
+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
+ }
+}