diff options
author | Vladyslav Katasonov <[email protected]> | 2021-02-05 02:00:53 +0000 |
---|---|---|
committer | Vladyslav Katasonov <[email protected]> | 2021-02-05 02:44:08 +0000 |
commit | 7eaa3e56a01e9a275129c76817232559b0e20f2b (patch) | |
tree | 4fc783439bcc784951763b2922fa2ddf462be760 /crates/assists/src/handlers/extract_function.rs | |
parent | 876ca603166dcd2680652b42fb6bdd5358e59aa6 (diff) |
allow extracted body to be indented(dedent it)
Diffstat (limited to 'crates/assists/src/handlers/extract_function.rs')
-rw-r--r-- | crates/assists/src/handlers/extract_function.rs | 114 |
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( | |||
818 | fn format_function_body_to( | 832 | fn 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" | ||
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 | } | ||
2156 | }", | ||
2157 | ); | ||
2158 | } | ||
2071 | } | 2159 | } |