Unnamed repository; edit this file 'description' to name the repository.
Merge #9944
9944: internal: introduce in-place indenting API r=matklad a=iDawer Introduce `edit_in_place::Indent` that uses mutable tree API and intended to replace `edit::AstNodeEdit`. Closes #9903 Co-authored-by: Dawer <[email protected]>
bors[bot] 2021-08-24
parent 49c02b9 · parent 08694dc · commit 6287d38
-rw-r--r--crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs16
-rw-r--r--crates/syntax/src/ast/edit.rs29
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs48
3 files changed, 68 insertions, 25 deletions
diff --git a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
index a7344572ba..f45dc782e3 100644
--- a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -1,6 +1,6 @@
use ide_db::helpers::FamousDefs;
use syntax::{
- ast::{self, edit::AstNodeEdit, make, ArgListOwner},
+ ast::{self, edit_in_place::Indent, make, ArgListOwner},
AstNode,
};
@@ -46,24 +46,26 @@ pub(crate) fn convert_iter_for_each_to_for(acc: &mut Assists, ctx: &AssistContex
let body = closure.body()?;
let stmt = method.syntax().parent().and_then(ast::ExprStmt::cast);
- let syntax = stmt.as_ref().map_or(method.syntax(), |stmt| stmt.syntax());
+ let range = stmt.as_ref().map_or(method.syntax(), AstNode::syntax).text_range();
acc.add(
AssistId("convert_iter_for_each_to_for", AssistKind::RefactorRewrite),
"Replace this `Iterator::for_each` with a for loop",
- syntax.text_range(),
+ range,
|builder| {
- let indent = stmt.as_ref().map_or(method.indent_level(), |stmt| stmt.indent_level());
+ let indent =
+ stmt.as_ref().map_or_else(|| method.indent_level(), ast::ExprStmt::indent_level);
let block = match body {
ast::Expr::BlockExpr(block) => block,
_ => make::block_expr(Vec::new(), Some(body)),
}
- .reset_indent()
- .indent(indent);
+ .clone_for_update();
+ block.reset_indent();
+ block.indent(indent);
let expr_for_loop = make::expr_for_loop(param, receiver, block);
- builder.replace(syntax.text_range(), expr_for_loop.syntax().text())
+ builder.replace(range, expr_for_loop.to_string())
},
)
}
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 54ee19a626..15e99ff0e9 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -117,9 +117,8 @@ impl IndentLevel {
/// }
/// ```
/// if you indent the block, the `{` token would stay put.
- fn increase_indent(self, node: SyntaxNode) -> SyntaxNode {
- let res = node.clone_subtree().clone_for_update();
- let tokens = res.preorder_with_tokens().filter_map(|event| match event {
+ pub(super) fn increase_indent(self, node: &SyntaxNode) {
+ let tokens = node.preorder_with_tokens().filter_map(|event| match event {
rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
_ => None,
});
@@ -131,12 +130,10 @@ impl IndentLevel {
}
}
}
- res.clone_subtree()
}
- fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode {
- let res = node.clone_subtree().clone_for_update();
- let tokens = res.preorder_with_tokens().filter_map(|event| match event {
+ 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,
});
@@ -150,7 +147,6 @@ impl IndentLevel {
}
}
}
- res.clone_subtree()
}
}
@@ -158,17 +154,30 @@ 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 {
- Self::cast(level.increase_indent(self.syntax().clone())).unwrap()
+ 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(indent_inner(self.syntax(), level)).unwrap()
}
#[must_use]
fn dedent(&self, level: IndentLevel) -> Self {
- Self::cast(level.decrease_indent(self.syntax().clone())).unwrap()
+ 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()
}
#[must_use]
fn reset_indent(&self) -> Self {
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index 3bee11ee03..4e0d97c3fd 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -7,11 +7,7 @@ use rowan::SyntaxElement;
use crate::{
algo::neighbor,
- ast::{
- self,
- edit::{AstNodeEdit, IndentLevel},
- make, GenericParamsOwner,
- },
+ ast::{self, edit::IndentLevel, make, GenericParamsOwner},
ted::{self, Position},
AstNode, AstToken, Direction,
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
@@ -20,7 +16,7 @@ use crate::{
use super::NameOwner;
-pub trait GenericParamsOwnerEdit: ast::GenericParamsOwner + AstNodeEdit {
+pub trait GenericParamsOwnerEdit: ast::GenericParamsOwner {
fn get_or_create_generic_param_list(&self) -> ast::GenericParamList;
fn get_or_create_where_clause(&self) -> ast::WhereClause;
}
@@ -198,7 +194,7 @@ fn create_generic_param_list(position: Position) -> ast::GenericParamList {
gpl
}
-pub trait AttrsOwnerEdit: ast::AttrsOwner + AstNodeEdit {
+pub trait AttrsOwnerEdit: ast::AttrsOwner {
fn remove_attrs_and_docs(&self) {
remove_attrs_and_docs(self.syntax());
@@ -222,7 +218,7 @@ pub trait AttrsOwnerEdit: ast::AttrsOwner + AstNodeEdit {
}
}
-impl<T: ast::AttrsOwner + AstNodeEdit> AttrsOwnerEdit for T {}
+impl<T: ast::AttrsOwner> AttrsOwnerEdit for T {}
impl ast::GenericParamList {
pub fn add_generic_param(&self, generic_param: ast::GenericParam) {
@@ -487,6 +483,24 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
Some(())
}
+pub trait Indent: AstNode + Clone + Sized {
+ fn indent_level(&self) -> IndentLevel {
+ IndentLevel::from_node(self.syntax())
+ }
+ fn indent(&self, level: IndentLevel) {
+ level.increase_indent(self.syntax());
+ }
+ fn dedent(&self, level: IndentLevel) {
+ level.decrease_indent(self.syntax());
+ }
+ fn reset_indent(&self) {
+ let level = IndentLevel::from_node(self.syntax());
+ self.dedent(level);
+ }
+}
+
+impl<N: AstNode + Clone> Indent for N {}
+
#[cfg(test)]
mod tests {
use std::fmt;
@@ -526,4 +540,22 @@ mod tests {
check_create_gpl::<ast::Enum>("enum E", "enum E<>");
check_create_gpl::<ast::Enum>("enum E {", "enum E<> {");
}
+
+ #[test]
+ fn test_increase_indent() {
+ let arm_list = ast_mut_from_text::<ast::Fn>(
+ "fn foo() {
+ ;
+ ;
+}",
+ );
+ arm_list.indent(IndentLevel(2));
+ assert_eq!(
+ arm_list.to_string(),
+ "fn foo() {
+ ;
+ ;
+ }",
+ );
+ }
}