1
- use std:: collections:: BTreeSet ;
2
-
3
- use either:: Either ;
4
- use hir:: {
5
- AsAssocItem , AssocItemContainer , ModPath , Module , ModuleDef , PathResolution , Semantics , Trait ,
6
- Type ,
7
- } ;
8
- use ide_db:: { imports_locator, RootDatabase } ;
9
- use insert_use:: ImportScope ;
10
- use rustc_hash:: FxHashSet ;
11
- use syntax:: {
12
- ast:: { self , AstNode } ,
13
- SyntaxNode ,
14
- } ;
1
+ use syntax:: ast;
15
2
16
3
use crate :: {
17
- utils:: insert_use, utils:: mod_path_to_ast, AssistContext , AssistId , AssistKind , Assists ,
18
- GroupLabel ,
4
+ utils:: import_assets:: { ImportAssets , ImportCandidate } ,
5
+ utils:: { insert_use, mod_path_to_ast, ImportScope } ,
6
+ AssistContext , AssistId , AssistKind , Assists , GroupLabel ,
19
7
} ;
20
8
21
9
// Assist: auto_import
@@ -38,16 +26,24 @@ use crate::{
38
26
// # pub mod std { pub mod collections { pub struct HashMap { } } }
39
27
// ```
40
28
pub ( crate ) fn auto_import ( acc : & mut Assists , ctx : & AssistContext ) -> Option < ( ) > {
41
- let auto_import_assets = AutoImportAssets :: new ( ctx) ?;
42
- let proposed_imports = auto_import_assets. search_for_imports ( ctx) ;
29
+ let import_assets =
30
+ if let Some ( path_under_caret) = ctx. find_node_at_offset_with_descend :: < ast:: Path > ( ) {
31
+ ImportAssets :: for_regular_path ( path_under_caret, & ctx. sema )
32
+ } else if let Some ( method_under_caret) =
33
+ ctx. find_node_at_offset_with_descend :: < ast:: MethodCallExpr > ( )
34
+ {
35
+ ImportAssets :: for_method_call ( method_under_caret, & ctx. sema )
36
+ } else {
37
+ None
38
+ } ?;
39
+ let proposed_imports = import_assets. search_for_imports ( & ctx. sema , & ctx. config . insert_use ) ;
43
40
if proposed_imports. is_empty ( ) {
44
41
return None ;
45
42
}
46
43
47
- let range = ctx. sema . original_range ( & auto_import_assets. syntax_under_caret ) . range ;
48
- let group = auto_import_assets. get_import_group_message ( ) ;
49
- let scope =
50
- ImportScope :: find_insert_use_container ( & auto_import_assets. syntax_under_caret , ctx) ?;
44
+ let range = ctx. sema . original_range ( import_assets. syntax_under_caret ( ) ) . range ;
45
+ let group = import_group_message ( import_assets. import_candidate ( ) ) ;
46
+ let scope = ImportScope :: find_insert_use_container ( import_assets. syntax_under_caret ( ) , ctx) ?;
51
47
let syntax = scope. as_syntax_node ( ) ;
52
48
for import in proposed_imports {
53
49
acc. add_group (
@@ -65,227 +61,18 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
65
61
Some ( ( ) )
66
62
}
67
63
68
- #[ derive( Debug ) ]
69
- struct AutoImportAssets {
70
- import_candidate : ImportCandidate ,
71
- module_with_name_to_import : Module ,
72
- syntax_under_caret : SyntaxNode ,
73
- }
74
-
75
- impl AutoImportAssets {
76
- fn new ( ctx : & AssistContext ) -> Option < Self > {
77
- if let Some ( path_under_caret) = ctx. find_node_at_offset_with_descend :: < ast:: Path > ( ) {
78
- Self :: for_regular_path ( path_under_caret, & ctx)
79
- } else {
80
- Self :: for_method_call ( ctx. find_node_at_offset_with_descend ( ) ?, & ctx)
81
- }
82
- }
83
-
84
- fn for_method_call ( method_call : ast:: MethodCallExpr , ctx : & AssistContext ) -> Option < Self > {
85
- let syntax_under_caret = method_call. syntax ( ) . to_owned ( ) ;
86
- let module_with_name_to_import = ctx. sema . scope ( & syntax_under_caret) . module ( ) ?;
87
- Some ( Self {
88
- import_candidate : ImportCandidate :: for_method_call ( & ctx. sema , & method_call) ?,
89
- module_with_name_to_import,
90
- syntax_under_caret,
91
- } )
92
- }
93
-
94
- fn for_regular_path ( path_under_caret : ast:: Path , ctx : & AssistContext ) -> Option < Self > {
95
- let syntax_under_caret = path_under_caret. syntax ( ) . to_owned ( ) ;
96
- if syntax_under_caret. ancestors ( ) . find_map ( ast:: Use :: cast) . is_some ( ) {
97
- return None ;
98
- }
99
-
100
- let module_with_name_to_import = ctx. sema . scope ( & syntax_under_caret) . module ( ) ?;
101
- Some ( Self {
102
- import_candidate : ImportCandidate :: for_regular_path ( & ctx. sema , & path_under_caret) ?,
103
- module_with_name_to_import,
104
- syntax_under_caret,
105
- } )
106
- }
107
-
108
- fn get_search_query ( & self ) -> & str {
109
- match & self . import_candidate {
110
- ImportCandidate :: UnqualifiedName ( name) => name,
111
- ImportCandidate :: QualifierStart ( qualifier_start) => qualifier_start,
112
- ImportCandidate :: TraitAssocItem ( _, trait_assoc_item_name) => trait_assoc_item_name,
113
- ImportCandidate :: TraitMethod ( _, trait_method_name) => trait_method_name,
114
- }
115
- }
116
-
117
- fn get_import_group_message ( & self ) -> GroupLabel {
118
- let name = match & self . import_candidate {
119
- ImportCandidate :: UnqualifiedName ( name) => format ! ( "Import {}" , name) ,
120
- ImportCandidate :: QualifierStart ( qualifier_start) => {
121
- format ! ( "Import {}" , qualifier_start)
122
- }
123
- ImportCandidate :: TraitAssocItem ( _, trait_assoc_item_name) => {
124
- format ! ( "Import a trait for item {}" , trait_assoc_item_name)
125
- }
126
- ImportCandidate :: TraitMethod ( _, trait_method_name) => {
127
- format ! ( "Import a trait for method {}" , trait_method_name)
128
- }
129
- } ;
130
- GroupLabel ( name)
131
- }
132
-
133
- fn search_for_imports ( & self , ctx : & AssistContext ) -> BTreeSet < ModPath > {
134
- let _p = profile:: span ( "auto_import::search_for_imports" ) ;
135
- let db = ctx. db ( ) ;
136
- let current_crate = self . module_with_name_to_import . krate ( ) ;
137
- imports_locator:: find_imports ( & ctx. sema , current_crate, & self . get_search_query ( ) )
138
- . into_iter ( )
139
- . filter_map ( |candidate| match & self . import_candidate {
140
- ImportCandidate :: TraitAssocItem ( assoc_item_type, _) => {
141
- let located_assoc_item = match candidate {
142
- Either :: Left ( ModuleDef :: Function ( located_function) ) => located_function
143
- . as_assoc_item ( db)
144
- . map ( |assoc| assoc. container ( db) )
145
- . and_then ( Self :: assoc_to_trait) ,
146
- Either :: Left ( ModuleDef :: Const ( located_const) ) => located_const
147
- . as_assoc_item ( db)
148
- . map ( |assoc| assoc. container ( db) )
149
- . and_then ( Self :: assoc_to_trait) ,
150
- _ => None ,
151
- } ?;
152
-
153
- let mut trait_candidates = FxHashSet :: default ( ) ;
154
- trait_candidates. insert ( located_assoc_item. into ( ) ) ;
155
-
156
- assoc_item_type
157
- . iterate_path_candidates (
158
- db,
159
- current_crate,
160
- & trait_candidates,
161
- None ,
162
- |_, assoc| Self :: assoc_to_trait ( assoc. container ( db) ) ,
163
- )
164
- . map ( ModuleDef :: from)
165
- . map ( Either :: Left )
166
- }
167
- ImportCandidate :: TraitMethod ( function_callee, _) => {
168
- let located_assoc_item =
169
- if let Either :: Left ( ModuleDef :: Function ( located_function) ) = candidate {
170
- located_function
171
- . as_assoc_item ( db)
172
- . map ( |assoc| assoc. container ( db) )
173
- . and_then ( Self :: assoc_to_trait)
174
- } else {
175
- None
176
- } ?;
177
-
178
- let mut trait_candidates = FxHashSet :: default ( ) ;
179
- trait_candidates. insert ( located_assoc_item. into ( ) ) ;
180
-
181
- function_callee
182
- . iterate_method_candidates (
183
- db,
184
- current_crate,
185
- & trait_candidates,
186
- None ,
187
- |_, function| {
188
- Self :: assoc_to_trait ( function. as_assoc_item ( db) ?. container ( db) )
189
- } ,
190
- )
191
- . map ( ModuleDef :: from)
192
- . map ( Either :: Left )
193
- }
194
- _ => Some ( candidate) ,
195
- } )
196
- . filter_map ( |candidate| match candidate {
197
- Either :: Left ( module_def) => self . module_with_name_to_import . find_use_path_prefixed (
198
- db,
199
- module_def,
200
- ctx. config . insert_use . prefix_kind ,
201
- ) ,
202
- Either :: Right ( macro_def) => self . module_with_name_to_import . find_use_path_prefixed (
203
- db,
204
- macro_def,
205
- ctx. config . insert_use . prefix_kind ,
206
- ) ,
207
- } )
208
- . filter ( |use_path| !use_path. segments . is_empty ( ) )
209
- . take ( 20 )
210
- . collect :: < BTreeSet < _ > > ( )
211
- }
212
-
213
- fn assoc_to_trait ( assoc : AssocItemContainer ) -> Option < Trait > {
214
- if let AssocItemContainer :: Trait ( extracted_trait) = assoc {
215
- Some ( extracted_trait)
216
- } else {
217
- None
218
- }
219
- }
220
- }
221
-
222
- #[ derive( Debug ) ]
223
- enum ImportCandidate {
224
- /// Simple name like 'HashMap'
225
- UnqualifiedName ( String ) ,
226
- /// First part of the qualified name.
227
- /// For 'std::collections::HashMap', that will be 'std'.
228
- QualifierStart ( String ) ,
229
- /// A trait associated function (with no self parameter) or associated constant.
230
- /// For 'test_mod::TestEnum::test_function', `Type` is the `test_mod::TestEnum` expression type
231
- /// and `String` is the `test_function`
232
- TraitAssocItem ( Type , String ) ,
233
- /// A trait method with self parameter.
234
- /// For 'test_enum.test_method()', `Type` is the `test_enum` expression type
235
- /// and `String` is the `test_method`
236
- TraitMethod ( Type , String ) ,
237
- }
238
-
239
- impl ImportCandidate {
240
- fn for_method_call (
241
- sema : & Semantics < RootDatabase > ,
242
- method_call : & ast:: MethodCallExpr ,
243
- ) -> Option < Self > {
244
- if sema. resolve_method_call ( method_call) . is_some ( ) {
245
- return None ;
246
- }
247
- Some ( Self :: TraitMethod (
248
- sema. type_of_expr ( & method_call. receiver ( ) ?) ?,
249
- method_call. name_ref ( ) ?. syntax ( ) . to_string ( ) ,
250
- ) )
251
- }
252
-
253
- fn for_regular_path (
254
- sema : & Semantics < RootDatabase > ,
255
- path_under_caret : & ast:: Path ,
256
- ) -> Option < Self > {
257
- if sema. resolve_path ( path_under_caret) . is_some ( ) {
258
- return None ;
64
+ fn import_group_message ( import_candidate : & ImportCandidate ) -> GroupLabel {
65
+ let name = match import_candidate {
66
+ ImportCandidate :: UnqualifiedName ( candidate)
67
+ | ImportCandidate :: QualifierStart ( candidate) => format ! ( "Import {}" , & candidate. name) ,
68
+ ImportCandidate :: TraitAssocItem ( candidate) => {
69
+ format ! ( "Import a trait for item {}" , & candidate. name)
259
70
}
260
-
261
- let segment = path_under_caret. segment ( ) ?;
262
- if let Some ( qualifier) = path_under_caret. qualifier ( ) {
263
- let qualifier_start = qualifier. syntax ( ) . descendants ( ) . find_map ( ast:: NameRef :: cast) ?;
264
- let qualifier_start_path =
265
- qualifier_start. syntax ( ) . ancestors ( ) . find_map ( ast:: Path :: cast) ?;
266
- if let Some ( qualifier_start_resolution) = sema. resolve_path ( & qualifier_start_path) {
267
- let qualifier_resolution = if qualifier_start_path == qualifier {
268
- qualifier_start_resolution
269
- } else {
270
- sema. resolve_path ( & qualifier) ?
271
- } ;
272
- if let PathResolution :: Def ( ModuleDef :: Adt ( assoc_item_path) ) = qualifier_resolution {
273
- Some ( ImportCandidate :: TraitAssocItem (
274
- assoc_item_path. ty ( sema. db ) ,
275
- segment. syntax ( ) . to_string ( ) ,
276
- ) )
277
- } else {
278
- None
279
- }
280
- } else {
281
- Some ( ImportCandidate :: QualifierStart ( qualifier_start. syntax ( ) . to_string ( ) ) )
282
- }
283
- } else {
284
- Some ( ImportCandidate :: UnqualifiedName (
285
- segment. syntax ( ) . descendants ( ) . find_map ( ast:: NameRef :: cast) ?. syntax ( ) . to_string ( ) ,
286
- ) )
71
+ ImportCandidate :: TraitMethod ( candidate) => {
72
+ format ! ( "Import a trait for method {}" , & candidate. name)
287
73
}
288
- }
74
+ } ;
75
+ GroupLabel ( name)
289
76
}
290
77
291
78
#[ cfg( test) ]
0 commit comments