diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs index 75347aeeed..4b5134249c 100644 --- a/crates/syntax/src/syntax_editor.rs +++ b/crates/syntax/src/syntax_editor.rs @@ -50,6 +50,14 @@ impl SyntaxEditor { self.annotations.append(&mut other.annotations); } + pub fn insert(&mut self, position: Position, element: impl Element) { + self.changes.push(Change::Insert(position, element.syntax_element())) + } + + pub fn insert_all(&mut self, position: Position, elements: Vec) { + self.changes.push(Change::InsertAll(position, elements)) + } + pub fn delete(&mut self, element: impl Element) { self.changes.push(Change::Replace(element.syntax_element(), None)); } @@ -117,6 +125,19 @@ pub struct Position { repr: PositionRepr, } +impl Position { + pub(crate) fn parent(&self) -> SyntaxNode { + self.place().0 + } + + pub(crate) fn place(&self) -> (SyntaxNode, usize) { + match &self.repr { + PositionRepr::FirstChild(parent) => (parent.clone(), 0), + PositionRepr::After(child) => (child.parent().unwrap(), child.index() + 1), + } + } +} + #[derive(Debug)] enum PositionRepr { FirstChild(SyntaxNode), @@ -155,6 +176,8 @@ impl Position { #[derive(Debug)] enum Change { + Insert(Position, SyntaxElement), + InsertAll(Position, Vec), /// Represents both a replace single element and a delete element operation. Replace(SyntaxElement, Option), } @@ -162,18 +185,27 @@ enum Change { impl Change { fn target_range(&self) -> TextRange { match self { + Change::Insert(target, _) | Change::InsertAll(target, _) => match &target.repr { + PositionRepr::FirstChild(parent) => TextRange::at( + parent.first_child_or_token().unwrap().text_range().start(), + 0.into(), + ), + PositionRepr::After(child) => TextRange::at(child.text_range().end(), 0.into()), + }, Change::Replace(target, _) => target.text_range(), } } fn target_parent(&self) -> SyntaxNode { match self { + Change::Insert(target, _) | Change::InsertAll(target, _) => target.parent(), Change::Replace(target, _) => target.parent().unwrap(), } } fn change_kind(&self) -> ChangeKind { match self { + Change::Insert(_, _) | Change::InsertAll(_, _) => ChangeKind::Insert, Change::Replace(_, _) => ChangeKind::Replace, } } diff --git a/crates/syntax/src/syntax_editor/edit_algo.rs b/crates/syntax/src/syntax_editor/edit_algo.rs index 734a26b600..4f0b30ed6a 100644 --- a/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/crates/syntax/src/syntax_editor/edit_algo.rs @@ -4,8 +4,8 @@ use rowan::TextRange; use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ - syntax_editor::{mapping::MissingMapping, Change, ChangeKind}, - ted, SyntaxElement, SyntaxNode, SyntaxNodePtr, + syntax_editor::{mapping::MissingMapping, Change, ChangeKind, Position, PositionRepr}, + SyntaxElement, SyntaxNode, SyntaxNodePtr, }; use super::{SyntaxEdit, SyntaxEditor}; @@ -94,6 +94,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { // Add to changed ancestors, if applicable match change { + Change::Insert(_, _) | Change::InsertAll(_, _) => {} Change::Replace(target, _) => { changed_ancestors.push_back(ChangedAncestor::single(target, change_index)) } @@ -106,23 +107,46 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { for index in independent_changes { match &mut changes[index as usize] { - Change::Replace(target, new_node) => { - *target = tree_mutator.make_element_mut(target); - - if let Some(new_node) = new_node { - changed_elements.push(new_node.clone()); - } + Change::Insert(target, _) | Change::InsertAll(target, _) => { + match &mut target.repr { + PositionRepr::FirstChild(parent) => { + *parent = tree_mutator.make_syntax_mut(parent); + } + PositionRepr::After(child) => { + *child = tree_mutator.make_element_mut(child); + } + }; } + Change::Replace(target, _) => { + *target = tree_mutator.make_element_mut(target); + } + } + + // Collect changed elements + match &changes[index as usize] { + Change::Insert(_, element) => changed_elements.push(element.clone()), + Change::InsertAll(_, elements) => changed_elements.extend(elements.iter().cloned()), + Change::Replace(_, Some(element)) => changed_elements.push(element.clone()), + Change::Replace(_, None) => {} } } for DependentChange { parent, child } in dependent_changes.into_iter() { let (input_ancestor, output_ancestor) = match &changes[parent as usize] { - // insert? unreachable + // No change will depend on an insert since changes can only depend on nodes in the root tree + Change::Insert(_, _) | Change::InsertAll(_, _) => unreachable!(), Change::Replace(target, Some(new_target)) => { (to_owning_node(target), to_owning_node(new_target)) } - Change::Replace(_, None) => continue, // silently drop outdated change + // Silently drop outdated change + Change::Replace(_, None) => continue, + }; + + let upmap_target_node = |target: &SyntaxNode| { + match mappings.upmap_child(target, &input_ancestor, &output_ancestor) { + Ok(it) => it, + Err(MissingMapping(current)) => unreachable!("no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}"), + } }; let upmap_target = |target: &SyntaxElement| { @@ -133,6 +157,14 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { }; match &mut changes[child as usize] { + Change::Insert(target, _) | Change::InsertAll(target, _) => match &mut target.repr { + PositionRepr::FirstChild(parent) => { + *parent = upmap_target_node(parent); + } + PositionRepr::After(child) => { + *child = upmap_target(child); + } + }, Change::Replace(target, _) => { *target = upmap_target(&target); } @@ -142,8 +174,21 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { // Apply changes for change in changes { match change { - Change::Replace(target, None) => ted::remove(target), - Change::Replace(target, Some(new_target)) => ted::replace(target, new_target), + Change::Insert(position, element) => { + let (parent, index) = position.place(); + parent.splice_children(index..index, vec![element]); + } + Change::InsertAll(position, elements) => { + let (parent, index) = position.place(); + parent.splice_children(index..index, elements); + } + Change::Replace(target, None) => { + target.detach(); + } + Change::Replace(target, Some(new_target)) => { + let parent = target.parent().unwrap(); + parent.splice_children(target.index()..target.index() + 1, vec![new_target]); + } } }