Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/syntax/src/ast/edit.rs')
| -rw-r--r-- | crates/syntax/src/ast/edit.rs | 91 |
1 files changed, 65 insertions, 26 deletions
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 37cb4a434f..9b30642fe4 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs @@ -6,9 +6,12 @@ use std::{fmt, iter, ops}; use crate::{ AstToken, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, ast::{self, AstNode, make}, + syntax_editor::{SyntaxEditor, SyntaxMappingBuilder}, ted, }; +use super::syntax_factory::SyntaxFactory; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct IndentLevel(pub u8); @@ -86,61 +89,97 @@ impl IndentLevel { _ => None, }); for token in tokens { - if let Some(ws) = ast::Whitespace::cast(token) { - if ws.text().contains('\n') { - let new_ws = make::tokens::whitespace(&format!("{}{self}", ws.syntax())); - ted::replace(ws.syntax(), &new_ws); - } + if let Some(ws) = ast::Whitespace::cast(token) + && ws.text().contains('\n') + { + let new_ws = make::tokens::whitespace(&format!("{}{self}", ws.syntax())); + ted::replace(ws.syntax(), &new_ws); } } } + pub(super) fn clone_increase_indent(self, node: &SyntaxNode) -> SyntaxNode { + let node = node.clone_subtree(); + let mut editor = SyntaxEditor::new(node.clone()); + let tokens = node + .preorder_with_tokens() + .filter_map(|event| match event { + rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it), + _ => None, + }) + .filter_map(ast::Whitespace::cast) + .filter(|ws| ws.text().contains('\n')); + for ws in tokens { + let new_ws = make::tokens::whitespace(&format!("{}{self}", ws.syntax())); + editor.replace(ws.syntax(), &new_ws); + } + editor.finish().new_root().clone() + } + pub(super) fn decrease_indent(self, node: &SyntaxNode) { let tokens = node.preorder_with_tokens().filter_map(|event| match event { rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it), _ => None, }); for token in tokens { - if let Some(ws) = ast::Whitespace::cast(token) { - if ws.text().contains('\n') { - let new_ws = make::tokens::whitespace( - &ws.syntax().text().replace(&format!("\n{self}"), "\n"), - ); - ted::replace(ws.syntax(), &new_ws); - } + if let Some(ws) = ast::Whitespace::cast(token) + && ws.text().contains('\n') + { + let new_ws = make::tokens::whitespace( + &ws.syntax().text().replace(&format!("\n{self}"), "\n"), + ); + ted::replace(ws.syntax(), &new_ws); } } } + + pub(super) fn clone_decrease_indent(self, node: &SyntaxNode) -> SyntaxNode { + let node = node.clone_subtree(); + let mut editor = SyntaxEditor::new(node.clone()); + let tokens = node + .preorder_with_tokens() + .filter_map(|event| match event { + rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it), + _ => None, + }) + .filter_map(ast::Whitespace::cast) + .filter(|ws| ws.text().contains('\n')); + for ws in tokens { + let new_ws = + make::tokens::whitespace(&ws.syntax().text().replace(&format!("\n{self}"), "\n")); + editor.replace(ws.syntax(), &new_ws); + } + editor.finish().new_root().clone() + } } fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { iter::successors(Some(token), |token| token.prev_token()) } -/// Soft-deprecated in favor of mutable tree editing API `edit_in_place::Ident`. pub trait AstNodeEdit: AstNode + Clone + Sized { fn indent_level(&self) -> IndentLevel { IndentLevel::from_node(self.syntax()) } #[must_use] fn indent(&self, level: IndentLevel) -> Self { - fn indent_inner(node: &SyntaxNode, level: IndentLevel) -> SyntaxNode { - let res = node.clone_subtree().clone_for_update(); - level.increase_indent(&res); - res.clone_subtree() + Self::cast(level.clone_increase_indent(self.syntax())).unwrap() + } + #[must_use] + fn indent_with_mapping(&self, level: IndentLevel, make: &SyntaxFactory) -> Self { + let new_node = self.indent(level); + if let Some(mut mapping) = make.mappings() { + let mut builder = SyntaxMappingBuilder::new(new_node.syntax().clone()); + for (old, new) in self.syntax().children().zip(new_node.syntax().children()) { + builder.map_node(old, new); + } + builder.finish(&mut mapping); } - - Self::cast(indent_inner(self.syntax(), level)).unwrap() + new_node } #[must_use] fn dedent(&self, level: IndentLevel) -> Self { - fn dedent_inner(node: &SyntaxNode, level: IndentLevel) -> SyntaxNode { - let res = node.clone_subtree().clone_for_update(); - level.decrease_indent(&res); - res.clone_subtree() - } - - Self::cast(dedent_inner(self.syntax(), level)).unwrap() + Self::cast(level.clone_decrease_indent(self.syntax())).unwrap() } #[must_use] fn reset_indent(&self) -> Self { |