[platform] IJPL-49: Improve API

GitOrigin-RevId: f71d0c16118a030166f102e9a8e0fb4e33930b68
This commit is contained in:
Konstantin Nisht
2023-03-27 18:07:26 +02:00
committed by intellij-monorepo-bot
parent 0e9a3c78af
commit b8ef3d50aa
13 changed files with 63 additions and 44 deletions

View File

@@ -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<EnvironmentKey> = listOf(JDK_KEY)
override fun getAllKeys(): List<EnvironmentKey> = listOf(JDK_KEY, JDK_NAME)
override suspend fun getRequiredKeys(project: Project): List<EnvironmentKey> = listOf()
}

View File

@@ -17,13 +17,14 @@ class JdkWarmupConfigurator : WarmupConfigurator {
override val configuratorPresentableName: String = "warmupJdkConfigurator"
override suspend fun prepareEnvironment(projectPath: Path) {
val configuredJdk = service<EnvironmentService>().getEnvironmentValueOrNull(JvmEnvironmentKeyProvider.JDK_KEY)
val configuredJdk = service<EnvironmentService>().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<EnvironmentService>().getEnvironmentValue(JvmEnvironmentKeyProvider.JDK_NAME)
val jdk = JavaSdk.getInstance().createJdk(jdkName, configuredJdkPath.toString())
writeAction {
ProjectJdkTable.getInstance().addJdk(jdk)
}

View File

@@ -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}''

View File

@@ -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
}

View File

@@ -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<EnvironmentService>.getEnvironmentValue(MY_AWESOME_KEY)
* val value : String = service<EnvironmentService>.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?
}

View File

@@ -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)
}

View File

@@ -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()
}

View File

@@ -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
}

View File

@@ -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<EnvironmentKey, String>) {
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 {

View File

@@ -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)

View File

@@ -29,7 +29,7 @@ class HeadlessEnvironmentServiceTest : LightPlatformTestCase() {
private suspend fun getExistingKey(key: EnvironmentKey): String {
val value1 = service<EnvironmentService>().requestEnvironmentValue(key)
val value2 = service<EnvironmentService>().getEnvironmentValueOrNull(key)
val value2 = service<EnvironmentService>().getEnvironmentValue(key)
TestCase.assertEquals(value1!!, value2)
return value1
}
@@ -115,7 +115,7 @@ class HeadlessEnvironmentServiceTest : LightPlatformTestCase() {
fun testAbsentKey() = runTestWithValues(null, null) {
try {
assertNull(service<EnvironmentService>().getEnvironmentValueOrNull(dummyKey))
assertNull(service<EnvironmentService>().getEnvironmentValue(dummyKey))
service<EnvironmentService>().requestEnvironmentValue(dummyKey)
fail("should throw")
} catch (e : HeadlessEnvironmentService.MissingEnvironmentKeyException) {
@@ -135,7 +135,7 @@ class HeadlessEnvironmentServiceTest : LightPlatformTestCase() {
fun testUnknownKey() = runTestWithValues(null, null) {
try {
assertNull(service<EnvironmentService>().getEnvironmentValueOrNull(dummyKey))
assertNull(service<EnvironmentService>().getEnvironmentValue(dummyKey))
service<EnvironmentService>().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 =
"""[

View File

@@ -62,7 +62,7 @@ class GradleCommandLineProjectConfigurator : CommandLineInspectionProjectConfigu
override fun configureProject(project: Project, context: ConfiguratorContext) {
val basePath = project.basePath ?: return
val service = service<EnvironmentService>()
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

View File

@@ -71,7 +71,7 @@ class MavenCommandLineInspectionProjectConfigurator : CommandLineInspectionProje
if (FileUtil.findFirstThatExist(pomXmlFile) == null) return
val service = service<EnvironmentService>()
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