aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists/src/handlers')
-rw-r--r--crates/assists/src/handlers/extract_function.rs114
1 files changed, 101 insertions, 13 deletions
diff --git a/crates/assists/src/handlers/extract_function.rs b/crates/assists/src/handlers/extract_function.rs
index 49ea1c4b3..d876eabca 100644
--- a/crates/assists/src/handlers/extract_function.rs
+++ b/crates/assists/src/handlers/extract_function.rs
@@ -13,7 +13,7 @@ use syntax::{
13 edit::{AstNodeEdit, IndentLevel}, 13 edit::{AstNodeEdit, IndentLevel},
14 AstNode, 14 AstNode,
15 }, 15 },
16 Direction, SyntaxElement, 16 AstToken, Direction, SyntaxElement,
17 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, 17 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR},
18 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, T, 18 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, T,
19}; 19};
@@ -105,9 +105,10 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
105 105
106 builder.replace(target_range, format_replacement(ctx, &fun)); 106 builder.replace(target_range, format_replacement(ctx, &fun));
107 107
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();
109 110
110 let fn_def = format_function(ctx, module, &fun, indent); 111 let fn_def = format_function(ctx, module, &fun, old_indent, new_indent);
111 let insert_offset = insert_after.text_range().end(); 112 let insert_offset = insert_after.text_range().end();
112 builder.insert(insert_offset, fn_def); 113 builder.insert(insert_offset, fn_def);
113 }, 114 },
@@ -260,6 +261,18 @@ impl FunctionBody {
260 Some(FunctionBody::Span { elements, leading_indent }) 261 Some(FunctionBody::Span { elements, leading_indent })
261 } 262 }
262 263
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
263 fn tail_expr(&self) -> Option<ast::Expr> { 276 fn tail_expr(&self) -> Option<ast::Expr> {
264 match &self { 277 match &self {
265 FunctionBody::Expr(expr) => Some(expr.clone()), 278 FunctionBody::Expr(expr) => Some(expr.clone()),
@@ -747,16 +760,17 @@ fn format_function(
747 ctx: &AssistContext, 760 ctx: &AssistContext,
748 module: hir::Module, 761 module: hir::Module,
749 fun: &Function, 762 fun: &Function,
750 indent: IndentLevel, 763 old_indent: IndentLevel,
764 new_indent: IndentLevel,
751) -> String { 765) -> String {
752 let mut fn_def = String::new(); 766 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);
754 format_function_param_list_to(&mut fn_def, ctx, module, fun); 768 format_function_param_list_to(&mut fn_def, ctx, module, fun);
755 fn_def.push(')'); 769 fn_def.push(')');
756 format_function_ret_to(&mut fn_def, ctx, module, fun); 770 format_function_ret_to(&mut fn_def, ctx, module, fun);
757 fn_def.push_str(" {"); 771 fn_def.push_str(" {");
758 format_function_body_to(&mut fn_def, ctx, indent, fun); 772 format_function_body_to(&mut fn_def, ctx, old_indent, new_indent, fun);
759 format_to!(fn_def, "{}}}", indent); 773 format_to!(fn_def, "{}}}", new_indent);
760 774
761 fn_def 775 fn_def
762} 776}
@@ -818,20 +832,32 @@ fn format_function_ret_to(
818fn format_function_body_to( 832fn format_function_body_to(
819 fn_def: &mut String, 833 fn_def: &mut String,
820 ctx: &AssistContext, 834 ctx: &AssistContext,
821 indent: IndentLevel, 835 old_indent: IndentLevel,
836 new_indent: IndentLevel,
822 fun: &Function, 837 fun: &Function,
823) { 838) {
824 match &fun.body { 839 match &fun.body {
825 FunctionBody::Expr(expr) => { 840 FunctionBody::Expr(expr) => {
826 fn_def.push('\n'); 841 fn_def.push('\n');
827 let expr = expr.indent(indent); 842 let expr = expr.dedent(old_indent).indent(new_indent + 1);
828 let expr = fix_param_usages(ctx, &fun.params, expr.syntax()); 843 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);
830 fn_def.push('\n'); 845 fn_def.push('\n');
831 } 846 }
832 FunctionBody::Span { elements, leading_indent } => { 847 FunctionBody::Span { elements, leading_indent } => {
833 format_to!(fn_def, "{}", leading_indent); 848 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
835 match element { 861 match element {
836 syntax::NodeOrToken::Node(node) => { 862 syntax::NodeOrToken::Node(node) => {
837 format_to!(fn_def, "{}", fix_param_usages(ctx, &fun.params, node)); 863 format_to!(fn_def, "{}", fix_param_usages(ctx, &fun.params, node));
@@ -849,9 +875,9 @@ fn format_function_body_to(
849 875
850 match fun.vars_defined_in_body_and_outlive.as_slice() { 876 match fun.vars_defined_in_body_and_outlive.as_slice() {
851 [] => {} 877 [] => {}
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()),
853 [v0, vs @ ..] => { 879 [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());
855 for var in vs { 881 for var in vs {
856 format_to!(fn_def, ", {}", var.name(ctx.db()).unwrap()); 882 format_to!(fn_def, ", {}", var.name(ctx.db()).unwrap());
857 } 883 }
@@ -2068,4 +2094,66 @@ fn $0fun_name(c: &Counter) {
2068}", 2094}",
2069 ); 2095 );
2070 } 2096 }
2097
2098 #[test]
2099 fn indented_stmts() {
2100 check_assist(
2101 extract_function,
2102 r"
2103fn foo() {
2104 if true {
2105 loop {
2106 $0let n = 1;
2107 let m = 2;$0
2108 }
2109 }
2110}",
2111 r"
2112fn foo() {
2113 if true {
2114 loop {
2115 fun_name();
2116 }
2117 }
2118}
2119
2120fn $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"
2132mod bar {
2133 fn foo() {
2134 if true {
2135 loop {
2136 $0let n = 1;
2137 let m = 2;$0
2138 }
2139 }
2140 }
2141}",
2142 r"
2143mod 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 }
2156}",
2157 );
2158 }
2071} 2159}