mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 13:48:50 +00:00
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 <7803845+iDawer@users.noreply.github.com>
This commit is contained in:
commit
6287d388c0
3 changed files with 68 additions and 25 deletions
|
@ -1,6 +1,6 @@
|
||||||
use ide_db::helpers::FamousDefs;
|
use ide_db::helpers::FamousDefs;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, edit::AstNodeEdit, make, ArgListOwner},
|
ast::{self, edit_in_place::Indent, make, ArgListOwner},
|
||||||
AstNode,
|
AstNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,24 +46,26 @@ pub(crate) fn convert_iter_for_each_to_for(acc: &mut Assists, ctx: &AssistContex
|
||||||
let body = closure.body()?;
|
let body = closure.body()?;
|
||||||
|
|
||||||
let stmt = method.syntax().parent().and_then(ast::ExprStmt::cast);
|
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(
|
acc.add(
|
||||||
AssistId("convert_iter_for_each_to_for", AssistKind::RefactorRewrite),
|
AssistId("convert_iter_for_each_to_for", AssistKind::RefactorRewrite),
|
||||||
"Replace this `Iterator::for_each` with a for loop",
|
"Replace this `Iterator::for_each` with a for loop",
|
||||||
syntax.text_range(),
|
range,
|
||||||
|builder| {
|
|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 {
|
let block = match body {
|
||||||
ast::Expr::BlockExpr(block) => block,
|
ast::Expr::BlockExpr(block) => block,
|
||||||
_ => make::block_expr(Vec::new(), Some(body)),
|
_ => make::block_expr(Vec::new(), Some(body)),
|
||||||
}
|
}
|
||||||
.reset_indent()
|
.clone_for_update();
|
||||||
.indent(indent);
|
block.reset_indent();
|
||||||
|
block.indent(indent);
|
||||||
|
|
||||||
let expr_for_loop = make::expr_for_loop(param, receiver, block);
|
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())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,9 +117,8 @@ impl IndentLevel {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
/// if you indent the block, the `{` token would stay put.
|
/// if you indent the block, the `{` token would stay put.
|
||||||
fn increase_indent(self, node: SyntaxNode) -> SyntaxNode {
|
pub(super) fn increase_indent(self, node: &SyntaxNode) {
|
||||||
let res = node.clone_subtree().clone_for_update();
|
let tokens = node.preorder_with_tokens().filter_map(|event| match event {
|
||||||
let tokens = res.preorder_with_tokens().filter_map(|event| match event {
|
|
||||||
rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
|
rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
|
@ -131,12 +130,10 @@ impl IndentLevel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.clone_subtree()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode {
|
pub(super) fn decrease_indent(self, node: &SyntaxNode) {
|
||||||
let res = node.clone_subtree().clone_for_update();
|
let tokens = node.preorder_with_tokens().filter_map(|event| match event {
|
||||||
let tokens = res.preorder_with_tokens().filter_map(|event| match event {
|
|
||||||
rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
|
rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
|
||||||
_ => None,
|
_ => 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())
|
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 {
|
pub trait AstNodeEdit: AstNode + Clone + Sized {
|
||||||
fn indent_level(&self) -> IndentLevel {
|
fn indent_level(&self) -> IndentLevel {
|
||||||
IndentLevel::from_node(self.syntax())
|
IndentLevel::from_node(self.syntax())
|
||||||
}
|
}
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn indent(&self, level: IndentLevel) -> Self {
|
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]
|
#[must_use]
|
||||||
fn dedent(&self, level: IndentLevel) -> Self {
|
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]
|
#[must_use]
|
||||||
fn reset_indent(&self) -> Self {
|
fn reset_indent(&self) -> Self {
|
||||||
|
|
|
@ -7,11 +7,7 @@ use rowan::SyntaxElement;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
algo::neighbor,
|
algo::neighbor,
|
||||||
ast::{
|
ast::{self, edit::IndentLevel, make, GenericParamsOwner},
|
||||||
self,
|
|
||||||
edit::{AstNodeEdit, IndentLevel},
|
|
||||||
make, GenericParamsOwner,
|
|
||||||
},
|
|
||||||
ted::{self, Position},
|
ted::{self, Position},
|
||||||
AstNode, AstToken, Direction,
|
AstNode, AstToken, Direction,
|
||||||
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
|
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
|
||||||
|
@ -20,7 +16,7 @@ use crate::{
|
||||||
|
|
||||||
use super::NameOwner;
|
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_generic_param_list(&self) -> ast::GenericParamList;
|
||||||
fn get_or_create_where_clause(&self) -> ast::WhereClause;
|
fn get_or_create_where_clause(&self) -> ast::WhereClause;
|
||||||
}
|
}
|
||||||
|
@ -198,7 +194,7 @@ fn create_generic_param_list(position: Position) -> ast::GenericParamList {
|
||||||
gpl
|
gpl
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AttrsOwnerEdit: ast::AttrsOwner + AstNodeEdit {
|
pub trait AttrsOwnerEdit: ast::AttrsOwner {
|
||||||
fn remove_attrs_and_docs(&self) {
|
fn remove_attrs_and_docs(&self) {
|
||||||
remove_attrs_and_docs(self.syntax());
|
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 {
|
impl ast::GenericParamList {
|
||||||
pub fn add_generic_param(&self, generic_param: ast::GenericParam) {
|
pub fn add_generic_param(&self, generic_param: ast::GenericParam) {
|
||||||
|
@ -487,6 +483,24 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
|
||||||
Some(())
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::fmt;
|
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<>");
|
||||||
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() {
|
||||||
|
;
|
||||||
|
;
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue