Refactor getting dependencies for script files API
Deprecate KotlinScriptDefinition#getDependenciesFor Expose dependencyResolver as a property KotlinScriptExternalImportsProvider is the component clients should be asking for dependencies
This commit is contained in:
+6
-68
@@ -18,17 +18,10 @@ package org.jetbrains.kotlin.script
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
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.name.Name
|
||||
import org.jetbrains.kotlin.name.NameUtils
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
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
|
||||
@@ -54,7 +47,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
|
||||
?: DEFAULT_SCRIPT_FILE_PATTERN
|
||||
}
|
||||
|
||||
val resolver: ScriptDependenciesResolver? by lazy {
|
||||
override val dependencyResolver: ScriptDependenciesResolver by lazy {
|
||||
val defAnn by lazy { takeUnlessError { template.annotations.firstIsInstanceOrNull<kotlin.script.templates.ScriptTemplateDefinition>() } }
|
||||
val legacyDefAnn by lazy { takeUnlessError { template.annotations.firstIsInstanceOrNull<org.jetbrains.kotlin.script.ScriptTemplateDefinition>() } }
|
||||
when {
|
||||
@@ -84,8 +77,8 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
|
||||
log.warn("[kts] Script def error ${ex.message}")
|
||||
null
|
||||
}
|
||||
else -> BasicScriptDependenciesResolver()
|
||||
}
|
||||
else -> null
|
||||
} ?: BasicScriptDependenciesResolver()
|
||||
}
|
||||
|
||||
val samWithReceiverAnnotations: List<String>? by lazy {
|
||||
@@ -93,7 +86,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
|
||||
?: takeUnlessError { template.annotations.firstIsInstanceOrNull<org.jetbrains.kotlin.script.SamWithReceiverAnnotations>()?.annotations?.toList() }
|
||||
}
|
||||
|
||||
private val acceptedAnnotations: List<KClass<out Annotation>> by lazy {
|
||||
override val acceptedAnnotations: List<KClass<out Annotation>> by lazy {
|
||||
|
||||
fun sameSignature(left: KFunction<*>, right: KFunction<*>): Boolean =
|
||||
left.parameters.size == right.parameters.size &&
|
||||
@@ -104,7 +97,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
|
||||
|
||||
val resolveMethod = ScriptDependenciesResolver::resolve
|
||||
val resolverMethodAnnotations =
|
||||
resolver?.let { it::class }?.memberFunctions?.find { function ->
|
||||
dependencyResolver::class.memberFunctions.find { function ->
|
||||
function.name == resolveMethod.name &&
|
||||
sameSignature(function, resolveMethod)
|
||||
}?.annotations?.filterIsInstance<AcceptedAnnotations>()
|
||||
@@ -122,62 +115,7 @@ open class KotlinScriptDefinitionFromAnnotatedTemplate(
|
||||
// TODO: implement other strategy - e.g. try to extract something from match with ScriptFilePattern
|
||||
override fun getScriptName(script: KtScript): Name = NameUtils.getScriptNameForFile(script.containingKtFile.name)
|
||||
|
||||
override fun <TF: Any> getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?): KotlinScriptExternalDependencies? {
|
||||
return takeUnlessError(reportError = false) {
|
||||
val fileDeps = resolver?.resolve(makeScriptContents(file, project), environment, ::logScriptDefMessage, previousDependencies)
|
||||
// TODO: use it as a Future
|
||||
fileDeps?.get()
|
||||
}
|
||||
}
|
||||
|
||||
fun <TF: Any> makeScriptContents(file: TF, project: Project) = BasicScriptContents(file, getAnnotations = {
|
||||
val classLoader = template.java.classLoader
|
||||
takeUnlessError(reportError = false) {
|
||||
getAnnotationEntries(file, project)
|
||||
.mapNotNull { psiAnn ->
|
||||
// TODO: consider advanced matching using semantic similar to actual resolving
|
||||
acceptedAnnotations.find { ann ->
|
||||
psiAnn.typeName.let { it == ann.simpleName || it == ann.qualifiedName }
|
||||
}?.let { constructAnnotation(psiAnn, classLoader.loadClass(it.qualifiedName).kotlin as KClass<out Annotation>) }
|
||||
}
|
||||
}
|
||||
?: emptyList()
|
||||
})
|
||||
|
||||
|
||||
fun getDependenciesFor(previousDependencies: KotlinScriptExternalDependencies?, scriptContents: ScriptContents): KotlinScriptExternalDependencies? {
|
||||
return takeUnlessError(reportError = false) {
|
||||
// TODO: use it as a Future
|
||||
resolver?.resolve(scriptContents, environment, ::logScriptDefMessage, previousDependencies)?.get()
|
||||
}
|
||||
}
|
||||
|
||||
private fun <TF: Any> 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.canonicalPath)
|
||||
?: throw IllegalArgumentException("Unable to find file ${file.canonicalPath}"))
|
||||
getAnnotationEntriesFromVirtualFile(virtualFile, project)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unsupported file type $file")
|
||||
}
|
||||
|
||||
private fun getAnnotationEntriesFromPsiFile(file: PsiFile) =
|
||||
(file as? KtFile)?.annotationEntries
|
||||
?: 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 IllegalArgumentException("Unable to load PSI from ${file.canonicalPath}")
|
||||
return getAnnotationEntriesFromPsiFile(psiFile)
|
||||
}
|
||||
|
||||
class BasicScriptContents<out TF: Any>(myFile: TF, getAnnotations: () -> Iterable<Annotation>) : ScriptContents {
|
||||
override val file: File? by lazy { getFile(myFile) }
|
||||
override val annotations: Iterable<Annotation> by lazy { getAnnotations() }
|
||||
override val text: CharSequence? by lazy { getFileContents(myFile) }
|
||||
}
|
||||
override fun <TF: Any> getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?) = error("Should not be called")
|
||||
|
||||
override fun toString(): String = "KotlinScriptDefinitionFromAnnotatedTemplate - ${template.simpleName}"
|
||||
|
||||
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.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.psi.KtAnnotationEntry
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import java.io.File
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.script.dependencies.KotlinScriptExternalDependencies
|
||||
import kotlin.script.dependencies.ScriptContents
|
||||
|
||||
abstract class KotlinScriptExternalImportsProviderBase(private val project: Project): KotlinScriptExternalImportsProvider {
|
||||
fun <TF : Any> getScriptContents(scriptDefinition: KotlinScriptDefinition, file: TF)
|
||||
= BasicScriptContents(file, getAnnotations = { loadAnnotations(scriptDefinition, file) })
|
||||
|
||||
private fun <TF : Any> loadAnnotations(scriptDefinition: KotlinScriptDefinition, file: TF): List<Annotation> {
|
||||
val classLoader = scriptDefinition.template.java.classLoader
|
||||
// TODO_R: report error on failure to load annotation class
|
||||
return 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 <TF: Any> 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.canonicalPath)
|
||||
?: throw IllegalArgumentException("Unable to find file ${file.canonicalPath}"))
|
||||
getAnnotationEntriesFromVirtualFile(virtualFile, project)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unsupported file type $file")
|
||||
}
|
||||
|
||||
private fun getAnnotationEntriesFromPsiFile(file: PsiFile) =
|
||||
(file as? KtFile)?.annotationEntries
|
||||
?: 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 IllegalArgumentException("Unable to load PSI from ${file.canonicalPath}")
|
||||
return getAnnotationEntriesFromPsiFile(psiFile)
|
||||
}
|
||||
|
||||
class BasicScriptContents<out TF: Any>(myFile: TF, getAnnotations: () -> Iterable<Annotation>) : ScriptContents {
|
||||
override val file: File? by lazy { getFile(myFile) }
|
||||
override val annotations: Iterable<Annotation> by lazy { getAnnotations() }
|
||||
override val text: CharSequence? by lazy { getFileContents(myFile) }
|
||||
}
|
||||
|
||||
fun <TF: Any> resolveDependencies(
|
||||
scriptDef: KotlinScriptDefinition,
|
||||
file: TF,
|
||||
oldDependencies: KotlinScriptExternalDependencies? = null
|
||||
): KotlinScriptExternalDependencies? {
|
||||
val scriptContents = getScriptContents(scriptDef, file)
|
||||
return scriptDef.dependencyResolver.resolve(
|
||||
scriptContents,
|
||||
(scriptDef as? KotlinScriptDefinitionFromAnnotatedTemplate)?.environment,
|
||||
{ _, _, _ -> Unit }, // TODO_R:
|
||||
oldDependencies
|
||||
).get()
|
||||
}
|
||||
}
|
||||
|
||||
+9
-13
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.script
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import java.io.File
|
||||
@@ -28,22 +27,24 @@ import kotlin.script.dependencies.KotlinScriptExternalDependencies
|
||||
class KotlinScriptExternalImportsProviderImpl(
|
||||
val project: Project,
|
||||
private val scriptDefinitionProvider: KotlinScriptDefinitionProvider
|
||||
) : KotlinScriptExternalImportsProvider {
|
||||
) : KotlinScriptExternalImportsProviderBase(project) {
|
||||
|
||||
private val cacheLock = ReentrantReadWriteLock()
|
||||
private val cache = hashMapOf<String, KotlinScriptExternalDependencies?>()
|
||||
|
||||
override fun <TF: Any> getExternalImports(file: TF): KotlinScriptExternalDependencies? = cacheLock.read {
|
||||
override fun <TF : Any> getScriptDependencies(file: TF): KotlinScriptExternalDependencies? = cacheLock.read {
|
||||
calculateExternalDependencies(file)
|
||||
}
|
||||
|
||||
private fun <TF: Any> calculateExternalDependencies(file: TF): KotlinScriptExternalDependencies? {
|
||||
private fun <TF : Any> calculateExternalDependencies(file: TF): KotlinScriptExternalDependencies? {
|
||||
val path = getFilePath(file)
|
||||
val cached = cache[path]
|
||||
return if (cached != null) cached else {
|
||||
return if (cached != null) cached
|
||||
else {
|
||||
val scriptDef = scriptDefinitionProvider.findScriptDefinition(file)
|
||||
if (scriptDef != null) {
|
||||
val deps = scriptDef.getDependenciesFor(file, project, null)
|
||||
val deps = resolveDependencies(scriptDef, file)
|
||||
|
||||
if (deps != null) {
|
||||
log.info("[kts] new cached deps for $path: ${deps.classpath.joinToString(File.pathSeparator)}")
|
||||
}
|
||||
@@ -55,11 +56,6 @@ class KotlinScriptExternalImportsProviderImpl(
|
||||
else null
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project): KotlinScriptExternalImportsProvider? =
|
||||
ServiceManager.getService(project, KotlinScriptExternalImportsProvider::class.java)
|
||||
internal val log = Logger.getInstance(KotlinScriptExternalImportsProvider::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
private val log = Logger.getInstance(KotlinScriptExternalImportsProvider::class.java)
|
||||
Reference in New Issue
Block a user