Files
openide/plugins/configuration-script/src/com/intellij/configurationScript/runConfigurationTemplateProvider.kt
T

66 lines
3.0 KiB
Kotlin

package com.intellij.configurationScript
import com.intellij.execution.configurations.ConfigurationFactory
import com.intellij.execution.configurations.RunConfigurationBase
import com.intellij.execution.impl.RunConfigurationTemplateProvider
import com.intellij.execution.impl.RunManagerImpl
import com.intellij.execution.impl.RunnerAndConfigurationSettingsImpl
import com.intellij.openapi.components.BaseState
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import gnu.trove.THashMap
import org.yaml.snakeyaml.nodes.MappingNode
import org.yaml.snakeyaml.nodes.ScalarNode
private class FactoryEntry(state: Any) {
var state: Any? = state
var settings: RunnerAndConfigurationSettingsImpl? = null
}
private class MyRunConfigurationTemplateProvider(private val project: Project) : RunConfigurationTemplateProvider {
private val map = SynchronizedClearableLazy<Map<ConfigurationFactory, FactoryEntry>> {
val node = project.service<ConfigurationFileManager>().getConfigurationNode() ?: return@SynchronizedClearableLazy emptyMap()
val map = THashMap<ConfigurationFactory, FactoryEntry>()
readRunConfigurations(node, isTemplatesOnly = true) { factory, state ->
map.put(factory, FactoryEntry(state))
}
map
}
init {
project.service<ConfigurationFileManager>().registerClearableLazyValue(map)
}
override fun getRunConfigurationTemplate(factory: ConfigurationFactory, runManager: RunManagerImpl): RunnerAndConfigurationSettingsImpl? {
val item = map.value.get(factory) ?: return null
synchronized(item) {
var settings = item.settings
if (settings != null) {
return settings
}
val configuration = factory.createTemplateConfiguration(runManager.project, runManager)
// see readRunConfiguration about how do we set isAllowRunningInParallel
(configuration as RunConfigurationBase).setState(item.state as BaseState)
settings = RunnerAndConfigurationSettingsImpl(runManager, configuration, isTemplate = true)
item.state = null
item.settings = settings
return settings
}
}
}
// we cannot use the same approach as we generate JSON scheme because we should load option classes only in a lazy manner
// that's why we don't use snakeyaml TypeDescription approach to load
internal fun readRunConfigurations(rootNode: MappingNode, isTemplatesOnly: Boolean, processor: (factory: ConfigurationFactory, state: Any) -> Unit) {
// later we can avoid full node graph building, but for now just use simple implementation (problem is that Yaml supports references and merge - proper support of it can be tricky)
// "load" under the hood uses "compose" - i.e. Yaml itself doesn't use stream API to build object model.
val dataReader = RunConfigurationListReader(processor)
for (tuple in rootNode.value) {
val keyNode = tuple.keyNode
if (keyNode is ScalarNode && keyNode.value == Keys.runConfigurations) {
val valueNode = tuple.valueNode as? MappingNode ?: continue
dataReader.read(valueNode, isTemplatesOnly)
}
}
}