diff options
Diffstat (limited to 'crates/assists/src/handlers/add_turbo_fish.rs')
-rw-r--r-- | crates/assists/src/handlers/add_turbo_fish.rs | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/add_turbo_fish.rs b/crates/assists/src/handlers/add_turbo_fish.rs new file mode 100644 index 000000000..f4f997d8e --- /dev/null +++ b/crates/assists/src/handlers/add_turbo_fish.rs | |||
@@ -0,0 +1,164 @@ | |||
1 | use ide_db::defs::{classify_name_ref, Definition, NameRefClass}; | ||
2 | use syntax::{ast, AstNode, SyntaxKind, T}; | ||
3 | use test_utils::mark; | ||
4 | |||
5 | use crate::{ | ||
6 | assist_context::{AssistContext, Assists}, | ||
7 | AssistId, AssistKind, | ||
8 | }; | ||
9 | |||
10 | // Assist: add_turbo_fish | ||
11 | // | ||
12 | // Adds `::<_>` to a call of a generic method or function. | ||
13 | // | ||
14 | // ``` | ||
15 | // fn make<T>() -> T { todo!() } | ||
16 | // fn main() { | ||
17 | // let x = make<|>(); | ||
18 | // } | ||
19 | // ``` | ||
20 | // -> | ||
21 | // ``` | ||
22 | // fn make<T>() -> T { todo!() } | ||
23 | // fn main() { | ||
24 | // let x = make::<${0:_}>(); | ||
25 | // } | ||
26 | // ``` | ||
27 | pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
28 | let ident = ctx.find_token_at_offset(SyntaxKind::IDENT).or_else(|| { | ||
29 | let arg_list = ctx.find_node_at_offset::<ast::ArgList>()?; | ||
30 | if arg_list.args().count() > 0 { | ||
31 | return None; | ||
32 | } | ||
33 | mark::hit!(add_turbo_fish_after_call); | ||
34 | arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) | ||
35 | })?; | ||
36 | let next_token = ident.next_token()?; | ||
37 | if next_token.kind() == T![::] { | ||
38 | mark::hit!(add_turbo_fish_one_fish_is_enough); | ||
39 | return None; | ||
40 | } | ||
41 | let name_ref = ast::NameRef::cast(ident.parent())?; | ||
42 | let def = match classify_name_ref(&ctx.sema, &name_ref)? { | ||
43 | NameRefClass::Definition(def) => def, | ||
44 | NameRefClass::ExternCrate(_) | NameRefClass::FieldShorthand { .. } => return None, | ||
45 | }; | ||
46 | let fun = match def { | ||
47 | Definition::ModuleDef(hir::ModuleDef::Function(it)) => it, | ||
48 | _ => return None, | ||
49 | }; | ||
50 | let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); | ||
51 | if generics.is_empty() { | ||
52 | mark::hit!(add_turbo_fish_non_generic); | ||
53 | return None; | ||
54 | } | ||
55 | acc.add( | ||
56 | AssistId("add_turbo_fish", AssistKind::RefactorRewrite), | ||
57 | "Add `::<>`", | ||
58 | ident.text_range(), | ||
59 | |builder| match ctx.config.snippet_cap { | ||
60 | Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"), | ||
61 | None => builder.insert(ident.text_range().end(), "::<_>"), | ||
62 | }, | ||
63 | ) | ||
64 | } | ||
65 | |||
66 | #[cfg(test)] | ||
67 | mod tests { | ||
68 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
69 | |||
70 | use super::*; | ||
71 | use test_utils::mark; | ||
72 | |||
73 | #[test] | ||
74 | fn add_turbo_fish_function() { | ||
75 | check_assist( | ||
76 | add_turbo_fish, | ||
77 | r#" | ||
78 | fn make<T>() -> T {} | ||
79 | fn main() { | ||
80 | make<|>(); | ||
81 | } | ||
82 | "#, | ||
83 | r#" | ||
84 | fn make<T>() -> T {} | ||
85 | fn main() { | ||
86 | make::<${0:_}>(); | ||
87 | } | ||
88 | "#, | ||
89 | ); | ||
90 | } | ||
91 | |||
92 | #[test] | ||
93 | fn add_turbo_fish_after_call() { | ||
94 | mark::check!(add_turbo_fish_after_call); | ||
95 | check_assist( | ||
96 | add_turbo_fish, | ||
97 | r#" | ||
98 | fn make<T>() -> T {} | ||
99 | fn main() { | ||
100 | make()<|>; | ||
101 | } | ||
102 | "#, | ||
103 | r#" | ||
104 | fn make<T>() -> T {} | ||
105 | fn main() { | ||
106 | make::<${0:_}>(); | ||
107 | } | ||
108 | "#, | ||
109 | ); | ||
110 | } | ||
111 | |||
112 | #[test] | ||
113 | fn add_turbo_fish_method() { | ||
114 | check_assist( | ||
115 | add_turbo_fish, | ||
116 | r#" | ||
117 | struct S; | ||
118 | impl S { | ||
119 | fn make<T>(&self) -> T {} | ||
120 | } | ||
121 | fn main() { | ||
122 | S.make<|>(); | ||
123 | } | ||
124 | "#, | ||
125 | r#" | ||
126 | struct S; | ||
127 | impl S { | ||
128 | fn make<T>(&self) -> T {} | ||
129 | } | ||
130 | fn main() { | ||
131 | S.make::<${0:_}>(); | ||
132 | } | ||
133 | "#, | ||
134 | ); | ||
135 | } | ||
136 | |||
137 | #[test] | ||
138 | fn add_turbo_fish_one_fish_is_enough() { | ||
139 | mark::check!(add_turbo_fish_one_fish_is_enough); | ||
140 | check_assist_not_applicable( | ||
141 | add_turbo_fish, | ||
142 | r#" | ||
143 | fn make<T>() -> T {} | ||
144 | fn main() { | ||
145 | make<|>::<()>(); | ||
146 | } | ||
147 | "#, | ||
148 | ); | ||
149 | } | ||
150 | |||
151 | #[test] | ||
152 | fn add_turbo_fish_non_generic() { | ||
153 | mark::check!(add_turbo_fish_non_generic); | ||
154 | check_assist_not_applicable( | ||
155 | add_turbo_fish, | ||
156 | r#" | ||
157 | fn make() -> () {} | ||
158 | fn main() { | ||
159 | make<|>(); | ||
160 | } | ||
161 | "#, | ||
162 | ); | ||
163 | } | ||
164 | } | ||