about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDropDemBits <r3usrlnd@gmail.com>2024-09-02 22:27:14 -0400
committerDropDemBits <r3usrlnd@gmail.com>2024-09-02 22:27:14 -0400
commitf6e05a744dcf47aaa229cac246228268394263d8 (patch)
treed5d5c03a2a67e7ad1271cc9fd18ff13b25c0ceb6
parentf03f95f36918c5d64ead3fdafc920915ac204e04 (diff)
downloadrust-f6e05a744dcf47aaa229cac246228268394263d8.tar.gz
rust-f6e05a744dcf47aaa229cac246228268394263d8.zip
handle replace_with_many and replace_all
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs47
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs66
2 files changed, 97 insertions, 16 deletions
diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
index 77a560bfe5c..3a05cc480b2 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
@@ -6,6 +6,7 @@
 
 use std::{
     num::NonZeroU32,
+    ops::RangeInclusive,
     sync::atomic::{AtomicU32, Ordering},
 };
 
@@ -76,6 +77,26 @@ impl SyntaxEditor {
         self.changes.push(Change::Replace(old.syntax_element(), Some(new.syntax_element())));
     }
 
+    pub fn replace_with_many(&mut self, old: impl Element, new: Vec<SyntaxElement>) {
+        let old = old.syntax_element();
+        debug_assert!(is_ancestor_or_self_of_element(&old, &self.root));
+        debug_assert!(
+            !(matches!(&old, SyntaxElement::Node(node) if node == &self.root) && new.len() > 1),
+            "cannot replace root node with many elements"
+        );
+        self.changes.push(Change::ReplaceWithMany(old.syntax_element(), new));
+    }
+
+    pub fn replace_all(&mut self, range: RangeInclusive<SyntaxElement>, new: Vec<SyntaxElement>) {
+        if range.start() == range.end() {
+            self.replace_with_many(range.start(), new);
+            return;
+        }
+
+        debug_assert!(is_ancestor_or_self_of_element(range.start(), &self.root));
+        self.changes.push(Change::ReplaceAll(range, new))
+    }
+
     pub fn finish(self) -> SyntaxEdit {
         edit_algo::apply_edits(self)
     }
@@ -186,10 +207,17 @@ impl Position {
 
 #[derive(Debug)]
 enum Change {
+    /// Inserts a single element at the specified position.
     Insert(Position, SyntaxElement),
+    /// Inserts many elements in-order at the specified position.
     InsertAll(Position, Vec<SyntaxElement>),
     /// Represents both a replace single element and a delete element operation.
     Replace(SyntaxElement, Option<SyntaxElement>),
+    /// Replaces a single element with many elements.
+    ReplaceWithMany(SyntaxElement, Vec<SyntaxElement>),
+    /// Replaces a range of elements with another list of elements.
+    /// Range will always have start != end.
+    ReplaceAll(RangeInclusive<SyntaxElement>, Vec<SyntaxElement>),
 }
 
 impl Change {
@@ -202,24 +230,29 @@ impl Change {
                 ),
                 PositionRepr::After(child) => TextRange::at(child.text_range().end(), 0.into()),
             },
-            Change::Replace(target, _) => target.text_range(),
+            Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => target.text_range(),
+            Change::ReplaceAll(range, _) => {
+                range.start().text_range().cover(range.end().text_range())
+            }
         }
     }
 
     fn target_parent(&self) -> SyntaxNode {
         match self {
             Change::Insert(target, _) | Change::InsertAll(target, _) => target.parent(),
-            Change::Replace(SyntaxElement::Node(target), _) => {
-                target.parent().unwrap_or_else(|| target.clone())
-            }
-            Change::Replace(SyntaxElement::Token(target), _) => target.parent().unwrap(),
+            Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => match target {
+                SyntaxElement::Node(target) => target.parent().unwrap_or_else(|| target.clone()),
+                SyntaxElement::Token(target) => target.parent().unwrap(),
+            },
+            Change::ReplaceAll(target, _) => target.start().parent().unwrap(),
         }
     }
 
     fn change_kind(&self) -> ChangeKind {
         match self {
             Change::Insert(_, _) | Change::InsertAll(_, _) => ChangeKind::Insert,
-            Change::Replace(_, _) => ChangeKind::Replace,
+            Change::Replace(_, _) | Change::ReplaceWithMany(_, _) => ChangeKind::Replace,
+            Change::ReplaceAll(_, _) => ChangeKind::ReplaceRange,
         }
     }
 }
@@ -227,7 +260,7 @@ impl Change {
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 enum ChangeKind {
     Insert,
-    // TODO: deal with replace spans
+    ReplaceRange,
     Replace,
 }
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs
index 2c331fc1f69..3b92ac1cbd8 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs
@@ -61,7 +61,13 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
         .zip(changes.iter().skip(1))
         .filter(|(l, r)| {
             // We only care about checking for disjoint replace ranges
-            l.change_kind() == ChangeKind::Replace && r.change_kind() == ChangeKind::Replace
+            matches!(
+                (l.change_kind(), r.change_kind()),
+                (
+                    ChangeKind::Replace | ChangeKind::ReplaceRange,
+                    ChangeKind::Replace | ChangeKind::ReplaceRange
+                )
+            )
         })
         .all(|(l, r)| {
             get_node_depth(l.target_parent()) != get_node_depth(r.target_parent())
@@ -97,6 +103,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
             // Pop off any ancestors that aren't applicable
             changed_ancestors.drain((index + 1)..);
 
+            // FIXME: Resolve changes that depend on a range of elements
             let ancestor = &changed_ancestors[index];
 
             dependent_changes.push(DependentChange {
@@ -115,9 +122,12 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
         // Add to changed ancestors, if applicable
         match change {
             Change::Insert(_, _) | Change::InsertAll(_, _) => {}
-            Change::Replace(target, _) => {
+            Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => {
                 changed_ancestors.push_back(ChangedAncestor::single(target, change_index))
             }
+            Change::ReplaceAll(range, _) => {
+                changed_ancestors.push_back(ChangedAncestor::multiple(range, change_index))
+            }
         }
     }
 
@@ -137,9 +147,15 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
                     }
                 };
             }
-            Change::Replace(target, _) => {
+            Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => {
                 *target = tree_mutator.make_element_mut(target);
             }
+            Change::ReplaceAll(range, _) => {
+                let start = tree_mutator.make_element_mut(range.start());
+                let end = tree_mutator.make_element_mut(range.end());
+
+                *range = start..=end;
+            }
         }
 
         // Collect changed elements
@@ -148,6 +164,10 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
             Change::InsertAll(_, elements) => changed_elements.extend(elements.iter().cloned()),
             Change::Replace(_, Some(element)) => changed_elements.push(element.clone()),
             Change::Replace(_, None) => {}
+            Change::ReplaceWithMany(_, elements) => {
+                changed_elements.extend(elements.iter().cloned())
+            }
+            Change::ReplaceAll(_, elements) => changed_elements.extend(elements.iter().cloned()),
         }
     }
 
@@ -160,6 +180,9 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
             }
             // Silently drop outdated change
             Change::Replace(_, None) => continue,
+            Change::ReplaceAll(_, _) | Change::ReplaceWithMany(_, _) => {
+                unimplemented!("cannot resolve changes that depend on replacing many elements")
+            }
         };
 
         let upmap_target_node = |target: &SyntaxNode| {
@@ -185,9 +208,12 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
                     *child = upmap_target(child);
                 }
             },
-            Change::Replace(target, _) => {
+            Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => {
                 *target = upmap_target(&target);
             }
+            Change::ReplaceAll(range, _) => {
+                *range = upmap_target(range.start())..=upmap_target(range.end());
+            }
         }
     }
 
@@ -214,6 +240,16 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
                 let parent = target.parent().unwrap();
                 parent.splice_children(target.index()..target.index() + 1, vec![new_target]);
             }
+            Change::ReplaceWithMany(target, elements) => {
+                let parent = target.parent().unwrap();
+                parent.splice_children(target.index()..target.index() + 1, elements);
+            }
+            Change::ReplaceAll(range, elements) => {
+                let start = range.start().index();
+                let end = range.end().index();
+                let parent = range.start().parent().unwrap();
+                parent.splice_children(start..end + 1, elements);
+            }
         }
     }
 
@@ -252,7 +288,7 @@ struct ChangedAncestor {
 
 enum ChangedAncestorKind {
     Single { node: SyntaxNode },
-    Range { changed_nodes: RangeInclusive<SyntaxNode>, in_parent: SyntaxNode },
+    Range { _changed_elements: RangeInclusive<SyntaxElement>, in_parent: SyntaxNode },
 }
 
 impl ChangedAncestor {
@@ -267,13 +303,25 @@ impl ChangedAncestor {
         Self { kind, change_index }
     }
 
+    fn multiple(range: &RangeInclusive<SyntaxElement>, change_index: usize) -> Self {
+        Self {
+            kind: ChangedAncestorKind::Range {
+                _changed_elements: range.clone(),
+                in_parent: range.start().parent().unwrap(),
+            },
+            change_index,
+        }
+    }
+
     fn affected_range(&self) -> TextRange {
         match &self.kind {
             ChangedAncestorKind::Single { node } => node.text_range(),
-            ChangedAncestorKind::Range { changed_nodes, in_parent: _ } => TextRange::new(
-                changed_nodes.start().text_range().start(),
-                changed_nodes.end().text_range().end(),
-            ),
+            ChangedAncestorKind::Range { _changed_elements: changed_nodes, in_parent: _ } => {
+                TextRange::new(
+                    changed_nodes.start().text_range().start(),
+                    changed_nodes.end().text_range().end(),
+                )
+            }
         }
     }
 }