KT-38027 Support Code Vision feature in Kotlin
This commit is contained in:
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.idea.AbstractSmartSelectionTest
|
||||
import org.jetbrains.kotlin.idea.actions.AbstractGotoTestOrCodeActionTest
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.*
|
||||
import org.jetbrains.kotlin.idea.codeInsight.*
|
||||
import org.jetbrains.kotlin.idea.codeInsight.codevision.AbstractKotlinCodeVisionProviderTest
|
||||
import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractCodeInsightActionTest
|
||||
import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateHashCodeAndEqualsActionTest
|
||||
import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateTestSupportMethodActionTest
|
||||
@@ -867,6 +868,10 @@ fun main() {
|
||||
model("codeInsight/postfix")
|
||||
}
|
||||
|
||||
testClass<AbstractKotlinCodeVisionProviderTest> {
|
||||
model("codeInsight/codeVision")
|
||||
}
|
||||
|
||||
testClass<AbstractScriptConfigurationHighlightingTest> {
|
||||
model("script/definition/highlighting", extension = null, recursive = false)
|
||||
model("script/definition/complex", extension = null, recursive = false, testMethod = "doComplexTest")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2231,3 +2231,18 @@ title.import.layout=Import Layout
|
||||
title.packages.to.use.import.with=Packages to Use Import with '*'
|
||||
redundant.qualifier.unnecessary.non.direct.parent.class.qualifier=Unnecessary non-direct parent classes qualifiers
|
||||
fix.add.exception.to.throws=Add ''{0}''
|
||||
|
||||
hints.title.codevision=Code Vision
|
||||
hints.title.codevision.show.hints.for=Show hints for:
|
||||
hints.title.codevision.usages=Usages
|
||||
hints.title.codevision.inheritors=Implementations/Inheritors/Overrides
|
||||
|
||||
hints.codevision.usages.format={0, choice, 1#1 Usage|2#{0,number} Usages}
|
||||
hints.codevision.usages.too_many.format={0,number}+ Usages
|
||||
hints.codevision.implementations.format={0, choice, 1#1 Implementation|2#{0,number} Implementations}
|
||||
hints.codevision.implementations.too_many.format={0,number}+ Implementations
|
||||
hints.codevision.inheritors.format={0, choice, 1#1 Inheritor|2#{0,number} Inheritors}
|
||||
hints.codevision.inheritors.to_many.format={0,number}+ Inheritors
|
||||
hints.codevision.overrides.format={0, choice, 1#1 Override|2#{0,number} Overrides}
|
||||
hints.codevision.overrides.to_many.format={0,number}+ Overrides
|
||||
hints.codevision.settings=Settings...
|
||||
@@ -3374,6 +3374,9 @@
|
||||
<stripTrailingSpacesFilterFactory implementation="org.jetbrains.kotlin.idea.editor.KotlinStripTrailingSpacesFilterFactory"/>
|
||||
|
||||
<navbar implementation="org.jetbrains.kotlin.idea.navigationToolbar.KotlinNavBarModelExtension" order="first"/>
|
||||
|
||||
<statistics.counterUsagesCollector groupId="kotlin.code.vision" version="1"/>
|
||||
<codeInsight.inlayProvider language="kotlin" implementationClass="org.jetbrains.kotlin.idea.codeInsight.codevision.KotlinCodeVisionProvider"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight.codevision
|
||||
|
||||
import com.intellij.codeInsight.hints.ChangeListener
|
||||
import com.intellij.codeInsight.hints.ImmediateConfigurable
|
||||
import com.intellij.codeInsight.hints.settings.InlayHintsConfigurable
|
||||
import com.intellij.internal.statistic.eventLog.FeatureUsageData
|
||||
import com.intellij.internal.statistic.service.fus.collectors.FUCounterUsageLogger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.ui.layout.panel
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import javax.swing.JPanel
|
||||
|
||||
|
||||
// FIX ME WHEN BUNCH 192 REMOVED
|
||||
typealias CodeVisionInlayHintsConfigurable = InlayHintsConfigurable
|
||||
|
||||
fun logUsageStatistics(project: Project?, groupId: String, eventId: String) =
|
||||
FUCounterUsageLogger.getInstance().logEvent(project, groupId, eventId)
|
||||
|
||||
fun logUsageStatistics(project: Project?, groupId: String, eventId: String, data: FeatureUsageData) =
|
||||
FUCounterUsageLogger.getInstance().logEvent(project, groupId, eventId, data)
|
||||
|
||||
fun createImmediateConfigurable(settings: KotlinCodeVisionProvider.KotlinCodeVisionSettings): ImmediateConfigurable {
|
||||
return object : ImmediateConfigurable {
|
||||
override fun createComponent(listener: ChangeListener): JPanel {
|
||||
return panel {}
|
||||
}
|
||||
|
||||
override val cases: List<ImmediateConfigurable.Case>
|
||||
get() = listOf(
|
||||
ImmediateConfigurable.Case(
|
||||
KotlinBundle.message("hints.title.codevision.usages"),
|
||||
"usages",
|
||||
settings::showUsages
|
||||
),
|
||||
ImmediateConfigurable.Case(
|
||||
KotlinBundle.message("hints.title.codevision.inheritors"),
|
||||
"inheritors",
|
||||
settings::showInheritors
|
||||
)
|
||||
)
|
||||
|
||||
override val mainCheckboxText: String
|
||||
get() = KotlinBundle.message("hints.title.codevision.show.hints.for")
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight.codevision
|
||||
|
||||
import com.intellij.codeInsight.hints.ChangeListener
|
||||
import com.intellij.codeInsight.hints.ImmediateConfigurable
|
||||
import com.intellij.codeInsight.hints.config.InlayHintsConfigurable
|
||||
import com.intellij.internal.statistic.eventLog.FeatureUsageData
|
||||
import com.intellij.internal.statistic.service.fus.collectors.FUCounterUsageLogger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.ui.components.JBCheckBox
|
||||
import com.intellij.ui.layout.panel
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import javax.swing.JPanel
|
||||
|
||||
|
||||
typealias CodeVisionInlayHintsConfigurable = InlayHintsConfigurable
|
||||
|
||||
fun logUsageStatistics(project: Project?, groupId: String, eventId: String) {
|
||||
project?.let { FUCounterUsageLogger.getInstance().logEvent(project, groupId, eventId) }
|
||||
}
|
||||
|
||||
fun logUsageStatistics(project: Project?, groupId: String, eventId: String, data: FeatureUsageData) {
|
||||
project?.let { FUCounterUsageLogger.getInstance().logEvent(project, groupId, eventId, data) }
|
||||
}
|
||||
|
||||
fun createImmediateConfigurable(settings: KotlinCodeVisionProvider.KotlinCodeVisionSettings): ImmediateConfigurable {
|
||||
return object : ImmediateConfigurable {
|
||||
val usagesText = KotlinBundle.message("hints.title.codevision.usages")
|
||||
val inheritorsText = KotlinBundle.message("hints.title.codevision.inheritors")
|
||||
|
||||
private val usagesField = JBCheckBox(usagesText, settings.showUsages)
|
||||
private val inheritorsField = JBCheckBox(inheritorsText, settings.showInheritors)
|
||||
|
||||
override fun createComponent(listener: ChangeListener): JPanel {
|
||||
usagesField.isSelected = settings.showUsages
|
||||
usagesField.addActionListener { settings.showUsages = usagesField.isSelected }
|
||||
|
||||
inheritorsField.isSelected = settings.showInheritors
|
||||
inheritorsField.addActionListener { settings.showInheritors = inheritorsField.isSelected }
|
||||
|
||||
return panel {
|
||||
row { usagesField(pushX) }
|
||||
row { inheritorsField(pushX) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight.codevision
|
||||
|
||||
import com.intellij.codeInsight.hints.FactoryInlayHintsCollector
|
||||
import com.intellij.codeInsight.hints.InlayHintsSink
|
||||
import com.intellij.codeInsight.hints.presentation.AttributesTransformerPresentation
|
||||
import com.intellij.codeInsight.hints.presentation.InlayPresentation
|
||||
import com.intellij.codeInsight.hints.presentation.MouseButton
|
||||
import com.intellij.codeInsight.hints.presentation.PresentationFactory
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.colors.EditorColors
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ProjectRootModificationTracker
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch
|
||||
import com.intellij.psi.search.searches.MethodReferencesSearch
|
||||
import com.intellij.psi.search.searches.OverridingMethodsSearch
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.psi.util.CachedValue
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import com.intellij.psi.util.PsiModificationTracker
|
||||
import com.intellij.util.ArrayUtil
|
||||
import com.intellij.util.Processor
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.idea.refactoring.isAbstract
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.toPossiblyFakeLightMethods
|
||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinDefinitionsSearcher
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtFunctionLiteral
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
class KotlinCodeVisionHintsCollector(
|
||||
editor: Editor, private val showUsages: Boolean, private val showInheritors: Boolean,
|
||||
private val usagesLimit: Int, private val inheritorsLimit: Int
|
||||
) : FactoryInlayHintsCollector(editor) {
|
||||
|
||||
companion object {
|
||||
private val hintsKey = Key.create<CachedValue<CachedHints>>("CodeVisionHints")
|
||||
|
||||
|
||||
fun searchUsages(element: PsiElement, usagesLimit: Int): Usages? {
|
||||
val countingProcessor = CountingUpToLimitProcessor<Any>(usagesLimit)
|
||||
if (element is KtClass) {
|
||||
ReferencesSearch.search(element).forEach(countingProcessor)
|
||||
} else {
|
||||
element.toPossiblyFakeLightMethods().firstOrNull() // inline methods don't have light analogues (fake needed)
|
||||
?.let { MethodReferencesSearch.search(it).forEach(countingProcessor) }
|
||||
}
|
||||
|
||||
val (usagesNum, limitReached) = countingProcessor
|
||||
return if (usagesNum > 0) Usages(usagesNum, limitReached) else null
|
||||
}
|
||||
|
||||
private fun searchFunctionOverrides(function: KtFunction, inheritorsLimit: Int): KotlinCodeVisionLimitedHint? {
|
||||
return LightClassUtil.getLightClassMethod(function)?.let { it ->
|
||||
val countingProcessor = CountingUpToLimitProcessor<Any>(inheritorsLimit)
|
||||
OverridingMethodsSearch.search(it, true).forEach(countingProcessor)
|
||||
val (overridingNum, limitReached) = countingProcessor
|
||||
|
||||
if (overridingNum > 0) {
|
||||
if (function.isAbstract()) FunctionImplementations(overridingNum, limitReached)
|
||||
else FunctionOverrides(overridingNum, limitReached)
|
||||
} else null
|
||||
}
|
||||
}
|
||||
|
||||
private fun searchClassInheritors(clazz: KtClass, inheritorsLimit: Int): KotlinCodeVisionLimitedHint? {
|
||||
return clazz.toLightClass()?.let {
|
||||
val countingProcessor = CountingUpToLimitProcessor<Any>(inheritorsLimit)
|
||||
DirectClassInheritorsSearch.search(it, clazz.useScope, true).forEach(countingProcessor)
|
||||
val (inheritorsNum, limitReached) = countingProcessor
|
||||
if (inheritorsNum > 0) {
|
||||
if (clazz.isInterface()) InterfaceImplementations(inheritorsNum, limitReached)
|
||||
else ClassInheritors(inheritorsNum, limitReached)
|
||||
} else null
|
||||
}
|
||||
}
|
||||
|
||||
private fun searchPropertyOverriding(property: KtProperty, inheritorsLimit: Int): KotlinCodeVisionLimitedHint? {
|
||||
val countingProcessor = CountingUpToLimitProcessor<PsiElement>(inheritorsLimit)
|
||||
KotlinDefinitionsSearcher.processPropertyImplementationsMethods(
|
||||
property.toPossiblyFakeLightMethods(),
|
||||
GlobalSearchScope.allScope(property.project),
|
||||
countingProcessor
|
||||
)
|
||||
val (overridesNum, limitReached) = countingProcessor
|
||||
return if (overridesNum > 0) PropertyOverrides(overridesNum, limitReached) else null
|
||||
}
|
||||
}
|
||||
|
||||
override fun collect(element: PsiElement, editor: Editor, sink: InlayHintsSink): Boolean {
|
||||
if (!showUsages && !showInheritors) return false
|
||||
if (!isElementOfInterest(element)) return true
|
||||
if (editor.project == null) return false
|
||||
|
||||
val hints = retrieveHints(editor.project!!, element)
|
||||
val inlayPresentations =
|
||||
hints.takeIf { it.isNotEmpty() }?.let { listOf(prepareBlockElements(element, editor, hints)) } ?: emptyList()
|
||||
|
||||
inlayPresentations.forEach { hintPair ->
|
||||
sink.addBlockElement(
|
||||
hintPair.first,
|
||||
relatesToPrecedingText = true,
|
||||
showAbove = true,
|
||||
priority = 0,
|
||||
presentation = hintPair.second
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun retrieveHints(project: Project, element: PsiElement): List<KotlinCodeVisionLimitedHint> {
|
||||
val cachedValueProvider = CachedHintsProvider(
|
||||
element, showUsages, showInheritors, usagesLimit, inheritorsLimit
|
||||
)
|
||||
|
||||
return CachedValuesManager.getManager(project).run {
|
||||
this.getCachedValue(element, hintsKey, cachedValueProvider, false).takeUnless { isStale(it) } ?: run {
|
||||
this.createCachedValue(cachedValueProvider).also { element.putUserData(hintsKey, it) }.value
|
||||
}
|
||||
}.hints
|
||||
}
|
||||
|
||||
private fun isStale(cachedValue: CachedHints): Boolean {
|
||||
return cachedValue.showUsages != showUsages || cachedValue.showInheritors != showInheritors
|
||||
}
|
||||
|
||||
@Suppress("GrazieInspection")
|
||||
private fun prepareBlockElements(
|
||||
element: PsiElement,
|
||||
editor: Editor,
|
||||
hints: List<KotlinCodeVisionHint>
|
||||
): Pair<Int, InlayPresentation> {
|
||||
|
||||
assert(hints.isNotEmpty()) { "Attempt to build block elements whereas hints don't exist" }
|
||||
assert(hints.size <= 2) { "Hints other than usages-implementations are not expected" }
|
||||
|
||||
val offset = element.textRange.startOffset
|
||||
val line = editor.document.getLineNumber(offset)
|
||||
val lineStart = editor.document.getLineStartOffset(line)
|
||||
val indent = offset - lineStart
|
||||
|
||||
/*
|
||||
* presentations: <indent>[<Usages>][<space><Inheritors>]
|
||||
* hints: hint[0] hint[1]
|
||||
*/
|
||||
val presentations = arrayOfNulls<InlayPresentation>(hints.size * 2) // 2 or 4
|
||||
presentations[0] = factory.text(StringUtil.repeat(" ", indent))
|
||||
var pInd = 1
|
||||
for (hInd in hints.indices) { // handling usages & inheritors
|
||||
val hint: KotlinCodeVisionHint = hints[hInd]
|
||||
if (hInd != 0)
|
||||
presentations[pInd++] = factory.text(" ")
|
||||
|
||||
presentations[pInd++] = createPresentation(factory, element, editor, hint) // either Usages or Inheritors
|
||||
}
|
||||
|
||||
val filledPresentations = presentations.requireNoNulls()
|
||||
|
||||
val seq = factory.seq(*filledPresentations)
|
||||
val withAppearingSettings = factory.changeOnHover(seq, {
|
||||
val spaceAndSettings = arrayOf(factory.text(" "), createSettings(factory, element, editor))
|
||||
val withSettings = ArrayUtil.mergeArrays(filledPresentations, spaceAndSettings)
|
||||
factory.seq(*withSettings)
|
||||
}) { true }
|
||||
|
||||
return Pair(lineStart, withAppearingSettings)
|
||||
}
|
||||
|
||||
private fun isElementOfInterest(element: PsiElement): Boolean =
|
||||
element is KtClass
|
||||
|| (element is KtFunction && element !is KtFunctionLiteral)
|
||||
|| (element is KtProperty && !element.isLocal)
|
||||
|
||||
private fun createPresentation(
|
||||
factory: PresentationFactory, element: PsiElement, editor: Editor, result: KotlinCodeVisionHint
|
||||
): InlayPresentation {
|
||||
val text = factory.smallText(result.regularText)
|
||||
return factory.changeOnHover(text, {
|
||||
val onClick = factory.onClick(text, MouseButton.Left)
|
||||
{ event, _ -> result.onClick(editor, element, event) }
|
||||
applyReferenceColor(onClick)
|
||||
}) { true }
|
||||
}
|
||||
|
||||
private fun applyReferenceColor(presentation: InlayPresentation): InlayPresentation {
|
||||
return AttributesTransformerPresentation(presentation) {
|
||||
val attributes = EditorColorsManager.getInstance()
|
||||
.globalScheme.getAttributes(EditorColors.REFERENCE_HYPERLINK_COLOR).clone()
|
||||
attributes.apply { effectType = null }
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSettings(factory: PresentationFactory, element: PsiElement, editor: Editor): InlayPresentation {
|
||||
return createPresentation(factory, element, editor, SettingsHint())
|
||||
}
|
||||
|
||||
class CachedHintsProvider(
|
||||
private val element: PsiElement,
|
||||
private val showUsages: Boolean,
|
||||
private val showInheritors: Boolean,
|
||||
private val usagesLimit: Int,
|
||||
private val inheritorsLimit: Int
|
||||
) : CachedValueProvider<CachedHints> {
|
||||
|
||||
override fun compute(): CachedValueProvider.Result<CachedHints>? {
|
||||
val hints: MutableList<KotlinCodeVisionLimitedHint> = SmartList()
|
||||
|
||||
if (showUsages)
|
||||
searchUsages(element, usagesLimit)?.let { hints += it }
|
||||
|
||||
if (showInheritors) {
|
||||
when (element) {
|
||||
is KtFunction -> searchFunctionOverrides(element, inheritorsLimit)?.let { hints += it }
|
||||
is KtClass -> searchClassInheritors(element, inheritorsLimit)?.let { hints += it }
|
||||
is KtProperty -> searchPropertyOverriding(element, inheritorsLimit)?.let { hints += it }
|
||||
}
|
||||
}
|
||||
|
||||
return CachedValueProvider.Result(
|
||||
CachedHints(hints, this.showUsages, this.showInheritors),
|
||||
// kotlinOutOfCodeBlockTracker doesn't work for usages: new reference inside the block is not detected
|
||||
// MODIFICATION_COUNT {+: file reopening -> cache hit, -: any code modification resets all caches (records are in PSI)}
|
||||
PsiModificationTracker.MODIFICATION_COUNT,
|
||||
ProjectRootModificationTracker.getInstance(element.project) // tracking project structure changes
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class CountingUpToLimitProcessor<T>(private val limit: Int) : Processor<T> {
|
||||
private val findings = mutableSetOf<T>() // for properties it's crucial not to calculate setters and getters together
|
||||
|
||||
override fun process(t: T): Boolean {
|
||||
findings.add(t)
|
||||
return findings.size < limit
|
||||
}
|
||||
|
||||
operator fun component1(): Int = findings.size
|
||||
operator fun component2(): Boolean = findings.size >= limit
|
||||
}
|
||||
|
||||
data class CachedHints(
|
||||
val hints: List<KotlinCodeVisionLimitedHint>,
|
||||
val showUsages: Boolean,
|
||||
val showInheritors: Boolean
|
||||
)
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight.codevision
|
||||
|
||||
import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction
|
||||
import com.intellij.internal.statistic.eventLog.FeatureUsageData
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.highlighter.markers.OVERRIDDEN_FUNCTION
|
||||
import org.jetbrains.kotlin.idea.highlighter.markers.OVERRIDDEN_PROPERTY
|
||||
import org.jetbrains.kotlin.idea.highlighter.markers.SUBCLASSED_CLASS
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import java.awt.event.MouseEvent
|
||||
|
||||
abstract class KotlinCodeVisionHint(hintKey: String) {
|
||||
open val regularText: String = KotlinBundle.message(hintKey)
|
||||
|
||||
abstract fun onClick(editor: Editor, element: PsiElement, event: MouseEvent?)
|
||||
}
|
||||
|
||||
abstract class KotlinCodeVisionLimitedHint(num: Int, limitReached: Boolean, regularHintKey: String, tooManyHintKey: String) :
|
||||
KotlinCodeVisionHint(regularHintKey) {
|
||||
|
||||
override val regularText: String =
|
||||
if (limitReached) KotlinBundle.message(tooManyHintKey, num) else KotlinBundle.message(regularHintKey, num)
|
||||
}
|
||||
|
||||
private const val IMPLEMENTATIONS_KEY = "hints.codevision.implementations.format"
|
||||
private const val IMPLEMENTATIONS_TO_MANY_KEY = "hints.codevision.implementations.too_many.format"
|
||||
|
||||
private const val INHERITORS_KEY = "hints.codevision.inheritors.format"
|
||||
private const val INHERITORS_TO_MANY_KEY = "hints.codevision.inheritors.to_many.format"
|
||||
|
||||
private const val OVERRIDES_KEY = "hints.codevision.overrides.format"
|
||||
private const val OVERRIDES_TOO_MANY_KEY = "hints.codevision.overrides.to_many.format"
|
||||
|
||||
private const val USAGES_KEY = "hints.codevision.usages.format"
|
||||
private const val USAGES_TOO_MANY_KEY = "hints.codevision.usages.too_many.format"
|
||||
|
||||
private const val SETTINGS_FORMAT = "hints.codevision.settings"
|
||||
|
||||
// FUS = Feature Usage Statistics
|
||||
const val FUS_GROUP_ID = "kotlin.code.vision"
|
||||
const val USAGES_CLICKED_EVENT_ID = "usages.clicked"
|
||||
const val INHERITORS_CLICKED_EVENT_ID = "inheritors.clicked"
|
||||
const val SETTING_CLICKED_EVENT_ID = "setting.clicked"
|
||||
|
||||
// FUD = Feature Usage Data
|
||||
const val FUD_KEY = "location"
|
||||
const val FUD_FUNCTION = "function"
|
||||
const val FUD_PROPERTY = "property"
|
||||
const val FUD_CLASS = "class"
|
||||
const val FUD_INTERFACE = "interface"
|
||||
|
||||
class Usages(usagesNum: Int, limitReached: Boolean) :
|
||||
KotlinCodeVisionLimitedHint(usagesNum, limitReached, USAGES_KEY, USAGES_TOO_MANY_KEY) {
|
||||
|
||||
override fun onClick(editor: Editor, element: PsiElement, event: MouseEvent?) {
|
||||
logUsageStatistics(editor.project, FUS_GROUP_ID, USAGES_CLICKED_EVENT_ID)
|
||||
GotoDeclarationAction.startFindUsages(editor, editor.project!!, element)
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionOverrides(overridesNum: Int, limitReached: Boolean) :
|
||||
KotlinCodeVisionLimitedHint(overridesNum, limitReached, OVERRIDES_KEY, OVERRIDES_TOO_MANY_KEY) {
|
||||
|
||||
override fun onClick(editor: Editor, element: PsiElement, event: MouseEvent?) {
|
||||
val data = FeatureUsageData().addData(FUD_KEY, FUD_FUNCTION)
|
||||
logUsageStatistics(editor.project, FUS_GROUP_ID, INHERITORS_CLICKED_EVENT_ID, data)
|
||||
val navigationHandler = OVERRIDDEN_FUNCTION.navigationHandler
|
||||
navigationHandler.navigate(event, (element as KtFunction).nameIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionImplementations(implNum: Int, limitReached: Boolean) :
|
||||
KotlinCodeVisionLimitedHint(implNum, limitReached, IMPLEMENTATIONS_KEY, IMPLEMENTATIONS_TO_MANY_KEY) {
|
||||
|
||||
override fun onClick(editor: Editor, element: PsiElement, event: MouseEvent?) {
|
||||
val data = FeatureUsageData().addData(FUD_KEY, FUD_FUNCTION)
|
||||
logUsageStatistics(editor.project, FUS_GROUP_ID, INHERITORS_CLICKED_EVENT_ID, data)
|
||||
val navigationHandler = OVERRIDDEN_FUNCTION.navigationHandler
|
||||
navigationHandler.navigate(event, (element as KtFunction).nameIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
class PropertyOverrides(overridesNum: Int, limitReached: Boolean) :
|
||||
KotlinCodeVisionLimitedHint(overridesNum, limitReached, OVERRIDES_KEY, OVERRIDES_TOO_MANY_KEY) {
|
||||
|
||||
override fun onClick(editor: Editor, element: PsiElement, event: MouseEvent?) {
|
||||
val data = FeatureUsageData().addData(FUD_KEY, FUD_PROPERTY)
|
||||
logUsageStatistics(editor.project, FUS_GROUP_ID, INHERITORS_CLICKED_EVENT_ID, data)
|
||||
val navigationHandler = OVERRIDDEN_PROPERTY.navigationHandler
|
||||
navigationHandler.navigate(event, (element as KtProperty).nameIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
class ClassInheritors(inheritorsNum: Int, limitReached: Boolean) :
|
||||
KotlinCodeVisionLimitedHint(inheritorsNum, limitReached, INHERITORS_KEY, INHERITORS_TO_MANY_KEY) {
|
||||
|
||||
override fun onClick(editor: Editor, element: PsiElement, event: MouseEvent?) {
|
||||
val data = FeatureUsageData().addData(FUD_KEY, FUD_CLASS)
|
||||
logUsageStatistics(editor.project, FUS_GROUP_ID, INHERITORS_CLICKED_EVENT_ID, data)
|
||||
val navigationHandler = SUBCLASSED_CLASS.navigationHandler
|
||||
navigationHandler.navigate(event, (element as KtClass).nameIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
class InterfaceImplementations(implNum: Int, limitReached: Boolean) :
|
||||
KotlinCodeVisionLimitedHint(implNum, limitReached, IMPLEMENTATIONS_KEY, IMPLEMENTATIONS_TO_MANY_KEY) {
|
||||
|
||||
override fun onClick(editor: Editor, element: PsiElement, event: MouseEvent?) {
|
||||
val data = FeatureUsageData().addData(FUD_KEY, FUD_INTERFACE)
|
||||
logUsageStatistics(editor.project, FUS_GROUP_ID, INHERITORS_CLICKED_EVENT_ID, data)
|
||||
val navigationHandler = SUBCLASSED_CLASS.navigationHandler
|
||||
navigationHandler.navigate(event, (element as KtClass).nameIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsHint : KotlinCodeVisionHint(SETTINGS_FORMAT) {
|
||||
override fun onClick(editor: Editor, element: PsiElement, event: MouseEvent?) {
|
||||
val project = element.project
|
||||
logUsageStatistics(project, FUS_GROUP_ID, SETTING_CLICKED_EVENT_ID)
|
||||
CodeVisionInlayHintsConfigurable.showSettingsDialogForLanguage(project, element.language)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight.codevision
|
||||
|
||||
import com.intellij.codeInsight.hints.*
|
||||
import com.intellij.lang.Language
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
class KotlinCodeVisionProvider : InlayHintsProvider<KotlinCodeVisionProvider.KotlinCodeVisionSettings> {
|
||||
|
||||
override val key: SettingsKey<KotlinCodeVisionSettings> = SettingsKey("CodeVision")
|
||||
override val name: String = KotlinBundle.message("hints.title.codevision")
|
||||
override val previewText: String? = null
|
||||
|
||||
var usagesLimit: Int = 100
|
||||
var inheritorsLimit: Int = 100
|
||||
|
||||
override fun isLanguageSupported(language: Language): Boolean = language is KotlinLanguage
|
||||
|
||||
override fun createConfigurable(settings: KotlinCodeVisionSettings): ImmediateConfigurable = createImmediateConfigurable(settings)
|
||||
|
||||
override fun createSettings(): KotlinCodeVisionSettings = KotlinCodeVisionSettings()
|
||||
|
||||
override fun getCollectorFor(
|
||||
file: PsiFile, editor: Editor, settings: KotlinCodeVisionSettings, sink: InlayHintsSink
|
||||
): InlayHintsCollector? {
|
||||
if (!settings.showUsages && !settings.showInheritors) return null
|
||||
return KotlinCodeVisionHintsCollector(editor, settings.showUsages, settings.showInheritors, usagesLimit, inheritorsLimit)
|
||||
}
|
||||
|
||||
data class KotlinCodeVisionSettings(var showUsages: Boolean = false, var showInheritors: Boolean = false)
|
||||
}
|
||||
@@ -144,7 +144,7 @@ interface TestableLineMarkerNavigator {
|
||||
fun getTargetsPopupDescriptor(element: PsiElement?): NavigationPopupDescriptor?
|
||||
}
|
||||
|
||||
private val SUBCLASSED_CLASS = MarkerType(
|
||||
val SUBCLASSED_CLASS = MarkerType(
|
||||
"SUBCLASSED_CLASS",
|
||||
{ getPsiClass(it)?.let(::getSubclassedClassTooltip) },
|
||||
object : LineMarkerNavigator() {
|
||||
@@ -155,7 +155,7 @@ private val SUBCLASSED_CLASS = MarkerType(
|
||||
}
|
||||
})
|
||||
|
||||
private val OVERRIDDEN_FUNCTION = object : MarkerType(
|
||||
val OVERRIDDEN_FUNCTION = object : MarkerType(
|
||||
"OVERRIDDEN_FUNCTION",
|
||||
{ getPsiMethod(it)?.let(::getOverriddenMethodTooltip) },
|
||||
object : LineMarkerNavigator() {
|
||||
@@ -176,7 +176,7 @@ private val OVERRIDDEN_FUNCTION = object : MarkerType(
|
||||
}
|
||||
}
|
||||
|
||||
private val OVERRIDDEN_PROPERTY = object : MarkerType(
|
||||
val OVERRIDDEN_PROPERTY = object : MarkerType(
|
||||
"OVERRIDDEN_PROPERTY",
|
||||
{ it?.let { getOverriddenPropertyTooltip(it.parent as KtNamedDeclaration) } },
|
||||
object : LineMarkerNavigator() {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// MODE: inheritors
|
||||
|
||||
<# block [ 2 Inheritors] #>
|
||||
abstract class SomeClass {
|
||||
<# block [ 1 Override] #>
|
||||
open fun someFun() = ""
|
||||
<# block [ 2 Implementations] #>
|
||||
abstract fun someAbstractFun()
|
||||
}
|
||||
|
||||
class DerivedClassA : SomeClass {
|
||||
override fun someFun() = "overridden"
|
||||
override fun someAbstractFun() = "overridden"
|
||||
}
|
||||
class DerivedClassB : SomeClass {
|
||||
override fun someAbstractFun() = "overridden"
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// MODE: inheritors
|
||||
|
||||
<# block [ 4 Inheritors] #>
|
||||
open class SomeClass {
|
||||
class NestedDerivedClass: SomeClass() {} // <== (1): nested class
|
||||
}
|
||||
<# block [ 1 Inheritor] #>
|
||||
open class DerivedClass : SomeClass {} // <== (2): direct derived one
|
||||
class AnotherDerivedClass : SomeClass {} // <== (3): yet another derived one
|
||||
class DerivedDerivedClass : DerivedClass { // <== (): indirect inheritor of SomeClass
|
||||
fun main() {
|
||||
val someClassInstance = object : SomeClass() { // <== (4): anonymous derived one
|
||||
val somethingHere = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// MODE: usages
|
||||
|
||||
<# block [ 1 Usage] #>
|
||||
abstract class SomeClass {
|
||||
<# block [ 3 Usages] #>
|
||||
abstract fun someFun(): String
|
||||
fun someOtherFun() = someFun() // <== (1): delegation from another method
|
||||
val someProperty = someFun() // <== (2): property initializer
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val instance = object: SomeClass {
|
||||
<# block [ 1 Usage] #>
|
||||
override fun someFun(): String {} // <== (): used below
|
||||
}
|
||||
instance.someFun() <== (3): call on an instance
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// MODE: inheritors
|
||||
|
||||
<# block [ 1 Inheritor] #>
|
||||
abstract class SomeClass {
|
||||
<# block [ 1 Override] #>
|
||||
abstract val someAbstractProperty: Int
|
||||
<# block [ 2 Overrides] #>
|
||||
open val nonAbstractProperty: Int = 10
|
||||
open val notToBeOverriddenProperty: Int = 10
|
||||
}
|
||||
|
||||
<# block [ 1 Inheritor] #>
|
||||
open class DerivedClassA : SomeClass() {
|
||||
override val someAbstractProperty: Int = 5
|
||||
<# block [ 1 Override] #>
|
||||
override val nonAbstractProperty: Int = 15 // NOTE that DerivedClassB overrides both getter and setter but counted once
|
||||
}
|
||||
|
||||
class DerivedClassB : DerivedClassA() {
|
||||
override var nonAbstractProperty: Int = 15
|
||||
get() = 20
|
||||
set(value) {field = value / 2}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// MODE: usages
|
||||
|
||||
<# block [ 1 Usage] #>
|
||||
interface SomeClass {
|
||||
<# block [ 3 Usages] #>
|
||||
var someProperty = "initialized"
|
||||
fun someFun() = "it's " + someProperty // <== (1): reference from expression
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val instance = object: SomeClass {}
|
||||
val someString = instance.someProperty // <== (2): getter call
|
||||
instance.someProperty = "anotherValue" // <== (3): setter call
|
||||
@@ -0,0 +1,11 @@
|
||||
// MODE: usages
|
||||
|
||||
<# block [ 5 Usages] #>
|
||||
open class SomeClass {}
|
||||
class SomeOtherClass : SomeClass {} // <== (1): class extension
|
||||
class SomeYetOtherClass : SomeClass { // <== (2): class extension
|
||||
<# block [ 1 Usage] #>
|
||||
fun acceptsClass(param: SomeClass) {} // <== (3): parameter type
|
||||
fun returnsInterface(): SomeClass {} // <== (4): return type
|
||||
fun main() = acceptsClass(object : SomeClass {}) // <== (5): anonymous class instance
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// MODE: usages
|
||||
|
||||
<# block [ 3 Usages] #>
|
||||
fun function(param: String): Int = 1
|
||||
<# block [ 1 Usage] #>
|
||||
fun higherOrderFun(s: String, param: (String) -> Int) = param(s)
|
||||
|
||||
fun main() {
|
||||
function("someString")
|
||||
val functionRef = ::function
|
||||
higherOrderFun("someString", ::function)
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// MODE: inheritors
|
||||
|
||||
<# block [ 1 Implementation] #>
|
||||
interface SomeInterface {
|
||||
<# block [ 2 Implementations] #>
|
||||
fun interfaceMethodA()
|
||||
}
|
||||
<# block [ 1 Inheritor] #>
|
||||
open class SomeClass : SomeInterface {
|
||||
<# block [ 1 Override] #>
|
||||
override fun interfaceMethodA() {} // <== (1)
|
||||
}
|
||||
|
||||
class SomeDerivedClass : SomeClass() {
|
||||
override fun interfaceMethodA() {} // <== (2)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// MODE: inheritors
|
||||
|
||||
<# block [ 3 Implementations] #>
|
||||
interface SomeInterface {}
|
||||
interface SomeOtherInterface : SomeInterface {} // <== (1): interface extension
|
||||
class SomeClass : SomeInterface { // <== (2): interface implementation
|
||||
fun acceptsInterface(param: SomeInterface) {}
|
||||
fun main() = acceptsInterface(object : SomeInterface {}) // <== (3): anonymous class instance
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// MODE: usages
|
||||
|
||||
<# block [ 1 Usage] #>
|
||||
interface SomeInterface {
|
||||
<# block [ 3 Usages] #>
|
||||
fun someFun(): String
|
||||
fun someOtherFun() = someFun() // <== (1): delegation from another interface method
|
||||
val someProperty = someFun() // <== (2): property initializer
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val instance = object: SomeInterface {
|
||||
<# block [ 1 Usage] #>
|
||||
override fun someFun(): String {} // <== (): used below
|
||||
}
|
||||
instance.someFun() <== (3): call on an instance
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// MODE: inheritors
|
||||
|
||||
<# block [ 1 Implementation] #>
|
||||
interface SomeInterface {
|
||||
<# block [ 1 Override] #>
|
||||
fun interfaceMethodA() = 10
|
||||
}
|
||||
|
||||
class SomeClass : SomeInterface {
|
||||
override fun interfaceMethodA() = 20 // <== (1)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// MODE: inheritors
|
||||
|
||||
<# block [ 1 Implementation] #>
|
||||
interface SomeInterface {
|
||||
<# block [ 1 Override] #>
|
||||
open val interfaceProperty: String
|
||||
}
|
||||
|
||||
class SomeClass : SomeInterface {
|
||||
override val interfaceProperty: String = "overridden" // <== (1)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// MODE: usages
|
||||
|
||||
<# block [ 1 Usage] #>
|
||||
interface SomeInterface {
|
||||
<# block [ 2 Usages] #>
|
||||
val someProperty = "initialized"
|
||||
fun someFun() = "it's " + someProperty // <== (1):
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val instance = object: SomeInterface {}
|
||||
val someString = instance.someProperty // <== (2): call on an instance
|
||||
@@ -0,0 +1,11 @@
|
||||
// MODE: usages
|
||||
|
||||
<# block [ 5 Usages] #>
|
||||
interface SomeInterface {}
|
||||
interface SomeOtherInterface : SomeInterface {} // <== (1): interface extension
|
||||
class SomeClass : SomeInterface { // <== (2): interface implementation
|
||||
<# block [ 1 Usage] #>
|
||||
fun acceptsInterface(param: SomeInterface) {} // <== (3): parameter type
|
||||
fun returnsInterface(): SomeInterface {} // <== (4): return type
|
||||
fun main() = acceptsInterface(object : SomeInterface {}) // <== (5): anonymous class instance
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// MODE: usages-&-inheritors
|
||||
// USAGES-LIMIT: 3
|
||||
// INHERITORS-LIMIT: 2
|
||||
|
||||
<# block [ 3+ Usages 2+ Inheritors] #>
|
||||
open class SomeClass {
|
||||
class NestedDerivedClass: SomeClass() {} // <== (1): nested class
|
||||
}
|
||||
<# block [ 1 Usage 1 Inheritor] #>
|
||||
open class DerivedClass : SomeClass {} // <== (2): direct derived one
|
||||
class AnotherDerivedClass : SomeClass {} // <== (3): yet another derived one
|
||||
class DerivedDerivedClass : DerivedClass { // <== (): indirect inheritor of SomeClass
|
||||
fun main() {
|
||||
val someClassInstance = object : SomeClass() // { <== (4): anonymous derived one
|
||||
val somethingHere = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// MODE: usages-&-inheritors
|
||||
|
||||
<# block [ 4 Usages 4 Inheritors] #>
|
||||
open class SomeClass {
|
||||
class NestedDerivedClass: SomeClass() {} // <== (1): nested class
|
||||
}
|
||||
<# block [ 1 Usage 1 Inheritor] #>
|
||||
open class DerivedClass : SomeClass {} // <== (2): direct derived one
|
||||
class AnotherDerivedClass : SomeClass {} // <== (3): yet another derived one
|
||||
class DerivedDerivedClass : DerivedClass { // <== (): indirect inheritor of SomeClass
|
||||
fun main() {
|
||||
val someClassInstance = object : SomeClass() { // <== (4): anonymous derived one
|
||||
val somethingHere = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight.codevision
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.testFramework.utils.inlays.InlayHintsProviderTestCase
|
||||
import org.jetbrains.kotlin.idea.codeInsight.codevision.KotlinCodeVisionProvider.KotlinCodeVisionSettings
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import java.io.File
|
||||
|
||||
open class AbstractKotlinCodeVisionProviderTest : InlayHintsProviderTestCase() { // Abstract- prefix is just a convention for GenerateTests
|
||||
|
||||
fun doTest(testPath: String) { // named according to the convention imposed by GenerateTests
|
||||
assertThatActualHintsMatch(testPath)
|
||||
}
|
||||
|
||||
private fun assertThatActualHintsMatch(fileName: String) {
|
||||
val fileContents = FileUtil.loadFile(File(fileName), true)
|
||||
|
||||
val usagesLimit = InTextDirectivesUtils.findStringWithPrefixes(fileContents, "// USAGES-LIMIT: ")?.toInt() ?: 100
|
||||
val inheritorsLimit = InTextDirectivesUtils.findStringWithPrefixes(fileContents, "// INHERITORS-LIMIT: ")?.toInt() ?: 100
|
||||
|
||||
val codeVisionProvider = KotlinCodeVisionProvider()
|
||||
codeVisionProvider.usagesLimit = usagesLimit
|
||||
codeVisionProvider.inheritorsLimit = inheritorsLimit
|
||||
|
||||
val mode: KotlinCodeVisionSettings = when (InTextDirectivesUtils.findStringWithPrefixes(fileContents, "// MODE: ")) {
|
||||
"inheritors" -> inheritorsEnabled()
|
||||
"usages" -> usagesEnabled()
|
||||
"usages-&-inheritors" -> usagesAndInheritorsEnabled()
|
||||
else -> codeVisionDisabled()
|
||||
}
|
||||
|
||||
testProvider("kotlinCodeVision.kt", fileContents, codeVisionProvider, mode)
|
||||
}
|
||||
|
||||
private fun usagesAndInheritorsEnabled(): KotlinCodeVisionSettings = KotlinCodeVisionSettings(showUsages = true, showInheritors = true)
|
||||
|
||||
private fun inheritorsEnabled(): KotlinCodeVisionSettings = KotlinCodeVisionSettings(showUsages = false, showInheritors = true)
|
||||
|
||||
private fun usagesEnabled(): KotlinCodeVisionSettings = KotlinCodeVisionSettings(showUsages = true, showInheritors = false)
|
||||
|
||||
private fun codeVisionDisabled(): KotlinCodeVisionSettings = KotlinCodeVisionSettings(showUsages = false, showInheritors = false)
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
package org.jetbrains.kotlin.idea.codeInsight.codevision
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight.codevision;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/codeInsight/codeVision")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class KotlinCodeVisionProviderTestGenerated extends AbstractKotlinCodeVisionProviderTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInCodeVision() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/codeInsight/codeVision"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("ClassFunctionOverrides.kt")
|
||||
public void testClassFunctionOverrides() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/ClassFunctionOverrides.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassInheritors.kt")
|
||||
public void testClassInheritors() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/ClassInheritors.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassMethodUsages.kt")
|
||||
public void testClassMethodUsages() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/ClassMethodUsages.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassPropertiesOverrides.kt")
|
||||
public void testClassPropertiesOverrides() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/ClassPropertiesOverrides.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassPropertyUsages.kt")
|
||||
public void testClassPropertyUsages() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/ClassPropertyUsages.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ClassUsages.kt")
|
||||
public void testClassUsages() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/ClassUsages.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("GlobalFunctionUsages.kt")
|
||||
public void testGlobalFunctionUsages() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/GlobalFunctionUsages.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("InterfaceAbstractMethodImplementation.kt")
|
||||
public void testInterfaceAbstractMethodImplementation() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/InterfaceAbstractMethodImplementation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("InterfaceImplementations.kt")
|
||||
public void testInterfaceImplementations() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/InterfaceImplementations.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("InterfaceMethodUsages.kt")
|
||||
public void testInterfaceMethodUsages() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/InterfaceMethodUsages.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("InterfaceMethodsOverrides.kt")
|
||||
public void testInterfaceMethodsOverrides() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/InterfaceMethodsOverrides.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("InterfacePropertiesOverrides.kt")
|
||||
public void testInterfacePropertiesOverrides() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/InterfacePropertiesOverrides.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("InterfacePropertyUsages.kt")
|
||||
public void testInterfacePropertyUsages() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/InterfacePropertyUsages.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("InterfaceUsages.kt")
|
||||
public void testInterfaceUsages() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/InterfaceUsages.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TooManyUsagesAndInheritors.kt")
|
||||
public void testTooManyUsagesAndInheritors() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/TooManyUsagesAndInheritors.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UsagesAndInheritanceTogether.kt")
|
||||
public void testUsagesAndInheritanceTogether() throws Exception {
|
||||
runTest("idea/testData/codeInsight/codeVision/UsagesAndInheritanceTogether.kt");
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
package org.jetbrains.kotlin.idea.codeInsight.codevision;
|
||||
Reference in New Issue
Block a user