diff --git a/java/java-impl/resources/META-INF/JavaPlugin.xml b/java/java-impl/resources/META-INF/JavaPlugin.xml
index bd63d0952089..9292ad62e922 100644
--- a/java/java-impl/resources/META-INF/JavaPlugin.xml
+++ b/java/java-impl/resources/META-INF/JavaPlugin.xml
@@ -1509,7 +1509,7 @@
implementationClass="com.intellij.codeInsight.completion.commands.JavaCommandCompletionFactory$JavaIntentionCommandOffsetProvider"/>
-
+
@@ -1520,12 +1520,10 @@
implementationClass="com.intellij.codeInsight.completion.command.commands.PsiElementCommentByLineCompletionCommandProvider"/>
-
+
-
-
-
-
+
+
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaBaseGenerateGetterSetterHandleCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaBaseGenerateGetterSetterHandleCompletionCommand.kt
index bea6f994c5ea..b15ab74a4284 100644
--- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaBaseGenerateGetterSetterHandleCompletionCommand.kt
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaBaseGenerateGetterSetterHandleCompletionCommand.kt
@@ -1,50 +1,69 @@
// Copyright 2000-2024 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.ApplicableCompletionCommand
+import com.intellij.codeInsight.completion.command.CommandCompletionProviderContext
+import com.intellij.codeInsight.completion.command.CommandProvider
+import com.intellij.codeInsight.completion.command.CompletionCommand
+import com.intellij.codeInsight.completion.command.CompletionCommandWithPreview
+import com.intellij.codeInsight.completion.command.HighlightInfoLookup
import com.intellij.codeInsight.completion.command.getCommandContext
import com.intellij.codeInsight.daemon.QuickFixBundle
import com.intellij.codeInsight.intention.QuickFixFactory
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler
+import com.intellij.codeInsight.intention.impl.preview.IntentionPreviewComputable
+import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
import com.intellij.modcommand.ActionContext
import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.colors.EditorColors
import com.intellij.psi.PsiField
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiIdentifier
import com.intellij.psi.util.PsiTreeUtil
import javax.swing.Icon
+internal class GenerateGetterSetterHandleCompletionCommandProvider : CommandProvider {
+ override fun getCommands(context: CommandCompletionProviderContext): List {
+ val element = getCommandContext(context.offset, context.psiFile) ?: return emptyList()
+ if (element !is PsiIdentifier) return emptyList()
+ val field = PsiTreeUtil.getParentOfType(element, PsiField::class.java) ?: return emptyList()
-internal class GenerateGetterSetterHandleCompletionCommand :
- BaseGenerateGetterSetterHandleCompletionCommand(true, true, "Generate 'Getter/Setter'",
- QuickFixBundle.message("create.getter.setter"))
+ val result = mutableListOf()
+ val possibleCases = listOf(
+ GetterSetterCase(true, true, "Generate 'Getter/Setter'", QuickFixBundle.message("create.getter.setter")),
+ GetterSetterCase(false, true, "Generate 'Setter'", QuickFixBundle.message("create.setter")),
+ GetterSetterCase(true, false, "Generate 'Getter'", QuickFixBundle.message("create.getter")),
+ )
+ for (case in possibleCases) {
+ val action = QuickFixFactory.getInstance()
+ .createCreateGetterOrSetterFix(case.generateGetter, case.generateSetter, field)
+ val actionContext = ActionContext.from(context.editor, context.psiFile).withElement(field)
+ val modCommandAction = action.asModCommandAction()
+ if (modCommandAction == null || modCommandAction.getPresentation(actionContext) == null) continue
+ result.add(BaseGenerateGetterSetterHandleCompletionCommand(case.generateGetter,
+ case.generateSetter,
+ case.name,
+ case.i18nName,
+ HighlightInfoLookup(field.textRange, EditorColors.SEARCH_RESULT_ATTRIBUTES, 0)) {
+ IntentionPreviewComputable(context.project, action, context.psiFile, context.editor, context.offset).call()
+ })
+ }
+ return result
+ }
+}
-internal class GenerateSetterHandleCompletionCommand :
- BaseGenerateGetterSetterHandleCompletionCommand(false, true, "Generate 'Setter'",
- QuickFixBundle.message("create.setter"))
+private data class GetterSetterCase(val generateGetter: Boolean, val generateSetter: Boolean, val name: String, val i18nName: String)
-internal class GenerateGetterHandleCompletionCommand :
- BaseGenerateGetterSetterHandleCompletionCommand(true, false, "Generate 'Getter'",
- QuickFixBundle.message("create.getter"))
-
-abstract class BaseGenerateGetterSetterHandleCompletionCommand(
+private class BaseGenerateGetterSetterHandleCompletionCommand(
val generateGetter: Boolean,
val generateSetter: Boolean,
override val name: String,
override val i18nName: String,
-) : ApplicableCompletionCommand() {
+ override val highlightInfo: HighlightInfoLookup?,
+ private val preview: () -> IntentionPreviewInfo?,
+) : CompletionCommand(), CompletionCommandWithPreview {
override val icon: Icon? = null
- override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean {
- val element = getCommandContext(offset, psiFile) ?: return false
- if (element !is PsiIdentifier) return false
- val field = PsiTreeUtil.getParentOfType(element, PsiField::class.java) ?: return false
- val action = QuickFixFactory.getInstance().createCreateGetterOrSetterFix(generateGetter, generateSetter, field)
- val context = ActionContext.from(editor, psiFile).withElement(field)
- return action.asModCommandAction()?.getPresentation(context) != null
- }
-
override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) {
val element = getCommandContext(offset, psiFile) ?: return
val field = PsiTreeUtil.getParentOfType(element, PsiField::class.java) ?: return
@@ -53,4 +72,8 @@ abstract class BaseGenerateGetterSetterHandleCompletionCommand(
@Suppress("DialogTitleCapitalization")
ShowIntentionActionsHandler.chooseActionAndInvoke(psiFile, editor, action, action.text)
}
+
+ override fun getPreview(): IntentionPreviewInfo? {
+ return preview()
+ }
}
\ No newline at end of file
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaDeleteCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaDeleteCompletionCommand.kt
index 78ec9e3e29a1..c4f71e195f59 100644
--- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaDeleteCompletionCommand.kt
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaDeleteCompletionCommand.kt
@@ -2,9 +2,14 @@
package com.intellij.codeInsight.completion.commands.impl
import com.intellij.codeInsight.actions.ReformatCodeProcessor
-import com.intellij.codeInsight.completion.command.ApplicableCompletionCommand
+import com.intellij.codeInsight.completion.command.CommandCompletionProviderContext
+import com.intellij.codeInsight.completion.command.CommandProvider
+import com.intellij.codeInsight.completion.command.CompletionCommand
+import com.intellij.codeInsight.completion.command.CompletionCommandWithPreview
import com.intellij.codeInsight.completion.command.HighlightInfoLookup
import com.intellij.codeInsight.completion.command.getCommandContext
+import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
+import com.intellij.ide.highlighter.JavaFileType
import com.intellij.java.JavaBundle
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.Editor
@@ -21,27 +26,42 @@ import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.annotations.Nls
import javax.swing.Icon
-internal class JavaDeleteCompletionCommand : ApplicableCompletionCommand(), DumbAware {
- private var _highlightInfo: HighlightInfoLookup? = null
+internal class JavaDeleteCompletionCommandProvider : CommandProvider {
+ override fun getCommands(context: CommandCompletionProviderContext): List {
+ val element = getCommandContext(context.offset, context.psiFile) ?: return emptyList()
+ var psiElement = PsiTreeUtil.getParentOfType(element, PsiStatement::class.java, PsiMember::class.java) ?: return emptyList()
+ val hasTheSameOffset = psiElement.textRange.endOffset == context.offset
+ if (!hasTheSameOffset) return emptyList()
+ psiElement = getTopWithTheSameOffset(psiElement, context.offset)
+ val highlightInfo = HighlightInfoLookup(psiElement.textRange, EditorColors.SEARCH_RESULT_ATTRIBUTES, 0)
+ return listOf(createCommand(highlightInfo, psiElement))
+ }
+
+ private fun createCommand(highlightInfo: HighlightInfoLookup, psiElement: PsiElement): CompletionCommand {
+ return JavaDeleteCompletionCommand(highlightInfo, psiElement.text)
+ }
+}
+
+private fun getTopWithTheSameOffset(psiElement: PsiElement, offset: Int): PsiElement {
+ var psiElement1 = psiElement
+ var curElement = psiElement1
+ while (curElement.textRange.endOffset == offset) {
+ psiElement1 = curElement
+ curElement = PsiTreeUtil.getParentOfType(curElement, PsiStatement::class.java, PsiMember::class.java) ?: break
+ }
+ return psiElement1
+}
+
+private class JavaDeleteCompletionCommand(
+ override val highlightInfo: HighlightInfoLookup?,
+ private val originalText: String,
+) : CompletionCommand(), CompletionCommandWithPreview, DumbAware {
override val name: String
get() = "Delete element"
override val i18nName: @Nls String
get() = JavaBundle.message("command.completion.delete.element.text")
override val icon: Icon?
get() = null
- override val highlightInfo: HighlightInfoLookup?
- get() = _highlightInfo
-
-
- override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean {
- val element = getCommandContext(offset, psiFile) ?: return false
- var psiElement = PsiTreeUtil.getParentOfType(element, PsiStatement::class.java, PsiMember::class.java) ?: return false
- val hasTheSameOffset = psiElement.textRange.endOffset == offset
- if (!hasTheSameOffset) return false
- psiElement = getTopWithTheSameOffset(psiElement, offset)
- _highlightInfo = HighlightInfoLookup(psiElement.textRange, EditorColors.DELETED_TEXT_ATTRIBUTES, 0)
- return true
- }
override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) {
val element = getCommandContext(offset, psiFile) ?: return
@@ -54,15 +74,10 @@ internal class JavaDeleteCompletionCommand : ApplicableCompletionCommand(), Dumb
parent.element?.let {
ReformatCodeProcessor(psiFile, arrayOf(it.textRange)).run()
}
- }, psiFile) }
+ }, psiFile)
+ }
- private fun getTopWithTheSameOffset(psiElement: PsiElement, offset: Int): PsiElement {
- var psiElement1 = psiElement
- var curElement = psiElement1
- while (curElement.textRange.endOffset == offset) {
- psiElement1 = curElement
- curElement = PsiTreeUtil.getParentOfType(curElement, PsiStatement::class.java, PsiMember::class.java) ?: break
- }
- return psiElement1
+ override fun getPreview(): IntentionPreviewInfo? {
+ return IntentionPreviewInfo.CustomDiff(JavaFileType.INSTANCE, originalText, "")
}
}
\ No newline at end of file
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaInlineVariableCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaInlineVariableCompletionCommand.kt
index a39f7aa72b67..7d9efd1386dd 100644
--- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaInlineVariableCompletionCommand.kt
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaInlineVariableCompletionCommand.kt
@@ -1,12 +1,20 @@
// Copyright 2000-2024 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.ApplicableCompletionCommand
+import com.intellij.codeInsight.completion.command.CommandCompletionProviderContext
+import com.intellij.codeInsight.completion.command.CommandProvider
+import com.intellij.codeInsight.completion.command.CompletionCommand
+import com.intellij.codeInsight.completion.command.CompletionCommandWithPreview
+import com.intellij.codeInsight.completion.command.HighlightInfoLookup
import com.intellij.codeInsight.completion.command.getCommandContext
+import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
import com.intellij.icons.AllIcons
+import com.intellij.idea.ActionsBundle
import com.intellij.java.JavaBundle
import com.intellij.lang.refactoring.InlineActionHandler
import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.colors.EditorColors
+import com.intellij.openapi.project.DumbAware
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiIdentifier
import com.intellij.psi.PsiJavaCodeReferenceElement
@@ -14,47 +22,55 @@ import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.annotations.Nls
import javax.swing.Icon
-internal class JavaInlineVariableCompletionCommand : ApplicableCompletionCommand() {
- override val name: String
- get() = "Inline"
+internal class JavaInlineVariableCompletionCommandProvider : CommandProvider {
+ override fun getCommands(context: CommandCompletionProviderContext): List {
+ val element = getCommandContext(context.offset, context.psiFile) ?: return emptyList()
+ if (element !is PsiIdentifier) return emptyList()
- override val i18nName: @Nls String
- get() = JavaBundle.message("command.completion.inline.text")
+ val javaRef = PsiTreeUtil.getParentOfType(element, PsiJavaCodeReferenceElement::class.java) ?: return emptyList()
+ val psiElement = javaRef.resolve() ?: return emptyList()
- override val icon: Icon
- get() = AllIcons.Actions.RefactoringBulb // Use an appropriate icon from IntelliJ's icon set
-
- override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean {
- if (editor == null) return false
- val context = getCommandContext(offset, psiFile)
- if (context !is PsiIdentifier) {
- return false
- }
- val javaRef = PsiTreeUtil.getParentOfType(context, PsiJavaCodeReferenceElement::class.java)
-
- val psiElement = javaRef?.resolve() ?: return false
+ // Check if any inline handler can handle this element
+ val editor = context.editor
val extensionList = InlineActionHandler.EP_NAME.extensionList
for (extension in extensionList) {
try {
if (extension.canInlineElementInEditor(psiElement, editor)) {
- return true
+ val highlightInfo = element.textRange?.let {
+ HighlightInfoLookup(it, EditorColors.SEARCH_RESULT_ATTRIBUTES, 0)
+ }
+ return listOf(JavaInlineVariableCompletionCommand(highlightInfo))
}
}
catch (_: Exception) {
continue
}
}
- return false
+
+ return emptyList()
}
+}
+
+private class JavaInlineVariableCompletionCommand(
+ override val highlightInfo: HighlightInfoLookup?,
+) : CompletionCommand(), DumbAware, CompletionCommandWithPreview {
+ override val name: String
+ get() = "Inline"
+
+ override val i18nName: @Nls String
+ get() = JavaBundle.message("command.completion.inline.text")
+
+
+ override val icon: Icon?
+ get() = AllIcons.Actions.RefactoringBulb // Use an appropriate icon from IntelliJ's icon set
override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) {
- val context = getCommandContext(offset, psiFile)
- if (context !is PsiIdentifier) {
- return
- }
- val javaRef = PsiTreeUtil.getParentOfType(context, PsiJavaCodeReferenceElement::class.java)
+ if (editor == null) return
+ val element = getCommandContext(offset, psiFile) ?: return
+ if (element !is PsiIdentifier) return
- val psiElement = javaRef?.resolve() ?: return
+ val javaRef = PsiTreeUtil.getParentOfType(element, PsiJavaCodeReferenceElement::class.java) ?: return
+ val psiElement = javaRef.resolve() ?: return
val extensionList = InlineActionHandler.EP_NAME.extensionList
for (extension in extensionList) {
@@ -63,7 +79,9 @@ internal class JavaInlineVariableCompletionCommand : ApplicableCompletionCommand
return
}
}
- return
}
+ override fun getPreview(): IntentionPreviewInfo? {
+ return IntentionPreviewInfo.Html(ActionsBundle.message("action.Inline.description"))
+ }
}
\ No newline at end of file
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaIntroduceVariableCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaIntroduceVariableCommand.kt
index ee63b2fb9e86..d26dcbc6a6a5 100644
--- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaIntroduceVariableCommand.kt
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaIntroduceVariableCommand.kt
@@ -1,11 +1,16 @@
// 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.ApplicableCompletionCommand
+import com.intellij.codeInsight.completion.command.CommandCompletionProviderContext
+import com.intellij.codeInsight.completion.command.CommandProvider
+import com.intellij.codeInsight.completion.command.CompletionCommand
+import com.intellij.codeInsight.completion.command.CompletionCommandWithPreview
import com.intellij.codeInsight.completion.command.getDataContext
import com.intellij.codeInsight.completion.command.getTargetContext
+import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
import com.intellij.icons.AllIcons
import com.intellij.ide.DataManager
+import com.intellij.idea.ActionsBundle
import com.intellij.lang.ContextAwareActionHandler
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.ActionUiKind
@@ -20,7 +25,26 @@ import com.intellij.refactoring.actions.IntroduceVariableAction
import org.jetbrains.annotations.Nls
import javax.swing.Icon
-internal class JavaIntroduceVariableCommand : ApplicableCompletionCommand() {
+internal class JavaIntroduceVariableCommandProvider : CommandProvider {
+ override fun getCommands(context: CommandCompletionProviderContext): List {
+ val editor = context.editor
+ val psiFile = context.psiFile
+ val offset = context.offset
+ val factory = JavaRefactoringActionHandlerFactory.getInstance()
+ val variableHandler = factory.createIntroduceVariableHandler()
+ if (variableHandler is ContextAwareActionHandler &&
+ variableHandler.isAvailableForQuickList(editor, psiFile, getDataContext(psiFile, editor, getTargetContext(offset, editor)))) {
+ return listOf(createCommand())
+ }
+ return emptyList()
+ }
+
+ private fun createCommand(): CompletionCommand {
+ return JavaIntroduceVariableCommand()
+ }
+}
+
+internal class JavaIntroduceVariableCommand : CompletionCommand(), CompletionCommandWithPreview {
override val name: String
get() = "Introduce variable"
@@ -30,16 +54,6 @@ internal class JavaIntroduceVariableCommand : ApplicableCompletionCommand() {
override val icon: Icon
get() = AllIcons.Nodes.Variable
- override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean {
- if (editor == null) return false
- val factory = JavaRefactoringActionHandlerFactory.getInstance()
- val variableHandler = factory.createIntroduceVariableHandler()
- if (variableHandler is ContextAwareActionHandler) {
- return variableHandler.isAvailableForQuickList(editor, psiFile, getDataContext(psiFile, editor, getTargetContext(offset, editor)))
- }
- return false
- }
-
override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) {
val action = IntroduceVariableAction()
if (editor == null) return
@@ -51,4 +65,8 @@ internal class JavaIntroduceVariableCommand : ApplicableCompletionCommand() {
ActionUtil.performActionDumbAwareWithCallbacks(action, event)
}
}
+
+ override fun getPreview(): IntentionPreviewInfo? {
+ return IntentionPreviewInfo.Html(ActionsBundle.message("action.IntroduceVariable.description"))
+ }
}
\ No newline at end of file
diff --git a/platform/analysis-api/api-dump-experimental.txt b/platform/analysis-api/api-dump-experimental.txt
index 1984f296fb37..c9c3be7cece4 100644
--- a/platform/analysis-api/api-dump-experimental.txt
+++ b/platform/analysis-api/api-dump-experimental.txt
@@ -1,9 +1,3 @@
-*a:com.intellij.codeInsight.completion.command.ApplicableCompletionCommand
-- com.intellij.codeInsight.completion.command.CompletionCommand
-- com.intellij.openapi.project.PossiblyDumbAware
-- ():V
-- a:isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z
-- supportsReadOnly():Z
*:com.intellij.codeInsight.completion.command.CommandCompletionFactory
- com.intellij.openapi.project.PossiblyDumbAware
- commandProviders(com.intellij.openapi.project.Project,com.intellij.lang.Language):java.util.List
diff --git a/platform/analysis-api/src/com/intellij/codeInsight/completion/command/CompletionCommand.kt b/platform/analysis-api/src/com/intellij/codeInsight/completion/command/CompletionCommand.kt
index ca0badb6e702..0602586c1bfa 100644
--- a/platform/analysis-api/src/com/intellij/codeInsight/completion/command/CompletionCommand.kt
+++ b/platform/analysis-api/src/com/intellij/codeInsight/completion/command/CompletionCommand.kt
@@ -5,12 +5,10 @@ import com.intellij.codeInsight.completion.PrefixMatcher
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.colors.TextAttributesKey
-import com.intellij.openapi.project.PossiblyDumbAware
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.util.concurrency.annotations.RequiresEdt
-import com.intellij.util.concurrency.annotations.RequiresReadLock
import org.jetbrains.annotations.Nls
import javax.swing.Icon
@@ -42,7 +40,19 @@ abstract class CompletionCommand {
* can be left null, in which case a default priority will be assumed.
*/
open val priority: Int? = null
+
+ /**
+ * Provides additional information about the command that can be displayed
+ * in the completion popup. This information helps users understand the
+ * purpose or effect of the command.
+ */
open val additionalInfo: String? = null
+
+ /**
+ * Specifies how the command should be highlighted in the editor.
+ * Contains information about the text range to highlight, the highlighting style,
+ * and the priority of the highlight effect.
+ */
open val highlightInfo: HighlightInfoLookup? = null
/**
@@ -85,38 +95,6 @@ fun getCommandContext(offset: Int, psiFile: PsiFile): PsiElement? {
return (if (offset == 0) psiFile.findElementAt(offset) else psiFile.findElementAt(offset - 1))
}
-/**
- * Represents an abstract base class for completion commands that are applicable under specific conditions.
- * This class extends the functionality of `CompletionCommand` and introduces methods to evaluate the
- * applicability of the command in a given context.
- *
- * Should be stateless and can be applied either to physical and non-physical classes
- * Should implement DumbAware to support DumbMode
- */
-@Deprecated("Use providers instead")
-abstract class ApplicableCompletionCommand : CompletionCommand(), PossiblyDumbAware {
-
- /**
- * Indicates whether the implementation supports non-written files.
- * (For example, a command can navigate to another file)
- *
- * @return true if non-written files are supported; false otherwise.
- */
- @Deprecated("Use providers instead")
- open fun supportsReadOnly(): Boolean = false
-
- /**
- * Determines whether the command is applicable based on the given context.
- * Can be called on non-physical classes and imaginary editors
- *
- * @param offset The offset in the file where the applicability should be checked.
- * @param psiFile The PSI file where the applicability should be evaluated.
- * @param editor The editor. Can be null. Used only for compatibility with old actions
- */
- @RequiresReadLock
- abstract fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean
-}
-
data class HighlightInfoLookup(
val range: TextRange,
val attributesKey: TextAttributesKey,
diff --git a/platform/lang-impl/api-dump-experimental.txt b/platform/lang-impl/api-dump-experimental.txt
index e2f3532ced2d..5cb624aacb2f 100644
--- a/platform/lang-impl/api-dump-experimental.txt
+++ b/platform/lang-impl/api-dump-experimental.txt
@@ -149,21 +149,6 @@ c:com.intellij.codeInsight.actions.VcsFacade
- documentationTarget(com.intellij.psi.PsiFile,com.intellij.codeInsight.lookup.LookupElement,I):com.intellij.platform.backend.documentation.DocumentationTarget
*f:com.intellij.codeInsight.completion.command.CommandCompletionDocumentationProviderKt
- sf:combineFragments(java.util.List,java.util.List):java.util.List
-*a:com.intellij.codeInsight.completion.command.commands.AbstractActionCompletionCommand
-- com.intellij.codeInsight.completion.command.ApplicableCompletionCommand
-- com.intellij.openapi.project.DumbAware
-- (java.lang.String,java.lang.String,java.lang.String,javax.swing.Icon,java.lang.Integer):V
-- b:(java.lang.String,java.lang.String,java.lang.String,javax.swing.Icon,java.lang.Integer,I,kotlin.jvm.internal.DefaultConstructorMarker):V
-- execute(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):V
-- f:getActionId():java.lang.String
-- getAdditionalInfo():java.lang.String
-- getI18nName():java.lang.String
-- getIcon():javax.swing.Icon
-- getName():java.lang.String
-- getPriority():java.lang.Integer
-- isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z
-- pf:isApplicableToProject(I,com.intellij.psi.PsiFile):Z
-- f:setActionId(java.lang.String):V
*a:com.intellij.codeInsight.completion.command.commands.AbstractChangeSignatureCompletionCommandProvider
- com.intellij.codeInsight.completion.command.commands.ActionCommandProvider
- ():V
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractActionCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractActionCompletionCommand.kt
index a053ae44fc21..a5a045503562 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractActionCompletionCommand.kt
+++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractActionCompletionCommand.kt
@@ -1,7 +1,6 @@
// 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.ApplicableCompletionCommand
import com.intellij.codeInsight.completion.command.CommandCompletionProviderContext
import com.intellij.codeInsight.completion.command.CommandProvider
import com.intellij.codeInsight.completion.command.CompletionCommand
@@ -29,6 +28,18 @@ import org.jetbrains.annotations.Nls
import javax.swing.Icon
+/**
+ * Provides completion commands that execute IDE actions identified by their action IDs.
+ * This provider creates commands that integrate with IntelliJ Platform's action system,
+ * allowing actions to be triggered through code completion features.
+ *
+ * @property actionId The unique identifier of the IDE action to be executed
+ * @property name The display name of the command
+ * @property i18nName The internationalized name of the command
+ * @property icon Optional icon to be displayed with the command
+ * @property priority Optional priority value affecting command ordering
+ * @property previewText Optional preview text shown when the command is selected
+ */
open class ActionCommandProvider(
@field:Language("devkit-action-id") var actionId: String,
val name: String,
@@ -38,12 +49,29 @@ open class ActionCommandProvider(
val previewText: @Nls String?,
) : CommandProvider, DumbAware {
+ /**
+ * Creates and returns a list of completion commands based on the provided context.
+ * The method checks if the action is applicable in the current context and creates
+ * the appropriate command if conditions are met.
+ *
+ * @param context The context containing information about the completion environment
+ * @return A list of completion commands, empty if the action is not applicable
+ */
override fun getCommands(context: CommandCompletionProviderContext): List {
if (!isApplicable(context.offset, context.psiFile, context.editor)) return emptyList()
val element = createCommand(context) ?: return emptyList()
return listOf(element)
}
+
+ /**
+ * Creates a new action completion command based on the provided context.
+ * This method instantiates an [ActionCompletionCommand] with the provider's configuration
+ * including action ID, name, icon, and preview settings.
+ *
+ * @param context The context containing information about the completion environment
+ * @return A new [ActionCompletionCommand] instance, or null if the command cannot be created
+ */
protected open fun createCommand(context: CommandCompletionProviderContext): ActionCompletionCommand? =
ActionCompletionCommand(actionId = actionId,
name = name,
@@ -52,6 +80,14 @@ open class ActionCommandProvider(
priority = priority,
previewText = previewText)
+ /**
+ * Checks whether the action associated with this provider is applicable in the current context.
+ *
+ * @param offset The caret offset in the editor
+ * @param psiFile The PSI file being edited
+ * @param editor The current editor instance, may be null
+ * @return true if the action can be applied in the current context, false otherwise
+ */
protected open fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean {
val action: AnAction? = ActionManager.getInstance().getAction(actionId)
if (action == null || editor == null) return false
@@ -129,76 +165,4 @@ open class ActionCompletionCommand(
if (previewText == null) return null
return IntentionPreviewInfo.Html(previewText)
}
-}
-
-/**
- * Represents an abstract action completion command that triggers a specific IDE action
- * identified by its `actionId`. This class allows integration of IntelliJ Platform actions
- * as part of code completion features, ensuring that the action is applicable and
- * executable within a given code editor context.
- */
-@Deprecated("Use providers instead")
-abstract class AbstractActionCompletionCommand(
- @Language("devkit-action-id") var actionId: String,
- override val name: String,
- override val i18nName: @Nls String,
- override val icon: Icon?,
- override val priority: Int? = null,
-) : ApplicableCompletionCommand(), DumbAware {
- private val action: AnAction? = ActionManager.getInstance().getAction(actionId)
-
- override val additionalInfo: String?
- get() {
- val shortcutText = KeymapUtil.getFirstKeyboardShortcutText(actionId)
- if (shortcutText.isNotEmpty()) {
- return shortcutText
- }
- return null
- }
-
- override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean {
- val action = action ?: return false
- if (editor == null) return false
- if (!DumbService.Companion.getInstance(psiFile.project).isUsableInCurrentContext(action)) return false
- val context = getTargetContext(offset, editor)
- val dataContext = getDataContext(psiFile, editor, context)
- val presentation: Presentation = action.templatePresentation.clone()
- val event = AnActionEvent.createEvent(action, dataContext, presentation, ActionPlaces.ACTION_PLACE_QUICK_LIST_POPUP_ACTION,
- ActionUiKind.Companion.NONE, null)
- if (ActionUtil.performDumbAwareUpdate(action, event, false)) {
- return false
- }
- return event.presentation.isEnabled && event.presentation.isVisible
- }
-
- override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) {
- val action = action ?: return
- if (editor == null) return
- val dataContext = DataManager.getInstance().getDataContext(editor.getComponent())
- val presentation: Presentation = action.templatePresentation.clone()
- val event = AnActionEvent.createEvent(action, dataContext, presentation, ActionPlaces.ACTION_PLACE_QUICK_LIST_POPUP_ACTION,
- ActionUiKind.Companion.NONE, null)
- if (ActionUtil.lastUpdateAndCheckDumb(action, event, false)) {
- ActionUtil.performActionDumbAwareWithCallbacks(action, event)
- }
- }
-
- /**
- * Determines if the action associated with this command can be applied to the given project context
- * by analyzing the specified offset and the PsiFile structure.
- * It is expected to be called outside any meaningful PsiElements
- */
- protected fun isApplicableToProject(offset: Int, psiFile: PsiFile): Boolean {
- if (offset - 1 < 0) return true
- val element = psiFile.findElementAt(offset - 1)
- if (element is PsiComment) return true
- val fileDocument = psiFile.fileDocument
- val lineNumber = fileDocument.getLineNumber(offset)
- val lineStartOffset = fileDocument.getLineStartOffset(lineNumber)
- for (ch in fileDocument.immutableCharSequence.subSequence(lineStartOffset, offset)) {
- if (!ch.isWhitespace()) return false
- if (ch == '\n') return true
- }
- return true
- }
}
\ No newline at end of file
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/DirectIntentionCommandProvider.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/DirectIntentionCommandProvider.kt
index 19f4dd90b835..79dc7b75bd82 100644
--- a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/DirectIntentionCommandProvider.kt
+++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/DirectIntentionCommandProvider.kt
@@ -1,12 +1,28 @@
// 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.*
+import com.intellij.codeInsight.completion.command.CommandCompletionProviderContext
+import com.intellij.codeInsight.completion.command.CommandCompletionUnsupportedOperationException
+import com.intellij.codeInsight.completion.command.CommandProvider
+import com.intellij.codeInsight.completion.command.CompletionCommand
+import com.intellij.codeInsight.completion.command.HighlightInfoLookup
+import com.intellij.codeInsight.completion.command.MyEditor
+import com.intellij.codeInsight.completion.command.commandCompletionEnabled
import com.intellij.codeInsight.daemon.HighlightDisplayKey
-import com.intellij.codeInsight.daemon.impl.*
+import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator
+import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.daemon.impl.HighlightInfo.IntentionActionDescriptor
+import com.intellij.codeInsight.daemon.impl.HighlightVisitorBasedInspection
+import com.intellij.codeInsight.daemon.impl.IntentionActionFilter
+import com.intellij.codeInsight.daemon.impl.IntentionMenuContributor
+import com.intellij.codeInsight.daemon.impl.SeverityRegistrar
+import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass
import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass.IntentionsInfo
-import com.intellij.codeInsight.intention.*
+import com.intellij.codeInsight.intention.CommonIntentionAction
+import com.intellij.codeInsight.intention.EmptyIntentionAction
+import com.intellij.codeInsight.intention.IntentionAction
+import com.intellij.codeInsight.intention.IntentionActionDelegate
+import com.intellij.codeInsight.intention.IntentionManager
import com.intellij.codeInsight.intention.impl.CachedIntentions
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler
import com.intellij.codeInsight.intention.impl.preview.IntentionPreviewComputable
@@ -25,6 +41,7 @@ import com.intellij.lang.LanguageExtension
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.lang.annotation.HighlightSeverity.INFORMATION
import com.intellij.lang.injection.InjectedLanguageManager
+import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.readAction
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
@@ -258,7 +275,9 @@ internal class DirectIntentionCommandProvider : CommandProvider {
val isInjected = topLevelFile != psiFile
val indicator = DaemonProgressIndicator()
val errorHighlightings: List? = jobToIndicator(coroutineContext.job, indicator) {
- HighlightVisitorBasedInspection.runAnnotatorsInGeneralHighlighting(topLevelFile, true, true, true)
+ ApplicationManager.getApplication().runReadAction?> {
+ HighlightVisitorBasedInspection.runAnnotatorsInGeneralHighlighting(topLevelFile, true, true, true)
+ }
}
if (errorHighlightings == null) return@readAction
var insideRange = getLineRange(topLevelFile, topLevelOffset)
diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/ExtensionBasedCommandProvider.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/ExtensionBasedCommandProvider.kt
deleted file mode 100644
index bf86a12704da..000000000000
--- a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/ExtensionBasedCommandProvider.kt
+++ /dev/null
@@ -1,34 +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.codeInsight.completion.command.ApplicableCompletionCommand
-import com.intellij.codeInsight.completion.command.CommandCompletionProviderContext
-import com.intellij.codeInsight.completion.command.CommandProvider
-import com.intellij.codeInsight.completion.command.CompletionCommand
-import com.intellij.codeInsight.completion.command.commandCompletionEnabled
-import com.intellij.lang.LanguageExtension
-import com.intellij.openapi.project.DumbAware
-import com.intellij.openapi.project.DumbService
-
-/**
- * An implementation of the CommandProvider interface that supplies completion commands
- * registered through extension points ('com.intellij.codeInsight.completion.applicable.command') for specific programming languages.
- */
-@Deprecated("use providers")
-internal class ExtensionPointCommandProvider : CommandProvider, DumbAware {
- override fun getCommands(context: CommandCompletionProviderContext): List {
- if (!commandCompletionEnabled()) return emptyList()
- val completionCommands =
- DumbService.getInstance(context.project)
- .filterByDumbAwareness(EP_NAME.allForLanguage(context.psiFile.language))
- return completionCommands.filter {
- !(context.isReadOnly && !it.supportsReadOnly()) && it.isApplicable(context.offset, context.psiFile, context.editor)
- }
- }
-
- override fun supportsReadOnly(): Boolean {
- return true
- }
-}
-
-private val EP_NAME: LanguageExtension = LanguageExtension("com.intellij.codeInsight.completion.applicable.command")
diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml
index 5572299df74c..5e96e3a20080 100644
--- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml
+++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml
@@ -763,10 +763,6 @@
beanClass="com.intellij.lang.LanguageExtensionPoint" dynamic="true">
-
-
-
diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml
index 493b28a9802c..b62d821676aa 100644
--- a/platform/platform-resources/src/META-INF/LangExtensions.xml
+++ b/platform/platform-resources/src/META-INF/LangExtensions.xml
@@ -755,7 +755,6 @@
-
diff --git a/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/commands/KotlinDeleteCompletionCommand.kt b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/commands/KotlinDeleteCompletionCommand.kt
index 12d3abb09fbe..cf2791dc2f63 100644
--- a/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/commands/KotlinDeleteCompletionCommand.kt
+++ b/plugins/kotlin/completion/impl-k2/src/org/jetbrains/kotlin/idea/completion/impl/k2/contributors/commands/KotlinDeleteCompletionCommand.kt
@@ -2,9 +2,8 @@
package org.jetbrains.kotlin.idea.completion.impl.k2.contributors.commands
import com.intellij.codeInsight.actions.ReformatCodeProcessor
-import com.intellij.codeInsight.completion.command.ApplicableCompletionCommand
-import com.intellij.codeInsight.completion.command.HighlightInfoLookup
-import com.intellij.codeInsight.completion.command.getCommandContext
+import com.intellij.codeInsight.completion.command.*
+import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
import com.intellij.idea.ActionsBundle
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.Editor
@@ -13,52 +12,63 @@ import com.intellij.openapi.project.DumbAware
import com.intellij.psi.*
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.annotations.Nls
+import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import javax.swing.Icon
-class KotlinDeleteCompletionCommand : ApplicableCompletionCommand(), DumbAware {
- private var _highlightInfo: HighlightInfoLookup? = null
- override val name: String
- get() = "Delete element"
- override val i18nName: @Nls String
- get() = ActionsBundle.message("action.EditorDelete.text")
- override val icon: Icon?
- get() = null
- override val highlightInfo: HighlightInfoLookup?
- get() = _highlightInfo
+internal class KotlinDeleteCompletionCommandProvider : CommandProvider {
+ override fun getCommands(context: CommandCompletionProviderContext): List {
+ val element = getCommandContext(context.offset, context.psiFile) ?: return emptyList()
+ var psiElement = PsiTreeUtil.getParentOfType(element, KtExpression::class.java, KtNamedDeclaration::class.java) ?: return emptyList()
+ val hasTheSameOffset = psiElement.textRange.endOffset == context.offset
+ if (!hasTheSameOffset) return emptyList()
+ psiElement = getTopWithTheSameOffset(psiElement, context.offset)
+ val highlightInfo = HighlightInfoLookup(psiElement.textRange, EditorColors.SEARCH_RESULT_ATTRIBUTES, 0)
+ return listOf(createCommand(highlightInfo, psiElement))
+ }
- override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean {
- val element = getCommandContext(offset, psiFile) ?: return false
- var psiElement = PsiTreeUtil.getParentOfType(element, KtExpression::class.java, KtNamedDeclaration::class.java) ?: return false
- val hasTheSameOffset = psiElement.textRange.endOffset == offset
- if (!hasTheSameOffset) return false
- psiElement = getTopWithTheSameOffset(psiElement, offset)
- _highlightInfo = HighlightInfoLookup(psiElement.textRange, EditorColors.DELETED_TEXT_ATTRIBUTES, 0)
- return true
- }
+ private fun createCommand(highlightInfo: HighlightInfoLookup, psiElement: PsiElement): CompletionCommand {
+ return KotlinDeleteCompletionCommand(highlightInfo, psiElement.text)
+ }
+}
- override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) {
- val element = getCommandContext(offset, psiFile) ?: return
- var psiElement = PsiTreeUtil.getParentOfType(element, KtExpression::class.java, KtNamedDeclaration::class.java) ?: return
- psiElement = getTopWithTheSameOffset(psiElement, offset)
- WriteCommandAction.runWriteCommandAction(psiFile.project, null, null, {
- val parent: SmartPsiElementPointer = SmartPointerManager.createPointer(psiElement.parent ?: psiFile)
- psiElement.delete()
- PsiDocumentManager.getInstance(psiFile.project).commitDocument(psiFile.fileDocument)
- parent.element?.let {
- ReformatCodeProcessor(psiFile, arrayOf(it.textRange)).run()
- }
- }, psiFile)
+private fun getTopWithTheSameOffset(psiElement: KtExpression, offset: Int): KtExpression {
+ var psiElement1 = psiElement
+ var curElement = psiElement1
+ while (curElement.textRange.endOffset == offset) {
+ psiElement1 = curElement
+ curElement = PsiTreeUtil.getParentOfType(curElement, KtExpression::class.java, KtNamedDeclaration::class.java) ?: break
}
+ return psiElement1
+}
- private fun getTopWithTheSameOffset(psiElement: KtExpression, offset: Int): KtExpression {
- var psiElement1 = psiElement
- var curElement = psiElement1
- while (curElement.textRange.endOffset == offset) {
- psiElement1 = curElement
- curElement = PsiTreeUtil.getParentOfType(curElement, KtExpression::class.java, KtNamedDeclaration::class.java) ?: break
- }
- return psiElement1
- }
+private class KotlinDeleteCompletionCommand(
+ override val highlightInfo: HighlightInfoLookup?,
+ private val originalText: String,
+) : CompletionCommand(), CompletionCommandWithPreview, DumbAware {
+ override val name: String
+ get() = "Delete element"
+ override val i18nName: @Nls String
+ get() = ActionsBundle.message("action.EditorDelete.text")
+ override val icon: Icon?
+ get() = null
+
+ override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) {
+ val element = getCommandContext(offset, psiFile) ?: return
+ var psiElement = PsiTreeUtil.getParentOfType(element, KtExpression::class.java, KtNamedDeclaration::class.java) ?: return
+ psiElement = getTopWithTheSameOffset(psiElement, offset)
+ WriteCommandAction.runWriteCommandAction(psiFile.project, null, null, {
+ val parent: SmartPsiElementPointer = SmartPointerManager.createPointer(psiElement.parent ?: psiFile)
+ psiElement.delete()
+ PsiDocumentManager.getInstance(psiFile.project).commitDocument(psiFile.fileDocument)
+ parent.element?.let {
+ ReformatCodeProcessor(psiFile, arrayOf(it.textRange)).run()
+ }
+ }, psiFile)
+ }
+
+ override fun getPreview(): IntentionPreviewInfo? {
+ return IntentionPreviewInfo.CustomDiff(KotlinFileType.INSTANCE, originalText, "")
+ }
}
\ No newline at end of file
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 eb7d6736ec7d..2268cd78ae65 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
@@ -27,8 +27,8 @@
implementationClass="com.intellij.codeInsight.completion.command.commands.PsiElementCommentByLineCompletionCommandProvider"/>
-
+