Refactor script dependencies management
This commit is contained in:
committed by
Pavel V. Talanov
parent
c244414f3c
commit
082290f8e3
@@ -149,15 +149,9 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
?: emptyList()
|
?: emptyList()
|
||||||
})
|
})
|
||||||
|
|
||||||
KotlinScriptExtraImportsProvider.getInstance(project)?.run {
|
KotlinScriptExternalImportsProvider.getInstance(project)?.run {
|
||||||
configuration.getMap(JVMConfigurationKeys.SCRIPTS_EXTRA_IMPORTS).entries.forEach {
|
|
||||||
val localFile = applicationEnvironment.localFileSystem.findFileByPath(it.key)
|
|
||||||
?: throw FileNotFoundException("Unable to find target for extra imports: ${it.key}")
|
|
||||||
preconfigureExtraImports(localFile, kotlin.collections.listOf(it.value))
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration.addJvmClasspathRoots(
|
configuration.addJvmClasspathRoots(
|
||||||
getCombinedClasspathFor(sourceFiles.mapNotNull { src -> src.virtualFile })
|
getCombinedClasspathFor(sourceFiles)
|
||||||
.map { File(it).canonicalFile }
|
.map { File(it).canonicalFile }
|
||||||
.distinct())
|
.distinct())
|
||||||
}
|
}
|
||||||
@@ -416,7 +410,7 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
with (projectEnvironment.project) {
|
with (projectEnvironment.project) {
|
||||||
val kotlinScriptDefinitionProvider = KotlinScriptDefinitionProvider()
|
val kotlinScriptDefinitionProvider = KotlinScriptDefinitionProvider()
|
||||||
registerService(KotlinScriptDefinitionProvider::class.java, kotlinScriptDefinitionProvider)
|
registerService(KotlinScriptDefinitionProvider::class.java, kotlinScriptDefinitionProvider)
|
||||||
registerService(KotlinScriptExtraImportsProvider::class.java, KotlinScriptExtraImportsProvider(projectEnvironment.project, kotlinScriptDefinitionProvider))
|
registerService(KotlinScriptExternalImportsProvider::class.java, KotlinScriptExternalImportsProvider(projectEnvironment.project, kotlinScriptDefinitionProvider))
|
||||||
registerService(KotlinJavaPsiFacade::class.java, KotlinJavaPsiFacade(this))
|
registerService(KotlinJavaPsiFacade::class.java, KotlinJavaPsiFacade(this))
|
||||||
registerService(KtLightClassForFacade.FacadeStubCache::class.java, KtLightClassForFacade.FacadeStubCache(this))
|
registerService(KtLightClassForFacade.FacadeStubCache::class.java, KtLightClassForFacade.FacadeStubCache(this))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,9 @@ package org.jetbrains.kotlin.config;
|
|||||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents;
|
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents;
|
||||||
import org.jetbrains.kotlin.modules.Module;
|
import org.jetbrains.kotlin.modules.Module;
|
||||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition;
|
import org.jetbrains.kotlin.script.KotlinScriptDefinition;
|
||||||
import org.jetbrains.kotlin.script.KotlinScriptExtraImport;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class JVMConfigurationKeys {
|
public class JVMConfigurationKeys {
|
||||||
private JVMConfigurationKeys() {
|
private JVMConfigurationKeys() {
|
||||||
@@ -43,9 +41,6 @@ public class JVMConfigurationKeys {
|
|||||||
public static final CompilerConfigurationKey<List<KotlinScriptDefinition>> SCRIPT_DEFINITIONS =
|
public static final CompilerConfigurationKey<List<KotlinScriptDefinition>> SCRIPT_DEFINITIONS =
|
||||||
CompilerConfigurationKey.create("script definitions");
|
CompilerConfigurationKey.create("script definitions");
|
||||||
|
|
||||||
public static final CompilerConfigurationKey<Map<String, KotlinScriptExtraImport>> SCRIPTS_EXTRA_IMPORTS =
|
|
||||||
CompilerConfigurationKey.create("scripts extra imports");
|
|
||||||
|
|
||||||
public static final CompilerConfigurationKey<Boolean> DISABLE_CALL_ASSERTIONS =
|
public static final CompilerConfigurationKey<Boolean> DISABLE_CALL_ASSERTIONS =
|
||||||
CompilerConfigurationKey.create("disable not-null call assertions");
|
CompilerConfigurationKey.create("disable not-null call assertions");
|
||||||
public static final CompilerConfigurationKey<Boolean> DISABLE_PARAM_ASSERTIONS =
|
public static final CompilerConfigurationKey<Boolean> DISABLE_PARAM_ASSERTIONS =
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import org.jetbrains.kotlin.resolve.scopes.ImportingScope
|
|||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||||
import org.jetbrains.kotlin.resolve.scopes.SubpackagesImportingScope
|
import org.jetbrains.kotlin.resolve.scopes.SubpackagesImportingScope
|
||||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||||
import org.jetbrains.kotlin.script.getScriptExtraImports
|
import org.jetbrains.kotlin.script.getScriptExternalDependencies
|
||||||
import org.jetbrains.kotlin.storage.StorageManager
|
import org.jetbrains.kotlin.storage.StorageManager
|
||||||
import org.jetbrains.kotlin.storage.getValue
|
import org.jetbrains.kotlin.storage.getValue
|
||||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||||
@@ -73,7 +73,7 @@ class FileScopeFactory(
|
|||||||
val explicitImportResolver = createImportResolver(ExplicitImportsIndexed(imports), bindingTrace)
|
val explicitImportResolver = createImportResolver(ExplicitImportsIndexed(imports), bindingTrace)
|
||||||
val allUnderImportResolver = createImportResolver(AllUnderImportsIndexed(imports), bindingTrace)
|
val allUnderImportResolver = createImportResolver(AllUnderImportsIndexed(imports), bindingTrace)
|
||||||
|
|
||||||
val extraImports = ktImportsFactory.createImportDirectives(getScriptExtraImports(file).flatMap { it.names.map { ImportPath(it) } })
|
val extraImports = ktImportsFactory.createImportDirectives(getScriptExternalDependencies(file).flatMap { it.imports.map { ImportPath(it) } })
|
||||||
val allImplicitImports = defaultImports + extraImports
|
val allImplicitImports = defaultImports + extraImports
|
||||||
|
|
||||||
val defaultImportsFiltered = if (aliasImportNames.isEmpty()) { // optimization
|
val defaultImportsFiltered = if (aliasImportNames.isEmpty()) { // optimization
|
||||||
|
|||||||
+19
-10
@@ -17,14 +17,6 @@
|
|||||||
package org.jetbrains.kotlin.script
|
package org.jetbrains.kotlin.script
|
||||||
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
|
||||||
import com.intellij.util.xmlb.XmlSerializer
|
|
||||||
import com.intellij.util.xmlb.annotations.AbstractCollection
|
|
||||||
import com.intellij.util.xmlb.annotations.Tag
|
|
||||||
import org.jdom.Document
|
|
||||||
import org.jdom.Element
|
|
||||||
import org.jdom.output.Format
|
|
||||||
import org.jdom.output.XMLOutputter
|
|
||||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
||||||
@@ -54,7 +46,24 @@ data class KotlinConfigurableScriptDefinition(val config: KotlinScriptConfig, va
|
|||||||
|
|
||||||
private val evaluatedClasspath by lazy { config.classpath.evalWithVars(environmentVars).distinct() }
|
private val evaluatedClasspath by lazy { config.classpath.evalWithVars(environmentVars).distinct() }
|
||||||
|
|
||||||
override fun getScriptDependenciesClasspath(): List<String> = evaluatedClasspath
|
override fun <TF> getDependenciesFor(file: TF, project: Project): KotlinScriptExternalDependencies? =
|
||||||
|
if (!isScript(file)) null
|
||||||
|
else {
|
||||||
|
val extDeps = getScriptDependenciesFromConfig(file)
|
||||||
|
when {
|
||||||
|
extDeps != null ->
|
||||||
|
object : KotlinScriptExternalDependencies {
|
||||||
|
override val classpath = evaluatedClasspath + extDeps.classpath.evalWithVars(environmentVars).distinct()
|
||||||
|
override val imports = extDeps.imports
|
||||||
|
override val sources = extDeps.sources.evalWithVars(environmentVars).distinct()
|
||||||
|
}
|
||||||
|
!evaluatedClasspath.isEmpty() ->
|
||||||
|
object : KotlinScriptExternalDependencies {
|
||||||
|
override val classpath = evaluatedClasspath
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -62,7 +71,7 @@ data class KotlinConfigurableScriptDefinition(val config: KotlinScriptConfig, va
|
|||||||
// if corresponding list of replacements is empty, all strings containing the reference to the var are removed
|
// if corresponding list of replacements is empty, all strings containing the reference to the var are removed
|
||||||
// TODO: fix and tests
|
// TODO: fix and tests
|
||||||
// TODO: move to some utils
|
// TODO: move to some utils
|
||||||
internal fun List<String>.evalWithVars(varsMap: Map<String, List<String>>?): List<String> =
|
internal fun Iterable<String>.evalWithVars(varsMap: Map<String, List<String>>?): Iterable<String> =
|
||||||
if (varsMap == null || varsMap.isEmpty()) this
|
if (varsMap == null || varsMap.isEmpty()) this
|
||||||
else this.flatMap { cpentry ->
|
else this.flatMap { cpentry ->
|
||||||
varsMap.entries.fold(listOf(cpentry)) { p, v ->
|
varsMap.entries.fold(listOf(cpentry)) { p, v ->
|
||||||
|
|||||||
@@ -16,10 +16,12 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.script
|
package org.jetbrains.kotlin.script
|
||||||
|
|
||||||
|
import com.intellij.openapi.fileTypes.LanguageFileType
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.util.PathUtil
|
|
||||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
@@ -36,19 +38,39 @@ import java.io.File
|
|||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
interface KotlinScriptDefinition {
|
interface KotlinScriptDefinition {
|
||||||
val name: String
|
val name: String get() = "Kotlin Script"
|
||||||
|
|
||||||
|
// TODO: consider creating separate type (subtype? for kotlin scripts)
|
||||||
|
val fileType: LanguageFileType get() = KotlinFileType.INSTANCE
|
||||||
|
|
||||||
|
fun <TF> isScript(file: TF): Boolean =
|
||||||
|
getFileName(file).endsWith(KotlinParserDefinition.STD_SCRIPT_EXT)
|
||||||
|
|
||||||
|
// TODO: replace these 3 functions with template property
|
||||||
fun getScriptParameters(scriptDescriptor: ScriptDescriptor): List<ScriptParameter>
|
fun getScriptParameters(scriptDescriptor: ScriptDescriptor): List<ScriptParameter>
|
||||||
fun getScriptSupertypes(scriptDescriptor: ScriptDescriptor): List<KotlinType> = emptyList()
|
fun getScriptSupertypes(scriptDescriptor: ScriptDescriptor): List<KotlinType> = emptyList()
|
||||||
fun getScriptParametersToPassToSuperclass(scriptDescriptor: ScriptDescriptor): List<Name> = emptyList()
|
fun getScriptParametersToPassToSuperclass(scriptDescriptor: ScriptDescriptor): List<Name> = emptyList()
|
||||||
fun <TF> isScript(file: TF): Boolean
|
|
||||||
fun getScriptName(script: KtScript): Name
|
fun getScriptName(script: KtScript): Name =
|
||||||
fun getScriptDependenciesClasspath(): List<String> = emptyList()
|
ScriptNameUtil.fileNameWithExtensionStripped(script, KotlinParserDefinition.STD_SCRIPT_EXT)
|
||||||
|
|
||||||
|
fun <TF> getDependenciesFor(file: TF, project: Project): KotlinScriptExternalDependencies? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KotlinScriptExternalDependencies {
|
||||||
|
val classpath: Iterable<String> get() = emptyList()
|
||||||
|
val imports: Iterable<String> get() = emptyList()
|
||||||
|
val sources: Iterable<String> get() = emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
class KotlinScriptExternalDependenciesUnion(val dependencies: Iterable<KotlinScriptExternalDependencies>) : KotlinScriptExternalDependencies {
|
||||||
|
override val classpath: Iterable<String> get() = dependencies.flatMap { it.classpath }
|
||||||
|
override val imports: Iterable<String> get() = dependencies.flatMap { it.imports }
|
||||||
|
override val sources: Iterable<String> get() = dependencies.flatMap { it.sources }
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ScriptParameter(val name: Name, val type: KotlinType)
|
data class ScriptParameter(val name: Name, val type: KotlinType)
|
||||||
|
|
||||||
fun <TF> getFileExtension(file: TF) = PathUtil.getFileExtension(getFileName(file))
|
|
||||||
|
|
||||||
fun <TF> getFileName(file: TF): String = when (file) {
|
fun <TF> getFileName(file: TF): String = when (file) {
|
||||||
is PsiFile -> file.originalFile.name
|
is PsiFile -> file.originalFile.name
|
||||||
is VirtualFile -> file.name
|
is VirtualFile -> file.name
|
||||||
@@ -56,17 +78,16 @@ fun <TF> getFileName(file: TF): String = when (file) {
|
|||||||
else -> throw IllegalArgumentException("Unsupported file type $file")
|
else -> throw IllegalArgumentException("Unsupported file type $file")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <TF> getFilePath(file: TF): String = when (file) {
|
||||||
|
is PsiFile -> file.originalFile.run { virtualFile?.path ?: name } // TODO: replace name with path of PSI elements
|
||||||
|
is VirtualFile -> file.path
|
||||||
|
is File -> file.canonicalPath
|
||||||
|
else -> throw IllegalArgumentException("Unsupported file type $file")
|
||||||
|
}
|
||||||
|
|
||||||
object StandardScriptDefinition : KotlinScriptDefinition {
|
object StandardScriptDefinition : KotlinScriptDefinition {
|
||||||
private val ARGS_NAME = Name.identifier("args")
|
private val ARGS_NAME = Name.identifier("args")
|
||||||
|
|
||||||
override val name = "Kotlin Script"
|
|
||||||
|
|
||||||
override fun getScriptName(script: KtScript): Name =
|
|
||||||
ScriptNameUtil.fileNameWithExtensionStripped(script, KotlinParserDefinition.STD_SCRIPT_EXT)
|
|
||||||
|
|
||||||
override fun <TF> isScript(file: TF): Boolean =
|
|
||||||
getFileExtension(file) == KotlinParserDefinition.STD_SCRIPT_SUFFIX
|
|
||||||
|
|
||||||
// NOTE: for now we treat .kts files as if they have 'args: Array<String>' parameter
|
// NOTE: for now we treat .kts files as if they have 'args: Array<String>' parameter
|
||||||
// this is not supposed to be final design
|
// this is not supposed to be final design
|
||||||
override fun getScriptParameters(scriptDescriptor: ScriptDescriptor): List<ScriptParameter> =
|
override fun getScriptParameters(scriptDescriptor: ScriptDescriptor): List<ScriptParameter> =
|
||||||
|
|||||||
+7
-12
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.script
|
package org.jetbrains.kotlin.script
|
||||||
|
|
||||||
import com.intellij.openapi.components.ServiceManager
|
import com.intellij.openapi.components.ServiceManager
|
||||||
|
import com.intellij.openapi.fileTypes.LanguageFileType
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
@@ -28,10 +29,8 @@ class KotlinScriptDefinitionProvider {
|
|||||||
|
|
||||||
private val definitions: MutableList<KotlinScriptDefinition> = arrayListOf(StandardScriptDefinition)
|
private val definitions: MutableList<KotlinScriptDefinition> = arrayListOf(StandardScriptDefinition)
|
||||||
private val definitionsLock = java.util.concurrent.locks.ReentrantReadWriteLock()
|
private val definitionsLock = java.util.concurrent.locks.ReentrantReadWriteLock()
|
||||||
private val notificationHandlers = ArrayList<() -> Unit>()
|
|
||||||
private val handlersLock = java.util.concurrent.locks.ReentrantReadWriteLock()
|
|
||||||
|
|
||||||
fun setScriptDefinitions(newDefinitions: List<KotlinScriptDefinition>): Unit {
|
fun setScriptDefinitions(newDefinitions: List<KotlinScriptDefinition>): Boolean {
|
||||||
var changed = false
|
var changed = false
|
||||||
definitionsLock.read {
|
definitionsLock.read {
|
||||||
if (newDefinitions != definitions) {
|
if (newDefinitions != definitions) {
|
||||||
@@ -42,11 +41,7 @@ class KotlinScriptDefinitionProvider {
|
|||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changed) {
|
return changed
|
||||||
handlersLock.read {
|
|
||||||
notificationHandlers.forEach { it() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun<TF> findScriptDefinition(file: TF): KotlinScriptDefinition? = definitionsLock.read {
|
fun<TF> findScriptDefinition(file: TF): KotlinScriptDefinition? = definitionsLock.read {
|
||||||
@@ -55,10 +50,6 @@ class KotlinScriptDefinitionProvider {
|
|||||||
|
|
||||||
fun<TF> isScript(file: TF): Boolean = findScriptDefinition(file) != null
|
fun<TF> isScript(file: TF): Boolean = findScriptDefinition(file) != null
|
||||||
|
|
||||||
fun subscribeOnDefinitionsChanged(handler: () -> Unit): Unit {
|
|
||||||
handlersLock.write { notificationHandlers.add(handler) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addScriptDefinition(scriptDefinition: KotlinScriptDefinition) {
|
fun addScriptDefinition(scriptDefinition: KotlinScriptDefinition) {
|
||||||
definitionsLock.write {
|
definitionsLock.write {
|
||||||
definitions.add(0, scriptDefinition)
|
definitions.add(0, scriptDefinition)
|
||||||
@@ -71,6 +62,10 @@ class KotlinScriptDefinitionProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAllKnownFileTypes(): Iterable<LanguageFileType> = definitionsLock.read {
|
||||||
|
definitions.map { it.fileType }.distinct()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getInstance(project: Project): KotlinScriptDefinitionProvider =
|
fun getInstance(project: Project): KotlinScriptDefinitionProvider =
|
||||||
|
|||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.components.ServiceManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
import kotlin.concurrent.read
|
||||||
|
import kotlin.concurrent.write
|
||||||
|
|
||||||
|
class KotlinScriptExternalImportsProvider(val project: Project, private val scriptDefinitionProvider: KotlinScriptDefinitionProvider) {
|
||||||
|
private val cacheLock = ReentrantReadWriteLock()
|
||||||
|
private val cache = hashMapOf<String, KotlinScriptExternalDependencies>()
|
||||||
|
private val cacheOfNulls = hashSetOf<String>()
|
||||||
|
|
||||||
|
fun <TF> getExternalImports(vararg files: TF): List<KotlinScriptExternalDependencies> = getExternalImports(files.asIterable())
|
||||||
|
|
||||||
|
fun <TF> getExternalImports(files: Iterable<TF>): List<KotlinScriptExternalDependencies> = cacheLock.read {
|
||||||
|
files.mapNotNull { file ->
|
||||||
|
val path = getFilePath(file)
|
||||||
|
cache[path]
|
||||||
|
?: if (cacheOfNulls.contains(path)) null
|
||||||
|
else scriptDefinitionProvider.findScriptDefinition(file)
|
||||||
|
?.let { it.getDependenciesFor(file, project) }
|
||||||
|
.apply { cacheLock.write {
|
||||||
|
if (this == null) {
|
||||||
|
cacheOfNulls.add(path)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cache.put(path, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <TF> cacheExternalImports(files: Iterable<TF>): Unit = cacheLock.write {
|
||||||
|
val uncached = hashSetOf<String>()
|
||||||
|
files.forEach { file ->
|
||||||
|
val path = getFilePath(file)
|
||||||
|
if (!cache.containsKey(path) && !cacheOfNulls.contains(path) && !uncached.contains(path)) {
|
||||||
|
val scriptDef = scriptDefinitionProvider.findScriptDefinition(file)
|
||||||
|
if (scriptDef != null) {
|
||||||
|
val deps = scriptDef.getDependenciesFor(file, project)
|
||||||
|
if (deps != null) {
|
||||||
|
cache.put(path, deps)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cacheOfNulls.add(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uncached.add(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun invalidateCaches() {
|
||||||
|
cacheLock.write {
|
||||||
|
cache.keys.toList().apply {
|
||||||
|
cache.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <TF> invalidateCachesFor(vararg files: TF) { invalidateCachesFor(files.asIterable()) }
|
||||||
|
|
||||||
|
fun <TF> invalidateCachesFor(files: Iterable<TF>) {
|
||||||
|
cacheLock.write {
|
||||||
|
files.forEach { file ->
|
||||||
|
val path = getFilePath(file)
|
||||||
|
cache.remove(path)
|
||||||
|
cacheOfNulls.remove(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getKnownCombinedClasspath(): List<String> = cacheLock.read {
|
||||||
|
cache.values.flatMap { it.classpath }
|
||||||
|
}.distinct()
|
||||||
|
|
||||||
|
fun <TF> getCombinedClasspathFor(files: Iterable<TF>): List<String> =
|
||||||
|
getExternalImports(files)
|
||||||
|
.flatMap { it.classpath }
|
||||||
|
.distinct()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun getInstance(project: Project): KotlinScriptExternalImportsProvider? =
|
||||||
|
ServiceManager.getService(project, KotlinScriptExternalImportsProvider::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
-132
@@ -1,132 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2016 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.components.ServiceManager
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
|
||||||
import kotlin.concurrent.read
|
|
||||||
import kotlin.concurrent.write
|
|
||||||
|
|
||||||
// TODO: replace VirtualFile usage with File for performance/unnecessary dependencies reasons
|
|
||||||
|
|
||||||
class KotlinScriptExtraImportsProvider(val project: Project, private val scriptDefinitionProvider: KotlinScriptDefinitionProvider) {
|
|
||||||
private val cacheLock = ReentrantReadWriteLock()
|
|
||||||
private val preconfigured = hashMapOf<String, List<KotlinScriptExtraImport>>()
|
|
||||||
private val cache = hashMapOf<String, List<KotlinScriptExtraImport>>()
|
|
||||||
private val envVars: Map<String, List<String>> by lazy { generateKotlinScriptClasspathEnvVars(project) }
|
|
||||||
private val notificationHandlers = ArrayList<(Iterable<String>) -> Unit>()
|
|
||||||
private val handlersLock = java.util.concurrent.locks.ReentrantReadWriteLock()
|
|
||||||
|
|
||||||
init {
|
|
||||||
val weakThis = WeakReference(this)
|
|
||||||
scriptDefinitionProvider.subscribeOnDefinitionsChanged { weakThis.get()?.invalidateAllExtraImports() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isExtraImportsConfig(file: VirtualFile): Boolean = file.name.endsWith(IMPORTS_FILE_EXTENSION)
|
|
||||||
|
|
||||||
fun getExtraImports(vararg files: VirtualFile): List<KotlinScriptExtraImport> = getExtraImports(files.asIterable())
|
|
||||||
|
|
||||||
fun getExtraImports(files: Iterable<VirtualFile>): List<KotlinScriptExtraImport> {
|
|
||||||
val newCashedFiles = ArrayList<String>()
|
|
||||||
val res = cacheLock.read {
|
|
||||||
files.flatMap { file ->
|
|
||||||
if (file.isValid && !file.isDirectory) {
|
|
||||||
preconfigured[file.path]
|
|
||||||
?: cache[file.path]
|
|
||||||
?: scriptDefinitionProvider.findScriptDefinition(file)?.let { def ->
|
|
||||||
(listOf(KotlinScriptExtraImportFromDefinition(def)) +
|
|
||||||
(file.parent.findFileByRelativePath(file.name + IMPORTS_FILE_EXTENSION)?.let {
|
|
||||||
loadScriptExtraImportConfigs(it.inputStream).map { KotlinScriptExtraImportFromConfig(it, envVars) }
|
|
||||||
} ?: emptyList()))
|
|
||||||
.apply {
|
|
||||||
cacheLock.write { cache.put(file.path, this) }
|
|
||||||
newCashedFiles.add(file.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?: emptyList()
|
|
||||||
}
|
|
||||||
else emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
notifyIfAny(newCashedFiles)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
fun preconfigureExtraImports(file: VirtualFile, extraImports: List<KotlinScriptExtraImport>?) {
|
|
||||||
cacheLock.write {
|
|
||||||
if (extraImports != null && extraImports.isNotEmpty()) {
|
|
||||||
preconfigured[file.path] = extraImports
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
preconfigured.remove(file.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun invalidateExtraImportsByImportsFiles(importsFiles: Iterable<VirtualFile>) {
|
|
||||||
importsFiles.mapNotNull { it.parent.findFileByRelativePath(it.name.removeSuffix(IMPORTS_FILE_EXTENSION))?.path?.let { file ->
|
|
||||||
cacheLock.write {
|
|
||||||
cache.remove(it.path)?.let { file }
|
|
||||||
}
|
|
||||||
} }.let {
|
|
||||||
notifyIfAny(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun invalidateAllExtraImports() {
|
|
||||||
cacheLock.write {
|
|
||||||
cache.keys.toList().apply {
|
|
||||||
cache.clear()
|
|
||||||
}
|
|
||||||
}.let {
|
|
||||||
notifyIfAny(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun notifyIfAny(files: Iterable<String>) {
|
|
||||||
if (files.any()) {
|
|
||||||
handlersLock.read {
|
|
||||||
notificationHandlers.forEach { it(files) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getKnownCombinedClasspath(): List<String> = cacheLock.read {
|
|
||||||
cache.values.flatMap { it.flatMap { it.classpath } }
|
|
||||||
}.distinct()
|
|
||||||
|
|
||||||
fun getCombinedClasspathFor(files: Iterable<VirtualFile>): List<String> =
|
|
||||||
getExtraImports(files)
|
|
||||||
.flatMap { it.classpath }
|
|
||||||
.distinct()
|
|
||||||
|
|
||||||
fun subscribeOnExtraImportsChanged(handler: (Iterable<String>) -> Unit): Unit {
|
|
||||||
handlersLock.write { notificationHandlers.add(handler) }
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun getInstance(project: Project): KotlinScriptExtraImportsProvider? =
|
|
||||||
ServiceManager.getService(project, KotlinScriptExtraImportsProvider::class.java)
|
|
||||||
|
|
||||||
val IMPORTS_FILE_EXTENSION = ".ktsimports.xml"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -26,11 +26,11 @@ fun getScriptDefinition(file: VirtualFile, project: Project): KotlinScriptDefini
|
|||||||
fun getScriptDefinition(psiFile: PsiFile): KotlinScriptDefinition? =
|
fun getScriptDefinition(psiFile: PsiFile): KotlinScriptDefinition? =
|
||||||
KotlinScriptDefinitionProvider.getInstance(psiFile.project).findScriptDefinition(psiFile)
|
KotlinScriptDefinitionProvider.getInstance(psiFile.project).findScriptDefinition(psiFile)
|
||||||
|
|
||||||
fun getScriptExtraImports(file: VirtualFile, project: Project): List<KotlinScriptExtraImport> =
|
fun getScriptExternalDependencies(file: VirtualFile, project: Project): List<KotlinScriptExternalDependencies> =
|
||||||
KotlinScriptExtraImportsProvider.getInstance(project)?.getExtraImports(file) ?: emptyList()
|
KotlinScriptExternalImportsProvider.getInstance(project)?.getExternalImports(file) ?: emptyList()
|
||||||
|
|
||||||
fun getScriptExtraImports(psiFile: PsiFile): List<KotlinScriptExtraImport> =
|
fun getScriptExternalDependencies(psiFile: PsiFile): List<KotlinScriptExternalDependencies> =
|
||||||
psiFile.virtualFile?.let { file ->
|
psiFile.virtualFile?.let { file ->
|
||||||
KotlinScriptExtraImportsProvider.getInstance(psiFile.project)?.getExtraImports(file)
|
KotlinScriptExternalImportsProvider.getInstance(psiFile.project)?.getExternalImports(file)
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ object SimpleUntypedAst {
|
|||||||
convert(expression.entries[0])
|
convert(expression.entries[0])
|
||||||
else
|
else
|
||||||
SimpleUntypedAst.Node.str(name, "")
|
SimpleUntypedAst.Node.str(name, "")
|
||||||
// convertStringTemplateExpression(expression, parent, expression.entries.size - 1)
|
// TODO: parse expressions, etc. e.g.:
|
||||||
|
// convertStringTemplateExpression(expression, parent, expression.entries.size - 1)
|
||||||
}
|
}
|
||||||
else -> Node.empty(name)
|
else -> Node.empty(name)
|
||||||
|
|
||||||
@@ -72,3 +73,9 @@ object SimpleUntypedAst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun parseAnnotation(ann: KtAnnotationEntry): SimpleUntypedAst.Node.list<Any> {
|
||||||
|
val wann = SimpleUntypedAst.KtAnnotationWrapper(ann)
|
||||||
|
val vals = wann.valueArguments
|
||||||
|
return SimpleUntypedAst.Node.list(wann.name, vals)
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.util.JDOMUtil
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.util.xmlb.XmlSerializer
|
||||||
|
import com.intellij.util.xmlb.annotations.AbstractCollection
|
||||||
|
import com.intellij.util.xmlb.annotations.Tag
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
@Tag("import")
|
||||||
|
class KotlinScriptExternalDependenciesConfig : KotlinScriptExternalDependencies {
|
||||||
|
@Tag("classpath")
|
||||||
|
@AbstractCollection(surroundWithTag = false, elementTag = "path", elementValueAttribute = "")
|
||||||
|
override var classpath: MutableList<String> = ArrayList()
|
||||||
|
|
||||||
|
@Tag("imports")
|
||||||
|
@AbstractCollection(surroundWithTag = false, elementTag = "name", elementValueAttribute = "")
|
||||||
|
override var imports: MutableList<String> = ArrayList()
|
||||||
|
|
||||||
|
@Tag("sources")
|
||||||
|
@AbstractCollection(surroundWithTag = false, elementTag = "path", elementValueAttribute = "")
|
||||||
|
override var sources: MutableList<String> = ArrayList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadScriptExternalImportConfigs(configFile: File): List<KotlinScriptExternalDependenciesConfig> =
|
||||||
|
JDOMUtil.loadDocument(configFile).rootElement.children.mapNotNull {
|
||||||
|
XmlSerializer.deserialize(it, KotlinScriptExternalDependenciesConfig::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadScriptExternalImportConfigs(configStream: InputStream): List<KotlinScriptExternalDependenciesConfig> =
|
||||||
|
JDOMUtil.loadDocument(configStream).rootElement.children.mapNotNull {
|
||||||
|
XmlSerializer.deserialize(it, KotlinScriptExternalDependenciesConfig::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <TF> getScriptDependenciesFromConfig(file: TF): KotlinScriptExternalDependencies? {
|
||||||
|
val IMPORTS_FILE_EXTENSION = ".ktsimports.xml"
|
||||||
|
fun streamFromSibling(file: VirtualFile): InputStream? =
|
||||||
|
file.parent.findFileByRelativePath(file.name + IMPORTS_FILE_EXTENSION)?.let { it.inputStream }
|
||||||
|
fun streamFromSibling(file: File): InputStream? {
|
||||||
|
val sibling = File(file.parentFile, file.name + IMPORTS_FILE_EXTENSION)
|
||||||
|
return if (sibling.exists()) sibling.inputStream()
|
||||||
|
else null
|
||||||
|
}
|
||||||
|
return when (file) {
|
||||||
|
is VirtualFile -> streamFromSibling(file)
|
||||||
|
is PsiFile -> streamFromSibling(file.originalFile.virtualFile)
|
||||||
|
is File -> streamFromSibling(file)
|
||||||
|
else -> throw IllegalArgumentException("Unsupported file type $file")
|
||||||
|
}?.let { KotlinScriptExternalDependenciesUnion(loadScriptExternalImportConfigs(it)) }
|
||||||
|
}
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2016 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.util.JDOMUtil
|
|
||||||
import com.intellij.util.xmlb.XmlSerializer
|
|
||||||
import com.intellij.util.xmlb.annotations.AbstractCollection
|
|
||||||
import com.intellij.util.xmlb.annotations.Tag
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A particular script dependency, additional to script kind dependencies defined by KotlinScriptDefinition
|
|
||||||
*/
|
|
||||||
interface KotlinScriptExtraImport {
|
|
||||||
val classpath: List<String>
|
|
||||||
val names: List<String>
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----
|
|
||||||
|
|
||||||
@Tag("import")
|
|
||||||
class KotlinScriptExtraImportConfig {
|
|
||||||
@Tag("classpath")
|
|
||||||
@AbstractCollection(surroundWithTag = false, elementTag = "path", elementValueAttribute = "")
|
|
||||||
var classpath: MutableList<String> = ArrayList()
|
|
||||||
|
|
||||||
@Tag("names")
|
|
||||||
@AbstractCollection(surroundWithTag = false, elementTag = "name", elementValueAttribute = "")
|
|
||||||
var names: MutableList<String> = ArrayList()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadScriptExtraImportConfigs(configFile: File): List<KotlinScriptExtraImportConfig> =
|
|
||||||
JDOMUtil.loadDocument(configFile).rootElement.children.mapNotNull {
|
|
||||||
XmlSerializer.deserialize(it, KotlinScriptExtraImportConfig::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun loadScriptExtraImportConfigs(configStream: InputStream): List<KotlinScriptExtraImportConfig> =
|
|
||||||
JDOMUtil.loadDocument(configStream).rootElement.children.mapNotNull {
|
|
||||||
XmlSerializer.deserialize(it, KotlinScriptExtraImportConfig::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
class KotlinScriptExtraImportFromConfig(val config : KotlinScriptExtraImportConfig, val envVars: Map<String, List<String>>) : KotlinScriptExtraImport {
|
|
||||||
override val classpath: List<String> by lazy { config.classpath.evalWithVars(envVars).distinct() }
|
|
||||||
override val names: List<String>
|
|
||||||
get() = config.names
|
|
||||||
}
|
|
||||||
|
|
||||||
class KotlinScriptExtraImportFromDefinition(val scriptDefinition: KotlinScriptDefinition) : KotlinScriptExtraImport {
|
|
||||||
override val classpath: List<String> get() = scriptDefinition.getScriptDependenciesClasspath()
|
|
||||||
override val names: List<String> = emptyList()
|
|
||||||
}
|
|
||||||
@@ -16,33 +16,33 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.script
|
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.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
||||||
import org.jetbrains.kotlin.psi.KtAnnotation
|
|
||||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
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.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import java.io.File
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class ScriptFilePattern(val pattern: String)
|
annotation class ScriptFilePattern(val pattern: String)
|
||||||
|
|
||||||
interface ScriptDependencies {
|
|
||||||
val classpath: List<String>
|
|
||||||
val implicitImports: List<String>
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetScriptDependencies {
|
interface GetScriptDependencies {
|
||||||
operator fun invoke(annotations: Iterable<KtAnnotationEntry>, context: Any?): ScriptDependencies? = null
|
operator fun invoke(annotations: Iterable<KtAnnotationEntry>, context: Any?): KotlinScriptExternalDependencies? = null
|
||||||
operator fun invoke(context: Any?): ScriptDependencies? = null
|
operator fun invoke(context: Any?): KotlinScriptExternalDependencies? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@Target(AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.CLASS)
|
||||||
@Retention(AnnotationRetention.RUNTIME)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
annotation class ScriptDependencyExtractor(val extractor: KClass<out GetScriptDependencies>)
|
annotation class ScriptDependencyResolver(val extractor: KClass<out GetScriptDependencies>)
|
||||||
|
|
||||||
data class KotlinScriptDefinitionFromTemplate(val template: KClass<out Any>, val context: Any?) : KotlinScriptDefinition {
|
data class KotlinScriptDefinitionFromTemplate(val template: KClass<out Any>, val context: Any?) : KotlinScriptDefinition {
|
||||||
override val name = template.simpleName!!
|
override val name = template.simpleName!!
|
||||||
@@ -63,13 +63,39 @@ data class KotlinScriptDefinitionFromTemplate(val template: KClass<out Any>, val
|
|||||||
override fun getScriptName(script: KtScript): Name = ScriptNameUtil.fileNameWithExtensionStripped(script, KotlinParserDefinition.STD_SCRIPT_EXT)
|
override fun getScriptName(script: KtScript): Name = ScriptNameUtil.fileNameWithExtensionStripped(script, KotlinParserDefinition.STD_SCRIPT_EXT)
|
||||||
|
|
||||||
private val dependenciesExtractors by lazy {
|
private val dependenciesExtractors by lazy {
|
||||||
template.annotations.mapNotNull { it as? ScriptDependencyExtractor }.map { it.extractor.constructors.first().call() }
|
template.annotations.mapNotNull { it as? ScriptDependencyResolver }.map { it.extractor.constructors.first().call() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private val dependencies by lazy {
|
private val dependencies: List<KotlinScriptExternalDependencies> by lazy {
|
||||||
dependenciesExtractors.mapNotNull { it(context) }
|
dependenciesExtractors.mapNotNull { it(context) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getScriptDependenciesClasspath(): List<String> = dependencies.flatMap { it.classpath }
|
override fun <TF> getDependenciesFor(file: TF, project: Project): KotlinScriptExternalDependencies? {
|
||||||
|
val fileAnnotations = getAnnotationEntries(file, project)
|
||||||
|
val fileDeps = dependenciesExtractors.mapNotNull { it(fileAnnotations, context) }
|
||||||
|
return KotlinScriptExternalDependenciesUnion(dependencies + fileDeps)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <TF> 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.absolutePath)
|
||||||
|
?: throw java.lang.IllegalArgumentException("Unable to find file ${file.canonicalPath}"))
|
||||||
|
getAnnotationEntriesFromVirtualFile(virtualFile, project)
|
||||||
|
}
|
||||||
|
else -> throw IllegalArgumentException("Unsupported file type $file")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAnnotationEntriesFromPsiFile(file: PsiFile) =
|
||||||
|
if (file is KtFile) file.annotationEntries
|
||||||
|
else 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 java.lang.IllegalArgumentException("Unable to load PSI from ${file.canonicalPath}")
|
||||||
|
return getAnnotationEntriesFromPsiFile(psifile)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.scripts
|
package org.jetbrains.kotlin.scripts
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.KtScript
|
import org.jetbrains.kotlin.psi.KtScript
|
||||||
@@ -26,15 +27,17 @@ import java.net.URL
|
|||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
abstract class BaseScriptDefinition (val extension: String, val classpath: List<String>? = null) : KotlinScriptDefinition {
|
abstract class BaseScriptDefinition (val extension: String, val cp: List<String>? = null) : KotlinScriptDefinition {
|
||||||
override val name = "Test Kotlin Script"
|
override val name = "Test Kotlin Script"
|
||||||
override fun <TF> isScript(file: TF): Boolean = org.jetbrains.kotlin.script.getFileName(file).endsWith(extension)
|
override fun <TF> isScript(file: TF): Boolean = getFileName(file).endsWith(extension)
|
||||||
override fun getScriptName(script: KtScript): Name = ScriptNameUtil.fileNameWithExtensionStripped(script, extension)
|
override fun getScriptName(script: KtScript): Name = ScriptNameUtil.fileNameWithExtensionStripped(script, extension)
|
||||||
override fun getScriptDependenciesClasspath(): List<String> =
|
override fun <TF> getDependenciesFor(file: TF, project: Project): KotlinScriptExternalDependencies? =
|
||||||
classpath ?: (classpathFromProperty() + classpathFromClassloader(BaseScriptDefinition::class.java.classLoader)).distinct()
|
object : KotlinScriptExternalDependencies {
|
||||||
|
override val classpath = cp ?: (classpathFromProperty() + classpathFromClassloader(BaseScriptDefinition::class.java.classLoader)).distinct()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class SimpleParamsWithClasspathTestScriptDefinition(extension: String, val parameters: List<ScriptParameter>, classpath: List<String>? = null)
|
open class SimpleParamsWithClasspathTestScriptDefinition(extension: String, val parameters: List<ScriptParameter>, classpath: List<String>? = null, val extraDependencies: KotlinScriptExternalDependencies? = null)
|
||||||
: BaseScriptDefinition(extension, classpath)
|
: BaseScriptDefinition(extension, classpath)
|
||||||
{
|
{
|
||||||
override fun getScriptParameters(scriptDescriptor: ScriptDescriptor) = parameters
|
override fun getScriptParameters(scriptDescriptor: ScriptDescriptor) = parameters
|
||||||
@@ -74,10 +77,10 @@ class StandardWithClasspathScriptDefinition(extension: String, classpath: List<S
|
|||||||
StandardScriptDefinition.getScriptParameters(scriptDescriptor)
|
StandardScriptDefinition.getScriptParameters(scriptDescriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimpleScriptExtraImport(
|
class SimpleScriptExtraDependencies(
|
||||||
override val classpath: List<String>,
|
override val classpath: List<String>,
|
||||||
override val names: List<String> = emptyList()
|
override val imports: List<String> = emptyList()
|
||||||
) : KotlinScriptExtraImport
|
) : KotlinScriptExternalDependencies
|
||||||
|
|
||||||
fun classpathFromProperty(): List<String> =
|
fun classpathFromProperty(): List<String> =
|
||||||
System.getProperty("java.class.path")?.let {
|
System.getProperty("java.class.path")?.let {
|
||||||
|
|||||||
@@ -26,11 +26,9 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
|||||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
|
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
|
||||||
import org.jetbrains.kotlin.codegen.CompilationException
|
import org.jetbrains.kotlin.codegen.CompilationException
|
||||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
|
||||||
import org.jetbrains.kotlin.config.addKotlinSourceRoot
|
import org.jetbrains.kotlin.config.addKotlinSourceRoot
|
||||||
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.KotlinScriptExtraImport
|
|
||||||
import org.jetbrains.kotlin.script.ScriptParameter
|
import org.jetbrains.kotlin.script.ScriptParameter
|
||||||
import org.jetbrains.kotlin.script.StandardScriptDefinition
|
import org.jetbrains.kotlin.script.StandardScriptDefinition
|
||||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||||
@@ -45,14 +43,14 @@ import java.lang.reflect.InvocationTargetException
|
|||||||
class ScriptTest {
|
class ScriptTest {
|
||||||
@Test
|
@Test
|
||||||
fun testScriptWithParam() {
|
fun testScriptWithParam() {
|
||||||
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()), null)
|
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()))
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testStandardScriptWithParams() {
|
fun testStandardScriptWithParams() {
|
||||||
val aClass = compileScript("fib_std.kts", StandardScriptDefinition, null)
|
val aClass = compileScript("fib_std.kts", StandardScriptDefinition)
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
val anObj = KotlinToJVMBytecodeCompiler.tryConstructClassPub(aClass!!, listOf("4", "comment"))
|
val anObj = KotlinToJVMBytecodeCompiler.tryConstructClassPub(aClass!!, listOf("4", "comment"))
|
||||||
Assert.assertNotNull(anObj)
|
Assert.assertNotNull(anObj)
|
||||||
@@ -60,7 +58,7 @@ class ScriptTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testStandardScriptWithoutParams() {
|
fun testStandardScriptWithoutParams() {
|
||||||
val aClass = compileScript("fib_std.kts", StandardScriptDefinition, null)
|
val aClass = compileScript("fib_std.kts", StandardScriptDefinition)
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
val anObj = KotlinToJVMBytecodeCompiler.tryConstructClassPub(aClass!!, emptyList())
|
val anObj = KotlinToJVMBytecodeCompiler.tryConstructClassPub(aClass!!, emptyList())
|
||||||
Assert.assertNotNull(anObj)
|
Assert.assertNotNull(anObj)
|
||||||
@@ -68,7 +66,7 @@ class ScriptTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testScriptWithParamConversion() {
|
fun testScriptWithParamConversion() {
|
||||||
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()), null)
|
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()))
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
val anObj = KotlinToJVMBytecodeCompiler.tryConstructClassPub(aClass!!, listOf("4"))
|
val anObj = KotlinToJVMBytecodeCompiler.tryConstructClassPub(aClass!!, listOf("4"))
|
||||||
Assert.assertNotNull(anObj)
|
Assert.assertNotNull(anObj)
|
||||||
@@ -76,67 +74,67 @@ class ScriptTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testScriptWithPackage() {
|
fun testScriptWithPackage() {
|
||||||
val aClass = compileScript("fib.pkg.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()), null)
|
val aClass = compileScript("fib.pkg.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()))
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testScriptWithScriptDefinition() {
|
fun testScriptWithScriptDefinition() {
|
||||||
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()), null)
|
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()))
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testScriptWithClassParameter() {
|
fun testScriptWithClassParameter() {
|
||||||
val aClass = compileScript("fib_cp.kts", ReflectedParamClassTestScriptDefinition(".kts", "param", TestParamClass::class), null, runIsolated = false)
|
val aClass = compileScript("fib_cp.kts", ReflectedParamClassTestScriptDefinition(".kts", "param", TestParamClass::class), runIsolated = false)
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
aClass!!.getConstructor(TestParamClass::class.java).newInstance(TestParamClass(4))
|
aClass!!.getConstructor(TestParamClass::class.java).newInstance(TestParamClass(4))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testScriptWithBaseClass() {
|
fun testScriptWithBaseClass() {
|
||||||
val aClass = compileScript("fib_dsl.kts", ReflectedSuperclassTestScriptDefinition(".kts", numIntParam(), TestDSLClass::class), null, runIsolated = false)
|
val aClass = compileScript("fib_dsl.kts", ReflectedSuperclassTestScriptDefinition(".kts", numIntParam(), TestDSLClass::class), runIsolated = false)
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testScriptWithBaseClassWithParam() {
|
fun testScriptWithBaseClassWithParam() {
|
||||||
val aClass = compileScript("fib_dsl.kts", ReflectedSuperclassWithParamsTestScriptDefinition(".kts", numIntParam() + numIntParam("passthrough"), TestDSLClassWithParam::class, numIntParam("passthrough")), null, runIsolated = false)
|
val aClass = compileScript("fib_dsl.kts", ReflectedSuperclassWithParamsTestScriptDefinition(".kts", numIntParam() + numIntParam("passthrough"), TestDSLClassWithParam::class, numIntParam("passthrough")), runIsolated = false)
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
aClass!!.getConstructor(Integer.TYPE, Integer.TYPE).newInstance(4, 1)
|
aClass!!.getConstructor(Integer.TYPE, Integer.TYPE).newInstance(4, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testScriptWithInterface() {
|
fun testScriptWithInterface() {
|
||||||
val aClass = compileScript("fib_dsl.kts", ReflectedSuperclassTestScriptDefinition(".kts", numIntParam(), TestDSLInterface::class), null, runIsolated = false)
|
val aClass = compileScript("fib_dsl.kts", ReflectedSuperclassTestScriptDefinition(".kts", numIntParam(), TestDSLInterface::class), runIsolated = false)
|
||||||
Assert.assertNotNull(aClass)
|
Assert.assertNotNull(aClass)
|
||||||
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testScriptWithClasspath() {
|
fun testScriptWithClasspath() {
|
||||||
val aClass1 = compileScript("fib_ext.kts", SimpleParamsWithClasspathTestScriptDefinition(".kts", numIntParam(), classpath = emptyList()), null, runIsolated = true, suppressOutput = true)
|
val aClass1 = compileScript("fib_ext.kts", SimpleParamsWithClasspathTestScriptDefinition(".kts", numIntParam(), classpath = emptyList()), runIsolated = true, suppressOutput = true)
|
||||||
Assert.assertNull(aClass1)
|
Assert.assertNull(aClass1)
|
||||||
|
|
||||||
val cp = classpathFromClassloader(ScriptTest::class.java.classLoader).filter { it.contains("kotlin-runtime") || it.contains("junit") }
|
val cp = classpathFromClassloader(ScriptTest::class.java.classLoader).filter { it.contains("kotlin-runtime") || it.contains("junit") }
|
||||||
Assert.assertFalse(cp.isEmpty())
|
Assert.assertFalse(cp.isEmpty())
|
||||||
|
|
||||||
val aClass2 = compileScript("fib_ext.kts", SimpleParamsWithClasspathTestScriptDefinition(".kts", numIntParam(), classpath = cp), null, runIsolated = true)
|
val aClass2 = compileScript("fib_ext.kts", SimpleParamsWithClasspathTestScriptDefinition(".kts", numIntParam(), classpath = cp), runIsolated = true)
|
||||||
Assert.assertNotNull(aClass2)
|
Assert.assertNotNull(aClass2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testScriptWithExtraImports() {
|
fun testScriptWithExtraImports() {
|
||||||
val aClass1 = compileScript("fib_ext.kts", SimpleParamsWithClasspathTestScriptDefinition(".kts", numIntParam(), classpath = emptyList()), extraImport = null, runIsolated = true, suppressOutput = true)
|
val aClass1 = compileScript("fib_ext.kts", SimpleParamsWithClasspathTestScriptDefinition(".kts", numIntParam(), classpath = emptyList()), runIsolated = true, suppressOutput = true)
|
||||||
Assert.assertNull(aClass1)
|
Assert.assertNull(aClass1)
|
||||||
|
|
||||||
val cp = classpathFromClassloader(ScriptTest::class.java.classLoader).filter { it.contains("kotlin-runtime") || it.contains("junit") }
|
val cp = classpathFromClassloader(ScriptTest::class.java.classLoader).filter { it.contains("kotlin-runtime") || it.contains("junit") }
|
||||||
Assert.assertFalse(cp.isEmpty())
|
Assert.assertFalse(cp.isEmpty())
|
||||||
|
|
||||||
val aClass2 = compileScript("fib_ext.kts", SimpleParamsWithClasspathTestScriptDefinition(".kts", numIntParam(), classpath = emptyList()), extraImport = SimpleScriptExtraImport(cp), runIsolated = true)
|
val aClass2 = compileScript("fib_ext.kts", SimpleParamsWithClasspathTestScriptDefinition(".kts", numIntParam(), classpath = cp, extraDependencies = SimpleScriptExtraDependencies(cp)), runIsolated = true)
|
||||||
Assert.assertNotNull(aClass2)
|
Assert.assertNotNull(aClass2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,22 +161,20 @@ class ScriptTest {
|
|||||||
private fun compileScript(
|
private fun compileScript(
|
||||||
scriptPath: String,
|
scriptPath: String,
|
||||||
scriptDefinition: KotlinScriptDefinition,
|
scriptDefinition: KotlinScriptDefinition,
|
||||||
extraImport: KotlinScriptExtraImport?,
|
|
||||||
runIsolated: Boolean = true,
|
runIsolated: Boolean = true,
|
||||||
suppressOutput: Boolean = false): Class<*>? =
|
suppressOutput: Boolean = false): Class<*>? =
|
||||||
compileScriptImpl("compiler/testData/script/" + scriptPath, scriptDefinition, extraImport, runIsolated, suppressOutput)
|
compileScriptImpl("compiler/testData/script/" + scriptPath, scriptDefinition, runIsolated, suppressOutput)
|
||||||
|
|
||||||
private fun compileSmokeTestScript(
|
private fun compileSmokeTestScript(
|
||||||
scriptPath: String,
|
scriptPath: String,
|
||||||
scriptDefinition: KotlinScriptDefinition,
|
scriptDefinition: KotlinScriptDefinition,
|
||||||
runIsolated: Boolean = true,
|
runIsolated: Boolean = true,
|
||||||
suppressOutput: Boolean = false): Class<*>? =
|
suppressOutput: Boolean = false): Class<*>? =
|
||||||
compileScriptImpl("compiler/testData/integration/smoke/" + scriptPath, scriptDefinition, null, runIsolated, suppressOutput)
|
compileScriptImpl("compiler/testData/integration/smoke/" + scriptPath, scriptDefinition, runIsolated, suppressOutput)
|
||||||
|
|
||||||
private fun compileScriptImpl(
|
private fun compileScriptImpl(
|
||||||
scriptPath: String,
|
scriptPath: String,
|
||||||
scriptDefinition: KotlinScriptDefinition,
|
scriptDefinition: KotlinScriptDefinition,
|
||||||
extraImport: KotlinScriptExtraImport?,
|
|
||||||
runIsolated: Boolean,
|
runIsolated: Boolean,
|
||||||
suppressOutput: Boolean): Class<*>?
|
suppressOutput: Boolean): Class<*>?
|
||||||
{
|
{
|
||||||
@@ -193,10 +189,6 @@ class ScriptTest {
|
|||||||
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
|
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
|
||||||
configuration.addKotlinSourceRoot(scriptPath)
|
configuration.addKotlinSourceRoot(scriptPath)
|
||||||
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
|
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition)
|
||||||
extraImport?.let {
|
|
||||||
configuration.put(JVMConfigurationKeys.SCRIPTS_EXTRA_IMPORTS, scriptPath, it)
|
|
||||||
}
|
|
||||||
scriptDefinition.getScriptDependenciesClasspath().forEach { configuration.addJvmClasspathRoot(File(it)) }
|
|
||||||
|
|
||||||
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
|
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class GetTestKotlinScriptDependencies : GetScriptDependencies {
|
|||||||
|
|
||||||
private val kotlinPaths by lazy { PathUtil.getKotlinPathsForCompiler() }
|
private val kotlinPaths by lazy { PathUtil.getKotlinPathsForCompiler() }
|
||||||
|
|
||||||
override fun invoke(annotations: Iterable<KtAnnotationEntry>, context: Any?): ScriptDependencies? {
|
override fun invoke(annotations: Iterable<KtAnnotationEntry>, context: Any?): KotlinScriptExternalDependencies? {
|
||||||
if (annotations.none()) return null
|
if (annotations.none()) return null
|
||||||
val anns = annotations.map { parseAnnotation(it) }.filter { it.name == depends::class.simpleName }
|
val anns = annotations.map { parseAnnotation(it) }.filter { it.name == depends::class.simpleName }
|
||||||
val cp = anns.flatMap {
|
val cp = anns.flatMap {
|
||||||
@@ -129,9 +129,8 @@ class GetTestKotlinScriptDependencies : GetScriptDependencies {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return object : ScriptDependencies {
|
return object : KotlinScriptExternalDependencies {
|
||||||
override val classpath = cp
|
override val classpath = cp
|
||||||
override val implicitImports = emptyList<String>()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,30 +140,23 @@ class GetTestKotlinScriptDependencies : GetScriptDependencies {
|
|||||||
?.filter { it.contains("out/test") }
|
?.filter { it.contains("out/test") }
|
||||||
?: emptyList()
|
?: emptyList()
|
||||||
|
|
||||||
override fun invoke(context: Any?): ScriptDependencies? {
|
override fun invoke(context: Any?): KotlinScriptExternalDependencies? {
|
||||||
return object : ScriptDependencies {
|
return object : KotlinScriptExternalDependencies {
|
||||||
override val classpath = classpathFromClassloader()
|
override val classpath = classpathFromClassloader()
|
||||||
override val implicitImports = emptyList<String>()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parseAnnotation(ann: KtAnnotationEntry): SimpleUntypedAst.Node.list<Any> {
|
|
||||||
val wann = SimpleUntypedAst.KtAnnotationWrapper(ann)
|
|
||||||
val vals = wann.valueArguments
|
|
||||||
return SimpleUntypedAst.Node.list(wann.name, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
@ScriptFilePattern(".*\\.kts")
|
@ScriptFilePattern(".*\\.kts")
|
||||||
@ScriptDependencyExtractor(GetTestKotlinScriptDependencies::class)
|
@ScriptDependencyResolver(GetTestKotlinScriptDependencies::class)
|
||||||
abstract class ScriptWithIntParam(num: Int)
|
abstract class ScriptWithIntParam(num: Int)
|
||||||
|
|
||||||
@ScriptFilePattern(".*\\.kts")
|
@ScriptFilePattern(".*\\.kts")
|
||||||
@ScriptDependencyExtractor(GetTestKotlinScriptDependencies::class)
|
@ScriptDependencyResolver(GetTestKotlinScriptDependencies::class)
|
||||||
abstract class ScriptWithClassParam(param: TestParamClass)
|
abstract class ScriptWithClassParam(param: TestParamClass)
|
||||||
|
|
||||||
@ScriptFilePattern(".*\\.kts")
|
@ScriptFilePattern(".*\\.kts")
|
||||||
@ScriptDependencyExtractor(GetTestKotlinScriptDependencies::class)
|
@ScriptDependencyResolver(GetTestKotlinScriptDependencies::class)
|
||||||
abstract class ScriptWithBaseClass(num: Int, passthrough: Int) : TestDSLClassWithParam(passthrough)
|
abstract class ScriptWithBaseClass(num: Int, passthrough: Int) : TestDSLClassWithParam(passthrough)
|
||||||
|
|
||||||
@Target(AnnotationTarget.FILE)
|
@Target(AnnotationTarget.FILE)
|
||||||
|
|||||||
+12
-17
@@ -23,27 +23,22 @@ import com.intellij.openapi.projectRoots.Sdk
|
|||||||
import com.intellij.openapi.roots.*
|
import com.intellij.openapi.roots.*
|
||||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||||
import com.intellij.openapi.roots.libraries.Library
|
import com.intellij.openapi.roots.libraries.Library
|
||||||
import com.intellij.openapi.roots.libraries.LibraryTable
|
|
||||||
import com.intellij.openapi.vfs.StandardFileSystems
|
import com.intellij.openapi.vfs.StandardFileSystems
|
||||||
import com.intellij.openapi.vfs.VfsUtilCore
|
import com.intellij.openapi.vfs.VfsUtilCore
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.openapi.vfs.VirtualFileManager
|
|
||||||
import com.intellij.psi.search.DelegatingGlobalSearchScope
|
import com.intellij.psi.search.DelegatingGlobalSearchScope
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.psi.util.CachedValueProvider
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
import com.intellij.psi.util.CachedValuesManager
|
import com.intellij.psi.util.CachedValuesManager
|
||||||
import com.intellij.util.SmartList
|
import com.intellij.util.SmartList
|
||||||
import com.intellij.util.io.URLUtil
|
import com.intellij.util.io.URLUtil
|
||||||
import org.jdom.Element
|
|
||||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
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.KotlinScriptExtraImport
|
|
||||||
import org.jetbrains.kotlin.utils.alwaysNull
|
import org.jetbrains.kotlin.utils.alwaysNull
|
||||||
import org.jetbrains.kotlin.utils.emptyOrSingletonList
|
import org.jetbrains.kotlin.utils.emptyOrSingletonList
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -297,8 +292,7 @@ class ScriptModuleSearchScope(val scriptFile: VirtualFile, baseScope: GlobalSear
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal data class ScriptModuleInfo(val project: Project, val module: Module?, val scriptFile: VirtualFile,
|
internal data class ScriptModuleInfo(val project: Project, val module: Module?, val scriptFile: VirtualFile,
|
||||||
val scriptDefinition: KotlinScriptDefinition,
|
val scriptDefinition: KotlinScriptDefinition) : IdeaModuleInfo {
|
||||||
val scriptExtraImports: List<KotlinScriptExtraImport>) : IdeaModuleInfo {
|
|
||||||
override val moduleOrigin: ModuleOrigin
|
override val moduleOrigin: ModuleOrigin
|
||||||
get() = ModuleOrigin.OTHER
|
get() = ModuleOrigin.OTHER
|
||||||
|
|
||||||
@@ -315,16 +309,17 @@ internal data class ScriptModuleInfo(val project: Project, val module: Module?,
|
|||||||
private fun dependenciesRoots(): List<VirtualFile> {
|
private fun dependenciesRoots(): List<VirtualFile> {
|
||||||
// TODO: find out whether it should be cashed (some changes listener should be implemented for the cached roots)
|
// TODO: find out whether it should be cashed (some changes listener should be implemented for the cached roots)
|
||||||
val jarfs = StandardFileSystems.jar()
|
val jarfs = StandardFileSystems.jar()
|
||||||
return (scriptDefinition.getScriptDependenciesClasspath() + scriptExtraImports.flatMap { it.classpath })
|
return scriptDefinition.getDependenciesFor(scriptFile, project)?.classpath
|
||||||
.map { File(it).canonicalFile }
|
?.map { File(it).canonicalFile }
|
||||||
.distinct()
|
?.distinct()
|
||||||
.mapNotNull {
|
?.mapNotNull {
|
||||||
// TODO: ensure that the entries are checked elsewhere, so diagnostics is delivered to a user if files are not correctly specified
|
// TODO: ensure that the entries are checked elsewhere, so diagnostics is delivered to a user if files are not correctly specified
|
||||||
if (it.isFile)
|
if (it.isFile)
|
||||||
jarfs.findFileByPath(it.absolutePath + URLUtil.JAR_SEPARATOR) ?: null // diag: Classpath entry points to a file that is not a JAR archive
|
jarfs.findFileByPath(it.absolutePath + URLUtil.JAR_SEPARATOR) ?: null // diag: Classpath entry points to a file that is not a JAR archive
|
||||||
else
|
else
|
||||||
StandardFileSystems.local().findFileByPath(it.absolutePath) ?: null // diag: Classpath entry points to a non-existent location
|
StandardFileSystems.local().findFileByPath(it.absolutePath) ?: null // diag: Classpath entry points to a non-existent location
|
||||||
}
|
}
|
||||||
|
?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dependencies() =
|
override fun dependencies() =
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
|||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||||
import org.jetbrains.kotlin.script.getScriptDefinition
|
import org.jetbrains.kotlin.script.getScriptDefinition
|
||||||
import org.jetbrains.kotlin.script.getScriptExtraImports
|
|
||||||
import org.jetbrains.kotlin.utils.sure
|
import org.jetbrains.kotlin.utils.sure
|
||||||
|
|
||||||
fun PsiElement.getModuleInfo(): IdeaModuleInfo = this.getModuleInfo { reason ->
|
fun PsiElement.getModuleInfo(): IdeaModuleInfo = this.getModuleInfo { reason ->
|
||||||
@@ -125,7 +124,7 @@ private fun getModuleInfoByVirtualFile(project: Project, virtualFile: VirtualFil
|
|||||||
|
|
||||||
val scriptDefinition = getScriptDefinition(virtualFile, project)
|
val scriptDefinition = getScriptDefinition(virtualFile, project)
|
||||||
if (scriptDefinition != null)
|
if (scriptDefinition != null)
|
||||||
return ScriptModuleInfo(project, module, virtualFile, scriptDefinition, getScriptExtraImports(virtualFile, project))
|
return ScriptModuleInfo(project, module, virtualFile, scriptDefinition)
|
||||||
|
|
||||||
return NotUnderContentRootModuleInfo
|
return NotUnderContentRootModuleInfo
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
|
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
|
||||||
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledTextIndexer
|
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledTextIndexer
|
||||||
import org.jetbrains.kotlin.idea.script.KotlinScriptConfigurationManager
|
import org.jetbrains.kotlin.idea.core.script.KotlinScriptConfigurationManager
|
||||||
import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex
|
import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex
|
||||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||||
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelFunctionFqnNameIndex
|
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelFunctionFqnNameIndex
|
||||||
|
|||||||
-164
@@ -1,164 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2016 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.idea.script
|
|
||||||
|
|
||||||
import com.intellij.openapi.components.AbstractProjectComponent
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.roots.ex.ProjectRootManagerEx
|
|
||||||
import com.intellij.openapi.util.EmptyRunnable
|
|
||||||
import com.intellij.openapi.vfs.StandardFileSystems
|
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
|
||||||
import com.intellij.openapi.vfs.VirtualFileManager
|
|
||||||
import com.intellij.openapi.vfs.newvfs.BulkFileListener
|
|
||||||
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
|
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
|
||||||
import com.intellij.util.indexing.IndexableSetContributor
|
|
||||||
import com.intellij.util.io.URLUtil
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.FileLibraryScope
|
|
||||||
import org.jetbrains.kotlin.script.*
|
|
||||||
import org.jetbrains.kotlin.utils.PathUtil
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileNotFoundException
|
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.concurrent.read
|
|
||||||
import kotlin.concurrent.write
|
|
||||||
|
|
||||||
@Suppress("unused") // project component
|
|
||||||
class KotlinScriptConfigurationManager(project: Project,
|
|
||||||
private val scriptDefinitionProvider: KotlinScriptDefinitionProvider,
|
|
||||||
private val scriptExtraImportsProvider: KotlinScriptExtraImportsProvider?,
|
|
||||||
private val kotlinScriptDependenciesIndexableSetContributor: KotlinScriptDependenciesIndexableSetContributor?
|
|
||||||
) : AbstractProjectComponent(project) {
|
|
||||||
|
|
||||||
private val kotlinEnvVars: Map<String, List<String>> by lazy { generateKotlinScriptClasspathEnvVarsFromPaths(myProject, PathUtil.getKotlinPathsForIdeaPlugin()) }
|
|
||||||
|
|
||||||
init {
|
|
||||||
reloadScriptDefinitions()
|
|
||||||
|
|
||||||
// TODO: get rid of this expensive call as soon as makeRootsChange call will work reliably
|
|
||||||
cacheAllScriptsExtraImports()
|
|
||||||
|
|
||||||
val weakThis = WeakReference(this)
|
|
||||||
myProject.messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener.Adapter() {
|
|
||||||
override fun after(events: List<VFileEvent>) {
|
|
||||||
val changedExtraImportConfigs = ArrayList<VirtualFile>()
|
|
||||||
var anyScriptDefinitionChanged = false
|
|
||||||
events.forEach {
|
|
||||||
it.file?.let {
|
|
||||||
if (isScriptDefinitionConfigFile(it)) {
|
|
||||||
anyScriptDefinitionChanged = true
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
weakThis.get()?.scriptExtraImportsProvider?.run {
|
|
||||||
if (isExtraImportsConfig(it)) {
|
|
||||||
changedExtraImportConfigs.add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (anyScriptDefinitionChanged) {
|
|
||||||
weakThis.get()?.reloadScriptDefinitions()
|
|
||||||
}
|
|
||||||
if (changedExtraImportConfigs.isNotEmpty()) {
|
|
||||||
weakThis.get()?.scriptExtraImportsProvider?.invalidateExtraImportsByImportsFiles(changedExtraImportConfigs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// omitting case then scriptExtraImportsProvider is not configured, considering it happens only in tests
|
|
||||||
scriptExtraImportsProvider?.subscribeOnExtraImportsChanged { files ->
|
|
||||||
weakThis.get()?.apply {
|
|
||||||
cacheLock.write {
|
|
||||||
allScriptsClasspathCache = null
|
|
||||||
}
|
|
||||||
ProjectRootManagerEx.getInstanceEx(myProject)?.makeRootsChange(EmptyRunnable.getInstance(), false, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var allScriptsClasspathCache: List<VirtualFile>? = null
|
|
||||||
private val cacheLock = java.util.concurrent.locks.ReentrantReadWriteLock()
|
|
||||||
|
|
||||||
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> =
|
|
||||||
scriptExtraImportsProvider
|
|
||||||
?.getExtraImports(file)
|
|
||||||
?.flatMap { it.classpath }
|
|
||||||
?.map { it.classpathEntryToVfs() }
|
|
||||||
?: emptyList()
|
|
||||||
|
|
||||||
fun getAllScriptsClasspath(): List<VirtualFile> = cacheLock.read {
|
|
||||||
if (allScriptsClasspathCache == null) {
|
|
||||||
allScriptsClasspathCache =
|
|
||||||
(scriptExtraImportsProvider?.getKnownCombinedClasspath() ?: emptyList())
|
|
||||||
.distinct()
|
|
||||||
.mapNotNull { it.classpathEntryToVfs() }
|
|
||||||
}
|
|
||||||
return allScriptsClasspathCache!!
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun String.classpathEntryToVfs(): VirtualFile =
|
|
||||||
if (File(this).isDirectory)
|
|
||||||
StandardFileSystems.local()?.findFileByPath(this) ?: throw FileNotFoundException("Classpath entry points to a non-existent location: ${this}")
|
|
||||||
else
|
|
||||||
StandardFileSystems.jar()?.findFileByPath(this + URLUtil.JAR_SEPARATOR) ?: throw FileNotFoundException("Classpath entry points to a file that is not a JAR archive: ${this}")
|
|
||||||
|
|
||||||
fun getAllScriptsClasspathScope(): GlobalSearchScope? {
|
|
||||||
return getAllScriptsClasspath().let { cp ->
|
|
||||||
if (cp.isEmpty()) null
|
|
||||||
else GlobalSearchScope.union(cp.map { FileLibraryScope(myProject, it) }.toTypedArray())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun reloadScriptDefinitions() {
|
|
||||||
loadScriptConfigsFromProjectRoot(File(myProject.basePath ?: ".")).let {
|
|
||||||
if (it.isNotEmpty()) {
|
|
||||||
scriptDefinitionProvider.setScriptDefinitions(
|
|
||||||
it.map { KotlinConfigurableScriptDefinition(it, kotlinEnvVars) } + StandardScriptDefinition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun cacheAllScriptsExtraImports() {
|
|
||||||
fun<R> VirtualFile.vfsWalkFiles(onFile: (VirtualFile) -> List<R>?): List<R> {
|
|
||||||
assert(isDirectory)
|
|
||||||
return children.flatMap { when {
|
|
||||||
it.isDirectory -> it.vfsWalkFiles(onFile)
|
|
||||||
else -> onFile(it) ?: emptyList()
|
|
||||||
} }
|
|
||||||
}
|
|
||||||
myProject.baseDir.vfsWalkFiles {
|
|
||||||
scriptExtraImportsProvider?.getExtraImports(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun getInstance(project: Project): KotlinScriptConfigurationManager =
|
|
||||||
project.getComponent(KotlinScriptConfigurationManager::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class KotlinScriptDependenciesIndexableSetContributor : IndexableSetContributor() {
|
|
||||||
|
|
||||||
override fun getAdditionalProjectRootsToIndex(project: Project): Set<VirtualFile> =
|
|
||||||
KotlinScriptConfigurationManager.getInstance(project).getAllScriptsClasspath().toSet()
|
|
||||||
|
|
||||||
override fun getAdditionalRootsToIndex(): Set<VirtualFile> = emptySet()
|
|
||||||
}
|
|
||||||
|
|
||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.idea.core.script
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.ServiceManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.vfs.StandardFileSystems
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.search.FileTypeIndex
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.util.indexing.IndexableSetContributor
|
||||||
|
import com.intellij.util.io.URLUtil
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.FileLibraryScope
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
import org.jetbrains.kotlin.script.*
|
||||||
|
import org.jetbrains.kotlin.utils.PathUtil
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
import kotlin.concurrent.read
|
||||||
|
|
||||||
|
class KotlinScriptConfigurationManager(
|
||||||
|
private val project: Project,
|
||||||
|
private val scriptDefinitionProvider: KotlinScriptDefinitionProvider,
|
||||||
|
private val scriptExternalImportsProvider: KotlinScriptExternalImportsProvider?
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val kotlinEnvVars: Map<String, List<String>> by lazy {
|
||||||
|
generateKotlinScriptClasspathEnvVarsFromPaths(project, PathUtil.getKotlinPathsForIdeaPlugin())
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
reloadScriptDefinitions()
|
||||||
|
// TODO: sort out read/write action business and if possible make it lazy (e.g. move to getAllScriptsClasspath)
|
||||||
|
runReadAction { cacheAllScriptsExtraImports() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private var allScriptsClasspathCache: List<VirtualFile>? = null
|
||||||
|
private val cacheLock = ReentrantReadWriteLock()
|
||||||
|
|
||||||
|
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> =
|
||||||
|
scriptExternalImportsProvider
|
||||||
|
?.getExternalImports(file)
|
||||||
|
?.flatMap { it.classpath }
|
||||||
|
?.map { it.classpathEntryToVfs() }
|
||||||
|
?: emptyList()
|
||||||
|
|
||||||
|
fun getAllScriptsClasspath(): List<VirtualFile> = cacheLock.read {
|
||||||
|
if (allScriptsClasspathCache == null) {
|
||||||
|
allScriptsClasspathCache =
|
||||||
|
(scriptExternalImportsProvider?.getKnownCombinedClasspath() ?: emptyList())
|
||||||
|
.distinct()
|
||||||
|
.mapNotNull { it.classpathEntryToVfs() }
|
||||||
|
}
|
||||||
|
return allScriptsClasspathCache!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.classpathEntryToVfs(): VirtualFile =
|
||||||
|
if (File(this).isDirectory)
|
||||||
|
StandardFileSystems.local()?.findFileByPath(this) ?: throw FileNotFoundException("Classpath entry points to a non-existent location: ${this}")
|
||||||
|
else
|
||||||
|
StandardFileSystems.jar()?.findFileByPath(this + URLUtil.JAR_SEPARATOR) ?: throw FileNotFoundException("Classpath entry points to a file that is not a JAR archive: ${this}")
|
||||||
|
|
||||||
|
fun getAllScriptsClasspathScope(): GlobalSearchScope? {
|
||||||
|
return getAllScriptsClasspath().let { cp ->
|
||||||
|
if (cp.isEmpty()) null
|
||||||
|
else GlobalSearchScope.union(cp.map { FileLibraryScope(project, it) }.toTypedArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reloadScriptDefinitions() {
|
||||||
|
loadScriptConfigsFromProjectRoot(File(project.basePath ?: "")).let {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
scriptDefinitionProvider.setScriptDefinitions(
|
||||||
|
it.map { KotlinConfigurableScriptDefinition(it, kotlinEnvVars) } + StandardScriptDefinition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cacheAllScriptsExtraImports() {
|
||||||
|
scriptExternalImportsProvider?.apply {
|
||||||
|
invalidateCaches()
|
||||||
|
cacheExternalImports(
|
||||||
|
scriptDefinitionProvider.getAllKnownFileTypes()
|
||||||
|
.flatMap { FileTypeIndex.getFiles(it, GlobalSearchScope.allScope(project)) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun getInstance(project: Project): KotlinScriptConfigurationManager =
|
||||||
|
ServiceManager.getService(project, KotlinScriptConfigurationManager::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class KotlinScriptDependenciesIndexableSetContributor : IndexableSetContributor() {
|
||||||
|
|
||||||
|
override fun getAdditionalProjectRootsToIndex(project: Project): Set<VirtualFile> =
|
||||||
|
KotlinScriptConfigurationManager.getInstance(project).getAllScriptsClasspath().toSet()
|
||||||
|
|
||||||
|
override fun getAdditionalRootsToIndex(): Set<VirtualFile> = emptySet()
|
||||||
|
}
|
||||||
|
|
||||||
+4
-3
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.idea.script
|
package org.jetbrains.kotlin.idea.core.script
|
||||||
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.roots.ProjectFileIndex
|
import com.intellij.openapi.roots.ProjectFileIndex
|
||||||
@@ -26,18 +26,19 @@ import com.intellij.psi.search.EverythingGlobalScope
|
|||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.util.containers.ConcurrentFactoryMap
|
import com.intellij.util.containers.ConcurrentFactoryMap
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.ScriptModuleSearchScope
|
import org.jetbrains.kotlin.idea.caches.resolve.ScriptModuleSearchScope
|
||||||
|
import org.jetbrains.kotlin.idea.core.script.KotlinScriptConfigurationManager
|
||||||
import org.jetbrains.kotlin.load.java.JavaClassFinderImpl
|
import org.jetbrains.kotlin.load.java.JavaClassFinderImpl
|
||||||
import org.jetbrains.kotlin.resolve.jvm.KotlinSafeClassFinder
|
import org.jetbrains.kotlin.resolve.jvm.KotlinSafeClassFinder
|
||||||
|
|
||||||
class KotlinScriptDependenciesClassFinder(project: Project,
|
class KotlinScriptDependenciesClassFinder(project: Project,
|
||||||
private val kotlinScriptConfigurationManager: KotlinScriptConfigurationManager
|
private val kotlinScriptConfigurationManager: KotlinScriptConfigurationManager
|
||||||
) : NonClasspathClassFinder(project), KotlinSafeClassFinder {
|
) : NonClasspathClassFinder(project), KotlinSafeClassFinder {
|
||||||
|
|
||||||
private val myCaches = object : ConcurrentFactoryMap<VirtualFile, PackageDirectoryCache>() {
|
private val myCaches = object : ConcurrentFactoryMap<VirtualFile, PackageDirectoryCache>() {
|
||||||
override fun create(file: VirtualFile): PackageDirectoryCache? {
|
override fun create(file: VirtualFile): PackageDirectoryCache? {
|
||||||
|
|
||||||
val scriptClasspath = kotlinScriptConfigurationManager.getScriptClasspath(file)
|
val scriptClasspath = kotlinScriptConfigurationManager.getScriptClasspath(file)
|
||||||
val v = NonClasspathClassFinder.createCache(scriptClasspath)
|
val v = createCache(scriptClasspath)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,9 +40,6 @@
|
|||||||
<component>
|
<component>
|
||||||
<implementation-class>org.jetbrains.kotlin.asJava.KotlinCodeBlockModificationListener</implementation-class>
|
<implementation-class>org.jetbrains.kotlin.asJava.KotlinCodeBlockModificationListener</implementation-class>
|
||||||
</component>
|
</component>
|
||||||
<component>
|
|
||||||
<implementation-class>org.jetbrains.kotlin.idea.script.KotlinScriptConfigurationManager</implementation-class>
|
|
||||||
</component>
|
|
||||||
</project-components>
|
</project-components>
|
||||||
|
|
||||||
<application-components>
|
<application-components>
|
||||||
@@ -250,8 +247,11 @@
|
|||||||
<projectService serviceInterface="org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider"
|
<projectService serviceInterface="org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider"
|
||||||
serviceImplementation="org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider"/>
|
serviceImplementation="org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider"/>
|
||||||
|
|
||||||
<projectService serviceInterface="org.jetbrains.kotlin.script.KotlinScriptExtraImportsProvider"
|
<projectService serviceInterface="org.jetbrains.kotlin.script.KotlinScriptExternalImportsProvider"
|
||||||
serviceImplementation="org.jetbrains.kotlin.script.KotlinScriptExtraImportsProvider"/>
|
serviceImplementation="org.jetbrains.kotlin.script.KotlinScriptExternalImportsProvider"/>
|
||||||
|
|
||||||
|
<projectService serviceInterface="org.jetbrains.kotlin.idea.core.script.KotlinScriptConfigurationManager"
|
||||||
|
serviceImplementation="org.jetbrains.kotlin.idea.core.script.KotlinScriptConfigurationManager"/>
|
||||||
|
|
||||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder"
|
<projectService serviceInterface="org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder"
|
||||||
serviceImplementation="org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder"/>
|
serviceImplementation="org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder"/>
|
||||||
@@ -293,7 +293,7 @@
|
|||||||
<projectService serviceInterface="org.jetbrains.kotlin.console.KotlinConsoleKeeper"
|
<projectService serviceInterface="org.jetbrains.kotlin.console.KotlinConsoleKeeper"
|
||||||
serviceImplementation="org.jetbrains.kotlin.console.KotlinConsoleKeeper"/>
|
serviceImplementation="org.jetbrains.kotlin.console.KotlinConsoleKeeper"/>
|
||||||
|
|
||||||
<indexedRootsProvider implementation="org.jetbrains.kotlin.idea.script.KotlinScriptDependenciesIndexableSetContributor"/>
|
<indexedRootsProvider implementation="org.jetbrains.kotlin.idea.core.script.KotlinScriptDependenciesIndexableSetContributor"/>
|
||||||
|
|
||||||
<errorHandler implementation="org.jetbrains.kotlin.idea.reporter.KotlinReportSubmitter"/>
|
<errorHandler implementation="org.jetbrains.kotlin.idea.reporter.KotlinReportSubmitter"/>
|
||||||
|
|
||||||
@@ -575,7 +575,7 @@
|
|||||||
implementationClass="org.jetbrains.kotlin.idea.hierarchy.overrides.KotlinOverrideHierarchyProvider" />
|
implementationClass="org.jetbrains.kotlin.idea.hierarchy.overrides.KotlinOverrideHierarchyProvider" />
|
||||||
|
|
||||||
<java.elementFinder implementation="org.jetbrains.kotlin.asJava.JavaElementFinder"/>
|
<java.elementFinder implementation="org.jetbrains.kotlin.asJava.JavaElementFinder"/>
|
||||||
<java.elementFinder implementation="org.jetbrains.kotlin.idea.script.KotlinScriptDependenciesClassFinder"/>
|
<java.elementFinder implementation="org.jetbrains.kotlin.idea.core.script.KotlinScriptDependenciesClassFinder"/>
|
||||||
<java.shortNamesCache implementation="org.jetbrains.kotlin.idea.caches.KotlinShortNamesCache"/>
|
<java.shortNamesCache implementation="org.jetbrains.kotlin.idea.caches.KotlinShortNamesCache"/>
|
||||||
|
|
||||||
<stubElementTypeHolder class="org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes"/>
|
<stubElementTypeHolder class="org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user