aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/auto_import.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-07-19 09:24:41 +0100
committerAleksey Kladov <[email protected]>2019-07-19 11:16:25 +0100
commit0343c4a815a0e82d5e55e76a01d21b0f7a00ff5b (patch)
tree126bafdfcbcb04741b87876d6204c449113d96b5 /crates/ra_assists/src/auto_import.rs
parente2b28f5bb8043e92b10f6a40696131007fc9dfe2 (diff)
migrate ra_assists to the new AST
Diffstat (limited to 'crates/ra_assists/src/auto_import.rs')
-rw-r--r--crates/ra_assists/src/auto_import.rs143
1 files changed, 76 insertions, 67 deletions
diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs
index f8f37e852..0eb4bdb62 100644
--- a/crates/ra_assists/src/auto_import.rs
+++ b/crates/ra_assists/src/auto_import.rs
@@ -12,25 +12,25 @@ use ra_syntax::{
12 SyntaxNode, TextRange, T, 12 SyntaxNode, TextRange, T,
13}; 13};
14 14
15fn collect_path_segments_raw<'a>( 15fn collect_path_segments_raw(
16 segments: &mut Vec<&'a ast::PathSegment>, 16 segments: &mut Vec<ast::PathSegment>,
17 mut path: &'a ast::Path, 17 mut path: ast::Path,
18) -> Option<usize> { 18) -> Option<usize> {
19 let oldlen = segments.len(); 19 let oldlen = segments.len();
20 loop { 20 loop {
21 let mut children = path.syntax().children_with_tokens(); 21 let mut children = path.syntax().children_with_tokens();
22 let (first, second, third) = ( 22 let (first, second, third) = (
23 children.next().map(|n| (n, n.kind())), 23 children.next().map(|n| (n.clone(), n.kind())),
24 children.next().map(|n| (n, n.kind())), 24 children.next().map(|n| (n.clone(), n.kind())),
25 children.next().map(|n| (n, n.kind())), 25 children.next().map(|n| (n.clone(), n.kind())),
26 ); 26 );
27 match (first, second, third) { 27 match (first, second, third) {
28 (Some((subpath, PATH)), Some((_, T![::])), Some((segment, PATH_SEGMENT))) => { 28 (Some((subpath, PATH)), Some((_, T![::])), Some((segment, PATH_SEGMENT))) => {
29 path = ast::Path::cast(subpath.as_node()?)?; 29 path = ast::Path::cast(subpath.as_node()?.clone())?;
30 segments.push(ast::PathSegment::cast(segment.as_node()?)?); 30 segments.push(ast::PathSegment::cast(segment.as_node()?.clone())?);
31 } 31 }
32 (Some((segment, PATH_SEGMENT)), _, _) => { 32 (Some((segment, PATH_SEGMENT)), _, _) => {
33 segments.push(ast::PathSegment::cast(segment.as_node()?)?); 33 segments.push(ast::PathSegment::cast(segment.as_node()?.clone())?);
34 break; 34 break;
35 } 35 }
36 (_, _, _) => return None, 36 (_, _, _) => return None,
@@ -60,7 +60,7 @@ fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) {
60} 60}
61 61
62// Returns the numeber of common segments. 62// Returns the numeber of common segments.
63fn compare_path_segments(left: &[SmolStr], right: &[&ast::PathSegment]) -> usize { 63fn compare_path_segments(left: &[SmolStr], right: &[ast::PathSegment]) -> usize {
64 left.iter().zip(right).filter(|(l, r)| compare_path_segment(l, r)).count() 64 left.iter().zip(right).filter(|(l, r)| compare_path_segment(l, r)).count()
65} 65}
66 66
@@ -81,12 +81,12 @@ fn compare_path_segment_with_name(a: &SmolStr, b: &ast::Name) -> bool {
81 a == b.text() 81 a == b.text()
82} 82}
83 83
84#[derive(Copy, Clone)] 84#[derive(Clone)]
85enum ImportAction<'a> { 85enum ImportAction {
86 Nothing, 86 Nothing,
87 // Add a brand new use statement. 87 // Add a brand new use statement.
88 AddNewUse { 88 AddNewUse {
89 anchor: Option<&'a SyntaxNode>, // anchor node 89 anchor: Option<SyntaxNode>, // anchor node
90 add_after_anchor: bool, 90 add_after_anchor: bool,
91 }, 91 },
92 92
@@ -94,9 +94,9 @@ enum ImportAction<'a> {
94 AddNestedImport { 94 AddNestedImport {
95 // how may segments matched with the target path 95 // how may segments matched with the target path
96 common_segments: usize, 96 common_segments: usize,
97 path_to_split: &'a ast::Path, 97 path_to_split: ast::Path,
98 // the first segment of path_to_split we want to add into the new nested list 98 // the first segment of path_to_split we want to add into the new nested list
99 first_segment_to_split: Option<&'a ast::PathSegment>, 99 first_segment_to_split: Option<ast::PathSegment>,
100 // Wether to add 'self' in addition to the target path 100 // Wether to add 'self' in addition to the target path
101 add_self: bool, 101 add_self: bool,
102 }, 102 },
@@ -104,20 +104,20 @@ enum ImportAction<'a> {
104 AddInTreeList { 104 AddInTreeList {
105 common_segments: usize, 105 common_segments: usize,
106 // The UseTreeList where to add the target path 106 // The UseTreeList where to add the target path
107 tree_list: &'a ast::UseTreeList, 107 tree_list: ast::UseTreeList,
108 add_self: bool, 108 add_self: bool,
109 }, 109 },
110} 110}
111 111
112impl<'a> ImportAction<'a> { 112impl ImportAction {
113 fn add_new_use(anchor: Option<&'a SyntaxNode>, add_after_anchor: bool) -> Self { 113 fn add_new_use(anchor: Option<SyntaxNode>, add_after_anchor: bool) -> Self {
114 ImportAction::AddNewUse { anchor, add_after_anchor } 114 ImportAction::AddNewUse { anchor, add_after_anchor }
115 } 115 }
116 116
117 fn add_nested_import( 117 fn add_nested_import(
118 common_segments: usize, 118 common_segments: usize,
119 path_to_split: &'a ast::Path, 119 path_to_split: ast::Path,
120 first_segment_to_split: Option<&'a ast::PathSegment>, 120 first_segment_to_split: Option<ast::PathSegment>,
121 add_self: bool, 121 add_self: bool,
122 ) -> Self { 122 ) -> Self {
123 ImportAction::AddNestedImport { 123 ImportAction::AddNestedImport {
@@ -130,14 +130,14 @@ impl<'a> ImportAction<'a> {
130 130
131 fn add_in_tree_list( 131 fn add_in_tree_list(
132 common_segments: usize, 132 common_segments: usize,
133 tree_list: &'a ast::UseTreeList, 133 tree_list: ast::UseTreeList,
134 add_self: bool, 134 add_self: bool,
135 ) -> Self { 135 ) -> Self {
136 ImportAction::AddInTreeList { common_segments, tree_list, add_self } 136 ImportAction::AddInTreeList { common_segments, tree_list, add_self }
137 } 137 }
138 138
139 fn better<'b>(left: &'b ImportAction<'a>, right: &'b ImportAction<'a>) -> &'b ImportAction<'a> { 139 fn better(left: ImportAction, right: ImportAction) -> ImportAction {
140 if left.is_better(right) { 140 if left.is_better(&right) {
141 left 141 left
142 } else { 142 } else {
143 right 143 right
@@ -166,12 +166,12 @@ impl<'a> ImportAction<'a> {
166 166
167// Find out the best ImportAction to import target path against current_use_tree. 167// Find out the best ImportAction to import target path against current_use_tree.
168// If current_use_tree has a nested import the function gets called recursively on every UseTree inside a UseTreeList. 168// If current_use_tree has a nested import the function gets called recursively on every UseTree inside a UseTreeList.
169fn walk_use_tree_for_best_action<'a>( 169fn walk_use_tree_for_best_action(
170 current_path_segments: &mut Vec<&'a ast::PathSegment>, // buffer containing path segments 170 current_path_segments: &mut Vec<ast::PathSegment>, // buffer containing path segments
171 current_parent_use_tree_list: Option<&'a ast::UseTreeList>, // will be Some value if we are in a nested import 171 current_parent_use_tree_list: Option<ast::UseTreeList>, // will be Some value if we are in a nested import
172 current_use_tree: &'a ast::UseTree, // the use tree we are currently examinating 172 current_use_tree: ast::UseTree, // the use tree we are currently examinating
173 target: &[SmolStr], // the path we want to import 173 target: &[SmolStr], // the path we want to import
174) -> ImportAction<'a> { 174) -> ImportAction {
175 // We save the number of segments in the buffer so we can restore the correct segments 175 // We save the number of segments in the buffer so we can restore the correct segments
176 // before returning. Recursive call will add segments so we need to delete them. 176 // before returning. Recursive call will add segments so we need to delete them.
177 let prev_len = current_path_segments.len(); 177 let prev_len = current_path_segments.len();
@@ -188,32 +188,36 @@ fn walk_use_tree_for_best_action<'a>(
188 .syntax() 188 .syntax()
189 .ancestors() 189 .ancestors()
190 .find_map(ast::UseItem::cast) 190 .find_map(ast::UseItem::cast)
191 .map(AstNode::syntax), 191 .map(|it| it.syntax().clone()),
192 true, 192 true,
193 ); 193 );
194 } 194 }
195 }; 195 };
196 196
197 // This can happen only if current_use_tree is a direct child of a UseItem 197 // This can happen only if current_use_tree is a direct child of a UseItem
198 if let Some(name) = alias.and_then(ast::NameOwner::name) { 198 if let Some(name) = alias.and_then(|it| it.name()) {
199 if compare_path_segment_with_name(&target[0], name) { 199 if compare_path_segment_with_name(&target[0], &name) {
200 return ImportAction::Nothing; 200 return ImportAction::Nothing;
201 } 201 }
202 } 202 }
203 203
204 collect_path_segments_raw(current_path_segments, path); 204 collect_path_segments_raw(current_path_segments, path.clone());
205 205
206 // We compare only the new segments added in the line just above. 206 // We compare only the new segments added in the line just above.
207 // The first prev_len segments were already compared in 'parent' recursive calls. 207 // The first prev_len segments were already compared in 'parent' recursive calls.
208 let left = target.split_at(prev_len).1; 208 let left = target.split_at(prev_len).1;
209 let right = current_path_segments.split_at(prev_len).1; 209 let right = current_path_segments.split_at(prev_len).1;
210 let common = compare_path_segments(left, right); 210 let common = compare_path_segments(left, &right);
211 let mut action = match common { 211 let mut action = match common {
212 0 => ImportAction::add_new_use( 212 0 => ImportAction::add_new_use(
213 // e.g: target is std::fmt and we can have 213 // e.g: target is std::fmt and we can have
214 // use foo::bar 214 // use foo::bar
215 // We add a brand new use statement 215 // We add a brand new use statement
216 current_use_tree.syntax().ancestors().find_map(ast::UseItem::cast).map(AstNode::syntax), 216 current_use_tree
217 .syntax()
218 .ancestors()
219 .find_map(ast::UseItem::cast)
220 .map(|it| it.syntax().clone()),
217 true, 221 true,
218 ), 222 ),
219 common if common == left.len() && left.len() == right.len() => { 223 common if common == left.len() && left.len() == right.len() => {
@@ -223,9 +227,9 @@ fn walk_use_tree_for_best_action<'a>(
223 if let Some(list) = tree_list { 227 if let Some(list) = tree_list {
224 // In case 2 we need to add self to the nested list 228 // In case 2 we need to add self to the nested list
225 // unless it's already there 229 // unless it's already there
226 let has_self = list.use_trees().map(ast::UseTree::path).any(|p| { 230 let has_self = list.use_trees().map(|it| it.path()).any(|p| {
227 p.and_then(ast::Path::segment) 231 p.and_then(|it| it.segment())
228 .and_then(ast::PathSegment::kind) 232 .and_then(|it| it.kind())
229 .filter(|k| *k == ast::PathSegmentKind::SelfKw) 233 .filter(|k| *k == ast::PathSegmentKind::SelfKw)
230 .is_some() 234 .is_some()
231 }); 235 });
@@ -248,7 +252,7 @@ fn walk_use_tree_for_best_action<'a>(
248 ImportAction::add_nested_import( 252 ImportAction::add_nested_import(
249 prev_len + common, 253 prev_len + common,
250 path, 254 path,
251 Some(segments_to_split[0]), 255 Some(segments_to_split[0].clone()),
252 false, 256 false,
253 ) 257 )
254 } 258 }
@@ -263,14 +267,18 @@ fn walk_use_tree_for_best_action<'a>(
263 .syntax() 267 .syntax()
264 .ancestors() 268 .ancestors()
265 .find_map(ast::UseItem::cast) 269 .find_map(ast::UseItem::cast)
266 .map(AstNode::syntax), 270 .map(|it| it.syntax().clone()),
267 true, 271 true,
268 ); 272 );
269 if let Some(list) = tree_list { 273 if let Some(list) = tree_list {
270 // Case 2, check recursively if the path is already imported in the nested list 274 // Case 2, check recursively if the path is already imported in the nested list
271 for u in list.use_trees() { 275 for u in list.use_trees() {
272 let child_action = 276 let child_action = walk_use_tree_for_best_action(
273 walk_use_tree_for_best_action(current_path_segments, Some(list), u, target); 277 current_path_segments,
278 Some(list.clone()),
279 u,
280 target,
281 );
274 if child_action.is_better(&better_action) { 282 if child_action.is_better(&better_action) {
275 better_action = child_action; 283 better_action = child_action;
276 if let ImportAction::Nothing = better_action { 284 if let ImportAction::Nothing = better_action {
@@ -291,7 +299,7 @@ fn walk_use_tree_for_best_action<'a>(
291 ImportAction::add_nested_import( 299 ImportAction::add_nested_import(
292 prev_len + common, 300 prev_len + common,
293 path, 301 path,
294 Some(segments_to_split[0]), 302 Some(segments_to_split[0].clone()),
295 true, 303 true,
296 ) 304 )
297 } 305 }
@@ -302,7 +310,7 @@ fn walk_use_tree_for_best_action<'a>(
302 ImportAction::add_nested_import( 310 ImportAction::add_nested_import(
303 prev_len + common, 311 prev_len + common,
304 path, 312 path,
305 Some(segments_to_split[0]), 313 Some(segments_to_split[0].clone()),
306 false, 314 false,
307 ) 315 )
308 } 316 }
@@ -311,7 +319,7 @@ fn walk_use_tree_for_best_action<'a>(
311 319
312 // If we are inside a UseTreeList adding a use statement become adding to the existing 320 // If we are inside a UseTreeList adding a use statement become adding to the existing
313 // tree list. 321 // tree list.
314 action = match (current_parent_use_tree_list, action) { 322 action = match (current_parent_use_tree_list, action.clone()) {
315 (Some(use_tree_list), ImportAction::AddNewUse { .. }) => { 323 (Some(use_tree_list), ImportAction::AddNewUse { .. }) => {
316 ImportAction::add_in_tree_list(prev_len, use_tree_list, false) 324 ImportAction::add_in_tree_list(prev_len, use_tree_list, false)
317 } 325 }
@@ -323,19 +331,20 @@ fn walk_use_tree_for_best_action<'a>(
323 action 331 action
324} 332}
325 333
326fn best_action_for_target<'b, 'a: 'b>( 334fn best_action_for_target(
327 container: &'a SyntaxNode, 335 container: SyntaxNode,
328 anchor: &'a SyntaxNode, 336 anchor: SyntaxNode,
329 target: &'b [SmolStr], 337 target: &[SmolStr],
330) -> ImportAction<'a> { 338) -> ImportAction {
331 let mut storage = Vec::with_capacity(16); // this should be the only allocation 339 let mut storage = Vec::with_capacity(16); // this should be the only allocation
332 let best_action = container 340 let best_action = container
333 .children() 341 .children()
334 .filter_map(ast::UseItem::cast) 342 .filter_map(ast::UseItem::cast)
335 .filter_map(ast::UseItem::use_tree) 343 .filter_map(|it| it.use_tree())
336 .map(|u| walk_use_tree_for_best_action(&mut storage, None, u, target)) 344 .map(|u| walk_use_tree_for_best_action(&mut storage, None, u, target))
337 .fold(None, |best, a| { 345 .fold(None, |best, a| match best {
338 best.and_then(|best| Some(*ImportAction::better(&best, &a))).or_else(|| Some(a)) 346 Some(best) => Some(ImportAction::better(best, a)),
347 None => Some(a),
339 }); 348 });
340 349
341 match best_action { 350 match best_action {
@@ -386,7 +395,7 @@ fn make_assist(action: &ImportAction, target: &[SmolStr], edit: &mut TextEditBui
386} 395}
387 396
388fn make_assist_add_new_use( 397fn make_assist_add_new_use(
389 anchor: &Option<&SyntaxNode>, 398 anchor: &Option<SyntaxNode>,
390 after: bool, 399 after: bool,
391 target: &[SmolStr], 400 target: &[SmolStr],
392 edit: &mut TextEditBuilder, 401 edit: &mut TextEditBuilder,
@@ -396,7 +405,7 @@ fn make_assist_add_new_use(
396 let mut buf = String::new(); 405 let mut buf = String::new();
397 if after { 406 if after {
398 buf.push_str("\n"); 407 buf.push_str("\n");
399 if let Some(spaces) = indent { 408 if let Some(spaces) = &indent {
400 buf.push_str(spaces); 409 buf.push_str(spaces);
401 } 410 }
402 } 411 }
@@ -405,8 +414,8 @@ fn make_assist_add_new_use(
405 buf.push_str(";"); 414 buf.push_str(";");
406 if !after { 415 if !after {
407 buf.push_str("\n\n"); 416 buf.push_str("\n\n");
408 if let Some(spaces) = indent { 417 if let Some(spaces) = &indent {
409 buf.push_str(spaces); 418 buf.push_str(&spaces);
410 } 419 }
411 } 420 }
412 let position = if after { anchor.range().end() } else { anchor.range().start() }; 421 let position = if after { anchor.range().end() } else { anchor.range().start() };
@@ -444,7 +453,7 @@ fn make_assist_add_in_tree_list(
444 453
445fn make_assist_add_nested_import( 454fn make_assist_add_nested_import(
446 path: &ast::Path, 455 path: &ast::Path,
447 first_segment_to_split: &Option<&ast::PathSegment>, 456 first_segment_to_split: &Option<ast::PathSegment>,
448 target: &[SmolStr], 457 target: &[SmolStr],
449 add_self: bool, 458 add_self: bool,
450 edit: &mut TextEditBuilder, 459 edit: &mut TextEditBuilder,
@@ -482,7 +491,7 @@ fn apply_auto_import(
482 target: &[SmolStr], 491 target: &[SmolStr],
483 edit: &mut TextEditBuilder, 492 edit: &mut TextEditBuilder,
484) { 493) {
485 let action = best_action_for_target(container, path.syntax(), target); 494 let action = best_action_for_target(container.clone(), path.syntax().clone(), target);
486 make_assist(&action, target, edit); 495 make_assist(&action, target, edit);
487 if let Some(last) = path.segment() { 496 if let Some(last) = path.segment() {
488 // Here we are assuming the assist will provide a correct use statement 497 // Here we are assuming the assist will provide a correct use statement
@@ -522,26 +531,26 @@ pub fn auto_import_text_edit(
522 edit: &mut TextEditBuilder, 531 edit: &mut TextEditBuilder,
523) { 532) {
524 let container = position.ancestors().find_map(|n| { 533 let container = position.ancestors().find_map(|n| {
525 if let Some(module) = ast::Module::cast(n) { 534 if let Some(module) = ast::Module::cast(n.clone()) {
526 return module.item_list().map(ast::AstNode::syntax); 535 return module.item_list().map(|it| it.syntax().clone());
527 } 536 }
528 ast::SourceFile::cast(n).map(ast::AstNode::syntax) 537 ast::SourceFile::cast(n).map(|it| it.syntax().clone())
529 }); 538 });
530 539
531 if let Some(container) = container { 540 if let Some(container) = container {
532 let action = best_action_for_target(container, anchor, target); 541 let action = best_action_for_target(container, anchor.clone(), target);
533 make_assist(&action, target, edit); 542 make_assist(&action, target, edit);
534 } 543 }
535} 544}
536 545
537pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 546pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
538 let path: &ast::Path = ctx.node_at_offset()?; 547 let path: ast::Path = ctx.node_at_offset()?;
539 // We don't want to mess with use statements 548 // We don't want to mess with use statements
540 if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() { 549 if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
541 return None; 550 return None;
542 } 551 }
543 552
544 let hir_path = hir::Path::from_ast(path)?; 553 let hir_path = hir::Path::from_ast(path.clone())?;
545 let segments = collect_hir_path_segments(&hir_path); 554 let segments = collect_hir_path_segments(&hir_path);
546 if segments.len() < 2 { 555 if segments.len() < 2 {
547 return None; 556 return None;
@@ -554,7 +563,7 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
554 format!("import {} in mod {}", fmt_segments(&segments), name.text()), 563 format!("import {} in mod {}", fmt_segments(&segments), name.text()),
555 |edit| { 564 |edit| {
556 let mut text_edit = TextEditBuilder::default(); 565 let mut text_edit = TextEditBuilder::default();
557 apply_auto_import(item_list.syntax(), path, &segments, &mut text_edit); 566 apply_auto_import(item_list.syntax(), &path, &segments, &mut text_edit);
558 edit.set_edit_builder(text_edit); 567 edit.set_edit_builder(text_edit);
559 }, 568 },
560 ); 569 );
@@ -566,7 +575,7 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
566 format!("import {} in the current file", fmt_segments(&segments)), 575 format!("import {} in the current file", fmt_segments(&segments)),
567 |edit| { 576 |edit| {
568 let mut text_edit = TextEditBuilder::default(); 577 let mut text_edit = TextEditBuilder::default();
569 apply_auto_import(current_file.syntax(), path, &segments, &mut text_edit); 578 apply_auto_import(current_file.syntax(), &path, &segments, &mut text_edit);
570 edit.set_edit_builder(text_edit); 579 edit.set_edit_builder(text_edit);
571 }, 580 },
572 ); 581 );