Make sure template classpath is always among script dependencies

This commit is contained in:
Pavel V. Talanov
2017-07-14 20:56:18 +03:00
parent f84cc7fb5b
commit b1ac451959
9 changed files with 85 additions and 18 deletions
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.NameUtils
import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KParameter
@@ -36,7 +37,8 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
template: KClass<out Any>,
providedResolver: DependenciesResolver? = null,
providedScriptFilePattern: String? = null,
val environment: Map<String, Any?>? = null
val environment: Map<String, Any?>? = null,
val templateClasspath: List<File> = emptyList()
) : KotlinScriptDefinition(template) {
val scriptFilePattern by lazy {
@@ -67,10 +67,18 @@ class ScriptContentLoader(private val project: Project) {
getEnvironment(scriptDef)
)
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports)
return result.dependencies
return result.dependencies?.adjustByDefinition(scriptDef)
}
fun getEnvironment(scriptDef: KotlinScriptDefinition) =
(scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.environment.orEmpty()
}
fun ScriptDependencies.adjustByDefinition(
scriptDef: KotlinScriptDefinition
): ScriptDependencies {
val additionalClasspath = (scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.templateClasspath ?: return this
if (additionalClasspath.isEmpty()) return this
return copy(classpath = additionalClasspath + classpath)
}
@@ -72,7 +72,10 @@ fun makeScriptDefsFromTemplatesProviders(providers: Iterable<ScriptTemplatesProv
try {
val loader = createClassLoader(provider)
provider.templateClassNames.map {
KotlinScriptDefinitionFromAnnotatedTemplate(loader.loadClass(it).kotlin, provider.resolver, provider.filePattern, provider.environment)
KotlinScriptDefinitionFromAnnotatedTemplate(
loader.loadClass(it).kotlin, provider.resolver,
provider.filePattern, provider.environment, provider.templateClasspath
)
}
}
catch (ex: Throwable) {
@@ -139,11 +139,23 @@ internal class ScriptDependenciesUpdater(
scriptDef: KotlinScriptDefinition
): TimeStampedJob {
val currentTimeStamp = TimeStamps.next()
fun process(result: DependenciesResolver.ResolveResult) {
val lastTimeStamp = requests[file.path]?.job?.timeStamp
val isLastSentRequest = lastTimeStamp == null || lastTimeStamp == currentTimeStamp
if (isLastSentRequest) {
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports)
val resultingDependencies = (result.dependencies ?: ScriptDependencies.Empty).adjustByDefinition(scriptDef)
if (cache(resultingDependencies, file)) {
onChange()
}
}
}
val dependenciesResolver = scriptDef.dependencyResolver
val newJob = if (dependenciesResolver is AsyncDependenciesResolver) {
launch(asyncUpdatesDispatcher) {
processResult(
file, currentTimeStamp,
process(
dependenciesResolver.resolveAsync(
contentLoader.getScriptContents(scriptDef, file),
contentLoader.getEnvironment(scriptDef)
@@ -154,8 +166,7 @@ internal class ScriptDependenciesUpdater(
else {
assert(dependenciesResolver is LegacyResolverWrapper)
launch(legacyUpdatesDispatcher) {
processResult(
file, currentTimeStamp,
process(
dependenciesResolver.resolve(
contentLoader.getScriptContents(scriptDef, file),
contentLoader.getEnvironment(scriptDef)
@@ -166,17 +177,6 @@ internal class ScriptDependenciesUpdater(
return TimeStampedJob(newJob, currentTimeStamp)
}
private fun processResult(file: VirtualFile, currentTimeStamp: TimeStamp, result: DependenciesResolver.ResolveResult) {
val lastTimeStamp = requests[file.path]?.job?.timeStamp
val isLastSentRequest = lastTimeStamp == null || lastTimeStamp == currentTimeStamp
if (isLastSentRequest) {
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports)
if (cache(result.dependencies ?: ScriptDependencies.Empty, file)) {
onChange()
}
}
}
fun updateSync(file: VirtualFile, scriptDef: KotlinScriptDefinition): Boolean {
val newDeps = contentLoader.loadContentsAndResolveDependencies(scriptDef, file) ?: ScriptDependencies.Empty
return cache(newDeps, file)
@@ -0,0 +1,3 @@
val j: Int = i
val s: String = str
@@ -0,0 +1,23 @@
package custom.scriptDefinition
import java.io.File
import kotlin.script.dependencies.*
import kotlin.script.dependencies.experimental.*
import kotlin.script.templates.ScriptTemplateDefinition
class TestDependenciesResolver : AsyncDependenciesResolver {
override suspend fun resolveAsync(
scriptContents: ScriptContents,
environment: Environment
): DependenciesResolver.ResolveResult {
return ScriptDependencies.Empty.asSuccess()
}
}
@ScriptTemplateDefinition(TestDependenciesResolver::class, scriptFilePattern = "script.kts")
open class Template: Base()
open class Base {
val i = 3
val str = ""
}
@@ -0,0 +1,3 @@
val j: Int = i
val s: String = str
@@ -0,0 +1,13 @@
package custom.scriptDefinition
import java.io.File
import kotlin.script.dependencies.*
import kotlin.script.templates.ScriptTemplateDefinition
@ScriptTemplateDefinition(scriptFilePattern = "script.kts")
class Template: Base()
open class Base {
val i = 3
val str = ""
}
@@ -78,6 +78,18 @@ public class ScriptConfigurationHighlightingTestGenerated extends AbstractScript
doTest(fileName);
}
@TestMetadata("emptyAsyncResolver")
public void testEmptyAsyncResolver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/script/definition/highlighting/emptyAsyncResolver/");
doTest(fileName);
}
@TestMetadata("noResolver")
public void testNoResolver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/script/definition/highlighting/noResolver/");
doTest(fileName);
}
@TestMetadata("simple")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/script/definition/highlighting/simple/");