Refactor script definitions loading

Make ScriptDefinitionProvider an interface
    and provide different implementation for cli and IDEA
Rework ide extension point to a simpler interface (ScriptDefinitionContributor)
Move template loading logic into a top level function
Allow script definitions to be reloaded in IDE
This commit is contained in:
Pavel V. Talanov
2017-10-19 20:02:30 +03:00
parent f063254cc1
commit cdfdc1d19b
32 changed files with 481 additions and 419 deletions
@@ -0,0 +1,44 @@
/*
* 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.cli.common.script
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.script.StandardScriptDefinition
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
class CliScriptDefinitionProvider : ScriptDefinitionProvider {
private val definitions: MutableList<KotlinScriptDefinition> = arrayListOf(StandardScriptDefinition)
private val lock = ReentrantReadWriteLock()
override fun findScriptDefinition(fileName: String) = lock.read {
definitions.firstOrNull { it.isScript(fileName) }
}
override fun isScript(fileName: String) = lock.read {
definitions.any { it.isScript(fileName) }
}
fun setScriptDefinitions(newDefinitions: List<KotlinScriptDefinition>) {
lock.write {
definitions.clear()
definitions.addAll(newDefinitions)
}
}
}
@@ -19,9 +19,9 @@ package org.jetbrains.kotlin.cli.common.script
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import org.jetbrains.kotlin.script.ScriptContentLoader
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
@@ -30,7 +30,7 @@ import kotlin.script.experimental.dependencies.ScriptDependencies
class CliScriptDependenciesProvider(
project: Project,
private val scriptDefinitionProvider: KotlinScriptDefinitionProvider
private val scriptDefinitionProvider: ScriptDefinitionProvider
) : ScriptDependenciesProvider {
private val cacheLock = ReentrantReadWriteLock()
@@ -47,7 +47,7 @@ import org.jetbrains.kotlin.javac.JavacWrapper
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.script.StandardScriptDefinition
import org.jetbrains.kotlin.util.PerformanceCounter
import org.jetbrains.kotlin.utils.KotlinPaths
@@ -166,7 +166,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
val environment = createEnvironmentWithScriptingSupport(rootDisposable, configuration, arguments, messageCollector)
?: return COMPILATION_ERROR
val scriptDefinitionProvider = KotlinScriptDefinitionProvider.getInstance(environment.project)!!
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(environment.project)!!
val scriptFile = File(sourcePath)
if (scriptFile.isDirectory || !scriptDefinitionProvider.isScript(scriptFile.name)) {
val extensionHint =
@@ -460,7 +460,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
for (template in scriptTemplates) {
try {
val cls = classloader.loadClass(template)
val def = KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, null, null, scriptResolverEnv)
val def = KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, scriptResolverEnv)
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, def)
messageCollector.report(
INFO,
@@ -29,7 +29,6 @@ import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.lang.MetaLanguage
import com.intellij.lang.java.JavaParserDefinition
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.TransactionGuard
import com.intellij.openapi.application.TransactionGuardImpl
import com.intellij.openapi.components.ServiceManager
@@ -39,7 +38,6 @@ import com.intellij.openapi.fileTypes.FileTypeExtensionPoint
import com.intellij.openapi.fileTypes.PlainTextFileType
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.*
@@ -71,6 +69,7 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING
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.cli.common.toBooleanLenient
@@ -104,7 +103,7 @@ import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtens
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import org.jetbrains.kotlin.script.ScriptReportSink
import org.jetbrains.kotlin.utils.PathUtil
@@ -191,7 +190,8 @@ class KotlinCoreEnvironment private constructor(
})
sourceFiles.sortBy { it.virtualFile.path }
KotlinScriptDefinitionProvider.getInstance(project)?.let { scriptDefinitionProvider ->
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
if (scriptDefinitionProvider != null) {
scriptDefinitionProvider.setScriptDefinitions(
configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS))
@@ -554,9 +554,9 @@ class KotlinCoreEnvironment private constructor(
@JvmStatic
fun registerProjectServices(projectEnvironment: JavaCoreProjectEnvironment, messageCollector: MessageCollector?) {
with (projectEnvironment.project) {
val kotlinScriptDefinitionProvider = KotlinScriptDefinitionProvider()
registerService(KotlinScriptDefinitionProvider::class.java, kotlinScriptDefinitionProvider)
registerService(ScriptDependenciesProvider::class.java, CliScriptDependenciesProvider(projectEnvironment.project, kotlinScriptDefinitionProvider))
val scriptDefinitionProvider = CliScriptDefinitionProvider()
registerService(ScriptDefinitionProvider::class.java, scriptDefinitionProvider)
registerService(ScriptDependenciesProvider::class.java, CliScriptDependenciesProvider(projectEnvironment.project, scriptDefinitionProvider))
registerService(KotlinJavaPsiFacade::class.java, KotlinJavaPsiFacade(this))
registerService(KtLightClassForFacade.FacadeStubCache::class.java, KtLightClassForFacade.FacadeStubCache(this))
if (messageCollector != null) {
@@ -63,7 +63,7 @@ open class KotlinJvmReplService(
try {
val cls = classloader.loadClass(templateClassName)
val def = KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, null, null, emptyMap())
val def = KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, emptyMap())
messageCollector.report(INFO, "New script definition $templateClassName: files pattern = \"${def.scriptFilePattern}\", " +
"resolver = ${def.dependencyResolver.javaClass.name}")
return def
@@ -35,21 +35,17 @@ import kotlin.script.templates.AcceptedAnnotations
open class KotlinScriptDefinitionFromAnnotatedTemplate(
template: KClass<out Any>,
providedResolver: DependenciesResolver? = null,
providedScriptFilePattern: String? = null,
val environment: Map<String, Any?>? = null,
val templateClasspath: List<File> = emptyList()
) : KotlinScriptDefinition(template) {
val scriptFilePattern by lazy {
providedScriptFilePattern
?: takeUnlessError { template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>()?.scriptFilePattern }
takeUnlessError { template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>()?.scriptFilePattern }
?: takeUnlessError { template.annotations.firstIsInstanceOrNull<org.jetbrains.kotlin.script.ScriptTemplateDefinition>()?.scriptFilePattern }
?: DEFAULT_SCRIPT_FILE_PATTERN
}
override val dependencyResolver: DependenciesResolver by lazy {
providedResolver ?:
resolverFromAnnotation(template) ?:
resolverFromLegacyAnnotation(template) ?:
DependenciesResolver.NoDependencies
@@ -1,95 +0,0 @@
/*
* 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.script
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.project.Project
import java.io.File
import java.net.URLClassLoader
import kotlin.script.experimental.dependencies.DependenciesResolver
interface ScriptTemplatesProvider {
// for resolving ambiguities
val id: String
@Deprecated("Parameter isn't used for resolving priorities anymore. " +
"com.intellij.openapi.extensions.LoadingOrder constants can be used to order providers when registered from Intellij plugin.",
ReplaceWith("0"))
val version: Int get() = 0
val isValid: Boolean
val templateClassNames: Iterable<String>
val resolver: DependenciesResolver? get() = null
val filePattern: String? get() = null
val templateClasspath: List<File>
// TODO: need to provide a way to specify this in compiler/repl .. etc
/*
* Allows to specify additional jars needed for DependenciesResolver (and not script template).
* Script template dependencies naturally become (part of) dependencies of the script which is not always desired for resolver dependencies.
* i.e. gradle resolver may depend on some jars that 'built.gradle.kts' files should not depend on.
*/
val additionalResolverClasspath: List<File> get() = emptyList()
val environment: Map<String, Any?>?
companion object {
val EP_NAME: ExtensionPointName<ScriptTemplatesProvider> =
ExtensionPointName.create<ScriptTemplatesProvider>("org.jetbrains.kotlin.scriptTemplatesProvider")
}
}
fun makeScriptDefsFromTemplatesProviderExtensions(project: Project,
errorsHandler: ((ScriptTemplatesProvider, Throwable) -> Unit)
): List<KotlinScriptDefinition> =
makeScriptDefsFromTemplatesProviders(Extensions.getArea(project).getExtensionPoint(ScriptTemplatesProvider.EP_NAME).extensions.asIterable(),
errorsHandler)
fun makeScriptDefsFromTemplatesProviders(providers: Iterable<ScriptTemplatesProvider>,
errorsHandler: ((ScriptTemplatesProvider, Throwable) -> Unit) = { _, ex -> throw ex }
): List<KotlinScriptDefinition> = providers.flatMap { provider ->
try {
val loader = createClassLoader(provider)
provider.templateClassNames.map {
KotlinScriptDefinitionFromAnnotatedTemplate(
loader.loadClass(it).kotlin, provider.resolver,
provider.filePattern, provider.environment, provider.templateClasspath
)
}
}
catch (ex: Throwable) {
LOG.info("Templates provider ${provider.id} is invalid: ${ex.message}")
errorsHandler(provider, ex)
emptyList<KotlinScriptDefinition>()
}
}
private fun createClassLoader(provider: ScriptTemplatesProvider): ClassLoader {
val classpath = provider.templateClasspath + provider.additionalResolverClasspath
LOG.info("[kts] loading script definitions ${provider.templateClassNames} using cp: ${classpath.joinToString(File.pathSeparator)}")
val baseLoader = ScriptTemplatesProvider::class.java.classLoader
return if (classpath.isEmpty()) baseLoader else URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), baseLoader)
}
private val LOG = Logger.getInstance("ScriptTemplatesProviders")
@@ -23,14 +23,14 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider;
import org.jetbrains.kotlin.script.ScriptDefinitionProvider;
public class KotlinParser implements PsiParser {
private final KotlinScriptDefinitionProvider scriptDefinitionProvider;
private final ScriptDefinitionProvider scriptDefinitionProvider;
public KotlinParser(Project project) {
scriptDefinitionProvider = KotlinScriptDefinitionProvider.getInstance(project);
scriptDefinitionProvider = ScriptDefinitionProvider.Companion.getInstance(project);
}
@Override
@@ -17,67 +17,23 @@
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
import kotlin.concurrent.read
import kotlin.concurrent.write
class KotlinScriptDefinitionProvider {
private val definitions: MutableList<KotlinScriptDefinition> = arrayListOf(StandardScriptDefinition)
private val definitionsLock = java.util.concurrent.locks.ReentrantReadWriteLock()
fun setScriptDefinitions(newDefinitions: List<KotlinScriptDefinition>): Boolean {
var changed = false
definitionsLock.read {
if (newDefinitions != definitions) {
definitionsLock.write {
definitions.clear()
definitions.addAll(newDefinitions)
}
changed = true
}
}
return changed
}
interface ScriptDefinitionProvider {
fun findScriptDefinition(fileName: String): KotlinScriptDefinition?
fun isScript(fileName: String): Boolean
fun findScriptDefinition(file: VirtualFile): KotlinScriptDefinition? = findScriptDefinition(file.name)
fun findScriptDefinition(fileName: String): KotlinScriptDefinition? = definitionsLock.read {
definitions.firstOrNull { it.isScript(fileName) }
}
fun isScript(fileName: String): Boolean = definitionsLock.read {
definitions.any { it.isScript(fileName) }
}
fun addScriptDefinition(scriptDefinition: KotlinScriptDefinition) {
definitionsLock.write {
definitions.add(0, scriptDefinition)
}
}
fun removeScriptDefinition(scriptDefinition: KotlinScriptDefinition) {
definitionsLock.write {
definitions.remove(scriptDefinition)
}
}
fun getAllKnownFileTypes(): Iterable<LanguageFileType> = definitionsLock.read {
definitions.map { it.fileType }.distinct()
}
companion object {
@JvmStatic
fun getInstance(project: Project): KotlinScriptDefinitionProvider? =
ServiceManager.getService(project, KotlinScriptDefinitionProvider::class.java)
fun getInstance(project: Project): ScriptDefinitionProvider =
ServiceManager.getService(project, ScriptDefinitionProvider::class.java)
}
}
fun getScriptDefinition(file: VirtualFile, project: Project): KotlinScriptDefinition? =
KotlinScriptDefinitionProvider.getInstance(project)?.findScriptDefinition(file)
ScriptDefinitionProvider.getInstance(project).findScriptDefinition(file)
fun getScriptDefinition(psiFile: PsiFile): KotlinScriptDefinition? =
KotlinScriptDefinitionProvider.getInstance(psiFile.project)?.findScriptDefinition(psiFile.name)
ScriptDefinitionProvider.getInstance(psiFile.project).findScriptDefinition(psiFile.name)
@@ -25,8 +25,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.checkers.CheckerTestUtil;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.resolve.AnalyzingUtils;
import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider;
import org.jetbrains.kotlin.script.StandardScriptDefinition;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import java.io.IOException;
@@ -133,10 +131,6 @@ public class CodegenTestFiles {
}
}
KotlinScriptDefinitionProvider scriptDefinitionProvider = KotlinScriptDefinitionProvider.getInstance(project);
assert scriptDefinitionProvider != null;
scriptDefinitionProvider.addScriptDefinition(StandardScriptDefinition.INSTANCE);
return new CodegenTestFiles(Collections.singletonList(file), expectedValues, scriptParameterValues);
}
}
@@ -184,7 +184,7 @@ internal class TestRepl(
private fun makeScriptDefinition(templateClasspath: List<File>, templateClassName: String): KotlinScriptDefinition {
val classloader = URLClassLoader(templateClasspath.map { it.toURI().toURL() }.toTypedArray(), this::class.java.classLoader)
val cls = classloader.loadClass(templateClassName)
return KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, null, null, emptyMap())
return KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, emptyMap())
}
private val scriptDef = makeScriptDefinition(templateClasspath, templateClassName)
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.codegen
import java.io.File
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
@@ -26,16 +25,18 @@ import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.addKotlinSourceRoots
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.scripts.ScriptWithIntParam
import org.jetbrains.kotlin.scripts.TestKotlinScriptDependenciesResolver
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.org.objectweb.asm.Opcodes
import java.io.File
import kotlin.script.templates.ScriptTemplateDefinition
class ScriptGenTest : CodegenTestCase() {
companion object {
private val FIB_SCRIPT_DEFINITION = KotlinScriptDefinitionFromAnnotatedTemplate(ScriptWithIntParam::class, providedScriptFilePattern = ".*\\.lang\\.kt")
private val NO_PARAM_SCRIPT_DEFINITION = KotlinScriptDefinitionFromAnnotatedTemplate(Any::class, providedScriptFilePattern = ".*\\.kts")
private val FIB_SCRIPT_DEFINITION = KotlinScriptDefinitionFromAnnotatedTemplate(ScriptWithIntParam::class)
private val NO_PARAM_SCRIPT_DEFINITION = KotlinScriptDefinitionFromAnnotatedTemplate(Any::class)
}
override fun setUp() {
@@ -124,3 +125,8 @@ class ScriptGenTest : CodegenTestCase() {
loadFiles(*sourcePaths.toTypedArray())
}
}
@ScriptTemplateDefinition(
scriptFilePattern =".*\\.lang\\.kt",
resolver = TestKotlinScriptDependenciesResolver::class)
abstract class ScriptWithIntParam(val num: Int)
@@ -28,8 +28,9 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.PathUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.KtNodeTypes;
import org.jetbrains.kotlin.cli.common.script.CliScriptDefinitionProvider;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider;
import org.jetbrains.kotlin.script.ScriptDefinitionProvider;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.testFramework.KtParsingTestCase;
@@ -41,7 +42,7 @@ public abstract class AbstractParsingTest extends KtParsingTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
getProject().registerService(KotlinScriptDefinitionProvider.class);
getProject().registerService(ScriptDefinitionProvider.class, CliScriptDefinitionProvider.class);
}
@Override
@@ -333,7 +333,7 @@ class ScriptTemplateTest : KtUsefulTestCase() {
includeKotlinRuntime: Boolean = true
): Class<*>? =
compileScriptImpl("compiler/testData/script/" + scriptPath, KotlinScriptDefinitionFromAnnotatedTemplate(
scriptTemplate, null, null, environment
scriptTemplate, environment
), runIsolated, messageCollector, includeKotlinRuntime)
private fun compileScriptImpl(
@@ -0,0 +1,190 @@
/*
* 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.idea.core.script
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.JavaSdk
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.projectRoots.ex.PathUtilEx
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
import java.io.File
import java.net.URLClassLoader
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
import kotlin.script.dependencies.Environment
import kotlin.script.dependencies.ScriptContents
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.dependencies.asSuccess
import kotlin.script.templates.standard.ScriptTemplateWithArgs
class ScriptDefinitionsManager(private val project: Project): ScriptDefinitionProvider {
private val lock = ReentrantReadWriteLock()
private var definitionsByContributor = mutableMapOf<ScriptDefinitionContributor, List<KotlinScriptDefinition>>()
private var definitions: List<KotlinScriptDefinition> = emptyList()
fun reloadDefinitionsBy(contributor: ScriptDefinitionContributor) = lock.write {
currentDefinitions() //force definitions to be computed
if (contributor !in definitionsByContributor) error("Unknown contributor: ${contributor.id}")
definitionsByContributor[contributor] = contributor.safeGetDefinitions()
updateDefinitions()
}
private fun currentDefinitions(): List<KotlinScriptDefinition> {
val hasDefinitions = definitions.isNotEmpty()
when {
hasDefinitions -> return definitions
else -> {
reloadScriptDefinitions()
return definitions
}
}
}
override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? = lock.read {
currentDefinitions().firstOrNull { it.isScript(fileName) }
}
override fun isScript(fileName: String) = lock.read {
currentDefinitions().any { it.isScript(fileName) }
}
private fun getContributors(): List<ScriptDefinitionContributor> =
Extensions.getArea(project).getExtensionPoint(ScriptDefinitionContributor.EP_NAME).extensions.toList()
fun reloadScriptDefinitions() = lock.write {
definitionsByContributor = getContributors().associateByTo(mutableMapOf(), { it }, { it.safeGetDefinitions() })
updateDefinitions()
}
private fun updateDefinitions() {
definitions = definitionsByContributor.values.flattenTo(mutableListOf())
// TODO: clear by script type/definition
project.service<ScriptDependenciesUpdater>().clear()
}
private fun ScriptDefinitionContributor.safeGetDefinitions(): List<KotlinScriptDefinition> {
return try {
getDefinitions()
}
catch (t: Throwable) {
// TODO: review exception handling
// possibly log, see KT-19276
emptyList()
}
}
companion object {
fun getInstance(project: Project): ScriptDefinitionsManager = project.service<ScriptDefinitionProvider>() as ScriptDefinitionsManager
}
}
private val LOG = Logger.getInstance("ScriptTemplatesProviders")
fun loadDefinitionsFromTemplates(
templateClassNames: List<String>,
templateClasspath: List<File>,
environment: Environment = emptyMap(),
// TODO: need to provide a way to specify this in compiler/repl .. etc
/*
* Allows to specify additional jars needed for DependenciesResolver (and not script template).
* Script template dependencies naturally become (part of) dependencies of the script which is not always desired for resolver dependencies.
* i.e. gradle resolver may depend on some jars that 'built.gradle.kts' files should not depend on.
*/
additionalResolverClasspath: List<File> = emptyList()
): List<KotlinScriptDefinitionFromAnnotatedTemplate> = try {
val classpath = templateClasspath + additionalResolverClasspath
LOG.info("[kts] loading script definitions $templateClassNames using cp: ${classpath.joinToString(File.pathSeparator)}")
val baseLoader = ScriptDefinitionContributor::class.java.classLoader
val loader = if (classpath.isEmpty()) baseLoader else URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), baseLoader)
templateClassNames.map {
KotlinScriptDefinitionFromAnnotatedTemplate(
loader.loadClass(it).kotlin,
environment,
templateClasspath
)
}
}
catch (ex: Throwable) {
// TODO: review exception handling
emptyList()
}
interface ScriptDefinitionContributor {
val id: String
fun getDefinitions(): List<KotlinScriptDefinition>
companion object {
val EP_NAME: ExtensionPointName<ScriptDefinitionContributor> =
ExtensionPointName.create<ScriptDefinitionContributor>("org.jetbrains.kotlin.scriptDefinitionContributor")
inline fun <reified T> find(project: Project) =
Extensions.getArea(project).getExtensionPoint(ScriptDefinitionContributor.EP_NAME).extensions.filterIsInstance<T>().firstOrNull()
}
}
class StandardScriptDefinitionContributor(private val project: Project) : ScriptDefinitionContributor {
override fun getDefinitions() = listOf(StandardIdeScriptDefinition(project))
override val id: String = "StandardKotlinScript"
}
class StandardIdeScriptDefinition(project: Project) : KotlinScriptDefinition(ScriptTemplateWithArgs::class) {
override val dependencyResolver = BundledKotlinScriptDependenciesResolver(project)
}
class BundledKotlinScriptDependenciesResolver(private val project: Project) : DependenciesResolver {
override fun resolve(
scriptContents: ScriptContents,
environment: Environment
): DependenciesResolver.ResolveResult {
val javaHome = getScriptSDK(project)
val dependencies = ScriptDependencies(
javaHome = javaHome?.let(::File),
classpath = with(PathUtil.kotlinPathsForIdeaPlugin) {
listOf(
reflectPath,
stdlibPath,
scriptRuntimePath
)
}
)
return dependencies.asSuccess()
}
private fun getScriptSDK(project: Project): String? {
val jdk = ProjectJdkTable.getInstance().allJdks.firstOrNull { sdk -> sdk.sdkType is JavaSdk } ?: PathUtilEx.getAnyJdk(project)
return jdk?.homePath
}
}
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.idea.core.script
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
@@ -27,6 +26,7 @@ import com.intellij.psi.search.NonClasspathDirectoriesScope
import com.intellij.util.containers.SLRUMap
import kotlinx.coroutines.experimental.launch
import org.jetbrains.kotlin.idea.core.util.EDT
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
@@ -37,11 +37,11 @@ import kotlin.reflect.KProperty0
import kotlin.reflect.jvm.isAccessible
import kotlin.script.experimental.dependencies.ScriptDependencies
private const val MAX_SCRIPTS_CACHED = 15
private const val MAX_SCRIPTS_CACHED = 10
class ScriptDependenciesCache(private val project: Project) {
private val cacheLock = ReentrantReadWriteLock()
private val cache = SLRUMap<VirtualFile, ScriptDependencies>(0, MAX_SCRIPTS_CACHED)
private val cache = SLRUMap<VirtualFile, ScriptDependencies>(MAX_SCRIPTS_CACHED, MAX_SCRIPTS_CACHED)
operator fun get(virtualFile: VirtualFile): ScriptDependencies? = cacheLock.read { cache[virtualFile] }
@@ -62,7 +62,7 @@ class ScriptDependenciesCache(private val project: Project) {
NonClasspathDirectoriesScope(allLibrarySources)
}
private fun onChange(file: VirtualFile?) {
private fun onChange(files: List<VirtualFile>) {
this::allScriptsClasspath.clearValue()
this::allScriptsClasspathScope.clearValue()
this::allLibrarySources.clearValue()
@@ -74,22 +74,18 @@ class ScriptDependenciesCache(private val project: Project) {
.single()
kotlinScriptDependenciesClassFinder.clearCache()
updateHighlighting(file)
updateHighlighting(files)
}
private fun updateHighlighting(file: VirtualFile?) {
private fun updateHighlighting(files: List<VirtualFile>) {
ScriptDependenciesModificationTracker.getInstance(project).incModificationCount()
launch(EDT(project)) {
if (file != null) {
file.let { PsiManager.getInstance(project).findFile(it) }?.let { psiFile ->
files.forEach {
PsiManager.getInstance(project).findFile(it)?.let { psiFile ->
DaemonCodeAnalyzer.getInstance(project).restart(psiFile)
}
}
else {
assert(ApplicationManager.getApplication().isUnitTestMode)
DaemonCodeAnalyzer.getInstance(project).restart()
}
}
}
@@ -99,8 +95,13 @@ class ScriptDependenciesCache(private val project: Project) {
}
fun clear() {
cacheLock.write(cache::clear)
onChange(null)
val keys = cacheLock.read {
val keys = mutableListOf<VirtualFile>()
cache.iterateKeys { keys.addIfNotNull(it) }
cacheLock.write(cache::clear)
keys
}
onChange(keys)
}
fun save(virtualFile: VirtualFile, new: ScriptDependencies): Boolean {
@@ -111,7 +112,7 @@ class ScriptDependenciesCache(private val project: Project) {
}
val changed = new != old
if (changed) {
onChange(virtualFile)
onChange(listOf(virtualFile))
}
return changed
@@ -122,7 +123,7 @@ class ScriptDependenciesCache(private val project: Project) {
cache.remove(virtualFile) != null
}
if (changed) {
onChange(virtualFile)
onChange(listOf(virtualFile))
}
return changed
}
@@ -23,9 +23,8 @@ import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.io.URLUtil
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import org.jetbrains.kotlin.script.makeScriptDefsFromTemplatesProviderExtensions
import java.io.File
import kotlin.script.experimental.dependencies.ScriptDependencies
@@ -41,25 +40,12 @@ class IdeScriptDependenciesProvider(
}
class ScriptDependenciesManager internal constructor(
private val project: Project,
private val scriptDefinitionProvider: KotlinScriptDefinitionProvider,
private val cacheUpdater: ScriptDependenciesUpdater,
private val cache: ScriptDependenciesCache
) {
init {
reloadScriptDefinitions()
}
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> = toVfsRoots(cacheUpdater.getCurrentDependencies(file).classpath)
fun getScriptDependencies(file: VirtualFile): ScriptDependencies = cacheUpdater.getCurrentDependencies(file)
private fun reloadScriptDefinitions() {
val def = makeScriptDefsFromTemplatesProviderExtensions(
project, { ep, ex -> /* do nothing, logging these exception creates too much noise*/}
)
scriptDefinitionProvider.setScriptDefinitions(def)
}
fun getAllScriptsClasspathScope() = cache.allScriptsClasspathScope
fun getAllLibrarySourcesScope() = cache.allLibrarySourcesScope
fun getAllLibrarySources() = cache.allLibrarySources
@@ -90,18 +76,10 @@ class ScriptDependenciesManager internal constructor(
@TestOnly
fun updateScriptDependenciesSynchronously(virtualFile: VirtualFile, project: Project) {
with(getInstance(project)) {
val scriptDefinition = KotlinScriptDefinitionProvider.getInstance(project)!!.findScriptDefinition(virtualFile)!!
val scriptDefinition = ScriptDefinitionProvider.getInstance(project).findScriptDefinition(virtualFile)!!
cacheUpdater.updateSync(virtualFile, scriptDefinition)
cacheUpdater.notifyRootsChanged()
}
}
@TestOnly
fun reloadScriptDefinitions(project: Project) {
with(getInstance(project)) {
reloadScriptDefinitions()
cacheUpdater.clear()
}
}
}
}
@@ -47,10 +47,10 @@ import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.dependencies.ScriptDependencies
internal class ScriptDependenciesUpdater(
class ScriptDependenciesUpdater(
private val project: Project,
private val cache: ScriptDependenciesCache,
private val scriptDefinitionProvider: KotlinScriptDefinitionProvider
private val scriptDefinitionProvider: ScriptDefinitionProvider
) {
private val requests = ConcurrentHashMap<String, ModStampedRequest>()
private val contentLoader = ScriptContentLoader(project)
@@ -21,28 +21,22 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.ResolveScopeProvider
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager
import org.jetbrains.kotlin.idea.core.script.StandardIdeScriptDefinition
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.script.getScriptDefinition
class KotlinScriptResolveScopeProvider : ResolveScopeProvider() {
companion object {
val USE_NULL_RESOLVE_SCOPE = "USE_NULL_RESOLVE_SCOPE"
}
override fun getResolveScope(file: VirtualFile, project: Project): GlobalSearchScope? {
val scriptDefinition = getScriptDefinition(file, project)
// TODO: this should get this particular scripts dependencies
return when {
scriptDefinition == null -> null
// This is a workaround for completion in scripts and REPL to provide module dependencies
// This is a workaround for completion in scripts and REPL to provide module dependencies
scriptDefinition.template == Any::class -> null
scriptDefinition is StandardIdeScriptDefinition -> null
scriptDefinition is KotlinScriptDefinitionFromAnnotatedTemplate -> // TODO: should include the file itself
if (scriptDefinition.environment?.containsKey(USE_NULL_RESOLVE_SCOPE) ?: false) {
null
}
else {
ScriptDependenciesManager.getInstance(project).getAllScriptsClasspathScope()
}
ScriptDependenciesManager.getInstance(project).getAllScriptsClasspathScope()
else -> null
}
}
@@ -24,23 +24,51 @@ import com.intellij.openapi.vfs.VirtualFile
import org.gradle.tooling.ProjectConnection
import org.jetbrains.kotlin.lexer.KotlinLexer
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.script.ScriptTemplatesProvider
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.plugins.gradle.service.execution.GradleExecutionHelper
import org.jetbrains.plugins.gradle.settings.GradleExecutionSettings
import org.jetbrains.plugins.gradle.util.GradleConstants
import java.io.File
import java.util.*
import kotlin.script.dependencies.Environment
import kotlin.script.experimental.dependencies.ScriptDependencies
abstract class AbstractGradleScriptTemplatesProvider(
project: Project, override val id: String, private val templateClass: String, private val dependencySelector: Regex
): ScriptTemplatesProvider {
private val project: Project,
override val id: String,
private val templateClass: String,
private val dependencySelector: Regex
): ScriptDefinitionContributor {
private val gradleExeSettings: GradleExecutionSettings? by lazy {
try {
ExternalSystemApiUtil.getExecutionSettings<GradleExecutionSettings>(
override fun getDefinitions(): List<KotlinScriptDefinition> {
val gradleExeSettings = getExeSettings() ?: return emptyList()
val gradleHome = gradleExeSettings.gradleHome ?: error("Unable to get Gradle home directory")
val gradleLibDir = File(gradleHome, "lib").let {
it.takeIf { it.exists() && it.isDirectory } ?: error("Invalid Gradle libraries directory $it")
}
val templateClasspath = gradleLibDir.listFiles { it ->
/* an inference problem without explicit 'it', TODO: remove when fixed */
dependencySelector.matches(it.name)
}.takeIf { it.isNotEmpty() }?.asList() ?: error("Missing jars in gradle directory")
return loadDefinitionsFromTemplates(
listOf(templateClass),
templateClasspath,
createEnvironment(gradleExeSettings),
additionalResolverClasspath(gradleLibDir)
)
}
open fun additionalResolverClasspath(gradleLibDir: File): List<File> = emptyList()
private fun getExeSettings(): GradleExecutionSettings? {
return try {
ExternalSystemApiUtil.getExecutionSettings(
project,
com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.toCanonicalPath(project.basePath!!),
org.jetbrains.plugins.gradle.util.GradleConstants.SYSTEM_ID)
ExternalSystemApiUtil.toCanonicalPath(project.basePath!!),
GradleConstants.SYSTEM_ID)
}
catch (e: Throwable) {
// TODO: consider displaying the warning to the user
@@ -49,44 +77,26 @@ abstract class AbstractGradleScriptTemplatesProvider(
}
}
private val gradleJvmOptions: List<String> by lazy {
gradleExeSettings?.daemonVmOptions?.let { vmOptions ->
private fun createEnvironment(gradleExeSettings: GradleExecutionSettings): Environment {
val gradleJvmOptions = gradleExeSettings.daemonVmOptions?.let { vmOptions ->
CommandLineTokenizer(vmOptions).toList()
.mapNotNull { it?.let { it as? String } }
.filterNot(String::isBlank)
.distinct()
} ?: emptyList()
}
internal val gradleLibDir: File by lazy {
val gradleHome = gradleExeSettings?.gradleHome ?: error("Unable to get Gradle home directory")
File(gradleHome, "lib").let {
it.takeIf { it.exists() && it.isDirectory } ?: error("Invalid Gradle libraries directory $it")
}
}
override val isValid: Boolean get() = true
override val templateClassNames get() = listOf(templateClass)
override val templateClasspath: List<File> get() {
return gradleLibDir.listFiles { it ->
/* an inference problem without explicit 'it', TODO: remove when fixed */
dependencySelector.matches(it.name)
}.takeIf { it.isNotEmpty() }?.asList() ?: error("Missing jars in gradle directory")
}
override val environment: Map<String, Any?>? by lazy {
mapOf(
"gradleHome" to gradleExeSettings?.gradleHome?.let(::File),
return mapOf(
"gradleHome" to gradleExeSettings.gradleHome?.let(::File),
"projectRoot" to (project.basePath ?: project.baseDir.canonicalPath)?.let(::File),
"gradleWithConnection" to { action: (ProjectConnection) -> Unit ->
GradleExecutionHelper().execute(project.basePath!!, null) { action(it) } },
"gradleJavaHome" to gradleExeSettings?.javaHome,
GradleExecutionHelper().execute(project.basePath!!, null) { action(it) }
},
"gradleJavaHome" to gradleExeSettings.javaHome,
"gradleJvmOptions" to gradleJvmOptions,
"getScriptSectionTokens" to ::topLevelSectionCodeTextTokens
)
}
}
@@ -97,11 +107,14 @@ abstract class AbstractGradleKotlinDSLTemplateProvider(project: Project, private
templateClass,
Regex("^gradle-(?:kotlin-dsl|core).*\\.jar\$")
) {
// TODO_R: check this against kotlin-dsl branch that uses daemon
override val additionalResolverClasspath: List<File> get() =
// additionally need compiler jar to load gradle resolver
gradleLibDir.listFiles { file -> file.name.startsWith("kotlin-compiler-embeddable") || file.name.startsWith("kotlin-stdlib") }
override fun additionalResolverClasspath(gradleLibDir: File): List<File> {
// additionally need compiler jar to load gradle resolver
return gradleLibDir.listFiles { file -> file.name.startsWith("kotlin-compiler-embeddable") || file.name.startsWith("kotlin-stdlib") }
.firstOrNull()?.let(::listOf).orEmpty()
}
}
class GradleSettingsKotlinDSLTemplateProvider(project: Project)
@@ -46,6 +46,7 @@ import com.intellij.psi.PsiFileFactory
import com.intellij.psi.PsiManager
import com.intellij.psi.impl.PsiFileFactoryImpl
import com.intellij.testFramework.LightVirtualFile
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.console.actions.BuildAndRestartConsoleAction
import org.jetbrains.kotlin.console.actions.KtExecuteCommandAction
@@ -54,7 +55,12 @@ import org.jetbrains.kotlin.console.gutter.ConsoleIndicatorRenderer
import org.jetbrains.kotlin.console.gutter.IconWithTooltip
import org.jetbrains.kotlin.console.gutter.ReplIcons
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.caches.resolve.*
import org.jetbrains.kotlin.idea.caches.resolve.NotUnderContentRootModuleInfo
import org.jetbrains.kotlin.idea.caches.resolve.productionSourceInfo
import org.jetbrains.kotlin.idea.caches.resolve.testSourceInfo
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.project.KOTLIN_CONSOLE_KEY
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.name.Name
@@ -66,7 +72,6 @@ import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor
import org.jetbrains.kotlin.resolve.repl.ReplState
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider
import java.awt.Color
import java.awt.Font
import java.util.concurrent.CountDownLatch
@@ -89,7 +94,9 @@ class KotlinConsoleRunner(
override fun finishConsole() {
KotlinConsoleKeeper.getInstance(project).removeConsole(consoleView.virtualFile)
KotlinScriptDefinitionProvider.getInstance(project)!!.removeScriptDefinition(consoleScriptDefinition)
val consoleContributor = ScriptDefinitionContributor.find<ConsoleScriptDefinitionContributor>(project)!!
consoleContributor.unregisterDefinition(consoleScriptDefinition)
ScriptDefinitionsManager.getInstance(project).reloadDefinitionsBy(consoleContributor)
if (ApplicationManager.getApplication().isUnitTestMode) {
consoleTerminated.countDown()
@@ -151,7 +158,10 @@ class KotlinConsoleRunner(
val executeAction = KtExecuteCommandAction(consoleView.virtualFile)
executeAction.registerCustomShortcutSet(CommonShortcuts.CTRL_ENTER, consoleView.consoleEditor.component)
KotlinScriptDefinitionProvider.getInstance(project)!!.addScriptDefinition(consoleScriptDefinition)
val consoleContributor = ScriptDefinitionContributor.find<ConsoleScriptDefinitionContributor>(project)!!
consoleContributor.registerDefinition(consoleScriptDefinition)
ScriptDefinitionsManager.getInstance(project).reloadDefinitionsBy(consoleContributor)
enableCompletion(consoleView)
return consoleView
@@ -285,4 +295,22 @@ class KotlinConsoleRunner(
private fun configureFileDependencies(psiFile: KtFile) {
psiFile.moduleInfo = module.testSourceInfo() ?: module.productionSourceInfo() ?: NotUnderContentRootModuleInfo
}
}
class ConsoleScriptDefinitionContributor: ScriptDefinitionContributor {
private val definitions = ContainerUtil.newConcurrentSet<KotlinScriptDefinition>()
override val id: String = "IDEA Console"
override fun getDefinitions(): List<KotlinScriptDefinition> {
return definitions.toList()
}
fun registerDefinition(definition: KotlinScriptDefinition) {
definitions.add(definition)
}
fun unregisterDefinition(definition: KotlinScriptDefinition) {
definitions.remove(definition)
}
}
+2 -2
View File
@@ -30,8 +30,8 @@
<extensionPoint name="storageComponentContainerContributor"
interface="org.jetbrains.kotlin.extensions.StorageComponentContainerContributor"
area="IDEA_PROJECT"/>
<extensionPoint name="scriptTemplatesProvider"
interface="org.jetbrains.kotlin.script.ScriptTemplatesProvider"
<extensionPoint name="scriptDefinitionContributor"
interface="org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor"
area="IDEA_PROJECT"/>
<extensionPoint name="facetConfigurationExtension"
interface="org.jetbrains.kotlin.idea.facet.KotlinFacetConfigurationExtension"/>
+6 -3
View File
@@ -25,13 +25,16 @@
<!-- diagnosticSuppressor decelerated in common.xml -->
<diagnosticSuppressor implementation="org.jetbrains.kotlin.idea.debugger.DiagnosticSuppressorForDebugger"/>
<scriptTemplatesProvider
<scriptDefinitionContributor
id="ScriptTemplatesFromCompilerSettingsProvider"
implementation="org.jetbrains.kotlin.idea.script.ScriptTemplatesFromCompilerSettingsProvider"/>
<scriptTemplatesProvider id="StandardKotlinScriptTemplateProvider"
<scriptDefinitionContributor id="StandardScriptDefinitionContributor"
order="last"
implementation="org.jetbrains.kotlin.idea.script.StandardKotlinScriptTemplateProvider"/>
implementation="org.jetbrains.kotlin.idea.core.script.StandardScriptDefinitionContributor"/>
<scriptDefinitionContributor id="ConsoleScriptDefinitionContributor"
implementation="org.jetbrains.kotlin.console.ConsoleScriptDefinitionContributor"/>
<idePlatformSupport implementation="org.jetbrains.kotlin.caches.resolve.JvmPlatformSupport"/>
<idePlatformSupport implementation="org.jetbrains.kotlin.caches.resolve.JsPlatformSupport"/>
+2 -2
View File
@@ -280,8 +280,8 @@
<projectService serviceInterface="org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer"
serviceImplementation="org.jetbrains.kotlin.resolve.DummyCodeAnalyzerInitializer"/>
<projectService serviceInterface="org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider"
serviceImplementation="org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider"/>
<projectService serviceInterface="org.jetbrains.kotlin.script.ScriptDefinitionProvider"
serviceImplementation="org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager"/>
<projectService serviceInterface="org.jetbrains.kotlin.script.ScriptDependenciesProvider"
serviceImplementation="org.jetbrains.kotlin.idea.core.script.IdeScriptDependenciesProvider"/>
@@ -1,74 +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.project.Project
import com.intellij.openapi.projectRoots.JavaSdk
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.projectRoots.ex.PathUtilEx
import org.jetbrains.kotlin.idea.core.script.dependencies.KotlinScriptResolveScopeProvider
import org.jetbrains.kotlin.script.ScriptTemplatesProvider
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import kotlin.script.dependencies.*
import kotlin.script.experimental.dependencies.*
import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult
import kotlin.script.templates.standard.ScriptTemplateWithArgs
class StandardKotlinScriptTemplateProvider(val project: Project) : ScriptTemplatesProvider {
override val id: String = "StandardKotlinScriptTemplateProvider"
override val isValid: Boolean = true
override val templateClassNames: Iterable<String> get() = listOf(ScriptTemplateWithArgs::class.qualifiedName!!)
override val templateClasspath get() = emptyList<File>()
override val environment: Map<String, Any?>? get() {
return mapOf(
KotlinScriptResolveScopeProvider.USE_NULL_RESOLVE_SCOPE to true,
"sdk" to getScriptSDK(project)
)
}
override val resolver: DependenciesResolver = BundledKotlinScriptDependenciesResolver()
private fun getScriptSDK(project: Project): String? {
val jdk = PathUtilEx.getAnyJdk(project) ?:
ProjectJdkTable.getInstance().allJdks.firstOrNull { sdk -> sdk.sdkType is JavaSdk }
return jdk?.homePath
}
}
class BundledKotlinScriptDependenciesResolver : DependenciesResolver {
override fun resolve(
scriptContents: ScriptContents,
environment: Environment
): ResolveResult {
val javaHome = environment.get("sdk") as String?
val dependencies = ScriptDependencies(
javaHome = javaHome?.let(::File),
classpath = with(PathUtil.kotlinPathsForIdeaPlugin) {
listOf(
reflectPath,
stdlibPath,
scriptRuntimePath
)
}
)
return dependencies.asSuccess()
}
}
@@ -18,20 +18,23 @@ package org.jetbrains.kotlin.idea.script
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings
import org.jetbrains.kotlin.script.ScriptTemplatesProvider
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor
import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplates
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import java.io.File
class ScriptTemplatesFromCompilerSettingsProvider(project: Project): ScriptTemplatesProvider
{
private val kotlinSettings = KotlinCompilerSettings.getInstance(project).settings
class ScriptTemplatesFromCompilerSettingsProvider(private val project: Project) : ScriptDefinitionContributor {
override fun getDefinitions(): List<KotlinScriptDefinition> {
val kotlinSettings = KotlinCompilerSettings.getInstance(project).settings
return loadDefinitionsFromTemplates(
templateClassNames = kotlinSettings.scriptTemplates.split(',', ' '),
templateClasspath = kotlinSettings.scriptTemplatesClasspath.split(File.pathSeparator).map(::File),
environment = mapOf(
"projectRoot" to (project.basePath ?: project.baseDir.canonicalPath)?.let(::File))
)
}
override val id: String = "KotlinCompilerScriptTemplatesSettings"
override val isValid: Boolean = kotlinSettings.scriptTemplates.isNotBlank()
override val templateClassNames: Iterable<String> get() = kotlinSettings.scriptTemplates.split(',', ' ')
override val templateClasspath get() = kotlinSettings.scriptTemplatesClasspath.split(File.pathSeparator).map(::File)
override val environment: Map<String, Any?>? by lazy { mapOf(
"projectRoot" to (project.basePath ?: project.baseDir.canonicalPath)?.let(::File))
}
}
@@ -24,10 +24,10 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.testFramework.PlatformTestUtil
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.idea.KotlinDaemonAnalyzerTestCase
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager.Companion.updateScriptDependenciesSynchronously
import org.jetbrains.kotlin.idea.navigation.GotoCheck
import org.jetbrains.kotlin.script.ScriptTemplatesProvider
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.MockLibraryUtil
@@ -39,12 +39,6 @@ import org.junit.Assert
import java.io.File
import java.util.regex.Pattern
import kotlin.script.dependencies.Environment
import kotlin.script.dependencies.ScriptContents
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.dependencies.asSuccess
import kotlin.script.templates.ScriptTemplateDefinition
abstract class AbstractScriptConfigurationHighlightingTest : AbstractScriptConfigurationTest() {
@@ -182,44 +176,10 @@ abstract class AbstractScriptConfigurationTest : KotlinDaemonAnalyzerTestCase()
PlatformTestUtil.registerExtension(
Extensions.getArea(project),
ScriptTemplatesProvider.EP_NAME,
ScriptDefinitionContributor.EP_NAME,
provider,
testRootDisposable
)
ScriptDependenciesManager.reloadScriptDefinitions(project)
ScriptDefinitionsManager.getInstance(project).reloadScriptDefinitions()
}
}
class CustomScriptTemplateProvider(
override val environment: Map<String, Any?>
) : ScriptTemplatesProvider {
override val id = "Test"
override val isValid = true
override val templateClassNames = listOf("custom.scriptDefinition.Template")
override val templateClasspath = listOfNotNull(environment["template-classes"] as? File)
}
class FromTextTemplateProvider(
override val environment: Map<String, Any?>
) : ScriptTemplatesProvider {
override val id = "Test"
override val isValid = true
override val templateClassNames = listOf("org.jetbrains.kotlin.idea.script.Template")
override val templateClasspath get() = emptyList<File>()
}
class FromTextDependenciesResolver : AsyncDependenciesResolver {
@Suppress("UNCHECKED_CAST")
suspend override fun resolveAsync(scriptContents: ScriptContents, environment: Environment): DependenciesResolver.ResolveResult {
return ScriptDependencies(
classpath = (environment["classpath"] as? List<File>).orEmpty(),
imports = (environment["imports"] as? List<String>).orEmpty(),
sources = (environment["sources"] as? List<File>).orEmpty()
).asSuccess()
}
}
@Suppress("unused")
@ScriptTemplateDefinition(FromTextDependenciesResolver::class, scriptFilePattern = "script.kts")
class Template
@@ -0,0 +1,69 @@
/*
* 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.idea.script
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor
import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplates
import java.io.File
import kotlin.script.dependencies.Environment
import kotlin.script.dependencies.ScriptContents
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
import kotlin.script.experimental.dependencies.DependenciesResolver
import kotlin.script.experimental.dependencies.ScriptDependencies
import kotlin.script.experimental.dependencies.asSuccess
import kotlin.script.templates.ScriptTemplateDefinition
class CustomScriptTemplateProvider(
val environment: Environment
) : ScriptDefinitionContributor {
override val id = "Test"
override fun getDefinitions() = loadDefinitionsFromTemplates(
templateClassNames = listOf("custom.scriptDefinition.Template"),
templateClasspath = listOfNotNull(environment["template-classes"] as? File),
environment = environment
)
}
class FromTextTemplateProvider(
val environment: Map<String, Any?>
) : ScriptDefinitionContributor {
override val id = "Test"
override fun getDefinitions() = loadDefinitionsFromTemplates(
templateClassNames = listOf("org.jetbrains.kotlin.idea.script.Template"),
templateClasspath = emptyList(),
environment = environment
)
}
class FromTextDependenciesResolver : AsyncDependenciesResolver {
@Suppress("UNCHECKED_CAST")
suspend override fun resolveAsync(scriptContents: ScriptContents, environment: Environment): DependenciesResolver.ResolveResult {
return ScriptDependencies(
classpath = (environment["classpath"] as? List<File>).orEmpty(),
imports = (environment["imports"] as? List<String>).orEmpty(),
sources = (environment["sources"] as? List<File>).orEmpty()
).asSuccess()
}
}
@Suppress("unused")
@ScriptTemplateDefinition(FromTextDependenciesResolver::class, scriptFilePattern = "script.kts")
class Template
@@ -23,7 +23,6 @@ import org.jetbrains.kotlin.cli.common.repl.*
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.repl.GenericReplCompiler
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.load.kotlin.JvmMetadataVersion
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.utils.PathUtil
@@ -64,7 +63,7 @@ class KotlinJsr223JvmLocalScriptEngine(
private fun makeScriptDefinition(templateClasspath: List<File>, templateClassName: String): KotlinScriptDefinition {
val classloader = URLClassLoader(templateClasspath.map { it.toURI().toURL() }.toTypedArray(), this.javaClass.classLoader)
val cls = classloader.loadClass(templateClassName)
return KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, null, null, emptyMap())
return KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, emptyMap())
}
private fun makeCompilerConfiguration() = CompilerConfiguration().apply {
@@ -115,7 +115,7 @@ done
suppressOutput: Boolean = false
): Class<*>? =
compileScriptImpl("src/test/resources/scripts/" + scriptFileName,
KotlinScriptDefinitionFromAnnotatedTemplate(scriptTemplate, null, null, environment), suppressOutput)
KotlinScriptDefinitionFromAnnotatedTemplate(scriptTemplate, environment), suppressOutput)
private fun compileScriptImpl(
scriptPath: String,
@@ -31,7 +31,7 @@ abstract class AbstractSamWithReceiverScriptTest : AbstractDiagnosticsTest() {
override fun createEnvironment(file: File) = super.createEnvironment(file).apply {
StorageComponentContainerContributor.registerExtension(project, CliSamWithReceiverComponentContributor(TEST_ANNOTATIONS))
val def = KotlinScriptDefinitionFromAnnotatedTemplate(ScriptForSamWithReceivers::class, null, null, emptyMap())
val def = KotlinScriptDefinitionFromAnnotatedTemplate(ScriptForSamWithReceivers::class, emptyMap())
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, def)
}
}
@@ -19,8 +19,6 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.addKotlinSourceRoot
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider
import org.jetbrains.kotlin.script.StandardScriptDefinition
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestJdkKind
@@ -51,8 +49,6 @@ abstract class AbstractKotlinUastTest : AbstractUastTest() {
initializeKotlinEnvironment()
KotlinScriptDefinitionProvider.getInstance(project)?.addScriptDefinition(StandardScriptDefinition)
val trace = CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace()
val environment = kotlinCoreEnvironment!!