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()
|
||||
})
|
||||
|
||||
KotlinScriptExtraImportsProvider.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))
|
||||
}
|
||||
|
||||
KotlinScriptExternalImportsProvider.getInstance(project)?.run {
|
||||
configuration.addJvmClasspathRoots(
|
||||
getCombinedClasspathFor(sourceFiles.mapNotNull { src -> src.virtualFile })
|
||||
getCombinedClasspathFor(sourceFiles)
|
||||
.map { File(it).canonicalFile }
|
||||
.distinct())
|
||||
}
|
||||
@@ -416,7 +410,7 @@ class KotlinCoreEnvironment private constructor(
|
||||
with (projectEnvironment.project) {
|
||||
val kotlinScriptDefinitionProvider = 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(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.modules.Module;
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition;
|
||||
import org.jetbrains.kotlin.script.KotlinScriptExtraImport;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class JVMConfigurationKeys {
|
||||
private JVMConfigurationKeys() {
|
||||
@@ -43,9 +41,6 @@ public class JVMConfigurationKeys {
|
||||
public static final CompilerConfigurationKey<List<KotlinScriptDefinition>> 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 =
|
||||
CompilerConfigurationKey.create("disable not-null call 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.SubpackagesImportingScope
|
||||
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.getValue
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
@@ -73,7 +73,7 @@ class FileScopeFactory(
|
||||
val explicitImportResolver = createImportResolver(ExplicitImportsIndexed(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 defaultImportsFiltered = if (aliasImportNames.isEmpty()) { // optimization
|
||||
|
||||
+19
-10
@@ -17,14 +17,6 @@
|
||||
package org.jetbrains.kotlin.script
|
||||
|
||||
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.name.Name
|
||||
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() }
|
||||
|
||||
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
|
||||
// TODO: fix and tests
|
||||
// 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
|
||||
else this.flatMap { cpentry ->
|
||||
varsMap.entries.fold(listOf(cpentry)) { p, v ->
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
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.psi.PsiFile
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -36,19 +38,39 @@ import java.io.File
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
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 getScriptSupertypes(scriptDescriptor: ScriptDescriptor): List<KotlinType> = emptyList()
|
||||
fun getScriptParametersToPassToSuperclass(scriptDescriptor: ScriptDescriptor): List<Name> = emptyList()
|
||||
fun <TF> isScript(file: TF): Boolean
|
||||
fun getScriptName(script: KtScript): Name
|
||||
fun getScriptDependenciesClasspath(): List<String> = emptyList()
|
||||
|
||||
fun getScriptName(script: KtScript): Name =
|
||||
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)
|
||||
|
||||
fun <TF> getFileExtension(file: TF) = PathUtil.getFileExtension(getFileName(file))
|
||||
|
||||
fun <TF> getFileName(file: TF): String = when (file) {
|
||||
is PsiFile -> file.originalFile.name
|
||||
is VirtualFile -> file.name
|
||||
@@ -56,17 +78,16 @@ fun <TF> getFileName(file: TF): String = when (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 {
|
||||
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
|
||||
// this is not supposed to be final design
|
||||
override fun getScriptParameters(scriptDescriptor: ScriptDescriptor): List<ScriptParameter> =
|
||||
|
||||
+7
-12
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.script
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.fileTypes.LanguageFileType
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiFile
|
||||
@@ -28,10 +29,8 @@ class KotlinScriptDefinitionProvider {
|
||||
|
||||
private val definitions: MutableList<KotlinScriptDefinition> = arrayListOf(StandardScriptDefinition)
|
||||
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
|
||||
definitionsLock.read {
|
||||
if (newDefinitions != definitions) {
|
||||
@@ -42,11 +41,7 @@ class KotlinScriptDefinitionProvider {
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
handlersLock.read {
|
||||
notificationHandlers.forEach { it() }
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
fun<TF> findScriptDefinition(file: TF): KotlinScriptDefinition? = definitionsLock.read {
|
||||
@@ -55,10 +50,6 @@ class KotlinScriptDefinitionProvider {
|
||||
|
||||
fun<TF> isScript(file: TF): Boolean = findScriptDefinition(file) != null
|
||||
|
||||
fun subscribeOnDefinitionsChanged(handler: () -> Unit): Unit {
|
||||
handlersLock.write { notificationHandlers.add(handler) }
|
||||
}
|
||||
|
||||
fun addScriptDefinition(scriptDefinition: KotlinScriptDefinition) {
|
||||
definitionsLock.write {
|
||||
definitions.add(0, scriptDefinition)
|
||||
@@ -71,6 +62,10 @@ class KotlinScriptDefinitionProvider {
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllKnownFileTypes(): Iterable<LanguageFileType> = definitionsLock.read {
|
||||
definitions.map { it.fileType }.distinct()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
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? =
|
||||
KotlinScriptDefinitionProvider.getInstance(psiFile.project).findScriptDefinition(psiFile)
|
||||
|
||||
fun getScriptExtraImports(file: VirtualFile, project: Project): List<KotlinScriptExtraImport> =
|
||||
KotlinScriptExtraImportsProvider.getInstance(project)?.getExtraImports(file) ?: emptyList()
|
||||
fun getScriptExternalDependencies(file: VirtualFile, project: Project): List<KotlinScriptExternalDependencies> =
|
||||
KotlinScriptExternalImportsProvider.getInstance(project)?.getExternalImports(file) ?: emptyList()
|
||||
|
||||
fun getScriptExtraImports(psiFile: PsiFile): List<KotlinScriptExtraImport> =
|
||||
fun getScriptExternalDependencies(psiFile: PsiFile): List<KotlinScriptExternalDependencies> =
|
||||
psiFile.virtualFile?.let { file ->
|
||||
KotlinScriptExtraImportsProvider.getInstance(psiFile.project)?.getExtraImports(file)
|
||||
KotlinScriptExternalImportsProvider.getInstance(psiFile.project)?.getExternalImports(file)
|
||||
} ?: emptyList()
|
||||
|
||||
|
||||
@@ -52,7 +52,8 @@ object SimpleUntypedAst {
|
||||
convert(expression.entries[0])
|
||||
else
|
||||
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)
|
||||
|
||||
@@ -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
|
||||
|
||||
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.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
|
||||
import org.jetbrains.kotlin.psi.KtAnnotation
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtScript
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import java.io.File
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class ScriptFilePattern(val pattern: String)
|
||||
|
||||
interface ScriptDependencies {
|
||||
val classpath: List<String>
|
||||
val implicitImports: List<String>
|
||||
}
|
||||
|
||||
interface GetScriptDependencies {
|
||||
operator fun invoke(annotations: Iterable<KtAnnotationEntry>, context: Any?): ScriptDependencies? = null
|
||||
operator fun invoke(context: Any?): ScriptDependencies? = null
|
||||
operator fun invoke(annotations: Iterable<KtAnnotationEntry>, context: Any?): KotlinScriptExternalDependencies? = null
|
||||
operator fun invoke(context: Any?): KotlinScriptExternalDependencies? = null
|
||||
}
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@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 {
|
||||
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)
|
||||
|
||||
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) }
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtScript
|
||||
@@ -26,15 +27,17 @@ import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
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 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 getScriptDependenciesClasspath(): List<String> =
|
||||
classpath ?: (classpathFromProperty() + classpathFromClassloader(BaseScriptDefinition::class.java.classLoader)).distinct()
|
||||
override fun <TF> getDependenciesFor(file: TF, project: Project): KotlinScriptExternalDependencies? =
|
||||
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)
|
||||
{
|
||||
override fun getScriptParameters(scriptDescriptor: ScriptDescriptor) = parameters
|
||||
@@ -74,10 +77,10 @@ class StandardWithClasspathScriptDefinition(extension: String, classpath: List<S
|
||||
StandardScriptDefinition.getScriptParameters(scriptDescriptor)
|
||||
}
|
||||
|
||||
class SimpleScriptExtraImport(
|
||||
class SimpleScriptExtraDependencies(
|
||||
override val classpath: List<String>,
|
||||
override val names: List<String> = emptyList()
|
||||
) : KotlinScriptExtraImport
|
||||
override val imports: List<String> = emptyList()
|
||||
) : KotlinScriptExternalDependencies
|
||||
|
||||
fun classpathFromProperty(): List<String> =
|
||||
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.codegen.CompilationException
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.addKotlinSourceRoot
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.script.KotlinScriptExtraImport
|
||||
import org.jetbrains.kotlin.script.ScriptParameter
|
||||
import org.jetbrains.kotlin.script.StandardScriptDefinition
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
@@ -45,14 +43,14 @@ import java.lang.reflect.InvocationTargetException
|
||||
class ScriptTest {
|
||||
@Test
|
||||
fun testScriptWithParam() {
|
||||
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()), null)
|
||||
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()))
|
||||
Assert.assertNotNull(aClass)
|
||||
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStandardScriptWithParams() {
|
||||
val aClass = compileScript("fib_std.kts", StandardScriptDefinition, null)
|
||||
val aClass = compileScript("fib_std.kts", StandardScriptDefinition)
|
||||
Assert.assertNotNull(aClass)
|
||||
val anObj = KotlinToJVMBytecodeCompiler.tryConstructClassPub(aClass!!, listOf("4", "comment"))
|
||||
Assert.assertNotNull(anObj)
|
||||
@@ -60,7 +58,7 @@ class ScriptTest {
|
||||
|
||||
@Test
|
||||
fun testStandardScriptWithoutParams() {
|
||||
val aClass = compileScript("fib_std.kts", StandardScriptDefinition, null)
|
||||
val aClass = compileScript("fib_std.kts", StandardScriptDefinition)
|
||||
Assert.assertNotNull(aClass)
|
||||
val anObj = KotlinToJVMBytecodeCompiler.tryConstructClassPub(aClass!!, emptyList())
|
||||
Assert.assertNotNull(anObj)
|
||||
@@ -68,7 +66,7 @@ class ScriptTest {
|
||||
|
||||
@Test
|
||||
fun testScriptWithParamConversion() {
|
||||
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()), null)
|
||||
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()))
|
||||
Assert.assertNotNull(aClass)
|
||||
val anObj = KotlinToJVMBytecodeCompiler.tryConstructClassPub(aClass!!, listOf("4"))
|
||||
Assert.assertNotNull(anObj)
|
||||
@@ -76,67 +74,67 @@ class ScriptTest {
|
||||
|
||||
@Test
|
||||
fun testScriptWithPackage() {
|
||||
val aClass = compileScript("fib.pkg.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()), null)
|
||||
val aClass = compileScript("fib.pkg.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()))
|
||||
Assert.assertNotNull(aClass)
|
||||
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScriptWithScriptDefinition() {
|
||||
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()), null)
|
||||
val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam()))
|
||||
Assert.assertNotNull(aClass)
|
||||
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
||||
}
|
||||
|
||||
@Test
|
||||
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)
|
||||
aClass!!.getConstructor(TestParamClass::class.java).newInstance(TestParamClass(4))
|
||||
}
|
||||
|
||||
@Test
|
||||
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)
|
||||
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
||||
}
|
||||
|
||||
@Test
|
||||
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)
|
||||
aClass!!.getConstructor(Integer.TYPE, Integer.TYPE).newInstance(4, 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
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)
|
||||
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
|
||||
}
|
||||
|
||||
@Test
|
||||
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)
|
||||
|
||||
val cp = classpathFromClassloader(ScriptTest::class.java.classLoader).filter { it.contains("kotlin-runtime") || it.contains("junit") }
|
||||
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)
|
||||
}
|
||||
|
||||
@Test
|
||||
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)
|
||||
|
||||
val cp = classpathFromClassloader(ScriptTest::class.java.classLoader).filter { it.contains("kotlin-runtime") || it.contains("junit") }
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -163,22 +161,20 @@ class ScriptTest {
|
||||
private fun compileScript(
|
||||
scriptPath: String,
|
||||
scriptDefinition: KotlinScriptDefinition,
|
||||
extraImport: KotlinScriptExtraImport?,
|
||||
runIsolated: Boolean = true,
|
||||
suppressOutput: Boolean = false): Class<*>? =
|
||||
compileScriptImpl("compiler/testData/script/" + scriptPath, scriptDefinition, extraImport, runIsolated, suppressOutput)
|
||||
compileScriptImpl("compiler/testData/script/" + scriptPath, scriptDefinition, runIsolated, suppressOutput)
|
||||
|
||||
private fun compileSmokeTestScript(
|
||||
scriptPath: String,
|
||||
scriptDefinition: KotlinScriptDefinition,
|
||||
runIsolated: Boolean = true,
|
||||
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(
|
||||
scriptPath: String,
|
||||
scriptDefinition: KotlinScriptDefinition,
|
||||
extraImport: KotlinScriptExtraImport?,
|
||||
runIsolated: Boolean,
|
||||
suppressOutput: Boolean): Class<*>?
|
||||
{
|
||||
@@ -193,10 +189,6 @@ class ScriptTest {
|
||||
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
|
||||
configuration.addKotlinSourceRoot(scriptPath)
|
||||
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)
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ class GetTestKotlinScriptDependencies : GetScriptDependencies {
|
||||
|
||||
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
|
||||
val anns = annotations.map { parseAnnotation(it) }.filter { it.name == depends::class.simpleName }
|
||||
val cp = anns.flatMap {
|
||||
@@ -129,9 +129,8 @@ class GetTestKotlinScriptDependencies : GetScriptDependencies {
|
||||
}
|
||||
}
|
||||
}
|
||||
return object : ScriptDependencies {
|
||||
return object : KotlinScriptExternalDependencies {
|
||||
override val classpath = cp
|
||||
override val implicitImports = emptyList<String>()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,30 +140,23 @@ class GetTestKotlinScriptDependencies : GetScriptDependencies {
|
||||
?.filter { it.contains("out/test") }
|
||||
?: emptyList()
|
||||
|
||||
override fun invoke(context: Any?): ScriptDependencies? {
|
||||
return object : ScriptDependencies {
|
||||
override fun invoke(context: Any?): KotlinScriptExternalDependencies? {
|
||||
return object : KotlinScriptExternalDependencies {
|
||||
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")
|
||||
@ScriptDependencyExtractor(GetTestKotlinScriptDependencies::class)
|
||||
@ScriptDependencyResolver(GetTestKotlinScriptDependencies::class)
|
||||
abstract class ScriptWithIntParam(num: Int)
|
||||
|
||||
@ScriptFilePattern(".*\\.kts")
|
||||
@ScriptDependencyExtractor(GetTestKotlinScriptDependencies::class)
|
||||
@ScriptDependencyResolver(GetTestKotlinScriptDependencies::class)
|
||||
abstract class ScriptWithClassParam(param: TestParamClass)
|
||||
|
||||
@ScriptFilePattern(".*\\.kts")
|
||||
@ScriptDependencyExtractor(GetTestKotlinScriptDependencies::class)
|
||||
@ScriptDependencyResolver(GetTestKotlinScriptDependencies::class)
|
||||
abstract class ScriptWithBaseClass(num: Int, passthrough: Int) : TestDSLClassWithParam(passthrough)
|
||||
|
||||
@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.impl.libraries.LibraryEx
|
||||
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.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.psi.search.DelegatingGlobalSearchScope
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.io.URLUtil
|
||||
import org.jdom.Element
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.script.KotlinScriptExtraImport
|
||||
import org.jetbrains.kotlin.utils.alwaysNull
|
||||
import org.jetbrains.kotlin.utils.emptyOrSingletonList
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.lang.reflect.Method
|
||||
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,
|
||||
val scriptDefinition: KotlinScriptDefinition,
|
||||
val scriptExtraImports: List<KotlinScriptExtraImport>) : IdeaModuleInfo {
|
||||
val scriptDefinition: KotlinScriptDefinition) : IdeaModuleInfo {
|
||||
override val moduleOrigin: ModuleOrigin
|
||||
get() = ModuleOrigin.OTHER
|
||||
|
||||
@@ -315,16 +309,17 @@ internal data class ScriptModuleInfo(val project: Project, val module: Module?,
|
||||
private fun dependenciesRoots(): List<VirtualFile> {
|
||||
// TODO: find out whether it should be cashed (some changes listener should be implemented for the cached roots)
|
||||
val jarfs = StandardFileSystems.jar()
|
||||
return (scriptDefinition.getScriptDependenciesClasspath() + scriptExtraImports.flatMap { it.classpath })
|
||||
.map { File(it).canonicalFile }
|
||||
.distinct()
|
||||
.mapNotNull {
|
||||
// TODO: ensure that the entries are checked elsewhere, so diagnostics is delivered to a user if files are not correctly specified
|
||||
if (it.isFile)
|
||||
jarfs.findFileByPath(it.absolutePath + URLUtil.JAR_SEPARATOR) ?: null // diag: Classpath entry points to a file that is not a JAR archive
|
||||
else
|
||||
StandardFileSystems.local().findFileByPath(it.absolutePath) ?: null // diag: Classpath entry points to a non-existent location
|
||||
}
|
||||
return scriptDefinition.getDependenciesFor(scriptFile, project)?.classpath
|
||||
?.map { File(it).canonicalFile }
|
||||
?.distinct()
|
||||
?.mapNotNull {
|
||||
// TODO: ensure that the entries are checked elsewhere, so diagnostics is delivered to a user if files are not correctly specified
|
||||
if (it.isFile)
|
||||
jarfs.findFileByPath(it.absolutePath + URLUtil.JAR_SEPARATOR) ?: null // diag: Classpath entry points to a file that is not a JAR archive
|
||||
else
|
||||
StandardFileSystems.local().findFileByPath(it.absolutePath) ?: null // diag: Classpath entry points to a non-existent location
|
||||
}
|
||||
?: emptyList()
|
||||
}
|
||||
|
||||
override fun dependencies() =
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||
import org.jetbrains.kotlin.script.getScriptDefinition
|
||||
import org.jetbrains.kotlin.script.getScriptExtraImports
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
|
||||
fun PsiElement.getModuleInfo(): IdeaModuleInfo = this.getModuleInfo { reason ->
|
||||
@@ -125,7 +124,7 @@ private fun getModuleInfoByVirtualFile(project: Project, virtualFile: VirtualFil
|
||||
|
||||
val scriptDefinition = getScriptDefinition(virtualFile, project)
|
||||
if (scriptDefinition != null)
|
||||
return ScriptModuleInfo(project, module, virtualFile, scriptDefinition, getScriptExtraImports(virtualFile, project))
|
||||
return ScriptModuleInfo(project, module, virtualFile, scriptDefinition)
|
||||
|
||||
return NotUnderContentRootModuleInfo
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.builtins.DefaultBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
|
||||
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.KotlinSourceFilterScope
|
||||
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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.script
|
||||
package org.jetbrains.kotlin.idea.core.script
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
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.util.containers.ConcurrentFactoryMap
|
||||
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.resolve.jvm.KotlinSafeClassFinder
|
||||
|
||||
class KotlinScriptDependenciesClassFinder(project: Project,
|
||||
private val kotlinScriptConfigurationManager: KotlinScriptConfigurationManager
|
||||
private val kotlinScriptConfigurationManager: KotlinScriptConfigurationManager
|
||||
) : NonClasspathClassFinder(project), KotlinSafeClassFinder {
|
||||
|
||||
private val myCaches = object : ConcurrentFactoryMap<VirtualFile, PackageDirectoryCache>() {
|
||||
override fun create(file: VirtualFile): PackageDirectoryCache? {
|
||||
|
||||
val scriptClasspath = kotlinScriptConfigurationManager.getScriptClasspath(file)
|
||||
val v = NonClasspathClassFinder.createCache(scriptClasspath)
|
||||
val v = createCache(scriptClasspath)
|
||||
return v
|
||||
}
|
||||
}
|
||||
@@ -40,9 +40,6 @@
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.kotlin.asJava.KotlinCodeBlockModificationListener</implementation-class>
|
||||
</component>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.kotlin.idea.script.KotlinScriptConfigurationManager</implementation-class>
|
||||
</component>
|
||||
</project-components>
|
||||
|
||||
<application-components>
|
||||
@@ -250,8 +247,11 @@
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider"
|
||||
serviceImplementation="org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider"/>
|
||||
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.script.KotlinScriptExtraImportsProvider"
|
||||
serviceImplementation="org.jetbrains.kotlin.script.KotlinScriptExtraImportsProvider"/>
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.script.KotlinScriptExternalImportsProvider"
|
||||
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"
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder"/>
|
||||
@@ -293,7 +293,7 @@
|
||||
<projectService serviceInterface="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"/>
|
||||
|
||||
@@ -575,7 +575,7 @@
|
||||
implementationClass="org.jetbrains.kotlin.idea.hierarchy.overrides.KotlinOverrideHierarchyProvider" />
|
||||
|
||||
<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"/>
|
||||
|
||||
<stubElementTypeHolder class="org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes"/>
|
||||
|
||||
Reference in New Issue
Block a user