about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-04-03 18:46:45 +0000
committerGitHub <noreply@github.com>2022-04-03 18:46:45 +0000
commit46d7ee68f26285db26b2640f2c07d6332380c756 (patch)
treeb1261b79f5334e057317f1999caee9c162521097
parent79a0fee082c6a8fac3dee5baf1f21669304e264d (diff)
parent156f9074e19346d79d8d7bc21ff643f50ba6a954 (diff)
downloadrust-46d7ee68f26285db26b2640f2c07d6332380c756.tar.gz
rust-46d7ee68f26285db26b2640f2c07d6332380c756.zip
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 <jonas@bushart.org>
-rw-r--r--crates/ide_db/src/imports/insert_use.rs114
-rw-r--r--crates/ide_db/src/imports/insert_use/tests.rs85
2 files changed, 116 insertions, 83 deletions
diff --git a/crates/ide_db/src/imports/insert_use.rs b/crates/ide_db/src/imports/insert_use.rs
index 9e39c26b45b..a19969ecc4a 100644
--- a/crates/ide_db/src/imports/insert_use.rs
+++ b/crates/ide_db/src/imports/insert_use.rs
@@ -337,67 +337,65 @@ fn insert_use_(
             Some((path, has_tl, node))
         });
 
-    if !group_imports {
+    if group_imports {
+        // Iterator that discards anything thats not in the required grouping
+        // This implementation allows the user to rearrange their import groups as this only takes the first group that fits
+        let group_iter = path_node_iter
+            .clone()
+            .skip_while(|(path, ..)| ImportGroup::new(path) != group)
+            .take_while(|(path, ..)| ImportGroup::new(path) == group);
+
+        // 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
+        let mut last = None;
+        // find the element that would come directly after our new import
+        let post_insert: Option<(_, _, SyntaxNode)> = group_iter
+            .inspect(|(.., node)| last = Some(node.clone()))
+            .find(|&(ref path, has_tl, _)| {
+                use_tree_path_cmp(insert_path, false, path, has_tl) != Ordering::Greater
+            });
+
+        if let Some((.., node)) = post_insert {
+            cov_mark::hit!(insert_group);
+            // insert our import before that element
+            return ted::insert(ted::Position::before(node), use_item.syntax());
+        }
+        if let Some(node) = last {
+            cov_mark::hit!(insert_group_last);
+            // there is no element after our new import, so append it to the end of the group
+            return ted::insert(ted::Position::after(node), use_item.syntax());
+        }
+
+        // the group we were looking for actually doesn't exist, so insert
+
+        let mut last = None;
+        // find the group that comes after where we want to insert
+        let post_group = path_node_iter
+            .inspect(|(.., node)| last = Some(node.clone()))
+            .find(|(p, ..)| ImportGroup::new(p) > group);
+        if let Some((.., node)) = post_group {
+            cov_mark::hit!(insert_group_new_group);
+            ted::insert(ted::Position::before(&node), use_item.syntax());
+            if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) {
+                ted::insert(ted::Position::after(node), make::tokens::single_newline());
+            }
+            return;
+        }
+        // there is no such group, so append after the last one
+        if let Some(node) = last {
+            cov_mark::hit!(insert_group_no_group);
+            ted::insert(ted::Position::after(&node), use_item.syntax());
+            ted::insert(ted::Position::after(node), make::tokens::single_newline());
+            return;
+        }
+    } else {
+        // There exists a group, so append to the end of it
         if let Some((_, _, node)) = path_node_iter.last() {
             cov_mark::hit!(insert_no_grouping_last);
             ted::insert(ted::Position::after(node), use_item.syntax());
-        } else {
-            cov_mark::hit!(insert_no_grouping_last2);
-            ted::insert(ted::Position::first_child_of(scope_syntax), make::tokens::blank_line());
-            ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax());
+            return;
         }
-        return;
-    }
-
-    // Iterator that discards anything thats not in the required grouping
-    // This implementation allows the user to rearrange their import groups as this only takes the first group that fits
-    let group_iter = path_node_iter
-        .clone()
-        .skip_while(|(path, ..)| ImportGroup::new(path) != group)
-        .take_while(|(path, ..)| ImportGroup::new(path) == group);
-
-    // 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
-    let mut last = None;
-    // find the element that would come directly after our new import
-    let post_insert: Option<(_, _, SyntaxNode)> = group_iter
-        .inspect(|(.., node)| last = Some(node.clone()))
-        .find(|&(ref path, has_tl, _)| {
-            use_tree_path_cmp(insert_path, false, path, has_tl) != Ordering::Greater
-        });
-
-    if let Some((.., node)) = post_insert {
-        cov_mark::hit!(insert_group);
-        // insert our import before that element
-        return ted::insert(ted::Position::before(node), use_item.syntax());
-    }
-    if let Some(node) = last {
-        cov_mark::hit!(insert_group_last);
-        // there is no element after our new import, so append it to the end of the group
-        return ted::insert(ted::Position::after(node), use_item.syntax());
     }
 
-    // the group we were looking for actually doesn't exist, so insert
-
-    let mut last = None;
-    // find the group that comes after where we want to insert
-    let post_group = path_node_iter
-        .inspect(|(.., node)| last = Some(node.clone()))
-        .find(|(p, ..)| ImportGroup::new(p) > group);
-    if let Some((.., node)) = post_group {
-        cov_mark::hit!(insert_group_new_group);
-        ted::insert(ted::Position::before(&node), use_item.syntax());
-        if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) {
-            ted::insert(ted::Position::after(node), make::tokens::single_newline());
-        }
-        return;
-    }
-    // there is no such group, so append after the last one
-    if let Some(node) = last {
-        cov_mark::hit!(insert_group_no_group);
-        ted::insert(ted::Position::after(&node), use_item.syntax());
-        ted::insert(ted::Position::after(node), make::tokens::single_newline());
-        return;
-    }
     // there are no imports in this file at all
     if let Some(last_inner_element) = scope_syntax
         .children_with_tokens()
@@ -407,14 +405,14 @@ fn insert_use_(
         })
         .last()
     {
-        cov_mark::hit!(insert_group_empty_inner_attr);
+        cov_mark::hit!(insert_empty_inner_attr);
         ted::insert(ted::Position::after(&last_inner_element), use_item.syntax());
         ted::insert(ted::Position::after(last_inner_element), make::tokens::single_newline());
         return;
     }
     let l_curly = match scope {
         ImportScope::File(_) => {
-            cov_mark::hit!(insert_group_empty_file);
+            cov_mark::hit!(insert_empty_file);
             ted::insert(ted::Position::first_child_of(scope_syntax), make::tokens::blank_line());
             ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax());
             return;
@@ -426,7 +424,7 @@ fn insert_use_(
     };
     match l_curly {
         Some(b) => {
-            cov_mark::hit!(insert_group_empty_module);
+            cov_mark::hit!(insert_empty_module);
             ted::insert(ted::Position::after(&b), make::tokens::single_newline());
             ted::insert(ted::Position::after(&b), use_item.syntax());
         }
diff --git a/crates/ide_db/src/imports/insert_use/tests.rs b/crates/ide_db/src/imports/insert_use/tests.rs
index e4b9651e5e8..acadb353b6a 100644
--- a/crates/ide_db/src/imports/insert_use/tests.rs
+++ b/crates/ide_db/src/imports/insert_use/tests.rs
@@ -85,25 +85,6 @@ use external_crate2::bar::A;",
 }
 
 #[test]
-fn insert_not_group_empty() {
-    cov_mark::check!(insert_no_grouping_last2);
-    check_with_config(
-        "use external_crate2::bar::A",
-        r"",
-        r"use external_crate2::bar::A;
-
-",
-        &InsertUseConfig {
-            granularity: ImportGranularity::Item,
-            enforce_granularity: true,
-            prefix_kind: PrefixKind::Plain,
-            group: false,
-            skip_glob_imports: true,
-        },
-    );
-}
-
-#[test]
 fn insert_existing() {
     check_crate("std::fs", "use std::fs;", "use std::fs;")
 }
@@ -321,7 +302,9 @@ fn main() {}",
 
 #[test]
 fn insert_empty_file() {
-    cov_mark::check!(insert_group_empty_file);
+    cov_mark::check_count!(insert_empty_file, 2);
+
+    // Default configuration
     // empty files will get two trailing newlines
     // this is due to the test case insert_no_imports above
     check_crate(
@@ -330,12 +313,30 @@ fn insert_empty_file() {
         r"use foo::bar;
 
 ",
-    )
+    );
+
+    // "not group" configuration
+    check_with_config(
+        "use external_crate2::bar::A",
+        r"",
+        r"use external_crate2::bar::A;
+
+",
+        &InsertUseConfig {
+            granularity: ImportGranularity::Item,
+            enforce_granularity: true,
+            prefix_kind: PrefixKind::Plain,
+            group: false,
+            skip_glob_imports: true,
+        },
+    );
 }
 
 #[test]
 fn insert_empty_module() {
-    cov_mark::check!(insert_group_empty_module);
+    cov_mark::check_count!(insert_empty_module, 2);
+
+    // Default configuration
     check(
         "foo::bar",
         r"
@@ -347,19 +348,53 @@ mod x {
 }
 ",
         ImportGranularity::Item,
-    )
+    );
+
+    // "not group" configuration
+    check_with_config(
+        "foo::bar",
+        r"mod x {$0}",
+        r"mod x {
+    use foo::bar;
+}",
+        &InsertUseConfig {
+            granularity: ImportGranularity::Item,
+            enforce_granularity: true,
+            prefix_kind: PrefixKind::Plain,
+            group: false,
+            skip_glob_imports: true,
+        },
+    );
 }
 
 #[test]
 fn insert_after_inner_attr() {
-    cov_mark::check!(insert_group_empty_inner_attr);
+    cov_mark::check_count!(insert_empty_inner_attr, 2);
+
+    // Default configuration
     check_crate(
         "foo::bar",
         r"#![allow(unused_imports)]",
         r"#![allow(unused_imports)]
 
 use foo::bar;",
-    )
+    );
+
+    // "not group" configuration
+    check_with_config(
+        "foo::bar",
+        r"#![allow(unused_imports)]",
+        r"#![allow(unused_imports)]
+
+use foo::bar;",
+        &InsertUseConfig {
+            granularity: ImportGranularity::Item,
+            enforce_granularity: true,
+            prefix_kind: PrefixKind::Plain,
+            group: false,
+            skip_glob_imports: true,
+        },
+    );
 }
 
 #[test]