diff options
-rw-r--r-- | crates/ra_assists/src/doc_tests/generated.rs | 27 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/add_function.rs | 807 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 2 | ||||
-rw-r--r-- | docs/user/assists.md | 26 |
4 files changed, 862 insertions, 0 deletions
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index 0848ab6bc..64444ee3a 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs | |||
@@ -59,6 +59,33 @@ fn main() { | |||
59 | } | 59 | } |
60 | 60 | ||
61 | #[test] | 61 | #[test] |
62 | fn doctest_add_function() { | ||
63 | check( | ||
64 | "add_function", | ||
65 | r#####" | ||
66 | struct Baz; | ||
67 | fn baz() -> Baz { Baz } | ||
68 | fn foo() { | ||
69 | bar<|>("", baz()); | ||
70 | } | ||
71 | |||
72 | "#####, | ||
73 | r#####" | ||
74 | struct Baz; | ||
75 | fn baz() -> Baz { Baz } | ||
76 | fn foo() { | ||
77 | bar("", baz()); | ||
78 | } | ||
79 | |||
80 | fn bar(arg: &str, baz: Baz) { | ||
81 | unimplemented!() | ||
82 | } | ||
83 | |||
84 | "#####, | ||
85 | ) | ||
86 | } | ||
87 | |||
88 | #[test] | ||
62 | fn doctest_add_hash() { | 89 | fn doctest_add_hash() { |
63 | check( | 90 | check( |
64 | "add_hash", | 91 | "add_hash", |
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs new file mode 100644 index 000000000..f6721d9df --- /dev/null +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -0,0 +1,807 @@ | |||
1 | use ra_syntax::{ | ||
2 | ast::{self, AstNode}, | ||
3 | SmolStr, SyntaxKind, SyntaxNode, TextUnit, | ||
4 | }; | ||
5 | |||
6 | use crate::{Assist, AssistCtx, AssistId}; | ||
7 | use ast::{ArgListOwner, CallExpr, Expr}; | ||
8 | use hir::HirDisplay; | ||
9 | use ra_fmt::leading_indent; | ||
10 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
11 | |||
12 | // Assist: add_function | ||
13 | // | ||
14 | // Adds a stub function with a signature matching the function under the cursor. | ||
15 | // | ||
16 | // ``` | ||
17 | // struct Baz; | ||
18 | // fn baz() -> Baz { Baz } | ||
19 | // fn foo() { | ||
20 | // bar<|>("", baz()); | ||
21 | // } | ||
22 | // | ||
23 | // ``` | ||
24 | // -> | ||
25 | // ``` | ||
26 | // struct Baz; | ||
27 | // fn baz() -> Baz { Baz } | ||
28 | // fn foo() { | ||
29 | // bar("", baz()); | ||
30 | // } | ||
31 | // | ||
32 | // fn bar(arg: &str, baz: Baz) { | ||
33 | // unimplemented!() | ||
34 | // } | ||
35 | // | ||
36 | // ``` | ||
37 | pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> { | ||
38 | let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; | ||
39 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; | ||
40 | let path = path_expr.path()?; | ||
41 | |||
42 | if path.qualifier().is_some() { | ||
43 | return None; | ||
44 | } | ||
45 | |||
46 | if ctx.sema.resolve_path(&path).is_some() { | ||
47 | // The function call already resolves, no need to add a function | ||
48 | return None; | ||
49 | } | ||
50 | |||
51 | let function_builder = FunctionBuilder::from_call(&ctx, &call)?; | ||
52 | |||
53 | ctx.add_assist(AssistId("add_function"), "Add function", |edit| { | ||
54 | edit.target(call.syntax().text_range()); | ||
55 | |||
56 | let function_template = function_builder.render(); | ||
57 | edit.set_cursor(function_template.cursor_offset); | ||
58 | edit.insert(function_template.insert_offset, function_template.fn_text); | ||
59 | }) | ||
60 | } | ||
61 | |||
62 | struct FunctionTemplate { | ||
63 | insert_offset: TextUnit, | ||
64 | cursor_offset: TextUnit, | ||
65 | fn_text: String, | ||
66 | } | ||
67 | |||
68 | struct FunctionBuilder { | ||
69 | start_offset: TextUnit, | ||
70 | fn_name: String, | ||
71 | fn_generics: String, | ||
72 | fn_args: String, | ||
73 | indent: String, | ||
74 | } | ||
75 | |||
76 | impl FunctionBuilder { | ||
77 | fn from_call(ctx: &AssistCtx, call: &ast::CallExpr) -> Option<Self> { | ||
78 | let (start, indent) = next_space_for_fn(&call)?; | ||
79 | let fn_name = fn_name(&call)?; | ||
80 | let fn_generics = fn_generics(&call)?; | ||
81 | let fn_args = fn_args(ctx, &call)?; | ||
82 | let indent = if let Some(i) = &indent { i.to_string() } else { String::new() }; | ||
83 | Some(Self { start_offset: start, fn_name, fn_generics, fn_args, indent }) | ||
84 | } | ||
85 | fn render(&self) -> FunctionTemplate { | ||
86 | let mut fn_buf = String::with_capacity(128); | ||
87 | fn_buf.push_str("\n\n"); | ||
88 | fn_buf.push_str(&self.indent); | ||
89 | fn_buf.push_str("fn "); | ||
90 | fn_buf.push_str(&self.fn_name); | ||
91 | fn_buf.push_str(&self.fn_generics); | ||
92 | fn_buf.push_str(&self.fn_args); | ||
93 | fn_buf.push_str(" {\n"); | ||
94 | fn_buf.push_str(&self.indent); | ||
95 | fn_buf.push_str(" "); | ||
96 | |||
97 | // We take the offset here to put the cursor in front of the `unimplemented!()` body | ||
98 | let offset = TextUnit::of_str(&fn_buf); | ||
99 | |||
100 | fn_buf.push_str("unimplemented!()\n"); | ||
101 | fn_buf.push_str(&self.indent); | ||
102 | fn_buf.push_str("}"); | ||
103 | |||
104 | let cursor_pos = self.start_offset + offset; | ||
105 | FunctionTemplate { | ||
106 | fn_text: fn_buf, | ||
107 | cursor_offset: cursor_pos, | ||
108 | insert_offset: self.start_offset, | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | fn fn_name(call: &CallExpr) -> Option<String> { | ||
114 | Some(call.expr()?.syntax().to_string()) | ||
115 | } | ||
116 | |||
117 | fn fn_generics(_call: &CallExpr) -> Option<String> { | ||
118 | // TODO | ||
119 | Some("".into()) | ||
120 | } | ||
121 | |||
122 | fn fn_args(ctx: &AssistCtx, call: &CallExpr) -> Option<String> { | ||
123 | let mut arg_names = Vec::new(); | ||
124 | let mut arg_types = Vec::new(); | ||
125 | for arg in call.arg_list()?.args() { | ||
126 | let arg_name = match fn_arg_name(&arg) { | ||
127 | Some(name) => name, | ||
128 | None => String::from("arg"), | ||
129 | }; | ||
130 | arg_names.push(arg_name); | ||
131 | arg_types.push(match fn_arg_type(ctx, &arg) { | ||
132 | Some(ty) => ty, | ||
133 | None => String::from("()"), | ||
134 | }); | ||
135 | } | ||
136 | deduplicate_arg_names(&mut arg_names); | ||
137 | Some(format!( | ||
138 | "({})", | ||
139 | arg_names | ||
140 | .into_iter() | ||
141 | .zip(arg_types) | ||
142 | .map(|(name, ty)| format!("{}: {}", name, ty)) | ||
143 | .collect::<Vec<_>>() | ||
144 | .join(", ") | ||
145 | )) | ||
146 | } | ||
147 | |||
148 | /// Makes duplicate argument names unique by appending incrementing numbers. | ||
149 | /// | ||
150 | /// ``` | ||
151 | /// let mut names: Vec<String> = | ||
152 | /// vec!["foo".into(), "foo".into(), "bar".into(), "baz".into(), "bar".into()]; | ||
153 | /// deduplicate_arg_names(&mut names); | ||
154 | /// let expected: Vec<String> = | ||
155 | /// vec!["foo_1".into(), "foo_2".into(), "bar_1".into(), "baz".into(), "bar_2".into()]; | ||
156 | /// assert_eq!(names, expected); | ||
157 | /// ``` | ||
158 | fn deduplicate_arg_names(arg_names: &mut Vec<String>) { | ||
159 | let arg_name_counts = arg_names.iter().fold(FxHashMap::default(), |mut m, name| { | ||
160 | *m.entry(name).or_insert(0) += 1; | ||
161 | m | ||
162 | }); | ||
163 | let duplicate_arg_names: FxHashSet<String> = arg_name_counts | ||
164 | .into_iter() | ||
165 | .filter(|(_, count)| *count >= 2) | ||
166 | .map(|(name, _)| name.clone()) | ||
167 | .collect(); | ||
168 | |||
169 | let mut counter_per_name = FxHashMap::default(); | ||
170 | for arg_name in arg_names.iter_mut() { | ||
171 | if duplicate_arg_names.contains(arg_name) { | ||
172 | let counter = counter_per_name.entry(arg_name.clone()).or_insert(1); | ||
173 | arg_name.push('_'); | ||
174 | arg_name.push_str(&counter.to_string()); | ||
175 | *counter += 1; | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | |||
180 | fn fn_arg_name(fn_arg: &Expr) -> Option<String> { | ||
181 | match fn_arg { | ||
182 | Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?), | ||
183 | _ => Some( | ||
184 | fn_arg | ||
185 | .syntax() | ||
186 | .descendants() | ||
187 | .filter(|d| ast::NameRef::can_cast(d.kind())) | ||
188 | .last()? | ||
189 | .to_string(), | ||
190 | ), | ||
191 | } | ||
192 | } | ||
193 | |||
194 | fn fn_arg_type(ctx: &AssistCtx, fn_arg: &Expr) -> Option<String> { | ||
195 | let ty = ctx.sema.type_of_expr(fn_arg)?; | ||
196 | if ty.is_unknown() { | ||
197 | return None; | ||
198 | } | ||
199 | Some(ty.display(ctx.sema.db).to_string()) | ||
200 | } | ||
201 | |||
202 | /// Returns the position inside the current mod or file | ||
203 | /// directly after the current block | ||
204 | /// We want to write the generated function directly after | ||
205 | /// fns, impls or macro calls, but inside mods | ||
206 | fn next_space_for_fn(expr: &CallExpr) -> Option<(TextUnit, Option<SmolStr>)> { | ||
207 | let mut ancestors = expr.syntax().ancestors().peekable(); | ||
208 | let mut last_ancestor: Option<SyntaxNode> = None; | ||
209 | while let Some(next_ancestor) = ancestors.next() { | ||
210 | match next_ancestor.kind() { | ||
211 | SyntaxKind::SOURCE_FILE => { | ||
212 | break; | ||
213 | } | ||
214 | SyntaxKind::ITEM_LIST => { | ||
215 | if ancestors.peek().map(|a| a.kind()) == Some(SyntaxKind::MODULE) { | ||
216 | break; | ||
217 | } | ||
218 | } | ||
219 | _ => {} | ||
220 | } | ||
221 | last_ancestor = Some(next_ancestor); | ||
222 | } | ||
223 | last_ancestor.map(|a| (a.text_range().end(), leading_indent(&a))) | ||
224 | } | ||
225 | |||
226 | #[cfg(test)] | ||
227 | mod tests { | ||
228 | use crate::helpers::{check_assist, check_assist_not_applicable}; | ||
229 | |||
230 | use super::*; | ||
231 | |||
232 | #[test] | ||
233 | fn add_function_with_no_args() { | ||
234 | check_assist( | ||
235 | add_function, | ||
236 | r" | ||
237 | fn foo() { | ||
238 | bar<|>(); | ||
239 | } | ||
240 | ", | ||
241 | r" | ||
242 | fn foo() { | ||
243 | bar(); | ||
244 | } | ||
245 | |||
246 | fn bar() { | ||
247 | <|>unimplemented!() | ||
248 | } | ||
249 | ", | ||
250 | ) | ||
251 | } | ||
252 | |||
253 | #[test] | ||
254 | fn add_function_from_method() { | ||
255 | // This ensures that the function is correctly generated | ||
256 | // in the next outer mod or file | ||
257 | check_assist( | ||
258 | add_function, | ||
259 | r" | ||
260 | impl Foo { | ||
261 | fn foo() { | ||
262 | bar<|>(); | ||
263 | } | ||
264 | } | ||
265 | ", | ||
266 | r" | ||
267 | impl Foo { | ||
268 | fn foo() { | ||
269 | bar(); | ||
270 | } | ||
271 | } | ||
272 | |||
273 | fn bar() { | ||
274 | <|>unimplemented!() | ||
275 | } | ||
276 | ", | ||
277 | ) | ||
278 | } | ||
279 | |||
280 | #[test] | ||
281 | fn add_function_directly_after_current_block() { | ||
282 | // The new fn should not be created at the end of the file or module | ||
283 | check_assist( | ||
284 | add_function, | ||
285 | r" | ||
286 | fn foo1() { | ||
287 | bar<|>(); | ||
288 | } | ||
289 | |||
290 | fn foo2() {} | ||
291 | ", | ||
292 | r" | ||
293 | fn foo1() { | ||
294 | bar(); | ||
295 | } | ||
296 | |||
297 | fn bar() { | ||
298 | <|>unimplemented!() | ||
299 | } | ||
300 | |||
301 | fn foo2() {} | ||
302 | ", | ||
303 | ) | ||
304 | } | ||
305 | |||
306 | #[test] | ||
307 | fn add_function_with_no_args_in_same_module() { | ||
308 | check_assist( | ||
309 | add_function, | ||
310 | r" | ||
311 | mod baz { | ||
312 | fn foo() { | ||
313 | bar<|>(); | ||
314 | } | ||
315 | } | ||
316 | ", | ||
317 | r" | ||
318 | mod baz { | ||
319 | fn foo() { | ||
320 | bar(); | ||
321 | } | ||
322 | |||
323 | fn bar() { | ||
324 | <|>unimplemented!() | ||
325 | } | ||
326 | } | ||
327 | ", | ||
328 | ) | ||
329 | } | ||
330 | |||
331 | #[test] | ||
332 | fn add_function_with_function_call_arg() { | ||
333 | check_assist( | ||
334 | add_function, | ||
335 | r" | ||
336 | struct Baz; | ||
337 | fn baz() -> Baz { unimplemented!() } | ||
338 | fn foo() { | ||
339 | bar<|>(baz()); | ||
340 | } | ||
341 | ", | ||
342 | r" | ||
343 | struct Baz; | ||
344 | fn baz() -> Baz { unimplemented!() } | ||
345 | fn foo() { | ||
346 | bar(baz()); | ||
347 | } | ||
348 | |||
349 | fn bar(baz: Baz) { | ||
350 | <|>unimplemented!() | ||
351 | } | ||
352 | ", | ||
353 | ); | ||
354 | } | ||
355 | |||
356 | #[test] | ||
357 | fn add_function_with_method_call_arg() { | ||
358 | check_assist( | ||
359 | add_function, | ||
360 | r" | ||
361 | struct Baz; | ||
362 | impl Baz { | ||
363 | fn foo(&self) -> Baz { | ||
364 | ba<|>r(self.baz()) | ||
365 | } | ||
366 | fn baz(&self) -> Baz { | ||
367 | Baz | ||
368 | } | ||
369 | } | ||
370 | ", | ||
371 | r" | ||
372 | struct Baz; | ||
373 | impl Baz { | ||
374 | fn foo(&self) -> Baz { | ||
375 | bar(self.baz()) | ||
376 | } | ||
377 | fn baz(&self) -> Baz { | ||
378 | Baz | ||
379 | } | ||
380 | } | ||
381 | |||
382 | fn bar(baz: Baz) { | ||
383 | <|>unimplemented!() | ||
384 | } | ||
385 | ", | ||
386 | ) | ||
387 | } | ||
388 | |||
389 | #[test] | ||
390 | fn add_function_with_string_literal_arg() { | ||
391 | check_assist( | ||
392 | add_function, | ||
393 | r#" | ||
394 | fn foo() { | ||
395 | <|>bar("bar") | ||
396 | } | ||
397 | "#, | ||
398 | r#" | ||
399 | fn foo() { | ||
400 | bar("bar") | ||
401 | } | ||
402 | |||
403 | fn bar(arg: &str) { | ||
404 | <|>unimplemented!() | ||
405 | } | ||
406 | "#, | ||
407 | ) | ||
408 | } | ||
409 | |||
410 | #[test] | ||
411 | fn add_function_with_char_literal_arg() { | ||
412 | check_assist( | ||
413 | add_function, | ||
414 | r#" | ||
415 | fn foo() { | ||
416 | <|>bar('x') | ||
417 | } | ||
418 | "#, | ||
419 | r#" | ||
420 | fn foo() { | ||
421 | bar('x') | ||
422 | } | ||
423 | |||
424 | fn bar(arg: char) { | ||
425 | <|>unimplemented!() | ||
426 | } | ||
427 | "#, | ||
428 | ) | ||
429 | } | ||
430 | |||
431 | #[test] | ||
432 | fn add_function_with_int_literal_arg() { | ||
433 | check_assist( | ||
434 | add_function, | ||
435 | r" | ||
436 | fn foo() { | ||
437 | <|>bar(42) | ||
438 | } | ||
439 | ", | ||
440 | r" | ||
441 | fn foo() { | ||
442 | bar(42) | ||
443 | } | ||
444 | |||
445 | fn bar(arg: i32) { | ||
446 | <|>unimplemented!() | ||
447 | } | ||
448 | ", | ||
449 | ) | ||
450 | } | ||
451 | |||
452 | #[test] | ||
453 | fn add_function_with_cast_int_literal_arg() { | ||
454 | check_assist( | ||
455 | add_function, | ||
456 | r" | ||
457 | fn foo() { | ||
458 | <|>bar(42 as u8) | ||
459 | } | ||
460 | ", | ||
461 | r" | ||
462 | fn foo() { | ||
463 | bar(42 as u8) | ||
464 | } | ||
465 | |||
466 | fn bar(arg: u8) { | ||
467 | <|>unimplemented!() | ||
468 | } | ||
469 | ", | ||
470 | ) | ||
471 | } | ||
472 | |||
473 | #[test] | ||
474 | fn name_of_cast_variable_is_used() { | ||
475 | // Ensures that the name of the cast type isn't used | ||
476 | // in the generated function signature. | ||
477 | check_assist( | ||
478 | add_function, | ||
479 | r" | ||
480 | fn foo() { | ||
481 | let x = 42; | ||
482 | bar<|>(x as u8) | ||
483 | } | ||
484 | ", | ||
485 | r" | ||
486 | fn foo() { | ||
487 | let x = 42; | ||
488 | bar(x as u8) | ||
489 | } | ||
490 | |||
491 | fn bar(x: u8) { | ||
492 | <|>unimplemented!() | ||
493 | } | ||
494 | ", | ||
495 | ) | ||
496 | } | ||
497 | |||
498 | #[test] | ||
499 | fn add_function_with_variable_arg() { | ||
500 | check_assist( | ||
501 | add_function, | ||
502 | r" | ||
503 | fn foo() { | ||
504 | let worble = (); | ||
505 | <|>bar(worble) | ||
506 | } | ||
507 | ", | ||
508 | r" | ||
509 | fn foo() { | ||
510 | let worble = (); | ||
511 | bar(worble) | ||
512 | } | ||
513 | |||
514 | fn bar(worble: ()) { | ||
515 | <|>unimplemented!() | ||
516 | } | ||
517 | ", | ||
518 | ) | ||
519 | } | ||
520 | |||
521 | #[test] | ||
522 | fn add_function_with_impl_trait_arg() { | ||
523 | check_assist( | ||
524 | add_function, | ||
525 | r" | ||
526 | trait Foo {} | ||
527 | fn foo() -> impl Foo { | ||
528 | unimplemented!() | ||
529 | } | ||
530 | fn baz() { | ||
531 | <|>bar(foo()) | ||
532 | } | ||
533 | ", | ||
534 | r" | ||
535 | trait Foo {} | ||
536 | fn foo() -> impl Foo { | ||
537 | unimplemented!() | ||
538 | } | ||
539 | fn baz() { | ||
540 | bar(foo()) | ||
541 | } | ||
542 | |||
543 | fn bar(foo: impl Foo) { | ||
544 | <|>unimplemented!() | ||
545 | } | ||
546 | ", | ||
547 | ) | ||
548 | } | ||
549 | |||
550 | #[test] | ||
551 | #[ignore] | ||
552 | // FIXME print paths properly to make this test pass | ||
553 | fn add_function_with_qualified_path_arg() { | ||
554 | check_assist( | ||
555 | add_function, | ||
556 | r" | ||
557 | mod Baz { | ||
558 | pub struct Bof; | ||
559 | pub fn baz() -> Bof { Bof } | ||
560 | } | ||
561 | mod Foo { | ||
562 | fn foo() { | ||
563 | <|>bar(super::Baz::baz()) | ||
564 | } | ||
565 | } | ||
566 | ", | ||
567 | r" | ||
568 | mod Baz { | ||
569 | pub struct Bof; | ||
570 | pub fn baz() -> Bof { Bof } | ||
571 | } | ||
572 | mod Foo { | ||
573 | fn foo() { | ||
574 | bar(super::Baz::baz()) | ||
575 | } | ||
576 | |||
577 | fn bar(baz: super::Baz::Bof) { | ||
578 | <|>unimplemented!() | ||
579 | } | ||
580 | } | ||
581 | ", | ||
582 | ) | ||
583 | } | ||
584 | |||
585 | #[test] | ||
586 | #[ignore] | ||
587 | // FIXME fix printing the generics of a `Ty` to make this test pass | ||
588 | fn add_function_with_generic_arg() { | ||
589 | check_assist( | ||
590 | add_function, | ||
591 | r" | ||
592 | fn foo<T>(t: T) { | ||
593 | <|>bar(t) | ||
594 | } | ||
595 | ", | ||
596 | r" | ||
597 | fn foo<T>(t: T) { | ||
598 | bar(t) | ||
599 | } | ||
600 | |||
601 | fn bar<T>(t: T) { | ||
602 | <|>unimplemented!() | ||
603 | } | ||
604 | ", | ||
605 | ) | ||
606 | } | ||
607 | |||
608 | #[test] | ||
609 | #[ignore] | ||
610 | // FIXME Fix function type printing to make this test pass | ||
611 | fn add_function_with_fn_arg() { | ||
612 | check_assist( | ||
613 | add_function, | ||
614 | r" | ||
615 | struct Baz; | ||
616 | impl Baz { | ||
617 | fn new() -> Self { Baz } | ||
618 | } | ||
619 | fn foo() { | ||
620 | <|>bar(Baz::new); | ||
621 | } | ||
622 | ", | ||
623 | r" | ||
624 | struct Baz; | ||
625 | impl Baz { | ||
626 | fn new() -> Self { Baz } | ||
627 | } | ||
628 | fn foo() { | ||
629 | bar(Baz::new); | ||
630 | } | ||
631 | |||
632 | fn bar(arg: fn() -> Baz) { | ||
633 | <|>unimplemented!() | ||
634 | } | ||
635 | ", | ||
636 | ) | ||
637 | } | ||
638 | |||
639 | #[test] | ||
640 | #[ignore] | ||
641 | // FIXME Fix closure type printing to make this test pass | ||
642 | fn add_function_with_closure_arg() { | ||
643 | check_assist( | ||
644 | add_function, | ||
645 | r" | ||
646 | fn foo() { | ||
647 | let closure = |x: i64| x - 1; | ||
648 | <|>bar(closure) | ||
649 | } | ||
650 | ", | ||
651 | r" | ||
652 | fn foo() { | ||
653 | let closure = |x: i64| x - 1; | ||
654 | bar(closure) | ||
655 | } | ||
656 | |||
657 | fn bar(closure: impl Fn(i64) -> i64) { | ||
658 | <|>unimplemented!() | ||
659 | } | ||
660 | ", | ||
661 | ) | ||
662 | } | ||
663 | |||
664 | #[test] | ||
665 | fn unresolveable_types_default_to_unit() { | ||
666 | check_assist( | ||
667 | add_function, | ||
668 | r" | ||
669 | fn foo() { | ||
670 | <|>bar(baz) | ||
671 | } | ||
672 | ", | ||
673 | r" | ||
674 | fn foo() { | ||
675 | bar(baz) | ||
676 | } | ||
677 | |||
678 | fn bar(baz: ()) { | ||
679 | <|>unimplemented!() | ||
680 | } | ||
681 | ", | ||
682 | ) | ||
683 | } | ||
684 | |||
685 | #[test] | ||
686 | fn arg_names_dont_overlap() { | ||
687 | check_assist( | ||
688 | add_function, | ||
689 | r" | ||
690 | struct Baz; | ||
691 | fn baz() -> Baz { Baz } | ||
692 | fn foo() { | ||
693 | <|>bar(baz(), baz()) | ||
694 | } | ||
695 | ", | ||
696 | r" | ||
697 | struct Baz; | ||
698 | fn baz() -> Baz { Baz } | ||
699 | fn foo() { | ||
700 | bar(baz(), baz()) | ||
701 | } | ||
702 | |||
703 | fn bar(baz_1: Baz, baz_2: Baz) { | ||
704 | <|>unimplemented!() | ||
705 | } | ||
706 | ", | ||
707 | ) | ||
708 | } | ||
709 | |||
710 | #[test] | ||
711 | fn arg_name_counters_start_at_1_per_name() { | ||
712 | check_assist( | ||
713 | add_function, | ||
714 | r#" | ||
715 | struct Baz; | ||
716 | fn baz() -> Baz { Baz } | ||
717 | fn foo() { | ||
718 | <|>bar(baz(), baz(), "foo", "bar") | ||
719 | } | ||
720 | "#, | ||
721 | r#" | ||
722 | struct Baz; | ||
723 | fn baz() -> Baz { Baz } | ||
724 | fn foo() { | ||
725 | bar(baz(), baz(), "foo", "bar") | ||
726 | } | ||
727 | |||
728 | fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { | ||
729 | <|>unimplemented!() | ||
730 | } | ||
731 | "#, | ||
732 | ) | ||
733 | } | ||
734 | |||
735 | #[test] | ||
736 | fn add_function_not_applicable_if_function_already_exists() { | ||
737 | check_assist_not_applicable( | ||
738 | add_function, | ||
739 | r" | ||
740 | fn foo() { | ||
741 | bar<|>(); | ||
742 | } | ||
743 | |||
744 | fn bar() {} | ||
745 | ", | ||
746 | ) | ||
747 | } | ||
748 | |||
749 | #[test] | ||
750 | fn add_function_not_applicable_if_unresolved_variable_in_call_is_selected() { | ||
751 | check_assist_not_applicable( | ||
752 | // bar is resolved, but baz isn't. | ||
753 | // The assist is only active if the cursor is on an unresolved path, | ||
754 | // but the assist should only be offered if the path is a function call. | ||
755 | add_function, | ||
756 | r" | ||
757 | fn foo() { | ||
758 | bar(b<|>az); | ||
759 | } | ||
760 | |||
761 | fn bar(baz: ()) {} | ||
762 | ", | ||
763 | ) | ||
764 | } | ||
765 | |||
766 | #[test] | ||
767 | fn add_function_not_applicable_if_function_path_not_singleton() { | ||
768 | // In the future this assist could be extended to generate functions | ||
769 | // if the path is in the same crate (or even the same workspace). | ||
770 | // For the beginning, I think this is fine. | ||
771 | check_assist_not_applicable( | ||
772 | add_function, | ||
773 | r" | ||
774 | fn foo() { | ||
775 | other_crate::bar<|>(); | ||
776 | } | ||
777 | ", | ||
778 | ) | ||
779 | } | ||
780 | |||
781 | #[test] | ||
782 | #[ignore] | ||
783 | fn create_method_with_no_args() { | ||
784 | check_assist( | ||
785 | add_function, | ||
786 | r" | ||
787 | struct Foo; | ||
788 | impl Foo { | ||
789 | fn foo(&self) { | ||
790 | self.bar()<|>; | ||
791 | } | ||
792 | } | ||
793 | ", | ||
794 | r" | ||
795 | struct Foo; | ||
796 | impl Foo { | ||
797 | fn foo(&self) { | ||
798 | self.bar(); | ||
799 | } | ||
800 | fn bar(&self) { | ||
801 | unimplemented!(); | ||
802 | } | ||
803 | } | ||
804 | ", | ||
805 | ) | ||
806 | } | ||
807 | } | ||
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index fa1f3dd26..66854cb5a 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -96,6 +96,7 @@ mod handlers { | |||
96 | mod add_custom_impl; | 96 | mod add_custom_impl; |
97 | mod add_derive; | 97 | mod add_derive; |
98 | mod add_explicit_type; | 98 | mod add_explicit_type; |
99 | mod add_function; | ||
99 | mod add_impl; | 100 | mod add_impl; |
100 | mod add_missing_impl_members; | 101 | mod add_missing_impl_members; |
101 | mod add_new; | 102 | mod add_new; |
@@ -128,6 +129,7 @@ mod handlers { | |||
128 | add_custom_impl::add_custom_impl, | 129 | add_custom_impl::add_custom_impl, |
129 | add_derive::add_derive, | 130 | add_derive::add_derive, |
130 | add_explicit_type::add_explicit_type, | 131 | add_explicit_type::add_explicit_type, |
132 | add_function::add_function, | ||
131 | add_impl::add_impl, | 133 | add_impl::add_impl, |
132 | add_missing_impl_members::add_missing_default_members, | 134 | add_missing_impl_members::add_missing_default_members, |
133 | add_missing_impl_members::add_missing_impl_members, | 135 | add_missing_impl_members::add_missing_impl_members, |
diff --git a/docs/user/assists.md b/docs/user/assists.md index 94b5ef85d..754131f6f 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md | |||
@@ -56,6 +56,32 @@ fn main() { | |||
56 | } | 56 | } |
57 | ``` | 57 | ``` |
58 | 58 | ||
59 | ## `add_function` | ||
60 | |||
61 | Adds a stub function with a signature matching the function under the cursor. | ||
62 | |||
63 | ```rust | ||
64 | // BEFORE | ||
65 | struct Baz; | ||
66 | fn baz() -> Baz { Baz } | ||
67 | fn foo() { | ||
68 | bar┃("", baz()); | ||
69 | } | ||
70 | |||
71 | |||
72 | // AFTER | ||
73 | struct Baz; | ||
74 | fn baz() -> Baz { Baz } | ||
75 | fn foo() { | ||
76 | bar("", baz()); | ||
77 | } | ||
78 | |||
79 | fn bar(arg: &str, baz: Baz) { | ||
80 | unimplemented!() | ||
81 | } | ||
82 | |||
83 | ``` | ||
84 | |||
59 | ## `add_hash` | 85 | ## `add_hash` |
60 | 86 | ||
61 | Adds a hash to a raw string literal. | 87 | Adds a hash to a raw string literal. |