[command-completion] IDEA-375959 Command completion: optimize imports is available only after imports section, but not on the line with imports

GitOrigin-RevId: eee1a3077747d0e7e75f98efed5c855e3f28dc52
This commit is contained in:
Mikhail Pyltsin
2025-07-28 17:12:09 +00:00
committed by intellij-monorepo-bot
parent 1051cd29ca
commit f3dae0e5ed
9 changed files with 126 additions and 24 deletions
@@ -1472,7 +1472,7 @@
<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaGoToDeclarationCommandCompletionProvider" />
<codeInsight.completion.command.provider language="JAVA"
implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaRenameActionCommandProvider"/>
<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.command.commands.OptimizeImportCompletionCommandProvider" />
<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaOptimizeImportCompletionCommandProvider" />
<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaFormatCodeCompletionCommandProvider" />
<!--<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.command.commands.LineCommentCompletionCommandProvider" />-->
<codeInsight.completion.command.provider language="JAVA"
@@ -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 com.intellij.codeInsight.completion.commands.impl
import com.intellij.codeInsight.completion.command.commands.AbstractOptimizeImportCompletionCommandProvider
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiImportList
import com.intellij.psi.PsiJavaFile
import com.intellij.psi.util.parentOfType
public class JavaOptimizeImportCompletionCommandProvider : AbstractOptimizeImportCompletionCommandProvider() {
override fun isImportList(psiFile: PsiFile, offset: Int): Boolean {
if (offset - 1 < 0) return false
val element = psiFile.findElementAt(offset - 1)
return element?.parentOfType<PsiImportList>(withSelf = true) != null
}
override fun getTextRangeImportList(psiFile: PsiFile, offset: Int): TextRange? {
if (psiFile is PsiJavaFile) return psiFile.importList?.textRange
return null
}
}
@@ -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;.<caret>
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, """
@@ -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
- <init>():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
- <init>():V
@@ -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?
}
@@ -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)
}
}
@@ -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<KtImportList>(withSelf = true) != null
}
override fun getTextRangeImportList(psiFile: PsiFile, offset: Int): TextRange? {
if (psiFile is KtFile) return psiFile.importList?.textRange
return null
}
}
@@ -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.<caret>
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)
@@ -11,7 +11,7 @@
<codeInsight.completion.command.provider language="kotlin"
implementationClass="org.jetbrains.kotlin.idea.completion.impl.k2.contributors.commands.KotlinRenameActionCommandProvider"/>
<codeInsight.completion.command.provider language="kotlin"
implementationClass="com.intellij.codeInsight.completion.command.commands.OptimizeImportCompletionCommandProvider"/>
implementationClass="org.jetbrains.kotlin.idea.completion.impl.k2.contributors.commands.KotlinOptimizeImportCompletionCommandProvider"/>
<!-- <codeInsight.completion.command.provider language="kotlin"-->
<!-- implementationClass="com.intellij.codeInsight.completion.command.commands.LineCommentCompletionCommandProvider"/>-->
<codeInsight.completion.command.provider language="kotlin"