Refactor script definitions and resolving/refining infrastructure:
- implement wrappers to wrap old and new API providers and resolvers - make old API deprecated (with error where possible) - drop old internal classes related to the old API - refactor usages accordingly - fix and add missing features to the scripting API where necessary
This commit is contained in:
+71
-56
@@ -39,11 +39,7 @@ import org.jetbrains.kotlin.idea.caches.project.SdkInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.getScriptRelatedModuleInfo
|
||||
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
|
||||
import org.jetbrains.kotlin.script.ScriptTemplatesProvider
|
||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinitionAdapterFromNewAPI
|
||||
import org.jetbrains.kotlin.scripting.definitions.LazyScriptDefinitionProvider
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
|
||||
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||
import org.jetbrains.kotlin.scripting.definitions.*
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
|
||||
@@ -53,35 +49,33 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.write
|
||||
import kotlin.script.dependencies.Environment
|
||||
import kotlin.script.dependencies.ScriptContents
|
||||
import kotlin.script.experimental.api.KotlinType
|
||||
import kotlin.script.experimental.dependencies.DependenciesResolver
|
||||
import kotlin.script.experimental.dependencies.ScriptDependencies
|
||||
import kotlin.script.experimental.dependencies.asSuccess
|
||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||
import kotlin.script.experimental.host.configurationDependencies
|
||||
import kotlin.script.experimental.host.createCompilationConfigurationFromTemplate
|
||||
import kotlin.script.experimental.jvm.JvmDependency
|
||||
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
||||
import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContextOrStdlib
|
||||
import kotlin.script.templates.standard.ScriptTemplateWithArgs
|
||||
|
||||
class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinitionProvider() {
|
||||
private var definitionsByContributor = mutableMapOf<ScriptDefinitionContributor, List<KotlinScriptDefinition>>()
|
||||
private var definitions: List<KotlinScriptDefinition>? = null
|
||||
private var definitionsBySource = mutableMapOf<ScriptDefinitionsSource, List<ScriptDefinition>>()
|
||||
private var definitions: List<ScriptDefinition>? = null
|
||||
|
||||
private val failedContributorsHashes = HashSet<Int>()
|
||||
|
||||
private val scriptDefinitionsCacheLock = ReentrantReadWriteLock()
|
||||
private val scriptDefinitionsCache = SLRUMap<String, KotlinScriptDefinition>(10, 10)
|
||||
private val scriptDefinitionsCache = SLRUMap<String, ScriptDefinition>(10, 10)
|
||||
|
||||
override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? {
|
||||
override fun findDefinition(fileName: String): ScriptDefinition? {
|
||||
if (nonScriptFileName(fileName)) return null
|
||||
if (!isReady()) return null
|
||||
|
||||
val cached = scriptDefinitionsCacheLock.write { scriptDefinitionsCache.get(fileName) }
|
||||
if (cached != null) return cached
|
||||
|
||||
val definition = super.findScriptDefinition(fileName) ?: return null
|
||||
val definition = super.findDefinition(fileName) ?: return null
|
||||
|
||||
scriptDefinitionsCacheLock.write {
|
||||
scriptDefinitionsCache.put(fileName, definition)
|
||||
@@ -90,48 +84,43 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
|
||||
return definition
|
||||
}
|
||||
|
||||
fun reloadDefinitionsBy(contributor: ScriptDefinitionContributor) = lock.write {
|
||||
override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? = findDefinition(fileName)?.legacyDefinition
|
||||
|
||||
fun reloadDefinitionsBy(source: ScriptDefinitionsSource) = lock.write {
|
||||
if (definitions == null) return // not loaded yet
|
||||
|
||||
if (contributor !in definitionsByContributor) error("Unknown contributor: ${contributor.id}")
|
||||
if (source !in definitionsBySource) error("Unknown script definition source: $source")
|
||||
|
||||
definitionsByContributor[contributor] = contributor.safeGetDefinitions()
|
||||
definitionsBySource[source] = source.safeGetDefinitions()
|
||||
|
||||
definitions = definitionsByContributor.values.flattenTo(mutableListOf())
|
||||
definitions = definitionsBySource.values.flattenTo(mutableListOf())
|
||||
|
||||
updateDefinitions()
|
||||
}
|
||||
|
||||
fun getDefinitionsBy(contributor: ScriptDefinitionContributor): List<KotlinScriptDefinition> = lock.write {
|
||||
if (definitions == null) return emptyList() // not loaded yet
|
||||
|
||||
if (contributor !in definitionsByContributor) error("Unknown contributor: ${contributor.id}")
|
||||
|
||||
return definitionsByContributor[contributor] ?: emptyList()
|
||||
}
|
||||
|
||||
override val currentDefinitions: Sequence<KotlinScriptDefinition>
|
||||
override val currentDefinitions
|
||||
get() =
|
||||
(definitions ?: kotlin.run {
|
||||
reloadScriptDefinitions()
|
||||
definitions!!
|
||||
}).asSequence().filter { KotlinScriptingSettings.getInstance(project).isScriptDefinitionEnabled(it) }
|
||||
|
||||
private fun getContributors(): List<ScriptDefinitionContributor> {
|
||||
private fun getSources(): List<ScriptDefinitionsSource> {
|
||||
@Suppress("DEPRECATION")
|
||||
val fromDeprecatedEP = Extensions.getArea(project).getExtensionPoint(ScriptTemplatesProvider.EP_NAME).extensions.toList()
|
||||
.map(::ScriptTemplatesProviderAdapter)
|
||||
.map { ScriptTemplatesProviderAdapter(it).asSource() }
|
||||
val fromNewEp = Extensions.getArea(project).getExtensionPoint(ScriptDefinitionContributor.EP_NAME).extensions.toList()
|
||||
.map { it.asSource() }
|
||||
return fromNewEp.dropLast(1) + fromDeprecatedEP + fromNewEp.last()
|
||||
}
|
||||
|
||||
fun reloadScriptDefinitions() = lock.write {
|
||||
for (contributor in getContributors()) {
|
||||
val definitions = contributor.safeGetDefinitions()
|
||||
definitionsByContributor[contributor] = definitions
|
||||
for (source in getSources()) {
|
||||
val definitions = source.safeGetDefinitions()
|
||||
definitionsBySource[source] = definitions
|
||||
}
|
||||
|
||||
definitions = definitionsByContributor.values.flattenTo(mutableListOf())
|
||||
definitions = definitionsBySource.values.flattenTo(mutableListOf())
|
||||
|
||||
updateDefinitions()
|
||||
}
|
||||
@@ -140,7 +129,7 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
|
||||
updateDefinitions()
|
||||
}
|
||||
|
||||
fun getAllDefinitions(): List<KotlinScriptDefinition> {
|
||||
fun getAllDefinitions(): List<ScriptDefinition> {
|
||||
return definitions ?: kotlin.run {
|
||||
reloadScriptDefinitions()
|
||||
definitions!!
|
||||
@@ -148,15 +137,16 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
|
||||
}
|
||||
|
||||
fun isReady(): Boolean {
|
||||
return definitionsByContributor.keys.all { contributor ->
|
||||
contributor.isReady()
|
||||
return definitionsBySource.keys.all { source ->
|
||||
// TODO: implement another API for readiness checking
|
||||
(source as? ScriptDefinitionContributor)?.isReady() != false
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDefaultScriptDefinition(): KotlinScriptDefinition {
|
||||
override fun getDefaultDefinition(): ScriptDefinition {
|
||||
val standardScriptDefinitionContributor = ScriptDefinitionContributor.find<StandardScriptDefinitionContributor>(project)
|
||||
?: error("StandardScriptDefinitionContributor should be registered is plugin.xml")
|
||||
return standardScriptDefinitionContributor.getDefinitions().last()
|
||||
return ScriptDefinition.FromLegacy(getScriptingHostConfiguration(), standardScriptDefinitionContributor.getDefinitions().last())
|
||||
}
|
||||
|
||||
private fun updateDefinitions() {
|
||||
@@ -187,7 +177,7 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
|
||||
scriptDefinitionsCacheLock.write { scriptDefinitionsCache.clear() }
|
||||
|
||||
// TODO: clear by script type/definition
|
||||
ServiceManager.getService(project, ScriptDependenciesCache::class.java).clear()
|
||||
ServiceManager.getService(project, ScriptsCompilationConfigurationCache::class.java).clear()
|
||||
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
if (!project.isDisposed) {
|
||||
@@ -196,9 +186,9 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
|
||||
}
|
||||
}
|
||||
|
||||
private fun ScriptDefinitionContributor.safeGetDefinitions(): List<KotlinScriptDefinition> {
|
||||
private fun ScriptDefinitionsSource.safeGetDefinitions(): List<ScriptDefinition> {
|
||||
if (!failedContributorsHashes.contains(this@safeGetDefinitions.hashCode())) try {
|
||||
return getDefinitions()
|
||||
return definitions.toList()
|
||||
} catch (t: Throwable) {
|
||||
// reporting failed loading only once
|
||||
LOG.error("[kts] cannot load script definitions using $this", t)
|
||||
@@ -207,6 +197,15 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
@Suppress("unused") // used in the 182/as33 bunches
|
||||
fun getDefinitionsBy(source: ScriptDefinitionsSource): List<ScriptDefinition> = lock.write {
|
||||
if (definitions == null) return emptyList() // not loaded yet
|
||||
|
||||
if (source !in definitionsBySource) error("Unknown source: ${source::class.java.name}")
|
||||
|
||||
return definitionsBySource[source] ?: emptyList()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): ScriptDefinitionsManager =
|
||||
ServiceManager.getService(project, ScriptDefinitionProvider::class.java) as ScriptDefinitionsManager
|
||||
@@ -216,10 +215,11 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit
|
||||
|
||||
private val LOG = Logger.getInstance("ScriptTemplatesProviders")
|
||||
|
||||
// TODO: consider rewriting to return sequence
|
||||
fun loadDefinitionsFromTemplates(
|
||||
templateClassNames: List<String>,
|
||||
templateClasspath: List<File>,
|
||||
environment: Environment = emptyMap(),
|
||||
baseHostConfiguration: ScriptingHostConfiguration,
|
||||
// TODO: need to provide a way to specify this in compiler/repl .. etc
|
||||
/*
|
||||
* Allows to specify additional jars needed for DependenciesResolver (and not script template).
|
||||
@@ -227,7 +227,7 @@ fun loadDefinitionsFromTemplates(
|
||||
* i.e. gradle resolver may depend on some jars that 'built.gradle.kts' files should not depend on.
|
||||
*/
|
||||
additionalResolverClasspath: List<File> = emptyList()
|
||||
): List<KotlinScriptDefinition> {
|
||||
): List<ScriptDefinition> {
|
||||
val classpath = templateClasspath + additionalResolverClasspath
|
||||
LOG.info("[kts] loading script definitions $templateClassNames using cp: ${classpath.joinToString(File.pathSeparator)}")
|
||||
val baseLoader = ScriptDefinitionContributor::class.java.classLoader
|
||||
@@ -240,24 +240,13 @@ fun loadDefinitionsFromTemplates(
|
||||
val template = loader.loadClass(templateClassName).kotlin
|
||||
when {
|
||||
template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>() != null -> {
|
||||
KotlinScriptDefinitionFromAnnotatedTemplate(
|
||||
template,
|
||||
environment,
|
||||
templateClasspath
|
||||
)
|
||||
ScriptDefinition.FromLegacyTemplate(baseHostConfiguration, template, templateClasspath)
|
||||
}
|
||||
template.annotations.firstIsInstanceOrNull<kotlin.script.experimental.annotations.KotlinScript>() != null -> {
|
||||
val hostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
|
||||
val hostConfiguration = ScriptingHostConfiguration(baseHostConfiguration) {
|
||||
configurationDependencies(JvmDependency(classpath))
|
||||
}
|
||||
KotlinScriptDefinitionAdapterFromNewAPI(
|
||||
createCompilationConfigurationFromTemplate(
|
||||
KotlinType(
|
||||
template
|
||||
), hostConfiguration, KotlinScriptDefinition::class
|
||||
),
|
||||
hostConfiguration
|
||||
)
|
||||
ScriptDefinition.FromTemplate(hostConfiguration, template, ScriptDefinition::class)
|
||||
}
|
||||
else -> {
|
||||
LOG.warn("[kts] cannot find a valid script definition annotation on the class $template")
|
||||
@@ -276,10 +265,16 @@ fun loadDefinitionsFromTemplates(
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("migrating to new configuration refinement: use ScriptDefinitionsSource internally and kotlin.script.experimental.intellij.ScriptDefinitionsProvider as a providing extension point")
|
||||
interface ScriptDefinitionContributor {
|
||||
|
||||
@Deprecated("migrating to new configuration refinement: drop usages")
|
||||
val id: String
|
||||
|
||||
@Deprecated("migrating to new configuration refinement: use ScriptDefinitionsSource instead")
|
||||
fun getDefinitions(): List<KotlinScriptDefinition>
|
||||
|
||||
@Deprecated("migrating to new configuration refinement: drop usages")
|
||||
fun isReady() = true
|
||||
|
||||
companion object {
|
||||
@@ -289,9 +284,29 @@ interface ScriptDefinitionContributor {
|
||||
inline fun <reified T> find(project: Project) =
|
||||
Extensions.getArea(project).getExtensionPoint(ScriptDefinitionContributor.EP_NAME).extensions.filterIsInstance<T>().firstOrNull()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Deprecated("migrating to new configuration refinement: use ScriptDefinitionsSource directly instead")
|
||||
interface ScriptDefinitionSourceAsContributor : ScriptDefinitionContributor, ScriptDefinitionsSource {
|
||||
|
||||
override fun getDefinitions(): List<KotlinScriptDefinition> = definitions.map { it.legacyDefinition }.toList()
|
||||
}
|
||||
|
||||
@Deprecated("migrating to new configuration refinement: convert all contributors to ScriptDefinitionsSource/ScriptDefinitionsProvider")
|
||||
class ScriptDefinitionSourceFromContributor(
|
||||
val contributor: ScriptDefinitionContributor,
|
||||
val hostConfiguration: ScriptingHostConfiguration = defaultJvmScriptingHostConfiguration
|
||||
) : ScriptDefinitionsSource {
|
||||
override val definitions: Sequence<ScriptDefinition>
|
||||
get() =
|
||||
if (contributor is ScriptDefinitionsSource) contributor.definitions
|
||||
else contributor.getDefinitions().asSequence().map { ScriptDefinition.FromLegacy(hostConfiguration, it) }
|
||||
}
|
||||
|
||||
fun ScriptDefinitionContributor.asSource(): ScriptDefinitionsSource =
|
||||
if (this is ScriptDefinitionsSource) this
|
||||
else ScriptDefinitionSourceFromContributor(this)
|
||||
|
||||
class StandardScriptDefinitionContributor(project: Project) : ScriptDefinitionContributor {
|
||||
private val standardIdeScriptDefinition = StandardIdeScriptDefinition(project)
|
||||
|
||||
|
||||
+27
-3
@@ -18,8 +18,9 @@ package org.jetbrains.kotlin.idea.core.script
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.util.*
|
||||
import java.io.DataInput
|
||||
import java.io.DataOutput
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import java.io.*
|
||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
||||
import kotlin.script.experimental.dependencies.ScriptDependencies
|
||||
|
||||
var VirtualFile.scriptDependencies: ScriptDependencies? by cachedFileAttribute(
|
||||
@@ -43,4 +44,27 @@ var VirtualFile.scriptDependencies: ScriptDependencies? by cachedFileAttribute(
|
||||
writeFileList(sources)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
var VirtualFile.scriptCompilationConfiguration: ScriptCompilationConfiguration? by cachedFileAttribute(
|
||||
name = "kotlin-script-compilation-configuration",
|
||||
version = 1,
|
||||
read = {
|
||||
val size = readInt()
|
||||
val bytes = ByteArray(size)
|
||||
read(bytes, 0, size)
|
||||
val bis = ByteArrayInputStream(bytes)
|
||||
ObjectInputStream(bis).use { ois ->
|
||||
ois.readObject() as ScriptCompilationConfiguration
|
||||
}
|
||||
},
|
||||
write = {
|
||||
val os = ByteArrayOutputStream()
|
||||
ObjectOutputStream(os).use { oos ->
|
||||
oos.writeObject((it as? ScriptCompilationConfigurationWrapper.FromCompilationConfiguration)?.configuration)
|
||||
}
|
||||
val bytes = os.toByteArray()
|
||||
writeInt(bytes.size)
|
||||
write(bytes)
|
||||
}
|
||||
)
|
||||
|
||||
+32
-33
@@ -30,8 +30,10 @@ import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.idea.caches.project.getAllProjectSdks
|
||||
import org.jetbrains.kotlin.idea.core.script.dependencies.SyncScriptDependenciesLoader
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.dependencies.ScriptDependencies
|
||||
import kotlin.script.experimental.api.valueOrNull
|
||||
|
||||
|
||||
// NOTE: this service exists exclusively because ScriptDependencyManager
|
||||
@@ -39,18 +41,21 @@ import kotlin.script.experimental.dependencies.ScriptDependencies
|
||||
class IdeScriptDependenciesProvider(
|
||||
private val scriptDependenciesManager: ScriptDependenciesManager
|
||||
) : ScriptDependenciesProvider {
|
||||
override fun getScriptDependencies(file: VirtualFile): ScriptDependencies? {
|
||||
return scriptDependenciesManager.getScriptDependencies(file)
|
||||
}
|
||||
override fun getScriptConfigurationResult(file: VirtualFile): ScriptCompilationConfigurationResult? = scriptDependenciesManager.getRefinedCompilationConfiguration(file)
|
||||
}
|
||||
|
||||
// TODO: rename and provide alias for compatibility - this is not only about dependencies anymore
|
||||
class ScriptDependenciesManager internal constructor(
|
||||
private val cacheUpdater: ScriptDependenciesUpdater,
|
||||
private val cache: ScriptDependenciesCache
|
||||
private val cacheUpdater: ScriptsCompilationConfigurationUpdater,
|
||||
private val cache: ScriptsCompilationConfigurationCache
|
||||
) {
|
||||
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> = toVfsRoots(cacheUpdater.getCurrentDependencies(file).classpath)
|
||||
fun getScriptDependencies(file: VirtualFile): ScriptDependencies = cacheUpdater.getCurrentDependencies(file)
|
||||
fun getScriptSdk(file: VirtualFile): Sdk? = getScriptSdk(getScriptDependencies(file))
|
||||
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> =
|
||||
toVfsRoots(cacheUpdater.getCurrentCompilationConfiguration(file)?.valueOrNull()?.dependenciesClassPath.orEmpty())
|
||||
|
||||
fun getRefinedCompilationConfiguration(file: VirtualFile): ScriptCompilationConfigurationResult? =
|
||||
cacheUpdater.getCurrentCompilationConfiguration(file)
|
||||
|
||||
fun getScriptSdk(file: VirtualFile): Sdk? = Companion.getScriptSdk(getRefinedCompilationConfiguration(file)?.valueOrNull())
|
||||
|
||||
fun getScriptDependenciesClassFilesScope(file: VirtualFile) = cache.scriptDependenciesClassFilesScope(file)
|
||||
|
||||
@@ -67,35 +72,29 @@ class ScriptDependenciesManager internal constructor(
|
||||
fun getInstance(project: Project): ScriptDependenciesManager =
|
||||
ServiceManager.getService(project, ScriptDependenciesManager::class.java)
|
||||
|
||||
fun getScriptSdk(dependencies: ScriptDependencies): Sdk? {
|
||||
fun getScriptSdk(compilationConfiguration: ScriptCompilationConfigurationWrapper?): Sdk? {
|
||||
// workaround for mismatched gradle wrapper and plugin version
|
||||
try {
|
||||
val javaHome = dependencies.javaHome
|
||||
?.let { VfsUtil.findFileByIoFile(it, true) }
|
||||
?: return null
|
||||
|
||||
return getAllProjectSdks().find { it.homeDirectory == javaHome }
|
||||
val javaHome = try {
|
||||
compilationConfiguration?.javaHome?.let { VfsUtil.findFileByIoFile(it, true) }
|
||||
} catch (e: Throwable) {
|
||||
return null
|
||||
}
|
||||
null
|
||||
} ?: return null
|
||||
|
||||
return getAllProjectSdks().find { it.homeDirectory == javaHome }
|
||||
}
|
||||
|
||||
fun getScriptDefaultSdk(project: Project): Sdk? {
|
||||
val projectSdk = getProjectSdk(project)
|
||||
if (projectSdk != null) return projectSdk
|
||||
|
||||
val anyJavaSdk = getAllProjectSdks().find { it.canBeUsedForScript() }
|
||||
if (anyJavaSdk != null) {
|
||||
return anyJavaSdk
|
||||
}
|
||||
|
||||
log.warn(
|
||||
"Default Script SDK is null: " +
|
||||
"projectSdk = ${ProjectRootManager.getInstance(project).projectSdk}, " +
|
||||
"all sdks = ${getAllProjectSdks().joinToString("\n")}"
|
||||
)
|
||||
return null
|
||||
}
|
||||
fun getScriptDefaultSdk(project: Project): Sdk? =
|
||||
getProjectSdk(project)
|
||||
?: getAllProjectSdks().find { it.canBeUsedForScript() }
|
||||
?: run {
|
||||
log.warn(
|
||||
"Default Script SDK is null: " +
|
||||
"projectSdk = ${ProjectRootManager.getInstance(project).projectSdk}, " +
|
||||
"all sdks = ${getAllProjectSdks().joinToString("; ")}"
|
||||
)
|
||||
null
|
||||
}
|
||||
|
||||
fun getProjectSdk(project: Project) = ProjectRootManager.getInstance(project).projectSdk?.takeIf { it.canBeUsedForScript() }
|
||||
|
||||
|
||||
+11
-11
@@ -16,7 +16,7 @@ import com.intellij.ui.EditorNotificationPanel
|
||||
import com.intellij.ui.HyperlinkLabel
|
||||
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
|
||||
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||
import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
|
||||
|
||||
fun VirtualFile.removeScriptDependenciesNotificationPanel(project: Project) {
|
||||
withSelectedEditor(project) { manager ->
|
||||
@@ -28,14 +28,14 @@ fun VirtualFile.removeScriptDependenciesNotificationPanel(project: Project) {
|
||||
}
|
||||
|
||||
fun VirtualFile.addScriptDependenciesNotificationPanel(
|
||||
resolveResult: ResolveResult,
|
||||
compilationConfigurationResult: ScriptCompilationConfigurationResult?,
|
||||
project: Project,
|
||||
onClick: (ResolveResult) -> Unit
|
||||
onClick: (ScriptCompilationConfigurationResult?) -> Unit
|
||||
) {
|
||||
withSelectedEditor(project) { manager ->
|
||||
val existingPanel = notificationPanel
|
||||
if (existingPanel != null) {
|
||||
if (existingPanel.resolveResult.dependencies == resolveResult.dependencies) {
|
||||
if (existingPanel.compilationConfigurationResult == compilationConfigurationResult) {
|
||||
return@withSelectedEditor
|
||||
}
|
||||
notificationPanel?.let {
|
||||
@@ -43,7 +43,7 @@ fun VirtualFile.addScriptDependenciesNotificationPanel(
|
||||
}
|
||||
}
|
||||
|
||||
val panel = NewScriptDependenciesNotificationPanel(onClick, resolveResult, project)
|
||||
val panel = NewScriptDependenciesNotificationPanel(onClick, compilationConfigurationResult, project)
|
||||
notificationPanel = panel
|
||||
manager.addTopComponent(this, panel)
|
||||
}
|
||||
@@ -61,19 +61,19 @@ private fun VirtualFile.withSelectedEditor(project: Project, f: FileEditor.(File
|
||||
private var FileEditor.notificationPanel: NewScriptDependenciesNotificationPanel? by UserDataProperty<FileEditor, NewScriptDependenciesNotificationPanel>(Key.create("script.dependencies.panel"))
|
||||
|
||||
private class NewScriptDependenciesNotificationPanel(
|
||||
onClick: (ResolveResult) -> Unit,
|
||||
val resolveResult: ResolveResult,
|
||||
onClick: (ScriptCompilationConfigurationResult?) -> Unit,
|
||||
val compilationConfigurationResult: ScriptCompilationConfigurationResult?,
|
||||
project: Project
|
||||
) : EditorNotificationPanel() {
|
||||
|
||||
init {
|
||||
setText("There are new script dependencies available.")
|
||||
createComponentActionLabel("Apply dependencies") {
|
||||
onClick(resolveResult)
|
||||
setText("There is a new script context available.")
|
||||
createComponentActionLabel("Apply context") {
|
||||
onClick(compilationConfigurationResult)
|
||||
}
|
||||
|
||||
createComponentActionLabel("Enable auto-reload") {
|
||||
onClick(resolveResult)
|
||||
onClick(compilationConfigurationResult)
|
||||
KotlinScriptingSettings.getInstance(project).isAutoReloadEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
+17
-9
@@ -6,17 +6,25 @@
|
||||
package org.jetbrains.kotlin.idea.core.script
|
||||
|
||||
import org.jetbrains.kotlin.script.ScriptTemplatesProvider
|
||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.getEnvironment
|
||||
import kotlin.script.experimental.host.ScriptingHostConfiguration
|
||||
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
|
||||
|
||||
class ScriptTemplatesProviderAdapter(private val templatesProvider: ScriptTemplatesProvider) : ScriptDefinitionSourceAsContributor {
|
||||
|
||||
class ScriptTemplatesProviderAdapter(private val templatesProvider: ScriptTemplatesProvider) :
|
||||
ScriptDefinitionContributor {
|
||||
override val id: String
|
||||
get() = templatesProvider.id
|
||||
|
||||
override fun getDefinitions(): List<KotlinScriptDefinition> {
|
||||
return loadDefinitionsFromTemplates(
|
||||
templatesProvider.templateClassNames.toList(), templatesProvider.templateClasspath,
|
||||
templatesProvider.environment.orEmpty(), templatesProvider.additionalResolverClasspath
|
||||
)
|
||||
}
|
||||
override val definitions: Sequence<ScriptDefinition>
|
||||
get() =
|
||||
loadDefinitionsFromTemplates(
|
||||
templatesProvider.templateClassNames.toList(), templatesProvider.templateClasspath,
|
||||
ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
|
||||
getEnvironment {
|
||||
templatesProvider.environment
|
||||
}
|
||||
},
|
||||
templatesProvider.additionalResolverClasspath
|
||||
).asSequence()
|
||||
}
|
||||
+20
-17
@@ -29,15 +29,17 @@ import com.intellij.util.containers.SLRUMap
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.kotlin.idea.core.util.EDT
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.write
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KProperty0
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
import kotlin.script.experimental.dependencies.ScriptDependencies
|
||||
import kotlin.script.experimental.api.valueOrNull
|
||||
|
||||
class ScriptDependenciesCache(private val project: Project) {
|
||||
class ScriptsCompilationConfigurationCache(private val project: Project) {
|
||||
|
||||
companion object {
|
||||
const val MAX_SCRIPTS_CACHED = 50
|
||||
@@ -45,10 +47,10 @@ class ScriptDependenciesCache(private val project: Project) {
|
||||
|
||||
private val cacheLock = ReentrantReadWriteLock()
|
||||
|
||||
private val scriptDependenciesCache = SLRUCacheWithLock<ScriptDependencies>()
|
||||
private val scriptDependenciesCache = SLRUCacheWithLock<ScriptCompilationConfigurationResult>()
|
||||
private val scriptsModificationStampsCache = SLRUCacheWithLock<Long>()
|
||||
|
||||
operator fun get(virtualFile: VirtualFile): ScriptDependencies? = scriptDependenciesCache.get(virtualFile)
|
||||
operator fun get(virtualFile: VirtualFile): ScriptCompilationConfigurationResult? = scriptDependenciesCache.get(virtualFile)
|
||||
|
||||
fun shouldRunDependenciesUpdate(file: VirtualFile): Boolean {
|
||||
return scriptsModificationStampsCache.replace(file, file.modificationStamp) != file.modificationStamp
|
||||
@@ -58,10 +60,10 @@ class ScriptDependenciesCache(private val project: Project) {
|
||||
|
||||
fun scriptDependenciesClassFilesScope(file: VirtualFile): GlobalSearchScope {
|
||||
return scriptsDependenciesClasspathScopeCache.getOrPut(file) {
|
||||
val dependencies = scriptDependenciesCache.get(file) ?: return@getOrPut GlobalSearchScope.EMPTY_SCOPE
|
||||
val roots = dependencies.classpath
|
||||
val compilationConfiguration = scriptDependenciesCache.get(file)?.valueOrNull() ?: return@getOrPut GlobalSearchScope.EMPTY_SCOPE
|
||||
val roots = compilationConfiguration.dependenciesClassPath
|
||||
|
||||
val sdk = ScriptDependenciesManager.getScriptSdk(dependencies)
|
||||
val sdk = ScriptDependenciesManager.getScriptSdk(compilationConfiguration)
|
||||
|
||||
@Suppress("FoldInitializerAndIfToElvis")
|
||||
if (sdk == null) {
|
||||
@@ -86,7 +88,8 @@ class ScriptDependenciesCache(private val project: Project) {
|
||||
.filter { it != ScriptDependenciesManager.getProjectSdk(project) }
|
||||
.flatMap { it.rootProvider.getFiles(OrderRootType.CLASSES).toList() }
|
||||
|
||||
val scriptDependenciesClasspath = scriptDependenciesCache.getAll().flatMap { it.value.classpath }.distinct()
|
||||
val scriptDependenciesClasspath = scriptDependenciesCache.getAll()
|
||||
.flatMap { it.value.valueOrNull()?.dependenciesClassPath ?: emptyList() }.distinct()
|
||||
|
||||
sdkFiles + ScriptDependenciesManager.toVfsRoots(scriptDependenciesClasspath)
|
||||
}
|
||||
@@ -96,7 +99,8 @@ class ScriptDependenciesCache(private val project: Project) {
|
||||
.filter { it != ScriptDependenciesManager.getProjectSdk(project) }
|
||||
.flatMap { it.rootProvider.getFiles(OrderRootType.SOURCES).toList() }
|
||||
|
||||
val scriptDependenciesSources = scriptDependenciesCache.getAll().flatMap { it.value.sources }.distinct()
|
||||
val scriptDependenciesSources = scriptDependenciesCache.getAll()
|
||||
.flatMap { it.value.valueOrNull()?.dependenciesSources ?: emptyList() }.distinct()
|
||||
sdkSources + ScriptDependenciesManager.toVfsRoots(scriptDependenciesSources)
|
||||
}
|
||||
|
||||
@@ -140,11 +144,10 @@ class ScriptDependenciesCache(private val project: Project) {
|
||||
}
|
||||
}
|
||||
|
||||
fun hasNotCachedRoots(scriptDependencies: ScriptDependencies): Boolean {
|
||||
return !allSdks.contains(ScriptDependenciesManager.getScriptSdk(scriptDependencies)) ||
|
||||
!allDependenciesClassFiles.containsAll(ScriptDependenciesManager.toVfsRoots(scriptDependencies.classpath)) ||
|
||||
!allDependenciesSources.containsAll(ScriptDependenciesManager.toVfsRoots(scriptDependencies.sources))
|
||||
|
||||
fun hasNotCachedRoots(compilationConfiguration: ScriptCompilationConfigurationWrapper): Boolean {
|
||||
return !allSdks.contains(ScriptDependenciesManager.getScriptSdk(compilationConfiguration)) ||
|
||||
!allDependenciesClassFiles.containsAll(ScriptDependenciesManager.toVfsRoots(compilationConfiguration.dependenciesClassPath)) ||
|
||||
!allDependenciesSources.containsAll(ScriptDependenciesManager.toVfsRoots(compilationConfiguration.dependenciesSources))
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
@@ -155,7 +158,7 @@ class ScriptDependenciesCache(private val project: Project) {
|
||||
onChange(keys)
|
||||
}
|
||||
|
||||
fun save(virtualFile: VirtualFile, new: ScriptDependencies): Boolean {
|
||||
fun save(virtualFile: VirtualFile, new: ScriptCompilationConfigurationResult): Boolean {
|
||||
val old = scriptDependenciesCache.replace(virtualFile, new)
|
||||
val changed = new != old
|
||||
if (changed) {
|
||||
@@ -207,8 +210,8 @@ private class SLRUCacheWithLock<T> {
|
||||
private val lock = ReentrantReadWriteLock()
|
||||
|
||||
val cache = SLRUMap<VirtualFile, T>(
|
||||
ScriptDependenciesCache.MAX_SCRIPTS_CACHED,
|
||||
ScriptDependenciesCache.MAX_SCRIPTS_CACHED
|
||||
ScriptsCompilationConfigurationCache.MAX_SCRIPTS_CACHED,
|
||||
ScriptsCompilationConfigurationCache.MAX_SCRIPTS_CACHED
|
||||
)
|
||||
|
||||
fun get(value: VirtualFile): T? = lock.write {
|
||||
+12
-11
@@ -41,14 +41,15 @@ import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
|
||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.LegacyResolverWrapper
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
|
||||
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
|
||||
import kotlin.script.experimental.dependencies.ScriptDependencies
|
||||
|
||||
class ScriptDependenciesUpdater(
|
||||
class ScriptsCompilationConfigurationUpdater(
|
||||
private val project: Project,
|
||||
private val cache: ScriptDependenciesCache
|
||||
private val cache: ScriptsCompilationConfigurationCache
|
||||
) {
|
||||
private val scriptsQueue = Alarm(Alarm.ThreadToUse.SWING_THREAD, project)
|
||||
private val scriptChangesListenerDelay = 1400
|
||||
@@ -64,13 +65,13 @@ class ScriptDependenciesUpdater(
|
||||
listenForChangesInScripts()
|
||||
}
|
||||
|
||||
fun getCurrentDependencies(file: VirtualFile): ScriptDependencies {
|
||||
fun getCurrentCompilationConfiguration(file: VirtualFile): ScriptCompilationConfigurationResult? {
|
||||
cache[file]?.let { return it }
|
||||
|
||||
updateDependencies(file)
|
||||
makeRootsChangeIfNeeded()
|
||||
|
||||
return cache[file] ?: ScriptDependencies.Empty
|
||||
return cache[file]
|
||||
}
|
||||
|
||||
fun updateDependenciesIfNeeded(files: List<VirtualFile>): Boolean {
|
||||
@@ -174,15 +175,15 @@ class ScriptDependenciesUpdater(
|
||||
return cache[file] != null || file.scriptDependencies != null
|
||||
}
|
||||
|
||||
fun isAsyncDependencyResolver(scriptDef: KotlinScriptDefinition): Boolean {
|
||||
val dependencyResolver = scriptDef.dependencyResolver
|
||||
return dependencyResolver is AsyncDependenciesResolver || dependencyResolver is LegacyResolverWrapper
|
||||
}
|
||||
fun isAsyncDependencyResolver(scriptDef: ScriptDefinition): Boolean =
|
||||
scriptDef.asLegacyOrNull<KotlinScriptDefinition>()?.dependencyResolver?.let {
|
||||
it is AsyncDependenciesResolver || it is LegacyResolverWrapper
|
||||
} ?: false
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project): ScriptDependenciesUpdater =
|
||||
ServiceManager.getService(project, ScriptDependenciesUpdater::class.java)
|
||||
fun getInstance(project: Project): ScriptsCompilationConfigurationUpdater =
|
||||
ServiceManager.getService(project, ScriptsCompilationConfigurationUpdater::class.java)
|
||||
|
||||
fun areDependenciesCached(file: KtFile): Boolean {
|
||||
return getInstance(file.project).areDependenciesCached(file.virtualFile)
|
||||
+9
-32
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.script.dependencies
|
||||
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.openapi.progress.EmptyProgressIndicator
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
@@ -13,18 +12,16 @@ import com.intellij.openapi.progress.util.BackgroundTaskUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import kotlinx.coroutines.Runnable
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdater
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater
|
||||
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
|
||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.asResolveFailure
|
||||
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
|
||||
import org.jetbrains.kotlin.scripting.resolve.refineScriptCompilationConfiguration
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.write
|
||||
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
|
||||
import kotlin.script.experimental.dependencies.DependenciesResolver
|
||||
|
||||
// TODO: rename and provide alias for compatibility - this is not only about dependencies anymore
|
||||
class AsyncScriptDependenciesLoader internal constructor(project: Project) : ScriptDependenciesLoader(project) {
|
||||
private val lock = ReentrantReadWriteLock()
|
||||
|
||||
@@ -33,7 +30,7 @@ class AsyncScriptDependenciesLoader internal constructor(project: Project) : Scr
|
||||
|
||||
override fun isApplicable(file: VirtualFile): Boolean {
|
||||
val scriptDefinition = file.findScriptDefinition(project) ?: return false
|
||||
return ScriptDependenciesUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition)
|
||||
return ScriptsCompilationConfigurationUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition)
|
||||
}
|
||||
|
||||
override fun loadDependencies(file: VirtualFile) {
|
||||
@@ -73,35 +70,15 @@ class AsyncScriptDependenciesLoader internal constructor(project: Project) : Scr
|
||||
}
|
||||
|
||||
private fun runDependenciesUpdate(file: VirtualFile) {
|
||||
val scriptDef = runReadAction { file.findScriptDefinition(project) } ?: return
|
||||
val scriptDef = file.findScriptDefinition(project) ?: return
|
||||
|
||||
debug(file) { "start async dependencies loading" }
|
||||
// runBlocking is using there to avoid loading dependencies asynchronously
|
||||
// because it leads to starting more than one gradle daemon in case of resolving dependencies in build.gradle.kts
|
||||
// It is more efficient to use one hot daemon consistently than multiple daemon in parallel
|
||||
val result = runBlocking {
|
||||
try {
|
||||
resolveDependencies(file, scriptDef)
|
||||
} catch (t: Throwable) {
|
||||
t.asResolveFailure(scriptDef)
|
||||
}
|
||||
}
|
||||
|
||||
val result = refineScriptCompilationConfiguration(VirtualFileScriptSource(file), scriptDef, project)
|
||||
|
||||
debug(file) { "finish async dependencies loading" }
|
||||
|
||||
processResult(result, file, scriptDef)
|
||||
}
|
||||
|
||||
private suspend fun resolveDependencies(file: VirtualFile, scriptDef: KotlinScriptDefinition): DependenciesResolver.ResolveResult {
|
||||
val dependenciesResolver = scriptDef.dependencyResolver
|
||||
val scriptContents = contentLoader.getScriptContents(scriptDef, file)
|
||||
val environment = contentLoader.getEnvironment(scriptDef)
|
||||
return if (dependenciesResolver is AsyncDependenciesResolver) {
|
||||
dependenciesResolver.resolveAsync(scriptContents, environment)
|
||||
} else {
|
||||
// TODO: shouldn't come here after dropping legacy resolvers, refactor accordingly
|
||||
dependenciesResolver.resolve(scriptContents, environment)
|
||||
}
|
||||
processRefinedConfiguration(result, file)
|
||||
}
|
||||
|
||||
private inner class LoaderBackgroundTask {
|
||||
|
||||
+16
-3
@@ -7,8 +7,13 @@ package org.jetbrains.kotlin.idea.core.script.dependencies
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.scriptCompilationConfiguration
|
||||
import org.jetbrains.kotlin.idea.core.script.scriptDependencies
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
|
||||
import kotlin.script.experimental.api.asSuccess
|
||||
|
||||
// TODO: rename and provide alias for compatibility - this is not only about dependencies anymore
|
||||
class FromFileAttributeScriptDependenciesLoader(project: Project) : ScriptDependenciesLoader(project) {
|
||||
|
||||
override fun isApplicable(file: VirtualFile): Boolean {
|
||||
@@ -16,9 +21,17 @@ class FromFileAttributeScriptDependenciesLoader(project: Project) : ScriptDepend
|
||||
}
|
||||
|
||||
override fun loadDependencies(file: VirtualFile) {
|
||||
val deserializedDependencies = file.scriptDependencies ?: return
|
||||
debug(file) { "dependencies from fileAttributes = $deserializedDependencies" }
|
||||
saveToCache(file, deserializedDependencies)
|
||||
file.scriptCompilationConfiguration?.let {
|
||||
ScriptCompilationConfigurationWrapper.FromCompilationConfiguration(VirtualFileScriptSource(file), it).apply {
|
||||
debug(file) { "refined configuration from fileAttributes = $it" }
|
||||
}
|
||||
} ?: file.scriptDependencies?.let {
|
||||
ScriptCompilationConfigurationWrapper.FromLegacy(VirtualFileScriptSource(file), it).apply {
|
||||
debug(file) { "dependencies from fileAttributes = $it" }
|
||||
}
|
||||
}?.let {
|
||||
saveToCache(file, it.asSuccess(), skipSaveToAttributes = true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldShowNotification(): Boolean = false
|
||||
|
||||
+7
-4
@@ -22,6 +22,7 @@ import com.intellij.psi.ResolveScopeProvider
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager
|
||||
import org.jetbrains.kotlin.idea.core.script.StandardIdeScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||
|
||||
@@ -36,11 +37,13 @@ class KotlinScriptResolveScopeProvider : ResolveScopeProvider() {
|
||||
// TODO: this should get this particular scripts dependencies
|
||||
return when {
|
||||
scriptDefinition == null -> null
|
||||
// This is a workaround for completion in scripts and REPL to provide module dependencies
|
||||
scriptDefinition.template == Any::class -> null
|
||||
scriptDefinition is StandardIdeScriptDefinition -> null
|
||||
scriptDefinition is KotlinScriptDefinitionFromAnnotatedTemplate -> // TODO: should include the file itself
|
||||
// This is a workaround for completion in scripts and REPL to provide module dependencies
|
||||
scriptDefinition.baseClassType.fromClass == Any::class -> null
|
||||
scriptDefinition.asLegacyOrNull<StandardIdeScriptDefinition>() != null -> null
|
||||
scriptDefinition is ScriptDefinition.FromConfigurations || scriptDefinition.asLegacyOrNull<KotlinScriptDefinitionFromAnnotatedTemplate>() != null -> {
|
||||
// TODO: should include the file itself
|
||||
ScriptDependenciesManager.getInstance(project).getAllScriptsDependenciesClassFilesScope()
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -18,9 +18,8 @@ class OutsiderFileDependenciesLoader(project: Project) : ScriptDependenciesLoade
|
||||
|
||||
override fun loadDependencies(file: VirtualFile) {
|
||||
val fileOrigin = OutsidersPsiFileSupportUtils.getOutsiderFileOrigin(project, file) ?: return
|
||||
val originDependencies = ScriptDependenciesManager.getInstance(project).getScriptDependencies(fileOrigin)
|
||||
debug(file) { "dependencies for outsider file = $originDependencies" }
|
||||
saveToCache(file, originDependencies)
|
||||
val compilationConfiguration = ScriptDependenciesManager.getInstance(project).getRefinedCompilationConfiguration(fileOrigin) ?: return
|
||||
saveToCache(file, compilationConfiguration)
|
||||
}
|
||||
|
||||
override fun shouldShowNotification(): Boolean = false
|
||||
|
||||
+42
-32
@@ -15,13 +15,14 @@ import com.intellij.openapi.util.EmptyRunnable
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.*
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptContentLoader
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
|
||||
import org.jetbrains.kotlin.scripting.resolve.adjustByDefinition
|
||||
import kotlin.script.experimental.dependencies.DependenciesResolver
|
||||
import kotlin.script.experimental.dependencies.ScriptDependencies
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.valueOrNull
|
||||
import kotlin.script.experimental.jvm.compat.mapToLegacyReports
|
||||
|
||||
// TODO: rename and provide alias for compatibility - this is not only about dependencies anymore
|
||||
abstract class ScriptDependenciesLoader(protected val project: Project) {
|
||||
|
||||
abstract fun isApplicable(file: VirtualFile): Boolean
|
||||
@@ -31,40 +32,40 @@ abstract class ScriptDependenciesLoader(protected val project: Project) {
|
||||
|
||||
protected var shouldNotifyRootsChanged = false
|
||||
|
||||
protected val contentLoader = ScriptContentLoader(project)
|
||||
protected val cache: ScriptDependenciesCache = ServiceManager.getService(project, ScriptDependenciesCache::class.java)
|
||||
protected val cache: ScriptsCompilationConfigurationCache = ServiceManager.getService(project, ScriptsCompilationConfigurationCache::class.java)
|
||||
|
||||
private val reporter: ScriptReportSink = ServiceManager.getService(project, ScriptReportSink::class.java)
|
||||
|
||||
protected fun processResult(result: DependenciesResolver.ResolveResult, file: VirtualFile, scriptDef: KotlinScriptDefinition) {
|
||||
debug(file) { "dependencies from ${this.javaClass} received = $result" }
|
||||
protected fun processRefinedConfiguration(result: ScriptCompilationConfigurationResult, file: VirtualFile) {
|
||||
debug(file) { "refined script compilation configuration from ${this.javaClass} received = $result" }
|
||||
|
||||
if (cache[file] == null) {
|
||||
saveDependencies(result, file, scriptDef)
|
||||
attachReportsIfChanged(result, file, scriptDef)
|
||||
val oldResult = cache[file]
|
||||
|
||||
if (oldResult == null) {
|
||||
save(result, file)
|
||||
attachReportsIfChanged(result, file)
|
||||
return
|
||||
}
|
||||
|
||||
val newDependencies = result.dependencies?.adjustByDefinition(scriptDef)
|
||||
if (cache[file] != newDependencies) {
|
||||
if (oldResult != result) {
|
||||
if (shouldShowNotification() && !ApplicationManager.getApplication().isUnitTestMode) {
|
||||
debug(file) {
|
||||
"dependencies changed, notification was shown: old = ${cache[file]}, new = $newDependencies"
|
||||
"dependencies changed, notification is shown: old = $oldResult, new = $result"
|
||||
}
|
||||
file.addScriptDependenciesNotificationPanel(result, project) {
|
||||
saveDependencies(it, file, scriptDef)
|
||||
attachReportsIfChanged(it, file, scriptDef)
|
||||
save(it, file)
|
||||
attachReportsIfChanged(result, file)
|
||||
submitMakeRootsChange()
|
||||
}
|
||||
} else {
|
||||
debug(file) {
|
||||
"dependencies changed, new dependencies were applied automatically: old = ${cache[file]}, new = $newDependencies"
|
||||
"dependencies changed, new dependencies are applied automatically: old = $oldResult, new = $result"
|
||||
}
|
||||
saveDependencies(result, file, scriptDef)
|
||||
attachReportsIfChanged(result, file, scriptDef)
|
||||
save(result, file)
|
||||
attachReportsIfChanged(result, file)
|
||||
}
|
||||
} else {
|
||||
attachReportsIfChanged(result, file, scriptDef)
|
||||
attachReportsIfChanged(result, file)
|
||||
|
||||
if (shouldShowNotification()) {
|
||||
file.removeScriptDependenciesNotificationPanel(project)
|
||||
@@ -72,28 +73,36 @@ abstract class ScriptDependenciesLoader(protected val project: Project) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun attachReportsIfChanged(result: DependenciesResolver.ResolveResult, file: VirtualFile, scriptDef: KotlinScriptDefinition) {
|
||||
private fun attachReportsIfChanged(result: ResultWithDiagnostics<*>, file: VirtualFile) {
|
||||
if (file.getUserData(IdeScriptReportSink.Reports) != result.reports.takeIf { it.isNotEmpty() }) {
|
||||
reporter.attachReports(file, result.reports)
|
||||
reporter.attachReports(file, result.reports.mapToLegacyReports())
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveDependencies(result: DependenciesResolver.ResolveResult, file: VirtualFile, scriptDef: KotlinScriptDefinition) {
|
||||
private fun save(compilationConfigurationResult: ScriptCompilationConfigurationResult?, file: VirtualFile) {
|
||||
if (shouldShowNotification()) {
|
||||
file.removeScriptDependenciesNotificationPanel(project)
|
||||
}
|
||||
|
||||
val dependencies = result.dependencies?.adjustByDefinition(scriptDef) ?: return
|
||||
saveToCache(file, dependencies)
|
||||
if (compilationConfigurationResult != null) {
|
||||
saveToCache(file, compilationConfigurationResult)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun saveToCache(file: VirtualFile, dependencies: ScriptDependencies) {
|
||||
val rootsChanged = cache.hasNotCachedRoots(dependencies)
|
||||
if (cache.save(file, dependencies)) {
|
||||
protected fun saveToCache(
|
||||
file: VirtualFile, compilationConfigurationResult: ScriptCompilationConfigurationResult, skipSaveToAttributes: Boolean = false
|
||||
) {
|
||||
val rootsChanged = compilationConfigurationResult.valueOrNull()?.let { cache.hasNotCachedRoots(it) } ?: false
|
||||
if (cache.save(file, compilationConfigurationResult)
|
||||
&& !skipSaveToAttributes
|
||||
&& compilationConfigurationResult is ResultWithDiagnostics.Success
|
||||
) {
|
||||
debug(file) {
|
||||
"dependencies were saved to file attributes: dependencies = $dependencies"
|
||||
"refined configuration is saved to file attributes: $compilationConfigurationResult"
|
||||
}
|
||||
file.scriptDependencies = dependencies
|
||||
if (compilationConfigurationResult.value is ScriptCompilationConfigurationWrapper.FromLegacy)
|
||||
file.scriptDependencies = compilationConfigurationResult.value.legacyDependencies
|
||||
else
|
||||
file.scriptCompilationConfiguration = compilationConfigurationResult.value.configuration
|
||||
}
|
||||
|
||||
if (rootsChanged) {
|
||||
@@ -101,6 +110,7 @@ abstract class ScriptDependenciesLoader(protected val project: Project) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
open fun notifyRootsChanged(): Boolean = submitMakeRootsChange()
|
||||
|
||||
protected fun submitMakeRootsChange(): Boolean {
|
||||
|
||||
+7
-5
@@ -7,21 +7,23 @@ package org.jetbrains.kotlin.idea.core.script.dependencies
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesUpdater
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
|
||||
import org.jetbrains.kotlin.scripting.resolve.refineScriptCompilationConfiguration
|
||||
|
||||
class SyncScriptDependenciesLoader internal constructor(project: Project) : ScriptDependenciesLoader(project) {
|
||||
class SyncScriptDependenciesLoader(project: Project) : ScriptDependenciesLoader(project) {
|
||||
override fun isApplicable(file: VirtualFile): Boolean {
|
||||
val scriptDefinition = file.findScriptDefinition(project) ?: return false
|
||||
return !ScriptDependenciesUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition)
|
||||
return !ScriptsCompilationConfigurationUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition)
|
||||
}
|
||||
|
||||
override fun loadDependencies(file: VirtualFile) {
|
||||
val scriptDef = file.findScriptDefinition(project) ?: return
|
||||
debug(file) { "start sync dependencies loading" }
|
||||
val result = contentLoader.loadContentsAndResolveDependencies(scriptDef, file)
|
||||
val result = refineScriptCompilationConfiguration(VirtualFileScriptSource(file), scriptDef, project)
|
||||
debug(file) { "finish sync dependencies loading" }
|
||||
processResult(result, file, scriptDef)
|
||||
processRefinedConfiguration(result, file)
|
||||
}
|
||||
|
||||
override fun shouldShowNotification(): Boolean = false
|
||||
|
||||
+7
-7
@@ -14,7 +14,7 @@ import com.intellij.util.addOptionTag
|
||||
import com.intellij.util.attribute
|
||||
import com.intellij.util.getAttributeBooleanValue
|
||||
import org.jdom.Element
|
||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
|
||||
@State(
|
||||
name = "KotlinScriptingSettings",
|
||||
@@ -72,22 +72,22 @@ class KotlinScriptingSettings : PersistentStateComponent<Element> {
|
||||
}
|
||||
}
|
||||
|
||||
fun setOrder(scriptDefinition: KotlinScriptDefinition, order: Int) {
|
||||
fun setOrder(scriptDefinition: ScriptDefinition, order: Int) {
|
||||
scriptDefinitions[scriptDefinition.toKey()] = scriptDefinitions[scriptDefinition.toKey()]?.copy(order = order) ?:
|
||||
KotlinScriptDefinitionValue(order)
|
||||
}
|
||||
|
||||
|
||||
fun setEnabled(scriptDefinition: KotlinScriptDefinition, isEnabled: Boolean) {
|
||||
fun setEnabled(scriptDefinition: ScriptDefinition, isEnabled: Boolean) {
|
||||
scriptDefinitions[scriptDefinition.toKey()] = scriptDefinitions[scriptDefinition.toKey()]?.copy(isEnabled = isEnabled) ?:
|
||||
KotlinScriptDefinitionValue(scriptDefinitions.size, isEnabled)
|
||||
}
|
||||
|
||||
fun getScriptDefinitionOrder(scriptDefinition: KotlinScriptDefinition): Int {
|
||||
fun getScriptDefinitionOrder(scriptDefinition: ScriptDefinition): Int {
|
||||
return scriptDefinitions[scriptDefinition.toKey()]?.order ?: Integer.MAX_VALUE
|
||||
}
|
||||
|
||||
fun isScriptDefinitionEnabled(scriptDefinition: KotlinScriptDefinition): Boolean {
|
||||
fun isScriptDefinitionEnabled(scriptDefinition: ScriptDefinition): Boolean {
|
||||
return scriptDefinitions[scriptDefinition.toKey()]?.isEnabled ?: true
|
||||
}
|
||||
|
||||
@@ -99,8 +99,8 @@ class KotlinScriptingSettings : PersistentStateComponent<Element> {
|
||||
getAttributeValue(KotlinScriptDefinitionKey::className.name)
|
||||
)
|
||||
|
||||
private fun KotlinScriptDefinition.toKey() =
|
||||
KotlinScriptDefinitionKey(this.name, this::class.qualifiedName ?: "unknown")
|
||||
private fun ScriptDefinition.toKey() =
|
||||
KotlinScriptDefinitionKey(this.name, this.definitionId)
|
||||
|
||||
private fun Element.addScriptDefinitionContentElement(definition: KotlinScriptDefinitionKey, settings: KotlinScriptDefinitionValue) {
|
||||
addElement(SCRIPT_DEFINITION_TAG).apply {
|
||||
|
||||
Reference in New Issue
Block a user