Unnamed repository; edit this file 'description' to name the repository.
introduce interior mutability to SyntaxEditor to let go of &mut usage
| -rw-r--r-- | crates/syntax/src/ast/edit.rs | 27 | ||||
| -rw-r--r-- | crates/syntax/src/syntax_editor.rs | 128 | ||||
| -rw-r--r-- | crates/syntax/src/syntax_editor/edit_algo.rs | 4 | ||||
| -rw-r--r-- | crates/syntax/src/syntax_editor/edits.rs | 50 |
4 files changed, 104 insertions, 105 deletions
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index ae1293fb2f..b20aa90d06 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs @@ -108,7 +108,7 @@ impl IndentLevel { } pub(super) fn clone_increase_indent(self, node: &SyntaxNode) -> SyntaxNode { - let (mut editor, node) = SyntaxEditor::new(node.clone()); + let (editor, node) = SyntaxEditor::new(node.clone()); let tokens = node .preorder_with_tokens() .filter_map(|event| match event { @@ -142,7 +142,7 @@ impl IndentLevel { } pub(super) fn clone_decrease_indent(self, node: &SyntaxNode) -> SyntaxNode { - let (mut editor, node) = SyntaxEditor::new(node.clone()); + let (editor, node) = SyntaxEditor::new(node.clone()); let tokens = node .preorder_with_tokens() .filter_map(|event| match event { @@ -198,11 +198,8 @@ pub trait AstNodeEdit: AstNode + Clone + Sized { impl<N: AstNode + Clone> AstNodeEdit for N {} impl ast::IdentPat { - pub fn set_pat( - &self, - pat: Option<ast::Pat>, - syntax_editor: &mut SyntaxEditor, - ) -> ast::IdentPat { + pub fn set_pat(&self, pat: Option<ast::Pat>, editor: &SyntaxEditor) -> ast::IdentPat { + let make = editor.make(); match pat { None => { if let Some(at_token) = self.at_token() { @@ -212,7 +209,7 @@ impl ast::IdentPat { .pat() .map(|it| it.syntax().clone().into()) .unwrap_or_else(|| at_token.into()); - syntax_editor.delete_all(start..=end); + editor.delete_all(start..=end); // Remove any trailing ws if let Some(last) = @@ -225,28 +222,28 @@ impl ast::IdentPat { Some(pat) => { if let Some(old_pat) = self.pat() { // Replace existing pattern - syntax_editor.replace(old_pat.syntax(), pat.syntax()) + editor.replace(old_pat.syntax(), pat.syntax()) } else if let Some(at_token) = self.at_token() { // Have an `@` token but not a pattern yet - syntax_editor.insert(Position::after(at_token), pat.syntax()); + editor.insert(Position::after(at_token), pat.syntax()); } else { // Don't have an `@`, should have a name let name = self.name().unwrap(); let elements = vec![ - syntax_editor.make().whitespace(" ").into(), - syntax_editor.make().token(T![@]).into(), - syntax_editor.make().whitespace(" ").into(), + make.whitespace(" ").into(), + make.token(T![@]).into(), + make.whitespace(" ").into(), pat.syntax().clone().into(), ]; if self.syntax().parent().is_none() { - let (mut local, local_self) = SyntaxEditor::with_ast_node(self); + let (local, local_self) = SyntaxEditor::with_ast_node(self); let local_name = local_self.name().unwrap(); local.insert_all(Position::after(local_name.syntax()), elements); let edit = local.finish(); return ast::IdentPat::cast(edit.new_root().clone()).unwrap(); } else { - syntax_editor.insert_all(Position::after(name.syntax()), elements); + editor.insert_all(Position::after(name.syntax()), elements); } } } diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs index 4c20dc7a15..b2bd10b354 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, @@ -29,8 +30,8 @@ pub use mapping::{SyntaxMapping, SyntaxMappingBuilder}; #[derive(Debug)] pub struct SyntaxEditor { root: SyntaxNode, - changes: Vec<Change>, - annotations: Vec<(SyntaxElement, SyntaxAnnotation)>, + changes: RefCell<Vec<Change>>, + annotations: RefCell<Vec<(SyntaxElement, SyntaxAnnotation)>>, make: SyntaxFactory, } @@ -50,8 +51,8 @@ impl SyntaxEditor { let editor = Self { root: root.clone(), - changes: Vec::new(), - annotations: Vec::new(), + changes: RefCell::new(Vec::new()), + annotations: RefCell::new(Vec::new()), make: SyntaxFactory::with_mappings(), }; @@ -72,20 +73,17 @@ impl SyntaxEditor { &self.make } - pub fn add_annotation(&mut self, element: impl Element, annotation: SyntaxAnnotation) { - self.annotations.push((element.syntax_element(), annotation)) + pub fn add_annotation(&self, element: impl Element, annotation: SyntaxAnnotation) { + self.annotations.borrow_mut().push((element.syntax_element(), annotation)) } - pub fn add_annotation_all( - &mut self, - elements: Vec<impl Element>, - annotation: SyntaxAnnotation, - ) { + 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 {:?}", @@ -93,32 +91,28 @@ impl SyntaxEditor { self.root ); - self.changes.append(&mut other.changes); + 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.append(&mut other.annotations); + 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 insert_with_whitespace(&mut self, position: Position, 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( - &mut self, - position: Position, - mut elements: Vec<SyntaxElement>, - ) { + 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) { @@ -132,50 +126,52 @@ impl SyntaxEditor { self.insert_all(position, elements) } - pub fn delete(&mut self, element: impl Element) { + 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 { @@ -555,7 +551,8 @@ mod tests { .into(), ); - let (mut editor, root) = SyntaxEditor::with_ast_node(&root); + 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(); @@ -567,11 +564,11 @@ mod tests { editor.add_annotation(name.syntax(), placeholder_snippet); editor.add_annotation(name_ref.syntax(), placeholder_snippet); - let new_block = editor.make().block_expr( + let new_block = make.block_expr( [editor .make() .let_stmt( - editor.make().ident_pat(false, false, name.clone()).into(), + make.ident_pat(false, false, name.clone()).into(), None, Some(to_replace.clone().into()), ) @@ -612,31 +609,28 @@ mod tests { None, ); - let (mut editor, root) = SyntaxEditor::with_ast_node(&root); + let (editor, root) = SyntaxEditor::with_ast_node(&root); + let make = editor.make(); let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); editor.insert( Position::first_child_of(root.stmt_list().unwrap().syntax()), - editor - .make() - .let_stmt( - make::ext::simple_ident_pat(make::name("first")).into(), - None, - Some(make::expr_literal("1").into()), - ) - .syntax(), + make.let_stmt( + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ) + .syntax(), ); editor.insert( Position::after(second_let.syntax()), - editor - .make() - .let_stmt( - make::ext::simple_ident_pat(make::name("third")).into(), - None, - Some(make::expr_literal("3").into()), - ) - .syntax(), + make.let_stmt( + make::ext::simple_ident_pat(make::name("third")).into(), + None, + Some(make::expr_literal("3").into()), + ) + .syntax(), ); let edit = editor.finish(); @@ -666,22 +660,22 @@ mod tests { ), ); - let (mut editor, root) = SyntaxEditor::with_ast_node(&root); + 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 new_block_expr = - editor.make().block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); + let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); - let first_let = editor.make().let_stmt( + let first_let = make.let_stmt( make::ext::simple_ident_pat(make::name("first")).into(), None, Some(make::expr_literal("1").into()), ); - let third_let = editor.make().let_stmt( + let third_let = make.let_stmt( make::ext::simple_ident_pat(make::name("third")).into(), None, Some(make::expr_literal("3").into()), @@ -719,14 +713,14 @@ mod tests { None, ); - let (mut editor, root) = SyntaxEditor::with_ast_node(&root); + let (editor, root) = SyntaxEditor::with_ast_node(&root); + let make = editor.make(); let inner_block = root; - let new_block_expr = - editor.make().block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); + let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone()))); - let first_let = editor.make().let_stmt( + let first_let = make.let_stmt( make::ext::simple_ident_pat(make::name("first")).into(), None, Some(make::expr_literal("1").into()), @@ -766,7 +760,7 @@ mod tests { false, ); - let (mut editor, parent_fn) = SyntaxEditor::with_ast_node(&parent_fn); + 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()); @@ -793,7 +787,7 @@ mod tests { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); - let (mut editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); + let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); let target_expr = make::token(parser::SyntaxKind::UNDERSCORE); @@ -812,7 +806,7 @@ mod tests { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); - let (mut editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); + let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); let target_expr = make::expr_literal("3").clone_for_update(); @@ -831,7 +825,7 @@ mod tests { let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]); - let (mut editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); + let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list); let target_expr = make::ext::expr_unit().clone_for_update(); diff --git a/crates/syntax/src/syntax_editor/edit_algo.rs b/crates/syntax/src/syntax_editor/edit_algo.rs index 4e08daba7b..27ea03ec09 100644 --- a/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/crates/syntax/src/syntax_editor/edit_algo.rs @@ -35,7 +35,9 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { // - changed nodes become part of the changed node set (useful for the formatter to only change those parts) // - Propagate annotations - let SyntaxEditor { root, mut changes, annotations, make } = editor; + let SyntaxEditor { root, changes, annotations, make } = editor; + let mut changes = changes.into_inner(); + let annotations = annotations.into_inner(); let mappings = make.take(); let mut node_depths = FxHashMap::<SyntaxNode, usize>::default(); diff --git a/crates/syntax/src/syntax_editor/edits.rs b/crates/syntax/src/syntax_editor/edits.rs index 1718f1a51c..62fd7db8d3 100644 --- a/crates/syntax/src/syntax_editor/edits.rs +++ b/crates/syntax/src/syntax_editor/edits.rs @@ -12,20 +12,21 @@ pub trait GetOrCreateWhereClause: ast::HasGenericParams { fn get_or_create_where_clause( &self, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, new_preds: impl Iterator<Item = ast::WherePred>, ) { + let make = editor.make(); let existing = self.where_clause(); let all_preds: Vec<_> = existing.iter().flat_map(|wc| wc.predicates()).chain(new_preds).collect(); - let new_where = editor.make().where_clause(all_preds); + let new_where = make.where_clause(all_preds); if let Some(existing) = &existing { editor.replace(existing.syntax(), new_where.syntax()); } else if let Some(pos) = self.where_clause_position() { editor.insert_all( pos, - vec![editor.make().whitespace(" ").into(), new_where.syntax().clone().into()], + vec![make.whitespace(" ").into(), new_where.syntax().clone().into()], ); } } @@ -109,7 +110,7 @@ impl GetOrCreateWhereClause for ast::Enum { impl SyntaxEditor { /// Adds a new generic param to the function using `SyntaxEditor` - pub fn add_generic_param(&mut self, function: &Fn, new_param: GenericParam) { + pub fn add_generic_param(&self, function: &Fn, new_param: GenericParam) { match function.generic_param_list() { Some(generic_param_list) => match generic_param_list.generic_params().last() { Some(last_param) => { @@ -173,7 +174,8 @@ impl SyntaxEditor { } } -fn get_or_insert_comma_after(editor: &mut SyntaxEditor, syntax: &SyntaxNode) -> SyntaxToken { +fn get_or_insert_comma_after(editor: &SyntaxEditor, syntax: &SyntaxNode) -> SyntaxToken { + let make = editor.make(); match syntax .siblings_with_tokens(Direction::Next) .filter_map(|it| it.into_token()) @@ -181,7 +183,7 @@ fn get_or_insert_comma_after(editor: &mut SyntaxEditor, syntax: &SyntaxNode) -> { Some(it) => it, None => { - let comma = editor.make().token(T![,]); + let comma = make.token(T![,]); editor.insert(Position::after(syntax), &comma); comma } @@ -193,7 +195,7 @@ impl ast::AssocItemList { /// /// Attention! This function does align the first line of `item` with respect to `self`, /// but it does _not_ change indentation of other lines (if any). - pub fn add_items(&self, editor: &mut SyntaxEditor, items: Vec<ast::AssocItem>) { + pub fn add_items(&self, editor: &SyntaxEditor, items: Vec<ast::AssocItem>) { let (indent, position, whitespace) = match self.assoc_items().last() { Some(last_item) => ( IndentLevel::from_node(last_item.syntax()), @@ -227,15 +229,16 @@ impl ast::AssocItemList { impl ast::Impl { pub fn get_or_create_assoc_item_list_with_editor( &self, - editor: &mut SyntaxEditor, + editor: &SyntaxEditor, ) -> ast::AssocItemList { + let make = editor.make(); if let Some(list) = self.assoc_item_list() { list } else { - let list = editor.make().assoc_item_list_empty(); + let list = make.assoc_item_list_empty(); editor.insert_all( Position::last_child_of(self.syntax()), - vec![editor.make().whitespace(" ").into(), list.syntax().clone().into()], + vec![make.whitespace(" ").into(), list.syntax().clone().into()], ); list } @@ -243,7 +246,8 @@ impl ast::Impl { } impl ast::VariantList { - pub fn add_variant(&self, editor: &mut SyntaxEditor, variant: &ast::Variant) { + pub fn add_variant(&self, editor: &SyntaxEditor, variant: &ast::Variant) { + let make = editor.make(); let (indent, position) = match self.variants().last() { Some(last_item) => ( IndentLevel::from_node(last_item.syntax()), @@ -258,16 +262,16 @@ impl ast::VariantList { }, }; let elements: Vec<SyntaxElement> = vec![ - editor.make().whitespace(&format!("{}{indent}", "\n")).into(), + make.whitespace(&format!("{}{indent}", "\n")).into(), variant.syntax().clone().into(), - editor.make().token(T![,]).into(), + make.token(T![,]).into(), ]; editor.insert_all(position, elements); } } impl ast::Fn { - pub fn replace_or_insert_body(&self, editor: &mut SyntaxEditor, body: ast::BlockExpr) { + pub fn replace_or_insert_body(&self, editor: &SyntaxEditor, body: ast::BlockExpr) { if let Some(old_body) = self.body() { editor.replace(old_body.syntax(), body.syntax()); } else { @@ -283,7 +287,8 @@ impl ast::Fn { } } -fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) -> Option<()> { +fn normalize_ws_between_braces(editor: &SyntaxEditor, node: &SyntaxNode) -> Option<()> { + let make = editor.make(); let l = node .children_with_tokens() .filter_map(|it| it.into_token()) @@ -298,11 +303,11 @@ fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) -> match l.next_sibling_or_token() { Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => { if ws.next_sibling_or_token()?.into_token()? == r { - editor.replace(ws, editor.make().whitespace(&format!("\n{indent}"))); + editor.replace(ws, make.whitespace(&format!("\n{indent}"))); } } Some(ws) if ws.kind() == T!['}'] => { - editor.insert(Position::after(l), editor.make().whitespace(&format!("\n{indent}"))); + editor.insert(Position::after(l), make.whitespace(&format!("\n{indent}"))); } _ => (), } @@ -310,11 +315,11 @@ fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) -> } pub trait Removable: AstNode { - fn remove(&self, editor: &mut SyntaxEditor); + fn remove(&self, editor: &SyntaxEditor); } impl Removable for ast::TypeBoundList { - fn remove(&self, editor: &mut SyntaxEditor) { + fn remove(&self, editor: &SyntaxEditor) { match self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) { Some(colon) => editor.delete_all(colon..=self.syntax().clone().into()), None => editor.delete(self.syntax()), @@ -323,7 +328,8 @@ impl Removable for ast::TypeBoundList { } impl Removable for ast::Use { - fn remove(&self, editor: &mut SyntaxEditor) { + fn remove(&self, editor: &SyntaxEditor) { + let make = editor.make(); let next_ws = self .syntax() .next_sibling_or_token() @@ -335,7 +341,7 @@ impl Removable for ast::Use { if rest.is_empty() { editor.delete(next_ws.syntax()); } else { - editor.replace(next_ws.syntax(), editor.make().whitespace(rest)); + editor.replace(next_ws.syntax(), make.whitespace(rest)); } } } @@ -345,7 +351,7 @@ impl Removable for ast::Use { } impl Removable for ast::UseTree { - fn remove(&self, editor: &mut SyntaxEditor) { + fn remove(&self, editor: &SyntaxEditor) { for dir in [Direction::Next, Direction::Prev] { if let Some(next_use_tree) = neighbor(self, dir) { let separators = self |