From d6c4165182a8825a317604b549e6aa19bbf75680 Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Wed, 18 Aug 2021 16:05:23 +0500 Subject: [PATCH 1/3] internal: introduce in-place indenting API --- crates/syntax/src/ast/edit.rs | 29 +++++++++------ crates/syntax/src/ast/edit_in_place.rs | 50 +++++++++++++++++++++----- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 54ee19a626..5f7d4c9eae 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(in 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(in 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 { 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..d71a7af534 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 AttrsOwnerEdit for T {} +impl AttrsOwnerEdit for T {} impl ast::GenericParamList { pub fn add_generic_param(&self, generic_param: ast::GenericParam) { @@ -487,6 +483,26 @@ 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) -> &Self { + level.increase_indent(self.syntax()); + self + } + fn dedent(&self, level: IndentLevel) -> &Self { + level.decrease_indent(self.syntax()); + self + } + fn reset_indent(&self) -> &Self { + let level = IndentLevel::from_node(self.syntax()); + self.dedent(level) + } +} + +impl Indent for N {} + #[cfg(test)] mod tests { use std::fmt; @@ -526,4 +542,22 @@ mod tests { check_create_gpl::("enum E", "enum E<>"); check_create_gpl::("enum E {", "enum E<> {"); } + + #[test] + fn test_increase_indent() { + let arm_list = ast_mut_from_text::( + "fn foo() { + ; + ; +}", + ); + arm_list.indent(IndentLevel(2)); + assert_eq!( + arm_list.to_string(), + "fn foo() { + ; + ; + }", + ); + } } From 98847e4d5b77208a45869a93c56fec647e9f3bd2 Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Wed, 18 Aug 2021 18:08:14 +0500 Subject: [PATCH 2/3] internal: use the new `edit_in_place::Indent` API --- .../src/handlers/convert_iter_for_each_to_for.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 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..5150cc9a6f 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,25 @@ 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_else(|| 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().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()) }, ) } From 08694dc1ef53569f079108e4a5b154916dc53198 Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Wed, 18 Aug 2021 19:01:29 +0500 Subject: [PATCH 3/3] minor: address review comments --- .../src/handlers/convert_iter_for_each_to_for.rs | 5 +++-- crates/syntax/src/ast/edit.rs | 4 ++-- crates/syntax/src/ast/edit_in_place.rs | 10 ++++------ 3 files changed, 9 insertions(+), 10 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 5150cc9a6f..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 @@ -46,7 +46,7 @@ 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 range = stmt.as_ref().map_or_else(|| method.syntax(), AstNode::syntax).text_range(); + let range = stmt.as_ref().map_or(method.syntax(), AstNode::syntax).text_range(); acc.add( AssistId("convert_iter_for_each_to_for", AssistKind::RefactorRewrite), @@ -61,7 +61,8 @@ pub(crate) fn convert_iter_for_each_to_for(acc: &mut Assists, ctx: &AssistContex _ => make::block_expr(Vec::new(), Some(body)), } .clone_for_update(); - block.reset_indent().indent(indent); + block.reset_indent(); + block.indent(indent); let expr_for_loop = make::expr_for_loop(param, receiver, block); 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 5f7d4c9eae..15e99ff0e9 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs @@ -117,7 +117,7 @@ impl IndentLevel { /// } /// ``` /// if you indent the block, the `{` token would stay put. - pub(in super) fn increase_indent(self, node: &SyntaxNode) { + 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, @@ -132,7 +132,7 @@ impl IndentLevel { } } - pub(in super) fn decrease_indent(self, node: &SyntaxNode) { + 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, diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index d71a7af534..4e0d97c3fd 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -487,17 +487,15 @@ pub trait Indent: AstNode + Clone + Sized { fn indent_level(&self) -> IndentLevel { IndentLevel::from_node(self.syntax()) } - fn indent(&self, level: IndentLevel) -> &Self { + fn indent(&self, level: IndentLevel) { level.increase_indent(self.syntax()); - self } - fn dedent(&self, level: IndentLevel) -> &Self { + fn dedent(&self, level: IndentLevel) { level.decrease_indent(self.syntax()); - self } - fn reset_indent(&self) -> &Self { + fn reset_indent(&self) { let level = IndentLevel::from_node(self.syntax()); - self.dedent(level) + self.dedent(level); } }