[command-completion] IDEA-369839 part of Support intention preview for all commands in command completion

GitOrigin-RevId: ece703b31a5e8ef765802e69b09f4629811deeb4
This commit is contained in:
Mikhail Pyltsin
2025-04-02 20:17:34 +00:00
committed by intellij-monorepo-bot
parent d7600f6acd
commit 8e4ed43a3f
15 changed files with 291 additions and 308 deletions
@@ -1509,7 +1509,7 @@
implementationClass="com.intellij.codeInsight.completion.commands.JavaCommandCompletionFactory$JavaIntentionCommandOffsetProvider"/>
<!--<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.command.commands.SimpleRunMarkerCommandProvider" />-->
<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaGenerateCommandCompletion" />
<codeInsight.completion.applicable.command language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaInlineVariableCompletionCommand" />
<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaInlineVariableCompletionCommandProvider" />
<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.command.commands.RenameActionCommandProvider"/>
@@ -1520,12 +1520,10 @@
implementationClass="com.intellij.codeInsight.completion.command.commands.PsiElementCommentByLineCompletionCommandProvider"/>
<codeInsight.completion.command.provider language="JAVA"
implementationClass="com.intellij.codeInsight.completion.command.commands.PsiElementCommentByBlockCompletionCommandProvider"/>
<codeInsight.completion.applicable.command language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaIntroduceVariableCommand" />
<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaIntroduceVariableCommandProvider" />
<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaShowUsagesActionCompletionCommandProvider" />
<codeInsight.completion.applicable.command language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.GenerateGetterSetterHandleCompletionCommand" />
<codeInsight.completion.applicable.command language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.GenerateSetterHandleCompletionCommand" />
<codeInsight.completion.applicable.command language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.GenerateGetterHandleCompletionCommand" />
<codeInsight.completion.applicable.command language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaDeleteCompletionCommand" />
<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.GenerateGetterSetterHandleCompletionCommandProvider" />
<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaDeleteCompletionCommandProvider" />
<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaCopyFQNCompletionCommandProvider" />
<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaGoToImplementationCommandCompletionProvider" />
<codeInsight.completion.command.provider language="JAVA" implementationClass="com.intellij.codeInsight.completion.commands.impl.JavaChangeSignatureCompletionCommandProvider" />
@@ -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<CompletionCommand> {
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<CompletionCommand>()
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()
}
}
@@ -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<CompletionCommand> {
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, "")
}
}
@@ -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<CompletionCommand> {
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"))
}
}
@@ -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<CompletionCommand> {
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"))
}
}
@@ -1,9 +1,3 @@
*a:com.intellij.codeInsight.completion.command.ApplicableCompletionCommand
- com.intellij.codeInsight.completion.command.CompletionCommand
- com.intellij.openapi.project.PossiblyDumbAware
- <init>():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
@@ -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,
@@ -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
- <init>(java.lang.String,java.lang.String,java.lang.String,javax.swing.Icon,java.lang.Integer):V
- b:<init>(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
- <init>():V
@@ -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<CompletionCommand> {
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
}
}
@@ -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<HighlightInfo?>? = jobToIndicator(coroutineContext.job, indicator) {
HighlightVisitorBasedInspection.runAnnotatorsInGeneralHighlighting(topLevelFile, true, true, true)
ApplicationManager.getApplication().runReadAction<List<HighlightInfo?>?> {
HighlightVisitorBasedInspection.runAnnotatorsInGeneralHighlighting(topLevelFile, true, true, true)
}
}
if (errorHighlightings == null) return@readAction
var insideRange = getLineRange(topLevelFile, topLevelOffset)
@@ -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<CompletionCommand> {
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<ApplicableCompletionCommand> = LanguageExtension<ApplicableCompletionCommand>("com.intellij.codeInsight.completion.applicable.command")
@@ -763,10 +763,6 @@
beanClass="com.intellij.lang.LanguageExtensionPoint" dynamic="true">
<with attribute="implementationClass" implements="com.intellij.codeInsight.completion.command.CommandProvider"/>
</extensionPoint>
<extensionPoint qualifiedName="com.intellij.codeInsight.completion.applicable.command"
beanClass="com.intellij.lang.LanguageExtensionPoint" dynamic="true">
<with attribute="implementationClass" implements="com.intellij.codeInsight.completion.command.ApplicableCompletionCommand"/>
</extensionPoint>
<extensionPoint qualifiedName="com.intellij.codeInsight.completion.intention.skipper"
beanClass="com.intellij.lang.LanguageExtensionPoint" dynamic="true">
<with attribute="implementationClass" implements="com.intellij.codeInsight.completion.command.commands.IntentionCommandSkipper"/>
@@ -755,7 +755,6 @@
<lookup.charFilter implementation="com.intellij.codeInsight.completion.command.CommandCompletionCharFilter" order="first" id="commandCompletion"/>
<lookup.customizer implementation="com.intellij.codeInsight.completion.command.CommandCompletionLookupCustomizer" order="last" id="commandCompletionLookupCustomizer"/>
<codeInsight.completion.command.provider language="" implementationClass="com.intellij.codeInsight.completion.command.commands.ExtensionPointCommandProvider" />
<codeInsight.completion.command.provider language="" implementationClass="com.intellij.codeInsight.completion.command.commands.DirectIntentionCommandProvider" order="last"/>
<platform.backend.documentation.lookupElementTargetProvider
implementation="com.intellij.codeInsight.completion.command.CommandCompletionDocumentationProvider"/>
@@ -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<CompletionCommand> {
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<PsiElement?> = 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<PsiElement?> = 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, "")
}
}
@@ -27,8 +27,8 @@
implementationClass="com.intellij.codeInsight.completion.command.commands.PsiElementCommentByLineCompletionCommandProvider"/>
<codeInsight.completion.command.provider language="kotlin"
implementationClass="com.intellij.codeInsight.completion.command.commands.PsiElementCommentByBlockCompletionCommandProvider"/>
<codeInsight.completion.applicable.command language="kotlin"
implementationClass="org.jetbrains.kotlin.idea.completion.impl.k2.contributors.commands.KotlinDeleteCompletionCommand"/>
<codeInsight.completion.command.provider language="kotlin"
implementationClass="org.jetbrains.kotlin.idea.completion.impl.k2.contributors.commands.KotlinDeleteCompletionCommandProvider"/>
<codeInsight.completion.command.provider language="kotlin"
implementationClass="org.jetbrains.kotlin.idea.completion.impl.k2.contributors.commands.KotlinChangeSignatureCompletionCommand"/>
<codeInsight.completion.command.provider language="kotlin" implementationClass="org.jetbrains.kotlin.idea.completion.impl.k2.contributors.commands.KotlinCreateFromUsagesCommandProvider" order="last"/>