Repository: gleam-lang/gleam (Rust)

Issue:
Unqualify code action readds import
When you have imported a module unqualified but you have it written with the module, the code action will readd the existing import.

Example:
```gleam
import gleam/dict.{type Dict}

pub fn a(d: dict.Dict(Int, Int)) {
  Nil
}
```
becomes
```gleam
import gleam/dict.{type Dict, type Dict}

pub fn a(d: Dict(Int, Int)) {
  Nil
}
```

Gleam: v1.9.1



Discussion:
Can confirm, just hit this today.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6351f3da5ff..74435f2bacf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
 # Changelog
 
+## Unreleased
+
+### Bug fixes
+
+- Fixed a bug where the code action to unqualify types and values would add an
+  unqualified import even if it was already imported.
+  ([Surya Rose](https://github.com/GearsDatapacks))
+
 ## v1.10.0-rc1 - 2025-04-05
 
 ### Compiler
diff --git a/compiler-core/src/language_server/code_action.rs b/compiler-core/src/language_server/code_action.rs
index 7c6ed24f684..00209317949 100644
--- a/compiler-core/src/language_server/code_action.rs
+++ b/compiler-core/src/language_server/code_action.rs
@@ -1518,7 +1518,7 @@ impl<'a> QualifiedToUnqualifiedImportSecondPass<'a> {
                 .any(|value| value.used_name() == constructor)
         } else {
             import
-                .unqualified_values
+                .unqualified_types
                 .iter()
                 .any(|type_| type_.used_name() == constructor)
         };


Test patch:
diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs
index 1592dcc803a..f54aea946f8 100644
--- a/compiler-core/src/language_server/tests/action.rs
+++ b/compiler-core/src/language_server/tests/action.rs
@@ -7621,3 +7621,21 @@ fn name() { "Jak" }
         find_position_of("hello").select_until(find_position_of("name()"))
     );
 }
+
+// https://github.com/gleam-lang/gleam/issues/4454
+#[test]
+fn unqualify_already_imported_type() {
+    let src = "
+import wibble.{type Wibble}
+
+pub fn main() -> wibble.Wibble {
+  todo
+}
+";
+
+    assert_code_action!(
+        "Unqualify wibble.Wibble",
+        TestProject::for_source(src).add_hex_module("wibble", "pub type Wibble"),
+        find_position_of("wibble.Wibble").to_selection(),
+    );
+}
diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__unqualify_already_imported_type.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__unqualify_already_imported_type.snap
new file mode 100644
index 00000000000..a35f7d86c99
--- /dev/null
+++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__action__unqualify_already_imported_type.snap
@@ -0,0 +1,21 @@
+---
+source: compiler-core/src/language_server/tests/action.rs
+expression: "\nimport wibble.{type Wibble}\n\npub fn main() -> wibble.Wibble {\n  todo\n}\n"
+---
+----- BEFORE ACTION
+
+import wibble.{type Wibble}
+
+pub fn main() -> wibble.Wibble {
+                 ↑              
+  todo
+}
+
+
+----- AFTER ACTION
+
+import wibble.{type Wibble}
+
+pub fn main() -> Wibble {
+  todo
+}