Skip to content

Commit 7eaa3e5

Browse files
committed
allow extracted body to be indented(dedent it)
1 parent 876ca60 commit 7eaa3e5

File tree

1 file changed

+101
-13
lines changed

1 file changed

+101
-13
lines changed

crates/assists/src/handlers/extract_function.rs

Lines changed: 101 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use syntax::{
1313
edit::{AstNodeEdit, IndentLevel},
1414
AstNode,
1515
},
16-
Direction, SyntaxElement,
16+
AstToken, Direction, SyntaxElement,
1717
SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR},
1818
SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, T,
1919
};
@@ -105,9 +105,10 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
105105

106106
builder.replace(target_range, format_replacement(ctx, &fun));
107107

108-
let indent = IndentLevel::from_node(&insert_after);
108+
let new_indent = IndentLevel::from_node(&insert_after);
109+
let old_indent = fun.body.indent_level();
109110

110-
let fn_def = format_function(ctx, module, &fun, indent);
111+
let fn_def = format_function(ctx, module, &fun, old_indent, new_indent);
111112
let insert_offset = insert_after.text_range().end();
112113
builder.insert(insert_offset, fn_def);
113114
},
@@ -260,6 +261,18 @@ impl FunctionBody {
260261
Some(FunctionBody::Span { elements, leading_indent })
261262
}
262263

264+
fn indent_level(&self) -> IndentLevel {
265+
match &self {
266+
FunctionBody::Expr(expr) => IndentLevel::from_node(expr.syntax()),
267+
FunctionBody::Span { elements, .. } => elements
268+
.iter()
269+
.filter_map(SyntaxElement::as_node)
270+
.map(IndentLevel::from_node)
271+
.min_by_key(|level| level.0)
272+
.expect("body must contain at least one node"),
273+
}
274+
}
275+
263276
fn tail_expr(&self) -> Option<ast::Expr> {
264277
match &self {
265278
FunctionBody::Expr(expr) => Some(expr.clone()),
@@ -747,16 +760,17 @@ fn format_function(
747760
ctx: &AssistContext,
748761
module: hir::Module,
749762
fun: &Function,
750-
indent: IndentLevel,
763+
old_indent: IndentLevel,
764+
new_indent: IndentLevel,
751765
) -> String {
752766
let mut fn_def = String::new();
753-
format_to!(fn_def, "\n\n{}fn $0{}(", indent, fun.name);
767+
format_to!(fn_def, "\n\n{}fn $0{}(", new_indent, fun.name);
754768
format_function_param_list_to(&mut fn_def, ctx, module, fun);
755769
fn_def.push(')');
756770
format_function_ret_to(&mut fn_def, ctx, module, fun);
757771
fn_def.push_str(" {");
758-
format_function_body_to(&mut fn_def, ctx, indent, fun);
759-
format_to!(fn_def, "{}}}", indent);
772+
format_function_body_to(&mut fn_def, ctx, old_indent, new_indent, fun);
773+
format_to!(fn_def, "{}}}", new_indent);
760774

761775
fn_def
762776
}
@@ -818,20 +832,32 @@ fn format_function_ret_to(
818832
fn format_function_body_to(
819833
fn_def: &mut String,
820834
ctx: &AssistContext,
821-
indent: IndentLevel,
835+
old_indent: IndentLevel,
836+
new_indent: IndentLevel,
822837
fun: &Function,
823838
) {
824839
match &fun.body {
825840
FunctionBody::Expr(expr) => {
826841
fn_def.push('\n');
827-
let expr = expr.indent(indent);
842+
let expr = expr.dedent(old_indent).indent(new_indent + 1);
828843
let expr = fix_param_usages(ctx, &fun.params, expr.syntax());
829-
format_to!(fn_def, "{}{}", indent + 1, expr);
844+
format_to!(fn_def, "{}{}", new_indent + 1, expr);
830845
fn_def.push('\n');
831846
}
832847
FunctionBody::Span { elements, leading_indent } => {
833848
format_to!(fn_def, "{}", leading_indent);
834-
for element in elements {
849+
let new_indent_str = format!("\n{}", new_indent + 1);
850+
for mut element in elements {
851+
let new_ws;
852+
if let Some(ws) = element.as_token().cloned().and_then(ast::Whitespace::cast) {
853+
let text = ws.syntax().text();
854+
if text.contains('\n') {
855+
let new_text = text.replace(&format!("\n{}", old_indent), &new_indent_str);
856+
new_ws = ast::make::tokens::whitespace(&new_text).into();
857+
element = &new_ws;
858+
}
859+
}
860+
835861
match element {
836862
syntax::NodeOrToken::Node(node) => {
837863
format_to!(fn_def, "{}", fix_param_usages(ctx, &fun.params, node));
@@ -849,9 +875,9 @@ fn format_function_body_to(
849875

850876
match fun.vars_defined_in_body_and_outlive.as_slice() {
851877
[] => {}
852-
[var] => format_to!(fn_def, "{}{}\n", indent + 1, var.name(ctx.db()).unwrap()),
878+
[var] => format_to!(fn_def, "{}{}\n", new_indent + 1, var.name(ctx.db()).unwrap()),
853879
[v0, vs @ ..] => {
854-
format_to!(fn_def, "{}({}", indent + 1, v0.name(ctx.db()).unwrap());
880+
format_to!(fn_def, "{}({}", new_indent + 1, v0.name(ctx.db()).unwrap());
855881
for var in vs {
856882
format_to!(fn_def, ", {}", var.name(ctx.db()).unwrap());
857883
}
@@ -2065,6 +2091,68 @@ fn foo() {
20652091
20662092
fn $0fun_name(c: &Counter) {
20672093
let n = c.0;
2094+
}",
2095+
);
2096+
}
2097+
2098+
#[test]
2099+
fn indented_stmts() {
2100+
check_assist(
2101+
extract_function,
2102+
r"
2103+
fn foo() {
2104+
if true {
2105+
loop {
2106+
$0let n = 1;
2107+
let m = 2;$0
2108+
}
2109+
}
2110+
}",
2111+
r"
2112+
fn foo() {
2113+
if true {
2114+
loop {
2115+
fun_name();
2116+
}
2117+
}
2118+
}
2119+
2120+
fn $0fun_name() {
2121+
let n = 1;
2122+
let m = 2;
2123+
}",
2124+
);
2125+
}
2126+
2127+
#[test]
2128+
fn indented_stmts_inside_mod() {
2129+
check_assist(
2130+
extract_function,
2131+
r"
2132+
mod bar {
2133+
fn foo() {
2134+
if true {
2135+
loop {
2136+
$0let n = 1;
2137+
let m = 2;$0
2138+
}
2139+
}
2140+
}
2141+
}",
2142+
r"
2143+
mod bar {
2144+
fn foo() {
2145+
if true {
2146+
loop {
2147+
fun_name();
2148+
}
2149+
}
2150+
}
2151+
2152+
fn $0fun_name() {
2153+
let n = 1;
2154+
let m = 2;
2155+
}
20682156
}",
20692157
);
20702158
}

0 commit comments

Comments
 (0)