diff options
-rw-r--r-- | crates/ide_assists/src/assist_context.rs | 5 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/extract_type_alias.rs | 149 | ||||
-rw-r--r-- | crates/ide_assists/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/tests/generated.rs | 19 | ||||
-rw-r--r-- | editors/code/src/snippets.ts | 10 |
5 files changed, 179 insertions, 6 deletions
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs index 1482d37f8..8714e4978 100644 --- a/crates/ide_assists/src/assist_context.rs +++ b/crates/ide_assists/src/assist_context.rs | |||
@@ -13,7 +13,7 @@ use ide_db::{ | |||
13 | RootDatabase, | 13 | RootDatabase, |
14 | }; | 14 | }; |
15 | use syntax::{ | 15 | use syntax::{ |
16 | algo::{self, find_node_at_offset, SyntaxRewriter}, | 16 | algo::{self, find_node_at_offset, find_node_at_range, SyntaxRewriter}, |
17 | AstNode, AstToken, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr, | 17 | AstNode, AstToken, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr, |
18 | SyntaxToken, TextRange, TextSize, TokenAtOffset, | 18 | SyntaxToken, TextRange, TextSize, TokenAtOffset, |
19 | }; | 19 | }; |
@@ -89,6 +89,9 @@ impl<'a> AssistContext<'a> { | |||
89 | pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> { | 89 | pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> { |
90 | find_node_at_offset(self.source_file.syntax(), self.offset()) | 90 | find_node_at_offset(self.source_file.syntax(), self.offset()) |
91 | } | 91 | } |
92 | pub(crate) fn find_node_at_range<N: AstNode>(&self) -> Option<N> { | ||
93 | find_node_at_range(self.source_file.syntax(), self.frange.range) | ||
94 | } | ||
92 | pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> { | 95 | pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> { |
93 | self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset()) | 96 | self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset()) |
94 | } | 97 | } |
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..442a209b9 --- /dev/null +++ b/crates/ide_assists/src/handlers/extract_type_alias.rs | |||
@@ -0,0 +1,149 @@ | |||
1 | use syntax::ast::{self, AstNode}; | ||
2 | |||
3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
4 | |||
5 | // Assist: extract_type_alias | ||
6 | // | ||
7 | // Extracts the selected type as a type alias. | ||
8 | // | ||
9 | // ``` | ||
10 | // struct S { | ||
11 | // field: $0(u8, u8, u8)$0, | ||
12 | // } | ||
13 | // ``` | ||
14 | // -> | ||
15 | // ``` | ||
16 | // type $0Type = (u8, u8, u8); | ||
17 | // | ||
18 | // struct S { | ||
19 | // field: Type, | ||
20 | // } | ||
21 | // ``` | ||
22 | pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
23 | if ctx.frange.range.is_empty() { | ||
24 | return None; | ||
25 | } | ||
26 | |||
27 | let node = ctx.find_node_at_range::<ast::Type>()?; | ||
28 | let insert = ctx.find_node_at_offset::<ast::Item>()?.syntax().text_range().start(); | ||
29 | let target = node.syntax().text_range(); | ||
30 | |||
31 | acc.add( | ||
32 | AssistId("extract_type_alias", AssistKind::RefactorExtract), | ||
33 | "Extract type as type alias", | ||
34 | target, | ||
35 | |builder| { | ||
36 | builder.edit_file(ctx.frange.file_id); | ||
37 | builder.replace(target, "Type"); | ||
38 | match ctx.config.snippet_cap { | ||
39 | Some(cap) => { | ||
40 | builder.insert_snippet(cap, insert, format!("type $0Type = {};\n\n", node)); | ||
41 | } | ||
42 | None => { | ||
43 | builder.insert(insert, format!("type Type = {};\n\n", node)); | ||
44 | } | ||
45 | } | ||
46 | }, | ||
47 | ) | ||
48 | } | ||
49 | |||
50 | #[cfg(test)] | ||
51 | mod tests { | ||
52 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
53 | |||
54 | use super::*; | ||
55 | |||
56 | #[test] | ||
57 | fn test_not_applicable_without_selection() { | ||
58 | check_assist_not_applicable( | ||
59 | extract_type_alias, | ||
60 | r" | ||
61 | struct S { | ||
62 | field: $0(u8, u8, u8), | ||
63 | } | ||
64 | ", | ||
65 | ); | ||
66 | } | ||
67 | |||
68 | #[test] | ||
69 | fn test_simple_types() { | ||
70 | check_assist( | ||
71 | extract_type_alias, | ||
72 | r" | ||
73 | struct S { | ||
74 | field: $0u8$0, | ||
75 | } | ||
76 | ", | ||
77 | r#" | ||
78 | type $0Type = u8; | ||
79 | |||
80 | struct S { | ||
81 | field: Type, | ||
82 | } | ||
83 | "#, | ||
84 | ); | ||
85 | } | ||
86 | |||
87 | #[test] | ||
88 | fn test_generic_type_arg() { | ||
89 | check_assist( | ||
90 | extract_type_alias, | ||
91 | r" | ||
92 | fn generic<T>() {} | ||
93 | |||
94 | fn f() { | ||
95 | generic::<$0()$0>(); | ||
96 | } | ||
97 | ", | ||
98 | r#" | ||
99 | fn generic<T>() {} | ||
100 | |||
101 | type $0Type = (); | ||
102 | |||
103 | fn f() { | ||
104 | generic::<Type>(); | ||
105 | } | ||
106 | "#, | ||
107 | ); | ||
108 | } | ||
109 | |||
110 | #[test] | ||
111 | fn test_inner_type_arg() { | ||
112 | check_assist( | ||
113 | extract_type_alias, | ||
114 | r" | ||
115 | struct Vec<T> {} | ||
116 | struct S { | ||
117 | v: Vec<Vec<$0Vec<u8>$0>>, | ||
118 | } | ||
119 | ", | ||
120 | r#" | ||
121 | struct Vec<T> {} | ||
122 | type $0Type = Vec<u8>; | ||
123 | |||
124 | struct S { | ||
125 | v: Vec<Vec<Type>>, | ||
126 | } | ||
127 | "#, | ||
128 | ); | ||
129 | } | ||
130 | |||
131 | #[test] | ||
132 | fn test_extract_inner_type() { | ||
133 | check_assist( | ||
134 | extract_type_alias, | ||
135 | r" | ||
136 | struct S { | ||
137 | field: ($0u8$0,), | ||
138 | } | ||
139 | ", | ||
140 | r#" | ||
141 | type $0Type = u8; | ||
142 | |||
143 | struct S { | ||
144 | field: (Type,), | ||
145 | } | ||
146 | "#, | ||
147 | ); | ||
148 | } | ||
149 | } | ||
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..03b7fb366 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] |
332 | fn doctest_extract_type_alias() { | ||
333 | check_doc_test( | ||
334 | "extract_type_alias", | ||
335 | r#####" | ||
336 | struct S { | ||
337 | field: $0(u8, u8, u8)$0, | ||
338 | } | ||
339 | "#####, | ||
340 | r#####" | ||
341 | type $0Type = (u8, u8, u8); | ||
342 | |||
343 | struct S { | ||
344 | field: Type, | ||
345 | } | ||
346 | "#####, | ||
347 | ) | ||
348 | } | ||
349 | |||
350 | #[test] | ||
332 | fn doctest_extract_variable() { | 351 | fn doctest_extract_variable() { |
333 | check_doc_test( | 352 | check_doc_test( |
334 | "extract_variable", | 353 | "extract_variable", |
diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts index dc53ebe2e..9561aa345 100644 --- a/editors/code/src/snippets.ts +++ b/editors/code/src/snippets.ts | |||
@@ -29,7 +29,7 @@ async function editorFromUri(uri: vscode.Uri): Promise<vscode.TextEditor | undef | |||
29 | } | 29 | } |
30 | 30 | ||
31 | export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) { | 31 | export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) { |
32 | let selection: vscode.Selection | undefined = undefined; | 32 | const selections: vscode.Selection[] = []; |
33 | let lineDelta = 0; | 33 | let lineDelta = 0; |
34 | await editor.edit((builder) => { | 34 | await editor.edit((builder) => { |
35 | for (const indel of edits) { | 35 | for (const indel of edits) { |
@@ -44,18 +44,18 @@ export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vs | |||
44 | indel.range.start.character + placeholderStart | 44 | indel.range.start.character + placeholderStart |
45 | : prefix.length - lastNewline - 1; | 45 | : prefix.length - lastNewline - 1; |
46 | const endColumn = startColumn + placeholderLength; | 46 | const endColumn = startColumn + placeholderLength; |
47 | selection = new vscode.Selection( | 47 | selections.push(new vscode.Selection( |
48 | new vscode.Position(startLine, startColumn), | 48 | new vscode.Position(startLine, startColumn), |
49 | new vscode.Position(startLine, endColumn), | 49 | new vscode.Position(startLine, endColumn), |
50 | ); | 50 | )); |
51 | builder.replace(indel.range, newText); | 51 | builder.replace(indel.range, newText); |
52 | } else { | 52 | } else { |
53 | lineDelta = countLines(indel.newText) - (indel.range.end.line - indel.range.start.line); | ||
54 | builder.replace(indel.range, indel.newText); | 53 | builder.replace(indel.range, indel.newText); |
55 | } | 54 | } |
55 | lineDelta = countLines(indel.newText) - (indel.range.end.line - indel.range.start.line); | ||
56 | } | 56 | } |
57 | }); | 57 | }); |
58 | if (selection) editor.selection = selection; | 58 | if (selections.length > 0) editor.selections = selections; |
59 | } | 59 | } |
60 | 60 | ||
61 | function parseSnippet(snip: string): [string, [number, number]] | undefined { | 61 | function parseSnippet(snip: string): [string, [number, number]] | undefined { |