aboutsummaryrefslogtreecommitdiff
path: root/crates/assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists')
-rw-r--r--crates/assists/src/ast_transform.rs74
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs2
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs2
-rw-r--r--crates/assists/src/utils/insert_use.rs204
4 files changed, 172 insertions, 110 deletions
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs
index 835da3bb2..4307e0191 100644
--- a/crates/assists/src/ast_transform.rs
+++ b/crates/assists/src/ast_transform.rs
@@ -5,12 +5,13 @@ use hir::{HirDisplay, PathResolution, SemanticsScope};
5use syntax::{ 5use syntax::{
6 algo::SyntaxRewriter, 6 algo::SyntaxRewriter,
7 ast::{self, AstNode}, 7 ast::{self, AstNode},
8 SyntaxNode,
8}; 9};
9 10
10pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { 11pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
11 SyntaxRewriter::from_fn(|element| match element { 12 SyntaxRewriter::from_fn(|element| match element {
12 syntax::SyntaxElement::Node(n) => { 13 syntax::SyntaxElement::Node(n) => {
13 let replacement = transformer.get_substitution(&n)?; 14 let replacement = transformer.get_substitution(&n, transformer)?;
14 Some(replacement.into()) 15 Some(replacement.into())
15 } 16 }
16 _ => None, 17 _ => None,
@@ -47,32 +48,35 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
47/// We'd want to somehow express this concept simpler, but so far nobody got to 48/// We'd want to somehow express this concept simpler, but so far nobody got to
48/// simplifying this! 49/// simplifying this!
49pub trait AstTransform<'a> { 50pub trait AstTransform<'a> {
50 fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode>; 51 fn get_substitution(
52 &self,
53 node: &SyntaxNode,
54 recur: &dyn AstTransform<'a>,
55 ) -> Option<SyntaxNode>;
51 56
52 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>;
53 fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a> 57 fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a>
54 where 58 where
55 Self: Sized + 'a, 59 Self: Sized + 'a,
56 { 60 {
57 self.chain_before(Box::new(other)) 61 Box::new(Or(Box::new(self), Box::new(other)))
58 } 62 }
59} 63}
60 64
61struct NullTransformer; 65struct Or<'a>(Box<dyn AstTransform<'a> + 'a>, Box<dyn AstTransform<'a> + 'a>);
62 66
63impl<'a> AstTransform<'a> for NullTransformer { 67impl<'a> AstTransform<'a> for Or<'a> {
64 fn get_substitution(&self, _node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { 68 fn get_substitution(
65 None 69 &self,
66 } 70 node: &SyntaxNode,
67 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { 71 recur: &dyn AstTransform<'a>,
68 other 72 ) -> Option<SyntaxNode> {
73 self.0.get_substitution(node, recur).or_else(|| self.1.get_substitution(node, recur))
69 } 74 }
70} 75}
71 76
72pub struct SubstituteTypeParams<'a> { 77pub struct SubstituteTypeParams<'a> {
73 source_scope: &'a SemanticsScope<'a>, 78 source_scope: &'a SemanticsScope<'a>,
74 substs: FxHashMap<hir::TypeParam, ast::Type>, 79 substs: FxHashMap<hir::TypeParam, ast::Type>,
75 previous: Box<dyn AstTransform<'a> + 'a>,
76} 80}
77 81
78impl<'a> SubstituteTypeParams<'a> { 82impl<'a> SubstituteTypeParams<'a> {
@@ -111,11 +115,7 @@ impl<'a> SubstituteTypeParams<'a> {
111 } 115 }
112 }) 116 })
113 .collect(); 117 .collect();
114 return SubstituteTypeParams { 118 return SubstituteTypeParams { source_scope, substs: substs_by_param };
115 source_scope,
116 substs: substs_by_param,
117 previous: Box::new(NullTransformer),
118 };
119 119
120 // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the 120 // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
121 // trait ref, and then go from the types in the substs back to the syntax). 121 // trait ref, and then go from the types in the substs back to the syntax).
@@ -140,7 +140,14 @@ impl<'a> SubstituteTypeParams<'a> {
140 Some(result) 140 Some(result)
141 } 141 }
142 } 142 }
143 fn get_substitution_inner(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { 143}
144
145impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
146 fn get_substitution(
147 &self,
148 node: &SyntaxNode,
149 _recur: &dyn AstTransform<'a>,
150 ) -> Option<SyntaxNode> {
144 let type_ref = ast::Type::cast(node.clone())?; 151 let type_ref = ast::Type::cast(node.clone())?;
145 let path = match &type_ref { 152 let path = match &type_ref {
146 ast::Type::PathType(path_type) => path_type.path()?, 153 ast::Type::PathType(path_type) => path_type.path()?,
@@ -154,27 +161,23 @@ impl<'a> SubstituteTypeParams<'a> {
154 } 161 }
155} 162}
156 163
157impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
158 fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> {
159 self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
160 }
161 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
162 Box::new(SubstituteTypeParams { previous: other, ..self })
163 }
164}
165
166pub struct QualifyPaths<'a> { 164pub struct QualifyPaths<'a> {
167 target_scope: &'a SemanticsScope<'a>, 165 target_scope: &'a SemanticsScope<'a>,
168 source_scope: &'a SemanticsScope<'a>, 166 source_scope: &'a SemanticsScope<'a>,
169 previous: Box<dyn AstTransform<'a> + 'a>,
170} 167}
171 168
172impl<'a> QualifyPaths<'a> { 169impl<'a> QualifyPaths<'a> {
173 pub fn new(target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>) -> Self { 170 pub fn new(target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>) -> Self {
174 Self { target_scope, source_scope, previous: Box::new(NullTransformer) } 171 Self { target_scope, source_scope }
175 } 172 }
173}
176 174
177 fn get_substitution_inner(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { 175impl<'a> AstTransform<'a> for QualifyPaths<'a> {
176 fn get_substitution(
177 &self,
178 node: &SyntaxNode,
179 recur: &dyn AstTransform<'a>,
180 ) -> Option<SyntaxNode> {
178 // FIXME handle value ns? 181 // FIXME handle value ns?
179 let from = self.target_scope.module()?; 182 let from = self.target_scope.module()?;
180 let p = ast::Path::cast(node.clone())?; 183 let p = ast::Path::cast(node.clone())?;
@@ -191,7 +194,7 @@ impl<'a> QualifyPaths<'a> {
191 let type_args = p 194 let type_args = p
192 .segment() 195 .segment()
193 .and_then(|s| s.generic_arg_list()) 196 .and_then(|s| s.generic_arg_list())
194 .map(|arg_list| apply(self, arg_list)); 197 .map(|arg_list| apply(recur, arg_list));
195 if let Some(type_args) = type_args { 198 if let Some(type_args) = type_args {
196 let last_segment = path.segment().unwrap(); 199 let last_segment = path.segment().unwrap();
197 path = path.with_segment(last_segment.with_generic_args(type_args)) 200 path = path.with_segment(last_segment.with_generic_args(type_args))
@@ -208,15 +211,6 @@ impl<'a> QualifyPaths<'a> {
208 } 211 }
209} 212}
210 213
211impl<'a> AstTransform<'a> for QualifyPaths<'a> {
212 fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> {
213 self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node))
214 }
215 fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> {
216 Box::new(QualifyPaths { previous: other, ..self })
217 }
218}
219
220pub(crate) fn path_to_ast(path: hir::ModPath) -> ast::Path { 214pub(crate) fn path_to_ast(path: hir::ModPath) -> ast::Path {
221 let parse = ast::SourceFile::parse(&path.to_string()); 215 let parse = ast::SourceFile::parse(&path.to_string());
222 parse 216 parse
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs
index 1ac5fefd6..51b5a2eb0 100644
--- a/crates/assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/assists/src/handlers/add_missing_impl_members.rs
@@ -146,7 +146,7 @@ fn add_missing_impl_members_inner(
146 146
147 let target = impl_def.syntax().text_range(); 147 let target = impl_def.syntax().text_range();
148 acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { 148 acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
149 let impl_item_list = impl_def.assoc_item_list().unwrap_or(make::assoc_item_list()); 149 let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list);
150 150
151 let n_existing_items = impl_item_list.assoc_items().count(); 151 let n_existing_items = impl_item_list.assoc_items().count();
152 let source_scope = ctx.sema.scope_for_def(trait_); 152 let source_scope = ctx.sema.scope_for_def(trait_);
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index d1eadaa99..f5f03ef36 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -91,7 +91,7 @@ fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVari
91 .module(db) 91 .module(db)
92 .scope(db, None) 92 .scope(db, None)
93 .into_iter() 93 .into_iter()
94 .any(|(name, _)| name.to_string() == variant_name.to_string()) 94 .any(|(name, _)| name.to_string() == variant_name)
95} 95}
96 96
97#[allow(dead_code)] 97#[allow(dead_code)]
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index 5719b06af..f6025c99a 100644
--- a/crates/assists/src/utils/insert_use.rs
+++ b/crates/assists/src/utils/insert_use.rs
@@ -4,13 +4,14 @@ use std::{
4 iter::{self, successors}, 4 iter::{self, successors},
5}; 5};
6 6
7use ast::{ 7use itertools::{EitherOrBoth, Itertools};
8 edit::{AstNodeEdit, IndentLevel},
9 PathSegmentKind, VisibilityOwner,
10};
11use syntax::{ 8use syntax::{
12 algo, 9 algo,
13 ast::{self, make, AstNode}, 10 ast::{
11 self,
12 edit::{AstNodeEdit, IndentLevel},
13 make, AstNode, PathSegmentKind, VisibilityOwner,
14 },
14 InsertPosition, SyntaxElement, SyntaxNode, 15 InsertPosition, SyntaxElement, SyntaxNode,
15}; 16};
16 17
@@ -174,7 +175,7 @@ pub(crate) fn try_merge_trees(
174 let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?; 175 let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?;
175 let lhs = lhs.split_prefix(&lhs_prefix); 176 let lhs = lhs.split_prefix(&lhs_prefix);
176 let rhs = rhs.split_prefix(&rhs_prefix); 177 let rhs = rhs.split_prefix(&rhs_prefix);
177 recursive_merge(&lhs, &rhs, merge).map(|(merged, _)| merged) 178 recursive_merge(&lhs, &rhs, merge)
178} 179}
179 180
180/// Recursively "zips" together lhs and rhs. 181/// Recursively "zips" together lhs and rhs.
@@ -182,22 +183,24 @@ fn recursive_merge(
182 lhs: &ast::UseTree, 183 lhs: &ast::UseTree,
183 rhs: &ast::UseTree, 184 rhs: &ast::UseTree,
184 merge: MergeBehaviour, 185 merge: MergeBehaviour,
185) -> Option<(ast::UseTree, bool)> { 186) -> Option<ast::UseTree> {
186 let mut use_trees = lhs 187 let mut use_trees = lhs
187 .use_tree_list() 188 .use_tree_list()
188 .into_iter() 189 .into_iter()
189 .flat_map(|list| list.use_trees()) 190 .flat_map(|list| list.use_trees())
190 // check if any of the use trees are nested, if they are and the behaviour is `last` we are not allowed to merge this 191 // we use Option here to early return from this function(this is not the same as a `filter` op)
191 // so early exit the iterator by using Option's Intoiterator impl 192 .map(|tree| match merge.is_tree_allowed(&tree) {
192 .map(|tree| match merge == MergeBehaviour::Last && tree.use_tree_list().is_some() { 193 true => Some(tree),
193 true => None, 194 false => None,
194 false => Some(tree),
195 }) 195 })
196 .collect::<Option<Vec<_>>>()?; 196 .collect::<Option<Vec<_>>>()?;
197 use_trees.sort_unstable_by(|a, b| path_cmp_opt(a.path(), b.path())); 197 use_trees.sort_unstable_by(|a, b| path_cmp_for_sort(a.path(), b.path()));
198 for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) { 198 for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) {
199 if !merge.is_tree_allowed(&rhs_t) {
200 return None;
201 }
199 let rhs_path = rhs_t.path(); 202 let rhs_path = rhs_t.path();
200 match use_trees.binary_search_by(|p| path_cmp_opt(p.path(), rhs_path.clone())) { 203 match use_trees.binary_search_by(|p| path_cmp_bin_search(p.path(), rhs_path.clone())) {
201 Ok(idx) => { 204 Ok(idx) => {
202 let lhs_t = &mut use_trees[idx]; 205 let lhs_t = &mut use_trees[idx];
203 let lhs_path = lhs_t.path()?; 206 let lhs_path = lhs_t.path()?;
@@ -239,17 +242,9 @@ fn recursive_merge(
239 } 242 }
240 let lhs = lhs_t.split_prefix(&lhs_prefix); 243 let lhs = lhs_t.split_prefix(&lhs_prefix);
241 let rhs = rhs_t.split_prefix(&rhs_prefix); 244 let rhs = rhs_t.split_prefix(&rhs_prefix);
242 let this_has_children = use_trees.len() > 0;
243 match recursive_merge(&lhs, &rhs, merge) { 245 match recursive_merge(&lhs, &rhs, merge) {
244 Some((_, has_multiple_children)) 246 Some(use_tree) => use_trees[idx] = use_tree,
245 if merge == MergeBehaviour::Last 247 None => return None,
246 && this_has_children
247 && has_multiple_children =>
248 {
249 return None
250 }
251 Some((use_tree, _)) => use_trees[idx] = use_tree,
252 None => use_trees.insert(idx, rhs_t),
253 } 248 }
254 } 249 }
255 Err(_) 250 Err(_)
@@ -264,8 +259,7 @@ fn recursive_merge(
264 } 259 }
265 } 260 }
266 } 261 }
267 let has_multiple_children = use_trees.len() > 1; 262 Some(lhs.with_use_tree_list(make::use_tree_list(use_trees)))
268 Some((lhs.with_use_tree_list(make::use_tree_list(use_trees)), has_multiple_children))
269} 263}
270 264
271/// Traverses both paths until they differ, returning the common prefix of both. 265/// Traverses both paths until they differ, returning the common prefix of both.
@@ -308,41 +302,83 @@ fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Cl
308 successors(first_segment(path), |p| p.parent_path().parent_path().and_then(|p| p.segment())) 302 successors(first_segment(path), |p| p.parent_path().parent_path().and_then(|p| p.segment()))
309} 303}
310 304
305fn path_len(path: ast::Path) -> usize {
306 segment_iter(&path).count()
307}
308
311/// Orders paths in the following way: 309/// Orders paths in the following way:
312/// the sole self token comes first, after that come uppercase identifiers, then lowercase identifiers 310/// the sole self token comes first, after that come uppercase identifiers, then lowercase identifiers
313// FIXME: rustfmt sort lowercase idents before uppercase, in general we want to have the same ordering rustfmt has 311// FIXME: rustfmt sorts lowercase idents before uppercase, in general we want to have the same ordering rustfmt has
314// which is `self` and `super` first, then identifier imports with lowercase ones first, then glob imports and at last list imports. 312// which is `self` and `super` first, then identifier imports with lowercase ones first, then glob imports and at last list imports.
315// Example foo::{self, foo, baz, Baz, Qux, *, {Bar}} 313// Example foo::{self, foo, baz, Baz, Qux, *, {Bar}}
316fn path_cmp(a: &ast::Path, b: &ast::Path) -> Ordering { 314fn path_cmp_for_sort(a: Option<ast::Path>, b: Option<ast::Path>) -> Ordering {
317 match (path_is_self(a), path_is_self(b)) { 315 match (a, b) {
318 (true, true) => Ordering::Equal, 316 (None, None) => Ordering::Equal,
319 (true, false) => Ordering::Less, 317 (None, Some(_)) => Ordering::Less,
320 (false, true) => Ordering::Greater, 318 (Some(_), None) => Ordering::Greater,
321 (false, false) => { 319 (Some(ref a), Some(ref b)) => match (path_is_self(a), path_is_self(b)) {
322 let a = segment_iter(a); 320 (true, true) => Ordering::Equal,
323 let b = segment_iter(b); 321 (true, false) => Ordering::Less,
324 // cmp_by would be useful for us here but that is currently unstable 322 (false, true) => Ordering::Greater,
325 // cmp doesnt work due the lifetimes on text's return type 323 (false, false) => path_cmp_short(a, b),
326 a.zip(b) 324 },
327 .flat_map(|(seg, seg2)| seg.name_ref().zip(seg2.name_ref()))
328 .find_map(|(a, b)| match a.text().cmp(b.text()) {
329 ord @ Ordering::Greater | ord @ Ordering::Less => Some(ord),
330 Ordering::Equal => None,
331 })
332 .unwrap_or(Ordering::Equal)
333 }
334 } 325 }
335} 326}
336 327
337fn path_cmp_opt(a: Option<ast::Path>, b: Option<ast::Path>) -> Ordering { 328/// Path comparison func for binary searching for merging.
338 match (a, b) { 329fn path_cmp_bin_search(lhs: Option<ast::Path>, rhs: Option<ast::Path>) -> Ordering {
330 match (lhs, rhs) {
339 (None, None) => Ordering::Equal, 331 (None, None) => Ordering::Equal,
340 (None, Some(_)) => Ordering::Less, 332 (None, Some(_)) => Ordering::Less,
341 (Some(_), None) => Ordering::Greater, 333 (Some(_), None) => Ordering::Greater,
342 (Some(a), Some(b)) => path_cmp(&a, &b), 334 (Some(ref a), Some(ref b)) => path_cmp_short(a, b),
343 } 335 }
344} 336}
345 337
338/// Short circuiting comparison, if both paths are equal until one of them ends they are considered
339/// equal
340fn path_cmp_short(a: &ast::Path, b: &ast::Path) -> Ordering {
341 let a = segment_iter(a);
342 let b = segment_iter(b);
343 // cmp_by would be useful for us here but that is currently unstable
344 // cmp doesnt work due the lifetimes on text's return type
345 a.zip(b)
346 .find_map(|(a, b)| match path_segment_cmp(&a, &b) {
347 Ordering::Equal => None,
348 ord => Some(ord),
349 })
350 .unwrap_or(Ordering::Equal)
351}
352
353/// Compares to paths, if one ends earlier than the other the has_tl parameters decide which is
354/// greater as a a path that has a tree list should be greater, while one that just ends without
355/// a tree list should be considered less.
356fn use_tree_path_cmp(a: &ast::Path, a_has_tl: bool, b: &ast::Path, b_has_tl: bool) -> Ordering {
357 let a_segments = segment_iter(a);
358 let b_segments = segment_iter(b);
359 // cmp_by would be useful for us here but that is currently unstable
360 // cmp doesnt work due the lifetimes on text's return type
361 a_segments
362 .zip_longest(b_segments)
363 .find_map(|zipped| match zipped {
364 EitherOrBoth::Both(ref a, ref b) => match path_segment_cmp(a, b) {
365 Ordering::Equal => None,
366 ord => Some(ord),
367 },
368 EitherOrBoth::Left(_) if !b_has_tl => Some(Ordering::Greater),
369 EitherOrBoth::Left(_) => Some(Ordering::Less),
370 EitherOrBoth::Right(_) if !a_has_tl => Some(Ordering::Less),
371 EitherOrBoth::Right(_) => Some(Ordering::Greater),
372 })
373 .unwrap_or(Ordering::Equal)
374}
375
376fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering {
377 let a = a.name_ref();
378 let b = b.name_ref();
379 a.as_ref().map(ast::NameRef::text).cmp(&b.as_ref().map(ast::NameRef::text))
380}
381
346/// What type of merges are allowed. 382/// What type of merges are allowed.
347#[derive(Copy, Clone, Debug, PartialEq, Eq)] 383#[derive(Copy, Clone, Debug, PartialEq, Eq)]
348pub enum MergeBehaviour { 384pub enum MergeBehaviour {
@@ -352,6 +388,19 @@ pub enum MergeBehaviour {
352 Last, 388 Last,
353} 389}
354 390
391impl MergeBehaviour {
392 #[inline]
393 fn is_tree_allowed(&self, tree: &ast::UseTree) -> bool {
394 match self {
395 MergeBehaviour::Full => true,
396 // only simple single segment paths are allowed
397 MergeBehaviour::Last => {
398 tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1)
399 }
400 }
401 }
402}
403
355#[derive(Eq, PartialEq, PartialOrd, Ord)] 404#[derive(Eq, PartialEq, PartialOrd, Ord)]
356enum ImportGroup { 405enum ImportGroup {
357 // the order here defines the order of new group inserts 406 // the order here defines the order of new group inserts
@@ -379,7 +428,6 @@ impl ImportGroup {
379 PathSegmentKind::Name(name) => match name.text().as_str() { 428 PathSegmentKind::Name(name) => match name.text().as_str() {
380 "std" => ImportGroup::Std, 429 "std" => ImportGroup::Std,
381 "core" => ImportGroup::Std, 430 "core" => ImportGroup::Std,
382 // FIXME: can be ThisModule as well
383 _ => ImportGroup::ExternCrate, 431 _ => ImportGroup::ExternCrate,
384 }, 432 },
385 PathSegmentKind::Type { .. } => unreachable!(), 433 PathSegmentKind::Type { .. } => unreachable!(),
@@ -405,30 +453,30 @@ fn find_insert_position(
405 .as_syntax_node() 453 .as_syntax_node()
406 .children() 454 .children()
407 .filter_map(|node| ast::Use::cast(node.clone()).zip(Some(node))) 455 .filter_map(|node| ast::Use::cast(node.clone()).zip(Some(node)))
408 .flat_map(|(use_, node)| use_.use_tree().and_then(|tree| tree.path()).zip(Some(node))); 456 .flat_map(|(use_, node)| {
457 let tree = use_.use_tree()?;
458 let path = tree.path()?;
459 let has_tl = tree.use_tree_list().is_some();
460 Some((path, has_tl, node))
461 });
409 // Iterator that discards anything thats not in the required grouping 462 // Iterator that discards anything thats not in the required grouping
410 // This implementation allows the user to rearrange their import groups as this only takes the first group that fits 463 // This implementation allows the user to rearrange their import groups as this only takes the first group that fits
411 let group_iter = path_node_iter 464 let group_iter = path_node_iter
412 .clone() 465 .clone()
413 .skip_while(|(path, _)| ImportGroup::new(path) != group) 466 .skip_while(|(path, ..)| ImportGroup::new(path) != group)
414 .take_while(|(path, _)| ImportGroup::new(path) == group); 467 .take_while(|(path, ..)| ImportGroup::new(path) == group);
415 468
416 let segments = segment_iter(&insert_path);
417 // track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place 469 // track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place
418 let mut last = None; 470 let mut last = None;
419 // find the element that would come directly after our new import 471 // find the element that would come directly after our new import
420 let post_insert = 472 let post_insert = group_iter.inspect(|(.., node)| last = Some(node.clone())).find(
421 group_iter.inspect(|(_, node)| last = Some(node.clone())).find(|(path, _)| { 473 |&(ref path, has_tl, _)| {
422 let check_segments = segment_iter(&path); 474 use_tree_path_cmp(&insert_path, false, path, has_tl) != Ordering::Greater
423 segments 475 },
424 .clone() 476 );
425 .zip(check_segments)
426 .flat_map(|(seg, seg2)| seg.name_ref().zip(seg2.name_ref()))
427 .all(|(l, r)| l.text() <= r.text())
428 });
429 match post_insert { 477 match post_insert {
430 // insert our import before that element 478 // insert our import before that element
431 Some((_, node)) => (InsertPosition::Before(node.into()), AddBlankLine::After), 479 Some((.., node)) => (InsertPosition::Before(node.into()), AddBlankLine::After),
432 // there is no element after our new import, so append it to the end of the group 480 // there is no element after our new import, so append it to the end of the group
433 None => match last { 481 None => match last {
434 Some(node) => (InsertPosition::After(node.into()), AddBlankLine::Before), 482 Some(node) => (InsertPosition::After(node.into()), AddBlankLine::Before),
@@ -438,10 +486,10 @@ fn find_insert_position(
438 let mut last = None; 486 let mut last = None;
439 // find the group that comes after where we want to insert 487 // find the group that comes after where we want to insert
440 let post_group = path_node_iter 488 let post_group = path_node_iter
441 .inspect(|(_, node)| last = Some(node.clone())) 489 .inspect(|(.., node)| last = Some(node.clone()))
442 .find(|(p, _)| ImportGroup::new(p) > group); 490 .find(|(p, ..)| ImportGroup::new(p) > group);
443 match post_group { 491 match post_group {
444 Some((_, node)) => { 492 Some((.., node)) => {
445 (InsertPosition::Before(node.into()), AddBlankLine::AfterTwice) 493 (InsertPosition::Before(node.into()), AddBlankLine::AfterTwice)
446 } 494 }
447 // there is no such group, so append after the last one 495 // there is no such group, so append after the last one
@@ -676,6 +724,11 @@ use std::io;",
676 } 724 }
677 725
678 #[test] 726 #[test]
727 fn merge_last_into_self() {
728 check_last("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};");
729 }
730
731 #[test]
679 fn merge_groups_full() { 732 fn merge_groups_full() {
680 check_full( 733 check_full(
681 "std::io", 734 "std::io",
@@ -819,8 +872,23 @@ use std::io;",
819 } 872 }
820 873
821 #[test] 874 #[test]
822 fn merge_last_too_long() { 875 fn skip_merge_last_too_long() {
823 check_last("foo::bar", r"use foo::bar::baz::Qux;", r"use foo::bar::{self, baz::Qux};"); 876 check_last(
877 "foo::bar",
878 r"use foo::bar::baz::Qux;",
879 r"use foo::bar;
880use foo::bar::baz::Qux;",
881 );
882 }
883
884 #[test]
885 fn skip_merge_last_too_long2() {
886 check_last(
887 "foo::bar::baz::Qux",
888 r"use foo::bar;",
889 r"use foo::bar;
890use foo::bar::baz::Qux;",
891 );
824 } 892 }
825 893
826 #[test] 894 #[test]