From dfc96d216be2361c2e08be207c8df4de7a10376e Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Wed, 1 Jun 2016 12:25:53 +0200 Subject: [PATCH] Refactor configs loading, Add config script definitions reloading on config files changes --- .../KotlinConfigurableScriptDefinition.kt | 21 +++++++------- .../script/KotlinScriptDefinitionProvider.kt | 21 ++++++++++---- .../KotlinScriptConfigurationManager.kt | 29 ++++++++++++++----- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinConfigurableScriptDefinition.kt b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinConfigurableScriptDefinition.kt index ea7e9fe170c..6520430770b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinConfigurableScriptDefinition.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinConfigurableScriptDefinition.kt @@ -40,23 +40,21 @@ import java.util.* val SCRIPT_CONFIG_FILE_EXTENSION = ".ktscfg.xml" -fun loadScriptDefinitionsFromDirectoryWithConfigs(dir: File, kotlinEnvVars: Map>? = null): List = - dir.walk().filter { it.isFile && it.name.endsWith(SCRIPT_CONFIG_FILE_EXTENSION, ignoreCase = true) }.toList() - .flatMap { loadScriptDefinitionsFromConfig(it, kotlinEnvVars) } +fun isScriptDefinitionConfigFile(it: File) = it.isFile && it.name.endsWith(SCRIPT_CONFIG_FILE_EXTENSION, ignoreCase = true) -fun loadScriptDefinitionsFromConfig(configFile: File, kotlinEnvVars: Map>? = null): List = +fun loadScriptConfigsFromProjectRoot(projectRoot: File): List = + projectRoot.walk().filter ( ::isScriptDefinitionConfigFile ).toList() + .flatMap { loadScriptConfigs(it) } + +fun loadScriptConfigs(configFile: File): List = JDOMUtil.loadDocument(configFile).rootElement.children.mapNotNull { - XmlSerializer.deserialize(it, KotlinScriptConfig::class.java)?.let { - KotlinConfigurableScriptDefinition(it, kotlinEnvVars) - } + XmlSerializer.deserialize(it, KotlinScriptConfig::class.java) } @Suppress("unused") // Used externally -fun loadScriptDefinitionsFromConfig(configStream: InputStream, kotlinEnvVars: Map>? = null): List = +fun loadScriptConfigs(configStream: InputStream): List = JDOMUtil.loadDocument(configStream).rootElement.children.mapNotNull { - XmlSerializer.deserialize(it, KotlinScriptConfig::class.java)?.let { - KotlinConfigurableScriptDefinition(it, kotlinEnvVars) - } + XmlSerializer.deserialize(it, KotlinScriptConfig::class.java) } @Suppress("unused") @@ -135,3 +133,4 @@ class KotlinConfigurableScriptDefinition(val config: KotlinScriptConfig, val env } } } + diff --git a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.kt b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.kt index 724b4b084e7..cd84ad6fd7d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.kt @@ -19,25 +19,34 @@ package org.jetbrains.kotlin.script import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile -import java.util.* +import kotlin.concurrent.read +import kotlin.concurrent.write class KotlinScriptDefinitionProvider { private val definitions: MutableList = arrayListOf(StandardScriptDefinition) + private val definitionsLock = java.util.concurrent.locks.ReentrantReadWriteLock() + var scriptDefinitions: List - get() = definitions + get() = definitionsLock.read { definitions } // TODO: remove as unsafe with locking, replace with particular data extractors set(definitions: List) { - this.definitions.clear() - this.definitions.addAll(definitions) + definitionsLock.write { + this.definitions.clear() + this.definitions.addAll(definitions) + } } - fun findScriptDefinition(file: VirtualFile?): KotlinScriptDefinition? = file?.let { file -> definitions.firstOrNull { it.isScript(file) } } + fun findScriptDefinition(file: VirtualFile?): KotlinScriptDefinition? = definitionsLock.read { + file?.let { file -> definitions.firstOrNull { it.isScript(file) } } + } fun isScript(file: VirtualFile?): Boolean = findScriptDefinition(file) != null fun addScriptDefinition(scriptDefinition: KotlinScriptDefinition) { - definitions.add(0, scriptDefinition) + definitionsLock.write { + definitions.add(0, scriptDefinition) + } } fun removeScriptDefinition(scriptDefinition: KotlinScriptDefinition) { diff --git a/idea/src/org/jetbrains/kotlin/idea/script/KotlinScriptConfigurationManager.kt b/idea/src/org/jetbrains/kotlin/idea/script/KotlinScriptConfigurationManager.kt index 7d9f1397bac..ec5db832742 100644 --- a/idea/src/org/jetbrains/kotlin/idea/script/KotlinScriptConfigurationManager.kt +++ b/idea/src/org/jetbrains/kotlin/idea/script/KotlinScriptConfigurationManager.kt @@ -18,15 +18,17 @@ package org.jetbrains.kotlin.idea.script import com.intellij.openapi.components.AbstractProjectComponent import com.intellij.openapi.project.Project -import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider -import org.jetbrains.kotlin.script.StandardScriptDefinition -import org.jetbrains.kotlin.script.loadScriptDefinitionsFromDirectoryWithConfigs +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.openapi.vfs.newvfs.BulkFileListener +import com.intellij.openapi.vfs.newvfs.events.VFileEvent +import org.jetbrains.kotlin.script.* import org.jetbrains.kotlin.utils.PathUtil import java.io.File -class KotlinScriptConfigurationManager(project: Project, scriptDefinitionProvider: KotlinScriptDefinitionProvider) : AbstractProjectComponent(project) { +@Suppress("unused") // project component +class KotlinScriptConfigurationManager(private val project: Project, private val scriptDefinitionProvider: KotlinScriptDefinitionProvider) : AbstractProjectComponent(project) { - val kotlinEnvVars: Map> by lazy { + private val kotlinEnvVars: Map> by lazy { val paths = PathUtil.getKotlinPathsForIdeaPlugin() mapOf("kotlin-runtime" to listOf(paths.runtimePath.canonicalPath), "project-root" to listOf(project.basePath ?: "."), @@ -34,9 +36,22 @@ class KotlinScriptConfigurationManager(project: Project, scriptDefinitionProvide } init { - loadScriptDefinitionsFromDirectoryWithConfigs(File(project.basePath ?: "."), kotlinEnvVars).let { + reloadScriptDefinitions() + val conn = project.messageBus.connect() + conn.subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener.Adapter() { + override fun after(events: List) { + events.firstOrNull { it is VFileEvent && isScriptDefinitionConfigFile(File(it.path)) }?.let { + reloadScriptDefinitions() + } + } + }) + } + + private fun reloadScriptDefinitions() { + loadScriptConfigsFromProjectRoot(File(project.basePath ?: ".")).let { if (it.isNotEmpty()) { - scriptDefinitionProvider.scriptDefinitions = it + StandardScriptDefinition + scriptDefinitionProvider.scriptDefinitions = + it.map { KotlinConfigurableScriptDefinition(it, kotlinEnvVars) } + StandardScriptDefinition } } }