diff options
-rw-r--r-- | crates/ra_assists/src/auto_import.rs | 151 |
1 files changed, 107 insertions, 44 deletions
diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs index 14a564301..77380b816 100644 --- a/crates/ra_assists/src/auto_import.rs +++ b/crates/ra_assists/src/auto_import.rs | |||
@@ -140,30 +140,64 @@ fn compare_path_segment_with_name(a: &ast::PathSegment, b: &ast::Name) -> bool { | |||
140 | enum ImportAction<'a> { | 140 | enum ImportAction<'a> { |
141 | Nothing, | 141 | Nothing, |
142 | // Add a brand new use statement. | 142 | // Add a brand new use statement. |
143 | AddNewUse( | 143 | AddNewUse { |
144 | Option<&'a SyntaxNode>, // anchor node | 144 | anchor: Option<&'a SyntaxNode>, // anchor node |
145 | bool, // true if we want to add the new statement after the anchor | 145 | add_after_anchor: bool, |
146 | ), | 146 | }, |
147 | |||
148 | // In the following actions we keep track of how may segments matched, | ||
149 | // so we can choose the best action to take. | ||
150 | 147 | ||
151 | // To split an existing use statement creating a nested import. | 148 | // To split an existing use statement creating a nested import. |
152 | AddNestedImport( | 149 | AddNestedImport { |
153 | usize, | 150 | // how may segments matched with the target path |
154 | &'a ast::Path, // the complete path we want to split | 151 | common_segments: usize, |
155 | Option<&'a ast::PathSegment>, // the first segment of path we want to add into the new nested list | 152 | path_to_split: &'a ast::Path, |
156 | bool, // true if we want to add 'self' in addition to the segment | 153 | // the first segment of path_to_split we want to add into the new nested list |
157 | ), | 154 | first_segment_to_split: Option<&'a ast::PathSegment>, |
155 | // Wether to add 'self' in addition to the target path | ||
156 | add_self: bool, | ||
157 | }, | ||
158 | // To add the target path to an existing nested import tree list. | 158 | // To add the target path to an existing nested import tree list. |
159 | AddInTreeList( | 159 | AddInTreeList { |
160 | usize, | 160 | common_segments: usize, |
161 | &'a ast::UseTreeList, | 161 | // The UseTreeList where to add the target path |
162 | bool, // true if we want to add 'self' | 162 | tree_list: &'a ast::UseTreeList, |
163 | ), | 163 | add_self: bool, |
164 | }, | ||
164 | } | 165 | } |
165 | 166 | ||
166 | impl<'a> ImportAction<'a> { | 167 | impl<'a> ImportAction<'a> { |
168 | fn add_new_use(anchor: Option<&'a SyntaxNode>, add_after_anchor: bool) -> Self { | ||
169 | ImportAction::AddNewUse { | ||
170 | anchor, | ||
171 | add_after_anchor, | ||
172 | } | ||
173 | } | ||
174 | |||
175 | fn add_nested_import( | ||
176 | common_segments: usize, | ||
177 | path_to_split: &'a ast::Path, | ||
178 | first_segment_to_split: Option<&'a ast::PathSegment>, | ||
179 | add_self: bool, | ||
180 | ) -> Self { | ||
181 | ImportAction::AddNestedImport { | ||
182 | common_segments, | ||
183 | path_to_split, | ||
184 | first_segment_to_split, | ||
185 | add_self, | ||
186 | } | ||
187 | } | ||
188 | |||
189 | fn add_in_tree_list( | ||
190 | common_segments: usize, | ||
191 | tree_list: &'a ast::UseTreeList, | ||
192 | add_self: bool, | ||
193 | ) -> Self { | ||
194 | ImportAction::AddInTreeList { | ||
195 | common_segments, | ||
196 | tree_list, | ||
197 | add_self, | ||
198 | } | ||
199 | } | ||
200 | |||
167 | fn better<'b>(left: &'b ImportAction<'a>, right: &'b ImportAction<'a>) -> &'b ImportAction<'a> { | 201 | fn better<'b>(left: &'b ImportAction<'a>, right: &'b ImportAction<'a>) -> &'b ImportAction<'a> { |
168 | if left.is_better(right) { | 202 | if left.is_better(right) { |
169 | left | 203 | left |
@@ -175,13 +209,27 @@ impl<'a> ImportAction<'a> { | |||
175 | fn is_better(&self, other: &ImportAction) -> bool { | 209 | fn is_better(&self, other: &ImportAction) -> bool { |
176 | match (self, other) { | 210 | match (self, other) { |
177 | (ImportAction::Nothing, _) => true, | 211 | (ImportAction::Nothing, _) => true, |
178 | (ImportAction::AddInTreeList(..), ImportAction::Nothing) => false, | 212 | (ImportAction::AddInTreeList { .. }, ImportAction::Nothing) => false, |
179 | (ImportAction::AddNestedImport(n, ..), ImportAction::AddInTreeList(m, ..)) => n > m, | 213 | ( |
180 | (ImportAction::AddInTreeList(n, ..), ImportAction::AddNestedImport(m, ..)) => n > m, | 214 | ImportAction::AddNestedImport { |
181 | (ImportAction::AddInTreeList(..), _) => true, | 215 | common_segments: n, .. |
182 | (ImportAction::AddNestedImport(..), ImportAction::Nothing) => false, | 216 | }, |
183 | (ImportAction::AddNestedImport(..), _) => true, | 217 | ImportAction::AddInTreeList { |
184 | (ImportAction::AddNewUse(..), _) => false, | 218 | common_segments: m, .. |
219 | }, | ||
220 | ) => n > m, | ||
221 | ( | ||
222 | ImportAction::AddInTreeList { | ||
223 | common_segments: n, .. | ||
224 | }, | ||
225 | ImportAction::AddNestedImport { | ||
226 | common_segments: m, .. | ||
227 | }, | ||
228 | ) => n > m, | ||
229 | (ImportAction::AddInTreeList { .. }, _) => true, | ||
230 | (ImportAction::AddNestedImport { .. }, ImportAction::Nothing) => false, | ||
231 | (ImportAction::AddNestedImport { .. }, _) => true, | ||
232 | (ImportAction::AddNewUse { .. }, _) => false, | ||
185 | } | 233 | } |
186 | } | 234 | } |
187 | } | 235 | } |
@@ -205,7 +253,7 @@ fn walk_use_tree_for_best_action<'a>( | |||
205 | Some(path) => path, | 253 | Some(path) => path, |
206 | None => { | 254 | None => { |
207 | // If the use item don't have a path, it means it's broken (syntax error) | 255 | // If the use item don't have a path, it means it's broken (syntax error) |
208 | return ImportAction::AddNewUse( | 256 | return ImportAction::add_new_use( |
209 | current_use_tree | 257 | current_use_tree |
210 | .syntax() | 258 | .syntax() |
211 | .ancestors() | 259 | .ancestors() |
@@ -231,7 +279,7 @@ fn walk_use_tree_for_best_action<'a>( | |||
231 | let right = current_path_segments.split_at(prev_len).1; | 279 | let right = current_path_segments.split_at(prev_len).1; |
232 | let common = compare_path_segments(left, right); | 280 | let common = compare_path_segments(left, right); |
233 | let mut action = match common { | 281 | let mut action = match common { |
234 | 0 => ImportAction::AddNewUse( | 282 | 0 => ImportAction::add_new_use( |
235 | // e.g: target is std::fmt and we can have | 283 | // e.g: target is std::fmt and we can have |
236 | // use foo::bar | 284 | // use foo::bar |
237 | // We add a brand new use statement | 285 | // We add a brand new use statement |
@@ -259,7 +307,7 @@ fn walk_use_tree_for_best_action<'a>( | |||
259 | if has_self { | 307 | if has_self { |
260 | ImportAction::Nothing | 308 | ImportAction::Nothing |
261 | } else { | 309 | } else { |
262 | ImportAction::AddInTreeList(current_path_segments.len(), list, true) | 310 | ImportAction::add_in_tree_list(current_path_segments.len(), list, true) |
263 | } | 311 | } |
264 | } else { | 312 | } else { |
265 | // Case 1 | 313 | // Case 1 |
@@ -271,7 +319,7 @@ fn walk_use_tree_for_best_action<'a>( | |||
271 | // use std::io; | 319 | // use std::io; |
272 | // We need to split. | 320 | // We need to split. |
273 | let segments_to_split = current_path_segments.split_at(prev_len + common).1; | 321 | let segments_to_split = current_path_segments.split_at(prev_len + common).1; |
274 | ImportAction::AddNestedImport( | 322 | ImportAction::add_nested_import( |
275 | prev_len + common, | 323 | prev_len + common, |
276 | path, | 324 | path, |
277 | Some(segments_to_split[0]), | 325 | Some(segments_to_split[0]), |
@@ -284,7 +332,7 @@ fn walk_use_tree_for_best_action<'a>( | |||
284 | // 2- use std::{ ... }; | 332 | // 2- use std::{ ... }; |
285 | 333 | ||
286 | // fallback action | 334 | // fallback action |
287 | let mut better_action = ImportAction::AddNewUse( | 335 | let mut better_action = ImportAction::add_new_use( |
288 | current_use_tree | 336 | current_use_tree |
289 | .syntax() | 337 | .syntax() |
290 | .ancestors() | 338 | .ancestors() |
@@ -306,7 +354,7 @@ fn walk_use_tree_for_best_action<'a>( | |||
306 | } | 354 | } |
307 | } else { | 355 | } else { |
308 | // Case 1, split | 356 | // Case 1, split |
309 | better_action = ImportAction::AddNestedImport(prev_len + common, path, None, true) | 357 | better_action = ImportAction::add_nested_import(prev_len + common, path, None, true) |
310 | } | 358 | } |
311 | better_action | 359 | better_action |
312 | } | 360 | } |
@@ -314,7 +362,12 @@ fn walk_use_tree_for_best_action<'a>( | |||
314 | // e.g: target is std::fmt and we can have | 362 | // e.g: target is std::fmt and we can have |
315 | // use std::fmt::Debug; | 363 | // use std::fmt::Debug; |
316 | let segments_to_split = current_path_segments.split_at(prev_len + common).1; | 364 | let segments_to_split = current_path_segments.split_at(prev_len + common).1; |
317 | ImportAction::AddNestedImport(prev_len + common, path, Some(segments_to_split[0]), true) | 365 | ImportAction::add_nested_import( |
366 | prev_len + common, | ||
367 | path, | ||
368 | Some(segments_to_split[0]), | ||
369 | true, | ||
370 | ) | ||
318 | } | 371 | } |
319 | _ => unreachable!(), | 372 | _ => unreachable!(), |
320 | }; | 373 | }; |
@@ -322,8 +375,8 @@ fn walk_use_tree_for_best_action<'a>( | |||
322 | // If we are inside a UseTreeList adding a use statement become adding to the existing | 375 | // If we are inside a UseTreeList adding a use statement become adding to the existing |
323 | // tree list. | 376 | // tree list. |
324 | action = match (current_parent_use_tree_list, action) { | 377 | action = match (current_parent_use_tree_list, action) { |
325 | (Some(use_tree_list), ImportAction::AddNewUse(..)) => { | 378 | (Some(use_tree_list), ImportAction::AddNewUse { .. }) => { |
326 | ImportAction::AddInTreeList(prev_len, use_tree_list, false) | 379 | ImportAction::add_in_tree_list(prev_len, use_tree_list, false) |
327 | } | 380 | } |
328 | (_, _) => action, | 381 | (_, _) => action, |
329 | }; | 382 | }; |
@@ -361,26 +414,36 @@ fn best_action_for_target<'b, 'a: 'b>( | |||
361 | .map(AstNode::syntax) | 414 | .map(AstNode::syntax) |
362 | .or(Some(path.syntax())); | 415 | .or(Some(path.syntax())); |
363 | 416 | ||
364 | return ImportAction::AddNewUse(anchor, false); | 417 | return ImportAction::add_new_use(anchor, false); |
365 | } | 418 | } |
366 | } | 419 | } |
367 | } | 420 | } |
368 | 421 | ||
369 | fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut AssistBuilder) { | 422 | fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut AssistBuilder) { |
370 | match action { | 423 | match action { |
371 | ImportAction::AddNewUse(anchor, after) => { | 424 | ImportAction::AddNewUse { |
372 | make_assist_add_new_use(anchor, *after, target, edit) | 425 | anchor, |
373 | } | 426 | add_after_anchor, |
374 | ImportAction::AddInTreeList(n, tree_list_node, add_self) => { | 427 | } => make_assist_add_new_use(anchor, *add_after_anchor, target, edit), |
428 | ImportAction::AddInTreeList { | ||
429 | common_segments, | ||
430 | tree_list, | ||
431 | add_self, | ||
432 | } => { | ||
375 | // We know that the fist n segments already exists in the use statement we want | 433 | // We know that the fist n segments already exists in the use statement we want |
376 | // to modify, so we want to add only the last target.len() - n segments. | 434 | // to modify, so we want to add only the last target.len() - n segments. |
377 | let segments_to_add = target.split_at(*n).1; | 435 | let segments_to_add = target.split_at(*common_segments).1; |
378 | make_assist_add_in_tree_list(tree_list_node, segments_to_add, *add_self, edit) | 436 | make_assist_add_in_tree_list(tree_list, segments_to_add, *add_self, edit) |
379 | } | 437 | } |
380 | ImportAction::AddNestedImport(n, path, first_segment_to_split, add_self) => { | 438 | ImportAction::AddNestedImport { |
381 | let segments_to_add = target.split_at(*n).1; | 439 | common_segments, |
440 | path_to_split, | ||
441 | first_segment_to_split, | ||
442 | add_self, | ||
443 | } => { | ||
444 | let segments_to_add = target.split_at(*common_segments).1; | ||
382 | make_assist_add_nested_import( | 445 | make_assist_add_nested_import( |
383 | path, | 446 | path_to_split, |
384 | first_segment_to_split, | 447 | first_segment_to_split, |
385 | segments_to_add, | 448 | segments_to_add, |
386 | *add_self, | 449 | *add_self, |