diff options
Diffstat (limited to 'crates/assists')
-rw-r--r-- | crates/assists/src/ast_transform.rs | 74 | ||||
-rw-r--r-- | crates/assists/src/handlers/add_missing_impl_members.rs | 2 | ||||
-rw-r--r-- | crates/assists/src/handlers/extract_struct_from_enum_variant.rs | 2 | ||||
-rw-r--r-- | crates/assists/src/utils/insert_use.rs | 204 |
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}; | |||
5 | use syntax::{ | 5 | use syntax::{ |
6 | algo::SyntaxRewriter, | 6 | algo::SyntaxRewriter, |
7 | ast::{self, AstNode}, | 7 | ast::{self, AstNode}, |
8 | SyntaxNode, | ||
8 | }; | 9 | }; |
9 | 10 | ||
10 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { | 11 | pub 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! |
49 | pub trait AstTransform<'a> { | 50 | pub 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 | ||
61 | struct NullTransformer; | 65 | struct Or<'a>(Box<dyn AstTransform<'a> + 'a>, Box<dyn AstTransform<'a> + 'a>); |
62 | 66 | ||
63 | impl<'a> AstTransform<'a> for NullTransformer { | 67 | impl<'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 | ||
72 | pub struct SubstituteTypeParams<'a> { | 77 | pub 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 | ||
78 | impl<'a> SubstituteTypeParams<'a> { | 82 | impl<'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 | |||
145 | impl<'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 | ||
157 | impl<'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 | |||
166 | pub struct QualifyPaths<'a> { | 164 | pub 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 | ||
172 | impl<'a> QualifyPaths<'a> { | 169 | impl<'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> { | 175 | impl<'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 | ||
211 | impl<'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 | |||
220 | pub(crate) fn path_to_ast(path: hir::ModPath) -> ast::Path { | 214 | pub(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 | ||
7 | use ast::{ | 7 | use itertools::{EitherOrBoth, Itertools}; |
8 | edit::{AstNodeEdit, IndentLevel}, | ||
9 | PathSegmentKind, VisibilityOwner, | ||
10 | }; | ||
11 | use syntax::{ | 8 | use 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 | ||
305 | fn 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}} |
316 | fn path_cmp(a: &ast::Path, b: &ast::Path) -> Ordering { | 314 | fn 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 | ||
337 | fn 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) { | 329 | fn 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 | ||
340 | fn 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. | ||
356 | fn 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 | |||
376 | fn 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)] |
348 | pub enum MergeBehaviour { | 384 | pub enum MergeBehaviour { |
@@ -352,6 +388,19 @@ pub enum MergeBehaviour { | |||
352 | Last, | 388 | Last, |
353 | } | 389 | } |
354 | 390 | ||
391 | impl 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)] |
356 | enum ImportGroup { | 405 | enum 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; | ||
880 | use 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; | ||
890 | use foo::bar::baz::Qux;", | ||
891 | ); | ||
824 | } | 892 | } |
825 | 893 | ||
826 | #[test] | 894 | #[test] |