aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/auto_import.rs151
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 {
140enum ImportAction<'a> { 140enum 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
166impl<'a> ImportAction<'a> { 167impl<'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
369fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut AssistBuilder) { 422fn 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,