diff options
-rw-r--r-- | crates/ide_assists/src/handlers/generate_function.rs | 73 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/postfix.rs | 14 | ||||
-rw-r--r-- | docs/dev/lsp-extensions.md | 20 |
3 files changed, 91 insertions, 16 deletions
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs index 3870b7e75..6f95b1a07 100644 --- a/crates/ide_assists/src/handlers/generate_function.rs +++ b/crates/ide_assists/src/handlers/generate_function.rs | |||
@@ -83,17 +83,18 @@ struct FunctionTemplate { | |||
83 | leading_ws: String, | 83 | leading_ws: String, |
84 | fn_def: ast::Fn, | 84 | fn_def: ast::Fn, |
85 | ret_type: ast::RetType, | 85 | ret_type: ast::RetType, |
86 | should_render_snippet: bool, | ||
86 | trailing_ws: String, | 87 | trailing_ws: String, |
87 | file: FileId, | 88 | file: FileId, |
88 | } | 89 | } |
89 | 90 | ||
90 | impl FunctionTemplate { | 91 | impl FunctionTemplate { |
91 | fn to_string(&self, cap: Option<SnippetCap>) -> String { | 92 | fn to_string(&self, cap: Option<SnippetCap>) -> String { |
92 | let f = match cap { | 93 | let f = match (cap, self.should_render_snippet) { |
93 | Some(cap) => { | 94 | (Some(cap), true) => { |
94 | render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax())) | 95 | render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax())) |
95 | } | 96 | } |
96 | None => self.fn_def.to_string(), | 97 | _ => self.fn_def.to_string(), |
97 | }; | 98 | }; |
98 | format!("{}{}{}", self.leading_ws, f, self.trailing_ws) | 99 | format!("{}{}{}", self.leading_ws, f, self.trailing_ws) |
99 | } | 100 | } |
@@ -104,6 +105,8 @@ struct FunctionBuilder { | |||
104 | fn_name: ast::Name, | 105 | fn_name: ast::Name, |
105 | type_params: Option<ast::GenericParamList>, | 106 | type_params: Option<ast::GenericParamList>, |
106 | params: ast::ParamList, | 107 | params: ast::ParamList, |
108 | ret_type: ast::RetType, | ||
109 | should_render_snippet: bool, | ||
107 | file: FileId, | 110 | file: FileId, |
108 | needs_pub: bool, | 111 | needs_pub: bool, |
109 | } | 112 | } |
@@ -132,7 +135,43 @@ impl FunctionBuilder { | |||
132 | let fn_name = fn_name(&path)?; | 135 | let fn_name = fn_name(&path)?; |
133 | let (type_params, params) = fn_args(ctx, target_module, &call)?; | 136 | let (type_params, params) = fn_args(ctx, target_module, &call)?; |
134 | 137 | ||
135 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) | 138 | // should_render_snippet intends to express a rough level of confidence about |
139 | // the correctness of the return type. | ||
140 | // | ||
141 | // If we are able to infer some return type, and that return type is not unit, we | ||
142 | // don't want to render the snippet. The assumption here is in this situation the | ||
143 | // return type is just as likely to be correct as any other part of the generated | ||
144 | // function. | ||
145 | // | ||
146 | // In the case where the return type is inferred as unit it is likely that the | ||
147 | // user does in fact intend for this generated function to return some non unit | ||
148 | // type, but that the current state of their code doesn't allow that return type | ||
149 | // to be accurately inferred. | ||
150 | let (ret_ty, should_render_snippet) = { | ||
151 | match ctx.sema.type_of_expr(&ast::Expr::CallExpr(call.clone())) { | ||
152 | Some(ty) if ty.is_unknown() || ty.is_unit() => (make::ty_unit(), true), | ||
153 | Some(ty) => { | ||
154 | let rendered = ty.display_source_code(ctx.db(), target_module.into()); | ||
155 | match rendered { | ||
156 | Ok(rendered) => (make::ty(&rendered), false), | ||
157 | Err(_) => (make::ty_unit(), true), | ||
158 | } | ||
159 | } | ||
160 | None => (make::ty_unit(), true), | ||
161 | } | ||
162 | }; | ||
163 | let ret_type = make::ret_type(ret_ty); | ||
164 | |||
165 | Some(Self { | ||
166 | target, | ||
167 | fn_name, | ||
168 | type_params, | ||
169 | params, | ||
170 | ret_type, | ||
171 | should_render_snippet, | ||
172 | file, | ||
173 | needs_pub, | ||
174 | }) | ||
136 | } | 175 | } |
137 | 176 | ||
138 | fn render(self) -> FunctionTemplate { | 177 | fn render(self) -> FunctionTemplate { |
@@ -145,7 +184,7 @@ impl FunctionBuilder { | |||
145 | self.type_params, | 184 | self.type_params, |
146 | self.params, | 185 | self.params, |
147 | fn_body, | 186 | fn_body, |
148 | Some(make::ret_type(make::ty_unit())), | 187 | Some(self.ret_type), |
149 | ); | 188 | ); |
150 | let leading_ws; | 189 | let leading_ws; |
151 | let trailing_ws; | 190 | let trailing_ws; |
@@ -171,6 +210,7 @@ impl FunctionBuilder { | |||
171 | insert_offset, | 210 | insert_offset, |
172 | leading_ws, | 211 | leading_ws, |
173 | ret_type: fn_def.ret_type().unwrap(), | 212 | ret_type: fn_def.ret_type().unwrap(), |
213 | should_render_snippet: self.should_render_snippet, | ||
174 | fn_def, | 214 | fn_def, |
175 | trailing_ws, | 215 | trailing_ws, |
176 | file: self.file, | 216 | file: self.file, |
@@ -546,7 +586,7 @@ impl Baz { | |||
546 | } | 586 | } |
547 | } | 587 | } |
548 | 588 | ||
549 | fn bar(baz: Baz) ${0:-> ()} { | 589 | fn bar(baz: Baz) -> Baz { |
550 | todo!() | 590 | todo!() |
551 | } | 591 | } |
552 | ", | 592 | ", |
@@ -1060,6 +1100,27 @@ pub(crate) fn bar() ${0:-> ()} { | |||
1060 | } | 1100 | } |
1061 | 1101 | ||
1062 | #[test] | 1102 | #[test] |
1103 | fn add_function_with_return_type() { | ||
1104 | check_assist( | ||
1105 | generate_function, | ||
1106 | r" | ||
1107 | fn main() { | ||
1108 | let x: u32 = foo$0(); | ||
1109 | } | ||
1110 | ", | ||
1111 | r" | ||
1112 | fn main() { | ||
1113 | let x: u32 = foo(); | ||
1114 | } | ||
1115 | |||
1116 | fn foo() -> u32 { | ||
1117 | todo!() | ||
1118 | } | ||
1119 | ", | ||
1120 | ) | ||
1121 | } | ||
1122 | |||
1123 | #[test] | ||
1063 | fn add_function_not_applicable_if_function_already_exists() { | 1124 | fn add_function_not_applicable_if_function_already_exists() { |
1064 | check_assist_not_applicable( | 1125 | check_assist_not_applicable( |
1065 | generate_function, | 1126 | generate_function, |
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs index 9c34ed0b6..d45ad7944 100644 --- a/crates/ide_completion/src/completions/postfix.rs +++ b/crates/ide_completion/src/completions/postfix.rs | |||
@@ -187,6 +187,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
187 | ctx, | 187 | ctx, |
188 | cap, | 188 | cap, |
189 | &dot_receiver, | 189 | &dot_receiver, |
190 | "err", | ||
191 | "Err(expr)", | ||
192 | &format!("Err({})", receiver_text), | ||
193 | ) | ||
194 | .add_to(acc); | ||
195 | |||
196 | postfix_snippet( | ||
197 | ctx, | ||
198 | cap, | ||
199 | &dot_receiver, | ||
190 | "some", | 200 | "some", |
191 | "Some(expr)", | 201 | "Some(expr)", |
192 | &format!("Some({})", receiver_text), | 202 | &format!("Some({})", receiver_text), |
@@ -325,6 +335,7 @@ fn main() { | |||
325 | sn match match expr {} | 335 | sn match match expr {} |
326 | sn box Box::new(expr) | 336 | sn box Box::new(expr) |
327 | sn ok Ok(expr) | 337 | sn ok Ok(expr) |
338 | sn err Err(expr) | ||
328 | sn some Some(expr) | 339 | sn some Some(expr) |
329 | sn dbg dbg!(expr) | 340 | sn dbg dbg!(expr) |
330 | sn dbgr dbg!(&expr) | 341 | sn dbgr dbg!(&expr) |
@@ -357,6 +368,7 @@ fn main() { | |||
357 | sn match match expr {} | 368 | sn match match expr {} |
358 | sn box Box::new(expr) | 369 | sn box Box::new(expr) |
359 | sn ok Ok(expr) | 370 | sn ok Ok(expr) |
371 | sn err Err(expr) | ||
360 | sn some Some(expr) | 372 | sn some Some(expr) |
361 | sn dbg dbg!(expr) | 373 | sn dbg dbg!(expr) |
362 | sn dbgr dbg!(&expr) | 374 | sn dbgr dbg!(&expr) |
@@ -380,6 +392,7 @@ fn main() { | |||
380 | sn match match expr {} | 392 | sn match match expr {} |
381 | sn box Box::new(expr) | 393 | sn box Box::new(expr) |
382 | sn ok Ok(expr) | 394 | sn ok Ok(expr) |
395 | sn err Err(expr) | ||
383 | sn some Some(expr) | 396 | sn some Some(expr) |
384 | sn dbg dbg!(expr) | 397 | sn dbg dbg!(expr) |
385 | sn dbgr dbg!(&expr) | 398 | sn dbgr dbg!(&expr) |
@@ -408,6 +421,7 @@ fn main() { | |||
408 | sn match match expr {} | 421 | sn match match expr {} |
409 | sn box Box::new(expr) | 422 | sn box Box::new(expr) |
410 | sn ok Ok(expr) | 423 | sn ok Ok(expr) |
424 | sn err Err(expr) | ||
411 | sn some Some(expr) | 425 | sn some Some(expr) |
412 | sn dbg dbg!(expr) | 426 | sn dbg dbg!(expr) |
413 | sn dbgr dbg!(&expr) | 427 | sn dbgr dbg!(&expr) |
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 164c8482e..dd3ecc18d 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -39,7 +39,7 @@ If a language client does not know about `rust-analyzer`'s configuration options | |||
39 | 39 | ||
40 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/724 | 40 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/724 |
41 | 41 | ||
42 | **Client Capability:** `{ "snippetTextEdit": boolean }` | 42 | **Experimental Client Capability:** `{ "snippetTextEdit": boolean }` |
43 | 43 | ||
44 | If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: | 44 | If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: |
45 | 45 | ||
@@ -72,7 +72,7 @@ At the moment, rust-analyzer guarantees that only a single edit will have `Inser | |||
72 | 72 | ||
73 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/994 | 73 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/994 |
74 | 74 | ||
75 | **Client Capability:** `{ "codeActionGroup": boolean }` | 75 | **Experimental Client Capability:** `{ "codeActionGroup": boolean }` |
76 | 76 | ||
77 | If this capability is set, `CodeAction` returned from the server contain an additional field, `group`: | 77 | If this capability is set, `CodeAction` returned from the server contain an additional field, `group`: |
78 | 78 | ||
@@ -119,7 +119,7 @@ Invoking code action at this position will yield two code actions for importing | |||
119 | 119 | ||
120 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1002 | 120 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1002 |
121 | 121 | ||
122 | **Server Capability:** `{ "parentModule": boolean }` | 122 | **Experimental Server Capability:** `{ "parentModule": boolean }` |
123 | 123 | ||
124 | This request is sent from client to server to handle "Goto Parent Module" editor action. | 124 | This request is sent from client to server to handle "Goto Parent Module" editor action. |
125 | 125 | ||
@@ -153,7 +153,7 @@ mod foo; | |||
153 | 153 | ||
154 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/992 | 154 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/992 |
155 | 155 | ||
156 | **Server Capability:** `{ "joinLines": boolean }` | 156 | **Experimental Server Capability:** `{ "joinLines": boolean }` |
157 | 157 | ||
158 | This request is sent from client to server to handle "Join Lines" editor action. | 158 | This request is sent from client to server to handle "Join Lines" editor action. |
159 | 159 | ||
@@ -200,7 +200,7 @@ fn main() { | |||
200 | 200 | ||
201 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1001 | 201 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1001 |
202 | 202 | ||
203 | **Server Capability:** `{ "onEnter": boolean }` | 203 | **Experimental Server Capability:** `{ "onEnter": boolean }` |
204 | 204 | ||
205 | This request is sent from client to server to handle <kbd>Enter</kbd> keypress. | 205 | This request is sent from client to server to handle <kbd>Enter</kbd> keypress. |
206 | 206 | ||
@@ -251,7 +251,7 @@ As proper cursor positioning is raison-d'etat for `onEnter`, it uses `SnippetTex | |||
251 | 251 | ||
252 | ## Structural Search Replace (SSR) | 252 | ## Structural Search Replace (SSR) |
253 | 253 | ||
254 | **Server Capability:** `{ "ssr": boolean }` | 254 | **Experimental Server Capability:** `{ "ssr": boolean }` |
255 | 255 | ||
256 | This request is sent from client to server to handle structural search replace -- automated syntax tree based transformation of the source. | 256 | This request is sent from client to server to handle structural search replace -- automated syntax tree based transformation of the source. |
257 | 257 | ||
@@ -293,7 +293,7 @@ SSR with query `foo($a, $b) ==>> ($a).foo($b)` will transform, eg `foo(y + 5, z) | |||
293 | 293 | ||
294 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/999 | 294 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/999 |
295 | 295 | ||
296 | **Server Capability:** `{ "matchingBrace": boolean }` | 296 | **Experimental Server Capability:** `{ "matchingBrace": boolean }` |
297 | 297 | ||
298 | This request is sent from client to server to handle "Matching Brace" editor action. | 298 | This request is sent from client to server to handle "Matching Brace" editor action. |
299 | 299 | ||
@@ -338,7 +338,7 @@ Moreover, it would be cool if editors didn't need to implement even basic langua | |||
338 | 338 | ||
339 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/944 | 339 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/944 |
340 | 340 | ||
341 | **Server Capability:** `{ "runnables": { "kinds": string[] } }` | 341 | **Experimental Server Capability:** `{ "runnables": { "kinds": string[] } }` |
342 | 342 | ||
343 | This request is sent from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`). | 343 | This request is sent from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`). |
344 | 344 | ||
@@ -421,7 +421,7 @@ Reloads project information (that is, re-executes `cargo metadata`). | |||
421 | 421 | ||
422 | ## Status Notification | 422 | ## Status Notification |
423 | 423 | ||
424 | **Client Capability:** `{ "statusNotification": boolean }` | 424 | **Experimental Client Capability:** `{ "statusNotification": boolean }` |
425 | 425 | ||
426 | **Method:** `rust-analyzer/status` | 426 | **Method:** `rust-analyzer/status` |
427 | 427 | ||
@@ -519,7 +519,7 @@ interface InlayHint { | |||
519 | 519 | ||
520 | ## Hover Actions | 520 | ## Hover Actions |
521 | 521 | ||
522 | **Client Capability:** `{ "hoverActions": boolean }` | 522 | **Experimental Client Capability:** `{ "hoverActions": boolean }` |
523 | 523 | ||
524 | If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`: | 524 | If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`: |
525 | 525 | ||