aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--crates/ra_assists/Cargo.toml1
-rw-r--r--crates/ra_assists/src/auto_import.rs102
3 files changed, 37 insertions, 67 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 40621e891..9c65b39f7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -900,7 +900,6 @@ version = "0.1.0"
900name = "ra_assists" 900name = "ra_assists"
901version = "0.1.0" 901version = "0.1.0"
902dependencies = [ 902dependencies = [
903 "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
904 "join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 903 "join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
905 "ra_db 0.1.0", 904 "ra_db 0.1.0",
906 "ra_fmt 0.1.0", 905 "ra_fmt 0.1.0",
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml
index a5808d5b7..71ab2dcd2 100644
--- a/crates/ra_assists/Cargo.toml
+++ b/crates/ra_assists/Cargo.toml
@@ -6,7 +6,6 @@ authors = ["Aleksey Kladov <[email protected]>"]
6 6
7[dependencies] 7[dependencies]
8join_to_string = "0.1.3" 8join_to_string = "0.1.3"
9itertools = "0.8.0"
10 9
11ra_syntax = { path = "../ra_syntax" } 10ra_syntax = { path = "../ra_syntax" }
12ra_text_edit = { path = "../ra_text_edit" } 11ra_text_edit = { path = "../ra_text_edit" }
diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs
index 2ac19ab27..14a564301 100644
--- a/crates/ra_assists/src/auto_import.rs
+++ b/crates/ra_assists/src/auto_import.rs
@@ -4,7 +4,6 @@ use ra_syntax::{
4 SyntaxKind::{ PATH, PATH_SEGMENT, COLONCOLON, COMMA } 4 SyntaxKind::{ PATH, PATH_SEGMENT, COLONCOLON, COMMA }
5}; 5};
6use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder}; 6use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder};
7use itertools::{ Itertools, EitherOrBoth };
8 7
9// TODO: refactor this before merge 8// TODO: refactor this before merge
10mod formatting { 9mod formatting {
@@ -101,44 +100,13 @@ fn fmt_segments_raw(segments: &[&ast::PathSegment], buf: &mut String) {
101 } 100 }
102} 101}
103 102
104#[derive(Copy, Clone)] 103// Returns the numeber of common segments.
105enum PathSegmentsMatch { 104fn compare_path_segments(left: &[&ast::PathSegment], right: &[&ast::PathSegment]) -> usize {
106 // Patch matches exactly 105 return left
107 Full, 106 .iter()
108 // When some of the segments matched 107 .zip(right)
109 Partial(usize), 108 .filter(|(l, r)| compare_path_segment(l, r))
110 // When all the segments of the right path are matched against the left path, 109 .count();
111 // but the left path is longer.
112 PartialLeft(usize),
113 // When all the segments of the left path are matched against the right path,
114 // but the right path is longer.
115 PartialRight(usize),
116 // In all the three cases above we keep track of how many segments matched
117}
118
119fn compare_path_segments(
120 left: &[&ast::PathSegment],
121 right: &[&ast::PathSegment],
122) -> PathSegmentsMatch {
123 let mut matching = 0;
124 for either_or_both in left.iter().zip_longest(right.iter()) {
125 match either_or_both {
126 EitherOrBoth::Both(left, right) => {
127 if compare_path_segment(left, right) {
128 matching += 1
129 } else {
130 return PathSegmentsMatch::Partial(matching);
131 }
132 }
133 EitherOrBoth::Left(_) => {
134 return PathSegmentsMatch::PartialLeft(matching);
135 }
136 EitherOrBoth::Right(_) => {
137 return PathSegmentsMatch::PartialRight(matching);
138 }
139 }
140 }
141 return PathSegmentsMatch::Full;
142} 110}
143 111
144fn compare_path_segment(a: &ast::PathSegment, b: &ast::PathSegment) -> bool { 112fn compare_path_segment(a: &ast::PathSegment, b: &ast::PathSegment) -> bool {
@@ -259,13 +227,22 @@ fn walk_use_tree_for_best_action<'a>(
259 227
260 // We compare only the new segments added in the line just above. 228 // We compare only the new segments added in the line just above.
261 // The first prev_len segments were already compared in 'parent' recursive calls. 229 // The first prev_len segments were already compared in 'parent' recursive calls.
262 let c = compare_path_segments( 230 let left = target.split_at(prev_len).1;
263 target.split_at(prev_len).1, 231 let right = current_path_segments.split_at(prev_len).1;
264 current_path_segments.split_at(prev_len).1, 232 let common = compare_path_segments(left, right);
265 ); 233 let mut action = match common {
266 234 0 => ImportAction::AddNewUse(
267 let mut action = match c { 235 // e.g: target is std::fmt and we can have
268 PathSegmentsMatch::Full => { 236 // use foo::bar
237 // We add a brand new use statement
238 current_use_tree
239 .syntax()
240 .ancestors()
241 .find_map(ast::UseItem::cast)
242 .map(AstNode::syntax),
243 true,
244 ),
245 common if common == left.len() && left.len() == right.len() => {
269 // e.g: target is std::fmt and we can have 246 // e.g: target is std::fmt and we can have
270 // 1- use std::fmt; 247 // 1- use std::fmt;
271 // 2- use std::fmt:{ ... } 248 // 2- use std::fmt:{ ... }
@@ -289,25 +266,19 @@ fn walk_use_tree_for_best_action<'a>(
289 ImportAction::Nothing 266 ImportAction::Nothing
290 } 267 }
291 } 268 }
292 PathSegmentsMatch::Partial(0) => ImportAction::AddNewUse( 269 common if common != left.len() && left.len() == right.len() => {
293 // e.g: target is std::fmt and we can have
294 // use foo::bar
295 // We add a brand new use statement
296 current_use_tree
297 .syntax()
298 .ancestors()
299 .find_map(ast::UseItem::cast)
300 .map(AstNode::syntax),
301 true,
302 ),
303 PathSegmentsMatch::Partial(n) => {
304 // e.g: target is std::fmt and we have 270 // e.g: target is std::fmt and we have
305 // use std::io; 271 // use std::io;
306 // We need to split. 272 // We need to split.
307 let segments_to_split = current_path_segments.split_at(prev_len + n).1; 273 let segments_to_split = current_path_segments.split_at(prev_len + common).1;
308 ImportAction::AddNestedImport(prev_len + n, path, Some(segments_to_split[0]), false) 274 ImportAction::AddNestedImport(
275 prev_len + common,
276 path,
277 Some(segments_to_split[0]),
278 false,
279 )
309 } 280 }
310 PathSegmentsMatch::PartialLeft(n) => { 281 common if left.len() > right.len() => {
311 // e.g: target is std::fmt and we can have 282 // e.g: target is std::fmt and we can have
312 // 1- use std; 283 // 1- use std;
313 // 2- use std::{ ... }; 284 // 2- use std::{ ... };
@@ -335,16 +306,17 @@ fn walk_use_tree_for_best_action<'a>(
335 } 306 }
336 } else { 307 } else {
337 // Case 1, split 308 // Case 1, split
338 better_action = ImportAction::AddNestedImport(prev_len + n, path, None, true) 309 better_action = ImportAction::AddNestedImport(prev_len + common, path, None, true)
339 } 310 }
340 better_action 311 better_action
341 } 312 }
342 PathSegmentsMatch::PartialRight(n) => { 313 common if left.len() < right.len() => {
343 // e.g: target is std::fmt and we can have 314 // e.g: target is std::fmt and we can have
344 // use std::fmt::Debug; 315 // use std::fmt::Debug;
345 let segments_to_split = current_path_segments.split_at(prev_len + n).1; 316 let segments_to_split = current_path_segments.split_at(prev_len + common).1;
346 ImportAction::AddNestedImport(prev_len + n, path, Some(segments_to_split[0]), true) 317 ImportAction::AddNestedImport(prev_len + common, path, Some(segments_to_split[0]), true)
347 } 318 }
319 _ => unreachable!(),
348 }; 320 };
349 321
350 // If we are inside a UseTreeList adding a use statement become adding to the existing 322 // If we are inside a UseTreeList adding a use statement become adding to the existing