]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/extract_type_alias.rs
Merge #9105
[rust.git] / crates / ide_assists / src / handlers / extract_type_alias.rs
1 use syntax::{
2     ast::{self, edit::IndentLevel, AstNode},
3     match_ast,
4 };
5
6 use crate::{AssistContext, AssistId, AssistKind, Assists};
7
8 // Assist: extract_type_alias
9 //
10 // Extracts the selected type as a type alias.
11 //
12 // ```
13 // struct S {
14 //     field: $0(u8, u8, u8)$0,
15 // }
16 // ```
17 // ->
18 // ```
19 // type $0Type = (u8, u8, u8);
20 //
21 // struct S {
22 //     field: Type,
23 // }
24 // ```
25 pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26     if ctx.frange.range.is_empty() {
27         return None;
28     }
29
30     let node = ctx.find_node_at_range::<ast::Type>()?;
31     let item = ctx.find_node_at_offset::<ast::Item>()?;
32     let insert = match_ast! {
33         match (item.syntax().parent()?) {
34             ast::AssocItemList(it) => it.syntax().parent()?.clone(),
35             _ => item.syntax().clone(),
36         }
37     };
38     let indent = IndentLevel::from_node(&insert);
39     let insert = insert.text_range().start();
40     let target = node.syntax().text_range();
41
42     acc.add(
43         AssistId("extract_type_alias", AssistKind::RefactorExtract),
44         "Extract type as type alias",
45         target,
46         |builder| {
47             builder.edit_file(ctx.frange.file_id);
48             builder.replace(target, "Type");
49             match ctx.config.snippet_cap {
50                 Some(cap) => {
51                     builder.insert_snippet(
52                         cap,
53                         insert,
54                         format!("type $0Type = {};\n\n{}", node, indent),
55                     );
56                 }
57                 None => {
58                     builder.insert(insert, format!("type Type = {};\n\n{}", node, indent));
59                 }
60             }
61         },
62     )
63 }
64
65 #[cfg(test)]
66 mod tests {
67     use crate::tests::{check_assist, check_assist_not_applicable};
68
69     use super::*;
70
71     #[test]
72     fn test_not_applicable_without_selection() {
73         check_assist_not_applicable(
74             extract_type_alias,
75             r"
76 struct S {
77     field: $0(u8, u8, u8),
78 }
79             ",
80         );
81     }
82
83     #[test]
84     fn test_simple_types() {
85         check_assist(
86             extract_type_alias,
87             r"
88 struct S {
89     field: $0u8$0,
90 }
91             ",
92             r#"
93 type $0Type = u8;
94
95 struct S {
96     field: Type,
97 }
98             "#,
99         );
100     }
101
102     #[test]
103     fn test_generic_type_arg() {
104         check_assist(
105             extract_type_alias,
106             r"
107 fn generic<T>() {}
108
109 fn f() {
110     generic::<$0()$0>();
111 }
112             ",
113             r#"
114 fn generic<T>() {}
115
116 type $0Type = ();
117
118 fn f() {
119     generic::<Type>();
120 }
121             "#,
122         );
123     }
124
125     #[test]
126     fn test_inner_type_arg() {
127         check_assist(
128             extract_type_alias,
129             r"
130 struct Vec<T> {}
131 struct S {
132     v: Vec<Vec<$0Vec<u8>$0>>,
133 }
134             ",
135             r#"
136 struct Vec<T> {}
137 type $0Type = Vec<u8>;
138
139 struct S {
140     v: Vec<Vec<Type>>,
141 }
142             "#,
143         );
144     }
145
146     #[test]
147     fn test_extract_inner_type() {
148         check_assist(
149             extract_type_alias,
150             r"
151 struct S {
152     field: ($0u8$0,),
153 }
154             ",
155             r#"
156 type $0Type = u8;
157
158 struct S {
159     field: (Type,),
160 }
161             "#,
162         );
163     }
164
165     #[test]
166     fn extract_from_impl_or_trait() {
167         // When invoked in an impl/trait, extracted type alias should be placed next to the
168         // impl/trait, not inside.
169         check_assist(
170             extract_type_alias,
171             r#"
172 impl S {
173     fn f() -> $0(u8, u8)$0 {}
174 }
175             "#,
176             r#"
177 type $0Type = (u8, u8);
178
179 impl S {
180     fn f() -> Type {}
181 }
182             "#,
183         );
184         check_assist(
185             extract_type_alias,
186             r#"
187 trait Tr {
188     fn f() -> $0(u8, u8)$0 {}
189 }
190             "#,
191             r#"
192 type $0Type = (u8, u8);
193
194 trait Tr {
195     fn f() -> Type {}
196 }
197             "#,
198         );
199     }
200
201     #[test]
202     fn indentation() {
203         check_assist(
204             extract_type_alias,
205             r#"
206 mod m {
207     fn f() -> $0u8$0 {}
208 }
209             "#,
210             r#"
211 mod m {
212     type $0Type = u8;
213
214     fn f() -> Type {}
215 }
216             "#,
217         );
218     }
219 }