[java-completion] IDEA-359174 command completion

- add hint
- add simple i18n

GitOrigin-RevId: c9a94a47dd706d6f2f23543d8b733aeb4b1467e7
This commit is contained in:
Mikhail Pyltsin
2024-11-21 14:58:08 +00:00
committed by intellij-monorepo-bot
parent dfa683d35b
commit 708b5271f6
17 changed files with 121 additions and 4 deletions
@@ -7,6 +7,7 @@ import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
import javax.swing.Icon
@ApiStatus.Experimental
@@ -16,6 +17,7 @@ abstract class CompletionCommand {
}
abstract val name: String
abstract val i18nName: @Nls String
abstract val icon: Icon?
open val priority: Int? = null
abstract fun isApplicable(offset: Int, psiFile: PsiFile): Boolean
@@ -0,0 +1,25 @@
// 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.openapi.components.*
import org.jetbrains.annotations.ApiStatus
@Service(Service.Level.APP)
@ApiStatus.Experimental
@ApiStatus.Internal
@State(name = "CommandCompletion", category = SettingsCategory.UI, storages = [Storage("CommandCompletion.xml", roamingType = RoamingType.DISABLED)])
class ApplicationCommandCompletionService : PersistentStateComponent<AppCommandCompletionSettings> {
private var myState = AppCommandCompletionSettings()
override fun getState(): AppCommandCompletionSettings = myState
override fun loadState(state: AppCommandCompletionSettings) {
myState = state
}
}
class AppCommandCompletionSettings(
var showCounts: Int = 0,
)
@@ -90,7 +90,10 @@ internal class CommandCompletionProvider : CompletionProvider<CompletionParamete
parameters.offset,
parameters.originalFile) { commands ->
withPrefixMatcher.addAllElements(commands.map { command ->
val i18nName = command.i18nName.replace("_", "").replace("...", "")
val tailText = if (command.name.equals(i18nName, ignoreCase = true)) "" else " ($i18nName)"
var element: LookupElement = CommandCompletionLookupElement(LookupElementBuilder.create(command.name)
.withTypeText(tailText)
.withIcon(command.icon ?: Lightning)
.withInsertHandler(CommandInsertHandler(command))
.withBoldness(true),
@@ -5,7 +5,9 @@ import com.intellij.codeInsight.completion.commands.api.CommandCompletionFactory
import com.intellij.codeInsight.daemon.impl.CommandCompletionServiceApi
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.daemon.impl.HintRenderer
import com.intellij.codeInsight.editorActions.TypedHandlerDelegate
import com.intellij.codeInsight.editorLineStripeHint.EditorLineStripeTextRenderer
import com.intellij.codeInsight.highlighting.HighlightManager
import com.intellij.codeInsight.hints.presentation.PresentationFactory
import com.intellij.codeInsight.hints.presentation.PresentationRenderer
@@ -15,10 +17,12 @@ import com.intellij.codeInsight.intention.impl.IntentionActionWithTextCaching
import com.intellij.codeInsight.lookup.*
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.annotation.HighlightSeverity
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Document
@@ -173,6 +177,33 @@ class CommandCompletionService(
editor.putUserData(PREVIOUS_HIGHLIGHT_CACHED_CONTAINER_INFO_CONTAINER_KEY, newList.toMutableList())
}
fun setHint(lookup: LookupImpl) {
if (lookup.getUserData(INSTALLED_HIHT_KEY) == false) return
lookup.putUserData(INSTALLED_HIHT_KEY, false)
val psiFile = lookup.psiFile ?: return
val completionService = lookup.project.service<CommandCompletionService>()
val completionFactory = completionService.getFactory(psiFile.language) ?: return
val fullSuffix = completionFactory.suffix() + completionFactory.filterSuffix().toString()
val index = findActualIndex(fullSuffix, lookup.editor.document.immutableCharSequence, lookup.lookupOriginalStart)
val startOffset = lookup.lookupOriginalStart - index
val endOffset = lookup.editor.caretModel.offset
if (endOffset - startOffset != 1) return
val editor = lookup.editor
if (lookup.items.none { it.`as`(CommandCompletionLookupElement::class.java) != null }) return
if (editor.inlayModel.getInlineElementsInRange(startOffset, endOffset).isNotEmpty()) return
val applicationCommandCompletionService = ApplicationManager.getApplication().getService(ApplicationCommandCompletionService::class.java)
val state = applicationCommandCompletionService.state
if (state.showCounts > 5) return
state.showCounts += 1
val inlineElement: Inlay<HintRenderer?> = editor.inlayModel.addInlineElement(endOffset, true, EditorLineStripeTextRenderer(" " + JavaBundle.message("command.completion.filter.hint")))
?: return
Disposer.register(lookup, inlineElement)
Disposer.register(lookup) { lookup.removeUserData(INSTALLED_HINT) }
lookup.putUserData(INSTALLED_HINT, inlineElement)
lookup.putUserData(INSTALLED_HIHT_KEY, true)
}
@ApiStatus.Internal
private object CommandCompletionLookupItemFilter : Condition<LookupElement> {
override fun value(e: LookupElement?): Boolean {
@@ -181,6 +212,8 @@ class CommandCompletionService(
}
}
private val INSTALLED_HINT: Key<Inlay<HintRenderer?>> = Key.create("completion.command.installed.hint")
private val INSTALLED_HIHT_KEY: Key<Boolean> = Key.create("completion.command.installed.hint")
private val INSTALLED_ADDITIONAL_MATCHER_KEY: Key<Boolean> = Key.create("completion.command.installed.additional.matcher")
private val INSTALLED_LISTENER_KEY: Key<AtomicBoolean> = Key.create("completion.command.installed.lookup.command.listener")
private val SUPPRESS_PREDICATE_KEY = Key.create<EditorHighlightingPredicate>("completion.command.suppress.completion.predicate")
@@ -290,6 +323,7 @@ private class CommandCompletionHighlightingListener(
override fun currentItemChanged(event: LookupEvent) {
val lookup = event.lookup
if (lookup !is LookupImpl) return
completionService?.setHint(lookup)
val item = event.item
val element = item?.`as`(CommandCompletionLookupElement::class.java)
if (element == null) {
@@ -339,6 +373,10 @@ private class CommandCompletionCharFilter : CharFilter() {
if (!Registry.`is`("java.completion.command.enabled")) return null
if (lookup !is LookupImpl) return null
val completionService = lookup.project.service<CommandCompletionService>()
val installedHint = lookup.removeUserData(INSTALLED_HINT)
if (installedHint != null) {
Disposer.dispose(installedHint)
}
val psiFile = lookup.psiFile ?: return null
val completionFactory = completionService.getFactory(psiFile.language) ?: return null
val offset = lookup.editor.caretModel.offset
@@ -11,11 +11,13 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.IndexNotReadyException
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.jetbrains.annotations.Nls
import javax.swing.Icon
abstract class AbstractActionCompletionCommand(
var actionId: String,
override val name: String,
override val i18nName: @Nls String,
override val icon: Icon?,
override val priority: Int? = null
) : OldCompletionCommand() {
@@ -1,6 +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
import com.intellij.idea.ActionsBundle
class CommentCompletionCommand : AbstractActionCompletionCommand("CommentByLineComment",
"Comment",
ActionsBundle.actionText("CommentByLineComment"),
null)
@@ -4,17 +4,22 @@ package com.intellij.codeInsight.completion.commands.impl
import com.intellij.codeInsight.actions.ReformatCodeProcessor
import com.intellij.codeInsight.completion.commands.api.CompletionCommand
import com.intellij.icons.AllIcons
import com.intellij.idea.ActionsBundle
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 : CompletionCommand() {
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
@@ -4,17 +4,23 @@ package com.intellij.codeInsight.completion.commands.impl
import com.intellij.codeInsight.completion.commands.api.OldCompletionCommand
import com.intellij.codeInsight.generation.actions.BaseGenerateAction
import com.intellij.ide.DataManager
import com.intellij.java.JavaBundle
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.psi.PsiFile
import org.jetbrains.annotations.Nls
import javax.swing.Icon
class GenerateCompletionCommand(private val action: BaseGenerateAction) : OldCompletionCommand() {
override val name: String
get() = "Generate \"" + action.templateText + "\""
get() = "Generate \'" + action.templateText + "\'"
override val i18nName: @Nls String
get() = JavaBundle.message(
"completion.command.generate.name",
action.templateText)
override val icon: Icon?
get() = null
@@ -4,16 +4,21 @@ 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.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"
get() = "Go to Declaration"
override val i18nName: @Nls String
get() = ActionsBundle.message("action.GotoDeclaration.text")
override val icon: Icon
get() = AllIcons.Ide.ExternalLinkArrowWhite // Use the appropriate icon
@@ -3,18 +3,23 @@ package com.intellij.codeInsight.completion.commands.impl
import com.intellij.codeInsight.completion.commands.api.OldCompletionCommand
import com.intellij.icons.AllIcons
import com.intellij.java.JavaBundle
import com.intellij.lang.refactoring.InlineActionHandler
import com.intellij.openapi.editor.Editor
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 InlineVariableCompletionCommand : OldCompletionCommand() {
override val name: String
get() = "inline"
override val i18nName: @Nls String
get() = JavaBundle.message("command.completion.inline.text")
override val icon: Icon
get() = AllIcons.Actions.RefactoringBulb // Use an appropriate icon from IntelliJ's icon set
@@ -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.core.CommandCompletionService
import com.intellij.codeInsight.completion.commands.api.OldCompletionCommand
import com.intellij.codeInsight.completion.commands.core.CommandCompletionService
import com.intellij.codeInsight.completion.commands.core.PREVIOUS_HIGHLIGHT_CACHED_CONTAINER_INFO_CONTAINER_KEY
import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass
import com.intellij.codeInsight.daemon.impl.quickfix.ExpensivePsiIntentionAction
@@ -16,6 +16,7 @@ import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiFile
import kotlinx.coroutines.launch
import org.jetbrains.annotations.Nls
import javax.swing.Icon
class IntentionCompletionCommand(
@@ -28,6 +29,9 @@ class IntentionCompletionCommand(
override val name: String
get() = intentionAction.text
override val i18nName: @Nls String
get() = intentionAction.text
override fun isApplicable(offset: Int, psiFile: PsiFile, editor: Editor?): Boolean {
if (editor == null) return false
return intentionAction.action !is ExpensivePsiIntentionAction && runReadAction {
@@ -1,7 +1,10 @@
// 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
class OptimizeImportCompletionCommand : AbstractActionCompletionCommand("OptimizeImports",
"Optimize imports",
ActionsBundle.message("action.OptimizeImports.text"),
null,
-100)
@@ -2,8 +2,10 @@
package com.intellij.codeInsight.completion.commands.impl
import com.intellij.ide.actions.ActivateToolWindowAction.Manager
import com.intellij.java.JavaBundle
class ProjectViewCompletionCommand : AbstractActionCompletionCommand(Manager.getActionIdForToolWindow("Project"),
"Project tree",
"Project tool",
JavaBundle.message("command.completion.project.tool.text"),
null,
-150)
@@ -1,7 +1,10 @@
// 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.java.JavaBundle
class RecentFilesCompletionCommand : AbstractActionCompletionCommand("RecentFiles",
"Recent files",
JavaBundle.message("command.completion.recent.files.text"),
null,
-150)
@@ -1,7 +1,10 @@
// 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
class RefactorThisCompletionCommand : AbstractActionCompletionCommand("Refactorings.QuickListPopupAction",
"Refactor",
ActionsBundle.message("action.Refactorings.QuickListPopupAction.text"),
null,
-100)
@@ -1,8 +1,10 @@
// 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.actionSystem.IdeActions
class RenameActionCompletionCommand : AbstractActionCompletionCommand(IdeActions.ACTION_RENAME,
"rename",
ActionsBundle.message("action.RenameElement.text"),
null)
@@ -1970,4 +1970,10 @@ 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