|
1 | 1 | //! Handle syntactic aspects of merging UseTrees.
|
2 | 2 | use std::cmp::Ordering;
|
3 |
| -use std::iter::{empty, successors}; |
| 3 | +use std::iter::empty; |
4 | 4 |
|
5 | 5 | use itertools::{EitherOrBoth, Itertools};
|
6 |
| -use parser::{SyntaxKind, T}; |
7 |
| -use rustc_hash::{FxHashMap, FxHashSet}; |
| 6 | +use parser::T; |
8 | 7 | use syntax::{
|
| 8 | + algo, |
9 | 9 | ast::{self, make, AstNode, HasAttrs, HasName, HasVisibility, PathSegmentKind},
|
10 | 10 | ted::{self, Position},
|
11 |
| - SyntaxElement, TokenText, |
| 11 | + Direction, TokenText, |
12 | 12 | };
|
13 | 13 |
|
14 | 14 | use crate::syntax_helpers::node_ext::vis_eq;
|
@@ -101,22 +101,6 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior)
|
101 | 101 | // same as a `filter` op).
|
102 | 102 | .map(|tree| merge.is_tree_allowed(&tree).then_some(tree))
|
103 | 103 | .collect::<Option<_>>()?;
|
104 |
| - // Preserves some positional formatting info before sorting. |
105 |
| - let positional_formatting_map: FxHashMap<_, _> = use_trees |
106 |
| - .iter() |
107 |
| - .enumerate() |
108 |
| - .filter_map(|(idx, tree)| { |
109 |
| - formatting_whitespace(&SyntaxElement::from(tree.syntax().clone())) |
110 |
| - .map(|trivia| (idx, trivia)) |
111 |
| - }) |
112 |
| - .collect(); |
113 |
| - let closing_formatting_trivia = if use_trees.len() > 0 { |
114 |
| - lhs.use_tree_list() |
115 |
| - .and_then(|list| list.r_curly_token()) |
116 |
| - .and_then(|token| formatting_whitespace(&SyntaxElement::from(token))) |
117 |
| - } else { |
118 |
| - None |
119 |
| - }; |
120 | 104 | // Sorts the use trees similar to rustfmt's algorithm for ordering imports
|
121 | 105 | // (see `use_tree_cmp` doc).
|
122 | 106 | use_trees.sort_unstable_by(|a, b| use_tree_cmp(a, b));
|
@@ -184,60 +168,59 @@ fn recursive_merge(lhs: &ast::UseTree, rhs: &ast::UseTree, merge: MergeBehavior)
|
184 | 168 | // Creates a new use tree list with the item.
|
185 | 169 | None => lhs.get_or_create_use_tree_list().add_use_tree(rhs_t),
|
186 | 170 | // Recreates the use tree list with sorted items (see `use_tree_cmp` doc).
|
187 |
| - // Also attempts to preserve formatting (but only in terms of index-based |
188 |
| - // "positions" of new lines and indents). |
189 |
| - Some(old_list) => { |
190 |
| - ted::remove(old_list.syntax()); |
| 171 | + Some(use_tree_list) => { |
| 172 | + if use_tree_list.l_curly_token().is_none() { |
| 173 | + ted::insert_raw( |
| 174 | + Position::first_child_of(use_tree_list.syntax()), |
| 175 | + make::token(T!['{']), |
| 176 | + ); |
| 177 | + } |
| 178 | + if use_tree_list.r_curly_token().is_none() { |
| 179 | + ted::insert_raw( |
| 180 | + Position::last_child_of(use_tree_list.syntax()), |
| 181 | + make::token(T!['}']), |
| 182 | + ); |
| 183 | + } |
191 | 184 |
|
192 |
| - let new_list = make::use_tree_list(empty()).clone_for_update(); |
193 | 185 | let mut elements = Vec::new();
|
194 | 186 | for (idx, tree) in use_trees.iter().enumerate() {
|
195 | 187 | if idx > 0 {
|
196 | 188 | elements.push(make::token(T![,]).into());
|
197 |
| - } |
198 |
| - match positional_formatting_map.get(&idx) { |
199 |
| - None if idx > 0 => { |
200 |
| - elements.push(make::tokens::single_space().into()) |
201 |
| - } |
202 |
| - Some(prev_trivia) => { |
203 |
| - elements.extend(prev_trivia.clone().into_iter()) |
204 |
| - } |
205 |
| - _ => (), |
| 189 | + elements.push(make::tokens::single_space().into()); |
206 | 190 | }
|
207 | 191 | elements.push(tree.syntax().clone().into());
|
208 | 192 | }
|
209 |
| - if let Some(ref trivia) = closing_formatting_trivia { |
210 |
| - elements.extend(trivia.clone().into_iter()) |
211 |
| - } |
212 | 193 |
|
213 |
| - let trees_pos = match new_list.l_curly_token() { |
214 |
| - Some(l_curly) => Position::after(l_curly), |
215 |
| - None => Position::last_child_of(new_list.syntax()), |
216 |
| - }; |
217 |
| - ted::insert_all_raw(trees_pos, elements); |
218 |
| - |
219 |
| - let list_pos = Position::last_child_of(lhs.syntax()); |
220 |
| - ted::insert_raw(list_pos, new_list.syntax()); |
| 194 | + let start = use_tree_list |
| 195 | + .l_curly_token() |
| 196 | + .and_then(|l_curly| { |
| 197 | + algo::non_trivia_sibling(l_curly.into(), Direction::Next) |
| 198 | + }) |
| 199 | + .filter(|it| it.kind() != T!['}']); |
| 200 | + let end = use_tree_list |
| 201 | + .r_curly_token() |
| 202 | + .and_then(|r_curly| { |
| 203 | + algo::non_trivia_sibling(r_curly.into(), Direction::Prev) |
| 204 | + }) |
| 205 | + .filter(|it| it.kind() != T!['{']); |
| 206 | + if let Some((start, end)) = start.zip(end) { |
| 207 | + // Attempt to insert elements while preserving preceding and trailing trivia. |
| 208 | + ted::replace_all(start..=end, elements); |
| 209 | + } else { |
| 210 | + let new_use_tree_list = make::use_tree_list(empty()).clone_for_update(); |
| 211 | + let trees_pos = match new_use_tree_list.l_curly_token() { |
| 212 | + Some(l_curly) => Position::after(l_curly), |
| 213 | + None => Position::last_child_of(new_use_tree_list.syntax()), |
| 214 | + }; |
| 215 | + ted::insert_all_raw(trees_pos, elements); |
| 216 | + ted::replace(use_tree_list.syntax(), new_use_tree_list.syntax()); |
| 217 | + } |
221 | 218 | }
|
222 | 219 | }
|
223 | 220 | }
|
224 | 221 | }
|
225 | 222 | }
|
226 |
| - return Some(()); |
227 |
| - |
228 |
| - // Returns all trivia preceding a syntax element if it may be relevant to formatting |
229 |
| - // (i.e. includes at least one new line character). |
230 |
| - fn formatting_whitespace(elem: &SyntaxElement) -> Option<FxHashSet<SyntaxElement>> { |
231 |
| - let succ = |
232 |
| - |item: &SyntaxElement| item.prev_sibling_or_token().filter(|it| it.kind().is_trivia()); |
233 |
| - let first = succ(elem); |
234 |
| - let trivia_set: FxHashSet<_> = successors(first, succ).collect(); |
235 |
| - let contains_formatting_whitespace = trivia_set.iter().any(|it| { |
236 |
| - it.kind() == SyntaxKind::WHITESPACE |
237 |
| - && it.as_token().is_some_and(|token| token.text().contains('\n')) |
238 |
| - }); |
239 |
| - contains_formatting_whitespace.then_some(trivia_set) |
240 |
| - } |
| 223 | + Some(()) |
241 | 224 | }
|
242 | 225 |
|
243 | 226 | /// Traverses both paths until they differ, returning the common prefix of both.
|
@@ -383,13 +366,13 @@ fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering {
|
383 | 366 | }
|
384 | 367 | }
|
385 | 368 |
|
386 |
| -// Orders for use trees with equal paths (see `use_tree_cmp` for details about use tree ordering). |
387 |
| -// |
388 |
| -// If the `strict` parameter is set to true and both trees have tree lists, the tree lists are |
389 |
| -// ordered by calling `use_tree_cmp` on their "sub-tree" pairs until either the tie is broken |
390 |
| -// or tree list equality is confirmed, otherwise (i.e. if either `strict` is false or at least |
391 |
| -// one of the trees does *not* have tree list), this potentially recursive step is skipped, |
392 |
| -// and only the presence of a glob pattern or an alias is used to determine the ordering. |
| 369 | +/// Orders for use trees with equal paths (see `use_tree_cmp` for details about use tree ordering). |
| 370 | +/// |
| 371 | +/// If the `strict` parameter is set to true and both trees have tree lists, the tree lists are |
| 372 | +/// ordered by calling `use_tree_cmp` on their "sub-tree" pairs until either the tie is broken |
| 373 | +/// or tree list equality is confirmed, otherwise (i.e. if either `strict` is false or at least |
| 374 | +/// one of the trees does *not* have tree list), this potentially recursive step is skipped, |
| 375 | +/// and only the presence of a glob pattern or an alias is used to determine the ordering. |
393 | 376 | fn use_tree_cmp_by_tree_list_glob_or_alias(
|
394 | 377 | a: &ast::UseTree,
|
395 | 378 | b: &ast::UseTree,
|
|
0 commit comments