KT-38027 Support Code Vision feature in Kotlin // experimental status

Feature received "experimental" status due to the low performance.
The root cause is slow find-usages functionality the feature is based
on. Once the later is improved Code-Vision should be revisited.

Previous commit of KT-38027 series contains production ready state.

To launch performance check again one needs to enable Code Vision in
AbstractPerformanceProjectsTest.setUp() with the following fun:

private fun enabledCodeVision() {
  val codeVisionProvider = KotlinCodeVisionProvider()
  val settings = codeVisionProvider.createSettings().apply {
     showUsages = true
     showInheritors = true
  }

  InlayHintsSettings.instance().storeSettings(codeVisionProvider.key,
   KotlinLanguage.INSTANCE, settings)
}
This commit is contained in:
Andrei Klunnyi
2020-06-04 18:25:08 +02:00
parent b08f501aac
commit a3c881da59
5 changed files with 45 additions and 62 deletions
@@ -147,6 +147,16 @@
defaultValue="true" defaultValue="true"
restartRequired="false"/> restartRequired="false"/>
<registryKey key="kotlin.code-vision.usages"
description="Enable Code-Vision usages search"
defaultValue="false"
restartRequired="false"/>
<registryKey key="kotlin.code-vision.inheritors"
description="Enable Code-Vision inheritors search"
defaultValue="false"
restartRequired="false"/>
<fileEditorProvider implementation="org.jetbrains.kotlin.idea.scratch.ui.KtScratchFileEditorProvider"/> <fileEditorProvider implementation="org.jetbrains.kotlin.idea.scratch.ui.KtScratchFileEditorProvider"/>
<moduleBuilder builderClass="org.jetbrains.kotlin.tools.projectWizard.wizard.NewProjectWizardModuleBuilder"/> <moduleBuilder builderClass="org.jetbrains.kotlin.tools.projectWizard.wizard.NewProjectWizardModuleBuilder"/>
@@ -14,7 +14,6 @@ import com.intellij.internal.statistic.eventLog.FeatureUsageData
import com.intellij.internal.statistic.service.fus.collectors.FUCounterUsageLogger import com.intellij.internal.statistic.service.fus.collectors.FUCounterUsageLogger
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.ui.layout.panel import com.intellij.ui.layout.panel
import org.jetbrains.kotlin.idea.KotlinBundle
import javax.swing.JPanel import javax.swing.JPanel
@@ -27,27 +26,12 @@ fun logUsageStatistics(project: Project?, groupId: String, eventId: String) =
fun logUsageStatistics(project: Project?, groupId: String, eventId: String, data: FeatureUsageData) = fun logUsageStatistics(project: Project?, groupId: String, eventId: String, data: FeatureUsageData) =
FUCounterUsageLogger.getInstance().logEvent(project, groupId, eventId, data) FUCounterUsageLogger.getInstance().logEvent(project, groupId, eventId, data)
fun createImmediateConfigurable(settings: KotlinCodeVisionProvider.KotlinCodeVisionSettings): ImmediateConfigurable { fun createImmediateConfigurable(): ImmediateConfigurable {
return object : ImmediateConfigurable { return object : ImmediateConfigurable {
override fun createComponent(listener: ChangeListener): JPanel { override fun createComponent(listener: ChangeListener): JPanel = panel {}
return panel {}
}
override val cases: List<ImmediateConfigurable.Case> override val cases: List<ImmediateConfigurable.Case> = emptyList()
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 override val mainCheckboxText: String = ""
get() = KotlinBundle.message("hints.title.codevision.show.hints.for")
} }
} }
@@ -27,25 +27,8 @@ fun logUsageStatistics(project: Project?, groupId: String, eventId: String, data
project?.let { FUCounterUsageLogger.getInstance().logEvent(project, groupId, eventId, data) } project?.let { FUCounterUsageLogger.getInstance().logEvent(project, groupId, eventId, data) }
} }
fun createImmediateConfigurable(settings: KotlinCodeVisionProvider.KotlinCodeVisionSettings): ImmediateConfigurable { fun createImmediateConfigurable(): ImmediateConfigurable {
return object : ImmediateConfigurable { return object : ImmediateConfigurable {
val usagesText = KotlinBundle.message("hints.title.codevision.usages") override fun createComponent(listener: ChangeListener): JPanel = panel {}
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) }
}
}
} }
} }
@@ -8,14 +8,15 @@ package org.jetbrains.kotlin.idea.codeInsight.codevision
import com.intellij.codeInsight.hints.* import com.intellij.codeInsight.hints.*
import com.intellij.lang.Language import com.intellij.lang.Language
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.KotlinLanguage
@Suppress("UnstableApiUsage") @Suppress("UnstableApiUsage")
class KotlinCodeVisionProvider : InlayHintsProvider<KotlinCodeVisionProvider.KotlinCodeVisionSettings> { class KotlinCodeVisionProvider : InlayHintsProvider<NoSettings> {
override val key: SettingsKey<KotlinCodeVisionSettings> = SettingsKey("CodeVision") override val key: SettingsKey<NoSettings> = SettingsKey("CodeVision")
override val name: String = KotlinBundle.message("hints.title.codevision") override val name: String = KotlinBundle.message("hints.title.codevision")
override val previewText: String? = null override val previewText: String? = null
@@ -24,16 +25,19 @@ class KotlinCodeVisionProvider : InlayHintsProvider<KotlinCodeVisionProvider.Kot
override fun isLanguageSupported(language: Language): Boolean = language is KotlinLanguage override fun isLanguageSupported(language: Language): Boolean = language is KotlinLanguage
override fun createConfigurable(settings: KotlinCodeVisionSettings): ImmediateConfigurable = createImmediateConfigurable(settings) override fun createConfigurable(settings: NoSettings): ImmediateConfigurable = createImmediateConfigurable()
override fun createSettings(): KotlinCodeVisionSettings = KotlinCodeVisionSettings() override fun createSettings(): NoSettings = NoSettings()
override fun getCollectorFor( override fun getCollectorFor(
file: PsiFile, editor: Editor, settings: KotlinCodeVisionSettings, sink: InlayHintsSink file: PsiFile, editor: Editor, settings: NoSettings, sink: InlayHintsSink
): InlayHintsCollector? { ): 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) val showUsages = Registry.`is`("kotlin.code-vision.usages", false)
val showInheritors = Registry.`is`("kotlin.code-vision.inheritors", false)
if (!showUsages && !showInheritors) return null
return KotlinCodeVisionHintsCollector(editor, showUsages, showInheritors, usagesLimit, inheritorsLimit)
}
} }
@@ -6,13 +6,18 @@
package org.jetbrains.kotlin.idea.codeInsight.codevision package org.jetbrains.kotlin.idea.codeInsight.codevision
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.registry.Registry
import com.intellij.testFramework.utils.inlays.InlayHintsProviderTestCase import com.intellij.testFramework.utils.inlays.InlayHintsProviderTestCase
import org.jetbrains.kotlin.idea.codeInsight.codevision.KotlinCodeVisionProvider.KotlinCodeVisionSettings
import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.InTextDirectivesUtils
import java.io.File import java.io.File
open class AbstractKotlinCodeVisionProviderTest : InlayHintsProviderTestCase() { // Abstract- prefix is just a convention for GenerateTests open class AbstractKotlinCodeVisionProviderTest : InlayHintsProviderTestCase() { // Abstract- prefix is just a convention for GenerateTests
companion object {
const val INHERITORS_KEY = "kotlin.code-vision.inheritors"
const val USAGES_KEY = "kotlin.code-vision.usages"
}
fun doTest(testPath: String) { // named according to the convention imposed by GenerateTests fun doTest(testPath: String) { // named according to the convention imposed by GenerateTests
assertThatActualHintsMatch(testPath) assertThatActualHintsMatch(testPath)
} }
@@ -27,21 +32,18 @@ open class AbstractKotlinCodeVisionProviderTest : InlayHintsProviderTestCase() {
codeVisionProvider.usagesLimit = usagesLimit codeVisionProvider.usagesLimit = usagesLimit
codeVisionProvider.inheritorsLimit = inheritorsLimit codeVisionProvider.inheritorsLimit = inheritorsLimit
val mode: KotlinCodeVisionSettings = when (InTextDirectivesUtils.findStringWithPrefixes(fileContents, "// MODE: ")) { when (InTextDirectivesUtils.findStringWithPrefixes(fileContents, "// MODE: ")) {
"inheritors" -> inheritorsEnabled() "inheritors" -> mode(usages = false, inheritors = true)
"usages" -> usagesEnabled() "usages" -> mode(usages = true, inheritors = false)
"usages-&-inheritors" -> usagesAndInheritorsEnabled() "usages-&-inheritors" -> mode(usages = true, inheritors = true)
else -> codeVisionDisabled() else -> mode(usages = false, inheritors = false)
} }
testProvider("kotlinCodeVision.kt", fileContents, codeVisionProvider, mode) testProvider("kotlinCodeVision.kt", fileContents, codeVisionProvider)
} }
private fun usagesAndInheritorsEnabled(): KotlinCodeVisionSettings = KotlinCodeVisionSettings(showUsages = true, showInheritors = true) private fun mode(usages: Boolean, inheritors: Boolean) {
Registry.get(USAGES_KEY).setValue(usages)
private fun inheritorsEnabled(): KotlinCodeVisionSettings = KotlinCodeVisionSettings(showUsages = false, showInheritors = true) Registry.get(INHERITORS_KEY).setValue(inheritors)
}
private fun usagesEnabled(): KotlinCodeVisionSettings = KotlinCodeVisionSettings(showUsages = true, showInheritors = false)
private fun codeVisionDisabled(): KotlinCodeVisionSettings = KotlinCodeVisionSettings(showUsages = false, showInheritors = false)
} }