diff --git a/java/java-impl/resources/META-INF/JavaPlugin.xml b/java/java-impl/resources/META-INF/JavaPlugin.xml index 6bd34c9cddc1..e2a2914b1a4e 100644 --- a/java/java-impl/resources/META-INF/JavaPlugin.xml +++ b/java/java-impl/resources/META-INF/JavaPlugin.xml @@ -292,20 +292,6 @@ - - - - - - - - - @@ -1503,29 +1489,29 @@ implementationClass="com.intellij.codeInsight.completion.JvmLoggerCompletionContributor"/> - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + @@ -2790,17 +2776,6 @@ - - - - - - - - - diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/JavaCommandCompletionFactory.java b/java/java-impl/src/com/intellij/codeInsight/completion/commands/JavaCommandCompletionFactory.java new file mode 100644 index 000000000000..4e732d522383 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/JavaCommandCompletionFactory.java @@ -0,0 +1,46 @@ +// 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; + +import com.intellij.codeInsight.completion.command.CommandCompletionFactory; +import com.intellij.codeInsight.completion.command.commands.IntentionCommandSkipper; +import com.intellij.codeInsight.daemon.impl.quickfix.CreateGetterOrSetterFix; +import com.intellij.codeInsight.daemon.impl.quickfix.ExpensivePsiIntentionAction; +import com.intellij.codeInsight.intention.CommonIntentionAction; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ex.QuickFixWrapper; +import com.intellij.modcommand.ModCommandAction; +import com.intellij.modcommand.ModCommandService; +import com.intellij.openapi.project.DumbAware; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; + +public class JavaCommandCompletionFactory implements CommandCompletionFactory, DumbAware { + + @Override + public boolean isApplicable(@NotNull PsiFile psiFile, int offset) { + //todo skip varargs! + if (!(psiFile instanceof PsiJavaFile)) return false; + PsiElement elementAt = psiFile.findElementAt(offset); + if (elementAt == null) return true; + if (!(elementAt.getParent() instanceof PsiParameterList)) return true; + PsiElement prevLeaf = PsiTreeUtil.prevLeaf(elementAt, true); + if (!(prevLeaf instanceof PsiJavaToken javaToken && javaToken.textMatches("."))) return true; + PsiElement prevPrevLeaf = PsiTreeUtil.prevLeaf(prevLeaf, true); + if (PsiTreeUtil.getParentOfType(prevPrevLeaf, PsiTypeElement.class) != null) return false; + return true; + } + + public static class JavaIntentionCommandSkipper implements IntentionCommandSkipper { + @Override + public boolean skip(@NotNull CommonIntentionAction action, @NotNull PsiFile psiFile, int offset) { + if (action instanceof ExpensivePsiIntentionAction) return true; + LocalQuickFix fix = QuickFixWrapper.unwrap(action); + if (fix != null) { + ModCommandAction unwrappedAction = ModCommandService.getInstance().unwrap(fix); + if (unwrappedAction instanceof CreateGetterOrSetterFix) return true; + } + return IntentionCommandSkipper.super.skip(action, psiFile, offset); + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/api/CommandCompletionFactory.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/api/CommandCompletionFactory.kt deleted file mode 100644 index eb7df4433aef..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/api/CommandCompletionFactory.kt +++ /dev/null @@ -1,44 +0,0 @@ -// 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.api - -import com.intellij.openapi.project.Project -import com.intellij.psi.PsiFile -import org.jetbrains.annotations.ApiStatus - -/** - * Factory interface for creating and managing completion commands in a specific context. - * - * Provides functionality to determine applicability based on the file and offset, - * retrieve a list of command providers, and define suffixes used during code completion. - * Serves as a customization point for enhancing code completion workflows. - * - * Should implement DumbAware to support dumb mode - */ -@ApiStatus.Experimental -interface CommandCompletionFactory { - /** - * Provides the default character suffix. After that, suffix command completion will be enabled - * - * @return The character suffix, which is '.'. - */ - fun suffix(): Char = '.' - - /** - * Determines the character suffix to filter only command lookup - */ - fun filterSuffix(): Char? = '.' - - /** - * Retrieves a list of command providers responsible for supplying completion commands. - */ - fun commandProviders(project: Project): List - - /** - * Determines whether the functionality is applicable in the given context. - * - * @param psiFile the PSI file representing the file in which the applicability is being evaluated - * @param offset the position within the file where the applicability should be checked - * @return true if the functionality is applicable at the specified context, false otherwise - */ - fun isApplicable(psiFile: PsiFile, offset: Int): Boolean -} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionLookupElement.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionLookupElement.kt deleted file mode 100644 index edf717718c29..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionLookupElement.kt +++ /dev/null @@ -1,35 +0,0 @@ -// 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.core - -import com.intellij.codeInsight.lookup.AutoCompletionPolicy -import com.intellij.codeInsight.lookup.AutoCompletionPolicy.NEVER_AUTOCOMPLETE -import com.intellij.codeInsight.lookup.LookupElement -import com.intellij.codeInsight.lookup.LookupElementDecorator -import com.intellij.openapi.editor.colors.TextAttributesKey -import com.intellij.openapi.util.TextRange -import org.jetbrains.annotations.ApiStatus -import javax.swing.Icon - -@ApiStatus.Experimental -class CommandCompletionLookupElement( - lookupElement: LookupElement, - val hostStartOffset: Int, - val suffix: String, - val icon: Icon?, - val highlighting: HighlightInfoLookup?, -) : LookupElementDecorator(lookupElement) { - override fun isWorthShowingInAutoPopup(): Boolean { - return true - } - - override fun getAutoCompletionPolicy(): AutoCompletionPolicy? { - return NEVER_AUTOCOMPLETE - } -} - -@ApiStatus.Experimental -data class HighlightInfoLookup( - val range: TextRange, - val attributesKey: TextAttributesKey, - val priority: Int, //higher is on the top -) \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/CopyFQNCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/CopyFQNCompletionCommand.kt deleted file mode 100644 index 4570f031b691..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/CopyFQNCompletionCommand.kt +++ /dev/null @@ -1,44 +0,0 @@ -// 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.ide.IdeBundle -import com.intellij.ide.actions.FqnUtil -import com.intellij.openapi.editor.Editor -import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiMember -import com.intellij.psi.PsiMethod -import com.intellij.psi.util.PsiTreeUtil - -class CopyFQNCompletionCommand : AbstractActionCompletionCommand("CopyReference", - "Copy Reference", - IdeBundle.message("copy.reference"), - null, - -150) { - override fun supportsReadOnly(): Boolean = true - - override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { - if (!super.isApplicable(offset, psiFile, editor)) return false - val element = getContext(offset, psiFile) - val member = PsiTreeUtil.getParentOfType(element, PsiMember::class.java) ?: return false - if (member is PsiClass) { - if (!TextRange(member.textRange.startOffset, member.lBrace?.textRange?.startOffset ?: member.textRange.endOffset) - .contains(offset) - ) { - return false - } - } - else if (member is PsiMethod) { - if (!TextRange(member.textRange.startOffset, member.body?.textRange?.startOffset ?: member.textRange.endOffset) - .contains(offset) - ) { - return false - } - } - else { - return false - } - return FqnUtil.elementToFqn(element, editor) != null - } -} diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/ExtensionBasedCommandProvider.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/ExtensionBasedCommandProvider.kt deleted file mode 100644 index 9fa248941591..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/ExtensionBasedCommandProvider.kt +++ /dev/null @@ -1,27 +0,0 @@ -// 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.commands.api.ApplicableCompletionCommand -import com.intellij.codeInsight.completion.commands.api.CommandCompletionProviderContext -import com.intellij.codeInsight.completion.commands.api.CommandProvider -import com.intellij.codeInsight.completion.commands.api.CompletionCommand -import com.intellij.openapi.project.DumbAware -import com.intellij.openapi.project.DumbService - - -class ExtensionPointCommandProvider : CommandProvider, DumbAware { - override fun getCommands(context: CommandCompletionProviderContext): List { - val completionCommands = DumbService.getDumbAwareExtensions(context.project, ApplicableCompletionCommand.EP_NAME) - return completionCommands.filter { - !(context.isReadOnly && !it.supportsReadOnly()) && it.isApplicable(context.offset, context.psiFile, context.editor) - } - } - - override fun getId(): String { - return "ExtensionPointCommandProvider" - } - - override fun supportsReadOnly(): Boolean { - return true - } -} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/FormatCodeCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/FormatCodeCompletionCommand.kt deleted file mode 100644 index 5281c688c213..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/FormatCodeCompletionCommand.kt +++ /dev/null @@ -1,38 +0,0 @@ -// 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.actions.ReformatCodeProcessor -import com.intellij.codeInsight.completion.commands.api.ApplicableCompletionCommand -import com.intellij.icons.AllIcons -import com.intellij.idea.ActionsBundle -import com.intellij.lang.injection.InjectedLanguageManager -import com.intellij.openapi.editor.Editor -import com.intellij.psi.PsiCodeBlock -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiMember -import com.intellij.psi.PsiStatement -import com.intellij.psi.util.parents -import org.jetbrains.annotations.Nls -import javax.swing.Icon - -class FormatCodeCompletionCommand : ApplicableCompletionCommand() { - override val name: String - get() = "format" - - override val i18nName: @Nls String - get() = ActionsBundle.message("action.ReformatCode.text") - - override val icon: Icon - get() = AllIcons.Actions.ReformatCode // Use the reformat icon - - override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { - if (InjectedLanguageManager.getInstance(psiFile.project).isInjectedFragment(psiFile)) return false - return true - } - - override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) { - val element = getContext(offset, psiFile) ?: return - val target = element.parents(true).first { it is PsiMember || it is PsiCodeBlock || it is PsiStatement } - ReformatCodeProcessor(element.containingFile, arrayOf(target.textRange)).run() - } -} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/GenerateCommandProvider.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/GenerateCommandProvider.kt deleted file mode 100644 index 85a95275ef47..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/GenerateCommandProvider.kt +++ /dev/null @@ -1,64 +0,0 @@ -// 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.commands.api.* -import com.intellij.codeInsight.generation.actions.BaseGenerateAction -import com.intellij.lang.injection.InjectedLanguageManager -import com.intellij.openapi.actionSystem.* -import com.intellij.openapi.actionSystem.impl.Utils -import com.intellij.openapi.editor.Document -import com.intellij.openapi.project.DumbAware -import com.intellij.openapi.project.DumbService -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiIdentifier -import com.intellij.util.containers.JBIterable.from - - -class GenerateCommandProvider : CommandProvider, DumbAware { - override fun getCommands(context: CommandCompletionProviderContext): List { - val psiFile = context.psiFile - val project = context.project - val offset = context.offset - val editor = context.editor - if (InjectedLanguageManager.getInstance(psiFile.project).isInjectedFragment(psiFile)) return emptyList() - val element = psiFile.findElementAt(if (offset - 1 >= 0) offset - 1 else offset) - val parent = element?.parent - //skip, probably extension to skip? - if (element == null || parent !is PsiClass) return emptyList() - if (!isOnlySpaceInLine(psiFile.fileDocument, offset) && - !(element is PsiIdentifier && parent.nameIdentifier == element)) return emptyList() - val context = getTargetContext(offset, editor) - val dataContext = getDataContext(psiFile, editor, context) - val actionEvent = AnActionEvent.createEvent(dataContext, null, ActionPlaces.UNKNOWN, ActionUiKind.NONE, null) - actionEvent.updateSession = UpdateSession.EMPTY - Utils.initUpdateSession(actionEvent) - val generateActions: MutableList = ArrayList() - val session = actionEvent.updateSession - val dumbService = DumbService.getInstance(project) - val activeActions = from(session.expandedChildren(ActionManager.getInstance().getAction(IdeActions.GROUP_GENERATE) as ActionGroup)) - .filter { dumbService.isUsableInCurrentContext(it) } - .filter { o: AnAction? -> o !is Separator && o != null && session.presentation(o).isEnabledAndVisible } - for (action in activeActions) { - if (action is BaseGenerateAction) { - generateActions.add(action) - } - } - return generateActions.map { GenerateCompletionCommand(it) } - } - - private fun isOnlySpaceInLine(document: Document, offset: Int): Boolean { - val lineNumber = document.getLineNumber(offset) - val lineStartOffset = document.getLineStartOffset(lineNumber) - val immutableCharSequence = document.immutableCharSequence - for (i in lineStartOffset..offset) { - if (!immutableCharSequence[i].isWhitespace()) { - return false - } - } - return true - } - - override fun getId(): String { - return "GenerateCommandProvider" - } -} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/GenerateCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/GenerateCompletionCommand.kt deleted file mode 100644 index 3816dbdbb9ef..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/GenerateCompletionCommand.kt +++ /dev/null @@ -1,38 +0,0 @@ -// 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.commands.api.CompletionCommand -import com.intellij.codeInsight.generation.actions.BaseGenerateAction -import com.intellij.ide.DataManager -import com.intellij.java.JavaBundle -import com.intellij.openapi.actionSystem.ActionPlaces -import com.intellij.openapi.actionSystem.ActionUiKind -import com.intellij.openapi.actionSystem.AnActionEvent -import com.intellij.openapi.actionSystem.Presentation -import com.intellij.openapi.actionSystem.ex.ActionUtil -import com.intellij.openapi.editor.Editor -import com.intellij.psi.PsiFile -import org.jetbrains.annotations.Nls -import javax.swing.Icon - - -class GenerateCompletionCommand(private val action: BaseGenerateAction) : CompletionCommand() { - override val name: String - get() = "Generate \'" + action.templateText + "\'" - override val i18nName: @Nls String - get() = JavaBundle.message( - "command.completion.generate.text", - action.templateText) - override val icon: Icon? - get() = null - - override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) { - 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.UNKNOWN, ActionUiKind.NONE, null) - if (ActionUtil.lastUpdateAndCheckDumb(action, event, false)) { - ActionUtil.performActionDumbAwareWithCallbacks(action, event) - } - } -} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/GoToDeclarationCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/GoToDeclarationCompletionCommand.kt deleted file mode 100644 index 5e7c5df2bf90..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/GoToDeclarationCompletionCommand.kt +++ /dev/null @@ -1,35 +0,0 @@ -// 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.idea.ActionsBundle -import com.intellij.openapi.editor.Editor -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiIdentifier -import com.intellij.psi.PsiJavaCodeReferenceElement -import com.intellij.psi.util.PsiTreeUtil - -class GoToDeclarationCompletionCommand : AbstractActionCompletionCommand("GotoDeclarationOnly", - "Go to Declaration", - ActionsBundle.message("action.GotoDeclarationOnly.text"), - null) { - override fun supportsReadOnly(): Boolean = true - - override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { - return super.isApplicable(offset, psiFile, editor) && hasToShow(offset, psiFile) - } - - private fun hasToShow(offset: Int, psiFile: PsiFile): Boolean { - val context = (getContext(offset, psiFile)) ?: return false - return canNavigateToDeclaration(context) - } - - private fun canNavigateToDeclaration(context: PsiElement): Boolean { - if (context !is PsiIdentifier) { - return false - } - val javaRef = PsiTreeUtil.getParentOfType(context, PsiJavaCodeReferenceElement::class.java) - val psiElement = javaRef?.resolve() - return psiElement != null - } -} \ No newline at end of file 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 5437b38fbfc3..f2af22c58982 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,7 +1,7 @@ // 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.commands.api.ApplicableCompletionCommand +import com.intellij.codeInsight.completion.command.ApplicableCompletionCommand import com.intellij.codeInsight.daemon.QuickFixBundle import com.intellij.codeInsight.intention.QuickFixFactory import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler @@ -15,15 +15,15 @@ import javax.swing.Icon class GenerateGetterSetterHandleCompletionCommand : - BaseGenerateGetterSetterHandleCompletionCommand(true, true, "Create Getter/Setter", + BaseGenerateGetterSetterHandleCompletionCommand(true, true, "Generate 'Getter/Setter'", QuickFixBundle.message("create.getter.setter")) class GenerateSetterHandleCompletionCommand : - BaseGenerateGetterSetterHandleCompletionCommand(false, true, "Create Setter", + BaseGenerateGetterSetterHandleCompletionCommand(false, true, "Generate 'Setter'", QuickFixBundle.message("create.setter")) class GenerateGetterHandleCompletionCommand : - BaseGenerateGetterSetterHandleCompletionCommand(true, false, "Create Getter", + BaseGenerateGetterSetterHandleCompletionCommand(true, false, "Generate 'Getter'", QuickFixBundle.message("create.getter")) abstract class BaseGenerateGetterSetterHandleCompletionCommand( diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaCommandCompletionFactory.java b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaCommandCompletionFactory.java deleted file mode 100644 index 457c2d95d187..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaCommandCompletionFactory.java +++ /dev/null @@ -1,29 +0,0 @@ -// 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.commands.api.CommandCompletionFactory; -import com.intellij.codeInsight.completion.commands.api.CommandProvider; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.DumbService; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.registry.Registry; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiJavaFile; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class JavaCommandCompletionFactory implements CommandCompletionFactory, DumbAware { - - @Override - public @NotNull List<@NotNull CommandProvider> commandProviders(@NotNull Project project) { - if (!Registry.is("java.completion.command.enabled")) return List.of(); - - return DumbService.getDumbAwareExtensions(project, CommandProvider.Companion.getEP_NAME()); - } - - @Override - public boolean isApplicable(@NotNull PsiFile psiFile, int offset) { - return psiFile instanceof PsiJavaFile; - } -} diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/GoToImplementationCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaCopyFQNCompletionCommand.kt similarity index 51% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/GoToImplementationCompletionCommand.kt rename to java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaCopyFQNCompletionCommand.kt index 3fb05f79f7eb..3442d025fddc 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/GoToImplementationCompletionCommand.kt +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaCopyFQNCompletionCommand.kt @@ -1,24 +1,18 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// 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.idea.ActionsBundle -import com.intellij.openapi.editor.Editor +import com.intellij.codeInsight.completion.command.commands.AbstractCopyFQNCompletionCommand +import com.intellij.ide.actions.FqnUtil import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiClass -import com.intellij.psi.PsiFile +import com.intellij.psi.PsiElement import com.intellij.psi.PsiMember import com.intellij.psi.PsiMethod import com.intellij.psi.util.PsiTreeUtil -class GoToImplementationCompletionCommand: AbstractActionCompletionCommand("GotoImplementation", - "Go to Implementation", - ActionsBundle.message("action.GotoImplementation.text"), - null) { - override fun supportsReadOnly(): Boolean = true - - override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { - if (!super.isApplicable(offset, psiFile, editor)) return false - val element = getContext(offset, psiFile) +class JavaCopyFQNCompletionCommand : AbstractCopyFQNCompletionCommand() { + override fun placeIsApplicable(element: PsiElement, offset: Int): Boolean { + if (FqnUtil.elementToFqn(element, null) == null) return false val member = PsiTreeUtil.getParentOfType(element, PsiMember::class.java) ?: return false if (member is PsiClass) { if (!TextRange(member.textRange.startOffset, member.lBrace?.textRange?.startOffset ?: member.textRange.endOffset) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/DeleteCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaDeleteCompletionCommand.kt similarity index 92% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/DeleteCompletionCommand.kt rename to java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaDeleteCompletionCommand.kt index fabff80bd552..48e642f10170 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/DeleteCompletionCommand.kt +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaDeleteCompletionCommand.kt @@ -2,7 +2,7 @@ package com.intellij.codeInsight.completion.commands.impl import com.intellij.codeInsight.actions.ReformatCodeProcessor -import com.intellij.codeInsight.completion.commands.api.ApplicableCompletionCommand +import com.intellij.codeInsight.completion.command.ApplicableCompletionCommand import com.intellij.idea.ActionsBundle import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.editor.Editor @@ -12,7 +12,7 @@ import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.annotations.Nls import javax.swing.Icon -class DeleteCompletionCommand : ApplicableCompletionCommand(), DumbAware { +class JavaDeleteCompletionCommand : ApplicableCompletionCommand(), DumbAware { override val name: String get() = "Delete" override val i18nName: @Nls String diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaFormatCodeCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaFormatCodeCompletionCommand.kt new file mode 100644 index 000000000000..b7690d5d6cde --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaFormatCodeCompletionCommand.kt @@ -0,0 +1,15 @@ +// 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.AbstractFormatCodeCompletionCommand +import com.intellij.psi.PsiCodeBlock +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMember +import com.intellij.psi.PsiStatement +import com.intellij.psi.util.parents + +class JavaFormatCodeCompletionCommand : AbstractFormatCodeCompletionCommand() { + override fun findTargetToRefactor(element: PsiElement): PsiElement { + return element.parents(true).first { it is PsiMember || it is PsiCodeBlock || it is PsiStatement } + } +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaGenerateCommandCompletion.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaGenerateCommandCompletion.kt new file mode 100644 index 000000000000..e6ef2b4b6107 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaGenerateCommandCompletion.kt @@ -0,0 +1,17 @@ +// 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.AbstractGenerateCommandProvider +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiIdentifier + +class JavaGenerateCommandCompletion : AbstractGenerateCommandProvider() { + override fun generationIsAvailable(element: PsiElement, offset: Int): Boolean { + val parent = element.parent + if (parent !is PsiClass) return false + if (!isOnlySpaceInLine(element.containingFile.fileDocument, offset) && + !(element is PsiIdentifier && parent.nameIdentifier == element)) return false + return true + } +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaGoToDeclarationCommandCompletion.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaGoToDeclarationCommandCompletion.kt new file mode 100644 index 000000000000..fc9003a70b8f --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaGoToDeclarationCommandCompletion.kt @@ -0,0 +1,19 @@ +// 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.AbstractGoToDeclarationCompletionCommand +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiIdentifier +import com.intellij.psi.PsiJavaCodeReferenceElement +import com.intellij.psi.util.PsiTreeUtil + +class JavaGoToDeclarationCommandCompletion : AbstractGoToDeclarationCompletionCommand() { + override fun canNavigateToDeclaration(context: PsiElement): Boolean { + if (context !is PsiIdentifier) { + return false + } + val javaRef = PsiTreeUtil.getParentOfType(context, PsiJavaCodeReferenceElement::class.java) + val psiElement = javaRef?.resolve() + return psiElement != null + } +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaGoToImplementationCommandCompletion.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaGoToImplementationCommandCompletion.kt new file mode 100644 index 000000000000..bdcb66130182 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaGoToImplementationCommandCompletion.kt @@ -0,0 +1,35 @@ +// 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.AbstractGoToImplementationCompletionCommand +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMember +import com.intellij.psi.PsiMethod +import com.intellij.psi.util.PsiTreeUtil + +class JavaGoToImplementationCommandCompletion : AbstractGoToImplementationCompletionCommand() { + override fun canGoToImplementation(element: PsiElement, offset: Int): Boolean { + + val member = PsiTreeUtil.getParentOfType(element, PsiMember::class.java) ?: return false + if (member is PsiClass) { + if (!TextRange(member.textRange.startOffset, member.lBrace?.textRange?.startOffset ?: member.textRange.endOffset) + .contains(offset) + ) { + return false + } + } + else if (member is PsiMethod) { + if (!TextRange(member.textRange.startOffset, member.body?.textRange?.startOffset ?: member.textRange.endOffset) + .contains(offset) + ) { + return false + } + } + else { + return false + } + return true + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/InlineVariableCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaInlineVariableCompletionCommand.kt similarity index 92% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/InlineVariableCompletionCommand.kt rename to java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaInlineVariableCompletionCommand.kt index f881e9ce18d7..f38fb0ddcc91 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/InlineVariableCompletionCommand.kt +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaInlineVariableCompletionCommand.kt @@ -1,7 +1,7 @@ // 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.commands.api.ApplicableCompletionCommand +import com.intellij.codeInsight.completion.command.ApplicableCompletionCommand import com.intellij.icons.AllIcons import com.intellij.java.JavaBundle import com.intellij.lang.refactoring.InlineActionHandler @@ -13,9 +13,9 @@ import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.annotations.Nls import javax.swing.Icon -class InlineVariableCompletionCommand : ApplicableCompletionCommand() { +class JavaInlineVariableCompletionCommand : ApplicableCompletionCommand() { override val name: String - get() = "inline" + get() = "Inline" override val i18nName: @Nls String get() = JavaBundle.message("command.completion.inline.text") diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/IntroduceVariableCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaIntroduceVariableCommand.kt similarity index 84% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/IntroduceVariableCommand.kt rename to java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaIntroduceVariableCommand.kt index b1250567cfdf..fc056c9814ee 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/IntroduceVariableCommand.kt +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaIntroduceVariableCommand.kt @@ -1,9 +1,9 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// 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.commands.api.ApplicableCompletionCommand -import com.intellij.codeInsight.completion.commands.api.getDataContext -import com.intellij.codeInsight.completion.commands.api.getTargetContext +import com.intellij.codeInsight.completion.command.ApplicableCompletionCommand +import com.intellij.codeInsight.completion.command.getDataContext +import com.intellij.codeInsight.completion.command.getTargetContext import com.intellij.icons.AllIcons import com.intellij.ide.DataManager import com.intellij.lang.ContextAwareActionHandler @@ -20,7 +20,7 @@ import com.intellij.refactoring.actions.IntroduceVariableAction import org.jetbrains.annotations.Nls import javax.swing.Icon -class IntroduceVariableCommand : ApplicableCompletionCommand() { +class JavaIntroduceVariableCommand : ApplicableCompletionCommand() { override val name: String get() = "Introduce variable" @@ -45,7 +45,7 @@ class IntroduceVariableCommand : ApplicableCompletionCommand() { 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.NONE, null) + 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) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaShowUsagesActionCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaShowUsagesActionCompletionCommand.kt new file mode 100644 index 000000000000..d9347018a3cd --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaShowUsagesActionCompletionCommand.kt @@ -0,0 +1,15 @@ +// 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.AbstractShowUsagesActionCompletionCommand +import com.intellij.psi.* +import com.intellij.psi.util.PsiTreeUtil + +class JavaShowUsagesActionCompletionCommand : AbstractShowUsagesActionCompletionCommand() { + override fun hasToShow(element: PsiElement): Boolean { + val namedIdentifierParent = PsiTreeUtil.getParentOfType(element, PsiNameIdentifierOwner::class.java) + return namedIdentifierParent?.nameIdentifier == element && + (namedIdentifierParent is PsiVariable || namedIdentifierParent is PsiMethod || namedIdentifierParent is PsiMember) + + } +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/ShowUsagesActionCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/ShowUsagesActionCompletionCommand.kt deleted file mode 100644 index 76614d2c6fdb..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/ShowUsagesActionCompletionCommand.kt +++ /dev/null @@ -1,28 +0,0 @@ -// 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.find.actions.ShowUsagesAction -import com.intellij.idea.ActionsBundle -import com.intellij.lang.injection.InjectedLanguageManager -import com.intellij.openapi.editor.Editor -import com.intellij.psi.* -import com.intellij.psi.util.PsiTreeUtil - -class ShowUsagesActionCompletionCommand : AbstractActionCompletionCommand(ShowUsagesAction.ID, - "Show usages", - ActionsBundle.message("action.ShowUsages.text"), - null) { - override fun supportsReadOnly(): Boolean = true - - override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { - return super.isApplicable(offset, psiFile, editor) && hasToShow(getContext(offset, psiFile)) && - !InjectedLanguageManager.getInstance(psiFile.project).isInjectedFragment(psiFile) - } - - private fun hasToShow(element: PsiElement?): Boolean { - if (element == null) return false - val namedIdentifierParent = PsiTreeUtil.getParentOfType(element, PsiNameIdentifierOwner::class.java) - return namedIdentifierParent?.nameIdentifier == element && - (namedIdentifierParent is PsiVariable || namedIdentifierParent is PsiMethod || namedIdentifierParent is PsiMember) - } -} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/commands/JavaCommandsCompletionTest.kt b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/commands/JavaCommandsCompletionTest.kt index d582bb25f6f4..8a7dd98eaa46 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/commands/JavaCommandsCompletionTest.kt +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/completion/commands/JavaCommandsCompletionTest.kt @@ -5,6 +5,7 @@ import com.intellij.codeInsight.completion.LightFixtureCompletionTestCase import com.intellij.codeInsight.hint.HintManager import com.intellij.codeInsight.hint.HintManagerImpl import com.intellij.ide.highlighter.JavaFileType +import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.impl.NonBlockingReadActionImpl import com.intellij.openapi.editor.Editor @@ -21,7 +22,7 @@ import javax.swing.JComponent class JavaCommandsCompletionTest : LightFixtureCompletionTestCase() { fun testFormatNotCall() { - Registry.get("java.completion.command.enabled").setValue(false, getTestRootDisposable()) + Registry.get("ide.completion.command.enabled").setValue(false, getTestRootDisposable()) myFixture.configureByText(JavaFileType.INSTANCE, """ class A { void foo() { @@ -35,7 +36,7 @@ class JavaCommandsCompletionTest : LightFixtureCompletionTestCase() { } fun testFormat() { - Registry.get("java.completion.command.enabled").setValue(true, getTestRootDisposable()) + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) myFixture.configureByText(JavaFileType.INSTANCE, """ class A { void foo() { @@ -56,8 +57,191 @@ class JavaCommandsCompletionTest : LightFixtureCompletionTestCase() { """.trimIndent()) } + fun testCopyFqn() { + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) + myFixture.configureByText(JavaFileType.INSTANCE, """ + class A { + void foo.() { + int y = 10; + } + } + """.trimIndent()) + val elements = myFixture.completeBasic() + selectItem(elements.first { element -> element.lookupString.contains("copy ref", ignoreCase = true) }) + myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE) + NonBlockingReadActionImpl.waitForAsyncTaskCompletion() + myFixture.checkResult(""" + class A { + void foofoo()() { + int y = 10; + } + } + """.trimIndent()) + } + + fun testGenerateConstructor() { + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) + myFixture.configureByText(JavaFileType.INSTANCE, """ + class A { + . + } + """.trimIndent()) + val elements = myFixture.completeBasic() + selectItem(elements.first { element -> element.lookupString.contains("Generate 'Con", ignoreCase = true) }) + myFixture.checkResult(""" + class A { + public A() { + } + } + """.trimIndent()) + } + + fun testGenerateConstructorInline() { + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) + Registry.get("ide.completion.command.full.line.enabled").setValue(true, getTestRootDisposable()) + myFixture.configureByText(JavaFileType.INSTANCE, """ + class A { + generate 'con + } + """.trimIndent()) + val elements = myFixture.completeBasic() + selectItem(elements.first { element -> element.lookupString.contains("Generate 'Con", ignoreCase = true) }) + myFixture.checkResult(""" + class A { + public A() { + } + } + """.trimIndent()) + } + + fun testOptimizeImport() { + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) + myFixture.configureByText(JavaFileType.INSTANCE, """ + import java.util.List; + . + class A { + void foo() { + String y = "1"; + } + }""".trimIndent()) + val elements = myFixture.completeBasic() + selectItem(elements.first { element -> element.lookupString.contains("Optimize im", ignoreCase = true) }) + NonBlockingReadActionImpl.waitForAsyncTaskCompletion() + myFixture.checkResult(""" + class A { + void foo() { + String y = "1"; + } + }""".trimIndent()) + } + + fun testGenerateGetter() { + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) + myFixture.configureByText(JavaFileType.INSTANCE, """ + class A { + String y.; + } + """.trimIndent()) + val elements = myFixture.completeBasic() + selectItem(elements.first { element -> element.lookupString.contains("Generate 'Ge", ignoreCase = true) }) + myFixture.checkResult(""" + class A { + String y; + + public String getY() { + return y; + } + } + """.trimIndent()) + } + + fun testDeleteLine() { + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) + myFixture.configureByText(JavaFileType.INSTANCE, """ + class A { + String y; + + public String getY() { + return y;. + } + }""".trimIndent()) + val elements = myFixture.completeBasic() + selectItem(elements.first { element -> element.lookupString.contains("Delete", ignoreCase = true) }) + myFixture.checkResult(""" + class A { + String y; + + public String getY() { + } + }""".trimIndent()) + + } + + fun testInline() { + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) + myFixture.configureByText(JavaFileType.INSTANCE, """ + class A { + public String getY() { + String y = "y"; + return y.; + } + } + """.trimIndent()) + val elements = myFixture.completeBasic() + selectItem(elements.first { element -> element.lookupString.contains("Inline", ignoreCase = true) }) + myFixture.checkResult(""" + class A { + public String getY() { + return "y"; + } + } + """.trimIndent()) + + } + + fun testCommentElement() { + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) + myFixture.configureByText(JavaFileType.INSTANCE, """ + class A { + public String getY() { + return "y"; + } + }.""".trimIndent()) + val elements = myFixture.completeBasic() + selectItem(elements.first { element -> element.lookupString.contains("Comment element", ignoreCase = true) }) + myFixture.checkResult(""" + //class A { + // public String getY() { + // return "y"; + // } + //}""".trimIndent()) + } + + fun testRename() { + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) + myFixture.configureByText(JavaFileType.INSTANCE, """ + class A { + void foo() { + String y. = "1"; + System.out.println(y); + } + }""".trimIndent()) + val elements = myFixture.completeBasic() + selectItem(elements.first { element -> element.lookupString.contains("Rename", ignoreCase = true) }) + myFixture.type('\n') + NonBlockingReadActionImpl.waitForAsyncTaskCompletion() + myFixture.checkResult(""" + class A { + void foo() { + String n + umber = "1"; + System.out.println(number); + } + }""".trimIndent()) + } + fun testCommandsOnlyGoToDeclaration() { - Registry.get("java.completion.command.enabled").setValue(true, getTestRootDisposable()) + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) myFixture.configureByText(JavaFileType.INSTANCE, """ class A { void foo() { @@ -83,8 +267,43 @@ class JavaCommandsCompletionTest : LightFixtureCompletionTestCase() { """.trimIndent()) } + fun testCommandsOnlyGoToImplementation() { + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) + myFixture.configureByText(JavaFileType.INSTANCE, """ + interface A{ + + public void a.(); + + class B implements A{ + + @Override + public void a() { + + } + } + } + """.trimIndent()) + val elements = myFixture.completeBasic() + selectItem(elements.first { element -> element.lookupString.contains("Go to impl", ignoreCase = true) }) + NonBlockingReadActionImpl.waitForAsyncTaskCompletion() + myFixture.checkResult(""" + interface A{ + + public void a(); + + class B implements A{ + + @Override + public void a() { + + } + } + } + """.trimIndent()) + } + fun testRedCode() { - Registry.get("java.completion.command.enabled").setValue(true, getTestRootDisposable()) + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) myFixture.configureByText(JavaFileType.INSTANCE, """ class A { void foo() { @@ -106,7 +325,7 @@ class JavaCommandsCompletionTest : LightFixtureCompletionTestCase() { } fun testComment() { - Registry.get("java.completion.command.enabled").setValue(true, getTestRootDisposable()) + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) myFixture.configureByText(JavaFileType.INSTANCE, """ class A { void foo() { @@ -128,7 +347,7 @@ class JavaCommandsCompletionTest : LightFixtureCompletionTestCase() { } fun testFlipIntention() { - Registry.get("java.completion.command.enabled").setValue(true, getTestRootDisposable()) + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) myFixture.configureByText(JavaFileType.INSTANCE, """ class A { void foo() { @@ -150,7 +369,7 @@ class JavaCommandsCompletionTest : LightFixtureCompletionTestCase() { } fun testInspection() { - Registry.get("java.completion.command.enabled").setValue(true, getTestRootDisposable()) + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) myFixture.enableInspections(SizeReplaceableByIsEmptyInspection()) myFixture.configureByText(JavaFileType.INSTANCE, """ import java.util.List; @@ -175,7 +394,7 @@ class JavaCommandsCompletionTest : LightFixtureCompletionTestCase() { } fun testBinaryNotAllowed() { - Registry.get("java.completion.command.enabled").setValue(false, getTestRootDisposable()) + Registry.get("ide.completion.command.enabled").setValue(false, getTestRootDisposable()) val psiClass = JavaPsiFacade.getInstance(project).findClass(JAVA_LANG_CLASS, GlobalSearchScope.allScope(project)) val file = psiClass?.containingFile?.virtualFile assertNotNull(file) @@ -193,7 +412,7 @@ class JavaCommandsCompletionTest : LightFixtureCompletionTestCase() { } fun testBinaryNotAllowedCalledCompletion() { - Registry.get("java.completion.command.enabled").setValue(true, getTestRootDisposable()) + Registry.get("ide.completion.command.enabled").setValue(true, getTestRootDisposable()) val psiClass = JavaPsiFacade.getInstance(project).findClass(JAVA_LANG_CLASS, GlobalSearchScope.allScope(project)) val file = psiClass?.containingFile?.virtualFile assertNotNull(file) diff --git a/java/openapi/resources/messages/JavaBundle.properties b/java/openapi/resources/messages/JavaBundle.properties index d17f78c5a4bb..737417020af6 100644 --- a/java/openapi/resources/messages/JavaBundle.properties +++ b/java/openapi/resources/messages/JavaBundle.properties @@ -1956,11 +1956,7 @@ checkbox.check.for.jdk.updates=Check for JDK updates intention.family.name.rename.to.ignored=Rename to ignored tooltip.variable.used=Variable ''{0}'' is used -command.completion.filter.hint=press '.' to show actions -command.completion.generate.text=Generate ''{0}'' command.completion.inline.text=Inline -command.completion.project.tool.text=Open Project tool window -command.completion.recent.files.text=Open recent files popup advanced.setting.java.show.irrelevant.templates.in.source.roots=Show irrelevant New File templates in Java source roots java.test.use.wall.time=Use Wall Time @@ -1971,6 +1967,5 @@ auto.test.on=Auto-test on disable.auto.test=Disable auto-test error.no.annotation.class.found=No annotation class found -java.psi.element.comment.completion.command.text=Comment element preview.api.usage={0} is a preview API and may be removed in a future release preview.api.usage.reflective={0} is a reflective preview API and may be removed in a future release diff --git a/platform/analysis-api/api-dump-experimental.txt b/platform/analysis-api/api-dump-experimental.txt index c9df636a778e..44641f28d68b 100644 --- a/platform/analysis-api/api-dump-experimental.txt +++ b/platform/analysis-api/api-dump-experimental.txt @@ -1,3 +1,62 @@ +*a:com.intellij.codeInsight.completion.command.ApplicableCompletionCommand +- com.intellij.codeInsight.completion.command.CompletionCommand +- ():V +- a:isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z +- supportsReadOnly():Z +*:com.intellij.codeInsight.completion.command.CommandCompletionFactory +- commandProviders(com.intellij.openapi.project.Project,com.intellij.lang.Language):java.util.List +- createFile(com.intellij.psi.PsiFile,java.lang.String):com.intellij.psi.PsiFile +- filterSuffix():java.lang.Character +- isApplicable(com.intellij.psi.PsiFile,I):Z +- suffix():C +*f:com.intellij.codeInsight.completion.command.CommandCompletionProviderContext +- (com.intellij.openapi.project.Project,com.intellij.openapi.editor.Editor,I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor,I,com.intellij.psi.PsiFile,Z):V +- f:component1():com.intellij.openapi.project.Project +- f:component2():com.intellij.openapi.editor.Editor +- f:component3():I +- f:component4():com.intellij.psi.PsiFile +- f:component5():com.intellij.openapi.editor.Editor +- f:component6():I +- f:component7():com.intellij.psi.PsiFile +- f:component8():Z +- f:copy(com.intellij.openapi.project.Project,com.intellij.openapi.editor.Editor,I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor,I,com.intellij.psi.PsiFile,Z):com.intellij.codeInsight.completion.command.CommandCompletionProviderContext +- bs:copy$default(com.intellij.codeInsight.completion.command.CommandCompletionProviderContext,com.intellij.openapi.project.Project,com.intellij.openapi.editor.Editor,I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor,I,com.intellij.psi.PsiFile,Z,I,java.lang.Object):com.intellij.codeInsight.completion.command.CommandCompletionProviderContext +- equals(java.lang.Object):Z +- f:getEditor():com.intellij.openapi.editor.Editor +- f:getOffset():I +- f:getOriginalEditor():com.intellij.openapi.editor.Editor +- f:getOriginalOffset():I +- f:getOriginalPsiFile():com.intellij.psi.PsiFile +- f:getProject():com.intellij.openapi.project.Project +- f:getPsiFile():com.intellij.psi.PsiFile +- hashCode():I +- f:isReadOnly():Z +*:com.intellij.codeInsight.completion.command.CommandProvider +- a:getCommands(com.intellij.codeInsight.completion.command.CommandCompletionProviderContext):java.util.List +- getId():java.lang.String +- supportsReadOnly():Z +*a:com.intellij.codeInsight.completion.command.CompletionCommand +- ():V +- a:execute(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):V +- getAdditionalInfo():java.lang.String +- getContext(I,com.intellij.psi.PsiFile):com.intellij.psi.PsiElement +- getHighlightInfo():com.intellij.codeInsight.completion.command.HighlightInfoLookup +- a:getI18nName():java.lang.String +- a:getIcon():javax.swing.Icon +- a:getName():java.lang.String +- getPriority():java.lang.Integer +*f:com.intellij.codeInsight.completion.command.HighlightInfoLookup +- (com.intellij.openapi.util.TextRange,com.intellij.openapi.editor.colors.TextAttributesKey,I):V +- f:component1():com.intellij.openapi.util.TextRange +- f:component2():com.intellij.openapi.editor.colors.TextAttributesKey +- f:component3():I +- f:copy(com.intellij.openapi.util.TextRange,com.intellij.openapi.editor.colors.TextAttributesKey,I):com.intellij.codeInsight.completion.command.HighlightInfoLookup +- bs:copy$default(com.intellij.codeInsight.completion.command.HighlightInfoLookup,com.intellij.openapi.util.TextRange,com.intellij.openapi.editor.colors.TextAttributesKey,I,I,java.lang.Object):com.intellij.codeInsight.completion.command.HighlightInfoLookup +- equals(java.lang.Object):Z +- f:getAttributesKey():com.intellij.openapi.editor.colors.TextAttributesKey +- f:getPriority():I +- f:getRange():com.intellij.openapi.util.TextRange +- hashCode():I *:com.intellij.codeInsight.completion.ml.MLRankingIgnorable - s:wrap(com.intellij.codeInsight.lookup.LookupElement):com.intellij.codeInsight.lookup.LookupElement *:com.intellij.codeInsight.highlighting.PsiHighlightedReference diff --git a/platform/analysis-api/src/com/intellij/codeInsight/completion/command/CommandCompletionFactory.kt b/platform/analysis-api/src/com/intellij/codeInsight/completion/command/CommandCompletionFactory.kt new file mode 100644 index 000000000000..d14b895c8900 --- /dev/null +++ b/platform/analysis-api/src/com/intellij/codeInsight/completion/command/CommandCompletionFactory.kt @@ -0,0 +1,69 @@ +// 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 + +import com.intellij.lang.Language +import com.intellij.lang.LanguageExtension +import com.intellij.openapi.project.DumbService +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.registry.Registry +import com.intellij.psi.PsiFile +import org.jetbrains.annotations.ApiStatus + +/** + * Factory interface for creating and managing completion commands in a specific context. + * + * Provides functionality to determine applicability based on the file and offset, + * retrieve a list of command providers, and define suffixes used during code completion. + * Serves as a customization point for enhancing code completion workflows. + * + * Should implement DumbAware to support dumb mode + */ +@ApiStatus.Experimental +interface CommandCompletionFactory { + /** + * Provides the default character suffix. After that, suffix command completion will be enabled + * + * @return The character suffix, which is '.'. + */ + fun suffix(): Char = '.' + + /** + * Determines the character suffix to filter only command lookup + */ + fun filterSuffix(): Char? = '.' + + /** + * Retrieves a list of command providers responsible for supplying completion commands. + * The default implementation returns a set of providers. + * Language plugins can add command providers via 'com.intellij.codeInsight.completion.command.provider' EP + * or implement [com.intellij.codeInsight.completion.command.ApplicableCompletionCommand] + * and register with 'com.intellij.codeInsight.completion.applicable.command' language extension + */ + fun commandProviders(project: Project, language: Language): List { + if (!Registry.`is`("ide.completion.command.enabled")) return mutableListOf() + return DumbService.getInstance(project).filterByDumbAwareness(EP_NAME.allForLanguageOrAny(language)) + } + + /** + * Determines whether the functionality is applicable in the given context. + * + * @param psiFile the PSI file representing the file in which the applicability is being evaluated + * @param offset the position within the file where the applicability should be checked + * @return true if the functionality is applicable at the specified context, false otherwise + */ + fun isApplicable(psiFile: PsiFile, offset: Int): Boolean { + return true + } + + /** + * Creates a new file based on the provided original file and text content. + * + * @param originalFile The base file from which the new file will be created. It must be of type `PsiFile`. + * @param text The text content to be used for the newly created file. + * @return new file, containing text or null if creation file with [com.intellij.psi.PsiFileFactory.createFileFromText] is available + * + */ + fun createFile(originalFile: PsiFile, text: String): PsiFile? = null +} + +private val EP_NAME = LanguageExtension("com.intellij.codeInsight.completion.command.provider") diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/api/CommandProvider.kt b/platform/analysis-api/src/com/intellij/codeInsight/completion/command/CommandProvider.kt similarity index 90% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/api/CommandProvider.kt rename to platform/analysis-api/src/com/intellij/codeInsight/completion/command/CommandProvider.kt index 2c864db21124..e06c24e88456 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/api/CommandProvider.kt +++ b/platform/analysis-api/src/com/intellij/codeInsight/completion/command/CommandProvider.kt @@ -1,8 +1,7 @@ // 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.api +package com.intellij.codeInsight.completion.command import com.intellij.openapi.editor.Editor -import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.project.Project import com.intellij.psi.PsiFile import org.jetbrains.annotations.ApiStatus.Experimental @@ -16,9 +15,6 @@ import org.jetbrains.annotations.ApiStatus.Experimental */ @Experimental interface CommandProvider { - companion object { - val EP_NAME = ExtensionPointName("com.intellij.codeInsight.completion.command.provider") - } /** @@ -31,7 +27,7 @@ interface CommandProvider { */ fun getCommands(context: CommandCompletionProviderContext): List - fun getId(): String + fun getId(): String = javaClass.name /** * Indicates whether the implementation supports non-written files. diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/api/CompletionCommand.kt b/platform/analysis-api/src/com/intellij/codeInsight/completion/command/CompletionCommand.kt similarity index 61% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/api/CompletionCommand.kt rename to platform/analysis-api/src/com/intellij/codeInsight/completion/command/CompletionCommand.kt index 27a4f0eeb4c7..add86b39574e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/api/CompletionCommand.kt +++ b/platform/analysis-api/src/com/intellij/codeInsight/completion/command/CompletionCommand.kt @@ -1,15 +1,9 @@ -// 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.api +// 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 -import com.intellij.codeInsight.TargetElementUtil -import com.intellij.codeInsight.completion.commands.core.HighlightInfoLookup -import com.intellij.openapi.actionSystem.CommonDataKeys -import com.intellij.openapi.actionSystem.DataContext -import com.intellij.openapi.actionSystem.LangDataKeys -import com.intellij.openapi.actionSystem.impl.SimpleDataContext import com.intellij.openapi.editor.Editor -import com.intellij.openapi.extensions.ExtensionPointName -import com.intellij.openapi.project.IndexNotReadyException +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.util.concurrency.annotations.RequiresEdt @@ -87,10 +81,6 @@ abstract class CompletionCommand { */ @ApiStatus.Experimental abstract class ApplicableCompletionCommand : CompletionCommand() { - companion object { - - val EP_NAME = ExtensionPointName("com.intellij.codeInsight.completion.command") - } /** * Determines whether the command is applicable based on the given context. @@ -112,47 +102,9 @@ abstract class ApplicableCompletionCommand : CompletionCommand() { open fun supportsReadOnly(): Boolean = false } -/** - * Constructs a [DataContext] instance using the specified parameters, including the PSI file, - * editor, and optionally a context element. The resulting data context is populated with - * relevant keys and values to be used within the context of the provided elements. - * - * @param psiFile The [PsiFile] associated with the current context. - * @param editor The [Editor] instance corresponding to the current context. - * @param context An optional [PsiElement] representing the context; can be null. - * @return A [DataContext] instance containing the provided context information. - */ -internal fun getDataContext( - psiFile: PsiFile, - editor: Editor, - context: PsiElement?, -): DataContext { - val dataContext = SimpleDataContext.builder() - .add(CommonDataKeys.PROJECT, psiFile.project) - .add(CommonDataKeys.EDITOR, editor) - .add(CommonDataKeys.PSI_ELEMENT, context) - .add(CommonDataKeys.PSI_FILE, psiFile) - .add(LangDataKeys.CONTEXT_LANGUAGES, arrayOf(psiFile.language)) - .build() - return dataContext -} - -/** - * Retrieves the PSI element located at the specified offset within the editor's context. - * This method attempts to find a target PSI element based on the editor's state and the offset, - * allowing interaction with the underlying code structure. - * - * @param offset The offset in the editor's document where the target element is to be located. - * @param editor The editor instance in which the method searches for the target element. - * @return The PSI element found at the specified offset, or null if no suitable element is found - * or if the index is not ready. - */ -internal fun getTargetContext(offset: Int, editor: Editor): PsiElement? { - try { - val util = TargetElementUtil.getInstance() - return util.findTargetElement(editor, util.getReferenceSearchFlags(), offset) - } - catch (_: IndexNotReadyException) { - return null - } -} \ No newline at end of file +@ApiStatus.Experimental +data class HighlightInfoLookup( + val range: TextRange, + val attributesKey: TextAttributesKey, + val priority: Int, //higher is on the top +) diff --git a/platform/lang-api/resources/messages/CodeInsightBundle.properties b/platform/lang-api/resources/messages/CodeInsightBundle.properties index 82aa3d77decd..109330c7ed7c 100644 --- a/platform/lang-api/resources/messages/CodeInsightBundle.properties +++ b/platform/lang-api/resources/messages/CodeInsightBundle.properties @@ -607,3 +607,8 @@ jvm.inspections.dependency.configure.button.text=Configure dependency rules checking.code.highlightings.in.background=Loading Code Highlighting title.checking.code.highlightings.in.background.task=Loading code passes documentation.download.button.label=Download documentation +command.completion.filter.hint=press ''{0}'' to show actions +command.completion.project.tool.text=Open Project tool window +command.completion.recent.files.text=Open recent files popup +command.completion.generate.text=Generate ''{0}'' +command.completion.psi.element.comment.text=Comment element \ No newline at end of file diff --git a/platform/lang-impl/api-dump-experimental.txt b/platform/lang-impl/api-dump-experimental.txt index 89f1d0100e87..2038203cdf5d 100644 --- a/platform/lang-impl/api-dump-experimental.txt +++ b/platform/lang-impl/api-dump-experimental.txt @@ -143,6 +143,95 @@ c:com.intellij.codeInsight.actions.VcsFacade *f:com.intellij.codeInsight.codeVision.CodeVisionProvider$Companion - sf:EP_NAME:java.lang.String - f:getProvidersExtensionPoint():com.intellij.openapi.extensions.ExtensionPointName +*a:com.intellij.codeInsight.completion.command.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.AbstractCopyFQNCompletionCommand +- com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand +- ():V +- f:isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z +- a:placeIsApplicable(com.intellij.psi.PsiElement,I):Z +- f:supportsReadOnly():Z +*a:com.intellij.codeInsight.completion.command.commands.AbstractFormatCodeCompletionCommand +- com.intellij.codeInsight.completion.command.ApplicableCompletionCommand +- ():V +- f:execute(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):V +- a:findTargetToRefactor(com.intellij.psi.PsiElement):com.intellij.psi.PsiElement +- f:getI18nName():java.lang.String +- f:getIcon():javax.swing.Icon +- f:getName():java.lang.String +- f:isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z +*a:com.intellij.codeInsight.completion.command.commands.AbstractGenerateCommandProvider +- com.intellij.codeInsight.completion.command.CommandProvider +- com.intellij.openapi.project.DumbAware +- ():V +- a:generationIsAvailable(com.intellij.psi.PsiElement,I):Z +- f:getCommands(com.intellij.codeInsight.completion.command.CommandCompletionProviderContext):java.util.List +- pf:isOnlySpaceInLine(com.intellij.openapi.editor.Document,I):Z +*a:com.intellij.codeInsight.completion.command.commands.AbstractGoToDeclarationCompletionCommand +- com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand +- ():V +- a:canNavigateToDeclaration(com.intellij.psi.PsiElement):Z +- f:isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z +- f:supportsReadOnly():Z +*a:com.intellij.codeInsight.completion.command.commands.AbstractGoToImplementationCompletionCommand +- com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand +- ():V +- a:canGoToImplementation(com.intellij.psi.PsiElement,I):Z +- f:isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z +- f:supportsReadOnly():Z +*a:com.intellij.codeInsight.completion.command.commands.AbstractShowUsagesActionCompletionCommand +- com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand +- ():V +- a:hasToShow(com.intellij.psi.PsiElement):Z +- f:isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z +- f:supportsReadOnly():Z +*f:com.intellij.codeInsight.completion.command.commands.DirectIntentionCommandProvider +- com.intellij.codeInsight.completion.command.CommandProvider +- ():V +- getCommands(com.intellij.codeInsight.completion.command.CommandCompletionProviderContext):java.util.List +*:com.intellij.codeInsight.completion.command.commands.IntentionCommandSkipper +- *sf:Companion:com.intellij.codeInsight.completion.command.commands.IntentionCommandSkipper$Companion +- skip(com.intellij.codeInsight.intention.CommonIntentionAction,com.intellij.psi.PsiFile,I):Z +*f:com.intellij.codeInsight.completion.command.commands.IntentionCommandSkipper$Companion +*f:com.intellij.codeInsight.completion.command.commands.LineCommentCompletionCommand +- com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand +- ():V +- execute(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):V +*f:com.intellij.codeInsight.completion.command.commands.OptimizeImportCompletionCommand +- com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand +- ():V +- isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z +*f:com.intellij.codeInsight.completion.command.commands.ProjectViewCompletionCommand +- com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand +- ():V +- isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z +- supportsReadOnly():Z +*f:com.intellij.codeInsight.completion.command.commands.RecentFilesCompletionCommand +- com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand +- ():V +- isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z +- supportsReadOnly():Z +*f:com.intellij.codeInsight.completion.command.commands.RenameActionCompletionCommand +- com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand +- ():V +- isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z +*f:com.intellij.codeInsight.completion.command.commands.SimpleRunMarkerCommandProvider +- com.intellij.codeInsight.completion.command.CommandProvider +- ():V +- getCommands(com.intellij.codeInsight.completion.command.CommandCompletionProviderContext):java.util.List *:com.intellij.codeInsight.daemon.impl.focusMode.FocusModeProvider - a:calcFocusZones(com.intellij.psi.PsiFile):java.util.List *:com.intellij.codeInsight.editorActions.TypingActionsExtension diff --git a/platform/lang-impl/api-dump-unreviewed.txt b/platform/lang-impl/api-dump-unreviewed.txt index 6393834394b6..dcc14a1c88e2 100644 --- a/platform/lang-impl/api-dump-unreviewed.txt +++ b/platform/lang-impl/api-dump-unreviewed.txt @@ -2133,6 +2133,11 @@ f:com.intellij.codeInsight.completion.addingPolicy.PassDirectlyPolicy - ():V - addAllElements(com.intellij.codeInsight.completion.CompletionResultSet,java.lang.Iterable):V - addElement(com.intellij.codeInsight.completion.CompletionResultSet,com.intellij.codeInsight.lookup.LookupElement):V +f:com.intellij.codeInsight.completion.command.commands.PsiElementCommentCompletionCommand +- com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand +- ():V +- execute(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):V +- isApplicable(I,com.intellij.psi.PsiFile,com.intellij.openapi.editor.Editor):Z c:com.intellij.codeInsight.completion.impl.BetterPrefixMatcher - com.intellij.codeInsight.completion.PrefixMatcher - (com.intellij.codeInsight.completion.PrefixMatcher,I):V diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/AbstractActionCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/AbstractActionCompletionCommand.kt similarity index 60% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/AbstractActionCompletionCommand.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/AbstractActionCompletionCommand.kt index 67cdbf37aa05..28daf3f63472 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/AbstractActionCompletionCommand.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/AbstractActionCompletionCommand.kt @@ -1,9 +1,6 @@ -// 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 +// 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 -import com.intellij.codeInsight.completion.commands.api.ApplicableCompletionCommand -import com.intellij.codeInsight.completion.commands.api.getDataContext -import com.intellij.codeInsight.completion.commands.api.getTargetContext import com.intellij.ide.DataManager import com.intellij.openapi.actionSystem.* import com.intellij.openapi.actionSystem.ex.ActionUtil @@ -13,9 +10,17 @@ import com.intellij.openapi.project.DumbAware import com.intellij.openapi.project.DumbService import com.intellij.psi.PsiComment import com.intellij.psi.PsiFile +import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.Nls import javax.swing.Icon +/** + * 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. + */ +@ApiStatus.Experimental abstract class AbstractActionCompletionCommand( var actionId: String, override val name: String, @@ -37,11 +42,11 @@ abstract class AbstractActionCompletionCommand( override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { val action = action ?: return false if (editor == null) return false - if (!DumbService.getInstance(psiFile.project).isUsableInCurrentContext(action)) 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.NONE, null) + 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 } @@ -53,20 +58,28 @@ abstract class AbstractActionCompletionCommand( 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.NONE, null) + 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) } } - companion object { - internal 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 ch = psiFile.fileDocument.immutableCharSequence[offset - 1] - if (!ch.isLetterOrDigit() && ch != ']' && ch != ')') return true - return false + /** + * 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/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/ApplicationCommandCompletionService.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/ApplicationCommandCompletionService.kt similarity index 72% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/core/ApplicationCommandCompletionService.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/ApplicationCommandCompletionService.kt index cd3a3441c492..70b6dea7ab24 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/ApplicationCommandCompletionService.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/ApplicationCommandCompletionService.kt @@ -1,10 +1,14 @@ -// 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.core +// 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 import com.intellij.openapi.components.* import org.jetbrains.annotations.ApiStatus +/** + * Service responsible for managing and persisting command completion settings at the application level. + * + */ @Service(Service.Level.APP) @ApiStatus.Experimental @ApiStatus.Internal @@ -20,6 +24,8 @@ class ApplicationCommandCompletionService : PersistentStateComponent(lookupElement) { + override fun isWorthShowingInAutoPopup(): Boolean { + return true + } + + override fun getAutoCompletionPolicy(): AutoCompletionPolicy? { + return NEVER_AUTOCOMPLETE + } +} + + +/** + * Constructs a [DataContext] instance using the specified parameters, including the PSI file, + * editor, and optionally a context element. The resulting data context is populated with + * relevant keys and values to be used within the context of the provided elements. + * + * @param psiFile The [PsiFile] associated with the current context. + * @param editor The [Editor] instance corresponding to the current context. + * @param context An optional [PsiElement] representing the context; can be null. + * @return A [DataContext] instance containing the provided context information. + */ +@Internal +@Experimental +fun getDataContext( + psiFile: PsiFile, + editor: Editor, + context: PsiElement?, +): DataContext { + val dataContext = SimpleDataContext.builder() + .add(CommonDataKeys.PROJECT, psiFile.project) + .add(CommonDataKeys.EDITOR, editor) + .add(CommonDataKeys.PSI_ELEMENT, context) + .add(CommonDataKeys.PSI_FILE, psiFile) + .add(LangDataKeys.CONTEXT_LANGUAGES, arrayOf(psiFile.language)) + .build() + return dataContext +} + +/** + * Retrieves the PSI element located at the specified offset within the editor's context. + * This method attempts to find a target PSI element based on the editor's state and the offset, + * allowing interaction with the underlying code structure. + * + * @param offset The offset in the editor's document where the target element is to be located. + * @param editor The editor instance in which the method searches for the target element. + * @return The PSI element found at the specified offset, or null if no suitable element is found + * or if the index is not ready. + */ +@Internal +@Experimental +fun getTargetContext(offset: Int, editor: Editor): PsiElement? { + try { + val util = TargetElementUtil.getInstance() + return util.findTargetElement(editor, util.getReferenceSearchFlags(), offset) + } + catch (_: IndexNotReadyException) { + return null + } +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionNonWriteAccessTypedHandler.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/CommandCompletionNonWriteAccessTypedHandler.kt similarity index 88% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionNonWriteAccessTypedHandler.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/CommandCompletionNonWriteAccessTypedHandler.kt index 60373fa419c6..b9775f3d1869 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionNonWriteAccessTypedHandler.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/CommandCompletionNonWriteAccessTypedHandler.kt @@ -1,5 +1,5 @@ -// 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.core +// 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 import com.intellij.codeInsight.completion.CodeCompletionHandlerBase import com.intellij.codeInsight.completion.CompletionType @@ -20,7 +20,7 @@ import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Key -import com.intellij.openapi.util.registry.Registry.Companion.`is` +import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.wm.IdeFocusManager import com.intellij.psi.PsiDocumentManager @@ -32,9 +32,17 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.jetbrains.annotations.ApiStatus +/** + * Handles non-write access typed events for command-based code completion functionality. + * This class extends the `NonWriteAccessTypedHandler` to provide custom behavior for handling typed characters + * and triggering command completions without modifying the editor's document directly. + * + */ +@ApiStatus.Experimental +@ApiStatus.Internal class CommandCompletionNonWriteAccessTypedHandler : NonWriteAccessTypedHandler { override fun isApplicable(editor: Editor, charTyped: Char, dataContext: DataContext): Boolean { - if (!`is`("java.completion.command.enabled")) return false + if (!Registry.`is`("ide.completion.command.enabled")) return false val project = editor.project ?: return false val commandCompletionService = project.getService(CommandCompletionService::class.java) if (commandCompletionService == null) return false @@ -56,7 +64,7 @@ class CommandCompletionNonWriteAccessTypedHandler : NonWriteAccessTypedHandler { } override fun handle(editor: Editor, charTyped: Char, dataContext: DataContext) { - if (!`is`("java.completion.command.enabled")) return + if (!Registry.`is`("ide.completion.command.enabled")) return val accessCommandCompletionService = editor.project?.getService(NonWriteAccessCommandCompletionService::class.java) if (accessCommandCompletionService == null) return accessCommandCompletionService.insertNewEditor(editor) @@ -70,12 +78,11 @@ internal val ORIGINAL_EDITOR = Key.create>("completion.command @ApiStatus.Experimental @Service(Service.Level.PROJECT) class NonWriteAccessCommandCompletionService( - private val project: Project, val coroutineScope: CoroutineScope, ) { fun insertNewEditor(editor: Editor) { - if (!`is`("java.completion.command.enabled")) return + if (!Registry.`is`("ide.completion.command.enabled")) return if (editor !is EditorImpl) return val project = editor.project ?: return val offset = editor.caretModel.offset diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionProvider.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/CommandCompletionProvider.kt similarity index 74% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionProvider.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/CommandCompletionProvider.kt index 439cf04ed5cc..827ae639db99 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionProvider.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/CommandCompletionProvider.kt @@ -1,10 +1,7 @@ -// 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.core +// 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 import com.intellij.codeInsight.completion.* -import com.intellij.codeInsight.completion.commands.api.CommandCompletionFactory -import com.intellij.codeInsight.completion.commands.api.CommandCompletionProviderContext -import com.intellij.codeInsight.completion.commands.api.CompletionCommand import com.intellij.codeInsight.completion.impl.CamelHumpMatcher import com.intellij.codeInsight.completion.ml.MLWeigherUtil import com.intellij.codeInsight.lookup.LookupElement @@ -36,6 +33,15 @@ import com.intellij.util.ProcessingContext import com.intellij.util.Processor import org.jetbrains.annotations.ApiStatus +/** + * Internal provider for handling command completion in IntelliJ-based editors. + * + * This class extends the `CompletionProvider` to provide custom completion suggestions + * in contexts where command completions are applicable. It is primarily used for adding + * completions for specific command-based scenarios in supported languages or files. + * + */ +@ApiStatus.Internal @ApiStatus.Experimental internal class CommandCompletionProvider : CompletionProvider() { @@ -48,7 +54,7 @@ internal class CommandCompletionProvider : CompletionProvider("add filter for command completion") { - override fun accepts(t: String, context: ProcessingContext?): Boolean { - return !isReadOnly && commandCompletionType.suffix.toString() + t == - commandCompletionFactory.suffix() + commandCompletionFactory.filterSuffix().toString() || - isReadOnly && commandCompletionType.suffix.toString() + t == commandCompletionFactory.filterSuffix().toString() - } - })) + withPrefixMatcher.restartCompletionOnPrefixChange( + StandardPatterns.string().with(object : PatternCondition("add filter for command completion") { + override fun accepts(t: String, context: ProcessingContext?): Boolean { + return !isReadOnly && commandCompletionType.suffix + t == + commandCompletionFactory.suffix() + commandCompletionFactory.filterSuffix().toString() || + isReadOnly && commandCompletionType.suffix.toString() + t == commandCompletionFactory.filterSuffix().toString() + } + })) // Fetch commands applicable to the position processCommandsForContext(commandCompletionFactory, @@ -179,13 +188,24 @@ internal class CommandCompletionProvider : CompletionProvider 0 && text.substring(offset - indexOf, offset - indexOf + 2).contains(factory.suffix())) { + //force call with one point + return InvocationCommandType.PartialSuffix(text.substring(offset - indexOf + 1, offset), + text.substring(offset - indexOf, offset - indexOf + 1)) + } + if (!Registry.`is`("ide.completion.command.full.line.enabled")) return null if (indexOf > 0) { - return InvocationCommandType.PartialSuffix(text.substring(offset - indexOf + 1, offset), text.substring(offset - indexOf, offset - indexOf + 1)) + //full empty line + return InvocationCommandType.FullLine(text.substring(offset - indexOf, offset), "") } return null } @@ -204,10 +224,11 @@ internal class CommandCompletionProvider : CompletionProvider= 0 && (text[offset - currentIndex].isLetter() || + text[offset - currentIndex] == ' ' || + text[offset - currentIndex] == '\'') + ) { + currentIndex++ + } + if (currentIndex <= 1 || text[offset - currentIndex] != '\n') return indexOf + while (currentIndex >= 0 && text[offset - currentIndex].isWhitespace()) { + currentIndex-- + } + if (currentIndex >= 0) indexOf = currentIndex + } return indexOf } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionService.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/CommandCompletionService.kt similarity index 90% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionService.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/CommandCompletionService.kt index f2c756a75395..25734a234132 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionService.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/CommandCompletionService.kt @@ -1,7 +1,7 @@ -// 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.core +// 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 -import com.intellij.codeInsight.completion.commands.api.CommandCompletionFactory +import com.intellij.codeInsight.CodeInsightBundle import com.intellij.codeInsight.daemon.impl.HintRenderer import com.intellij.codeInsight.editorLineStripeHint.EditorLineStripeTextRenderer import com.intellij.codeInsight.highlighting.HighlightManager @@ -12,7 +12,6 @@ import com.intellij.codeInsight.lookup.CharFilter.CUSTOM_DEFAULT_CHAR_FILTERS import com.intellij.codeInsight.lookup.impl.LookupCustomizer import com.intellij.codeInsight.lookup.impl.LookupImpl import com.intellij.codeInsight.template.impl.TemplateColors -import com.intellij.java.JavaBundle import com.intellij.lang.Language import com.intellij.lang.LanguageExtension import com.intellij.lang.injection.InjectedLanguageManager @@ -29,7 +28,6 @@ import com.intellij.openapi.editor.impl.EditorImpl import com.intellij.openapi.editor.markup.HighlighterLayer import com.intellij.openapi.editor.markup.HighlighterTargetArea import com.intellij.openapi.editor.markup.RangeHighlighter -import com.intellij.openapi.project.Project import com.intellij.openapi.util.* import com.intellij.openapi.util.registry.Registry import com.intellij.psi.PsiDocumentManager @@ -43,11 +41,17 @@ import org.jetbrains.annotations.ApiStatus import java.util.concurrent.atomic.AtomicBoolean import kotlin.math.min +/** + * Service class responsible for managing and providing functionality for command completion within a project. + * + * This class allows filtering and enhancing the behavior of code completion for commands, assisting in the + * integration of additional matchers, hint rendering, and custom lookup arrangements. It relies on language-specific + * completion factories to customize behavior according to individual requirements. + */ @ApiStatus.Internal @ApiStatus.Experimental @Service(Service.Level.PROJECT) internal class CommandCompletionService( - private val project: Project, val coroutineScope: CoroutineScope, ) : Disposable { @@ -126,7 +130,7 @@ internal class CommandCompletionService( val state = applicationCommandCompletionService.state if (state.showCounts > 5) return state.showCounts += 1 - val inlineElement: Inlay = editor.inlayModel.addInlineElement(endOffset, true, EditorLineStripeTextRenderer(" " + JavaBundle.message("command.completion.filter.hint"))) + val inlineElement: Inlay = editor.inlayModel.addInlineElement(endOffset, true, EditorLineStripeTextRenderer(" " + CodeInsightBundle.message("command.completion.filter.hint", completionFactory.filterSuffix()))) ?: return Disposer.register(lookup, inlineElement) Disposer.register(lookup) { lookup.removeUserData(INSTALLED_HINT) } @@ -156,7 +160,7 @@ private const val PROMPT_LAYER = HighlighterLayer.ERROR + 10 private class CommandCompletionListener : LookupManagerListener { override fun activeLookupChanged(oldLookup: Lookup?, newLookup: Lookup?) { - if (!Registry.`is`("java.completion.command.enabled")) return + if (!Registry.`is`("ide.completion.command.enabled")) return var editor = newLookup?.editor ?: return val originalEditor = editor.getUserData(ORIGINAL_EDITOR) var psiFile = newLookup.psiFile ?: return @@ -253,7 +257,8 @@ private class CommandCompletionHighlightingListener( private fun update(lookup: LookupImpl, item: CommandCompletionLookupElement) { val installed = ConcurrencyUtil.computeIfAbsent(lookup, INSTALLED_LISTENER_KEY) { AtomicBoolean(false) } - val startOffset = lookup.lookupOriginalStart - findActualIndex(item.suffix, editor.document.immutableCharSequence, lookup.lookupOriginalStart) + val startOffset = lookup.lookupOriginalStart - findActualIndex(item.suffix, editor.document.immutableCharSequence, + lookup.lookupOriginalStart) val lookupEditor = InjectedLanguageEditorUtil.getTopLevelEditor(lookup.editor) val endOffset = lookupEditor.caretModel.offset if (!installed.get()) { @@ -295,7 +300,9 @@ private class CommandCompletionHighlightingListener( val previousHighlighting = lookup.removeUserData(LOOKUP_HIGHLIGHTING) previousHighlighting?.forEach { t -> highlightManager.removeSegmentHighlighter(editor, t) } val startOffset = lookup.lookupOriginalStart - - if (nonWrittenFiles) 0 else findActualIndex(element.suffix, editor.document.immutableCharSequence, lookup.lookupOriginalStart) + if (nonWrittenFiles) 0 + else findActualIndex(element.suffix, editor.document.immutableCharSequence, + lookup.lookupOriginalStart) val highlightInfo = element.highlighting ?: return val rangeHighlighters = mutableListOf() val endOffset = min(highlightInfo.range.endOffset, startOffset) @@ -323,9 +330,17 @@ private class CommandCompletionHighlightingListener( } } +/** + * A custom implementation of [CharFilter] that provides specific behavior for handling character inputs + * during completion, particularly for commands. + * This implementation works in conjunction with the `CommandCompletionService`. + * This filter evaluates whether a given character should modify the current completion prefix, + * select an item, or hide the lookup based on various conditions, such as the presence of + * specific data in the current lookup or the state of the caret/editor. + */ private class CommandCompletionCharFilter : CharFilter() { override fun acceptChar(c: Char, prefixLength: Int, lookup: Lookup?): Result? { - if (!Registry.`is`("java.completion.command.enabled")) return null + if (!Registry.`is`("ide.completion.command.enabled")) return null if (lookup !is LookupImpl) return null val completionService = lookup.project.service() val installedHint = lookup.removeUserData(INSTALLED_HINT) @@ -351,9 +366,13 @@ private class CommandCompletionCharFilter : CharFilter() { } } +/** + * A private implementation of the `LookupCustomizer` interface that modifies a lookup instance + * to insert additional flags. + */ private class CommandCompletionLookupCustomizer : LookupCustomizer { override fun customizeLookup(lookupImpl: LookupImpl) { - if (!Registry.`is`("java.completion.command.enabled")) return + if (!Registry.`is`("ide.completion.command.enabled")) return val project = lookupImpl.project val service = project.service() val editor = lookupImpl.editor diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandInsertHandler.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/CommandInsertHandler.kt similarity index 90% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandInsertHandler.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/CommandInsertHandler.kt index d630a7842e1d..97ebdebfd2b6 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandInsertHandler.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/CommandInsertHandler.kt @@ -1,9 +1,8 @@ -// 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.core +// 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 import com.intellij.codeInsight.completion.InsertHandler import com.intellij.codeInsight.completion.InsertionContext -import com.intellij.codeInsight.completion.commands.api.CompletionCommand import com.intellij.codeInsight.lookup.LookupElement import com.intellij.injected.editor.DocumentWindow import com.intellij.lang.injection.InjectedLanguageManager @@ -17,12 +16,18 @@ import com.intellij.psi.impl.source.tree.injected.InjectedLanguageEditorUtil import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil import org.jetbrains.annotations.ApiStatus +/** + * A handler for managing the insertion of commands during code completion. + * This class manages specific behaviors that occur when a command from the lookup + * list is selected and inserted into the editor. + */ +@ApiStatus.Internal @ApiStatus.Experimental internal class CommandInsertHandler(private val completionCommand: CompletionCommand) : InsertHandler { override fun handleInsert(context: InsertionContext, item: LookupElement) { var editor = context.editor val originalEditor = editor.getUserData(ORIGINAL_EDITOR) - var startOffset = 0 + var startOffset: Int var psiFile = context.file if (originalEditor != null) { startOffset = originalEditor.second diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractCopyFQNCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractCopyFQNCompletionCommand.kt new file mode 100644 index 000000000000..11edd19b1331 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractCopyFQNCompletionCommand.kt @@ -0,0 +1,39 @@ +// 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.command.commands + +import com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand +import com.intellij.ide.IdeBundle +import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import org.jetbrains.annotations.ApiStatus + +/** + * Represents an abstract command for copying Fully Qualified Names (FQN) as part of the code completion process. + * This command provides functionality to determine its applicability based on a specific element within the PSI + * file and the current editor context. + */ +@ApiStatus.Experimental +abstract class AbstractCopyFQNCompletionCommand : AbstractActionCompletionCommand("CopyReference", + "Copy Reference", + IdeBundle.message("copy.reference"), + null, + -150) { + final override fun supportsReadOnly(): Boolean = true + + final override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { + if (!super.isApplicable(offset, psiFile, editor)) return false + val element = getContext(offset, psiFile) + if (element == null) return false + return placeIsApplicable(element, offset) + } + + /** + * Determines whether a specific context within a PSI element is applicable for the command execution. + * + * @param element The PSI element to evaluate, or null if no context is applicable. + * @param offset The position in the document where the applicability is being checked. + * @return true if the place is applicable based on the provided element and offset, false otherwise. + */ + abstract fun placeIsApplicable(element: PsiElement, offset: Int): Boolean +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractFormatCodeCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractFormatCodeCompletionCommand.kt new file mode 100644 index 000000000000..262996571717 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractFormatCodeCompletionCommand.kt @@ -0,0 +1,47 @@ +// 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.actions.ReformatCodeProcessor +import com.intellij.codeInsight.completion.command.ApplicableCompletionCommand +import com.intellij.icons.AllIcons +import com.intellij.idea.ActionsBundle +import com.intellij.lang.injection.InjectedLanguageManager +import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import org.jetbrains.annotations.ApiStatus +import org.jetbrains.annotations.Nls +import javax.swing.Icon + +/** + * An abstract command designed to provide a context-specific code formatting completion action. + * This class handles the completion logic for reformatting code based on the PSI (Program Structure Interface) + * context and specific applicability conditions. + */ +@ApiStatus.Experimental +abstract class AbstractFormatCodeCompletionCommand : ApplicableCompletionCommand() { + final override val name: String + get() = "Format" + + final override val i18nName: @Nls String + get() = ActionsBundle.message("action.ReformatCode.text") + + final override val icon: Icon + get() = AllIcons.Actions.ReformatCode // Use the reformat icon + + final override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { + if (InjectedLanguageManager.getInstance(psiFile.project).isInjectedFragment(psiFile)) return false + return true + } + + final override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) { + val element = getContext(offset, psiFile) ?: return + val target = findTargetToRefactor(element) + ReformatCodeProcessor(element.containingFile, arrayOf(target.textRange)).run() + } + + /** + * Finds the appropriate parent element for refactoring based on the given PSI element. + */ + abstract fun findTargetToRefactor(element: PsiElement): PsiElement +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractGenerateCommandProvider.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractGenerateCommandProvider.kt new file mode 100644 index 000000000000..84646c3b0aa6 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractGenerateCommandProvider.kt @@ -0,0 +1,97 @@ +// 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.CodeInsightBundle +import com.intellij.codeInsight.actions.CodeInsightAction +import com.intellij.codeInsight.completion.command.* +import com.intellij.ide.DataManager +import com.intellij.lang.injection.InjectedLanguageManager +import com.intellij.openapi.actionSystem.* +import com.intellij.openapi.actionSystem.ex.ActionUtil +import com.intellij.openapi.actionSystem.impl.Utils +import com.intellij.openapi.editor.Document +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.DumbAware +import com.intellij.openapi.project.DumbService +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.util.containers.JBIterable +import org.jetbrains.annotations.ApiStatus.Experimental +import org.jetbrains.annotations.Nls +import javax.swing.Icon + +/** + * Abstract base class that provides a framework for generating completion commands + * in a code completion system. + */ +@Experimental +abstract class AbstractGenerateCommandProvider : CommandProvider, DumbAware { + final override fun getCommands(context: CommandCompletionProviderContext): List { + val psiFile = context.psiFile + val project = context.project + val offset = context.offset + val editor = context.editor + if (InjectedLanguageManager.getInstance(psiFile.project).isInjectedFragment(psiFile)) return emptyList() + val element = psiFile.findElementAt(if (offset - 1 >= 0) offset - 1 else offset) + if (element == null) return emptyList() + if (!generationIsAvailable(element, offset)) return emptyList() + val context = getTargetContext(offset, editor) + val dataContext = getDataContext(psiFile, editor, context) + val actionEvent = AnActionEvent.createEvent(dataContext, null, ActionPlaces.UNKNOWN, ActionUiKind.Companion.NONE, null) + actionEvent.updateSession = UpdateSession.EMPTY + Utils.initUpdateSession(actionEvent) + val generateActions: MutableList = ArrayList() + val session = actionEvent.updateSession + val dumbService = DumbService.Companion.getInstance(project) + val activeActions = JBIterable.from( + session.expandedChildren(ActionManager.getInstance().getAction(IdeActions.GROUP_GENERATE) as ActionGroup)) + .filter { dumbService.isUsableInCurrentContext(it) } + .filter { o: AnAction? -> o !is Separator && o != null && session.presentation(o).isEnabledAndVisible } + for (action in activeActions) { + if (action is CodeInsightAction) { + generateActions.add(action) + } + } + return generateActions.map { GenerateCompletionCommand(it) } + } + + protected fun isOnlySpaceInLine(document: Document, offset: Int): Boolean { + val lineNumber = document.getLineNumber(offset) + val lineStartOffset = document.getLineStartOffset(lineNumber) + val immutableCharSequence = document.immutableCharSequence + for (i in lineStartOffset..offset) { + if (!immutableCharSequence[i].isWhitespace()) { + return false + } + } + return true + } + + /** + * Determines if generation actions are available for the provided PSI element. + * + * @param element A PSI element for which the availability of generation actions is to be checked. + * @param offset A current offset. + * @return `true` if generation actions are available for the specified element; `false` otherwise. + */ + abstract fun generationIsAvailable(element: PsiElement, offset: Int): Boolean +} + +internal class GenerateCompletionCommand(private val action: CodeInsightAction) : CompletionCommand() { + override val name: String + get() = "Generate \'" + action.templateText + "\'" + override val i18nName: @Nls String + get() = CodeInsightBundle.message("command.completion.generate.text", action.templateText) + override val icon: Icon? + get() = null + + override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) { + 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.UNKNOWN, ActionUiKind.NONE, null) + if (ActionUtil.lastUpdateAndCheckDumb(action, event, false)) { + ActionUtil.performActionDumbAwareWithCallbacks(action, event) + } + } +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractGoToDeclarationCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractGoToDeclarationCompletionCommand.kt new file mode 100644 index 000000000000..00e0bf75a249 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractGoToDeclarationCompletionCommand.kt @@ -0,0 +1,38 @@ +// 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.AbstractActionCompletionCommand +import com.intellij.idea.ActionsBundle +import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import org.jetbrains.annotations.ApiStatus.Experimental + +/** + * Represents an abstract completion command specifically designed for navigating to declarations + * in the code editor. + */ +@Experimental +abstract class AbstractGoToDeclarationCompletionCommand : AbstractActionCompletionCommand("GotoDeclarationOnly", + "Go to Declaration", + ActionsBundle.message("action.GotoDeclarationOnly.text"), + null) { + final override fun supportsReadOnly(): Boolean = true + + final override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { + return super.isApplicable(offset, psiFile, editor) && hasToShow(offset, psiFile) + } + + private fun hasToShow(offset: Int, psiFile: PsiFile): Boolean { + val context = (getContext(offset, psiFile)) ?: return false + return canNavigateToDeclaration(context) + } + + /** + * Determines whether it is possible to navigate to a declaration for a given context element in the code. + * + * @param context the PsiElement representing the context in which the navigation is being evaluated + * @return true if navigation to the declaration is possible, false otherwise + */ + abstract fun canNavigateToDeclaration(context: PsiElement): Boolean +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractGoToImplementationCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractGoToImplementationCompletionCommand.kt new file mode 100644 index 000000000000..23674f7d92aa --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractGoToImplementationCompletionCommand.kt @@ -0,0 +1,36 @@ +// 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.AbstractActionCompletionCommand +import com.intellij.idea.ActionsBundle +import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import org.jetbrains.annotations.ApiStatus + +/** + * Represents an abstract command to navigate to the implementation(s) + * of a given element within a code editor during code completion processes. + */ +@ApiStatus.Experimental +abstract class AbstractGoToImplementationCompletionCommand : AbstractActionCompletionCommand("GotoImplementation", + "Go to Implementation", + ActionsBundle.message("action.GotoImplementation.text"), + null) { + final override fun supportsReadOnly(): Boolean = true + + final override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { + if (!super.isApplicable(offset, psiFile, editor)) return false + val element = getContext(offset, psiFile) ?: return false + return canGoToImplementation(element, offset) + } + + /** + * Determines if navigation to the implementation of the specified element is possible. + * + * @param element the PSI element for which the implementation is to be checked; can be null + * @param offset the offset within the element's document, typically representing the caret's position + * @return true if navigation to the implementation is possible, otherwise false + */ + abstract fun canGoToImplementation(element: PsiElement, offset: Int): Boolean +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractShowUsagesActionCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractShowUsagesActionCompletionCommand.kt new file mode 100644 index 000000000000..f7a1eb219621 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/AbstractShowUsagesActionCompletionCommand.kt @@ -0,0 +1,38 @@ +// 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.AbstractActionCompletionCommand +import com.intellij.find.actions.ShowUsagesAction +import com.intellij.idea.ActionsBundle +import com.intellij.lang.injection.InjectedLanguageManager +import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import org.jetbrains.annotations.ApiStatus.Experimental + +/** + * Abstract base class for defining commands that are intended to execute the "Show Usages" action + * specifically within the context of code completion. + */ +@Experimental +abstract class AbstractShowUsagesActionCompletionCommand : AbstractActionCompletionCommand(ShowUsagesAction.ID, + "Show usages", + ActionsBundle.message("action.ShowUsages.text"), + null) { + final override fun supportsReadOnly(): Boolean = true + + final override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { + val element = getContext(offset, psiFile) ?: return false + return super.isApplicable(offset, psiFile, editor) && hasToShow(element) && + !InjectedLanguageManager.getInstance(psiFile.project).isInjectedFragment(psiFile) + } + + /** + * Determines whether a given PSI element should be used to show its usages + * in the context of the command execution. + * + * @param element the PSI element being checked; may be null + * @return true if the element should be shown, false otherwise + */ + abstract fun hasToShow(element: PsiElement): Boolean +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/DirectErrorFixCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/DirectErrorFixCompletionCommand.kt similarity index 92% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/DirectErrorFixCompletionCommand.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/DirectErrorFixCompletionCommand.kt index bfd78960ec17..87613d733ffe 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/DirectErrorFixCompletionCommand.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/DirectErrorFixCompletionCommand.kt @@ -1,9 +1,9 @@ // 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 +package com.intellij.codeInsight.completion.command.commands import com.intellij.analysis.AnalysisBundle.message -import com.intellij.codeInsight.completion.commands.api.CompletionCommand -import com.intellij.codeInsight.completion.commands.core.HighlightInfoLookup +import com.intellij.codeInsight.completion.command.CompletionCommand +import com.intellij.codeInsight.completion.command.HighlightInfoLookup import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator import com.intellij.codeInsight.daemon.impl.HighlightInfo.IntentionActionDescriptor import com.intellij.codeInsight.daemon.impl.HighlightVisitorBasedInspection.runAnnotatorsInGeneralHighlighting @@ -18,9 +18,11 @@ import com.intellij.platform.ide.progress.runWithModalProgressBlocking import com.intellij.psi.PsiFile import com.intellij.psi.impl.source.tree.injected.InjectedLanguageEditorUtil import kotlinx.coroutines.job +import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.Nls import javax.swing.Icon +@ApiStatus.Experimental internal class DirectErrorFixCompletionCommand( override val name: @Nls String, override val priority: Int?, @@ -58,7 +60,6 @@ internal class DirectErrorFixCompletionCommand( for (fix in fixes) { if (fix.action.text == name) { return@jobToIndicator fix.action - break } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/DirectInspectionFixCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/DirectInspectionFixCompletionCommand.kt similarity index 91% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/DirectInspectionFixCompletionCommand.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/DirectInspectionFixCompletionCommand.kt index 219349ae735a..1c0fde8db3d3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/DirectInspectionFixCompletionCommand.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/DirectInspectionFixCompletionCommand.kt @@ -1,9 +1,9 @@ -// 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 +// 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.analysis.AnalysisBundle.message -import com.intellij.codeInsight.completion.commands.api.CompletionCommand -import com.intellij.codeInsight.completion.commands.core.HighlightInfoLookup +import com.intellij.codeInsight.completion.command.CompletionCommand +import com.intellij.codeInsight.completion.command.HighlightInfoLookup import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.availableFor @@ -25,10 +25,12 @@ import com.intellij.psi.PsiFile import com.intellij.psi.impl.source.tree.injected.InjectedLanguageEditorUtil import com.intellij.psi.util.PsiTreeUtil import kotlinx.coroutines.job +import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.Nls import javax.swing.Icon -class DirectInspectionFixCompletionCommand( +@ApiStatus.Experimental +internal class DirectInspectionFixCompletionCommand( private val inspectionId: String, override val name: @Nls String, override val priority: Int?, @@ -47,7 +49,7 @@ class DirectInspectionFixCompletionCommand( val topLevelOffset = injectedLanguageManager.injectedToHost(psiFile, offset) val isInjected = topLevelFile != psiFile - var action: IntentionAction? = runWithModalProgressBlocking(psiFile.project, message("scanning.scope.progress.title")) { + val action: IntentionAction? = runWithModalProgressBlocking(psiFile.project, message("scanning.scope.progress.title")) { val profileToUse = ProjectInspectionProfileManager.Companion.getInstance(psiFile.project).getCurrentProfile() val inspectionWrapper = InspectionProfileWrapper(profileToUse) var inspectionTool = inspectionWrapper.inspectionProfile.getInspectionTool(inspectionId, psiFile.project) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/DirectIntentionCommandProvider.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/DirectIntentionCommandProvider.kt similarity index 87% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/DirectIntentionCommandProvider.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/DirectIntentionCommandProvider.kt index 9a6af98fd339..1575a2760278 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/DirectIntentionCommandProvider.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/DirectIntentionCommandProvider.kt @@ -1,20 +1,12 @@ -// 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 +// 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.commands.api.CommandCompletionProviderContext -import com.intellij.codeInsight.completion.commands.api.CommandProvider -import com.intellij.codeInsight.completion.commands.api.CompletionCommand -import com.intellij.codeInsight.completion.commands.core.CommandCompletionUnsupportedOperationException -import com.intellij.codeInsight.completion.commands.core.HighlightInfoLookup +import com.intellij.codeInsight.completion.command.* import com.intellij.codeInsight.daemon.HighlightDisplayKey import com.intellij.codeInsight.daemon.impl.* import com.intellij.codeInsight.daemon.impl.HighlightInfo.IntentionActionDescriptor import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass.IntentionsInfo -import com.intellij.codeInsight.daemon.impl.quickfix.CreateGetterOrSetterFix -import com.intellij.codeInsight.daemon.impl.quickfix.ExpensivePsiIntentionAction -import com.intellij.codeInsight.intention.EmptyIntentionAction -import com.intellij.codeInsight.intention.IntentionAction -import com.intellij.codeInsight.intention.IntentionManager +import com.intellij.codeInsight.intention.* import com.intellij.codeInsight.intention.impl.CachedIntentions import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler import com.intellij.codeInsight.intention.impl.preview.IntentionPreviewUnsupportedOperationException @@ -28,10 +20,10 @@ import com.intellij.codeInspection.ex.LocalInspectionToolWrapper import com.intellij.codeInspection.ex.QuickFixWrapper import com.intellij.icons.AllIcons import com.intellij.lang.Language +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.modcommand.ModCommandService import com.intellij.openapi.application.readAction import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.Editor @@ -43,8 +35,11 @@ import com.intellij.openapi.progress.runBlockingCancellable import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.IndexNotReadyException import com.intellij.openapi.project.ProjectTypeService +import com.intellij.openapi.util.Iconable +import com.intellij.openapi.util.Iconable.ICON_FLAG_VISIBILITY import com.intellij.openapi.util.Key import com.intellij.openapi.util.TextRange +import com.intellij.openapi.util.registry.Registry import com.intellij.profile.codeInspection.ProjectInspectionProfileManager.Companion.getInstance import com.intellij.psi.PsiFile import com.intellij.psi.impl.source.tree.injected.InjectedLanguageEditorUtil @@ -53,10 +48,17 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.job +import org.jetbrains.annotations.ApiStatus import java.util.function.Predicate +/** + * Provides supplying completion commands + * based on intentions, errors, and inspections within the given context. + */ +@ApiStatus.Experimental class DirectIntentionCommandProvider : CommandProvider { override fun getCommands(context: CommandCompletionProviderContext): List { + if (!Registry.`is`("ide.completion.command.enabled")) return emptyList() return runBlockingCancellable { val originalEditor = context.originalEditor val psiFile = context.psiFile @@ -79,7 +81,7 @@ class DirectIntentionCommandProvider : CommandProvider { } val asyncInspections = if (cachedInspectionCommand == null) asyncInspectionHighlighting(psiFile, editor, offset) else null - val asyncIntentions = asyncIntentions(editor, psiFile, originalPsiFile) + val asyncIntentions = asyncIntentions(editor, psiFile, originalPsiFile, offset) val errors = asyncErrorHighlighting?.await() errors?.let { @@ -128,7 +130,7 @@ class DirectIntentionCommandProvider : CommandProvider { offset: Int, ): Deferred> = async { return@async readAction { - + val intentionCommandSkipper = IntentionCommandSkipper.EP_NAME.forLanguage(psiFile.language) val injectedLanguageManager = InjectedLanguageManager.getInstance(psiFile.project) val topLevelFile = injectedLanguageManager.getTopLevelFile(psiFile) val topLevelEditor = InjectedLanguageEditorUtil.getTopLevelEditor(editor) @@ -175,13 +177,8 @@ class DirectIntentionCommandProvider : CommandProvider { val level = ProblemDescriptorUtil.highlightTypeFromDescriptor(descriptor, severity, severityRegistrar) for (i in 0..fixes.size - 1) { val action = QuickFixWrapper.wrap(descriptor, i) - if (action is EmptyIntentionAction || action is ExpensivePsiIntentionAction) continue - val fix = QuickFixWrapper.unwrap(action) - if (fix != null) { - val unwrappedAction = ModCommandService.getInstance().unwrap(fix) - //skip, probably create extensions - if (unwrappedAction is CreateGetterOrSetterFix) continue - } + if (action is EmptyIntentionAction) continue + if (intentionCommandSkipper != null && intentionCommandSkipper.skip(action, psiFile, offset)) continue if (!isInjected && !ShowIntentionActionsHandler.availableFor(topLevelFile, topLevelEditor, topLevelOffset, action)) continue if (isInjected && !ShowIntentionActionsHandler.availableFor(psiFile, editor, offset, action)) continue val priority = if (level.getSeverity(null) == INFORMATION) 70 else 80 @@ -245,14 +242,19 @@ class DirectIntentionCommandProvider : CommandProvider { editor: Editor, psiFile: PsiFile, originalFile: PsiFile, + offset: Int, ): Deferred> = async { return@async readAction { - + val intentionCommandSkipper = IntentionCommandSkipper.EP_NAME.forLanguage(psiFile.language) val availableIntentions = IntentionManager.getInstance().getAvailableIntentions(mutableListOf(originalFile.language.id)) val actionsToShow = IntentionsInfo() for (action in availableIntentions) { + val intentionAction = IntentionActionDelegate.unwrap(action) val descriptor = - IntentionActionDescriptor(action, null, null, null, null, null, null, null) + IntentionActionDescriptor(action, null, null, + if (intentionAction is Iconable) + intentionAction.getIcon(ICON_FLAG_VISIBILITY) + else null, null, null, null, null) actionsToShow.intentionsToShow.add(descriptor) } val dumbService = DumbService.getInstance(originalFile.project) @@ -267,6 +269,7 @@ class DirectIntentionCommandProvider : CommandProvider { ProgressManager.checkCanceled() if (!dumbService.isUsableInCurrentContext(intention) || !intention.action.isAvailable(originalFile.project, editor, psiFile) && + //todo temporary workaround for AI intention.action.familyName !in ("AI Actions…") || !filter.test(intention.action)) { toRemove.add(intention) @@ -286,18 +289,15 @@ class DirectIntentionCommandProvider : CommandProvider { intentionsCache.wrapAndUpdateActions(actionsToShow, false) val result = mutableListOf() for (intention in intentionsCache.intentions) { - if (intention.action is EmptyIntentionAction || intention.action is ExpensivePsiIntentionAction) continue + if (intention.action is EmptyIntentionAction || + intentionCommandSkipper != null && intentionCommandSkipper.skip(intention.action, psiFile, offset)) continue val intentionCommand = - IntentionCompletionCommand(intention, 50, AllIcons.Actions.IntentionBulbGrey, null) + IntentionCompletionCommand(intention, 50, intention.icon ?: AllIcons.Actions.IntentionBulbGrey, null) result.add(intentionCommand) } return@readAction result } } - - override fun getId(): String { - return "DirectIntentionCommandProvider" - } } internal fun getLineRange(psiFile: PsiFile, offset: Int): TextRange { @@ -343,3 +343,13 @@ private fun getInspectionTools(profile: InspectionProfileWrapper, file: PsiFile) } return enabled } + +@ApiStatus.Experimental +interface IntentionCommandSkipper { + companion object { + internal val EP_NAME: LanguageExtension = LanguageExtension("com.intellij.codeInsight.completion.intention.skipper") + } + + fun skip(action: CommonIntentionAction, psiFile: PsiFile, offset: Int): Boolean = false +} + 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 new file mode 100644 index 000000000000..b4a65d498224 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/ExtensionBasedCommandProvider.kt @@ -0,0 +1,35 @@ +// 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.lang.LanguageExtension +import com.intellij.openapi.project.DumbAware +import com.intellij.openapi.project.DumbService +import com.intellij.openapi.util.registry.Registry +import org.jetbrains.annotations.ApiStatus + +/** + * An implementation of the CommandProvider interface that supplies completion commands + * registered through extension points ('com.intellij.codeInsight.completion.applicable.command') for specific programming languages. + */ +@ApiStatus.Experimental +internal class ExtensionPointCommandProvider : CommandProvider, DumbAware { + override fun getCommands(context: CommandCompletionProviderContext): List { + if (!Registry.`is`("ide.completion.command.enabled")) 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/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/IntentionCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/IntentionCompletionCommand.kt similarity index 72% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/IntentionCompletionCommand.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/IntentionCompletionCommand.kt index fc03982020f8..e2c077c6cc5c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/IntentionCompletionCommand.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/IntentionCompletionCommand.kt @@ -1,16 +1,18 @@ -// 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 +// 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.commands.api.CompletionCommand -import com.intellij.codeInsight.completion.commands.core.HighlightInfoLookup +import com.intellij.codeInsight.completion.command.CompletionCommand +import com.intellij.codeInsight.completion.command.HighlightInfoLookup import com.intellij.codeInsight.intention.impl.IntentionActionWithTextCaching import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler import com.intellij.openapi.editor.Editor import com.intellij.psi.PsiFile +import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.Nls import javax.swing.Icon -class IntentionCompletionCommand( +@ApiStatus.Experimental +internal class IntentionCompletionCommand( private val intentionAction: IntentionActionWithTextCaching, override val priority: Int?, override val icon: Icon?, diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/LineCommentCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/LineCommentCompletionCommand.kt similarity index 75% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/LineCommentCompletionCommand.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/LineCommentCompletionCommand.kt index 5ac4a3134668..6c448bfee005 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/LineCommentCompletionCommand.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/LineCommentCompletionCommand.kt @@ -1,10 +1,17 @@ -// 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 +// 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.AbstractActionCompletionCommand import com.intellij.idea.ActionsBundle import com.intellij.openapi.editor.Editor import com.intellij.psi.PsiFile +import org.jetbrains.annotations.ApiStatus.Experimental +/** + * A command that executes the "Comment by Line Comment" action + * within the context of code completion. + */ +@Experimental class LineCommentCompletionCommand : AbstractActionCompletionCommand("CommentByLineComment", "Comment line", ActionsBundle.actionText("CommentByLineComment"), diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/OptimizeImportCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/OptimizeImportCompletionCommand.kt similarity index 72% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/OptimizeImportCompletionCommand.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/OptimizeImportCompletionCommand.kt index 764a7fbd6dd3..4dcc3a910136 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/OptimizeImportCompletionCommand.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/OptimizeImportCompletionCommand.kt @@ -1,11 +1,17 @@ -// 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 +// 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.AbstractActionCompletionCommand import com.intellij.idea.ActionsBundle import com.intellij.lang.injection.InjectedLanguageManager import com.intellij.openapi.editor.Editor import com.intellij.psi.PsiFile +import org.jetbrains.annotations.ApiStatus.Experimental +/** + * A command that triggers the "Optimize Imports" action within the context of code completion. + */ +@Experimental class OptimizeImportCompletionCommand : AbstractActionCompletionCommand("OptimizeImports", "Optimize imports", ActionsBundle.message("action.OptimizeImports.text"), diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/ProjectViewCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/ProjectViewCompletionCommand.kt similarity index 64% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/ProjectViewCompletionCommand.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/ProjectViewCompletionCommand.kt index f625d71ff919..206a9f9f4c5c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/ProjectViewCompletionCommand.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/ProjectViewCompletionCommand.kt @@ -1,14 +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 +// 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.CodeInsightBundle +import com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand import com.intellij.ide.actions.ActivateToolWindowAction.Manager -import com.intellij.java.JavaBundle import com.intellij.openapi.editor.Editor import com.intellij.psi.PsiFile +import org.jetbrains.annotations.ApiStatus +/** + * A completion command, which triggers the "Project" tool window in the IDE. + */ +@ApiStatus.Experimental class ProjectViewCompletionCommand : AbstractActionCompletionCommand(Manager.getActionIdForToolWindow("Project"), "Project tool", - JavaBundle.message("command.completion.project.tool.text"), + CodeInsightBundle.message("command.completion.project.tool.text"), null, -150) { override fun supportsReadOnly(): Boolean = true diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaPsiElementCommentCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/PsiElementCommentCompletionCommand.kt similarity index 79% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaPsiElementCommentCompletionCommand.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/PsiElementCommentCompletionCommand.kt index 2897459293c4..199bd230a3c3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/JavaPsiElementCommentCompletionCommand.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/PsiElementCommentCompletionCommand.kt @@ -1,15 +1,16 @@ -// 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 +// 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.java.JavaBundle +import com.intellij.codeInsight.CodeInsightBundle +import com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand import com.intellij.openapi.editor.Editor import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile -class JavaPsiElementCommentCompletionCommand : AbstractActionCompletionCommand("CommentByLineComment", - "Comment element", - JavaBundle.message("java.psi.element.comment.completion.command.text"), - null) { +class PsiElementCommentCompletionCommand : AbstractActionCompletionCommand("CommentByLineComment", + "Comment element", + CodeInsightBundle.message("command.completion.psi.element.comment.text"), + null) { override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { if (!super.isApplicable(offset, psiFile, editor)) return false diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/RecentFilesCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/RecentFilesCompletionCommand.kt similarity index 61% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/RecentFilesCompletionCommand.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/RecentFilesCompletionCommand.kt index 247306ef231a..20bbea120110 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/RecentFilesCompletionCommand.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/RecentFilesCompletionCommand.kt @@ -1,13 +1,19 @@ -// 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 +// 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.java.JavaBundle +import com.intellij.codeInsight.CodeInsightBundle +import com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand import com.intellij.openapi.editor.Editor import com.intellij.psi.PsiFile +import org.jetbrains.annotations.ApiStatus +/** + * This command enables triggering the "Recent Files" action during code completion. + */ +@ApiStatus.Experimental class RecentFilesCompletionCommand : AbstractActionCompletionCommand("RecentFiles", "Recent files", - JavaBundle.message("command.completion.recent.files.text"), + CodeInsightBundle.message("command.completion.recent.files.text"), null, -150) { override fun supportsReadOnly(): Boolean = true diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/RenameActionCompletionCommand.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/RenameActionCompletionCommand.kt similarity index 77% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/RenameActionCompletionCommand.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/RenameActionCompletionCommand.kt index 45ff01ad0fbe..346ff55634b8 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/RenameActionCompletionCommand.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/RenameActionCompletionCommand.kt @@ -1,16 +1,19 @@ -// 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 +// 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.commands.api.getTargetContext +import com.intellij.codeInsight.completion.command.AbstractActionCompletionCommand +import com.intellij.codeInsight.completion.command.getTargetContext import com.intellij.idea.ActionsBundle import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.editor.Editor import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiNamedElement +import org.jetbrains.annotations.ApiStatus +@ApiStatus.Experimental class RenameActionCompletionCommand : AbstractActionCompletionCommand(IdeActions.ACTION_RENAME, - "rename", + "Rename", ActionsBundle.message("action.RenameElement.text"), null) { override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/SimpleRunMarkerCommandProvider.kt b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/SimpleRunMarkerCommandProvider.kt similarity index 91% rename from java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/SimpleRunMarkerCommandProvider.kt rename to platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/SimpleRunMarkerCommandProvider.kt index ec232e1126d2..42ae4bb9b8c1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/SimpleRunMarkerCommandProvider.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/command/commands/SimpleRunMarkerCommandProvider.kt @@ -1,7 +1,8 @@ -// 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 +// 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.commands.api.* +import com.intellij.analysis.AnalysisBundle +import com.intellij.codeInsight.completion.command.* import com.intellij.codeInsight.daemon.MergeableLineMarkerInfo import com.intellij.codeInsight.daemon.impl.GutterIntentionAction import com.intellij.codeInsight.daemon.impl.IntentionActionFilter @@ -23,13 +24,19 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.util.Iconable import com.intellij.openapi.util.NlsSafe import com.intellij.openapi.util.text.StringUtil +import com.intellij.platform.ide.progress.runWithModalProgressBlocking import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.util.containers.JBIterable.from +import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.Nls import java.util.function.Predicate import javax.swing.Icon +/** + * A provider for run marker-related completion commands in the code editor. + */ +@ApiStatus.Experimental class SimpleRunMarkerCommandProvider : CommandProvider { override fun getCommands(context: CommandCompletionProviderContext): List { return runBlockingCancellable { @@ -88,10 +95,6 @@ class SimpleRunMarkerCommandProvider : CommandProvider { return@runBlockingCancellable commands } } - - override fun getId(): String { - return "RunMarkerCommandProvider" - } } private fun collectActions( @@ -137,7 +140,9 @@ private class RunMarkerCompletionCommand( get() = "" override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) { - val (_, collectedActions) = collectActions(psiFile, offsetElement, psiFile.project) + val (_, collectedActions) = runWithModalProgressBlocking(psiFile.project, AnalysisBundle.message("scanning.scope.progress.title")) { + collectActions(psiFile, offsetElement, psiFile.project) + } val cachedIntentions = CachedIntentions(psiFile.project, psiFile, editor) val intentionsInfo = ShowIntentionsPass.IntentionsInfo() intentionsInfo.guttersToShow.addAll(collectedActions) diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index e2cc96a9e55d..3a534fef47e1 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -748,5 +748,22 @@ qualifiedName="com.intellij.microservices.featuresAvailabilityProvider" dynamic="true"/> + + + + + + + + + + + + + diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index e5f806672bc1..5118c154a25e 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -726,7 +726,8 @@ id="liveTemplates" order="first"/> - + @@ -736,6 +737,13 @@ + + + + + + + @@ -1684,6 +1692,11 @@ + + + @@ -1862,5 +1875,7 @@ topic="com.intellij.platform.backend.workspace.WorkspaceModelChangeListener"/> + - + \ No newline at end of file