mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
InlayHints: migrate annotation hints to provider model
GitOrigin-RevId: 8a0de6f22597fa336c92590ba94199293a341e8a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
075cb7338a
commit
b524745152
@@ -60,6 +60,9 @@
|
||||
<component>
|
||||
<implementation-class>com.intellij.codeInsight.hints.AnnotationHintsPassFactory</implementation-class>
|
||||
</component>
|
||||
<component>
|
||||
<implementation-class>com.intellij.lang.java.JavaModuleRenameListener</implementation-class>
|
||||
</component>
|
||||
<component>
|
||||
<implementation-class>com.intellij.openapi.vcs.impl.ModuleVcsDetector</implementation-class>
|
||||
</component>
|
||||
@@ -299,7 +302,6 @@
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<highlightingPassFactory implementation="com.intellij.codeInsight.hints.AnnotationHintsPassFactory"/>
|
||||
<highlightingPassFactory implementation="com.intellij.framework.detection.impl.FrameworkDetectionManager$FrameworkDetectionHighlightingPassFactory"/>
|
||||
|
||||
<virtualFileSystem implementationClass="com.intellij.openapi.vfs.impl.jrt.JrtFileSystemImpl" key="jrt" physical="true"/>
|
||||
@@ -1109,7 +1111,6 @@
|
||||
<joinLinesHandler implementation="com.intellij.codeInsight.editorActions.LiteralJoinLinesHandler"/>
|
||||
<editorSmartKeysConfigurable instance="com.intellij.application.options.JavadocOptionsProvider" id="editor.preferences.javadocOptions"
|
||||
key="javadoc.generate.message.title" bundle="messages.JavadocBundle"/>
|
||||
<editorAppearanceConfigurable instance="com.intellij.codeInsight.hints.AnnotationHintsConfigurable"/>
|
||||
<wordBoundaryFilter language="JAVA" implementationClass="com.intellij.codeInsight.editorActions.JavaWordBoundaryFilter"/>
|
||||
<editorActionHandler action="PrevParameter" implementationClass="com.intellij.codeInsight.editorActions.JavaPrevParameterHandler"/>
|
||||
<editorActionHandler action="NextParameter" implementationClass="com.intellij.codeInsight.editorActions.JavaNextParameterHandler"/>
|
||||
@@ -1155,6 +1156,7 @@
|
||||
<codeInsight.parameterInfo language="JAVA" implementationClass="com.intellij.codeInsight.hint.api.impls.MethodParameterInfoHandler"/>
|
||||
<codeInsight.parameterNameHints language="JAVA" implementationClass="com.intellij.codeInsight.hints.JavaInlayParameterHintsProvider"/>
|
||||
<codeInsight.inlayProvider language="JAVA" implementationClass="com.intellij.codeInsight.hints.MethodChainsInlayProvider"/>
|
||||
<codeInsight.inlayProvider language="JAVA" implementationClass="com.intellij.codeInsight.hints.AnnotationInlayProvider"/>
|
||||
<lang.foldingBuilder language="JAVA" implementationClass="com.intellij.codeInsight.folding.impl.JavaFoldingBuilder"/>
|
||||
<lang.braceMatcher language="JAVA" implementationClass="com.intellij.codeInsight.highlighting.JavaPairedBraceMatcher"/>
|
||||
<anonymousElementProvider implementation="com.intellij.lang.java.JavaAnonymousClassesProvider" />
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.hints
|
||||
|
||||
import com.intellij.application.options.editor.CodeFoldingOptionsProvider
|
||||
import com.intellij.codeInsight.CodeInsightSettings
|
||||
import com.intellij.openapi.application.ApplicationBundle
|
||||
import com.intellij.openapi.options.BeanConfigurable
|
||||
|
||||
/**
|
||||
* @author egor
|
||||
*/
|
||||
class AnnotationHintsConfigurable : BeanConfigurable<CodeInsightSettings>(), CodeFoldingOptionsProvider {
|
||||
init {
|
||||
val settings = CodeInsightSettings.getInstance()
|
||||
|
||||
checkBox(ApplicationBundle.message("editor.appearance.show.external.annotations"), settings::SHOW_EXTERNAL_ANNOTATIONS_INLINE)
|
||||
checkBox(ApplicationBundle.message("editor.appearance.show.inferred.annotations"), settings::SHOW_INFERRED_ANNOTATIONS_INLINE)
|
||||
}
|
||||
|
||||
override fun apply() {
|
||||
super.apply()
|
||||
AnnotationHintsPassFactory.modificationStampHolder.forceHintsUpdateOnNextPass()
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.hints
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightSettings
|
||||
import com.intellij.codeInsight.ExternalAnnotationsManager
|
||||
import com.intellij.codeInsight.InferredAnnotationsManager
|
||||
import com.intellij.codeInsight.daemon.impl.HintRenderer
|
||||
import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.ToggleAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.Inlay
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiAnnotation
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiModifierListOwner
|
||||
|
||||
class AnnotationHintsPass(
|
||||
rootElement: PsiElement, editor: Editor,
|
||||
modificationStampHolder: ModificationStampHolder
|
||||
) : ElementProcessingHintPass(rootElement, editor, modificationStampHolder) {
|
||||
|
||||
override fun isAvailable(virtualFile: VirtualFile): Boolean =
|
||||
(CodeInsightSettings.getInstance().SHOW_EXTERNAL_ANNOTATIONS_INLINE
|
||||
&& ExternalAnnotationsManager.getInstance(myProject).hasAnnotationRootsForFile(virtualFile))
|
||||
|| CodeInsightSettings.getInstance().SHOW_INFERRED_ANNOTATIONS_INLINE
|
||||
|
||||
override fun collectElementHints(element: PsiElement, collector: (offset: Int, hint: String) -> Unit) {
|
||||
if (element is PsiModifierListOwner) {
|
||||
var annotations = emptySequence<PsiAnnotation>()
|
||||
if (CodeInsightSettings.getInstance().SHOW_EXTERNAL_ANNOTATIONS_INLINE) {
|
||||
annotations += ExternalAnnotationsManager.getInstance(myProject).findExternalAnnotations(element).orEmpty()
|
||||
}
|
||||
if (CodeInsightSettings.getInstance().SHOW_INFERRED_ANNOTATIONS_INLINE) {
|
||||
annotations += InferredAnnotationsManager.getInstance(myProject).findInferredAnnotations(element)
|
||||
}
|
||||
|
||||
val shownAnnotations = mutableSetOf<String>()
|
||||
annotations.forEach {
|
||||
val nameReferenceElement = it.nameReferenceElement
|
||||
if (nameReferenceElement != null && element.modifierList != null &&
|
||||
(shownAnnotations.add(nameReferenceElement.qualifiedName) || JavaDocInfoGenerator.isRepeatableAnnotationType(it))) {
|
||||
val offset = element.modifierList!!.textRange.startOffset
|
||||
collector.invoke(offset, "@" + nameReferenceElement.referenceName + it.parameterList.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getHintKey(): Key<Boolean> = ANNOTATION_INLAY_KEY
|
||||
override fun createRenderer(text: String): HintRenderer = AnnotationHintRenderer(text)
|
||||
|
||||
companion object {
|
||||
private val ANNOTATION_INLAY_KEY = Key.create<Boolean>("ANNOTATION_INLAY_KEY")
|
||||
}
|
||||
|
||||
private class AnnotationHintRenderer(text: String) : HintRenderer(text) {
|
||||
override fun getContextMenuGroupId(inlay: Inlay<*>) = "AnnotationHintsContextMenu"
|
||||
}
|
||||
|
||||
class ToggleExternalAnnotationsHintsAction : ToggleAction() {
|
||||
override fun isSelected(e: AnActionEvent): Boolean = CodeInsightSettings.getInstance().SHOW_EXTERNAL_ANNOTATIONS_INLINE
|
||||
|
||||
override fun setSelected(e: AnActionEvent, state: Boolean) {
|
||||
CodeInsightSettings.getInstance().SHOW_EXTERNAL_ANNOTATIONS_INLINE = state
|
||||
AnnotationHintsPassFactory.modificationStampHolder.forceHintsUpdateOnNextPass()
|
||||
}
|
||||
}
|
||||
|
||||
class ToggleInferredAnnotationsHintsAction : ToggleAction() {
|
||||
override fun isSelected(e: AnActionEvent): Boolean = CodeInsightSettings.getInstance().SHOW_INFERRED_ANNOTATIONS_INLINE
|
||||
|
||||
override fun setSelected(e: AnActionEvent, state: Boolean) {
|
||||
CodeInsightSettings.getInstance().SHOW_INFERRED_ANNOTATIONS_INLINE = state
|
||||
AnnotationHintsPassFactory.modificationStampHolder.forceHintsUpdateOnNextPass()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.hints
|
||||
|
||||
import com.intellij.codeHighlighting.TextEditorHighlightingPass
|
||||
import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory
|
||||
import com.intellij.codeHighlighting.TextEditorHighlightingPassFactoryRegistrar
|
||||
import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
|
||||
internal class AnnotationHintsPassFactory : TextEditorHighlightingPassFactory, TextEditorHighlightingPassFactoryRegistrar {
|
||||
override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) {
|
||||
registrar.registerTextEditorHighlightingPass(this, null, null, false, -1)
|
||||
}
|
||||
|
||||
override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass? {
|
||||
if (editor.isOneLineMode ||
|
||||
file !is PsiJavaFile ||
|
||||
modificationStampHolder.isNotChanged(editor, file)) return null
|
||||
return AnnotationHintsPass(file, editor, modificationStampHolder)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val modificationStampHolder: ModificationStampHolder = ModificationStampHolder(Key.create<Long>("LAST_PASS_MODIFICATION_TIMESTAMP"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInsight.hints
|
||||
|
||||
import com.intellij.codeInsight.ExternalAnnotationsManager
|
||||
import com.intellij.codeInsight.InferredAnnotationsManager
|
||||
import com.intellij.codeInsight.MakeInferredAnnotationExplicit
|
||||
import com.intellij.codeInsight.hints.presentation.InsetPresentation
|
||||
import com.intellij.codeInsight.hints.presentation.MenuOnClickPresentation
|
||||
import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.application.ApplicationBundle
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.ui.layout.*
|
||||
import javax.swing.JComponent
|
||||
import kotlin.reflect.KMutableProperty0
|
||||
|
||||
class AnnotationInlayProvider : InlayHintsProvider<AnnotationInlayProvider.Settings> {
|
||||
override fun getCollectorFor(file: PsiFile,
|
||||
editor: Editor,
|
||||
settings: Settings,
|
||||
sink: InlayHintsSink): InlayHintsCollector? {
|
||||
val project = file.project
|
||||
return object : FactoryInlayHintsCollector(editor) {
|
||||
override fun collect(element: PsiElement, editor: Editor, sink: InlayHintsSink): Boolean {
|
||||
if (element is PsiModifierListOwner) {
|
||||
var annotations = emptySequence<PsiAnnotation>()
|
||||
if (settings.showExternal) {
|
||||
annotations += ExternalAnnotationsManager.getInstance(project).findExternalAnnotations(element).orEmpty()
|
||||
}
|
||||
if (settings.showInferred) {
|
||||
annotations += InferredAnnotationsManager.getInstance(project).findInferredAnnotations(element)
|
||||
}
|
||||
|
||||
val shownAnnotations = mutableSetOf<String>()
|
||||
annotations.forEach {
|
||||
val nameReferenceElement = it.nameReferenceElement
|
||||
if (nameReferenceElement != null && element.modifierList != null &&
|
||||
(shownAnnotations.add(nameReferenceElement.qualifiedName) || JavaDocInfoGenerator.isRepeatableAnnotationType(it))) {
|
||||
val offset = element.modifierList!!.textRange.startOffset
|
||||
val presentation = annotationPresentation(it)
|
||||
val menuOnClick = MenuOnClickPresentation(presentation, project) {
|
||||
val makeExplicit = InsertAnnotationAction(project, file, element)
|
||||
listOf(
|
||||
makeExplicit,
|
||||
ToggleSettingsAction("Turn off external annotations", settings::showExternal),
|
||||
ToggleSettingsAction("Turn off inferred annotations", settings::showInferred)
|
||||
)
|
||||
}
|
||||
sink.addInlineElement(offset, true, menuOnClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun annotationPresentation(annotation: PsiAnnotation): InsetPresentation = with(factory) {
|
||||
val nameReferenceElement = annotation.nameReferenceElement
|
||||
val parameterList = annotation.parameterList
|
||||
inset(
|
||||
roundWithBackground(seq(
|
||||
smallText("@"),
|
||||
psiSingleReference(smallText(nameReferenceElement?.referenceName ?: "")) { nameReferenceElement?.resolve() },
|
||||
parametersPresentation(parameterList)
|
||||
)),
|
||||
left = 1,
|
||||
right = 1
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
private fun parametersPresentation(parameterList: PsiAnnotationParameterList) = with(factory) {
|
||||
val attributes = parameterList.attributes
|
||||
when {
|
||||
attributes.isEmpty() -> smallText("()")
|
||||
else -> insideParametersPresentation(attributes, collapsed = parameterList.textLength > 30)
|
||||
}
|
||||
}
|
||||
|
||||
private fun insideParametersPresentation(attributes: Array<PsiNameValuePair>, collapsed: Boolean) = with(factory) {
|
||||
collapsible(
|
||||
smallText("("),
|
||||
smallText("..."),
|
||||
{
|
||||
join(
|
||||
presentations = attributes.map { pairPresentation(it) },
|
||||
separator = { smallText(", ") }
|
||||
)
|
||||
},
|
||||
smallText(")"),
|
||||
collapsed
|
||||
)
|
||||
}
|
||||
|
||||
private fun pairPresentation(attribute: PsiNameValuePair) = with(factory) {
|
||||
seq(
|
||||
psiSingleReference(smallText(attribute.name ?: ""), resolve = { attribute.reference?.resolve() }),
|
||||
smallText(" = "),
|
||||
smallText(attribute.value?.text ?: "") // TODO values references, at least annotations and enum names
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createSettings(): Settings = Settings(showInferred = true, showExternal = true)
|
||||
|
||||
override val name: String
|
||||
get() = "Annotations"
|
||||
override val key: SettingsKey<Settings>
|
||||
get() = ourKey
|
||||
override val previewText: String?
|
||||
get() = "" // TODO
|
||||
|
||||
override fun createConfigurable(settings: Settings): ImmediateConfigurable {
|
||||
return object : ImmediateConfigurable {
|
||||
override fun createComponent(listener: ChangeListener): JComponent {
|
||||
return panel {
|
||||
row {
|
||||
checkBox(ApplicationBundle.message("editor.appearance.show.external.annotations"), settings::showExternal)
|
||||
}
|
||||
row {
|
||||
checkBox(ApplicationBundle.message("editor.appearance.show.external.annotations"), settings::showExternal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ourKey: SettingsKey<Settings> = SettingsKey("annotation.hints")
|
||||
}
|
||||
|
||||
data class Settings(var showInferred: Boolean = true, var showExternal: Boolean = true)
|
||||
|
||||
|
||||
class ToggleSettingsAction(val text: String, val prop: KMutableProperty0<Boolean>) : AnAction() {
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
val presentation = e.presentation
|
||||
presentation.text = text
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
prop.set(!prop.get())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class InsertAnnotationAction(
|
||||
private val project: Project,
|
||||
private val file: PsiFile,
|
||||
private val element: PsiModifierListOwner
|
||||
) : AnAction() {
|
||||
override fun update(e: AnActionEvent) {
|
||||
e.presentation.text = "Insert annotation"
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val intention = MakeInferredAnnotationExplicit()
|
||||
if (intention.isAvailable(project, file, element)) {
|
||||
intention.makeAnnotationsExplicit(project, file, element)
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -117,6 +117,11 @@ class PresentationFactory(private val editor: EditorImpl) {
|
||||
}), onClick = unwrapAction)
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
fun inset(base: InlayPresentation, left: Int = 0, right: Int = 0, top: Int = 0, down: Int = 0): InsetPresentation {
|
||||
return InsetPresentation(base, left, right, top, down)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates node, that can be collapsed/expanded by clicking on prefix/suffix.
|
||||
* If presentation is collapsed, clicking to content will expand it.
|
||||
@@ -270,6 +275,19 @@ class PresentationFactory(private val editor: EditorImpl) {
|
||||
}
|
||||
}
|
||||
|
||||
fun join(presentations: List<InlayPresentation>, separator: () -> InlayPresentation) : InlayPresentation {
|
||||
val seq = mutableListOf<InlayPresentation>()
|
||||
var first = true
|
||||
for (presentation in presentations) {
|
||||
if (!first) {
|
||||
seq.add(separator())
|
||||
}
|
||||
seq.add(presentation)
|
||||
first = false
|
||||
}
|
||||
return SequencePresentation(seq)
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
fun rounding(arcWidth: Int, arcHeight: Int, presentation: InlayPresentation): InlayPresentation =
|
||||
RoundPresentation(presentation, arcWidth, arcHeight)
|
||||
|
||||
@@ -775,17 +775,6 @@
|
||||
<action class="com.intellij.compiler.backwardRefs.view.TestCompilerRefFunctionalExpressionSearchAction" id="TestCompilerReferenceServiceFunctionalExpressionSearch" internal="true"/>
|
||||
<action class="com.intellij.compiler.backwardRefs.view.TestCompilerRefInheritanceAction" id="TestCompilerReferenceServiceInheritance" internal="true"/>
|
||||
|
||||
<group id="AnnotationHintsContextMenu" popup="true">
|
||||
<action id="ToggleExternalAnnotationsHintsAction"
|
||||
class="com.intellij.codeInsight.hints.AnnotationHintsPass$ToggleExternalAnnotationsHintsAction"
|
||||
text="Show External Annotations"/>
|
||||
<action id="ToggleInferredAnnotationsHintsAction"
|
||||
class="com.intellij.codeInsight.hints.AnnotationHintsPass$ToggleInferredAnnotationsHintsAction"
|
||||
text="Show Inferred Annotations"/>
|
||||
<separator/>
|
||||
<reference ref="EditorPopupMenu"/>
|
||||
</group>
|
||||
|
||||
|
||||
<action id="MethodOverloadSwitchUp" class="com.intellij.codeInsight.editorActions.JavaMethodOverloadSwitchUpAction"/>
|
||||
<action id="MethodOverloadSwitchDown" class="com.intellij.codeInsight.editorActions.JavaMethodOverloadSwitchDownAction" />
|
||||
|
||||
Reference in New Issue
Block a user