Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/syntax/src/syntax_editor.rs')
-rw-r--r--crates/syntax/src/syntax_editor.rs109
1 files changed, 105 insertions, 4 deletions
diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs
index edd063ffd4..7d15195c6f 100644
--- a/crates/syntax/src/syntax_editor.rs
+++ b/crates/syntax/src/syntax_editor.rs
@@ -133,7 +133,19 @@ impl SyntaxEditor {
!matches!(&element, SyntaxElement::Node(node) if node == &self.root),
"should not delete root node"
);
- self.changes.borrow_mut().push(Change::Replace(element.syntax_element(), None));
+ let mut changes = self.changes.borrow_mut();
+ for change in changes.iter_mut() {
+ if let Change::Replace(existing, replacement) = change
+ && *existing == element
+ {
+ if replacement.is_none() {
+ return;
+ }
+ *replacement = None;
+ return;
+ }
+ }
+ changes.push(Change::Replace(element, None));
}
pub fn delete_all(&self, range: RangeInclusive<SyntaxElement>) {
@@ -149,9 +161,23 @@ impl SyntaxEditor {
pub fn replace(&self, old: impl Element, new: impl Element) {
let old = old.syntax_element();
debug_assert!(is_ancestor_or_self_of_element(&old, &self.root));
- self.changes
- .borrow_mut()
- .push(Change::Replace(old.syntax_element(), Some(new.syntax_element())));
+ let new = new.syntax_element();
+ let mut changes = self.changes.borrow_mut();
+ for change in changes.iter_mut() {
+ if let Change::Replace(existing, replacement) = change
+ && *existing == old
+ {
+ match replacement {
+ None => return,
+ Some(existing_new) if *existing_new == new => return,
+ Some(existing_new) => {
+ *existing_new = new;
+ return;
+ }
+ }
+ }
+ }
+ changes.push(Change::Replace(old, Some(new)));
}
pub fn replace_with_many(&self, old: impl Element, new: Vec<SyntaxElement>) {
@@ -177,6 +203,14 @@ impl SyntaxEditor {
pub fn finish(self) -> SyntaxEdit {
edit_algo::apply_edits(self)
}
+
+ pub fn deleted(&self, element: impl Element) -> bool {
+ let element = element.syntax_element();
+ self.changes
+ .borrow()
+ .iter()
+ .any(|change| matches!(change, Change::Replace(existing, None) if *existing == element))
+ }
}
/// Represents a completed [`SyntaxEditor`] operation.
@@ -216,6 +250,17 @@ impl SyntaxEdit {
pub fn find_annotation(&self, annotation: SyntaxAnnotation) -> &[SyntaxElement] {
self.annotations.get(&annotation).as_ref().map_or(&[], |it| it.as_slice())
}
+
+ pub fn find_element(&self, old_node: &SyntaxNode) -> Option<SyntaxNode> {
+ let old_root_start = self.old_root.text_range().start();
+ let old_start = old_node.text_range().start() - old_root_start;
+ let new_root_start = self.new_root.text_range().start();
+ let kind = old_node.kind();
+
+ self.new_root
+ .descendants()
+ .find(|it| it.kind() == kind && it.text_range().start() - new_root_start == old_start)
+ }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -702,6 +747,62 @@ mod tests {
}
#[test]
+ fn test_dependent_change_prefers_nearest_changed_ancestor() {
+ let root = make::block_expr(
+ [],
+ Some(
+ make::block_expr(
+ [make::let_stmt(
+ make::ext::simple_ident_pat(make::name("second")).into(),
+ None,
+ Some(make::expr_literal("2").into()),
+ )
+ .into()],
+ None,
+ )
+ .into(),
+ ),
+ );
+
+ let (editor, root) = SyntaxEditor::with_ast_node(&root);
+ let make = editor.make();
+
+ let inner_block =
+ root.syntax().descendants().flat_map(ast::BlockExpr::cast).nth(1).unwrap();
+
+ let outer_replacement = make.block_expr([], Some(ast::Expr::BlockExpr(root.clone())));
+ let inner_replacement =
+ make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone())));
+
+ let first_let = make.let_stmt(
+ make::ext::simple_ident_pat(make::name("first")).into(),
+ None,
+ Some(make::expr_literal("1").into()),
+ );
+
+ editor.insert(
+ Position::first_child_of(inner_block.stmt_list().unwrap().syntax()),
+ first_let.syntax(),
+ );
+ editor.replace(inner_block.syntax(), inner_replacement.syntax());
+ editor.replace(root.syntax(), outer_replacement.syntax());
+
+ let edit = editor.finish();
+
+ let expect = expect![[r#"
+ {
+ {
+ {
+ let first = 1;{
+ let second = 2;
+ }
+ }
+ }
+ }"#]];
+ expect.assert_eq(&edit.new_root.to_string());
+ }
+
+ #[test]
fn test_replace_root_with_dependent() {
let root = make::block_expr(
[make::let_stmt(