diff --git a/java/java-impl/resources/META-INF/JavaPlugin.xml b/java/java-impl/resources/META-INF/JavaPlugin.xml
index 513d2c9e270a..93c8886688f1 100644
--- a/java/java-impl/resources/META-INF/JavaPlugin.xml
+++ b/java/java-impl/resources/META-INF/JavaPlugin.xml
@@ -1472,7 +1472,7 @@
-
+
(withSelf = true) != null
+ }
+
+ override fun getTextRangeImportList(psiFile: PsiFile, offset: Int): TextRange? {
+ if (psiFile is PsiJavaFile) return psiFile.importList?.textRange
+ return null
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/commands/JavaCommandsCompletionTest.kt b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/commands/JavaCommandsCompletionTest.kt
index 208cb8a7d5e2..07338f9b1419 100644
--- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/commands/JavaCommandsCompletionTest.kt
+++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/commands/JavaCommandsCompletionTest.kt
@@ -237,6 +237,20 @@ class JavaCommandsCompletionTest : LightFixtureCompletionTestCase() {
}""".trimIndent())
}
+ fun testOptimizeImport2() {
+ Registry.get("ide.completion.command.force.enabled").setValue(true, getTestRootDisposable())
+ myFixture.configureByText(JavaFileType.INSTANCE, """
+ import java.util.List;.
+
+ class A {
+ void foo() {
+ String y = "1";
+ }
+ }""".trimIndent())
+ val elements = myFixture.completeBasic()
+ assertTrue(elements.any { element -> element.lookupString.contains("Optimize im", ignoreCase = true) })
+ }
+
fun testGenerateGetter() {
Registry.get("ide.completion.command.force.enabled").setValue(true, getTestRootDisposable())
myFixture.configureByText(JavaFileType.INSTANCE, """
diff --git a/platform/lang-impl/api-dump-experimental.txt b/platform/lang-impl/api-dump-experimental.txt
index 3be8ebfeca7f..3a1c3c6e9bd6 100644
--- a/platform/lang-impl/api-dump-experimental.txt
+++ b/platform/lang-impl/api-dump-experimental.txt
@@ -248,6 +248,13 @@ c:com.intellij.codeInsight.actions.VcsFacade
- p:createCommand(com.intellij.codeInsight.completion.command.CommandCompletionProviderContext):com.intellij.codeInsight.completion.command.commands.ActionCompletionCommand
- a:findMoveClassOffset(I,com.intellij.psi.PsiFile):java.lang.Integer
- p:isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z
+*a:com.intellij.codeInsight.completion.command.commands.AbstractOptimizeImportCompletionCommandProvider
+- com.intellij.codeInsight.completion.command.commands.ActionCommandProvider
+- ():V
+- p:createCommand(com.intellij.codeInsight.completion.command.CommandCompletionProviderContext):com.intellij.codeInsight.completion.command.commands.ActionCompletionCommand
+- a:getTextRangeImportList(com.intellij.psi.PsiFile,I):com.intellij.openapi.util.TextRange
+- p:isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z
+- a:isImportList(com.intellij.psi.PsiFile,I):Z
*a:com.intellij.codeInsight.completion.command.commands.AbstractRenameActionCommandProvider
- com.intellij.codeInsight.completion.command.commands.ActionCommandProvider
- ():V
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractOptimizeImportCompletionCommandProvider.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractOptimizeImportCompletionCommandProvider.kt
new file mode 100644
index 000000000000..daed59fbb2ac
--- /dev/null
+++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractOptimizeImportCompletionCommandProvider.kt
@@ -0,0 +1,45 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.codeInsight.completion.command.commands
+
+import com.intellij.codeInsight.completion.command.CommandCompletionProviderContext
+import com.intellij.codeInsight.completion.command.HighlightInfoLookup
+import com.intellij.idea.ActionsBundle
+import com.intellij.lang.injection.InjectedLanguageManager
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.colors.EditorColors
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiFile
+
+abstract class AbstractOptimizeImportCompletionCommandProvider :
+ ActionCommandProvider(actionId = "OptimizeImports",
+ synonyms = listOf("Optimize imports"),
+ presentableName = ActionsBundle.message("action.OptimizeImports.text"),
+ icon = null,
+ priority = -100,
+ previewText = ActionsBundle.message("action.OptimizeImports.description")) {
+
+ override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean {
+ if (!super.isApplicable(offset, psiFile, editor)) return false
+ if (InjectedLanguageManager.getInstance(psiFile.project).isInjectedFragment(psiFile)) return false
+ return isApplicableToProject(offset, psiFile) || isImportList(psiFile, offset)
+ }
+
+ override fun createCommand(context: CommandCompletionProviderContext): ActionCompletionCommand? {
+ val range: TextRange? = getTextRangeImportList(context.psiFile, context.offset)
+ return ActionCompletionCommand(actionId = super.actionId,
+ presentableActionName = super.presentableName,
+ icon = super.icon,
+ priority = super.priority,
+ previewText = super.previewText,
+ synonyms = super.synonyms,
+ highlightInfo = if (range != null) {
+ HighlightInfoLookup(range, EditorColors.SEARCH_RESULT_ATTRIBUTES, 0)
+ }
+ else {
+ null
+ })
+ }
+
+ abstract fun isImportList(psiFile: PsiFile, offset: Int): Boolean
+ abstract fun getTextRangeImportList(psiFile: PsiFile, offset: Int): TextRange?
+}
\ No newline at end of file
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/OptimizeImportCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/OptimizeImportCompletionCommand.kt
deleted file mode 100644
index b5d1535c4c2f..000000000000
--- a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/OptimizeImportCompletionCommand.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
-package com.intellij.codeInsight.completion.command.commands
-
-import com.intellij.idea.ActionsBundle
-import com.intellij.lang.injection.InjectedLanguageManager
-import com.intellij.openapi.editor.Editor
-import com.intellij.psi.PsiFile
-
-internal class OptimizeImportCompletionCommandProvider :
- ActionCommandProvider(actionId = "OptimizeImports",
- synonyms = listOf("Optimize imports"),
- presentableName = ActionsBundle.message("action.OptimizeImports.text"),
- icon = null,
- priority = -100,
- previewText = ActionsBundle.message("action.OptimizeImports.description")) {
-
- override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean {
- if (!super.isApplicable(offset, psiFile, editor)) return false
- if (InjectedLanguageManager.getInstance(psiFile.project).isInjectedFragment(psiFile)) return false
- return isApplicableToProject(offset, psiFile)
- }
-}
\ No newline at end of file
diff --git a/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/commands/KotlinOptimizeImportCompletionCommandProvider.kt b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/commands/KotlinOptimizeImportCompletionCommandProvider.kt
new file mode 100644
index 000000000000..23e79578c90b
--- /dev/null
+++ b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/commands/KotlinOptimizeImportCompletionCommandProvider.kt
@@ -0,0 +1,22 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package org.jetbrains.kotlin.idea.completion.impl.k2.contributors.commands
+
+import com.intellij.codeInsight.completion.command.commands.AbstractOptimizeImportCompletionCommandProvider
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiFile
+import com.intellij.psi.util.parentOfType
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtImportList
+
+class KotlinOptimizeImportCompletionCommandProvider : AbstractOptimizeImportCompletionCommandProvider() {
+ override fun isImportList(psiFile: PsiFile, offset: Int): Boolean {
+ if (offset - 1 < 0) return false
+ val element = psiFile.findElementAt(offset - 1)
+ return element?.parentOfType(withSelf = true) != null
+ }
+
+ override fun getTextRangeImportList(psiFile: PsiFile, offset: Int): TextRange? {
+ if (psiFile is KtFile) return psiFile.importList?.textRange
+ return null
+ }
+}
\ No newline at end of file
diff --git a/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/K2CommandCompletionTest.kt b/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/K2CommandCompletionTest.kt
index a307dda18f94..43f81353171e 100644
--- a/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/K2CommandCompletionTest.kt
+++ b/plugins/kotlin/fir/tests/test/org/jetbrains/kotlin/idea/fir/completion/K2CommandCompletionTest.kt
@@ -588,6 +588,7 @@ class K2CommandCompletionTest : KotlinLightCodeInsightFixtureTestCase() {
val elements = myFixture.complete(CompletionType.BASIC, 0)
assertTrue(elements[0].`as`(CommandCompletionLookupElement::class.java) != null)
}
+
fun testNotFirstCompletion() {
Registry.get("ide.completion.command.force.enabled").setValue(true, getTestRootDisposable())
myFixture.configureByText(
@@ -605,6 +606,19 @@ class K2CommandCompletionTest : KotlinLightCodeInsightFixtureTestCase() {
assertFalse(elements[0].`as`(CommandCompletionLookupElement::class.java) != null)
}
+ fun testOptimizeImport() {
+ Registry.get("ide.completion.command.force.enabled").setValue(true, getTestRootDisposable())
+ myFixture.configureByText(
+ "x.kt", """"
+ import java.util.List.
+
+ class A {
+ }""".trimIndent()
+ )
+ val elements = myFixture.completeBasic()
+ assertTrue(elements.any { element -> element.lookupString.contains("Optimize im", ignoreCase = true) })
+ }
+
private fun selectItem(item: LookupElement, completionChar: Char = 0.toChar()) {
val lookup: LookupImpl = myFixture.lookup as LookupImpl
lookup.setCurrentItem(item)
diff --git a/plugins/kotlin/plugin/k2/resources/META-INF/command-completion-k2.xml b/plugins/kotlin/plugin/k2/resources/META-INF/command-completion-k2.xml
index d037d861ffb0..766d494041b3 100644
--- a/plugins/kotlin/plugin/k2/resources/META-INF/command-completion-k2.xml
+++ b/plugins/kotlin/plugin/k2/resources/META-INF/command-completion-k2.xml
@@ -11,7 +11,7 @@
+ implementationClass="org.jetbrains.kotlin.idea.completion.impl.k2.contributors.commands.KotlinOptimizeImportCompletionCommandProvider"/>