Refactor getting dependencies for script files API

Deprecate KotlinScriptDefinition#getDependenciesFor
Expose dependencyResolver as a property
KotlinScriptExternalImportsProvider is the component clients should be asking for dependencies
This commit is contained in:
Pavel V. Talanov
2017-06-19 19:59:39 +03:00
parent ff391628de
commit 788eaf8a18
11 changed files with 132 additions and 101 deletions
@@ -463,7 +463,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
messageCollector.report( messageCollector.report(
INFO, INFO,
"Added script definition $template to configuration: files pattern = \"${def.scriptFilePattern}\", " + "Added script definition $template to configuration: files pattern = \"${def.scriptFilePattern}\", " +
"resolver = ${def.resolver?.javaClass?.name}" "resolver = ${def.dependencyResolver.javaClass.name}"
) )
} }
catch (ex: ClassNotFoundException) { catch (ex: ClassNotFoundException) {
@@ -187,7 +187,7 @@ class KotlinCoreEnvironment private constructor(
KotlinScriptExternalImportsProvider.getInstance(project)?.let { importsProvider -> KotlinScriptExternalImportsProvider.getInstance(project)?.let { importsProvider ->
configuration.addJvmClasspathRoots( configuration.addJvmClasspathRoots(
sourceFiles.mapNotNull(importsProvider::getExternalImports) sourceFiles.mapNotNull(importsProvider::getScriptDependencies)
.flatMap { it.classpath } .flatMap { it.classpath }
.distinctBy { it.absolutePath }) .distinctBy { it.absolutePath })
} }
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.psi.KtScriptInitializer
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.script.KotlinScriptDefinition import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.KotlinScriptExternalImportsProvider
import java.io.File import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.write import kotlin.concurrent.write
@@ -67,7 +68,7 @@ open class GenericReplCompiler(disposable: Disposable,
Pair(compilerState.lastLineState!!.psiFile, compilerState.lastLineState!!.errorHolder) Pair(compilerState.lastLineState!!.psiFile, compilerState.lastLineState!!.errorHolder)
} }
val newDependencies = scriptDefinition.getDependenciesFor(psiFile, checker.environment.project, compilerState.lastDependencies) val newDependencies = KotlinScriptExternalImportsProvider.getInstance(checker.environment.project).getScriptDependencies(psiFile)
var classpathAddendum: List<File>? = null var classpathAddendum: List<File>? = null
if (compilerState.lastDependencies != newDependencies) { if (compilerState.lastDependencies != newDependencies) {
compilerState.lastDependencies = newDependencies compilerState.lastDependencies = newDependencies
@@ -66,7 +66,7 @@ open class KotlinJvmReplService(
val cls = classloader.loadClass(templateClassName) val cls = classloader.loadClass(templateClassName)
val def = KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, null, null, emptyMap()) val def = KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, null, null, emptyMap())
messageCollector.report(INFO, "New script definition $templateClassName: files pattern = \"${def.scriptFilePattern}\", " + messageCollector.report(INFO, "New script definition $templateClassName: files pattern = \"${def.scriptFilePattern}\", " +
"resolver = ${def.resolver?.javaClass?.name}") "resolver = ${def.dependencyResolver.javaClass.name}")
return def return def
} }
catch (ex: ClassNotFoundException) { catch (ex: ClassNotFoundException) {
@@ -18,17 +18,10 @@ package org.jetbrains.kotlin.script
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.NameUtils import org.jetbrains.kotlin.name.NameUtils
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KFunction import kotlin.reflect.KFunction
import kotlin.reflect.KParameter import kotlin.reflect.KParameter
@@ -54,7 +47,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
?: DEFAULT_SCRIPT_FILE_PATTERN ?: DEFAULT_SCRIPT_FILE_PATTERN
} }
val resolver: ScriptDependenciesResolver? by lazy { override val dependencyResolver: ScriptDependenciesResolver by lazy {
val defAnn by lazy { takeUnlessError { template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>() } } val defAnn by lazy { takeUnlessError { template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>() } }
val legacyDefAnn by lazy { takeUnlessError { template.annotations.firstIsInstanceOrNull<org.jetbrains.kotlin.script.ScriptTemplateDefinition>() } } val legacyDefAnn by lazy { takeUnlessError { template.annotations.firstIsInstanceOrNull<org.jetbrains.kotlin.script.ScriptTemplateDefinition>() } }
when { when {
@@ -84,8 +77,8 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
log.warn("[kts] Script def error ${ex.message}") log.warn("[kts] Script def error ${ex.message}")
null null
} }
else -> BasicScriptDependenciesResolver() else -> null
} } ?: BasicScriptDependenciesResolver()
} }
val samWithReceiverAnnotations: List<String>? by lazy { val samWithReceiverAnnotations: List<String>? by lazy {
@@ -93,7 +86,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
?: takeUnlessError { template.annotations.firstIsInstanceOrNull<org.jetbrains.kotlin.script.SamWithReceiverAnnotations>()?.annotations?.toList() } ?: takeUnlessError { template.annotations.firstIsInstanceOrNull<org.jetbrains.kotlin.script.SamWithReceiverAnnotations>()?.annotations?.toList() }
} }
private val acceptedAnnotations: List<KClass<out Annotation>> by lazy { override val acceptedAnnotations: List<KClass<out Annotation>> by lazy {
fun sameSignature(left: KFunction<*>, right: KFunction<*>): Boolean = fun sameSignature(left: KFunction<*>, right: KFunction<*>): Boolean =
left.parameters.size == right.parameters.size && left.parameters.size == right.parameters.size &&
@@ -104,7 +97,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
val resolveMethod = ScriptDependenciesResolver::resolve val resolveMethod = ScriptDependenciesResolver::resolve
val resolverMethodAnnotations = val resolverMethodAnnotations =
resolver?.let { it::class }?.memberFunctions?.find { function -> dependencyResolver::class.memberFunctions.find { function ->
function.name == resolveMethod.name && function.name == resolveMethod.name &&
sameSignature(function, resolveMethod) sameSignature(function, resolveMethod)
}?.annotations?.filterIsInstance<AcceptedAnnotations>() }?.annotations?.filterIsInstance<AcceptedAnnotations>()
@@ -122,62 +115,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
// TODO: implement other strategy - e.g. try to extract something from match with ScriptFilePattern // TODO: implement other strategy - e.g. try to extract something from match with ScriptFilePattern
override fun getScriptName(script: KtScript): Name = NameUtils.getScriptNameForFile(script.containingKtFile.name) override fun getScriptName(script: KtScript): Name = NameUtils.getScriptNameForFile(script.containingKtFile.name)
override fun <TF: Any> getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?): KotlinScriptExternalDependencies? { override fun <TF: Any> getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?) = error("Should not be called")
return takeUnlessError(reportError = false) {
val fileDeps = resolver?.resolve(makeScriptContents(file, project), environment, ::logScriptDefMessage, previousDependencies)
// TODO: use it as a Future
fileDeps?.get()
}
}
fun <TF: Any> makeScriptContents(file: TF, project: Project) = BasicScriptContents(file, getAnnotations = {
val classLoader = template.java.classLoader
takeUnlessError(reportError = false) {
getAnnotationEntries(file, project)
.mapNotNull { psiAnn ->
// TODO: consider advanced matching using semantic similar to actual resolving
acceptedAnnotations.find { ann ->
psiAnn.typeName.let { it == ann.simpleName || it == ann.qualifiedName }
}?.let { constructAnnotation(psiAnn, classLoader.loadClass(it.qualifiedName).kotlin as KClass<out Annotation>) }
}
}
?: emptyList()
})
fun getDependenciesFor(previousDependencies: KotlinScriptExternalDependencies?, scriptContents: ScriptContents): KotlinScriptExternalDependencies? {
return takeUnlessError(reportError = false) {
// TODO: use it as a Future
resolver?.resolve(scriptContents, environment, ::logScriptDefMessage, previousDependencies)?.get()
}
}
private fun <TF: Any> getAnnotationEntries(file: TF, project: Project): Iterable<KtAnnotationEntry> = when (file) {
is PsiFile -> getAnnotationEntriesFromPsiFile(file)
is VirtualFile -> getAnnotationEntriesFromVirtualFile(file, project)
is File -> {
val virtualFile = (StandardFileSystems.local().findFileByPath(file.canonicalPath)
?: throw IllegalArgumentException("Unable to find file ${file.canonicalPath}"))
getAnnotationEntriesFromVirtualFile(virtualFile, project)
}
else -> throw IllegalArgumentException("Unsupported file type $file")
}
private fun getAnnotationEntriesFromPsiFile(file: PsiFile) =
(file as? KtFile)?.annotationEntries
?: throw IllegalArgumentException("Unable to extract kotlin annotations from ${file.name} (${file.fileType})")
private fun getAnnotationEntriesFromVirtualFile(file: VirtualFile, project: Project): Iterable<KtAnnotationEntry> {
val psiFile: PsiFile = PsiManager.getInstance(project).findFile(file)
?: throw IllegalArgumentException("Unable to load PSI from ${file.canonicalPath}")
return getAnnotationEntriesFromPsiFile(psiFile)
}
class BasicScriptContents<out TF: Any>(myFile: TF, getAnnotations: () -> Iterable<Annotation>) : ScriptContents {
override val file: File? by lazy { getFile(myFile) }
override val annotations: Iterable<Annotation> by lazy { getAnnotations() }
override val text: CharSequence? by lazy { getFileContents(myFile) }
}
override fun toString(): String = "KotlinScriptDefinitionFromAnnotatedTemplate - ${template.simpleName}" override fun toString(): String = "KotlinScriptDefinitionFromAnnotatedTemplate - ${template.simpleName}"
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.script
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtFile
import java.io.File
import kotlin.reflect.KClass
import kotlin.script.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.ScriptContents
abstract class KotlinScriptExternalImportsProviderBase(private val project: Project): KotlinScriptExternalImportsProvider {
fun <TF : Any> getScriptContents(scriptDefinition: KotlinScriptDefinition, file: TF)
= BasicScriptContents(file, getAnnotations = { loadAnnotations(scriptDefinition, file) })
private fun <TF : Any> loadAnnotations(scriptDefinition: KotlinScriptDefinition, file: TF): List<Annotation> {
val classLoader = scriptDefinition.template.java.classLoader
// TODO_R: report error on failure to load annotation class
return getAnnotationEntries(file, project)
.mapNotNull { psiAnn ->
// TODO: consider advanced matching using semantic similar to actual resolving
scriptDefinition.acceptedAnnotations.find { ann ->
psiAnn.typeName.let { it == ann.simpleName || it == ann.qualifiedName }
}?.let { constructAnnotation(psiAnn, classLoader.loadClass(it.qualifiedName).kotlin as KClass<out Annotation>) }
}
}
private fun <TF: Any> getAnnotationEntries(file: TF, project: Project): Iterable<KtAnnotationEntry> = when (file) {
is PsiFile -> getAnnotationEntriesFromPsiFile(file)
is VirtualFile -> getAnnotationEntriesFromVirtualFile(file, project)
is File -> {
val virtualFile = (StandardFileSystems.local().findFileByPath(file.canonicalPath)
?: throw IllegalArgumentException("Unable to find file ${file.canonicalPath}"))
getAnnotationEntriesFromVirtualFile(virtualFile, project)
}
else -> throw IllegalArgumentException("Unsupported file type $file")
}
private fun getAnnotationEntriesFromPsiFile(file: PsiFile) =
(file as? KtFile)?.annotationEntries
?: throw IllegalArgumentException("Unable to extract kotlin annotations from ${file.name} (${file.fileType})")
private fun getAnnotationEntriesFromVirtualFile(file: VirtualFile, project: Project): Iterable<KtAnnotationEntry> {
val psiFile: PsiFile = PsiManager.getInstance(project).findFile(file)
?: throw IllegalArgumentException("Unable to load PSI from ${file.canonicalPath}")
return getAnnotationEntriesFromPsiFile(psiFile)
}
class BasicScriptContents<out TF: Any>(myFile: TF, getAnnotations: () -> Iterable<Annotation>) : ScriptContents {
override val file: File? by lazy { getFile(myFile) }
override val annotations: Iterable<Annotation> by lazy { getAnnotations() }
override val text: CharSequence? by lazy { getFileContents(myFile) }
}
fun <TF: Any> resolveDependencies(
scriptDef: KotlinScriptDefinition,
file: TF,
oldDependencies: KotlinScriptExternalDependencies? = null
): KotlinScriptExternalDependencies? {
val scriptContents = getScriptContents(scriptDef, file)
return scriptDef.dependencyResolver.resolve(
scriptContents,
(scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.environment,
{ _, _, _ -> Unit }, // TODO_R:
oldDependencies
).get()
}
}
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.script package org.jetbrains.kotlin.script
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import java.io.File import java.io.File
@@ -28,22 +27,24 @@ import kotlin.script.dependencies.KotlinScriptExternalDependencies
class KotlinScriptExternalImportsProviderImpl( class KotlinScriptExternalImportsProviderImpl(
val project: Project, val project: Project,
private val scriptDefinitionProvider: KotlinScriptDefinitionProvider private val scriptDefinitionProvider: KotlinScriptDefinitionProvider
) : KotlinScriptExternalImportsProvider { ) : KotlinScriptExternalImportsProviderBase(project) {
private val cacheLock = ReentrantReadWriteLock() private val cacheLock = ReentrantReadWriteLock()
private val cache = hashMapOf<String, KotlinScriptExternalDependencies?>() private val cache = hashMapOf<String, KotlinScriptExternalDependencies?>()
override fun <TF: Any> getExternalImports(file: TF): KotlinScriptExternalDependencies? = cacheLock.read { override fun <TF : Any> getScriptDependencies(file: TF): KotlinScriptExternalDependencies? = cacheLock.read {
calculateExternalDependencies(file) calculateExternalDependencies(file)
} }
private fun <TF: Any> calculateExternalDependencies(file: TF): KotlinScriptExternalDependencies? { private fun <TF : Any> calculateExternalDependencies(file: TF): KotlinScriptExternalDependencies? {
val path = getFilePath(file) val path = getFilePath(file)
val cached = cache[path] val cached = cache[path]
return if (cached != null) cached else { return if (cached != null) cached
else {
val scriptDef = scriptDefinitionProvider.findScriptDefinition(file) val scriptDef = scriptDefinitionProvider.findScriptDefinition(file)
if (scriptDef != null) { if (scriptDef != null) {
val deps = scriptDef.getDependenciesFor(file, project, null) val deps = resolveDependencies(scriptDef, file)
if (deps != null) { if (deps != null) {
log.info("[kts] new cached deps for $path: ${deps.classpath.joinToString(File.pathSeparator)}") log.info("[kts] new cached deps for $path: ${deps.classpath.joinToString(File.pathSeparator)}")
} }
@@ -55,11 +56,6 @@ class KotlinScriptExternalImportsProviderImpl(
else null else null
} }
} }
companion object {
@JvmStatic
fun getInstance(project: Project): KotlinScriptExternalImportsProvider? =
ServiceManager.getService(project, KotlinScriptExternalImportsProvider::class.java)
internal val log = Logger.getInstance(KotlinScriptExternalImportsProvider::class.java)
}
} }
private val log = Logger.getInstance(KotlinScriptExternalImportsProvider::class.java)
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.psi.KtScript import org.jetbrains.kotlin.psi.KtScript
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.script.dependencies.KotlinScriptExternalDependencies import kotlin.script.dependencies.KotlinScriptExternalDependencies
import kotlin.script.dependencies.ScriptDependenciesResolver
import kotlin.script.templates.standard.ScriptTemplateWithArgs import kotlin.script.templates.standard.ScriptTemplateWithArgs
open class KotlinScriptDefinition(val template: KClass<out Any>) { open class KotlinScriptDefinition(val template: KClass<out Any>) {
@@ -37,13 +38,20 @@ open class KotlinScriptDefinition(val template: KClass<out Any>) {
open val annotationsForSamWithReceivers: List<String> open val annotationsForSamWithReceivers: List<String>
get() = emptyList() get() = emptyList()
open fun <TF: Any> isScript(file: TF): Boolean = open fun <TF : Any> isScript(file: TF): Boolean =
getFileName(file).endsWith(KotlinParserDefinition.STD_SCRIPT_EXT) getFileName(file).endsWith(KotlinParserDefinition.STD_SCRIPT_EXT)
open fun getScriptName(script: KtScript): Name = open fun getScriptName(script: KtScript): Name =
NameUtils.getScriptNameForFile(script.containingKtFile.name) NameUtils.getScriptNameForFile(script.containingKtFile.name)
open fun <TF: Any> getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?): KotlinScriptExternalDependencies? = null @Deprecated("Use dependencyResolver instead", level = DeprecationLevel.ERROR)
open fun <TF : Any> getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?): KotlinScriptExternalDependencies? = null
open val dependencyResolver: ScriptDependenciesResolver = EmptyDependencyResolver
open val acceptedAnnotations: List<KClass<out Annotation>> = emptyList()
private object EmptyDependencyResolver : ScriptDependenciesResolver
} }
object StandardScriptDefinition : KotlinScriptDefinition(ScriptTemplateWithArgs::class) object StandardScriptDefinition : KotlinScriptDefinition(ScriptTemplateWithArgs::class)
@@ -22,7 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile
import kotlin.script.dependencies.KotlinScriptExternalDependencies import kotlin.script.dependencies.KotlinScriptExternalDependencies
interface KotlinScriptExternalImportsProvider { interface KotlinScriptExternalImportsProvider {
fun <TF: Any> getExternalImports(file: TF): KotlinScriptExternalDependencies? fun <TF: Any> getScriptDependencies(file: TF): KotlinScriptExternalDependencies?
companion object { companion object {
fun getInstance(project: Project): KotlinScriptExternalImportsProvider = fun getInstance(project: Project): KotlinScriptExternalImportsProvider =
@@ -31,4 +31,4 @@ interface KotlinScriptExternalImportsProvider {
} }
fun getScriptExternalDependencies(file: VirtualFile, project: Project): KotlinScriptExternalDependencies? = fun getScriptExternalDependencies(file: VirtualFile, project: Project): KotlinScriptExternalDependencies? =
KotlinScriptExternalImportsProvider.getInstance(project).getExternalImports(file) KotlinScriptExternalImportsProvider.getInstance(project).getScriptDependencies(file)
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.idea.core.script.KotlinScriptConfigurationManager
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.script.KotlinScriptDefinition import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.KotlinScriptExternalImportsProvider
import kotlin.script.dependencies.KotlinScriptExternalDependencies import kotlin.script.dependencies.KotlinScriptExternalDependencies
class ScriptModuleSearchScope(val scriptFile: VirtualFile, baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope) { class ScriptModuleSearchScope(val scriptFile: VirtualFile, baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope) {
@@ -44,7 +43,7 @@ data class ScriptModuleInfo(val project: Project, val scriptFile: VirtualFile,
get() = ModuleOrigin.OTHER get() = ModuleOrigin.OTHER
val externalDependencies: KotlinScriptExternalDependencies? val externalDependencies: KotlinScriptExternalDependencies?
get() = KotlinScriptConfigurationManager.getInstance(project).getExternalImports(scriptFile) get() = KotlinScriptConfigurationManager.getInstance(project).getScriptDependencies(scriptFile)
override val name: Name = Name.special("<script ${scriptFile.name} ${scriptDefinition.name}>") override val name: Name = Name.special("<script ${scriptFile.name} ${scriptDefinition.name}>")
@@ -49,19 +49,20 @@ import kotlin.concurrent.write
import kotlin.script.dependencies.KotlinScriptExternalDependencies import kotlin.script.dependencies.KotlinScriptExternalDependencies
// NOTE: this service exists exclusively because KotlinScriptConfigurationManager
// cannot be registered as implementing two services (state would be duplicated)
class IdeScriptExternalImportsProvider( class IdeScriptExternalImportsProvider(
private val scriptConfigurationManager: KotlinScriptConfigurationManager private val scriptConfigurationManager: KotlinScriptConfigurationManager
) : KotlinScriptExternalImportsProvider { ) : KotlinScriptExternalImportsProvider {
override fun <TF : Any> getExternalImports(file: TF): KotlinScriptExternalDependencies? { override fun <TF : Any> getScriptDependencies(file: TF): KotlinScriptExternalDependencies? {
return scriptConfigurationManager.getExternalImports(file) return scriptConfigurationManager.getScriptDependencies(file)
} }
} }
class KotlinScriptConfigurationManager( class KotlinScriptConfigurationManager(
private val project: Project, private val project: Project,
private val scriptDefinitionProvider: KotlinScriptDefinitionProvider private val scriptDefinitionProvider: KotlinScriptDefinitionProvider
) { ) : KotlinScriptExternalImportsProviderBase(project) {
private val cacheLock = ReentrantReadWriteLock() private val cacheLock = ReentrantReadWriteLock()
private val threadPool = newFixedThreadPool(max(1, Runtime.getRuntime().availableProcessors() / 2)) private val threadPool = newFixedThreadPool(max(1, Runtime.getRuntime().availableProcessors() / 2))
@@ -128,7 +129,7 @@ class KotlinScriptConfigurationManager(
} }
} }
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> = toVfsRoots(getExternalImports(file).classpath) fun getScriptClasspath(file: VirtualFile): List<VirtualFile> = toVfsRoots(getScriptDependencies(file).classpath)
fun getAllScriptsClasspath(): List<VirtualFile> = allScriptsClasspathCache.get() fun getAllScriptsClasspath(): List<VirtualFile> = allScriptsClasspathCache.get()
@@ -153,7 +154,7 @@ class KotlinScriptConfigurationManager(
private val cache = hashMapOf<String, DataAndRequest?>() private val cache = hashMapOf<String, DataAndRequest?>()
fun <TF : Any> getExternalImports(file: TF): KotlinScriptExternalDependencies = cacheLock.read { override fun <TF : Any> getScriptDependencies(file: TF): KotlinScriptExternalDependencies = cacheLock.read {
val path = getFilePath(file) val path = getFilePath(file)
val cached = cache[path] val cached = cache[path]
cached?.dependencies?.let { return it } cached?.dependencies?.let { return it }
@@ -222,9 +223,9 @@ class KotlinScriptConfigurationManager(
oldDataAndRequest: DataAndRequest? oldDataAndRequest: DataAndRequest?
): Pair<TimeStamp, CompletableFuture<Unit>> { ): Pair<TimeStamp, CompletableFuture<Unit>> {
val currentTimeStamp = TimeStamps.next() val currentTimeStamp = TimeStamps.next()
val scriptContents = scriptDefinition.makeScriptContents(file, project)
val newFuture = supplyAsync(Supplier { val newFuture = supplyAsync(Supplier {
val newDependencies = scriptDefinition.getDependenciesFor(oldDataAndRequest?.dependencies, scriptContents) ?: EmptyDependencies val newDependencies = resolveDependencies(scriptDefinition, file, oldDataAndRequest?.dependencies) ?: EmptyDependencies
cacheLock.read { cacheLock.read {
val lastTimeStamp = cache[path]?.requestInProgress?.timeStamp val lastTimeStamp = cache[path]?.requestInProgress?.timeStamp
if (lastTimeStamp == currentTimeStamp) { if (lastTimeStamp == currentTimeStamp) {
@@ -254,8 +255,8 @@ class KotlinScriptConfigurationManager(
private fun <TF : Any> updateSync(file: TF, scriptDef: KotlinScriptDefinition): Boolean { private fun <TF : Any> updateSync(file: TF, scriptDef: KotlinScriptDefinition): Boolean {
val path = getFilePath(file) val path = getFilePath(file)
val oldDeps = cache[path]?.dependencies val oldDeps = cache[path]?.dependencies
val deps = scriptDef.getDependenciesFor(file, project, oldDeps) ?: EmptyDependencies val newDeps = resolveDependencies(scriptDef, file, oldDeps) ?: EmptyDependencies
return cacheSync(deps, oldDeps, path, file as? VirtualFile) return cacheSync(newDeps, oldDeps, path, file as? VirtualFile)
} }
private fun cacheSync( private fun cacheSync(