diff --git a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/NewProjectWizardModuleBuilder.kt b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/NewProjectWizardModuleBuilder.kt index a75025b707c..e6230b4ec0e 100644 --- a/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/NewProjectWizardModuleBuilder.kt +++ b/idea/idea-new-project-wizard/src/org/jetbrains/kotlin/tools/projectWizard/wizard/NewProjectWizardModuleBuilder.kt @@ -12,9 +12,9 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ui.configuration.ModulesProvider import com.intellij.openapi.ui.Messages import com.intellij.util.SystemProperties +import org.jetbrains.kotlin.idea.configuration.ExperimentalFeatures import org.jetbrains.kotlin.idea.framework.KotlinModuleSettingStep import org.jetbrains.kotlin.idea.framework.KotlinTemplatesFactory -import org.jetbrains.kotlin.idea.projectWizard.NewProjectWizardService import org.jetbrains.kotlin.idea.projectWizard.ProjectCreationStats import org.jetbrains.kotlin.idea.projectWizard.UiEditorUsageStats import org.jetbrains.kotlin.idea.projectWizard.WizardStatsService @@ -61,7 +61,7 @@ class NewProjectWizardModuleBuilder : EmptyModuleBuilder() { private const val INVALID_PROJECT_NAME_MESSAGE = "Invalid project name" } - override fun isAvailable(): Boolean = NewProjectWizardService.isEnabled + override fun isAvailable(): Boolean = ExperimentalFeatures.NewWizard.isEnabled private var wizardContext: WizardContext? = null diff --git a/idea/performanceTests/test/org/jetbrains/kotlin/idea/perf/AbstractPerformanceJavaToKotlinCopyPasteConversionTest.kt b/idea/performanceTests/test/org/jetbrains/kotlin/idea/perf/AbstractPerformanceJavaToKotlinCopyPasteConversionTest.kt index b95c24ab433..b94995caae9 100644 --- a/idea/performanceTests/test/org/jetbrains/kotlin/idea/perf/AbstractPerformanceJavaToKotlinCopyPasteConversionTest.kt +++ b/idea/performanceTests/test/org/jetbrains/kotlin/idea/perf/AbstractPerformanceJavaToKotlinCopyPasteConversionTest.kt @@ -11,11 +11,11 @@ import com.intellij.openapi.application.runWriteAction import com.intellij.psi.PsiDocumentManager import com.intellij.util.ui.UIUtil import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.idea.configuration.ExperimentalFeatures import org.jetbrains.kotlin.idea.conversion.copy.AbstractJavaToKotlinCopyPasteConversionTest import org.jetbrains.kotlin.idea.conversion.copy.ConvertJavaCopyPasteProcessor import org.jetbrains.kotlin.idea.perf.Stats.Companion.WARM_UP import org.jetbrains.kotlin.idea.testFramework.commitAllDocuments -import org.jetbrains.kotlin.j2k.J2kConverterExtension import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.KotlinTestUtils import java.io.File @@ -38,7 +38,7 @@ abstract class AbstractPerformanceJavaToKotlinCopyPasteConversionTest(private va override fun setUp() { super.setUp() - J2kConverterExtension.isNewJ2k = newJ2K + ExperimentalFeatures.NewJ2k.isEnabled = newJ2K val index = j2kIndex() if (!warmedUp[index]) { diff --git a/idea/resources/META-INF/extensions/ide.xml b/idea/resources/META-INF/extensions/ide.xml index 08eecd58c6a..a673325201d 100644 --- a/idea/resources/META-INF/extensions/ide.xml +++ b/idea/resources/META-INF/extensions/ide.xml @@ -131,6 +131,16 @@ description="Allow a trailing comma on call-site" defaultValue="false" restartRequired="false"/> + + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt index ef67b6be1c3..db10e1bf7cc 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/JavaToKotlinAction.kt @@ -47,6 +47,7 @@ import com.intellij.psi.PsiJavaFile import com.intellij.psi.PsiManager import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.idea.configuration.ExperimentalFeatures import org.jetbrains.kotlin.idea.core.util.toPsiFile import org.jetbrains.kotlin.idea.formatter.commitAndUnblockDocument import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices @@ -129,7 +130,7 @@ class JavaToKotlinAction : AnAction() { project, ConverterSettings.defaultSettings, IdeaJavaToKotlinServices - ) else J2kConverterExtension.extension().createJavaToKotlinConverter( + ) else J2kConverterExtension.extension(useNewJ2k = ExperimentalFeatures.NewJ2k.isEnabled).createJavaToKotlinConverter( project, module, ConverterSettings.defaultSettings, @@ -138,7 +139,7 @@ class JavaToKotlinAction : AnAction() { converterResult = converter.filesToKotlin( javaFiles, if (forceUsingOldJ2k) J2kPostProcessor(formatCode = true) - else J2kConverterExtension.extension().createPostProcessor(formatCode = true), + else J2kConverterExtension.extension(useNewJ2k = ExperimentalFeatures.NewJ2k.isEnabled).createPostProcessor(formatCode = true), progress = ProgressManager.getInstance().progressIndicator!! ) } @@ -153,7 +154,7 @@ class JavaToKotlinAction : AnAction() { logJ2kConversionStatistics( ConversionType.FILES, - J2kConverterExtension.isNewJ2k, + ExperimentalFeatures.NewJ2k.isEnabled, conversionTime, linesCount, javaFiles.size @@ -232,7 +233,7 @@ class JavaToKotlinAction : AnAction() { .showInCenterOf(statusBar.component) } - if (!J2kConverterExtension.extension().doCheckBeforeConversion(project, module)) return + if (!J2kConverterExtension.extension(useNewJ2k = ExperimentalFeatures.NewJ2k.isEnabled).doCheckBeforeConversion(project, module)) return val firstSyntaxError = javaFiles.asSequence().map { PsiTreeUtil.findChildOfType(it, PsiErrorElement::class.java) }.firstOrNull() diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/ExperimentalFeatures.kt b/idea/src/org/jetbrains/kotlin/idea/configuration/ExperimentalFeatures.kt new file mode 100644 index 00000000000..09414de39d4 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/ExperimentalFeatures.kt @@ -0,0 +1,100 @@ +/* + * 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.configuration + +import com.intellij.openapi.util.registry.Registry +import com.intellij.ui.components.JBCheckBox +import org.jdesktop.swingx.VerticalLayout +import org.jetbrains.kotlin.config.KotlinCompilerVersion +import org.jetbrains.kotlin.idea.PlatformVersion +import org.jetbrains.kotlin.idea.projectWizard.WizardStatsService +import org.jetbrains.kotlin.idea.util.isDev +import org.jetbrains.kotlin.idea.util.isEap +import org.jetbrains.kotlin.idea.util.isSnapshot +import javax.swing.JCheckBox +import javax.swing.JPanel + +object ExperimentalFeatures { + val NewJ2k = ExperimentalFeature( + title = "New Java to Kotlin Converter", + registryKey = "kotlin.experimental.new.j2k", + enabledByDefault = true + ) + + val NewWizard = ExperimentalFeature( + title = "New Experimental Project Wizard", + registryKey = "kotlin.experimental.project.wizard", + enabledByDefault = false, + shouldBeShown = { + val platformVersion = PlatformVersion.getCurrent() ?: return@ExperimentalFeature true + platformVersion.platform != PlatformVersion.Platform.ANDROID_STUDIO + }, + onFeatureStatusChanged = { enabled -> + WizardStatsService.logWizardStatusChanged(isEnabled = enabled) + } + ) + + val allFeatures: List = listOf( + NewJ2k, + NewWizard + ) +} + +class ExperimentalFeature( + val title: String, + private val registryKey: String, + private val enabledByDefault: Boolean, + val shouldBeShown: () -> Boolean = { true }, + val onFeatureStatusChanged: (enabled: Boolean) -> Unit = {} +) { + var isEnabled + get() = Registry.`is`(registryKey, enabledByDefault) + set(value) { + Registry.get(registryKey).setValue(value) + } +} + +class ExperimentalFeaturesPanel : JPanel(VerticalLayout(5)) { + private val featuresWithCheckboxes = ExperimentalFeatures.allFeatures.map { feature -> + FeatureWithCheckbox( + feature, + JBCheckBox(feature.title, feature.isEnabled) + ) + } + + init { + featuresWithCheckboxes.forEach { (feature, checkBox) -> + if (feature.shouldBeShown()) { + add(checkBox) + } + } + } + + private data class FeatureWithCheckbox( + val feature: ExperimentalFeature, + val checkbox: JCheckBox + ) + + fun isModified() = featuresWithCheckboxes.any { (feature, checkBox) -> + feature.isEnabled != checkBox.isSelected + } + + fun applySelectedChanges() { + featuresWithCheckboxes.forEach { (feature, checkBox) -> + if (feature.isEnabled != checkBox.isSelected) { + feature.isEnabled = checkBox.isSelected + feature.onFeatureStatusChanged(checkBox.isSelected) + } + } + } + + companion object { + fun shouldBeShown(): Boolean { + val version = KotlinCompilerVersion.VERSION + return isEap(version) || isDev(version) || isSnapshot(version) + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinLanguageConfiguration.kt b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinLanguageConfiguration.kt index 7bb78bf2e69..f0d315adb08 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinLanguageConfiguration.kt +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinLanguageConfiguration.kt @@ -13,8 +13,6 @@ import org.jetbrains.kotlin.idea.KotlinPluginUpdater import org.jetbrains.kotlin.idea.KotlinPluginUtil import org.jetbrains.kotlin.idea.PluginUpdateStatus import org.jetbrains.kotlin.idea.configuration.ui.KotlinLanguageConfigurationForm -import org.jetbrains.kotlin.idea.projectWizard.NewProjectWizardService -import org.jetbrains.kotlin.j2k.J2kConverterExtension import javax.swing.JComponent class KotlinLanguageConfiguration : SearchableConfigurable, Configurable.NoScroll { @@ -65,14 +63,12 @@ class KotlinLanguageConfiguration : SearchableConfigurable, Configurable.NoScrol override fun getDisplayName(): String = "Kotlin" override fun isModified() = - form.useNewJ2kCheckBox.isSelected != J2kConverterExtension.isNewJ2k - || form.useNewProjectWizardCheckBox.isSelected != NewProjectWizardService.isEnabled + form.experimentalFeaturesPanel.isModified() override fun apply() { // Selected channel is now saved automatically - J2kConverterExtension.isNewJ2k = form.useNewJ2kCheckBox.isSelected - NewProjectWizardService.isEnabled = form.useNewProjectWizardCheckBox.isSelected + form.experimentalFeaturesPanel.applySelectedChanges() } private fun setInstalledVersion(installedVersion: String?, installingStatus: String?) { @@ -81,8 +77,6 @@ class KotlinLanguageConfiguration : SearchableConfigurable, Configurable.NoScrol } override fun createComponent(): JComponent? { - form.useNewJ2kCheckBox.isSelected = J2kConverterExtension.isNewJ2k - form.useNewProjectWizardCheckBox.isSelected = NewProjectWizardService.isEnabled form.updateCheckProgressIcon.suspend() form.updateCheckProgressIcon.setPaintPassiveIcon(false) diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/ui/KotlinLanguageConfigurationForm.form b/idea/src/org/jetbrains/kotlin/idea/configuration/ui/KotlinLanguageConfigurationForm.form index 71e522348cb..7717588c78c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/ui/KotlinLanguageConfigurationForm.form +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/ui/KotlinLanguageConfigurationForm.form @@ -150,7 +150,7 @@ - + @@ -161,22 +161,17 @@ - + - + - - - + - + - + - - - - + diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/ui/KotlinLanguageConfigurationForm.java b/idea/src/org/jetbrains/kotlin/idea/configuration/ui/KotlinLanguageConfigurationForm.java index c8ac3a6eb2f..8a034bd3f82 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/ui/KotlinLanguageConfigurationForm.java +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/ui/KotlinLanguageConfigurationForm.java @@ -10,6 +10,7 @@ import com.intellij.openapi.util.registry.Registry; import com.intellij.util.ui.AsyncProcessIcon; import org.jetbrains.kotlin.idea.KotlinPluginUtil; import org.jetbrains.kotlin.idea.PlatformVersion; +import org.jetbrains.kotlin.idea.configuration.ExperimentalFeaturesPanel; import org.jetbrains.kotlin.idea.util.VersioningKt; import javax.swing.*; @@ -28,14 +29,12 @@ public class KotlinLanguageConfigurationForm { private JTextPane currentVersion; private JPanel bundledCompilerVersionPanel; private JTextPane compilerVersion; - public JCheckBox useNewJ2kCheckBox; - public JCheckBox useNewProjectWizardCheckBox; + public ExperimentalFeaturesPanel experimentalFeaturesPanel; + private JPanel experimentalFeaturesPanelContainer; public KotlinLanguageConfigurationForm() { showVerifierDisabledStatus(); - - useNewProjectWizardCheckBox.setVisible(!isAndroidStudio()); - + experimentalFeaturesPanelContainer.setVisible(ExperimentalFeaturesPanel.Companion.shouldBeShown()); String pluginVersion = KotlinPluginUtil.getPluginVersion(); if (KotlinPluginUtil.isPatched()) { diff --git a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPasteProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPasteProcessor.kt index 4e53207495c..4173aa63c2e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPasteProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/ConvertJavaCopyPasteProcessor.kt @@ -36,6 +36,7 @@ import org.jetbrains.kotlin.idea.actions.JavaToKotlinAction import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference import org.jetbrains.kotlin.idea.codeInsight.KotlinCopyPasteReferenceProcessor import org.jetbrains.kotlin.idea.codeInsight.KotlinReferenceData +import org.jetbrains.kotlin.idea.configuration.ExperimentalFeatures import org.jetbrains.kotlin.idea.core.util.range import org.jetbrains.kotlin.idea.core.util.start import org.jetbrains.kotlin.idea.editor.KotlinEditorOptions @@ -190,7 +191,7 @@ class ConvertJavaCopyPasteProcessor : CopyPastePostProcessor("org.jetbrains.kotlin.j2kConverterExtension") - fun extension(useNewJ2k: Boolean = isNewJ2k): J2kConverterExtension = + fun extension(useNewJ2k: Boolean): J2kConverterExtension = EP_NAME.extensions.first { it.isNewJ2k == useNewJ2k } } }