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.rs249
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() {