Implement support for additional compiler arguments for scripts in IDE
fixes #KT-19120
This commit is contained in:
+13
-3
@@ -28,21 +28,22 @@ import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KParameter
|
||||
import kotlin.reflect.full.memberFunctions
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
import kotlin.script.dependencies.ScriptDependenciesResolver
|
||||
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
|
||||
import kotlin.script.experimental.dependencies.DependenciesResolver
|
||||
import kotlin.script.templates.AcceptedAnnotations
|
||||
import kotlin.script.templates.*
|
||||
|
||||
open class KotlinScriptDefinitionFromAnnotatedTemplate(
|
||||
template: KClass<out Any>,
|
||||
val environment: Map<String, Any?>? = null,
|
||||
val templateClasspath: List<File> = emptyList()
|
||||
) : KotlinScriptDefinition(template) {
|
||||
|
||||
val scriptFilePattern by lazy {
|
||||
val pattern =
|
||||
takeUnlessError {
|
||||
template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>()?.scriptFilePattern
|
||||
val ann = template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>()
|
||||
ann?.scriptFilePattern
|
||||
}
|
||||
?: takeUnlessError { template.annotations.firstIsInstanceOrNull<ScriptTemplateDefinition>()?.scriptFilePattern }
|
||||
?: DEFAULT_SCRIPT_FILE_PATTERN
|
||||
@@ -144,6 +145,15 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
|
||||
override val annotationsForSamWithReceivers: List<String>
|
||||
get() = samWithReceiverAnnotations ?: super.annotationsForSamWithReceivers
|
||||
|
||||
override val additionalCompilerArguments: Iterable<String>? by lazy {
|
||||
takeUnlessError {
|
||||
template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateAdditionalCompilerArguments>()?.let {
|
||||
val res = it.provider.primaryConstructor?.call(it.arguments.asIterable())
|
||||
res
|
||||
}
|
||||
}?.getAdditionalCompilerArguments(environment)
|
||||
}
|
||||
|
||||
private inline fun<T> takeUnlessError(reportError: Boolean = true, body: () -> T?): T? =
|
||||
try {
|
||||
body()
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.script
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
@@ -38,13 +39,15 @@ class ScriptContentLoader(private val project: Project) {
|
||||
private fun loadAnnotations(scriptDefinition: KotlinScriptDefinition, file: VirtualFile): List<Annotation> {
|
||||
val classLoader = scriptDefinition.template.java.classLoader
|
||||
// TODO_R: report error on failure to load annotation class
|
||||
return getAnnotationEntries(file, project)
|
||||
return ApplicationManager.getApplication().runReadAction<List<Annotation>> {
|
||||
getAnnotationEntries(file, project)
|
||||
.mapNotNull { psiAnn ->
|
||||
// TODO: consider advanced matching using semantic similar to actual resolving
|
||||
scriptDefinition.acceptedAnnotations.find { ann ->
|
||||
psiAnn.typeName.let { it == ann.simpleName || it == ann.qualifiedName }
|
||||
}?.let { constructAnnotation(psiAnn, classLoader.loadClass(it.qualifiedName).kotlin as KClass<out Annotation>) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAnnotationEntries(file: VirtualFile, project: Project): Iterable<KtAnnotationEntry> {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.script
|
||||
|
||||
import com.intellij.openapi.fileTypes.LanguageFileType
|
||||
import com.intellij.openapi.util.UserDataHolderBase
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.NameUtils
|
||||
@@ -26,7 +27,7 @@ import kotlin.reflect.KClass
|
||||
import kotlin.script.experimental.dependencies.DependenciesResolver
|
||||
import kotlin.script.templates.standard.ScriptTemplateWithArgs
|
||||
|
||||
open class KotlinScriptDefinition(val template: KClass<out Any>) {
|
||||
open class KotlinScriptDefinition(val template: KClass<out Any>) : UserDataHolderBase() {
|
||||
|
||||
open val name: String = "Kotlin Script"
|
||||
|
||||
@@ -45,6 +46,9 @@ open class KotlinScriptDefinition(val template: KClass<out Any>) {
|
||||
open val dependencyResolver: DependenciesResolver get() = DependenciesResolver.NoDependencies
|
||||
|
||||
open val acceptedAnnotations: List<KClass<out Annotation>> get() = emptyList()
|
||||
|
||||
@Deprecated("temporary workaround for missing functionality, will be replaced by the new API soon")
|
||||
open val additionalCompilerArguments: Iterable<String>? = null
|
||||
}
|
||||
|
||||
object StandardScriptDefinition : KotlinScriptDefinition(ScriptTemplateWithArgs::class)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2000-2018 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 kotlin.script.templates
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.script.dependencies.Environment
|
||||
|
||||
@Deprecated("temporary workaround for missing functionality, will be replaced by the new API soon")
|
||||
// Note: all subclasses should provide the same constructor
|
||||
open class ScriptTemplateAdditionalCompilerArgumentsProvider(val arguments: Iterable<String> = emptyList()) {
|
||||
open fun getAdditionalCompilerArguments(@Suppress("UNUSED_PARAMETER") environment: Environment?): Iterable<String> = arguments
|
||||
}
|
||||
|
||||
// Should be deprecated as well, but since we don't have replacement as of yet, leaving it as is
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class ScriptTemplateAdditionalCompilerArguments(
|
||||
val arguments: Array<String> = [],
|
||||
val provider: KClass<out ScriptTemplateAdditionalCompilerArgumentsProvider> = ScriptTemplateAdditionalCompilerArgumentsProvider::class
|
||||
)
|
||||
+39
@@ -18,10 +18,16 @@ package org.jetbrains.kotlin.idea.compiler
|
||||
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ProjectRootModificationTracker
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.util.CachedValue
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import org.jetbrains.kotlin.analyzer.LanguageSettingsProvider
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.cli.common.arguments.Jsr305Parser
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.config.AnalysisFlag
|
||||
import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider
|
||||
@@ -29,15 +35,18 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.TargetPlatformVersion
|
||||
import org.jetbrains.kotlin.idea.caches.project.LibraryInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.ScriptModuleInfo
|
||||
import org.jetbrains.kotlin.idea.project.getLanguageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.project.targetPlatform
|
||||
import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
|
||||
object IDELanguageSettingsProvider : LanguageSettingsProvider {
|
||||
override fun getLanguageVersionSettings(moduleInfo: ModuleInfo, project: Project): LanguageVersionSettings =
|
||||
when (moduleInfo) {
|
||||
is ModuleSourceInfo -> moduleInfo.module.languageVersionSettings
|
||||
is LibraryInfo -> project.getLanguageVersionSettings(extraAnalysisFlags = getExtraAnalysisFlags(project))
|
||||
is ScriptModuleInfo -> getVersionLanguageSettingsForScripts(project, moduleInfo.scriptDefinition)
|
||||
else -> project.getLanguageVersionSettings()
|
||||
}
|
||||
|
||||
@@ -60,3 +69,33 @@ object IDELanguageSettingsProvider : LanguageSettingsProvider {
|
||||
return (moduleInfo as? ModuleSourceInfo)?.module?.targetPlatform?.version ?: TargetPlatformVersion.NoVersion
|
||||
}
|
||||
}
|
||||
|
||||
private val LANGUAGE_VERSION_SETTINGS = Key.create<CachedValue<LanguageVersionSettings>>("LANGUAGE_VERSION_SETTINGS")
|
||||
|
||||
private fun getVersionLanguageSettingsForScripts(project: Project, scriptDefinition: KotlinScriptDefinition): LanguageVersionSettings {
|
||||
val args = scriptDefinition.additionalCompilerArguments
|
||||
return if (args == null || args.none()) {
|
||||
project.getLanguageVersionSettings()
|
||||
} else {
|
||||
val settings = scriptDefinition.getUserData(LANGUAGE_VERSION_SETTINGS) ?: createCachedValue(project) {
|
||||
val compilerArguments = K2JVMCompilerArguments()
|
||||
parseCommandLineArguments(args.toList(), compilerArguments)
|
||||
// TODO: reporting
|
||||
compilerArguments.configureLanguageVersionSettings(MessageCollector.NONE)
|
||||
}.also { scriptDefinition.putUserData(LANGUAGE_VERSION_SETTINGS, it) }
|
||||
settings.value
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCachedValue(project: Project, body: () -> LanguageVersionSettings): CachedValue<LanguageVersionSettings> {
|
||||
return CachedValuesManager
|
||||
.getManager(project)
|
||||
.createCachedValue(
|
||||
{
|
||||
CachedValueProvider.Result(
|
||||
body(),
|
||||
ProjectRootModificationTracker.getInstance(project)
|
||||
)
|
||||
}, false
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user