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.rs | 249 |
1 files changed, 190 insertions, 59 deletions
diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs index e6937e4d0f..edd063ffd4 100644 --- a/crates/syntax/src/syntax_editor.rs +++ b/crates/syntax/src/syntax_editor.rs @@ -5,6 +5,7 @@ //! [`SyntaxEditor`]: https://github.com/dotnet/roslyn/blob/43b0b05cc4f492fd5de00f6f6717409091df8daa/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs use std::{ + cell::RefCell, fmt, iter, num::NonZeroU32, ops::RangeInclusive, @@ -14,7 +15,10 @@ use std::{ use rowan::TextRange; use rustc_hash::FxHashMap; -use crate::{SyntaxElement, SyntaxNode, SyntaxToken}; +use crate::{ + AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T, + ast::{self, edit::IndentLevel, syntax_factory::SyntaxFactory}, +}; mod edit_algo; mod edits; @@ -26,31 +30,60 @@ pub use mapping::{SyntaxMapping, SyntaxMappingBuilder}; #[derive(Debug)] pub struct SyntaxEditor { root: SyntaxNode, - changes: Vec<Change>, - mappings: SyntaxMapping, - annotations: Vec<(SyntaxElement, SyntaxAnnotation)>, + changes: RefCell<Vec<Change>>, + annotations: RefCell<Vec<(SyntaxElement, SyntaxAnnotation)>>, + make: SyntaxFactory, } impl SyntaxEditor { - /// Creates a syntax editor to start editing from `root` - pub fn new(root: SyntaxNode) -> Self { - Self { root, changes: vec![], mappings: SyntaxMapping::default(), annotations: vec![] } + /// Creates a syntax editor from `root`. + /// + /// The returned `root` is guaranteed to be a detached, immutable node. + /// If the provided node is not a root (i.e., has a parent) or is already + /// mutable, it is cloned into a fresh subtree to satisfy syntax editor + /// invariants. + pub fn new(root: SyntaxNode) -> (Self, SyntaxNode) { + let mut root = root; + + if root.parent().is_some() || root.is_mutable() { + root = root.clone_subtree() + }; + + let editor = Self { + root: root.clone(), + changes: RefCell::new(Vec::new()), + annotations: RefCell::new(Vec::new()), + make: SyntaxFactory::with_mappings(), + }; + + (editor, root) } - pub fn add_annotation(&mut self, element: impl Element, annotation: SyntaxAnnotation) { - self.annotations.push((element.syntax_element(), annotation)) + /// Typed-node variant of [`SyntaxEditor::new`]. + pub fn with_ast_node<T>(root: &T) -> (Self, T) + where + T: AstNode, + { + let (editor, root) = Self::new(root.syntax().clone()); + + (editor, T::cast(root).unwrap()) } - pub fn add_annotation_all( - &mut self, - elements: Vec<impl Element>, - annotation: SyntaxAnnotation, - ) { + pub fn make(&self) -> &SyntaxFactory { + &self.make + } + + pub fn add_annotation(&self, element: impl Element, annotation: SyntaxAnnotation) { + self.annotations.borrow_mut().push((element.syntax_element(), annotation)) + } + + pub fn add_annotation_all(&self, elements: Vec<impl Element>, annotation: SyntaxAnnotation) { self.annotations + .borrow_mut() .extend(elements.into_iter().map(|e| e.syntax_element()).zip(iter::repeat(annotation))); } - pub fn merge(&mut self, mut other: SyntaxEditor) { + pub fn merge(&self, other: SyntaxEditor) { debug_assert!( self.root == other.root || other.root.ancestors().any(|node| node == self.root), "{:?} is not in the same tree as {:?}", @@ -58,74 +91,92 @@ impl SyntaxEditor { self.root ); - self.changes.append(&mut other.changes); - self.mappings.merge(other.mappings); - self.annotations.append(&mut other.annotations); + self.changes.borrow_mut().append(&mut other.changes.into_inner()); + if let Some(mut m) = self.make.mappings() { + m.merge(other.make.take()); + } + self.annotations.borrow_mut().append(&mut other.annotations.into_inner()); } - pub fn insert(&mut self, position: Position, element: impl Element) { + pub fn insert(&self, position: Position, element: impl Element) { debug_assert!(is_ancestor_or_self(&position.parent(), &self.root)); - self.changes.push(Change::Insert(position, element.syntax_element())) + self.changes.borrow_mut().push(Change::Insert(position, element.syntax_element())) } - pub fn insert_all(&mut self, position: Position, elements: Vec<SyntaxElement>) { + pub fn insert_all(&self, position: Position, elements: Vec<SyntaxElement>) { debug_assert!(is_ancestor_or_self(&position.parent(), &self.root)); - self.changes.push(Change::InsertAll(position, elements)) + self.changes.borrow_mut().push(Change::InsertAll(position, elements)) } - pub fn delete(&mut self, element: impl Element) { + pub fn insert_with_whitespace(&self, position: Position, element: impl Element) { + self.insert_all_with_whitespace(position, vec![element.syntax_element()]) + } + + pub fn insert_all_with_whitespace(&self, position: Position, mut elements: Vec<SyntaxElement>) { + if let Some(first) = elements.first() + && let Some(ws) = ws_before(&position, first, &self.make) + { + elements.insert(0, ws.into()); + } + if let Some(last) = elements.last() + && let Some(ws) = ws_after(&position, last, &self.make) + { + elements.push(ws.into()); + } + self.insert_all(position, elements) + } + + pub fn delete(&self, element: impl Element) { let element = element.syntax_element(); debug_assert!(is_ancestor_or_self_of_element(&element, &self.root)); debug_assert!( !matches!(&element, SyntaxElement::Node(node) if node == &self.root), "should not delete root node" ); - self.changes.push(Change::Replace(element.syntax_element(), None)); + self.changes.borrow_mut().push(Change::Replace(element.syntax_element(), None)); } - pub fn delete_all(&mut self, range: RangeInclusive<SyntaxElement>) { + pub fn delete_all(&self, range: RangeInclusive<SyntaxElement>) { if range.start() == range.end() { self.delete(range.start()); return; } debug_assert!(is_ancestor_or_self_of_element(range.start(), &self.root)); - self.changes.push(Change::ReplaceAll(range, Vec::new())) + self.changes.borrow_mut().push(Change::ReplaceAll(range, Vec::new())) } - pub fn replace(&mut self, old: impl Element, new: impl Element) { + 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.push(Change::Replace(old.syntax_element(), Some(new.syntax_element()))); + self.changes + .borrow_mut() + .push(Change::Replace(old.syntax_element(), Some(new.syntax_element()))); } - pub fn replace_with_many(&mut self, old: impl Element, new: Vec<SyntaxElement>) { + pub fn replace_with_many(&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)); + self.changes.borrow_mut().push(Change::ReplaceWithMany(old.syntax_element(), new)); } - pub fn replace_all(&mut self, range: RangeInclusive<SyntaxElement>, new: Vec<SyntaxElement>) { + pub fn replace_all(&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)) + self.changes.borrow_mut().push(Change::ReplaceAll(range, new)) } pub fn finish(self) -> SyntaxEdit { edit_algo::apply_edits(self) } - - pub fn add_mappings(&mut self, other: SyntaxMapping) { - self.mappings.merge(other); - } } /// Represents a completed [`SyntaxEditor`] operation. @@ -384,6 +435,86 @@ impl Element for SyntaxToken { } } +fn ws_before( + position: &Position, + new: &SyntaxElement, + factory: &SyntaxFactory, +) -> Option<SyntaxToken> { + let prev = match &position.repr { + PositionRepr::FirstChild(_) => return None, + PositionRepr::After(it) => it, + }; + + if prev.kind() == T!['{'] + && new.kind() == SyntaxKind::USE + && let Some(item_list) = prev.parent().and_then(ast::ItemList::cast) + { + let mut indent = IndentLevel::from_element(&item_list.syntax().clone().into()); + indent.0 += 1; + return Some(factory.whitespace(&format!("\n{indent}"))); + } + + if prev.kind() == T!['{'] + && ast::Stmt::can_cast(new.kind()) + && let Some(stmt_list) = prev.parent().and_then(ast::StmtList::cast) + { + let mut indent = IndentLevel::from_element(&stmt_list.syntax().clone().into()); + indent.0 += 1; + return Some(factory.whitespace(&format!("\n{indent}"))); + } + + ws_between(prev, new, factory) +} + +fn ws_after( + position: &Position, + new: &SyntaxElement, + factory: &SyntaxFactory, +) -> Option<SyntaxToken> { + let next = match &position.repr { + PositionRepr::FirstChild(parent) => parent.first_child_or_token()?, + PositionRepr::After(sibling) => sibling.next_sibling_or_token()?, + }; + ws_between(new, &next, factory) +} + +fn ws_between( + left: &SyntaxElement, + right: &SyntaxElement, + factory: &SyntaxFactory, +) -> Option<SyntaxToken> { + if left.kind() == SyntaxKind::WHITESPACE || right.kind() == SyntaxKind::WHITESPACE { + return None; + } + if right.kind() == T![;] || right.kind() == T![,] { + return None; + } + if left.kind() == T![<] || right.kind() == T![>] { + return None; + } + if left.kind() == T![&] && right.kind() == SyntaxKind::LIFETIME { + return None; + } + if right.kind() == SyntaxKind::GENERIC_ARG_LIST { + return None; + } + if right.kind() == SyntaxKind::USE { + let mut indent = IndentLevel::from_element(left); + if left.kind() == SyntaxKind::USE { + indent.0 = IndentLevel::from_element(right).0.max(indent.0); + } + return Some(factory.whitespace(&format!("\n{indent}"))); + } + if left.kind() == SyntaxKind::ATTR { + let mut indent = IndentLevel::from_element(right); + if right.kind() == SyntaxKind::ATTR { + indent.0 = IndentLevel::from_element(left).0.max(indent.0); + } + return Some(factory.whitespace(&format!("\n{indent}"))); + } + Some(factory.whitespace(" ")) +} + fn is_ancestor_or_self(node: &SyntaxNode, ancestor: &SyntaxNode) -> bool { node == ancestor || node.ancestors().any(|it| &it == ancestor) } @@ -399,7 +530,7 @@ mod tests { use crate::{ AstNode, - ast::{self, make, syntax_factory::SyntaxFactory}, + ast::{self, make}, }; use super::*; @@ -420,12 +551,12 @@ mod tests { .into(), ); + let (editor, root) = SyntaxEditor::with_ast_node(&root); + let make = editor.make(); + let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap(); let to_replace = root.syntax().descendants().find_map(ast::BinExpr::cast).unwrap(); - let mut editor = SyntaxEditor::new(root.syntax().clone()); - let make = SyntaxFactory::with_mappings(); - let name = make::name("var_name"); let name_ref = make::name_ref("var_name").clone_for_update(); @@ -434,7 +565,8 @@ mod tests { editor.add_annotation(name_ref.syntax(), placeholder_snippet); let new_block = make.block_expr( - [make + [editor + .make() .let_stmt( make.ident_pat(false, false, name.clone()).into(), None, @@ -446,7 +578,6 @@ mod tests { editor.replace(to_replace.syntax(), name_ref.syntax()); editor.replace(to_wrap.syntax(), new_block.syntax()); - editor.add_mappings(make.finish_with_mappings()); let edit = editor.finish(); @@ -460,8 +591,8 @@ mod tests { assert_eq!(edit.find_annotation(placeholder_snippet).len(), 2); assert!( edit.annotations - .iter() - .flat_map(|(_, elements)| elements) + .values() + .flatten() .all(|element| element.ancestors().any(|it| &it == edit.new_root())) ) } @@ -478,11 +609,10 @@ mod tests { None, ); + let (editor, root) = SyntaxEditor::with_ast_node(&root); + let make = editor.make(); let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); - let mut editor = SyntaxEditor::new(root.syntax().clone()); - let make = SyntaxFactory::without_mappings(); - editor.insert( Position::first_child_of(root.stmt_list().unwrap().syntax()), make.let_stmt( @@ -530,13 +660,13 @@ mod tests { ), ); + 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 second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); - let mut editor = SyntaxEditor::new(root.syntax().clone()); - let make = SyntaxFactory::with_mappings(); - let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); let first_let = make.let_stmt( @@ -557,7 +687,6 @@ mod tests { ); editor.insert(Position::after(second_let.syntax()), third_let.syntax()); editor.replace(inner_block.syntax(), new_block_expr.syntax()); - editor.add_mappings(make.finish_with_mappings()); let edit = editor.finish(); @@ -584,10 +713,10 @@ mod tests { None, ); - let inner_block = root.clone(); + let (editor, root) = SyntaxEditor::with_ast_node(&root); + let make = editor.make(); - let mut editor = SyntaxEditor::new(root.syntax().clone()); - let make = SyntaxFactory::with_mappings(); + let inner_block = root; let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); @@ -602,7 +731,6 @@ mod tests { first_let.syntax(), ); editor.replace(inner_block.syntax(), new_block_expr.syntax()); - editor.add_mappings(make.finish_with_mappings()); let edit = editor.finish(); @@ -632,7 +760,7 @@ mod tests { false, ); - let mut editor = SyntaxEditor::new(parent_fn.syntax().clone()); + let (editor, parent_fn) = SyntaxEditor::with_ast_node(&parent_fn); if let Some(ret_ty) = parent_fn.ret_type() { editor.delete(ret_ty.syntax().clone()); @@ -659,7 +787,8 @@ mod tests { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); - let mut editor = SyntaxEditor::new(arg_list.syntax().clone()); + let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); + let target_expr = make::token(parser::SyntaxKind::UNDERSCORE); for arg in arg_list.args() { @@ -677,7 +806,8 @@ mod tests { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); - let mut editor = SyntaxEditor::new(arg_list.syntax().clone()); + let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); + let target_expr = make::expr_literal("3").clone_for_update(); for arg in arg_list.args() { @@ -695,7 +825,8 @@ mod tests { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); - let mut editor = SyntaxEditor::new(arg_list.syntax().clone()); + let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); + let target_expr = make::ext::expr_unit().clone_for_update(); for arg in arg_list.args() { |