From b524745152194c3816a35c6ca26da3f50165fb18 Mon Sep 17 00:00:00 2001 From: "Roman.Ivanov" Date: Wed, 5 Jun 2019 14:09:06 +0700 Subject: [PATCH] InlayHints: migrate annotation hints to provider model GitOrigin-RevId: 8a0de6f22597fa336c92590ba94199293a341e8a --- java/java-impl/src/META-INF/JavaPlugin.xml | 6 +- .../hints/AnnotationHintsConfigurable.kt | 24 --- .../codeInsight/hints/AnnotationHintsPass.kt | 79 --------- .../hints/AnnotationHintsPassFactory.kt | 29 --- .../hints/AnnotationInlayProvider.kt | 167 ++++++++++++++++++ .../hints/presentation/PresentationFactory.kt | 18 ++ resources/src/idea/JavaActions.xml | 11 -- 7 files changed, 189 insertions(+), 145 deletions(-) delete mode 100644 java/java-impl/src/com/intellij/codeInsight/hints/AnnotationHintsConfigurable.kt delete mode 100644 java/java-impl/src/com/intellij/codeInsight/hints/AnnotationHintsPass.kt delete mode 100644 java/java-impl/src/com/intellij/codeInsight/hints/AnnotationHintsPassFactory.kt create mode 100644 java/java-impl/src/com/intellij/codeInsight/hints/AnnotationInlayProvider.kt diff --git a/java/java-impl/src/META-INF/JavaPlugin.xml b/java/java-impl/src/META-INF/JavaPlugin.xml index 6e3a4228adcb..808b60de41ac 100644 --- a/java/java-impl/src/META-INF/JavaPlugin.xml +++ b/java/java-impl/src/META-INF/JavaPlugin.xml @@ -60,6 +60,9 @@ com.intellij.codeInsight.hints.AnnotationHintsPassFactory + + com.intellij.lang.java.JavaModuleRenameListener + com.intellij.openapi.vcs.impl.ModuleVcsDetector @@ -299,7 +302,6 @@ - @@ -1109,7 +1111,6 @@ - @@ -1155,6 +1156,7 @@ + diff --git a/java/java-impl/src/com/intellij/codeInsight/hints/AnnotationHintsConfigurable.kt b/java/java-impl/src/com/intellij/codeInsight/hints/AnnotationHintsConfigurable.kt deleted file mode 100644 index daa9cc67c4bd..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/hints/AnnotationHintsConfigurable.kt +++ /dev/null @@ -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(), 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() - } -} diff --git a/java/java-impl/src/com/intellij/codeInsight/hints/AnnotationHintsPass.kt b/java/java-impl/src/com/intellij/codeInsight/hints/AnnotationHintsPass.kt deleted file mode 100644 index 5ab09d137a23..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/hints/AnnotationHintsPass.kt +++ /dev/null @@ -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() - 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() - 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 = ANNOTATION_INLAY_KEY - override fun createRenderer(text: String): HintRenderer = AnnotationHintRenderer(text) - - companion object { - private val ANNOTATION_INLAY_KEY = Key.create("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() - } - } -} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/hints/AnnotationHintsPassFactory.kt b/java/java-impl/src/com/intellij/codeInsight/hints/AnnotationHintsPassFactory.kt deleted file mode 100644 index 78fb3ac66909..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/hints/AnnotationHintsPassFactory.kt +++ /dev/null @@ -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("LAST_PASS_MODIFICATION_TIMESTAMP")) - } -} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/hints/AnnotationInlayProvider.kt b/java/java-impl/src/com/intellij/codeInsight/hints/AnnotationInlayProvider.kt new file mode 100644 index 000000000000..708f0e96f103 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/hints/AnnotationInlayProvider.kt @@ -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 { + 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() + if (settings.showExternal) { + annotations += ExternalAnnotationsManager.getInstance(project).findExternalAnnotations(element).orEmpty() + } + if (settings.showInferred) { + annotations += InferredAnnotationsManager.getInstance(project).findInferredAnnotations(element) + } + + val shownAnnotations = mutableSetOf() + 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, 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 + 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 = SettingsKey("annotation.hints") + } + + data class Settings(var showInferred: Boolean = true, var showExternal: Boolean = true) + + + class ToggleSettingsAction(val text: String, val prop: KMutableProperty0) : 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) + } + } +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/presentation/PresentationFactory.kt b/platform/lang-impl/src/com/intellij/codeInsight/hints/presentation/PresentationFactory.kt index 87db8182268e..40a37fcc81c0 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/presentation/PresentationFactory.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/presentation/PresentationFactory.kt @@ -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, separator: () -> InlayPresentation) : InlayPresentation { + val seq = mutableListOf() + 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) diff --git a/resources/src/idea/JavaActions.xml b/resources/src/idea/JavaActions.xml index 129d1fc79f26..65bc03557736 100644 --- a/resources/src/idea/JavaActions.xml +++ b/resources/src/idea/JavaActions.xml @@ -775,17 +775,6 @@ - - - - - - -