Move CliScriptDefinition provider to the plugin too, fix tests
This commit is contained in:
+6
-4
@@ -10,9 +10,8 @@ import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.fileTypes.FileTypeRegistry
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.script.CliScriptDefinitionProvider
|
||||
import org.jetbrains.kotlin.cli.common.script.CliScriptDependenciesProvider
|
||||
import org.jetbrains.kotlin.cli.common.script.CliScriptReportSink
|
||||
import org.jetbrains.kotlin.scripting.legacy.CliScriptDependenciesProvider
|
||||
import org.jetbrains.kotlin.scripting.legacy.CliScriptReportSink
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
@@ -23,6 +22,7 @@ import org.jetbrains.kotlin.script.ScriptDefinitionProvider
|
||||
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
|
||||
import org.jetbrains.kotlin.script.ScriptReportSink
|
||||
import org.jetbrains.kotlin.script.StandardScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.legacy.CliScriptDefinitionProvider
|
||||
import java.io.File
|
||||
|
||||
class ScriptingCompilerConfigurationExtension(val project: MockProject) : CompilerConfigurationExtension {
|
||||
@@ -108,7 +108,9 @@ class ScriptingCompilerConfigurationComponentRegistrar : ComponentRegistrar {
|
||||
|
||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
if (messageCollector != null) {
|
||||
project.registerService(ScriptReportSink::class.java, CliScriptReportSink(messageCollector))
|
||||
project.registerService(ScriptReportSink::class.java,
|
||||
CliScriptReportSink(messageCollector)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.legacy
|
||||
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.script.ScriptDefinitionsSource
|
||||
import org.jetbrains.kotlin.script.StandardScriptDefinition
|
||||
import kotlin.concurrent.write
|
||||
|
||||
class CliScriptDefinitionProvider : LazyScriptDefinitionProvider() {
|
||||
private val definitionsFromSources: MutableList<Sequence<KotlinScriptDefinition>> = arrayListOf()
|
||||
private val definitions: MutableList<KotlinScriptDefinition> = arrayListOf(StandardScriptDefinition)
|
||||
|
||||
override val currentDefinitions: Sequence<KotlinScriptDefinition> =
|
||||
definitionsFromSources.asSequence().flatMap { it } + definitions.asSequence()
|
||||
|
||||
override fun getDefaultScriptDefinition(): KotlinScriptDefinition {
|
||||
return StandardScriptDefinition
|
||||
}
|
||||
|
||||
fun setScriptDefinitions(newDefinitions: List<KotlinScriptDefinition>) {
|
||||
lock.write {
|
||||
definitions.clear()
|
||||
definitions.addAll(newDefinitions)
|
||||
}
|
||||
}
|
||||
|
||||
fun setScriptDefinitionsSources(newSources: List<ScriptDefinitionsSource>) {
|
||||
lock.write {
|
||||
definitionsFromSources.clear()
|
||||
for (it in newSources) {
|
||||
definitionsFromSources.add(it.definitions.constrainOnce())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.legacy
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.script.*
|
||||
import java.io.File
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
import kotlin.script.experimental.dependencies.ScriptDependencies
|
||||
|
||||
class CliScriptDependenciesProvider(private val project: Project) : ScriptDependenciesProvider {
|
||||
private val cacheLock = ReentrantReadWriteLock()
|
||||
private val cache = hashMapOf<String, ScriptDependencies?>()
|
||||
private val scriptContentLoader = ScriptContentLoader(project)
|
||||
|
||||
override fun getScriptDependencies(file: VirtualFile): ScriptDependencies? = cacheLock.read {
|
||||
calculateExternalDependencies(file)
|
||||
}
|
||||
|
||||
private fun calculateExternalDependencies(file: VirtualFile): ScriptDependencies? {
|
||||
val path = file.path
|
||||
val cached = cache[path]
|
||||
return if (cached != null) cached
|
||||
else {
|
||||
val scriptDef = findScriptDefinition(file, project)
|
||||
if (scriptDef != null) {
|
||||
val result = scriptContentLoader.loadContentsAndResolveDependencies(scriptDef, file)
|
||||
|
||||
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports)
|
||||
|
||||
val deps = result.dependencies?.adjustByDefinition(scriptDef)
|
||||
|
||||
if (deps != null) {
|
||||
log.info("[kts] new cached deps for $path: ${deps.classpath.joinToString(File.pathSeparator)}")
|
||||
}
|
||||
cacheLock.write {
|
||||
cache.put(path, deps)
|
||||
}
|
||||
deps
|
||||
} else null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val log = Logger.getInstance(ScriptDependenciesProvider::class.java)
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.legacy
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.script.ScriptReportSink
|
||||
import kotlin.script.experimental.dependencies.ScriptReport
|
||||
|
||||
internal class CliScriptReportSink(private val messageCollector: MessageCollector) : ScriptReportSink {
|
||||
override fun attachReports(scriptFile: VirtualFile, reports: List<ScriptReport>) {
|
||||
reports.forEach {
|
||||
messageCollector.report(it.severity.convertSeverity(), it.message, location(scriptFile, it.position))
|
||||
}
|
||||
}
|
||||
|
||||
private fun location(scriptFile: VirtualFile, position: ScriptReport.Position?): CompilerMessageLocation? {
|
||||
if (position == null) return CompilerMessageLocation.create(scriptFile.path)
|
||||
|
||||
return CompilerMessageLocation.create(scriptFile.path, position.startLine, position.startColumn, null)
|
||||
}
|
||||
|
||||
private fun ScriptReport.Severity.convertSeverity(): CompilerMessageSeverity = when (this) {
|
||||
ScriptReport.Severity.FATAL -> CompilerMessageSeverity.ERROR
|
||||
ScriptReport.Severity.ERROR -> CompilerMessageSeverity.ERROR
|
||||
ScriptReport.Severity.WARNING -> CompilerMessageSeverity.WARNING
|
||||
ScriptReport.Severity.INFO -> CompilerMessageSeverity.INFO
|
||||
ScriptReport.Severity.DEBUG -> CompilerMessageSeverity.LOGGING
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.scripting.legacy
|
||||
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
abstract class LazyScriptDefinitionProvider : ScriptDefinitionProvider {
|
||||
|
||||
protected val lock = ReentrantReadWriteLock()
|
||||
|
||||
protected abstract val currentDefinitions: Sequence<KotlinScriptDefinition>
|
||||
|
||||
private var _cachedDefinitions: Sequence<KotlinScriptDefinition>? = null
|
||||
private val cachedDefinitions: Sequence<KotlinScriptDefinition>
|
||||
get() {
|
||||
assert(lock.readLockCount > 0) { "cachedDefinitions should only be used under the read lock" }
|
||||
if (_cachedDefinitions == null) lock.write {
|
||||
_cachedDefinitions = CachingSequence(currentDefinitions.constrainOnce())
|
||||
}
|
||||
return _cachedDefinitions!!
|
||||
}
|
||||
|
||||
protected fun clearCache() {
|
||||
lock.write {
|
||||
_cachedDefinitions = null
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun nonScriptFileName(fileName: String) = nonScriptFilenameSuffixes.any {
|
||||
fileName.endsWith(it, ignoreCase = true)
|
||||
}
|
||||
|
||||
override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? =
|
||||
if (nonScriptFileName(fileName)) null
|
||||
else lock.read {
|
||||
cachedDefinitions.firstOrNull { it.isScript(fileName) }
|
||||
}
|
||||
|
||||
override fun isScript(fileName: String) = findScriptDefinition(fileName) != null
|
||||
|
||||
override fun getKnownFilenameExtensions(): Sequence<String> = lock.read {
|
||||
cachedDefinitions.map { it.fileExtension }
|
||||
}
|
||||
|
||||
companion object {
|
||||
// TODO: find a common place for storing kotlin-related extensions and reuse values from it everywhere
|
||||
protected val nonScriptFilenameSuffixes = arrayOf(".${KotlinFileType.EXTENSION}", ".${JavaFileType.DEFAULT_EXTENSION}")
|
||||
}
|
||||
}
|
||||
|
||||
private class CachingSequence<T>(from: Sequence<T>) : Sequence<T> {
|
||||
|
||||
private val lock = ReentrantReadWriteLock()
|
||||
private val sequenceIterator = from.iterator()
|
||||
private val cache = arrayListOf<T>()
|
||||
|
||||
private inner class CachingIterator : Iterator<T> {
|
||||
|
||||
private var cacheCursor = 0
|
||||
|
||||
override fun hasNext(): Boolean =
|
||||
lock.read { cacheCursor < cache.size }
|
||||
// iterator's hasNext can mutate the iterator's state, therefore write lock is needed
|
||||
|| lock.write { cacheCursor < cache.size || sequenceIterator.hasNext() }
|
||||
|
||||
override fun next(): T {
|
||||
lock.read {
|
||||
if (cacheCursor < cache.size) return cache[cacheCursor++]
|
||||
}
|
||||
// lock.write is not an upgrade but retake, therefore - one more check needed
|
||||
lock.write {
|
||||
return if (cacheCursor < cache.size) cache[cacheCursor++]
|
||||
else sequenceIterator.next().also { cache.add(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<T> = CachingIterator()
|
||||
}
|
||||
Reference in New Issue
Block a user