diff options
Diffstat (limited to 'crates/ra_assists/src/handlers')
-rw-r--r-- | crates/ra_assists/src/handlers/add_function.rs | 71 |
1 files changed, 57 insertions, 14 deletions
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index a1261fe15..b65ded871 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -3,7 +3,7 @@ use ra_syntax::{ | |||
3 | SyntaxKind, SyntaxNode, TextUnit, | 3 | SyntaxKind, SyntaxNode, TextUnit, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::{Assist, AssistCtx, AssistId}; | 6 | use crate::{Assist, AssistCtx, AssistFile, AssistId}; |
7 | use ast::{edit::IndentLevel, ArgListOwner, ModuleItemOwner}; | 7 | use ast::{edit::IndentLevel, ArgListOwner, ModuleItemOwner}; |
8 | use hir::HirDisplay; | 8 | use hir::HirDisplay; |
9 | use rustc_hash::{FxHashMap, FxHashSet}; | 9 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -44,10 +44,10 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> { | |||
44 | } | 44 | } |
45 | 45 | ||
46 | let target_module = if let Some(qualifier) = path.qualifier() { | 46 | let target_module = if let Some(qualifier) = path.qualifier() { |
47 | if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(resolved))) = | 47 | if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) = |
48 | ctx.sema.resolve_path(&qualifier) | 48 | ctx.sema.resolve_path(&qualifier) |
49 | { | 49 | { |
50 | Some(resolved.definition_source(ctx.sema.db).value) | 50 | Some(module.definition_source(ctx.sema.db)) |
51 | } else { | 51 | } else { |
52 | return None; | 52 | return None; |
53 | } | 53 | } |
@@ -61,6 +61,7 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> { | |||
61 | edit.target(call.syntax().text_range()); | 61 | edit.target(call.syntax().text_range()); |
62 | 62 | ||
63 | if let Some(function_template) = function_builder.render() { | 63 | if let Some(function_template) = function_builder.render() { |
64 | edit.set_file(function_template.file); | ||
64 | edit.set_cursor(function_template.cursor_offset); | 65 | edit.set_cursor(function_template.cursor_offset); |
65 | edit.insert(function_template.insert_offset, function_template.fn_def.to_string()); | 66 | edit.insert(function_template.insert_offset, function_template.fn_def.to_string()); |
66 | } | 67 | } |
@@ -71,6 +72,7 @@ struct FunctionTemplate { | |||
71 | insert_offset: TextUnit, | 72 | insert_offset: TextUnit, |
72 | cursor_offset: TextUnit, | 73 | cursor_offset: TextUnit, |
73 | fn_def: ast::SourceFile, | 74 | fn_def: ast::SourceFile, |
75 | file: AssistFile, | ||
74 | } | 76 | } |
75 | 77 | ||
76 | struct FunctionBuilder { | 78 | struct FunctionBuilder { |
@@ -78,6 +80,7 @@ struct FunctionBuilder { | |||
78 | fn_name: ast::Name, | 80 | fn_name: ast::Name, |
79 | type_params: Option<ast::TypeParamList>, | 81 | type_params: Option<ast::TypeParamList>, |
80 | params: ast::ParamList, | 82 | params: ast::ParamList, |
83 | file: AssistFile, | ||
81 | } | 84 | } |
82 | 85 | ||
83 | impl FunctionBuilder { | 86 | impl FunctionBuilder { |
@@ -87,16 +90,19 @@ impl FunctionBuilder { | |||
87 | ctx: &AssistCtx, | 90 | ctx: &AssistCtx, |
88 | call: &ast::CallExpr, | 91 | call: &ast::CallExpr, |
89 | path: &ast::Path, | 92 | path: &ast::Path, |
90 | generate_in: Option<hir::ModuleSource>, | 93 | generate_in: Option<hir::InFile<hir::ModuleSource>>, |
91 | ) -> Option<Self> { | 94 | ) -> Option<Self> { |
95 | let mut file = AssistFile::default(); | ||
92 | let target = if let Some(generate_in_module) = generate_in { | 96 | let target = if let Some(generate_in_module) = generate_in { |
93 | next_space_for_fn_in_module(generate_in_module)? | 97 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, generate_in_module)?; |
98 | file = in_file; | ||
99 | target | ||
94 | } else { | 100 | } else { |
95 | next_space_for_fn_after_call_site(&call)? | 101 | next_space_for_fn_after_call_site(&call)? |
96 | }; | 102 | }; |
97 | let fn_name = fn_name(&path)?; | 103 | let fn_name = fn_name(&path)?; |
98 | let (type_params, params) = fn_args(ctx, &call)?; | 104 | let (type_params, params) = fn_args(ctx, &call)?; |
99 | Some(Self { target, fn_name, type_params, params }) | 105 | Some(Self { target, fn_name, type_params, params, file }) |
100 | } | 106 | } |
101 | fn render(self) -> Option<FunctionTemplate> { | 107 | fn render(self) -> Option<FunctionTemplate> { |
102 | let placeholder_expr = ast::make::expr_todo(); | 108 | let placeholder_expr = ast::make::expr_todo(); |
@@ -130,7 +136,7 @@ impl FunctionBuilder { | |||
130 | .text_range() | 136 | .text_range() |
131 | .start(); | 137 | .start(); |
132 | let cursor_offset = insert_offset + cursor_offset_from_fn_start; | 138 | let cursor_offset = insert_offset + cursor_offset_from_fn_start; |
133 | Some(FunctionTemplate { insert_offset, cursor_offset, fn_def }) | 139 | Some(FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file }) |
134 | } | 140 | } |
135 | } | 141 | } |
136 | 142 | ||
@@ -250,23 +256,29 @@ fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFu | |||
250 | last_ancestor.map(GeneratedFunctionTarget::BehindItem) | 256 | last_ancestor.map(GeneratedFunctionTarget::BehindItem) |
251 | } | 257 | } |
252 | 258 | ||
253 | fn next_space_for_fn_in_module(module: hir::ModuleSource) -> Option<GeneratedFunctionTarget> { | 259 | fn next_space_for_fn_in_module( |
254 | match module { | 260 | db: &dyn hir::db::AstDatabase, |
261 | module: hir::InFile<hir::ModuleSource>, | ||
262 | ) -> Option<(AssistFile, GeneratedFunctionTarget)> { | ||
263 | let file = module.file_id.original_file(db); | ||
264 | let assist_file = AssistFile::TargetFile(file); | ||
265 | let assist_item = match module.value { | ||
255 | hir::ModuleSource::SourceFile(it) => { | 266 | hir::ModuleSource::SourceFile(it) => { |
256 | if let Some(last_item) = it.items().last() { | 267 | if let Some(last_item) = it.items().last() { |
257 | Some(GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())) | 268 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) |
258 | } else { | 269 | } else { |
259 | Some(GeneratedFunctionTarget::BehindItem(it.syntax().clone())) | 270 | GeneratedFunctionTarget::BehindItem(it.syntax().clone()) |
260 | } | 271 | } |
261 | } | 272 | } |
262 | hir::ModuleSource::Module(it) => { | 273 | hir::ModuleSource::Module(it) => { |
263 | if let Some(last_item) = it.item_list().and_then(|it| it.items().last()) { | 274 | if let Some(last_item) = it.item_list().and_then(|it| it.items().last()) { |
264 | Some(GeneratedFunctionTarget::BehindItem(last_item.syntax().clone())) | 275 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) |
265 | } else { | 276 | } else { |
266 | it.item_list().map(GeneratedFunctionTarget::InEmptyItemList) | 277 | GeneratedFunctionTarget::InEmptyItemList(it.item_list()?) |
267 | } | 278 | } |
268 | } | 279 | } |
269 | } | 280 | }; |
281 | Some((assist_file, assist_item)) | ||
270 | } | 282 | } |
271 | 283 | ||
272 | #[cfg(test)] | 284 | #[cfg(test)] |
@@ -885,6 +897,37 @@ fn foo() { | |||
885 | } | 897 | } |
886 | 898 | ||
887 | #[test] | 899 | #[test] |
900 | fn add_function_in_another_file() { | ||
901 | check_assist( | ||
902 | add_function, | ||
903 | r" | ||
904 | //- /main.rs | ||
905 | mod foo; | ||
906 | |||
907 | fn main() { | ||
908 | foo::bar<|>() | ||
909 | } | ||
910 | |||
911 | //- /foo.rs | ||
912 | |||
913 | ", | ||
914 | r" | ||
915 | //- /main.rs | ||
916 | mod foo; | ||
917 | |||
918 | fn main() { | ||
919 | foo::bar() | ||
920 | } | ||
921 | |||
922 | //- /foo.rs | ||
923 | fn bar() { | ||
924 | <|>todo!() | ||
925 | } | ||
926 | ", | ||
927 | ) | ||
928 | } | ||
929 | |||
930 | #[test] | ||
888 | fn add_function_not_applicable_if_function_already_exists() { | 931 | fn add_function_not_applicable_if_function_already_exists() { |
889 | check_assist_not_applicable( | 932 | check_assist_not_applicable( |
890 | add_function, | 933 | add_function, |