Refactor GradleScriptingSupport to support multiple gradle projects linked to one idea project
For each externalGradleProject separate GradleScriptingSupport is created. This allows to save script configurations by gradle projects ^KT-37806 Obsolete ^KT-35151 Fixed
This commit is contained in:
committed by
Natalia Selezneva
parent
34df3066b3
commit
73760effee
+3
-6
@@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.JavaSdkType
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.ProjectRootManager
|
||||
import com.intellij.openapi.roots.ProjectRootManager.*
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
@@ -30,7 +31,6 @@ import com.intellij.util.io.URLUtil
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.idea.caches.project.getAllProjectSdks
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.CompositeScriptConfigurationManager
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.DefaultScriptingSupport
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptConfigurationUpdater
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.loader.ScriptConfigurationLoader
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
@@ -38,7 +38,6 @@ import org.jetbrains.kotlin.psi.UserDataProperty
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.api.asSuccess
|
||||
import kotlin.script.experimental.api.makeFailureResult
|
||||
@@ -162,15 +161,13 @@ interface ScriptConfigurationManager {
|
||||
@TestOnly
|
||||
fun updateScriptDependenciesSynchronously(file: PsiFile) {
|
||||
// TODO: review the usages of this method
|
||||
(getInstance(file.project) as CompositeScriptConfigurationManager).managers
|
||||
.firstIsInstance<DefaultScriptingSupport>()
|
||||
(getInstance(file.project) as CompositeScriptConfigurationManager).default
|
||||
.updateScriptDependenciesSynchronously(file)
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun clearCaches(project: Project) {
|
||||
(getInstance(project) as CompositeScriptConfigurationManager).managers
|
||||
.firstIsInstance<DefaultScriptingSupport>()
|
||||
(getInstance(project) as CompositeScriptConfigurationManager).default
|
||||
.clearCaches()
|
||||
}
|
||||
|
||||
|
||||
+18
-7
@@ -22,16 +22,25 @@ import org.jetbrains.kotlin.idea.core.script.configuration.utils.getKtFile
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.isNonScript
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
|
||||
class CompositeScriptConfigurationManager(val project: Project) : ScriptConfigurationManager {
|
||||
@Suppress("unused")
|
||||
private val notifier = ScriptChangesNotifier(project, updater)
|
||||
|
||||
// todo public for tests
|
||||
val managers = ScriptingSupport.SCRIPTING_SUPPORT.getPoint(project).extensionList
|
||||
private val providers = ScriptingSupport.Provider.EPN.getPoint(project).extensionList
|
||||
|
||||
val default = DefaultScriptingSupport(project)
|
||||
|
||||
private val managers
|
||||
get() = mutableListOf<ScriptingSupport>().also { managers ->
|
||||
managers.add(default)
|
||||
providers.forEach { managers.addAll(it.all) }
|
||||
}
|
||||
|
||||
private fun getRelatedManager(file: VirtualFile): ScriptingSupport =
|
||||
providers.firstNotNullResult { it.getSupport(file) } ?: default
|
||||
|
||||
private fun getRelatedManager(file: VirtualFile): ScriptingSupport = managers.first { it.isRelated(file) }
|
||||
private fun getRelatedManager(file: KtFile): ScriptingSupport =
|
||||
getRelatedManager(file.originalFile.virtualFile)
|
||||
|
||||
@@ -76,14 +85,16 @@ class CompositeScriptConfigurationManager(val project: Project) : ScriptConfigur
|
||||
* Loads script configuration if classpath roots don't contain [file] yet
|
||||
*/
|
||||
private fun getActualClasspathRoots(file: VirtualFile): ScriptClassRootsCache {
|
||||
val classpathRoots = getRelatedManager(file).classpathRoots
|
||||
val manager = getRelatedManager(file)
|
||||
|
||||
val classpathRoots = manager.classpathRoots
|
||||
if (classpathRoots.contains(file)) {
|
||||
return classpathRoots
|
||||
}
|
||||
|
||||
getOrLoadConfiguration(file)
|
||||
|
||||
return getRelatedManager(file).classpathRoots
|
||||
return manager.classpathRoots
|
||||
}
|
||||
|
||||
override fun getScriptSdk(file: VirtualFile): Sdk? =
|
||||
@@ -115,7 +126,7 @@ class CompositeScriptConfigurationManager(val project: Project) : ScriptConfigur
|
||||
override fun forceReloadConfiguration(file: VirtualFile, loader: ScriptConfigurationLoader): ScriptCompilationConfigurationWrapper? {
|
||||
val ktFile = project.getKtFile(file, null) ?: return null
|
||||
|
||||
return managers.firstIsInstanceOrNull<DefaultScriptingSupport>()?.forceReloadConfiguration(ktFile, loader)
|
||||
return default.forceReloadConfiguration(ktFile, loader)
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
+1
-5
@@ -33,7 +33,6 @@ import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||
@@ -312,8 +311,6 @@ abstract class DefaultScriptingSupportBase(val project: Project) : ScriptingSupp
|
||||
fun getAppliedConfiguration(file: VirtualFile?): ScriptConfigurationSnapshot? =
|
||||
getCachedConfigurationState(file)?.applied
|
||||
|
||||
override fun isRelated(file: VirtualFile): Boolean = true
|
||||
|
||||
override fun hasCachedConfiguration(file: KtFile): Boolean =
|
||||
getAppliedConfiguration(file.originalFile.virtualFile) != null
|
||||
|
||||
@@ -438,6 +435,5 @@ object DefaultScriptConfigurationManagerExtensions {
|
||||
}
|
||||
|
||||
val ScriptConfigurationManager.testingBackgroundExecutor
|
||||
get() = (this as CompositeScriptConfigurationManager).managers
|
||||
.firstIsInstance<DefaultScriptingSupport>()
|
||||
get() = (this as CompositeScriptConfigurationManager).default
|
||||
.backgroundExecutor as TestingBackgroundExecutor
|
||||
+10
-6
@@ -21,7 +21,16 @@ import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
abstract class ScriptingSupport {
|
||||
abstract fun isRelated(file: VirtualFile): Boolean
|
||||
abstract class Provider {
|
||||
abstract val all: Collection<ScriptingSupport>
|
||||
|
||||
abstract fun getSupport(file: VirtualFile): ScriptingSupport?
|
||||
|
||||
companion object {
|
||||
val EPN: ExtensionPointName<Provider> =
|
||||
ExtensionPointName.create("org.jetbrains.kotlin.scripting.idea.scriptingSupportProvider")
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun clearCaches()
|
||||
abstract fun hasCachedConfiguration(file: KtFile): Boolean
|
||||
@@ -68,9 +77,4 @@ abstract class ScriptingSupport {
|
||||
|
||||
ScriptDependenciesModificationTracker.getInstance(project).incModificationCount()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val SCRIPTING_SUPPORT: ExtensionPointName<ScriptingSupport> =
|
||||
ExtensionPointName.create("org.jetbrains.kotlin.scripting.idea.scriptingSupport")
|
||||
}
|
||||
}
|
||||
+11
-22
@@ -16,15 +16,16 @@ import java.io.File
|
||||
internal class DefaultClassRootsCache(
|
||||
project: Project,
|
||||
private val all: Map<VirtualFile, ScriptCompilationConfigurationWrapper>
|
||||
) : ScriptClassRootsCache(project, extractRoots(all, project)) {
|
||||
) : ScriptClassRootsCache(
|
||||
project,
|
||||
ScriptClassRootsStorage.Companion.Key("default"),
|
||||
extractRoots(all, project)
|
||||
) {
|
||||
|
||||
override val fileToConfiguration: (VirtualFile) -> ScriptCompilationConfigurationWrapper?
|
||||
get() = { all[it] }
|
||||
override fun getConfiguration(file: VirtualFile) = all[file]
|
||||
|
||||
override fun contains(file: VirtualFile): Boolean = file in all
|
||||
|
||||
override val rootsCacheKey = ScriptClassRootsStorage.Companion.Key("default")
|
||||
|
||||
private val scriptsSdksCache: Map<VirtualFile, Sdk?> =
|
||||
ConcurrentFactoryMap.createWeakMap { file ->
|
||||
return@createWeakMap getScriptSdk(
|
||||
@@ -44,30 +45,18 @@ internal class DefaultClassRootsCache(
|
||||
project: Project,
|
||||
configuration: ScriptCompilationConfigurationWrapper
|
||||
): ScriptClassRootsStorage.Companion.ScriptClassRoots {
|
||||
val scriptSdk =
|
||||
getScriptSdkOfDefault(
|
||||
configuration.javaHome,
|
||||
project
|
||||
)
|
||||
val scriptSdk = getScriptSdk(configuration.javaHome)
|
||||
if (scriptSdk != null && !scriptSdk.isAlreadyIndexed(project)) {
|
||||
return ScriptClassRootsStorage.Companion.ScriptClassRoots(
|
||||
toStringValues(
|
||||
configuration.dependenciesClassPath
|
||||
),
|
||||
toStringValues(
|
||||
configuration.dependenciesSources
|
||||
),
|
||||
toStringValues(configuration.dependenciesClassPath),
|
||||
toStringValues(configuration.dependenciesSources),
|
||||
setOf(scriptSdk)
|
||||
)
|
||||
}
|
||||
|
||||
return ScriptClassRootsStorage.Companion.ScriptClassRoots(
|
||||
toStringValues(
|
||||
configuration.dependenciesClassPath
|
||||
),
|
||||
toStringValues(
|
||||
configuration.dependenciesSources
|
||||
),
|
||||
toStringValues(configuration.dependenciesClassPath),
|
||||
toStringValues(configuration.dependenciesSources),
|
||||
emptySet()
|
||||
)
|
||||
}
|
||||
|
||||
+22
-6
@@ -18,15 +18,13 @@ import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
|
||||
import org.jetbrains.kotlin.idea.util.getProjectJdkTableSafe
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import java.io.File
|
||||
import java.nio.file.FileSystems
|
||||
|
||||
abstract class ScriptClassRootsCache(
|
||||
private val project: Project,
|
||||
private val rootsCacheKey: ScriptClassRootsStorage.Companion.Key,
|
||||
private val roots: ScriptClassRootsStorage.Companion.ScriptClassRoots
|
||||
) {
|
||||
protected abstract val fileToConfiguration: (VirtualFile) -> ScriptCompilationConfigurationWrapper?
|
||||
|
||||
protected abstract val rootsCacheKey: ScriptClassRootsStorage.Companion.Key
|
||||
protected abstract fun getConfiguration(file: VirtualFile): ScriptCompilationConfigurationWrapper?
|
||||
|
||||
abstract val firstScriptSdk: Sdk?
|
||||
|
||||
@@ -34,7 +32,7 @@ abstract class ScriptClassRootsCache(
|
||||
|
||||
abstract fun contains(file: VirtualFile): Boolean
|
||||
|
||||
class Fat(
|
||||
private class Fat(
|
||||
val scriptConfiguration: ScriptCompilationConfigurationWrapper,
|
||||
val classFilesScope: GlobalSearchScope
|
||||
)
|
||||
@@ -57,7 +55,7 @@ abstract class ScriptClassRootsCache(
|
||||
|
||||
private val scriptsDependenciesCache: MutableMap<VirtualFile, Fat> =
|
||||
ConcurrentFactoryMap.createWeakMap { file ->
|
||||
val configuration = fileToConfiguration(file) ?: return@createWeakMap null
|
||||
val configuration = getConfiguration(file) ?: return@createWeakMap null
|
||||
|
||||
val roots = configuration.dependenciesClassPath
|
||||
val sdk = getScriptSdk(file)
|
||||
@@ -120,6 +118,24 @@ abstract class ScriptClassRootsCache(
|
||||
fun Sdk.isAlreadyIndexed(project: Project): Boolean {
|
||||
return ModuleManager.getInstance(project).modules.any { ModuleRootManager.getInstance(it).sdk == this }
|
||||
}
|
||||
|
||||
fun empty(project: Project) = object : ScriptClassRootsCache(
|
||||
project,
|
||||
ScriptClassRootsStorage.Companion.Key("empty"),
|
||||
ScriptClassRootsStorage.Companion.ScriptClassRoots(
|
||||
setOf(),
|
||||
setOf(),
|
||||
setOf()
|
||||
)
|
||||
) {
|
||||
override fun getConfiguration(file: VirtualFile): ScriptCompilationConfigurationWrapper? = null
|
||||
|
||||
override val firstScriptSdk: Sdk? = null
|
||||
|
||||
override fun getScriptSdk(file: VirtualFile): Sdk? = null
|
||||
|
||||
override fun contains(file: VirtualFile): Boolean = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+44
-10
@@ -7,31 +7,65 @@ package org.jetbrains.kotlin.idea.scripting.gradle
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsCache
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsStorage
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsStorage.Companion.ScriptClassRoots
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModel
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
|
||||
import org.jetbrains.kotlin.scripting.resolve.adjustByDefinition
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.jvm.JvmDependency
|
||||
import kotlin.script.experimental.jvm.jdkHome
|
||||
import kotlin.script.experimental.jvm.jvm
|
||||
|
||||
internal class GradleClassRootsCache(
|
||||
project: Project,
|
||||
configuration: Configuration?,
|
||||
override val fileToConfiguration: (VirtualFile) -> ScriptCompilationConfigurationWrapper?
|
||||
) : ScriptClassRootsCache(project, extractRoots(configuration)) {
|
||||
class GradleClassRootsCache(
|
||||
val project: Project,
|
||||
val context: GradleKtsContext,
|
||||
val configuration: Configuration
|
||||
) : ScriptClassRootsCache(
|
||||
project,
|
||||
ScriptClassRootsStorage.Companion.Key("gradle"),
|
||||
extractRoots(context, configuration, project)
|
||||
) {
|
||||
override fun getConfiguration(file: VirtualFile): ScriptCompilationConfigurationWrapper? =
|
||||
configuration.scriptModel(file)?.toScriptConfiguration()
|
||||
|
||||
override val rootsCacheKey = ScriptClassRootsStorage.Companion.Key("gradle")
|
||||
private fun KotlinDslScriptModel.toScriptConfiguration(): ScriptCompilationConfigurationWrapper? {
|
||||
val scriptFile = File(file)
|
||||
val virtualFile = VfsUtil.findFile(scriptFile.toPath(), true)!!
|
||||
|
||||
val definition = virtualFile.findScriptDefinition(project) ?: return null
|
||||
|
||||
return ScriptCompilationConfigurationWrapper.FromCompilationConfiguration(
|
||||
VirtualFileScriptSource(virtualFile),
|
||||
definition.compilationConfiguration.with {
|
||||
if (context.javaHome != null) {
|
||||
jvm.jdkHome(context.javaHome)
|
||||
}
|
||||
defaultImports(imports)
|
||||
dependencies(JvmDependency(classPath.map { File(it) }))
|
||||
ide.dependenciesSources(JvmDependency(sourcePath.map { File(it) }))
|
||||
}.adjustByDefinition(definition)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getScriptSdk(file: VirtualFile): Sdk? {
|
||||
return firstScriptSdk
|
||||
}
|
||||
|
||||
override val firstScriptSdk: Sdk? = configuration?.let { getScriptSdk(configuration.context.javaHome) }
|
||||
override val firstScriptSdk: Sdk? = configuration?.let { getScriptSdk(context.javaHome) }
|
||||
|
||||
// TODO what should we do if no configuration is loaded yet
|
||||
// called to ensure that configuration for file is loaded
|
||||
// as we cannot force loading, we always return true
|
||||
override fun contains(file: VirtualFile): Boolean = true
|
||||
|
||||
companion object {
|
||||
fun extractRoots(configuration: Configuration?): ScriptClassRoots {
|
||||
fun extractRoots(context: GradleKtsContext, configuration: Configuration?, project: Project): ScriptClassRoots {
|
||||
if (configuration == null) {
|
||||
return ScriptClassRootsStorage.EMPTY
|
||||
}
|
||||
@@ -46,7 +80,7 @@ internal class GradleClassRootsCache(
|
||||
return ScriptClassRoots(
|
||||
configuration.classFilePath,
|
||||
configuration.sourcePath,
|
||||
getScriptSdk(configuration.context.javaHome)?.let { setOf(it) } ?: setOf()
|
||||
getScriptSdk(context.javaHome)?.let { setOf(it) } ?: setOf()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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.scripting.gradle
|
||||
|
||||
import java.io.File
|
||||
|
||||
class GradleKtsContext(val javaHome: File?)
|
||||
+25
-96
@@ -5,42 +5,37 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.scripting.gradle
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.ScriptingSupport
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.ScriptingSupportHelper
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptConfigurationUpdater
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsCache
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsIndexer
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.GradleKtsContext
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.createGradleKtsContextIfPossible
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModel
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.toScriptConfiguration
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import java.nio.file.Paths
|
||||
|
||||
|
||||
internal data class ConfigurationData(
|
||||
data class ConfigurationData(
|
||||
val templateClasspath: List<String>,
|
||||
val models: List<KotlinDslScriptModel>
|
||||
)
|
||||
|
||||
internal class Configuration(val context: GradleKtsContext, data: ConfigurationData) {
|
||||
val scripts = data.models.associateBy { it.file }
|
||||
val sourcePath = data.models.flatMapTo(mutableSetOf()) { it.sourcePath }
|
||||
class Configuration(val data: ConfigurationData) {
|
||||
private val scripts: Map<String, KotlinDslScriptModel>
|
||||
|
||||
val sourcePath: MutableSet<String>
|
||||
val classFilePath: MutableSet<String> = mutableSetOf()
|
||||
|
||||
init {
|
||||
val allModels = data.models
|
||||
|
||||
scripts = allModels.associateBy { it.file }
|
||||
sourcePath = allModels.flatMapTo(mutableSetOf()) { it.sourcePath }
|
||||
|
||||
classFilePath.addAll(data.templateClasspath)
|
||||
data.models.flatMapTo(classFilePath) { it.classPath }
|
||||
allModels.flatMapTo(classFilePath) { it.classPath }
|
||||
}
|
||||
|
||||
fun scriptModel(file: VirtualFile): KotlinDslScriptModel? {
|
||||
@@ -48,74 +43,33 @@ internal class Configuration(val context: GradleKtsContext, data: ConfigurationD
|
||||
}
|
||||
}
|
||||
|
||||
class GradleScriptingSupport(val project: Project) : ScriptingSupport() {
|
||||
@Volatile
|
||||
private var configuration: Configuration? = null
|
||||
|
||||
private val rootsIndexer = ScriptClassRootsIndexer(project)
|
||||
class GradleScriptingSupport(
|
||||
private val rootsIndexer: ScriptClassRootsIndexer,
|
||||
val project: Project,
|
||||
val buildRoot: VirtualFile,
|
||||
val context: GradleKtsContext,
|
||||
val configuration: Configuration
|
||||
) : ScriptingSupport() {
|
||||
|
||||
init {
|
||||
if (isKotlinDslScriptsModelImportSupported(project)) {
|
||||
ApplicationManager.getApplication().executeOnPooledThread {
|
||||
val data = KotlinDslScriptModels.read(project)
|
||||
val gradleKtsContext = createGradleKtsContextIfPossible(project)
|
||||
if (data != null && gradleKtsContext != null) {
|
||||
val newConfiguration = Configuration(
|
||||
gradleKtsContext,
|
||||
data
|
||||
)
|
||||
|
||||
configuration = newConfiguration
|
||||
configurationChangedCallback(newConfiguration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun recreateRootsCache(): ScriptClassRootsCache {
|
||||
return GradleClassRootsCache(project, configuration) {
|
||||
configuration?.let { conf ->
|
||||
val model = conf.scriptModel(it)
|
||||
model?.toScriptConfiguration(conf.context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun replace(context: GradleKtsContext, models: List<KotlinDslScriptModel>) {
|
||||
if (models.isEmpty()) return
|
||||
|
||||
val anyScript = VfsUtil.findFile(Paths.get(models.first().file), true)!!
|
||||
|
||||
val definition = anyScript.findScriptDefinition(context.project) ?: return
|
||||
val templateClasspath = definition.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()
|
||||
?.templateClasspath?.map { it.path } ?: return
|
||||
|
||||
val data = ConfigurationData(templateClasspath, models)
|
||||
KotlinDslScriptModels.write(project, data)
|
||||
|
||||
val newConfiguration = Configuration(context, data)
|
||||
configuration = newConfiguration
|
||||
|
||||
configurationChangedCallback(newConfiguration)
|
||||
}
|
||||
|
||||
private fun configurationChangedCallback(newConfiguration: Configuration) {
|
||||
rootsIndexer.transaction {
|
||||
if (classpathRoots.hasNotCachedRoots(GradleClassRootsCache.extractRoots(newConfiguration))) {
|
||||
if (classpathRoots.hasNotCachedRoots(GradleClassRootsCache.extractRoots(context, configuration, project))) {
|
||||
rootsIndexer.markNewRoot()
|
||||
}
|
||||
|
||||
clearClassRootsCaches(project)
|
||||
|
||||
ScriptingSupportHelper.updateHighlighting(project) {
|
||||
configuration?.scriptModel(it) != null
|
||||
configuration.scriptModel(it) != null
|
||||
}
|
||||
}
|
||||
|
||||
hideNotificationForProjectImport(project)
|
||||
}
|
||||
|
||||
fun updateNotification(file: KtFile) {
|
||||
override fun recreateRootsCache() = GradleClassRootsCache(project, context, configuration)
|
||||
|
||||
private fun updateNotification(file: KtFile) {
|
||||
val vFile = file.originalFile.virtualFile
|
||||
val scriptModel = configuration?.scriptModel(vFile) ?: return
|
||||
|
||||
@@ -126,34 +80,15 @@ class GradleScriptingSupport(val project: Project) : ScriptingSupport() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun isRelated(file: VirtualFile): Boolean {
|
||||
if (isGradleKotlinScript(file)) {
|
||||
return isKotlinDslScriptsModelImportSupported(project)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun isKotlinDslScriptsModelImportSupported(project: Project): Boolean {
|
||||
val gradleVersion = getGradleVersion(project)
|
||||
return gradleVersion != null && kotlinDslScriptsModelImportSupported(gradleVersion)
|
||||
}
|
||||
|
||||
override fun clearCaches() {
|
||||
// todo should clear up to date
|
||||
// todo: should clear up to date
|
||||
}
|
||||
|
||||
override fun hasCachedConfiguration(file: KtFile): Boolean =
|
||||
configuration?.scriptModel(file.originalFile.virtualFile) != null
|
||||
configuration.scriptModel(file.originalFile.virtualFile) != null
|
||||
|
||||
override fun getOrLoadConfiguration(virtualFile: VirtualFile, preloadedKtFile: KtFile?): ScriptCompilationConfigurationWrapper? {
|
||||
val configuration = configuration
|
||||
if (configuration == null) {
|
||||
// todo: show notification "Import gradle project"
|
||||
return null
|
||||
} else {
|
||||
return classpathRoots.getScriptConfiguration(virtualFile)
|
||||
}
|
||||
return classpathRoots.getScriptConfiguration(virtualFile)
|
||||
}
|
||||
|
||||
override val updater: ScriptConfigurationUpdater
|
||||
@@ -169,10 +104,4 @@ class GradleScriptingSupport(val project: Project) : ScriptingSupport() {
|
||||
updateNotification(file)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): GradleScriptingSupport {
|
||||
return SCRIPTING_SUPPORT.getPoint(project).extensionList.firstIsInstance()
|
||||
}
|
||||
}
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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.scripting.gradle
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.ScriptingSupport
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.listener.ScriptConfigurationUpdater
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsCache
|
||||
import org.jetbrains.kotlin.idea.core.script.configuration.utils.ScriptClassRootsIndexer
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslGradleBuildSync
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
import org.jetbrains.plugins.gradle.config.GradleSettingsListenerAdapter
|
||||
import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings
|
||||
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings
|
||||
import org.jetbrains.plugins.gradle.settings.GradleSettingsListener
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
|
||||
/**
|
||||
* Creates [GradleScriptingSupport] per each linked Gradle build.
|
||||
* Gradle project sync should call [update].
|
||||
*/
|
||||
class GradleScriptingSupportProvider(val project: Project) : ScriptingSupport.Provider() {
|
||||
private val rootsIndexer = ScriptClassRootsIndexer(project)
|
||||
|
||||
val roots = RootsIndex<GradleScriptingSupport>()
|
||||
override val all get() = roots.values
|
||||
|
||||
private val VirtualFile.localPath
|
||||
get() = path
|
||||
|
||||
private fun findRoot(file: VirtualFile) = roots.findRoot(file.localPath)
|
||||
|
||||
override fun getSupport(file: VirtualFile): ScriptingSupport? {
|
||||
if (isGradleKotlinScript(file)) {
|
||||
findRoot(file)?.let { return it }
|
||||
|
||||
val externalProjectSettings = findExternalProjectSettings(file) ?: return null
|
||||
if (kotlinDslScriptsModelImportSupported(getGradleVersion(project, externalProjectSettings))) {
|
||||
return unlinkedFilesSupport
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun findExternalProjectSettings(file: VirtualFile): GradleProjectSettings? {
|
||||
return getGradleProjectSettings(project)
|
||||
.filter { file.localPath.startsWith(it.externalProjectPath) }
|
||||
.maxBy { it.externalProjectPath.length }
|
||||
}
|
||||
|
||||
init {
|
||||
ApplicationManager.getApplication().executeOnPooledThread {
|
||||
roots.update { set ->
|
||||
getGradleProjectSettings(project).forEach { gradleProjectSettings ->
|
||||
if (kotlinDslScriptsModelImportSupported(getGradleVersion(project, gradleProjectSettings))) {
|
||||
val support = createSupport(gradleProjectSettings.externalProjectPath) {
|
||||
KotlinDslScriptModels.read(it)
|
||||
}
|
||||
|
||||
if (support != null) {
|
||||
set(support.buildRoot.localPath, support)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// subscribe to gradle build unlink
|
||||
val listener = object : GradleSettingsListenerAdapter() {
|
||||
override fun onProjectsUnlinked(linkedProjectPaths: MutableSet<String>) {
|
||||
linkedProjectPaths.forEach {
|
||||
val buildRoot = VfsUtil.findFile(Paths.get(it), false)
|
||||
if (buildRoot != null) {
|
||||
if (roots.remove(buildRoot.localPath) != null) {
|
||||
KotlinDslScriptModels.remove(buildRoot)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project.messageBus.connect(project).subscribe(GradleSettingsListener.TOPIC, listener)
|
||||
}
|
||||
|
||||
fun update(build: KotlinDslGradleBuildSync) {
|
||||
// fast path for linked gradle builds without .gradle.kts support
|
||||
if (build.models.isEmpty()) {
|
||||
val root = roots.findRoot(build.workingDir) ?: return
|
||||
if (root.configuration.data.models.isEmpty()) return
|
||||
}
|
||||
|
||||
val templateClasspath = findTemplateClasspath(build) ?: return
|
||||
val data = ConfigurationData(templateClasspath, build.models)
|
||||
val newSupport = createSupport(build.workingDir) { data } ?: return
|
||||
KotlinDslScriptModels.write(newSupport.buildRoot, data)
|
||||
|
||||
roots[newSupport.buildRoot.localPath] = newSupport
|
||||
}
|
||||
|
||||
private fun createSupport(
|
||||
externalProjectPath: String,
|
||||
dataProvider: (buildRoot: VirtualFile) -> ConfigurationData?
|
||||
): GradleScriptingSupport? {
|
||||
val gradleExeSettings =
|
||||
ExternalSystemApiUtil.getExecutionSettings<GradleExecutionSettings>(
|
||||
project,
|
||||
externalProjectPath,
|
||||
GradleConstants.SYSTEM_ID
|
||||
)
|
||||
|
||||
val buildRoot = VfsUtil.findFile(Paths.get(externalProjectPath), true) ?: return null
|
||||
val data = dataProvider(buildRoot) ?: return null
|
||||
|
||||
return GradleScriptingSupport(
|
||||
rootsIndexer,
|
||||
project,
|
||||
buildRoot,
|
||||
GradleKtsContext(gradleExeSettings.javaHome?.let { File(it) }),
|
||||
Configuration(data)
|
||||
)
|
||||
}
|
||||
|
||||
private fun findTemplateClasspath(build: KotlinDslGradleBuildSync): List<String>? {
|
||||
val anyScript = VfsUtil.findFile(Paths.get(build.models.first().file), true)!!
|
||||
// todo: find definition according to build.workingDir
|
||||
val definition = anyScript.findScriptDefinition(project) ?: return null
|
||||
return definition.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>()
|
||||
?.templateClasspath?.map { it.path }
|
||||
}
|
||||
|
||||
private val unlinkedFilesSupport = object : ScriptingSupport() {
|
||||
override fun clearCaches() {
|
||||
}
|
||||
|
||||
override fun hasCachedConfiguration(file: KtFile): Boolean = false
|
||||
|
||||
override fun getOrLoadConfiguration(
|
||||
virtualFile: VirtualFile,
|
||||
preloadedKtFile: KtFile?
|
||||
): ScriptCompilationConfigurationWrapper? = null
|
||||
|
||||
override val updater: ScriptConfigurationUpdater
|
||||
get() = object : ScriptConfigurationUpdater {
|
||||
override fun ensureUpToDatedConfigurationSuggested(file: KtFile) {
|
||||
}
|
||||
|
||||
override fun ensureConfigurationUpToDate(files: List<KtFile>): Boolean = false
|
||||
|
||||
override fun suggestToUpdateConfigurationIfOutOfDate(file: KtFile) {
|
||||
}
|
||||
}
|
||||
|
||||
override fun recreateRootsCache(): ScriptClassRootsCache = ScriptClassRootsCache.empty(project)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): GradleScriptingSupportProvider =
|
||||
EPN.getPoint(project).extensionList.firstIsInstance()
|
||||
}
|
||||
}
|
||||
+18
-58
@@ -9,29 +9,22 @@ import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import org.gradle.tooling.model.kotlin.dsl.EditorReportSeverity
|
||||
import org.gradle.tooling.model.kotlin.dsl.KotlinDslScriptsModel
|
||||
import org.jetbrains.kotlin.idea.KotlinIdeaGradleBundle
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptInputsWatcher
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptingSupport
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptingSupportProvider
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.getGradleScriptInputsStamp
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.getJavaHomeForGradleProject
|
||||
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
|
||||
import org.jetbrains.kotlin.scripting.resolve.adjustByDefinition
|
||||
import org.jetbrains.plugins.gradle.service.project.ProjectResolverContext
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.jvm.JvmDependency
|
||||
import kotlin.script.experimental.jvm.jdkHome
|
||||
import kotlin.script.experimental.jvm.jvm
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
var Project.kotlinDslModels: ArrayList<KotlinDslScriptModel>
|
||||
by NotNullableUserDataProperty<Project, ArrayList<KotlinDslScriptModel>>(
|
||||
Key("Kotlin DSL Scripts Models"), arrayListOf()
|
||||
var Project.kotlinGradleDslSync: MutableMap<ExternalSystemTaskId, KotlinDslGradleBuildSync>
|
||||
by NotNullableUserDataProperty<Project, MutableMap<ExternalSystemTaskId, KotlinDslGradleBuildSync>>(
|
||||
Key("Kotlin DSL Scripts Models"), ConcurrentHashMap()
|
||||
)
|
||||
|
||||
fun processScriptModel(
|
||||
@@ -44,14 +37,11 @@ fun processScriptModel(
|
||||
"Couldn't get KotlinDslScriptsModel for $projectName:\n${model.message}\n${model.stackTrace}"
|
||||
)
|
||||
} else {
|
||||
val project = resolverCtx.externalSystemTaskId.findProject() ?: return
|
||||
val task = resolverCtx.externalSystemTaskId
|
||||
val project = task.findProject() ?: return
|
||||
val models = model.toListOfScriptModels(project)
|
||||
project.kotlinDslModels.addAll(
|
||||
models
|
||||
)
|
||||
if (models.containsErrors()) {
|
||||
throw IllegalStateException(KotlinIdeaGradleBundle.message("title.kotlin.build.script"))
|
||||
}
|
||||
|
||||
project.kotlinGradleDslSync[task]?.models?.addAll(models)
|
||||
|
||||
if (models.containsErrors()) {
|
||||
throw IllegalStateException(KotlinIdeaGradleBundle.message("title.kotlin.build.script"))
|
||||
@@ -111,52 +101,22 @@ private fun KotlinDslScriptsModel.toListOfScriptModels(project: Project): List<K
|
||||
)
|
||||
}
|
||||
|
||||
fun createGradleKtsContextIfPossible(project: Project): GradleKtsContext? {
|
||||
val javaHome = getJavaHomeForGradleProject(project)?.let { File(it)}
|
||||
|
||||
return GradleKtsContext(project, javaHome)
|
||||
class KotlinDslGradleBuildSync(val workingDir: String, val taskId: ExternalSystemTaskId) {
|
||||
val models = mutableListOf<KotlinDslScriptModel>()
|
||||
}
|
||||
|
||||
class GradleKtsContext(val project: Project, val javaHome: File?)
|
||||
fun saveScriptModels(project: Project, build: KotlinDslGradleBuildSync) {
|
||||
val errorReporter = KotlinGradleDslErrorReporter(project, build.taskId)
|
||||
|
||||
fun KotlinDslScriptModel.toScriptConfiguration(context: GradleKtsContext): ScriptCompilationConfigurationWrapper? {
|
||||
val scriptFile = File(file)
|
||||
val virtualFile = VfsUtil.findFile(scriptFile.toPath(), true)!!
|
||||
|
||||
val definition = virtualFile.findScriptDefinition(context.project) ?: return null
|
||||
|
||||
return ScriptCompilationConfigurationWrapper.FromCompilationConfiguration(
|
||||
VirtualFileScriptSource(virtualFile),
|
||||
definition.compilationConfiguration.with {
|
||||
if (context.javaHome != null) {
|
||||
jvm.jdkHome(context.javaHome)
|
||||
}
|
||||
defaultImports(imports)
|
||||
dependencies(JvmDependency(classPath.map { File(it) }))
|
||||
ide.dependenciesSources(JvmDependency(sourcePath.map { File(it) }))
|
||||
}.adjustByDefinition(definition)
|
||||
)
|
||||
}
|
||||
|
||||
fun saveScriptModels(
|
||||
project: Project,
|
||||
task: ExternalSystemTaskId,
|
||||
javaHomeStr: String?,
|
||||
models: List<KotlinDslScriptModel>
|
||||
) {
|
||||
val errorReporter = KotlinGradleDslErrorReporter(project, task)
|
||||
|
||||
val javaHome = javaHomeStr?.let { File(it) }
|
||||
val context = GradleKtsContext(project, javaHome)
|
||||
|
||||
models.forEach { model ->
|
||||
build.models.forEach { model ->
|
||||
errorReporter.reportError(File(model.file), model)
|
||||
}
|
||||
|
||||
project.service<GradleScriptInputsWatcher>().saveGradleProjectRootsAfterImport(
|
||||
models.map { File(it.file).parent }.toSet()
|
||||
build.models.map { FileUtil.toSystemIndependentName(File(it.file).parent) }.toSet()
|
||||
)
|
||||
|
||||
GradleScriptingSupport.getInstance(project).replace(context, models)
|
||||
GradleScriptingSupportProvider.getInstance(project).update(build)
|
||||
|
||||
project.service<GradleScriptInputsWatcher>().clearState()
|
||||
}
|
||||
+12
-10
@@ -9,23 +9,25 @@ import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListenerAdapter
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType
|
||||
import org.jetbrains.kotlin.idea.framework.GRADLE_SYSTEM_ID
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.getJavaHomeForGradleProject
|
||||
|
||||
class KotlinDslSyncListener : ExternalSystemTaskNotificationListenerAdapter() {
|
||||
override fun onEnd(id: ExternalSystemTaskId) {
|
||||
if (id.type != ExternalSystemTaskType.RESOLVE_PROJECT || id.projectSystemId != GRADLE_SYSTEM_ID) {
|
||||
return
|
||||
}
|
||||
override fun onStart(id: ExternalSystemTaskId, workingDir: String?) {
|
||||
if (id.type != ExternalSystemTaskType.RESOLVE_PROJECT) return
|
||||
if (id.projectSystemId != GRADLE_SYSTEM_ID) return
|
||||
|
||||
if (workingDir == null) return
|
||||
val project = id.findProject() ?: return
|
||||
|
||||
val models: List<KotlinDslScriptModel> = project.kotlinDslModels
|
||||
project.kotlinDslModels = arrayListOf()
|
||||
project.kotlinGradleDslSync[id] = KotlinDslGradleBuildSync(workingDir, id)
|
||||
}
|
||||
|
||||
if (models.isEmpty()) return
|
||||
override fun onEnd(id: ExternalSystemTaskId) {
|
||||
if (id.type != ExternalSystemTaskType.RESOLVE_PROJECT) return
|
||||
if (id.projectSystemId != GRADLE_SYSTEM_ID) return
|
||||
|
||||
val javaHome = getJavaHomeForGradleProject(project)
|
||||
val project = id.findProject() ?: return
|
||||
val sync = project.kotlinGradleDslSync.remove(id) ?: return
|
||||
|
||||
saveScriptModels(project, id, javaHome, models)
|
||||
saveScriptModels(project, sync)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,35 +5,45 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.scripting.gradle
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.newvfs.FileAttribute
|
||||
import org.jetbrains.kotlin.idea.core.util.readNullable
|
||||
import org.jetbrains.kotlin.idea.core.util.readString
|
||||
import org.jetbrains.kotlin.idea.core.util.writeNullable
|
||||
import org.jetbrains.kotlin.idea.core.util.writeString
|
||||
import org.jetbrains.kotlin.idea.scripting.gradle.importing.KotlinDslScriptModel
|
||||
import java.io.DataInput
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import java.io.DataOutput
|
||||
|
||||
internal object KotlinDslScriptModels {
|
||||
private val attribute = FileAttribute("kotlin-dsl-script-models", 2, false)
|
||||
private val attribute = FileAttribute("kotlin-dsl-script-models", 3, false)
|
||||
|
||||
fun read(project: Project): ConfigurationData? {
|
||||
return attribute.readAttribute(project.projectFile ?: return null)?.use {
|
||||
readKotlinDslScriptModels(it)
|
||||
fun read(buildRoot: VirtualFile): ConfigurationData? {
|
||||
return attribute.readAttribute(buildRoot)?.use {
|
||||
it.readNullable {
|
||||
readKotlinDslScriptModels(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun write(project: Project, configuration: ConfigurationData) {
|
||||
attribute.writeAttribute(project.projectFile ?: return).use {
|
||||
writeKotlinDslScriptModels(it, configuration)
|
||||
fun write(buildRoot: VirtualFile, configuration: ConfigurationData?) {
|
||||
attribute.writeAttribute(buildRoot).use {
|
||||
it.writeNullable(configuration) {
|
||||
writeKotlinDslScriptModels(this, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun remove(buildRoot: VirtualFile) {
|
||||
write(buildRoot, null)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun writeKotlinDslScriptModels(output: DataOutputStream, data: ConfigurationData) {
|
||||
internal fun writeKotlinDslScriptModels(output: DataOutput, data: ConfigurationData) {
|
||||
val strings = StringsPool.writer(output)
|
||||
strings.addStrings(data.templateClasspath)
|
||||
val scriptModels = data.models
|
||||
scriptModels.forEach {
|
||||
data.models.forEach {
|
||||
strings.addString(it.file)
|
||||
strings.addStrings(it.classPath)
|
||||
strings.addStrings(it.sourcePath)
|
||||
@@ -41,7 +51,7 @@ internal fun writeKotlinDslScriptModels(output: DataOutputStream, data: Configur
|
||||
}
|
||||
strings.writeHeader()
|
||||
strings.writeStringIds(data.templateClasspath)
|
||||
output.writeList(scriptModels) {
|
||||
output.writeList(data.models) {
|
||||
strings.writeStringId(it.file)
|
||||
output.writeString(it.inputs.sections)
|
||||
output.writeLong(it.inputs.inputsTS)
|
||||
@@ -71,9 +81,9 @@ internal fun readKotlinDslScriptModels(input: DataInputStream): ConfigurationDat
|
||||
}
|
||||
|
||||
private object StringsPool {
|
||||
fun writer(output: DataOutputStream) = Writer(output)
|
||||
fun writer(output: DataOutput) = Writer(output)
|
||||
|
||||
class Writer(val output: DataOutputStream) {
|
||||
class Writer(val output: DataOutput) {
|
||||
var freeze = false
|
||||
val ids = mutableMapOf<String, Int>()
|
||||
|
||||
@@ -106,7 +116,7 @@ private object StringsPool {
|
||||
output.writeInt(getStringId(it))
|
||||
}
|
||||
|
||||
fun writeStringIds(strings: List<String>) {
|
||||
fun writeStringIds(strings: Collection<String>) {
|
||||
output.writeInt(strings.size)
|
||||
strings.forEach {
|
||||
writeStringId(it)
|
||||
@@ -128,12 +138,12 @@ private object StringsPool {
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T> DataOutputStream.writeList(list: Collection<T>, write: (T) -> Unit) {
|
||||
private inline fun <T> DataOutput.writeList(list: Collection<T>, write: (T) -> Unit) {
|
||||
writeInt(list.size)
|
||||
list.forEach { write(it) }
|
||||
}
|
||||
|
||||
private inline fun <T> DataInputStream.readList(read: () -> T): List<T> {
|
||||
private inline fun <T> DataInput.readList(read: () -> T): List<T> {
|
||||
val n = readInt()
|
||||
val result = ArrayList<T>(n)
|
||||
repeat(n) {
|
||||
|
||||
@@ -20,10 +20,10 @@ import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtScriptInitializer
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
|
||||
import org.jetbrains.plugins.gradle.settings.GradleLocalSettings
|
||||
import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings
|
||||
import org.jetbrains.plugins.gradle.settings.GradleProjectSettings
|
||||
import org.jetbrains.plugins.gradle.settings.GradleSettings
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants
|
||||
import java.util.*
|
||||
|
||||
private val sections = arrayListOf("buildscript", "plugins", "initscript", "pluginManagement")
|
||||
|
||||
@@ -95,34 +95,50 @@ fun useScriptConfigurationFromImportOnly(): Boolean {
|
||||
return Registry.`is`("kotlin.gradle.scripts.useIdeaProjectImport", false)
|
||||
}
|
||||
|
||||
fun getGradleVersion(project: Project, externalProjectPath: String): String? {
|
||||
val localVersion = GradleLocalSettings.getInstance(project).getGradleVersion(externalProjectPath)
|
||||
fun getGradleVersion(project: Project, settings: GradleProjectSettings): String {
|
||||
val localVersion = GradleLocalSettings.getInstance(project).getGradleVersion(settings.externalProjectPath)
|
||||
if (localVersion != null) return localVersion
|
||||
|
||||
return GradleSettings.getInstance(project).getLinkedProjectSettings(externalProjectPath)?.resolveGradleVersion()?.version
|
||||
}
|
||||
|
||||
fun getGradleVersion(project: Project): String? {
|
||||
val projectSettings = getGradleProjectSettings(project).firstOrNull()
|
||||
|
||||
if (projectSettings != null) {
|
||||
return getGradleVersion(projectSettings.externalProjectPath)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getJavaHomeForGradleProject(project: Project): String? {
|
||||
val projectSettings = getGradleProjectSettings(project).firstOrNull() ?: return null
|
||||
|
||||
val gradleExeSettings = ExternalSystemApiUtil.getExecutionSettings<GradleExecutionSettings>(
|
||||
project,
|
||||
projectSettings.externalProjectPath,
|
||||
GradleConstants.SYSTEM_ID
|
||||
)
|
||||
return gradleExeSettings.javaHome
|
||||
return settings.resolveGradleVersion().version
|
||||
}
|
||||
|
||||
fun getGradleProjectSettings(project: Project): Collection<GradleProjectSettings> {
|
||||
val gradleSettings = ExternalSystemApiUtil.getSettings(project, GradleConstants.SYSTEM_ID) as GradleSettings
|
||||
return gradleSettings.getLinkedProjectsSettings()
|
||||
}
|
||||
|
||||
class RootsIndex<T : Any> {
|
||||
internal val tree = TreeMap<String, T>()
|
||||
var values: Collection<T> = listOf()
|
||||
internal set
|
||||
|
||||
fun findRoot(path: String): T? {
|
||||
// race condition can be ignored
|
||||
val values = values
|
||||
val size = values.size
|
||||
if (size == 0) return null
|
||||
if (size == 1) return values.single() // we can omit prefix check
|
||||
return tree.floorEntry(path).takeIf { path.startsWith(it.key) }?.value
|
||||
}
|
||||
|
||||
internal inline fun update(updater: (insert: (prefix: String, value: T) -> Unit) -> Unit) {
|
||||
synchronized(this) {
|
||||
updater { prefix, value -> tree[prefix] = value }
|
||||
values = tree.values
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
operator fun set(prefix: String, value: T) {
|
||||
val moreCommon = tree.lowerKey(prefix)
|
||||
check(moreCommon == null || !prefix.startsWith(moreCommon)) {
|
||||
"Cannot add root `${prefix}`. More common root already added: `$moreCommon`"
|
||||
}
|
||||
|
||||
tree[prefix] = value
|
||||
values = tree.values
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun remove(prefix: String) = tree.remove(prefix)
|
||||
}
|
||||
+4
-5
@@ -16,9 +16,7 @@ import kotlin.test.assertEquals
|
||||
class KotlinDslScriptModelsTest {
|
||||
@Test
|
||||
fun write() {
|
||||
val buffer = ByteArrayOutputStream()
|
||||
|
||||
val conf = ConfigurationData(
|
||||
val data = ConfigurationData(
|
||||
listOf("a", "b", "c"),
|
||||
listOf(
|
||||
KotlinDslScriptModel(
|
||||
@@ -40,10 +38,11 @@ class KotlinDslScriptModelsTest {
|
||||
)
|
||||
)
|
||||
|
||||
writeKotlinDslScriptModels(DataOutputStream(buffer), conf)
|
||||
val buffer = ByteArrayOutputStream()
|
||||
writeKotlinDslScriptModels(DataOutputStream(buffer), data)
|
||||
|
||||
val restored = readKotlinDslScriptModels(DataInputStream(ByteArrayInputStream(buffer.toByteArray())))
|
||||
|
||||
assertEquals(conf.toString(), restored.toString())
|
||||
assertEquals(data.toString(), restored.toString())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.scripting.gradle
|
||||
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFails
|
||||
|
||||
class RootsIndexTest {
|
||||
@Test
|
||||
fun findRoot() {
|
||||
val roots = RootsIndex<Int>()
|
||||
roots["a/b/c"] = 1
|
||||
assertFails { roots["a/b/c/d"] = 2 }
|
||||
roots["a/c"] = 3
|
||||
roots["d"] = 4
|
||||
|
||||
assertEquals(1, roots.findRoot("a/b/c/x"))
|
||||
assertEquals(1, roots.findRoot("a/b/c"))
|
||||
assertEquals(3, roots.findRoot("a/c/x"))
|
||||
assertEquals(3, roots.findRoot("a/c"))
|
||||
assertEquals(4, roots.findRoot("d"))
|
||||
assertEquals(4, roots.findRoot("d/x"))
|
||||
assertEquals(null, roots.findRoot("e"))
|
||||
assertEquals(null, roots.findRoot("e/f"))
|
||||
|
||||
roots["d"] = 5
|
||||
assertEquals(5, roots.findRoot("d"))
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin.scripting.idea">
|
||||
<listener order="first" implementation="org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptListener"/>
|
||||
<loader order="first" implementation="org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptConfigurationLoader"/>
|
||||
<scriptingSupport
|
||||
implementation="org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptingSupport"
|
||||
<scriptingSupportProvider
|
||||
implementation="org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptingSupportProvider"
|
||||
/>
|
||||
</extensions>
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin.scripting.idea">
|
||||
<listener order="first" implementation="org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptListener"/>
|
||||
<loader order="first" implementation="org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptConfigurationLoader"/>
|
||||
<scriptingSupport
|
||||
implementation="org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptingSupport"
|
||||
<scriptingSupportProvider
|
||||
implementation="org.jetbrains.kotlin.idea.scripting.gradle.GradleScriptingSupportProvider"
|
||||
/>
|
||||
</extensions>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<idea-plugin>
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.scripting.idea.scriptingSupport"
|
||||
interface="org.jetbrains.kotlin.idea.core.script.configuration.ScriptingSupport"
|
||||
<extensionPoint qualifiedName="org.jetbrains.kotlin.scripting.idea.scriptingSupportProvider"
|
||||
interface="org.jetbrains.kotlin.idea.core.script.configuration.ScriptingSupport$Provider"
|
||||
area="IDEA_PROJECT"/>
|
||||
</extensionPoints>
|
||||
|
||||
@@ -10,11 +10,4 @@
|
||||
|
||||
<trafficLightRendererContributor implementation="org.jetbrains.kotlin.idea.core.script.ScriptTrafficLightRendererContributor"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin.scripting.idea">
|
||||
<scriptingSupport
|
||||
implementation="org.jetbrains.kotlin.idea.core.script.configuration.DefaultScriptingSupport"
|
||||
order="last"
|
||||
/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
Reference in New Issue
Block a user