From b8ef3d50aa00fb208d377fcedc04600d634a60fe Mon Sep 17 00:00:00 2001 From: Konstantin Nisht Date: Mon, 27 Mar 2023 18:07:26 +0200 Subject: [PATCH] [platform] IJPL-49: Improve API GitOrigin-RevId: f71d0c16118a030166f102e9a8e0fb4e33930b68 --- .../environment/JvmEnvironmentKeyProvider.kt | 3 +- .../ide/warmup/JdkWarmupConfigurator.kt | 5 ++- .../resources/messages/JavaBundle.properties | 1 + .../ide/environment/EnvironmentKey.kt | 42 +++++++++++++------ .../ide/environment/EnvironmentService.kt | 28 +++++++------ .../impl/DefaultEnvironmentService.kt | 4 +- .../impl/EnvironmentKeyStubGenerator.kt | 2 +- .../impl/HeadlessEnvironmentService.kt | 6 +-- .../com/intellij/ide/environment/impl/util.kt | 2 + .../ide/startup/CheckKeysStartupActivity.kt | 2 +- .../HeadlessEnvironmentServiceTest.kt | 8 ++-- .../GradleCommandLineProjectConfigurator.kt | 2 +- ...ommandLineInspectionProjectConfigurator.kt | 2 +- 13 files changed, 63 insertions(+), 44 deletions(-) diff --git a/java/execution/openapi/src/com/intellij/execution/environment/JvmEnvironmentKeyProvider.kt b/java/execution/openapi/src/com/intellij/execution/environment/JvmEnvironmentKeyProvider.kt index a553ef78a5ef..75a8412c4934 100644 --- a/java/execution/openapi/src/com/intellij/execution/environment/JvmEnvironmentKeyProvider.kt +++ b/java/execution/openapi/src/com/intellij/execution/environment/JvmEnvironmentKeyProvider.kt @@ -10,8 +10,9 @@ class JvmEnvironmentKeyProvider : EnvironmentKeyProvider { companion object { val JDK_KEY = EnvironmentKey.create("project.jdk", JavaBundle.messagePointer("environment.key.description.project.jdk")) + val JDK_NAME = EnvironmentKey.createWithDefaultValue("project.jdk.name", JavaBundle.messagePointer("environment.key.description.project.jdk.name"), "warmup_jdk") } - override fun getAllKeys(): List = listOf(JDK_KEY) + override fun getAllKeys(): List = listOf(JDK_KEY, JDK_NAME) override suspend fun getRequiredKeys(project: Project): List = listOf() } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/ide/warmup/JdkWarmupConfigurator.kt b/java/java-impl/src/com/intellij/ide/warmup/JdkWarmupConfigurator.kt index 5d38da19e220..0e828b4adcd8 100644 --- a/java/java-impl/src/com/intellij/ide/warmup/JdkWarmupConfigurator.kt +++ b/java/java-impl/src/com/intellij/ide/warmup/JdkWarmupConfigurator.kt @@ -17,13 +17,14 @@ class JdkWarmupConfigurator : WarmupConfigurator { override val configuratorPresentableName: String = "warmupJdkConfigurator" override suspend fun prepareEnvironment(projectPath: Path) { - val configuredJdk = service().getEnvironmentValueOrNull(JvmEnvironmentKeyProvider.JDK_KEY) + val configuredJdk = service().getEnvironmentValue(JvmEnvironmentKeyProvider.JDK_KEY) if (configuredJdk == null) { println("Environment does not provide configured JDK") return } val configuredJdkPath = Path.of(configuredJdk) - val jdk = JavaSdk.getInstance().createJdk("warmup_jdk", configuredJdkPath.toString()) + val jdkName = service().getEnvironmentValue(JvmEnvironmentKeyProvider.JDK_NAME) + val jdk = JavaSdk.getInstance().createJdk(jdkName, configuredJdkPath.toString()) writeAction { ProjectJdkTable.getInstance().addJdk(jdk) } diff --git a/java/openapi/resources/messages/JavaBundle.properties b/java/openapi/resources/messages/JavaBundle.properties index 365c7b89dd0c..80b3e2eec3bd 100644 --- a/java/openapi/resources/messages/JavaBundle.properties +++ b/java/openapi/resources/messages/JavaBundle.properties @@ -1780,6 +1780,7 @@ intention.family.name.upgrade.jdk=Upgrade JDK intention.name.upgrade.jdk.to=Upgrade JDK to {0}+ intention.family.name.box.primitive.in.conditional.branch=Box primitive value in conditional branch environment.key.description.project.jdk=Absolute path to project JDK +environment.key.description.project.jdk.name=IDE-visible name of project JDK. progress.title.detecting.jdk=Detecting JDK progress.title.restore.references=Restoring references increase.language.level.preview.description=Language level for module ''{0}'' will be changed to ''{1}'' diff --git a/platform/platform-api/src/com/intellij/ide/environment/EnvironmentKey.kt b/platform/platform-api/src/com/intellij/ide/environment/EnvironmentKey.kt index 2bd24b57c305..3fbe1f8026e8 100644 --- a/platform/platform-api/src/com/intellij/ide/environment/EnvironmentKey.kt +++ b/platform/platform-api/src/com/intellij/ide/environment/EnvironmentKey.kt @@ -27,29 +27,37 @@ sealed interface EnvironmentKey { */ val description: Supplier<@EnvironmentKeyDescription String> - /** - * The default value for a key. - * If [defaultValue] for a key is empty, then the key has **no** default value, - * and [EnvironmentService.requestEnvironmentValue] enters in its error handling state. - */ - val defaultValue: @NonNls String - companion object { @JvmStatic - @JvmOverloads - fun create(id: @NonNls String, description: Supplier<@EnvironmentKeyDescription String>, defaultValue: @NonNls String = ""): EnvironmentKey { - return EnvironmentKeyImpl(id, description, defaultValue) + fun create(id: @NonNls String, description: Supplier<@EnvironmentKeyDescription String>): EnvironmentKey { + return EnvironmentKeyImpl(id, description) } - private class EnvironmentKeyImpl(override val id: @NonNls String, - override val description: Supplier<@Nls String>, - override val defaultValue: @NonNls String) : EnvironmentKey { + @JvmStatic + fun createWithDefaultValue(id: @NonNls String, description: Supplier<@EnvironmentKeyDescription String>, defaultValue: @NonNls String): DefaultedEnvironmentKey { + require(defaultValue.isNotEmpty()) { + "Empty strings as default values are not supported." + } + return DefaultEnvironmentKeyImpl(id, description, defaultValue) + } + + private open class EnvironmentKeyImpl( + override val id: @NonNls String, + override val description: Supplier<@Nls String>) : EnvironmentKey { override fun equals(other: Any?): Boolean = other is EnvironmentKeyImpl && other.id == this.id override fun hashCode(): Int = id.hashCode() } + private class DefaultEnvironmentKeyImpl( + id: String, + description: Supplier<@EnvironmentKeyDescription String>, + override val defaultValue: @NonNls String): EnvironmentKeyImpl(id, description), DefaultedEnvironmentKey { + override fun equals(other: Any?): Boolean = super.equals(other) + override fun hashCode(): Int = super.hashCode() + } + @NlsContext(prefix = "environment.key.description") @Nls(capitalization = Nls.Capitalization.Sentence) @Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE, AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.FUNCTION, @@ -58,4 +66,12 @@ sealed interface EnvironmentKey { } } +interface DefaultedEnvironmentKey : EnvironmentKey { + /** + * The default value for a key. + * The default value **must be non-empty**. + */ + val defaultValue: @NonNls String +} + diff --git a/platform/platform-api/src/com/intellij/ide/environment/EnvironmentService.kt b/platform/platform-api/src/com/intellij/ide/environment/EnvironmentService.kt index c8934015cbfd..724411475310 100644 --- a/platform/platform-api/src/com/intellij/ide/environment/EnvironmentService.kt +++ b/platform/platform-api/src/com/intellij/ide/environment/EnvironmentService.kt @@ -14,9 +14,21 @@ import org.jetbrains.annotations.ApiStatus interface EnvironmentService { /** - * Retrieves a value from the environment. + * Retrieves a value for [key] from the environment and returns `null` if value is not defined. + * + * This method can be used if a client can provide their own ways to mitigate the absence of a value. + * If the key is necessary, then [requestEnvironmentValue] should be used + */ + suspend fun getEnvironmentValue(key: EnvironmentKey): String? + + /** + * The same as [getEnvironmentValue], but avoids nullability issues when [key] has a default value. + */ + suspend fun getEnvironmentValue(key: DefaultedEnvironmentKey): String = getEnvironmentValue(key as EnvironmentKey) ?: key.defaultValue + + /** + * Retrieves a value from the environment and performs environment-specific action if value is not defined. * - * If the environment does not have a value for the [key], then the behavior of this method is implementation-defined. * In particular, current implementations of this service have the following semantics on an absent key: * - If the environment allows the usage of UI, then this method returns `null`, and the client may use a modal dialog to get a value. * - If the environment does not allow the usage of UI, then [java.util.concurrent.CancellationException] is thrown, so [requestEnvironmentValue] never @@ -25,7 +37,7 @@ interface EnvironmentService { * The supposed workflow is sketched in the following snippet: * ``` * suspend fun requestUserDecision() : ChoiceData { - * val value = service.getEnvironmentValue(MY_AWESOME_KEY) + * val value : String = service.requestEnvironmentValue(MY_AWESOME_KEY) * // if we are in a headless environment and we have no value for a key, * // then the next line will not be executed because of an exception * if (value != null) { @@ -39,14 +51,4 @@ interface EnvironmentService { */ suspend fun requestEnvironmentValue(key: EnvironmentKey): String? - /** - * The same as [requestEnvironmentValue], but always returns `null` when a value for [key] is absent. - * - * Please avoid using this method if you are not sure that you need it. - * The environment has more capabilities to obtain a value than the client code. - * - * For specifying default values, consider using [EnvironmentKey.defaultValue]. - */ - suspend fun getEnvironmentValueOrNull(key: EnvironmentKey): String? - } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ide/environment/impl/DefaultEnvironmentService.kt b/platform/platform-impl/src/com/intellij/ide/environment/impl/DefaultEnvironmentService.kt index 0aa9163f583a..eb693973f93a 100644 --- a/platform/platform-impl/src/com/intellij/ide/environment/impl/DefaultEnvironmentService.kt +++ b/platform/platform-impl/src/com/intellij/ide/environment/impl/DefaultEnvironmentService.kt @@ -11,9 +11,9 @@ class DefaultEnvironmentService : BaseEnvironmentService() { LOG.warn("Access to UI is not allowed in the headless environment") } checkKeyRegistered(key) - return key.defaultValue.ifEmpty { null } + return null } - override suspend fun getEnvironmentValueOrNull(key: EnvironmentKey): String? = requestEnvironmentValue(key) + override suspend fun getEnvironmentValue(key: EnvironmentKey): String? = requestEnvironmentValue(key) } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ide/environment/impl/EnvironmentKeyStubGenerator.kt b/platform/platform-impl/src/com/intellij/ide/environment/impl/EnvironmentKeyStubGenerator.kt index 1b6e6e3bae2c..147ae20d2b11 100644 --- a/platform/platform-impl/src/com/intellij/ide/environment/impl/EnvironmentKeyStubGenerator.kt +++ b/platform/platform-impl/src/com/intellij/ide/environment/impl/EnvironmentKeyStubGenerator.kt @@ -82,7 +82,7 @@ private suspend fun generateKeyConfig(generateDescriptions: Boolean, configurati writeEndArray() } writeStringField("key", key.id) - val value = configuration.get(key) ?: key.defaultValue + val value = configuration.get(key) ?: "" writeStringField("value", value) writeEndObject() } diff --git a/platform/platform-impl/src/com/intellij/ide/environment/impl/HeadlessEnvironmentService.kt b/platform/platform-impl/src/com/intellij/ide/environment/impl/HeadlessEnvironmentService.kt index 57060a710b41..c210f77d4ae8 100644 --- a/platform/platform-impl/src/com/intellij/ide/environment/impl/HeadlessEnvironmentService.kt +++ b/platform/platform-impl/src/com/intellij/ide/environment/impl/HeadlessEnvironmentService.kt @@ -31,10 +31,6 @@ class HeadlessEnvironmentService(scope: CoroutineScope) : BaseEnvironmentService return valueFromConfigurationFile } - val valueAsDefault = key.defaultValue.ifEmpty { null } - if (valueAsDefault != null) { - return valueAsDefault - } return onError() } @@ -42,7 +38,7 @@ class HeadlessEnvironmentService(scope: CoroutineScope) : BaseEnvironmentService throw MissingEnvironmentKeyException(key) } - override suspend fun getEnvironmentValueOrNull(key: EnvironmentKey): String? = scanForEnvironmentValue(key) { + override suspend fun getEnvironmentValue(key: EnvironmentKey): String? = scanForEnvironmentValue(key) { null } diff --git a/platform/platform-impl/src/com/intellij/ide/environment/impl/util.kt b/platform/platform-impl/src/com/intellij/ide/environment/impl/util.kt index a0393984b38a..701b5a3109c9 100644 --- a/platform/platform-impl/src/com/intellij/ide/environment/impl/util.kt +++ b/platform/platform-impl/src/com/intellij/ide/environment/impl/util.kt @@ -3,6 +3,7 @@ package com.intellij.ide.environment.impl import com.intellij.ide.CommandLineInspectionProgressReporter import com.intellij.ide.CommandLineInspectionProjectConfigurator +import com.intellij.ide.environment.DefaultedEnvironmentKey import com.intellij.ide.environment.EnvironmentKey import com.intellij.ide.warmup.WarmupConfigurator import com.intellij.openapi.diagnostic.logger @@ -19,6 +20,7 @@ class EnvironmentConfiguration(private val map: Map) { val EMPTY : EnvironmentConfiguration = EnvironmentConfiguration(emptyMap()) } fun get(key: EnvironmentKey) : String? = map[key] + fun get(key: DefaultedEnvironmentKey) : String = get(key as EnvironmentKey) ?: key.defaultValue } suspend fun produceConfigurationContext(projectDir: Path?): CommandLineInspectionProjectConfigurator.ConfiguratorContext { diff --git a/platform/platform-impl/src/com/intellij/ide/startup/CheckKeysStartupActivity.kt b/platform/platform-impl/src/com/intellij/ide/startup/CheckKeysStartupActivity.kt index 260019dd7d2b..b0581ac50664 100644 --- a/platform/platform-impl/src/com/intellij/ide/startup/CheckKeysStartupActivity.kt +++ b/platform/platform-impl/src/com/intellij/ide/startup/CheckKeysStartupActivity.kt @@ -22,7 +22,7 @@ class CheckKeysStartupActivity : ProjectActivity { var exceptionOccurred = false for (registry in blockingContext { EnvironmentKeyProvider.EP_NAME.extensionList }) { for (requiredKey in registry.getRequiredKeys(project)) { - val value = environmentService.getEnvironmentValueOrNull(requiredKey) + val value = environmentService.getEnvironmentValue(requiredKey) if (value == null) { exceptionOccurred = true messageBuilder.appendLine(HeadlessEnvironmentService.MissingEnvironmentKeyException(requiredKey).message) diff --git a/platform/platform-tests/testSrc/com/intellij/ide/environment/HeadlessEnvironmentServiceTest.kt b/platform/platform-tests/testSrc/com/intellij/ide/environment/HeadlessEnvironmentServiceTest.kt index aff678e5fb35..365267b27f8b 100644 --- a/platform/platform-tests/testSrc/com/intellij/ide/environment/HeadlessEnvironmentServiceTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/ide/environment/HeadlessEnvironmentServiceTest.kt @@ -29,7 +29,7 @@ class HeadlessEnvironmentServiceTest : LightPlatformTestCase() { private suspend fun getExistingKey(key: EnvironmentKey): String { val value1 = service().requestEnvironmentValue(key) - val value2 = service().getEnvironmentValueOrNull(key) + val value2 = service().getEnvironmentValue(key) TestCase.assertEquals(value1!!, value2) return value1 } @@ -115,7 +115,7 @@ class HeadlessEnvironmentServiceTest : LightPlatformTestCase() { fun testAbsentKey() = runTestWithValues(null, null) { try { - assertNull(service().getEnvironmentValueOrNull(dummyKey)) + assertNull(service().getEnvironmentValue(dummyKey)) service().requestEnvironmentValue(dummyKey) fail("should throw") } catch (e : HeadlessEnvironmentService.MissingEnvironmentKeyException) { @@ -135,7 +135,7 @@ class HeadlessEnvironmentServiceTest : LightPlatformTestCase() { fun testUnknownKey() = runTestWithValues(null, null) { try { - assertNull(service().getEnvironmentValueOrNull(dummyKey)) + assertNull(service().getEnvironmentValue(dummyKey)) service().requestEnvironmentValue(notRegisteredDummyKey) // the warning in log is intentional fail("should throw") @@ -147,7 +147,7 @@ class HeadlessEnvironmentServiceTest : LightPlatformTestCase() { private val dummyKey: EnvironmentKey = EnvironmentKey.create("my.dummy.test.key", { "My dummy test key\nWith a long description" }) private val notRegisteredDummyKey: EnvironmentKey = EnvironmentKey.create("not.registered.dummy.key", { "My dummy test key" }) -private val dummyKeyWithDefaultValue: EnvironmentKey = EnvironmentKey.create("my.dummy.test.key.with.default.value", { "My dummy test key with default value"}, "Foo") +private val dummyKeyWithDefaultValue: DefaultedEnvironmentKey = EnvironmentKey.createWithDefaultValue("my.dummy.test.key.with.default.value", { "My dummy test key with default value"}, "Foo") fun getJsonContents(value1: String?, value2: String?) : String = """[ diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleCommandLineProjectConfigurator.kt b/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleCommandLineProjectConfigurator.kt index 0104d0441704..b950148bd2b1 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleCommandLineProjectConfigurator.kt +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/GradleCommandLineProjectConfigurator.kt @@ -62,7 +62,7 @@ class GradleCommandLineProjectConfigurator : CommandLineInspectionProjectConfigu override fun configureProject(project: Project, context: ConfiguratorContext) { val basePath = project.basePath ?: return val service = service() - val projectSelectionKey = runBlockingCancellable { service.getEnvironmentValueOrNull(ProjectOpenKeyProvider.PROJECT_OPEN_PROCESSOR) } + val projectSelectionKey = runBlockingCancellable { service.getEnvironmentValue(ProjectOpenKeyProvider.PROJECT_OPEN_PROCESSOR) } if (projectSelectionKey != null && projectSelectionKey != "Gradle") { // something else was selected to open the project return diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/MavenCommandLineInspectionProjectConfigurator.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/MavenCommandLineInspectionProjectConfigurator.kt index ec788b2f6a00..a6667f2da01f 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/MavenCommandLineInspectionProjectConfigurator.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/MavenCommandLineInspectionProjectConfigurator.kt @@ -71,7 +71,7 @@ class MavenCommandLineInspectionProjectConfigurator : CommandLineInspectionProje if (FileUtil.findFirstThatExist(pomXmlFile) == null) return val service = service() - val projectSelectionKey = runBlockingCancellable { service.getEnvironmentValueOrNull(ProjectOpenKeyProvider.PROJECT_OPEN_PROCESSOR) } + val projectSelectionKey = runBlockingCancellable { service.getEnvironmentValue(ProjectOpenKeyProvider.PROJECT_OPEN_PROCESSOR) } if (projectSelectionKey != null && projectSelectionKey != "Maven") { // something else was selected to open the project return