diff --git a/java/java-analysis-impl/resources/messages/QuickFixBundle.properties b/java/java-analysis-impl/resources/messages/QuickFixBundle.properties index e5ccf38f3e74..ee6b2daec928 100644 --- a/java/java-analysis-impl/resources/messages/QuickFixBundle.properties +++ b/java/java-analysis-impl/resources/messages/QuickFixBundle.properties @@ -134,6 +134,8 @@ create.write.only.property.from.usage.full.text=Create write-only property ''{0} create.getter=Create Getter # suppress inspection "UnusedProperty" create.setter=Create Setter +# suppress inspection "UnusedProperty" +create.getter.setter=Create Getter/Setter create.annotation.family=Add annotation to declaration create.annotation.text=Annotate as @{0} defer.final.assignment.with.temp.family=Defer final assignment with temp diff --git a/java/java-impl/resources/META-INF/JavaPlugin.xml b/java/java-impl/resources/META-INF/JavaPlugin.xml index 12ae1df835f7..c7efedf3e034 100644 --- a/java/java-impl/resources/META-INF/JavaPlugin.xml +++ b/java/java-impl/resources/META-INF/JavaPlugin.xml @@ -1505,6 +1505,12 @@ + + + + + + diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/api/CompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/api/CompletionCommand.kt index 055b20ae42e0..c31c910d6509 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/api/CompletionCommand.kt +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/api/CompletionCommand.kt @@ -1,9 +1,15 @@ // 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.codeInsight.TargetElementUtil import com.intellij.modcommand.ModHighlight.HighlightInfo +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.psi.PsiElement import com.intellij.psi.PsiFile import org.jetbrains.annotations.ApiStatus @@ -41,4 +47,30 @@ abstract class OldCompletionCommand : CompletionCommand() { override fun execute(offset: Int, psiFile: PsiFile) { execute(offset, psiFile, null) } + + + internal fun dataContext( + 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 + } + + internal fun getTargetContext(offset: Int, editor: Editor): PsiElement? { + try { + val util = TargetElementUtil.getInstance() + return util.findTargetElement(editor, util.getReferenceSearchFlags(), offset) + } + catch (e: IndexNotReadyException) { + return null; + } + } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionService.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionService.kt index 7b92a46b935a..36702ffb8864 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionService.kt +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/core/CommandCompletionService.kt @@ -114,7 +114,8 @@ class CommandCompletionService( if (highlightInfoContainer == null || actionContainers == null) return null if (document.immutableCharSequence.hashCode() != highlightInfoContainer.hashcode) return null if (highlightInfoContainer.offset != offset) return null - val actionContainer = actionContainers.firstOrNull { it.hashcode == highlightInfoContainer.hashcode } ?: return null + val actionContainer = actionContainers.firstOrNull { it.hashcode == highlightInfoContainer.hashcode && it.offset == offset } + ?: return null val allActions = mutableListOf() allActions.addAll(actionContainer.highlighters.allActions) val revertMap: MutableMap = mutableMapOf() diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/AbstractActionCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/AbstractActionCompletionCommand.kt index 521386a2ecb4..90426a7ff950 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/AbstractActionCompletionCommand.kt +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/AbstractActionCompletionCommand.kt @@ -1,16 +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 -import com.intellij.codeInsight.TargetElementUtil import com.intellij.codeInsight.completion.commands.api.OldCompletionCommand import com.intellij.ide.DataManager import com.intellij.openapi.actionSystem.* import com.intellij.openapi.actionSystem.ex.ActionUtil -import com.intellij.openapi.actionSystem.impl.SimpleDataContext import com.intellij.openapi.editor.Editor -import com.intellij.openapi.project.IndexNotReadyException import com.intellij.psi.PsiComment -import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiWhiteSpace import org.jetbrains.annotations.Nls @@ -29,13 +25,7 @@ abstract class AbstractActionCompletionCommand( val action = action ?: return false if (editor == null) return false val context = getTargetContext(offset, editor) - 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() + val dataContext = dataContext(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) @@ -44,16 +34,6 @@ abstract class AbstractActionCompletionCommand( return event.presentation.isEnabled && event.presentation.isVisible } - private fun getTargetContext(offset: Int, editor: Editor): PsiElement? { - try { - val util = TargetElementUtil.getInstance() - return util.findTargetElement(editor, util.getReferenceSearchFlags(), offset) - } - catch (e: IndexNotReadyException) { - return null; - } - } - override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) { val action = action ?: return if (editor == null) return @@ -61,7 +41,7 @@ abstract class AbstractActionCompletionCommand( val presentation: Presentation = action.templatePresentation.clone() val event = AnActionEvent.createEvent(action, dataContext, presentation, ActionPlaces.ACTION_PLACE_QUICK_LIST_POPUP_ACTION, ActionUiKind.NONE, null) if (ActionUtil.lastUpdateAndCheckDumb(action, event, false)) { - ActionUtil.performActionDumbAwareWithCallbacks(action, event); + ActionUtil.performActionDumbAwareWithCallbacks(action, event) } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/BaseGenerateGetterSetterHandleCompletionCommand.kt b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/BaseGenerateGetterSetterHandleCompletionCommand.kt new file mode 100644 index 000000000000..b1333a1ca052 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/BaseGenerateGetterSetterHandleCompletionCommand.kt @@ -0,0 +1,53 @@ +// 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.OldCompletionCommand +import com.intellij.codeInsight.daemon.QuickFixBundle +import com.intellij.codeInsight.intention.QuickFixFactory +import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler +import com.intellij.modcommand.ActionContext +import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiField +import com.intellij.psi.PsiFile +import com.intellij.psi.util.PsiTreeUtil +import javax.swing.Icon + + +class GenerateGetterSetterHandleCompletionCommand : + BaseGenerateGetterSetterHandleCompletionCommand(true, true, "Create Getter/Setter", + QuickFixBundle.message("create.getter.setter")) + +class GenerateSetterHandleCompletionCommand : + BaseGenerateGetterSetterHandleCompletionCommand(false, true, "Create Setter", + QuickFixBundle.message("create.setter")) + +class GenerateGetterHandleCompletionCommand : + BaseGenerateGetterSetterHandleCompletionCommand(true, false, "Create Getter", + QuickFixBundle.message("create.getter")) + +abstract class BaseGenerateGetterSetterHandleCompletionCommand( + val generateGetter: Boolean, + val generateSetter: Boolean, + override val name: String, + override val i18nName: String, +) : OldCompletionCommand() { + + override val icon: Icon? = null + + override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { + val element = getContext(offset, psiFile) ?: return false + val field = PsiTreeUtil.getParentOfType(element, PsiField::class.java) ?: return false + val action = QuickFixFactory.getInstance().createCreateGetterOrSetterFix(generateGetter, generateSetter, field) + val context = ActionContext.from(editor, psiFile).withElement(field) + return action.asModCommandAction()?.getPresentation(context) != null + } + + override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) { + val element = getContext(offset, psiFile) ?: return + val field = PsiTreeUtil.getParentOfType(element, PsiField::class.java) ?: return + val action = QuickFixFactory.getInstance().createCreateGetterOrSetterFix(generateGetter, generateSetter, field) + if (editor == null) return + @Suppress("DialogTitleCapitalization") + ShowIntentionActionsHandler.chooseActionAndInvoke(psiFile, editor, action, action.text) + } +} \ 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 index b07d09bfa04a..2d5b4ede2a33 100644 --- 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 @@ -1,8 +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 -import com.intellij.codeInsight.completion.commands.api.CompletionCommand import com.intellij.codeInsight.completion.commands.api.CommandProvider +import com.intellij.codeInsight.completion.commands.api.CompletionCommand import com.intellij.codeInsight.generation.actions.BaseGenerateAction import com.intellij.openapi.actionSystem.* import com.intellij.openapi.actionSystem.impl.SimpleDataContext @@ -39,6 +39,7 @@ class GenerateCommandProvider : CommandProvider { } } return generateActions.map { GenerateCompletionCommand(it) } + .filter { it.isApplicable(offset, psiFile, editor) } } override fun getId(): String { 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 index d07db5baeb70..9a1783eb513f 100644 --- 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 @@ -2,6 +2,7 @@ package com.intellij.codeInsight.completion.commands.impl import com.intellij.codeInsight.completion.commands.api.OldCompletionCommand +import com.intellij.codeInsight.completion.commands.impl.AbstractActionCompletionCommand.Companion.isApplicableToProject import com.intellij.codeInsight.generation.actions.BaseGenerateAction import com.intellij.ide.DataManager import com.intellij.java.JavaBundle @@ -25,6 +26,9 @@ class GenerateCompletionCommand(private val action: BaseGenerateAction) : OldCom get() = null override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { + if (!isApplicableToProject(offset, psiFile)) { + return false + } val context = psiFile.findElementAt(offset) ?: return false if (editor == null) return false val dataContext = SimpleDataContext.builder() 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 index 51e2c9f2bc49..fde97844dcff 100644 --- 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 @@ -1,50 +1,30 @@ // 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.icons.AllIcons -import com.intellij.ide.util.EditSourceUtil 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 -import org.jetbrains.annotations.Nls -import javax.swing.Icon -class GoToDeclarationCompletionCommand : CompletionCommand() { - override val name: String - get() = "Go to Declaration" +class GoToDeclarationCompletionCommand : AbstractActionCompletionCommand("GotoDeclarationOnly", + "Go to Declaration", + ActionsBundle.message("action.GotoDeclarationOnly.text"), + null) { + override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { + return super.isApplicable(offset, psiFile, editor) && hasToShow(offset, psiFile) + } - override val i18nName: @Nls String - get() = ActionsBundle.message("action.GotoDeclaration.text") - - override val icon: Icon - get() = AllIcons.Ide.ExternalLinkArrowWhite // Use the appropriate icon - - override fun isApplicable(offset: Int, psiFile: PsiFile): Boolean { + private fun hasToShow(offset: Int, psiFile: PsiFile): Boolean { val context = (getContext(offset, psiFile)) ?: return false return canNavigateToDeclaration(context) } - override fun execute(offset: Int, psiFile: PsiFile) { - val element = getContext(offset, psiFile) ?: return - if (element !is PsiIdentifier) { - return - } - val javaRef = PsiTreeUtil.getParentOfType(element, PsiJavaCodeReferenceElement::class.java) - - val psiElement = javaRef?.resolve() - if (psiElement != null) { - EditSourceUtil.navigateToPsiElement(psiElement) - } - } - - private fun canNavigateToDeclaration(context: PsiElement): Boolean { if (context !is PsiIdentifier) { - return false; + return false } val javaRef = PsiTreeUtil.getParentOfType(context, PsiJavaCodeReferenceElement::class.java) val psiElement = javaRef?.resolve() 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/IntroduceVariableCommand.kt new file mode 100644 index 000000000000..eaacc03608c5 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/IntroduceVariableCommand.kt @@ -0,0 +1,50 @@ +// 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.OldCompletionCommand +import com.intellij.icons.AllIcons +import com.intellij.lang.ContextAwareActionHandler +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 com.intellij.refactoring.JavaRefactoringActionHandlerFactory +import com.intellij.refactoring.RefactoringBundle +import com.intellij.refactoring.actions.IntroduceVariableAction +import org.jetbrains.annotations.Nls +import javax.swing.Icon + +class IntroduceVariableCommand : OldCompletionCommand() { + override val name: String + get() = "Introduce variable" + + override val i18nName: @Nls String + get() = RefactoringBundle.message("introduce.variable.title") + + override val icon: Icon + get() = AllIcons.Nodes.Variable + + override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { + if (editor == null) return false + val factory = JavaRefactoringActionHandlerFactory.getInstance() + val variableHandler = factory.createIntroduceVariableHandler() + if (variableHandler is ContextAwareActionHandler) { + return variableHandler.isAvailableForQuickList(editor, psiFile, dataContext(psiFile, editor, getTargetContext(offset, editor))) + } + return false + } + + override fun execute(offset: Int, psiFile: PsiFile, editor: Editor?) { + val action = IntroduceVariableAction() + if (editor == null) return + val context = getTargetContext(offset, editor) + val dataContext = dataContext(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) + action.update(event) + ActionUtil.performActionDumbAwareWithCallbacks(action, event) + } +} \ 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 new file mode 100644 index 000000000000..38e25d830b72 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/commands/impl/ShowUsagesActionCompletionCommand.kt @@ -0,0 +1,24 @@ +// 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.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 isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean { + return super.isApplicable(offset, psiFile, editor) && hasToShow(getContext(offset, 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