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- } ;
15-
161use crate :: {
17- utils:: insert_use, utils:: mod_path_to_ast, AssistContext , AssistId , AssistKind , Assists ,
18- GroupLabel ,
2+ utils:: import_assets:: { ImportAssets , ImportCandidate } ,
3+ utils:: { insert_use, mod_path_to_ast, ImportScope } ,
4+ AssistContext , AssistId , AssistKind , Assists , GroupLabel ,
195} ;
206
217// Assist: auto_import
@@ -38,16 +24,16 @@ use crate::{
3824// # pub mod std { pub mod collections { pub struct HashMap { } } }
3925// ```
4026pub ( 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) ;
27+ let auto_import_assets = ImportAssets :: new ( & ctx) ?;
28+ let proposed_imports = auto_import_assets. search_for_imports ( & ctx. sema , & ctx . config . insert_use ) ;
4329 if proposed_imports. is_empty ( ) {
4430 return None ;
4531 }
4632
47- let range = ctx. sema . original_range ( & auto_import_assets. syntax_under_caret ) . range ;
48- let group = auto_import_assets. get_import_group_message ( ) ;
33+ let range = ctx. sema . original_range ( auto_import_assets. syntax_under_caret ( ) ) . range ;
34+ let group = import_group_message ( auto_import_assets. import_candidate ( ) ) ;
4935 let scope =
50- ImportScope :: find_insert_use_container ( & auto_import_assets. syntax_under_caret , ctx) ?;
36+ ImportScope :: find_insert_use_container ( auto_import_assets. syntax_under_caret ( ) , ctx) ?;
5137 let syntax = scope. as_syntax_node ( ) ;
5238 for import in proposed_imports {
5339 acc. add_group (
@@ -65,227 +51,18 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
6551 Some ( ( ) )
6652}
6753
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)
54+ fn import_group_message ( import_candidate : & ImportCandidate ) -> GroupLabel {
55+ let name = match import_candidate {
56+ ImportCandidate :: UnqualifiedName ( name) => format ! ( "Import {}" , name) ,
57+ ImportCandidate :: QualifierStart ( qualifier_start) => format ! ( "Import {}" , qualifier_start) ,
58+ ImportCandidate :: TraitAssocItem ( _, trait_assoc_item_name) => {
59+ format ! ( "Import a trait for item {}" , trait_assoc_item_name)
8160 }
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 ;
61+ ImportCandidate :: TraitMethod ( _, trait_method_name) => {
62+ format ! ( "Import a trait for method {}" , trait_method_name)
9863 }
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 ;
259- }
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- ) )
287- }
288- }
64+ } ;
65+ GroupLabel ( name)
28966}
29067
29168#[ cfg( test) ]
0 commit comments