Skip to content

Commit 46d7ee6

Browse files
bors[bot]jonasbb
andauthored
Merge #11865
11865: Fix: Select correct insert position for disabled group import r=jonasbb a=jonasbb The logic for importing with and without `group_imports` differed significantly when no previous group existed. This lead to the problem of using the wrong position when importing inside a module (#11585) or when inner attributes are involved. The existing code for grouped imports is better and takes these things into account. This PR changes the flow to use the pre-existing code for adding a new import group even for the non-grouped import settings. Some coverage markers are updated and the `group` is removed, since they are now invoked in both cases (grouping and no grouping). Tests are updated and two tests (empty module and inner attribute) are added. Fixes #11585 Co-authored-by: Jonas Bushart <[email protected]>
2 parents 79a0fee + 156f907 commit 46d7ee6

File tree

2 files changed

+116
-83
lines changed

2 files changed

+116
-83
lines changed

crates/ide_db/src/imports/insert_use.rs

Lines changed: 56 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -337,67 +337,65 @@ fn insert_use_(
337337
Some((path, has_tl, node))
338338
});
339339

340-
if !group_imports {
340+
if group_imports {
341+
// Iterator that discards anything thats not in the required grouping
342+
// This implementation allows the user to rearrange their import groups as this only takes the first group that fits
343+
let group_iter = path_node_iter
344+
.clone()
345+
.skip_while(|(path, ..)| ImportGroup::new(path) != group)
346+
.take_while(|(path, ..)| ImportGroup::new(path) == group);
347+
348+
// 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
349+
let mut last = None;
350+
// find the element that would come directly after our new import
351+
let post_insert: Option<(_, _, SyntaxNode)> = group_iter
352+
.inspect(|(.., node)| last = Some(node.clone()))
353+
.find(|&(ref path, has_tl, _)| {
354+
use_tree_path_cmp(insert_path, false, path, has_tl) != Ordering::Greater
355+
});
356+
357+
if let Some((.., node)) = post_insert {
358+
cov_mark::hit!(insert_group);
359+
// insert our import before that element
360+
return ted::insert(ted::Position::before(node), use_item.syntax());
361+
}
362+
if let Some(node) = last {
363+
cov_mark::hit!(insert_group_last);
364+
// there is no element after our new import, so append it to the end of the group
365+
return ted::insert(ted::Position::after(node), use_item.syntax());
366+
}
367+
368+
// the group we were looking for actually doesn't exist, so insert
369+
370+
let mut last = None;
371+
// find the group that comes after where we want to insert
372+
let post_group = path_node_iter
373+
.inspect(|(.., node)| last = Some(node.clone()))
374+
.find(|(p, ..)| ImportGroup::new(p) > group);
375+
if let Some((.., node)) = post_group {
376+
cov_mark::hit!(insert_group_new_group);
377+
ted::insert(ted::Position::before(&node), use_item.syntax());
378+
if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) {
379+
ted::insert(ted::Position::after(node), make::tokens::single_newline());
380+
}
381+
return;
382+
}
383+
// there is no such group, so append after the last one
384+
if let Some(node) = last {
385+
cov_mark::hit!(insert_group_no_group);
386+
ted::insert(ted::Position::after(&node), use_item.syntax());
387+
ted::insert(ted::Position::after(node), make::tokens::single_newline());
388+
return;
389+
}
390+
} else {
391+
// There exists a group, so append to the end of it
341392
if let Some((_, _, node)) = path_node_iter.last() {
342393
cov_mark::hit!(insert_no_grouping_last);
343394
ted::insert(ted::Position::after(node), use_item.syntax());
344-
} else {
345-
cov_mark::hit!(insert_no_grouping_last2);
346-
ted::insert(ted::Position::first_child_of(scope_syntax), make::tokens::blank_line());
347-
ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax());
395+
return;
348396
}
349-
return;
350-
}
351-
352-
// Iterator that discards anything thats not in the required grouping
353-
// This implementation allows the user to rearrange their import groups as this only takes the first group that fits
354-
let group_iter = path_node_iter
355-
.clone()
356-
.skip_while(|(path, ..)| ImportGroup::new(path) != group)
357-
.take_while(|(path, ..)| ImportGroup::new(path) == group);
358-
359-
// 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
360-
let mut last = None;
361-
// find the element that would come directly after our new import
362-
let post_insert: Option<(_, _, SyntaxNode)> = group_iter
363-
.inspect(|(.., node)| last = Some(node.clone()))
364-
.find(|&(ref path, has_tl, _)| {
365-
use_tree_path_cmp(insert_path, false, path, has_tl) != Ordering::Greater
366-
});
367-
368-
if let Some((.., node)) = post_insert {
369-
cov_mark::hit!(insert_group);
370-
// insert our import before that element
371-
return ted::insert(ted::Position::before(node), use_item.syntax());
372-
}
373-
if let Some(node) = last {
374-
cov_mark::hit!(insert_group_last);
375-
// there is no element after our new import, so append it to the end of the group
376-
return ted::insert(ted::Position::after(node), use_item.syntax());
377397
}
378398

379-
// the group we were looking for actually doesn't exist, so insert
380-
381-
let mut last = None;
382-
// find the group that comes after where we want to insert
383-
let post_group = path_node_iter
384-
.inspect(|(.., node)| last = Some(node.clone()))
385-
.find(|(p, ..)| ImportGroup::new(p) > group);
386-
if let Some((.., node)) = post_group {
387-
cov_mark::hit!(insert_group_new_group);
388-
ted::insert(ted::Position::before(&node), use_item.syntax());
389-
if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) {
390-
ted::insert(ted::Position::after(node), make::tokens::single_newline());
391-
}
392-
return;
393-
}
394-
// there is no such group, so append after the last one
395-
if let Some(node) = last {
396-
cov_mark::hit!(insert_group_no_group);
397-
ted::insert(ted::Position::after(&node), use_item.syntax());
398-
ted::insert(ted::Position::after(node), make::tokens::single_newline());
399-
return;
400-
}
401399
// there are no imports in this file at all
402400
if let Some(last_inner_element) = scope_syntax
403401
.children_with_tokens()
@@ -407,14 +405,14 @@ fn insert_use_(
407405
})
408406
.last()
409407
{
410-
cov_mark::hit!(insert_group_empty_inner_attr);
408+
cov_mark::hit!(insert_empty_inner_attr);
411409
ted::insert(ted::Position::after(&last_inner_element), use_item.syntax());
412410
ted::insert(ted::Position::after(last_inner_element), make::tokens::single_newline());
413411
return;
414412
}
415413
let l_curly = match scope {
416414
ImportScope::File(_) => {
417-
cov_mark::hit!(insert_group_empty_file);
415+
cov_mark::hit!(insert_empty_file);
418416
ted::insert(ted::Position::first_child_of(scope_syntax), make::tokens::blank_line());
419417
ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax());
420418
return;
@@ -426,7 +424,7 @@ fn insert_use_(
426424
};
427425
match l_curly {
428426
Some(b) => {
429-
cov_mark::hit!(insert_group_empty_module);
427+
cov_mark::hit!(insert_empty_module);
430428
ted::insert(ted::Position::after(&b), make::tokens::single_newline());
431429
ted::insert(ted::Position::after(&b), use_item.syntax());
432430
}

crates/ide_db/src/imports/insert_use/tests.rs

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -84,25 +84,6 @@ use external_crate2::bar::A;",
8484
);
8585
}
8686

87-
#[test]
88-
fn insert_not_group_empty() {
89-
cov_mark::check!(insert_no_grouping_last2);
90-
check_with_config(
91-
"use external_crate2::bar::A",
92-
r"",
93-
r"use external_crate2::bar::A;
94-
95-
",
96-
&InsertUseConfig {
97-
granularity: ImportGranularity::Item,
98-
enforce_granularity: true,
99-
prefix_kind: PrefixKind::Plain,
100-
group: false,
101-
skip_glob_imports: true,
102-
},
103-
);
104-
}
105-
10687
#[test]
10788
fn insert_existing() {
10889
check_crate("std::fs", "use std::fs;", "use std::fs;")
@@ -321,7 +302,9 @@ fn main() {}",
321302

322303
#[test]
323304
fn insert_empty_file() {
324-
cov_mark::check!(insert_group_empty_file);
305+
cov_mark::check_count!(insert_empty_file, 2);
306+
307+
// Default configuration
325308
// empty files will get two trailing newlines
326309
// this is due to the test case insert_no_imports above
327310
check_crate(
@@ -330,12 +313,30 @@ fn insert_empty_file() {
330313
r"use foo::bar;
331314
332315
",
333-
)
316+
);
317+
318+
// "not group" configuration
319+
check_with_config(
320+
"use external_crate2::bar::A",
321+
r"",
322+
r"use external_crate2::bar::A;
323+
324+
",
325+
&InsertUseConfig {
326+
granularity: ImportGranularity::Item,
327+
enforce_granularity: true,
328+
prefix_kind: PrefixKind::Plain,
329+
group: false,
330+
skip_glob_imports: true,
331+
},
332+
);
334333
}
335334

336335
#[test]
337336
fn insert_empty_module() {
338-
cov_mark::check!(insert_group_empty_module);
337+
cov_mark::check_count!(insert_empty_module, 2);
338+
339+
// Default configuration
339340
check(
340341
"foo::bar",
341342
r"
@@ -347,19 +348,53 @@ mod x {
347348
}
348349
",
349350
ImportGranularity::Item,
350-
)
351+
);
352+
353+
// "not group" configuration
354+
check_with_config(
355+
"foo::bar",
356+
r"mod x {$0}",
357+
r"mod x {
358+
use foo::bar;
359+
}",
360+
&InsertUseConfig {
361+
granularity: ImportGranularity::Item,
362+
enforce_granularity: true,
363+
prefix_kind: PrefixKind::Plain,
364+
group: false,
365+
skip_glob_imports: true,
366+
},
367+
);
351368
}
352369

353370
#[test]
354371
fn insert_after_inner_attr() {
355-
cov_mark::check!(insert_group_empty_inner_attr);
372+
cov_mark::check_count!(insert_empty_inner_attr, 2);
373+
374+
// Default configuration
356375
check_crate(
357376
"foo::bar",
358377
r"#![allow(unused_imports)]",
359378
r"#![allow(unused_imports)]
360379
361380
use foo::bar;",
362-
)
381+
);
382+
383+
// "not group" configuration
384+
check_with_config(
385+
"foo::bar",
386+
r"#![allow(unused_imports)]",
387+
r"#![allow(unused_imports)]
388+
389+
use foo::bar;",
390+
&InsertUseConfig {
391+
granularity: ImportGranularity::Item,
392+
enforce_granularity: true,
393+
prefix_kind: PrefixKind::Plain,
394+
group: false,
395+
skip_glob_imports: true,
396+
},
397+
);
363398
}
364399

365400
#[test]

0 commit comments

Comments
 (0)