aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Schievink <[email protected]>2021-03-26 18:39:20 +0000
committerJonas Schievink <[email protected]>2021-03-26 18:39:20 +0000
commite39979aa91c8c08219e35a74ae5aa7aa5d8bc4d6 (patch)
tree47581f4f5555036fafdb39ea2da4eb4eb7eac44b
parent4ecaad98e074c42dbf637a11afcb630aafffd7b3 (diff)
Implement "Extract type alias" assist
-rw-r--r--crates/ide_assists/src/handlers/extract_type_alias.rs165
-rw-r--r--crates/ide_assists/src/lib.rs2
-rw-r--r--crates/ide_assists/src/tests/generated.rs19
3 files changed, 186 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/extract_type_alias.rs b/crates/ide_assists/src/handlers/extract_type_alias.rs
new file mode 100644
index 000000000..771868234
--- /dev/null
+++ b/crates/ide_assists/src/handlers/extract_type_alias.rs
@@ -0,0 +1,165 @@
1use syntax::{
2 ast::{self, AstNode},
3 SyntaxKind,
4};
5
6use 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 Type = (u8, u8, u8);
20//
21// struct S {
22// field: Type,
23// }
24// ```
25pub(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 = match ctx.covering_element() {
31 syntax::NodeOrToken::Node(node) => node,
32 syntax::NodeOrToken::Token(tok) => tok.parent()?,
33 };
34 let range = node.text_range();
35 let mut type_like_node = None;
36 for node in node.ancestors() {
37 if node.text_range() != range {
38 break;
39 }
40
41 let kind = node.kind();
42 if ast::Type::can_cast(kind) || kind == SyntaxKind::TYPE_ARG {
43 type_like_node = Some(node);
44 break;
45 }
46 }
47
48 let node = type_like_node?;
49
50 let insert = ctx.find_node_at_offset::<ast::Item>()?.syntax().text_range().start();
51 let target = node.text_range();
52
53 acc.add(
54 AssistId("extract_type_alias", AssistKind::RefactorExtract),
55 "Extract type as type alias",
56 target,
57 |builder| {
58 builder.edit_file(ctx.frange.file_id);
59 // FIXME: add snippet support
60 builder.replace(target, "Type");
61 builder.insert(insert, format!("type Type = {};\n\n", node));
62 },
63 )
64}
65
66#[cfg(test)]
67mod tests {
68 use crate::tests::{check_assist, check_assist_not_applicable};
69
70 use super::*;
71
72 #[test]
73 fn test_not_applicable_without_selection() {
74 check_assist_not_applicable(
75 extract_type_alias,
76 r"
77struct S {
78 field: $0(u8, u8, u8),
79}
80 ",
81 );
82 }
83
84 #[test]
85 fn test_simple_types() {
86 check_assist(
87 extract_type_alias,
88 r"
89struct S {
90 field: $0u8$0,
91}
92 ",
93 r#"
94type Type = u8;
95
96struct S {
97 field: Type,
98}
99 "#,
100 );
101 }
102
103 #[test]
104 fn test_generic_type_arg() {
105 check_assist(
106 extract_type_alias,
107 r"
108fn generic<T>() {}
109
110fn f() {
111 generic::<$0()$0>();
112}
113 ",
114 r#"
115fn generic<T>() {}
116
117type Type = ();
118
119fn f() {
120 generic::<Type>();
121}
122 "#,
123 );
124 }
125
126 #[test]
127 fn test_inner_type_arg() {
128 check_assist(
129 extract_type_alias,
130 r"
131struct Vec<T> {}
132struct S {
133 v: Vec<Vec<$0Vec<u8>$0>>,
134}
135 ",
136 r#"
137struct Vec<T> {}
138type Type = Vec<u8>;
139
140struct S {
141 v: Vec<Vec<Type>>,
142}
143 "#,
144 );
145 }
146
147 #[test]
148 fn test_extract_inner_type() {
149 check_assist(
150 extract_type_alias,
151 r"
152struct S {
153 field: ($0u8$0,),
154}
155 ",
156 r#"
157type Type = u8;
158
159struct S {
160 field: (Type,),
161}
162 "#,
163 );
164 }
165}
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 8c068a6c0..3d1dcef4c 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -121,6 +121,7 @@ mod handlers {
121 mod expand_glob_import; 121 mod expand_glob_import;
122 mod extract_function; 122 mod extract_function;
123 mod extract_struct_from_enum_variant; 123 mod extract_struct_from_enum_variant;
124 mod extract_type_alias;
124 mod extract_variable; 125 mod extract_variable;
125 mod fill_match_arms; 126 mod fill_match_arms;
126 mod fix_visibility; 127 mod fix_visibility;
@@ -187,6 +188,7 @@ mod handlers {
187 early_return::convert_to_guarded_return, 188 early_return::convert_to_guarded_return,
188 expand_glob_import::expand_glob_import, 189 expand_glob_import::expand_glob_import,
189 extract_struct_from_enum_variant::extract_struct_from_enum_variant, 190 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
191 extract_type_alias::extract_type_alias,
190 fill_match_arms::fill_match_arms, 192 fill_match_arms::fill_match_arms,
191 fix_visibility::fix_visibility, 193 fix_visibility::fix_visibility,
192 flip_binexpr::flip_binexpr, 194 flip_binexpr::flip_binexpr,
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 736027ff0..6bb65e6bc 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -329,6 +329,25 @@ enum A { One(One) }
329} 329}
330 330
331#[test] 331#[test]
332fn doctest_extract_type_alias() {
333 check_doc_test(
334 "extract_type_alias",
335 r#####"
336struct S {
337 field: $0(u8, u8, u8)$0,
338}
339"#####,
340 r#####"
341type Type = (u8, u8, u8);
342
343struct S {
344 field: Type,
345}
346"#####,
347 )
348}
349
350#[test]
332fn doctest_extract_variable() { 351fn doctest_extract_variable() {
333 check_doc_test( 352 check_doc_test(
334 "extract_variable", 353 "extract_variable",