diff options
Diffstat (limited to 'crates/ra_assists/src')
10 files changed, 362 insertions, 130 deletions
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 3085c4330..a680f752b 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -49,7 +49,7 @@ use crate::{Assist, AssistId, GroupLabel, ResolvedAssist}; | |||
49 | /// easier to just compute the edit eagerly :-) | 49 | /// easier to just compute the edit eagerly :-) |
50 | pub(crate) struct AssistContext<'a> { | 50 | pub(crate) struct AssistContext<'a> { |
51 | pub(crate) sema: Semantics<'a, RootDatabase>, | 51 | pub(crate) sema: Semantics<'a, RootDatabase>, |
52 | pub(super) db: &'a RootDatabase, | 52 | pub(crate) db: &'a RootDatabase, |
53 | pub(crate) frange: FileRange, | 53 | pub(crate) frange: FileRange, |
54 | source_file: SourceFile, | 54 | source_file: SourceFile, |
55 | } | 55 | } |
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 55409e501..7ced00626 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -23,6 +23,7 @@ use crate::{AssistContext, AssistId, Assists}; | |||
23 | // ``` | 23 | // ``` |
24 | pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 24 | pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; | 25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; |
26 | let module = ctx.sema.scope(stmt.syntax()).module()?; | ||
26 | let expr = stmt.initializer()?; | 27 | let expr = stmt.initializer()?; |
27 | let pat = stmt.pat()?; | 28 | let pat = stmt.pat()?; |
28 | // Must be a binding | 29 | // Must be a binding |
@@ -57,17 +58,17 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
57 | return None; | 58 | return None; |
58 | } | 59 | } |
59 | 60 | ||
60 | let db = ctx.db; | 61 | let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; |
61 | let new_type_string = ty.display_truncated(db, None).to_string(); | ||
62 | acc.add( | 62 | acc.add( |
63 | AssistId("add_explicit_type"), | 63 | AssistId("add_explicit_type"), |
64 | format!("Insert explicit type '{}'", new_type_string), | 64 | format!("Insert explicit type '{}'", inferred_type), |
65 | pat_range, | 65 | pat_range, |
66 | |edit| { | 66 | |builder| match ascribed_ty { |
67 | if let Some(ascribed_ty) = ascribed_ty { | 67 | Some(ascribed_ty) => { |
68 | edit.replace(ascribed_ty.syntax().text_range(), new_type_string); | 68 | builder.replace(ascribed_ty.syntax().text_range(), inferred_type); |
69 | } else { | 69 | } |
70 | edit.insert(name_range.end(), format!(": {}", new_type_string)); | 70 | None => { |
71 | builder.insert(name_range.end(), format!(": {}", inferred_type)); | ||
71 | } | 72 | } |
72 | }, | 73 | }, |
73 | ) | 74 | ) |
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 275184e24..6a49b7dbd 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs | |||
@@ -1,8 +1,5 @@ | |||
1 | use ra_ide_db::RootDatabase; | 1 | use ra_ide_db::RootDatabase; |
2 | use ra_syntax::{ | 2 | use ra_syntax::ast::{self, AstNode, NameOwner}; |
3 | ast::{self, AstNode, NameOwner}, | ||
4 | TextSize, | ||
5 | }; | ||
6 | use stdx::format_to; | 3 | use stdx::format_to; |
7 | use test_utils::tested_by; | 4 | use test_utils::tested_by; |
8 | 5 | ||
@@ -69,7 +66,6 @@ impl From<{0}> for {1} {{ | |||
69 | variant_name | 66 | variant_name |
70 | ); | 67 | ); |
71 | edit.insert(start_offset, buf); | 68 | edit.insert(start_offset, buf); |
72 | edit.set_cursor(start_offset + TextSize::of("\n\n")); | ||
73 | }, | 69 | }, |
74 | ) | 70 | ) |
75 | } | 71 | } |
@@ -97,19 +93,20 @@ fn existing_from_impl( | |||
97 | 93 | ||
98 | #[cfg(test)] | 94 | #[cfg(test)] |
99 | mod tests { | 95 | mod tests { |
100 | use super::*; | 96 | use test_utils::covers; |
101 | 97 | ||
102 | use crate::tests::{check_assist, check_assist_not_applicable}; | 98 | use crate::tests::{check_assist, check_assist_not_applicable}; |
103 | use test_utils::covers; | 99 | |
100 | use super::*; | ||
104 | 101 | ||
105 | #[test] | 102 | #[test] |
106 | fn test_add_from_impl_for_enum() { | 103 | fn test_add_from_impl_for_enum() { |
107 | check_assist( | 104 | check_assist( |
108 | add_from_impl_for_enum, | 105 | add_from_impl_for_enum, |
109 | "enum A { <|>One(u32) }", | 106 | "enum A { <|>One(u32) }", |
110 | r#"enum A { One(u32) } | 107 | r#"enum A { <|>One(u32) } |
111 | 108 | ||
112 | <|>impl From<u32> for A { | 109 | impl From<u32> for A { |
113 | fn from(v: u32) -> Self { | 110 | fn from(v: u32) -> Self { |
114 | A::One(v) | 111 | A::One(v) |
115 | } | 112 | } |
@@ -121,10 +118,10 @@ mod tests { | |||
121 | fn test_add_from_impl_for_enum_complicated_path() { | 118 | fn test_add_from_impl_for_enum_complicated_path() { |
122 | check_assist( | 119 | check_assist( |
123 | add_from_impl_for_enum, | 120 | add_from_impl_for_enum, |
124 | "enum A { <|>One(foo::bar::baz::Boo) }", | 121 | r#"enum A { <|>One(foo::bar::baz::Boo) }"#, |
125 | r#"enum A { One(foo::bar::baz::Boo) } | 122 | r#"enum A { <|>One(foo::bar::baz::Boo) } |
126 | 123 | ||
127 | <|>impl From<foo::bar::baz::Boo> for A { | 124 | impl From<foo::bar::baz::Boo> for A { |
128 | fn from(v: foo::bar::baz::Boo) -> Self { | 125 | fn from(v: foo::bar::baz::Boo) -> Self { |
129 | A::One(v) | 126 | A::One(v) |
130 | } | 127 | } |
@@ -184,9 +181,9 @@ impl From<String> for A { | |||
184 | pub trait From<T> { | 181 | pub trait From<T> { |
185 | fn from(T) -> Self; | 182 | fn from(T) -> Self; |
186 | }"#, | 183 | }"#, |
187 | r#"enum A { One(u32), Two(String), } | 184 | r#"enum A { <|>One(u32), Two(String), } |
188 | 185 | ||
189 | <|>impl From<u32> for A { | 186 | impl From<u32> for A { |
190 | fn from(v: u32) -> Self { | 187 | fn from(v: u32) -> Self { |
191 | A::One(v) | 188 | A::One(v) |
192 | } | 189 | } |
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 6b5616aa9..de016ae4e 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -1,7 +1,11 @@ | |||
1 | use hir::HirDisplay; | 1 | use hir::HirDisplay; |
2 | use ra_db::FileId; | 2 | use ra_db::FileId; |
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast::{self, edit::IndentLevel, ArgListOwner, AstNode, ModuleItemOwner}, | 4 | ast::{ |
5 | self, | ||
6 | edit::{AstNodeEdit, IndentLevel}, | ||
7 | ArgListOwner, AstNode, ModuleItemOwner, | ||
8 | }, | ||
5 | SyntaxKind, SyntaxNode, TextSize, | 9 | SyntaxKind, SyntaxNode, TextSize, |
6 | }; | 10 | }; |
7 | use rustc_hash::{FxHashMap, FxHashSet}; | 11 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -43,16 +47,12 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
43 | return None; | 47 | return None; |
44 | } | 48 | } |
45 | 49 | ||
46 | let target_module = if let Some(qualifier) = path.qualifier() { | 50 | let target_module = match path.qualifier() { |
47 | if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) = | 51 | Some(qualifier) => match ctx.sema.resolve_path(&qualifier) { |
48 | ctx.sema.resolve_path(&qualifier) | 52 | Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => Some(module), |
49 | { | 53 | _ => return None, |
50 | Some(module.definition_source(ctx.sema.db)) | 54 | }, |
51 | } else { | 55 | None => None, |
52 | return None; | ||
53 | } | ||
54 | } else { | ||
55 | None | ||
56 | }; | 56 | }; |
57 | 57 | ||
58 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; | 58 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; |
@@ -83,25 +83,29 @@ struct FunctionBuilder { | |||
83 | } | 83 | } |
84 | 84 | ||
85 | impl FunctionBuilder { | 85 | impl FunctionBuilder { |
86 | /// Prepares a generated function that matches `call` in `generate_in` | 86 | /// Prepares a generated function that matches `call`. |
87 | /// (or as close to `call` as possible, if `generate_in` is `None`) | 87 | /// The function is generated in `target_module` or next to `call` |
88 | fn from_call( | 88 | fn from_call( |
89 | ctx: &AssistContext, | 89 | ctx: &AssistContext, |
90 | call: &ast::CallExpr, | 90 | call: &ast::CallExpr, |
91 | path: &ast::Path, | 91 | path: &ast::Path, |
92 | target_module: Option<hir::InFile<hir::ModuleSource>>, | 92 | target_module: Option<hir::Module>, |
93 | ) -> Option<Self> { | 93 | ) -> Option<Self> { |
94 | let needs_pub = target_module.is_some(); | ||
95 | let mut file = ctx.frange.file_id; | 94 | let mut file = ctx.frange.file_id; |
96 | let target = if let Some(target_module) = target_module { | 95 | let target = match &target_module { |
97 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, target_module)?; | 96 | Some(target_module) => { |
98 | file = in_file; | 97 | let module_source = target_module.definition_source(ctx.db); |
99 | target | 98 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; |
100 | } else { | 99 | file = in_file; |
101 | next_space_for_fn_after_call_site(&call)? | 100 | target |
101 | } | ||
102 | None => next_space_for_fn_after_call_site(&call)?, | ||
102 | }; | 103 | }; |
104 | let needs_pub = target_module.is_some(); | ||
105 | let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?; | ||
103 | let fn_name = fn_name(&path)?; | 106 | let fn_name = fn_name(&path)?; |
104 | let (type_params, params) = fn_args(ctx, &call)?; | 107 | let (type_params, params) = fn_args(ctx, target_module, &call)?; |
108 | |||
105 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) | 109 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) |
106 | } | 110 | } |
107 | 111 | ||
@@ -116,17 +120,16 @@ impl FunctionBuilder { | |||
116 | let (fn_def, insert_offset) = match self.target { | 120 | let (fn_def, insert_offset) = match self.target { |
117 | GeneratedFunctionTarget::BehindItem(it) => { | 121 | GeneratedFunctionTarget::BehindItem(it) => { |
118 | let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); | 122 | let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); |
119 | let indented = IndentLevel::from_node(&it).increase_indent(with_leading_blank_line); | 123 | let indented = with_leading_blank_line.indent(IndentLevel::from_node(&it)); |
120 | (indented, it.text_range().end()) | 124 | (indented, it.text_range().end()) |
121 | } | 125 | } |
122 | GeneratedFunctionTarget::InEmptyItemList(it) => { | 126 | GeneratedFunctionTarget::InEmptyItemList(it) => { |
123 | let indent_once = IndentLevel(1); | 127 | let indent_once = IndentLevel(1); |
124 | let indent = IndentLevel::from_node(it.syntax()); | 128 | let indent = IndentLevel::from_node(it.syntax()); |
125 | |||
126 | let fn_def = ast::make::add_leading_newlines(1, fn_def); | 129 | let fn_def = ast::make::add_leading_newlines(1, fn_def); |
127 | let fn_def = indent_once.increase_indent(fn_def); | 130 | let fn_def = fn_def.indent(indent_once); |
128 | let fn_def = ast::make::add_trailing_newlines(1, fn_def); | 131 | let fn_def = ast::make::add_trailing_newlines(1, fn_def); |
129 | let fn_def = indent.increase_indent(fn_def); | 132 | let fn_def = fn_def.indent(indent); |
130 | (fn_def, it.syntax().text_range().start() + TextSize::of('{')) | 133 | (fn_def, it.syntax().text_range().start() + TextSize::of('{')) |
131 | } | 134 | } |
132 | }; | 135 | }; |
@@ -144,6 +147,15 @@ enum GeneratedFunctionTarget { | |||
144 | InEmptyItemList(ast::ItemList), | 147 | InEmptyItemList(ast::ItemList), |
145 | } | 148 | } |
146 | 149 | ||
150 | impl GeneratedFunctionTarget { | ||
151 | fn syntax(&self) -> &SyntaxNode { | ||
152 | match self { | ||
153 | GeneratedFunctionTarget::BehindItem(it) => it, | ||
154 | GeneratedFunctionTarget::InEmptyItemList(it) => it.syntax(), | ||
155 | } | ||
156 | } | ||
157 | } | ||
158 | |||
147 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { | 159 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { |
148 | let name = call.segment()?.syntax().to_string(); | 160 | let name = call.segment()?.syntax().to_string(); |
149 | Some(ast::make::name(&name)) | 161 | Some(ast::make::name(&name)) |
@@ -152,17 +164,17 @@ fn fn_name(call: &ast::Path) -> Option<ast::Name> { | |||
152 | /// Computes the type variables and arguments required for the generated function | 164 | /// Computes the type variables and arguments required for the generated function |
153 | fn fn_args( | 165 | fn fn_args( |
154 | ctx: &AssistContext, | 166 | ctx: &AssistContext, |
167 | target_module: hir::Module, | ||
155 | call: &ast::CallExpr, | 168 | call: &ast::CallExpr, |
156 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { | 169 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { |
157 | let mut arg_names = Vec::new(); | 170 | let mut arg_names = Vec::new(); |
158 | let mut arg_types = Vec::new(); | 171 | let mut arg_types = Vec::new(); |
159 | for arg in call.arg_list()?.args() { | 172 | for arg in call.arg_list()?.args() { |
160 | let arg_name = match fn_arg_name(&arg) { | 173 | arg_names.push(match fn_arg_name(&arg) { |
161 | Some(name) => name, | 174 | Some(name) => name, |
162 | None => String::from("arg"), | 175 | None => String::from("arg"), |
163 | }; | 176 | }); |
164 | arg_names.push(arg_name); | 177 | arg_types.push(match fn_arg_type(ctx, target_module, &arg) { |
165 | arg_types.push(match fn_arg_type(ctx, &arg) { | ||
166 | Some(ty) => ty, | 178 | Some(ty) => ty, |
167 | None => String::from("()"), | 179 | None => String::from("()"), |
168 | }); | 180 | }); |
@@ -218,12 +230,21 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> { | |||
218 | } | 230 | } |
219 | } | 231 | } |
220 | 232 | ||
221 | fn fn_arg_type(ctx: &AssistContext, fn_arg: &ast::Expr) -> Option<String> { | 233 | fn fn_arg_type( |
234 | ctx: &AssistContext, | ||
235 | target_module: hir::Module, | ||
236 | fn_arg: &ast::Expr, | ||
237 | ) -> Option<String> { | ||
222 | let ty = ctx.sema.type_of_expr(fn_arg)?; | 238 | let ty = ctx.sema.type_of_expr(fn_arg)?; |
223 | if ty.is_unknown() { | 239 | if ty.is_unknown() { |
224 | return None; | 240 | return None; |
225 | } | 241 | } |
226 | Some(ty.display(ctx.sema.db).to_string()) | 242 | |
243 | if let Ok(rendered) = ty.display_source_code(ctx.db, target_module.into()) { | ||
244 | Some(rendered) | ||
245 | } else { | ||
246 | None | ||
247 | } | ||
227 | } | 248 | } |
228 | 249 | ||
229 | /// Returns the position inside the current mod or file | 250 | /// Returns the position inside the current mod or file |
@@ -252,10 +273,10 @@ fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFu | |||
252 | 273 | ||
253 | fn next_space_for_fn_in_module( | 274 | fn next_space_for_fn_in_module( |
254 | db: &dyn hir::db::AstDatabase, | 275 | db: &dyn hir::db::AstDatabase, |
255 | module: hir::InFile<hir::ModuleSource>, | 276 | module_source: &hir::InFile<hir::ModuleSource>, |
256 | ) -> Option<(FileId, GeneratedFunctionTarget)> { | 277 | ) -> Option<(FileId, GeneratedFunctionTarget)> { |
257 | let file = module.file_id.original_file(db); | 278 | let file = module_source.file_id.original_file(db); |
258 | let assist_item = match module.value { | 279 | let assist_item = match &module_source.value { |
259 | hir::ModuleSource::SourceFile(it) => { | 280 | hir::ModuleSource::SourceFile(it) => { |
260 | if let Some(last_item) = it.items().last() { | 281 | if let Some(last_item) = it.items().last() { |
261 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) | 282 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) |
@@ -599,8 +620,33 @@ fn bar(foo: impl Foo) { | |||
599 | } | 620 | } |
600 | 621 | ||
601 | #[test] | 622 | #[test] |
602 | #[ignore] | 623 | fn borrowed_arg() { |
603 | // FIXME print paths properly to make this test pass | 624 | check_assist( |
625 | add_function, | ||
626 | r" | ||
627 | struct Baz; | ||
628 | fn baz() -> Baz { todo!() } | ||
629 | |||
630 | fn foo() { | ||
631 | bar<|>(&baz()) | ||
632 | } | ||
633 | ", | ||
634 | r" | ||
635 | struct Baz; | ||
636 | fn baz() -> Baz { todo!() } | ||
637 | |||
638 | fn foo() { | ||
639 | bar(&baz()) | ||
640 | } | ||
641 | |||
642 | fn bar(baz: &Baz) { | ||
643 | <|>todo!() | ||
644 | } | ||
645 | ", | ||
646 | ) | ||
647 | } | ||
648 | |||
649 | #[test] | ||
604 | fn add_function_with_qualified_path_arg() { | 650 | fn add_function_with_qualified_path_arg() { |
605 | check_assist( | 651 | check_assist( |
606 | add_function, | 652 | add_function, |
@@ -609,10 +655,8 @@ mod Baz { | |||
609 | pub struct Bof; | 655 | pub struct Bof; |
610 | pub fn baz() -> Bof { Bof } | 656 | pub fn baz() -> Bof { Bof } |
611 | } | 657 | } |
612 | mod Foo { | 658 | fn foo() { |
613 | fn foo() { | 659 | <|>bar(Baz::baz()) |
614 | <|>bar(super::Baz::baz()) | ||
615 | } | ||
616 | } | 660 | } |
617 | ", | 661 | ", |
618 | r" | 662 | r" |
@@ -620,14 +664,12 @@ mod Baz { | |||
620 | pub struct Bof; | 664 | pub struct Bof; |
621 | pub fn baz() -> Bof { Bof } | 665 | pub fn baz() -> Bof { Bof } |
622 | } | 666 | } |
623 | mod Foo { | 667 | fn foo() { |
624 | fn foo() { | 668 | bar(Baz::baz()) |
625 | bar(super::Baz::baz()) | 669 | } |
626 | } | ||
627 | 670 | ||
628 | fn bar(baz: super::Baz::Bof) { | 671 | fn bar(baz: Baz::Bof) { |
629 | <|>todo!() | 672 | <|>todo!() |
630 | } | ||
631 | } | 673 | } |
632 | ", | 674 | ", |
633 | ) | 675 | ) |
@@ -809,6 +851,40 @@ fn foo() { | |||
809 | } | 851 | } |
810 | 852 | ||
811 | #[test] | 853 | #[test] |
854 | #[ignore] | ||
855 | // Ignored until local imports are supported. | ||
856 | // See https://github.com/rust-analyzer/rust-analyzer/issues/1165 | ||
857 | fn qualified_path_uses_correct_scope() { | ||
858 | check_assist( | ||
859 | add_function, | ||
860 | " | ||
861 | mod foo { | ||
862 | pub struct Foo; | ||
863 | } | ||
864 | fn bar() { | ||
865 | use foo::Foo; | ||
866 | let foo = Foo; | ||
867 | baz<|>(foo) | ||
868 | } | ||
869 | ", | ||
870 | " | ||
871 | mod foo { | ||
872 | pub struct Foo; | ||
873 | } | ||
874 | fn bar() { | ||
875 | use foo::Foo; | ||
876 | let foo = Foo; | ||
877 | baz(foo) | ||
878 | } | ||
879 | |||
880 | fn baz(foo: foo::Foo) { | ||
881 | <|>todo!() | ||
882 | } | ||
883 | ", | ||
884 | ) | ||
885 | } | ||
886 | |||
887 | #[test] | ||
812 | fn add_function_in_module_containing_other_items() { | 888 | fn add_function_in_module_containing_other_items() { |
813 | check_assist( | 889 | check_assist( |
814 | add_function, | 890 | add_function, |
@@ -920,21 +996,6 @@ fn bar(baz: ()) {} | |||
920 | } | 996 | } |
921 | 997 | ||
922 | #[test] | 998 | #[test] |
923 | fn add_function_not_applicable_if_function_path_not_singleton() { | ||
924 | // In the future this assist could be extended to generate functions | ||
925 | // if the path is in the same crate (or even the same workspace). | ||
926 | // For the beginning, I think this is fine. | ||
927 | check_assist_not_applicable( | ||
928 | add_function, | ||
929 | r" | ||
930 | fn foo() { | ||
931 | other_crate::bar<|>(); | ||
932 | } | ||
933 | ", | ||
934 | ) | ||
935 | } | ||
936 | |||
937 | #[test] | ||
938 | #[ignore] | 999 | #[ignore] |
939 | fn create_method_with_no_args() { | 1000 | fn create_method_with_no_args() { |
940 | check_assist( | 1001 | check_assist( |
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index 3482a75bf..c1ce87914 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -2,7 +2,7 @@ use hir::HasSource; | |||
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{ | 3 | ast::{ |
4 | self, | 4 | self, |
5 | edit::{self, IndentLevel}, | 5 | edit::{self, AstNodeEdit, IndentLevel}, |
6 | make, AstNode, NameOwner, | 6 | make, AstNode, NameOwner, |
7 | }, | 7 | }, |
8 | SmolStr, | 8 | SmolStr, |
@@ -176,8 +176,7 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | |||
176 | if fn_def.body().is_some() { | 176 | if fn_def.body().is_some() { |
177 | return fn_def; | 177 | return fn_def; |
178 | } | 178 | } |
179 | let body = make::block_expr(None, Some(make::expr_todo())); | 179 | let body = make::block_expr(None, Some(make::expr_todo())).indent(IndentLevel(1)); |
180 | let body = IndentLevel(1).increase_indent(body); | ||
181 | fn_def.with_body(body) | 180 | fn_def.with_body(body) |
182 | } | 181 | } |
183 | 182 | ||
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 810784ad5..66b296081 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs | |||
@@ -2,7 +2,11 @@ use std::{iter::once, ops::RangeInclusive}; | |||
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::replace_children, | 4 | algo::replace_children, |
5 | ast::{self, edit::IndentLevel, make}, | 5 | ast::{ |
6 | self, | ||
7 | edit::{AstNodeEdit, IndentLevel}, | ||
8 | make, | ||
9 | }, | ||
6 | AstNode, | 10 | AstNode, |
7 | SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE}, | 11 | SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE}, |
8 | SyntaxNode, | 12 | SyntaxNode, |
@@ -105,8 +109,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
105 | let then_branch = | 109 | let then_branch = |
106 | make::block_expr(once(make::expr_stmt(early_expression).into()), None); | 110 | make::block_expr(once(make::expr_stmt(early_expression).into()), None); |
107 | let cond = invert_boolean_expression(cond_expr); | 111 | let cond = invert_boolean_expression(cond_expr); |
108 | let e = make::expr_if(make::condition(cond, None), then_branch); | 112 | make::expr_if(make::condition(cond, None), then_branch).indent(if_indent_level) |
109 | if_indent_level.increase_indent(e) | ||
110 | }; | 113 | }; |
111 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) | 114 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) |
112 | } | 115 | } |
@@ -140,7 +143,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
140 | make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), | 143 | make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), |
141 | Some(match_expr), | 144 | Some(match_expr), |
142 | ); | 145 | ); |
143 | let let_stmt = if_indent_level.increase_indent(let_stmt); | 146 | let let_stmt = let_stmt.indent(if_indent_level); |
144 | replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) | 147 | replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) |
145 | } | 148 | } |
146 | }; | 149 | }; |
@@ -153,7 +156,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
153 | parent_block: &ast::BlockExpr, | 156 | parent_block: &ast::BlockExpr, |
154 | if_expr: &ast::IfExpr, | 157 | if_expr: &ast::IfExpr, |
155 | ) -> SyntaxNode { | 158 | ) -> SyntaxNode { |
156 | let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); | 159 | let then_block_items = then_block.dedent(IndentLevel::from(1)); |
157 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); | 160 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); |
158 | let end_of_then = | 161 | let end_of_then = |
159 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { | 162 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { |
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index a59a06efa..65f5fc6ab 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -1,6 +1,10 @@ | |||
1 | use ra_fmt::unwrap_trivial_block; | 1 | use ra_fmt::unwrap_trivial_block; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, edit::IndentLevel, make}, | 3 | ast::{ |
4 | self, | ||
5 | edit::{AstNodeEdit, IndentLevel}, | ||
6 | make, | ||
7 | }, | ||
4 | AstNode, | 8 | AstNode, |
5 | }; | 9 | }; |
6 | 10 | ||
@@ -61,10 +65,9 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
61 | make::match_arm(vec![pattern], else_expr) | 65 | make::match_arm(vec![pattern], else_expr) |
62 | }; | 66 | }; |
63 | make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) | 67 | make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) |
68 | .indent(IndentLevel::from_node(if_expr.syntax())) | ||
64 | }; | 69 | }; |
65 | 70 | ||
66 | let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr); | ||
67 | |||
68 | edit.set_cursor(if_expr.syntax().text_range().start()); | 71 | edit.set_cursor(if_expr.syntax().text_range().start()); |
69 | edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); | 72 | edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); |
70 | }) | 73 | }) |
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs index d3f214591..482957dc6 100644 --- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs | |||
@@ -53,8 +53,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> | |||
53 | ) | 53 | ) |
54 | .into(), | 54 | .into(), |
55 | }; | 55 | }; |
56 | let block = | 56 | let block = make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax())); |
57 | IndentLevel::from_node(let_stmt.syntax()).increase_indent(make::block_expr(None, None)); | ||
58 | let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block); | 57 | let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block); |
59 | let stmt = make::expr_stmt(if_); | 58 | let stmt = make::expr_stmt(if_); |
60 | 59 | ||
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs index a46998b8e..c4b56f6e9 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs | |||
@@ -1,7 +1,11 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast::{self, edit::IndentLevel, make}, | 4 | ast::{ |
5 | self, | ||
6 | edit::{AstNodeEdit, IndentLevel}, | ||
7 | make, | ||
8 | }, | ||
5 | AstNode, | 9 | AstNode, |
6 | }; | 10 | }; |
7 | 11 | ||
@@ -51,8 +55,8 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
51 | let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); | 55 | let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); |
52 | 56 | ||
53 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); | 57 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); |
54 | let match_expr = make::expr_match(caller.clone(), match_arm_list); | 58 | let match_expr = make::expr_match(caller.clone(), match_arm_list) |
55 | let match_expr = IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); | 59 | .indent(IndentLevel::from_node(method_call.syntax())); |
56 | 60 | ||
57 | edit.set_cursor(caller.syntax().text_range().start()); | 61 | edit.set_cursor(caller.syntax().text_range().start()); |
58 | edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); | 62 | edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); |
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index eba0631a4..e52ec557e 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use crate::{AssistContext, AssistId, Assists}; | 1 | use crate::{AssistContext, AssistId, Assists}; |
2 | 2 | ||
3 | use ast::LoopBodyOwner; | 3 | use ast::{ElseBranch, Expr, LoopBodyOwner}; |
4 | use ra_fmt::unwrap_trivial_block; | 4 | use ra_fmt::unwrap_trivial_block; |
5 | use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; | 5 | use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; |
6 | 6 | ||
@@ -25,19 +25,11 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
25 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; | 25 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; |
26 | let block = ast::BlockExpr::cast(l_curly_token.parent())?; | 26 | let block = ast::BlockExpr::cast(l_curly_token.parent())?; |
27 | let parent = block.syntax().parent()?; | 27 | let parent = block.syntax().parent()?; |
28 | let assist_id = AssistId("unwrap_block"); | ||
29 | let assist_label = "Unwrap block"; | ||
30 | |||
28 | let (expr, expr_to_unwrap) = match_ast! { | 31 | let (expr, expr_to_unwrap) = match_ast! { |
29 | match parent { | 32 | match parent { |
30 | ast::IfExpr(if_expr) => { | ||
31 | let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr)); | ||
32 | let expr_to_unwrap = expr_to_unwrap?; | ||
33 | // Find if we are in a else if block | ||
34 | let ancestor = if_expr.syntax().parent().and_then(ast::IfExpr::cast); | ||
35 | |||
36 | match ancestor { | ||
37 | None => (ast::Expr::IfExpr(if_expr), expr_to_unwrap), | ||
38 | Some(ancestor) => (ast::Expr::IfExpr(ancestor), expr_to_unwrap), | ||
39 | } | ||
40 | }, | ||
41 | ast::ForExpr(for_expr) => { | 33 | ast::ForExpr(for_expr) => { |
42 | let block_expr = for_expr.loop_body()?; | 34 | let block_expr = for_expr.loop_body()?; |
43 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | 35 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; |
@@ -53,27 +45,62 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
53 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | 45 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; |
54 | (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) | 46 | (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) |
55 | }, | 47 | }, |
48 | ast::IfExpr(if_expr) => { | ||
49 | let mut resp = None; | ||
50 | |||
51 | let then_branch = if_expr.then_branch()?; | ||
52 | if then_branch.l_curly_token()?.text_range().contains_range(ctx.frange.range) { | ||
53 | if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) { | ||
54 | // For `else if` blocks | ||
55 | let ancestor_then_branch = ancestor.then_branch()?; | ||
56 | let l_curly_token = then_branch.l_curly_token()?; | ||
57 | |||
58 | let target = then_branch.syntax().text_range(); | ||
59 | return acc.add(assist_id, assist_label, target, |edit| { | ||
60 | let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | ||
61 | let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); | ||
62 | |||
63 | edit.set_cursor(ancestor_then_branch.syntax().text_range().end()); | ||
64 | edit.delete(range_to_del_rest); | ||
65 | edit.delete(range_to_del_else_if); | ||
66 | edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); | ||
67 | }); | ||
68 | } else { | ||
69 | resp = Some((ast::Expr::IfExpr(if_expr.clone()), Expr::BlockExpr(then_branch))); | ||
70 | } | ||
71 | } else if let Some(else_branch) = if_expr.else_branch() { | ||
72 | match else_branch { | ||
73 | ElseBranch::Block(else_block) => { | ||
74 | let l_curly_token = else_block.l_curly_token()?; | ||
75 | if l_curly_token.text_range().contains_range(ctx.frange.range) { | ||
76 | let target = else_block.syntax().text_range(); | ||
77 | return acc.add(assist_id, assist_label, target, |edit| { | ||
78 | let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | ||
79 | |||
80 | edit.set_cursor(then_branch.syntax().text_range().end()); | ||
81 | edit.delete(range_to_del); | ||
82 | edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{'])); | ||
83 | }); | ||
84 | } | ||
85 | }, | ||
86 | ElseBranch::IfExpr(_) => {}, | ||
87 | } | ||
88 | } | ||
89 | |||
90 | resp? | ||
91 | }, | ||
56 | _ => return None, | 92 | _ => return None, |
57 | } | 93 | } |
58 | }; | 94 | }; |
59 | 95 | ||
60 | let target = expr_to_unwrap.syntax().text_range(); | 96 | let target = expr_to_unwrap.syntax().text_range(); |
61 | acc.add(AssistId("unwrap_block"), "Unwrap block", target, |edit| { | 97 | acc.add(assist_id, assist_label, target, |edit| { |
62 | edit.set_cursor(expr.syntax().text_range().start()); | 98 | edit.set_cursor(expr.syntax().text_range().start()); |
63 | 99 | ||
64 | let pat_start: &[_] = &[' ', '{', '\n']; | 100 | edit.replace( |
65 | let expr_to_unwrap = expr_to_unwrap.to_string(); | 101 | expr.syntax().text_range(), |
66 | let expr_string = expr_to_unwrap.trim_start_matches(pat_start); | 102 | update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), |
67 | let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); | 103 | ); |
68 | expr_string_lines.pop(); // Delete last line | ||
69 | |||
70 | let expr_string = expr_string_lines | ||
71 | .into_iter() | ||
72 | .map(|line| line.replacen(" ", "", 1)) // Delete indentation | ||
73 | .collect::<Vec<String>>() | ||
74 | .join("\n"); | ||
75 | |||
76 | edit.replace(expr.syntax().text_range(), expr_string); | ||
77 | }) | 104 | }) |
78 | } | 105 | } |
79 | 106 | ||
@@ -87,6 +114,18 @@ fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::E | |||
87 | } | 114 | } |
88 | } | 115 | } |
89 | 116 | ||
117 | fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String { | ||
118 | let expr_string = expr_str.trim_start_matches(trim_start_pat); | ||
119 | let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); | ||
120 | expr_string_lines.pop(); // Delete last line | ||
121 | |||
122 | expr_string_lines | ||
123 | .into_iter() | ||
124 | .map(|line| line.replacen(" ", "", 1)) // Delete indentation | ||
125 | .collect::<Vec<String>>() | ||
126 | .join("\n") | ||
127 | } | ||
128 | |||
90 | #[cfg(test)] | 129 | #[cfg(test)] |
91 | mod tests { | 130 | mod tests { |
92 | use crate::tests::{check_assist, check_assist_not_applicable}; | 131 | use crate::tests::{check_assist, check_assist_not_applicable}; |
@@ -142,7 +181,13 @@ mod tests { | |||
142 | r#" | 181 | r#" |
143 | fn main() { | 182 | fn main() { |
144 | bar(); | 183 | bar(); |
145 | <|>println!("bar"); | 184 | if true { |
185 | foo(); | ||
186 | |||
187 | //comment | ||
188 | bar(); | ||
189 | }<|> | ||
190 | println!("bar"); | ||
146 | } | 191 | } |
147 | "#, | 192 | "#, |
148 | ); | 193 | ); |
@@ -170,7 +215,127 @@ mod tests { | |||
170 | r#" | 215 | r#" |
171 | fn main() { | 216 | fn main() { |
172 | //bar(); | 217 | //bar(); |
173 | <|>println!("bar"); | 218 | if true { |
219 | println!("true"); | ||
220 | |||
221 | //comment | ||
222 | //bar(); | ||
223 | }<|> | ||
224 | println!("bar"); | ||
225 | } | ||
226 | "#, | ||
227 | ); | ||
228 | } | ||
229 | |||
230 | #[test] | ||
231 | fn simple_if_else_if_nested() { | ||
232 | check_assist( | ||
233 | unwrap_block, | ||
234 | r#" | ||
235 | fn main() { | ||
236 | //bar(); | ||
237 | if true { | ||
238 | println!("true"); | ||
239 | |||
240 | //comment | ||
241 | //bar(); | ||
242 | } else if false { | ||
243 | println!("bar"); | ||
244 | } else if true {<|> | ||
245 | println!("foo"); | ||
246 | } | ||
247 | } | ||
248 | "#, | ||
249 | r#" | ||
250 | fn main() { | ||
251 | //bar(); | ||
252 | if true { | ||
253 | println!("true"); | ||
254 | |||
255 | //comment | ||
256 | //bar(); | ||
257 | } else if false { | ||
258 | println!("bar"); | ||
259 | }<|> | ||
260 | println!("foo"); | ||
261 | } | ||
262 | "#, | ||
263 | ); | ||
264 | } | ||
265 | |||
266 | #[test] | ||
267 | fn simple_if_else_if_nested_else() { | ||
268 | check_assist( | ||
269 | unwrap_block, | ||
270 | r#" | ||
271 | fn main() { | ||
272 | //bar(); | ||
273 | if true { | ||
274 | println!("true"); | ||
275 | |||
276 | //comment | ||
277 | //bar(); | ||
278 | } else if false { | ||
279 | println!("bar"); | ||
280 | } else if true { | ||
281 | println!("foo"); | ||
282 | } else {<|> | ||
283 | println!("else"); | ||
284 | } | ||
285 | } | ||
286 | "#, | ||
287 | r#" | ||
288 | fn main() { | ||
289 | //bar(); | ||
290 | if true { | ||
291 | println!("true"); | ||
292 | |||
293 | //comment | ||
294 | //bar(); | ||
295 | } else if false { | ||
296 | println!("bar"); | ||
297 | } else if true { | ||
298 | println!("foo"); | ||
299 | }<|> | ||
300 | println!("else"); | ||
301 | } | ||
302 | "#, | ||
303 | ); | ||
304 | } | ||
305 | |||
306 | #[test] | ||
307 | fn simple_if_else_if_nested_middle() { | ||
308 | check_assist( | ||
309 | unwrap_block, | ||
310 | r#" | ||
311 | fn main() { | ||
312 | //bar(); | ||
313 | if true { | ||
314 | println!("true"); | ||
315 | |||
316 | //comment | ||
317 | //bar(); | ||
318 | } else if false { | ||
319 | println!("bar"); | ||
320 | } else if true {<|> | ||
321 | println!("foo"); | ||
322 | } else { | ||
323 | println!("else"); | ||
324 | } | ||
325 | } | ||
326 | "#, | ||
327 | r#" | ||
328 | fn main() { | ||
329 | //bar(); | ||
330 | if true { | ||
331 | println!("true"); | ||
332 | |||
333 | //comment | ||
334 | //bar(); | ||
335 | } else if false { | ||
336 | println!("bar"); | ||
337 | }<|> | ||
338 | println!("foo"); | ||
174 | } | 339 | } |
175 | "#, | 340 | "#, |
176 | ); | 341 | ); |