diff options
Diffstat (limited to 'crates/ra_assists')
-rw-r--r-- | crates/ra_assists/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_assists/src/auto_import.rs | 102 |
2 files changed, 37 insertions, 66 deletions
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] |
8 | join_to_string = "0.1.3" | 8 | join_to_string = "0.1.3" |
9 | itertools = "0.8.0" | ||
10 | 9 | ||
11 | ra_syntax = { path = "../ra_syntax" } | 10 | ra_syntax = { path = "../ra_syntax" } |
12 | ra_text_edit = { path = "../ra_text_edit" } | 11 | ra_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 | }; |
6 | use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder}; | 6 | use crate::assist_ctx::{AssistCtx, Assist, AssistBuilder}; |
7 | use itertools::{ Itertools, EitherOrBoth }; | ||
8 | 7 | ||
9 | // TODO: refactor this before merge | 8 | // TODO: refactor this before merge |
10 | mod formatting { | 9 | mod 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. |
105 | enum PathSegmentsMatch { | 104 | fn 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 | |||
119 | fn 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 | ||
144 | fn compare_path_segment(a: &ast::PathSegment, b: &ast::PathSegment) -> bool { | 112 | fn 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 |