Rewrite scripting related API to PsiFile instead of VirtualFile
There were several places where we converted virtualFile to PsiFile. This operation need a read access and may throw ProcessCanceledException, so we want to minimize its usages in IDE
This commit is contained in:
+1
-1
@@ -90,7 +90,7 @@ sealed class ScriptDependenciesInfo(val project: Project) : IdeaModuleInfo, Bina
|
||||
) : ScriptDependenciesInfo(project) {
|
||||
override val sdk: Sdk?
|
||||
get() {
|
||||
return ScriptDependenciesManager.getInstance(project).getScriptSdk(scriptFile, project)
|
||||
return ScriptDependenciesManager.getInstance(project).getScriptSdk(scriptFile)
|
||||
}
|
||||
|
||||
override fun contentScope(): GlobalSearchScope {
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ object KotlinHighlightingUtil {
|
||||
private fun shouldHighlightScript(ktFile: KtFile): Boolean {
|
||||
if (isRunningInCidrIde) return false // There is no Java support in CIDR. So do not highlight errors in KTS if running in CIDR.
|
||||
if (!ScriptsCompilationConfigurationUpdater.areDependenciesCached(ktFile)) return false
|
||||
if (IdeScriptReportSink.getReports(ktFile.virtualFile).any { it.severity == ScriptDiagnostic.Severity.FATAL }) {
|
||||
if (IdeScriptReportSink.getReports(ktFile).any { it.severity == ScriptDiagnostic.Severity.FATAL }) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ class ScriptExternalHighlightingPass(
|
||||
|
||||
if (!file.isScript()) return
|
||||
|
||||
val reports = IdeScriptReportSink.getReports(file.virtualFile)
|
||||
val reports = IdeScriptReportSink.getReports(file)
|
||||
|
||||
val annotations = reports.mapNotNull { scriptDiagnostic ->
|
||||
val (startOffset, endOffset) = scriptDiagnostic.location?.let { computeOffsets(document, it) } ?: 0 to 0
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.impl.cache.impl.id.IdIndex
|
||||
import com.intellij.psi.impl.cache.impl.id.IdIndexEntry
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
@@ -123,7 +124,7 @@ fun PsiSearchHelper.isCheapEnoughToSearchConsideringOperators(
|
||||
return isCheapEnoughToSearch(name, scope, fileToIgnoreOccurrencesIn, progress)
|
||||
}
|
||||
|
||||
fun findScriptsWithUsages(declaration: KtNamedDeclaration): List<VirtualFile> {
|
||||
fun findScriptsWithUsages(declaration: KtNamedDeclaration): List<KtFile> {
|
||||
val project = declaration.project
|
||||
val scope = PsiSearchHelper.getInstance(project).getUseScope(declaration) as? GlobalSearchScope
|
||||
?: return emptyList()
|
||||
@@ -138,5 +139,8 @@ fun findScriptsWithUsages(declaration: KtNamedDeclaration): List<VirtualFile> {
|
||||
scope
|
||||
)
|
||||
}
|
||||
return collector.results.filter { it.findScriptDefinition(project) != null }.toList()
|
||||
return collector.results
|
||||
.mapNotNull { PsiManager.getInstance(project).findFile(it) as? KtFile }
|
||||
.filter { it.findScriptDefinition() != null }
|
||||
.toList()
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.ui.EditorNotifications
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
|
||||
import kotlin.script.experimental.api.ScriptDiagnostic
|
||||
@@ -49,6 +50,10 @@ class IdeScriptReportSink(val project: Project) : ScriptReportSink {
|
||||
return file.scriptDiagnostics ?: emptyList()
|
||||
}
|
||||
|
||||
fun getReports(file: KtFile): List<ScriptDiagnostic> {
|
||||
return file.originalFile.virtualFile?.scriptDiagnostics ?: emptyList()
|
||||
}
|
||||
|
||||
private var VirtualFile.scriptDiagnostics: List<ScriptDiagnostic>? by UserDataProperty(Key.create("KOTLIN_SCRIPT_DIAGNOSTICS"))
|
||||
}
|
||||
}
|
||||
+14
@@ -18,10 +18,18 @@ package org.jetbrains.kotlin.idea.core.script
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.util.*
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.getScriptOriginalFile
|
||||
import java.io.*
|
||||
import kotlin.script.experimental.api.ScriptCompilationConfiguration
|
||||
import kotlin.script.experimental.dependencies.ScriptDependencies
|
||||
|
||||
var KtFile.scriptDependencies: ScriptDependencies?
|
||||
get() = this.getScriptOriginalFile()?.virtualFile?.scriptDependencies
|
||||
set(value) {
|
||||
this.getScriptOriginalFile()?.virtualFile?.scriptDependencies = value
|
||||
}
|
||||
|
||||
var VirtualFile.scriptDependencies: ScriptDependencies? by cachedFileAttribute(
|
||||
name = "kotlin-script-dependencies",
|
||||
version = 3,
|
||||
@@ -45,6 +53,12 @@ var VirtualFile.scriptDependencies: ScriptDependencies? by cachedFileAttribute(
|
||||
}
|
||||
)
|
||||
|
||||
var KtFile.scriptCompilationConfiguration: ScriptCompilationConfiguration?
|
||||
get() = this.getScriptOriginalFile()?.virtualFile?.scriptCompilationConfiguration
|
||||
set(value) {
|
||||
this.getScriptOriginalFile()?.virtualFile?.scriptCompilationConfiguration = value
|
||||
}
|
||||
|
||||
var VirtualFile.scriptCompilationConfiguration: ScriptCompilationConfiguration? by cachedFileAttribute(
|
||||
name = "kotlin-script-compilation-configuration",
|
||||
version = 1,
|
||||
|
||||
+28
-28
@@ -23,16 +23,17 @@ import com.intellij.openapi.projectRoots.JavaSdkType
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.ProjectRootManager
|
||||
import com.intellij.openapi.vfs.StandardFileSystems
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.util.io.URLUtil
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.idea.caches.project.getAllProjectSdks
|
||||
import org.jetbrains.kotlin.idea.core.script.dependencies.SyncScriptDependenciesLoader
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.api.valueOrNull
|
||||
|
||||
@@ -40,28 +41,35 @@ import kotlin.script.experimental.api.valueOrNull
|
||||
// NOTE: this service exists exclusively because ScriptDependencyManager
|
||||
// cannot be registered as implementing two services (state would be duplicated)
|
||||
class IdeScriptDependenciesProvider(
|
||||
private val scriptDependenciesManager: ScriptDependenciesManager
|
||||
) : ScriptDependenciesProvider {
|
||||
override fun getScriptConfigurationResult(file: VirtualFile): ScriptCompilationConfigurationResult? = scriptDependenciesManager.getRefinedCompilationConfiguration(file)
|
||||
private val scriptDependenciesManager: ScriptDependenciesManager,
|
||||
project: Project
|
||||
) : ScriptDependenciesProvider(project) {
|
||||
override fun getScriptConfigurationResult(file: KtFile): ScriptCompilationConfigurationResult? {
|
||||
return scriptDependenciesManager.getRefinedCompilationConfiguration(file)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: rename and provide alias for compatibility - this is not only about dependencies anymore
|
||||
class ScriptDependenciesManager internal constructor(
|
||||
private val cacheUpdater: ScriptsCompilationConfigurationUpdater,
|
||||
private val cache: ScriptsCompilationConfigurationCache
|
||||
private val cache: ScriptsCompilationConfigurationCache,
|
||||
private val project: Project
|
||||
) {
|
||||
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> =
|
||||
toVfsRoots(cacheUpdater.getCurrentCompilationConfiguration(file)?.valueOrNull()?.dependenciesClassPath.orEmpty())
|
||||
|
||||
fun getRefinedCompilationConfiguration(file: VirtualFile): ScriptCompilationConfigurationResult? =
|
||||
cacheUpdater.getCurrentCompilationConfiguration(file)
|
||||
|
||||
fun getScriptSdk(file: VirtualFile, project: Project): Sdk? {
|
||||
return getScriptSdk(getRefinedCompilationConfiguration(file)?.valueOrNull())
|
||||
?: getScriptDefaultSdk(project)
|
||||
@Deprecated("Use getScriptClasspath(KtFile) instead")
|
||||
fun getScriptClasspath(file: VirtualFile): List<VirtualFile> {
|
||||
val ktFile = PsiManager.getInstance(project).findFile(file) as? KtFile ?: return emptyList()
|
||||
return getScriptClasspath(ktFile)
|
||||
}
|
||||
|
||||
fun getScriptClasspath(file: KtFile): List<VirtualFile> =
|
||||
toVfsRoots(cacheUpdater.getCurrentCompilationConfiguration(file)?.valueOrNull()?.dependenciesClassPath.orEmpty())
|
||||
|
||||
fun getRefinedCompilationConfiguration(file: KtFile): ScriptCompilationConfigurationResult? =
|
||||
cacheUpdater.getCurrentCompilationConfiguration(file)
|
||||
|
||||
fun getScriptDependenciesClassFilesScope(file: VirtualFile) = cache.scriptDependenciesClassFilesScope(file)
|
||||
fun getScriptSdk(file: VirtualFile) = cache.getScriptSdk(file)
|
||||
|
||||
fun getAllScriptsSdks() = cache.allSdks
|
||||
|
||||
@@ -76,17 +84,6 @@ class ScriptDependenciesManager internal constructor(
|
||||
fun getInstance(project: Project): ScriptDependenciesManager =
|
||||
ServiceManager.getService(project, ScriptDependenciesManager::class.java)
|
||||
|
||||
fun getScriptSdk(compilationConfiguration: ScriptCompilationConfigurationWrapper?): Sdk? {
|
||||
// workaround for mismatched gradle wrapper and plugin version
|
||||
val javaHome = try {
|
||||
compilationConfiguration?.javaHome?.let { VfsUtil.findFileByIoFile(it, true) }
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
} ?: return null
|
||||
|
||||
return getAllProjectSdks().find { it.homeDirectory == javaHome }
|
||||
}
|
||||
|
||||
fun getScriptDefaultSdk(project: Project): Sdk? {
|
||||
val projectSdk = ProjectRootManager.getInstance(project).projectSdk?.takeIf { it.canBeUsedForScript() }
|
||||
if (projectSdk != null) return projectSdk
|
||||
@@ -124,10 +121,13 @@ class ScriptDependenciesManager internal constructor(
|
||||
internal val log = Logger.getInstance(ScriptDependenciesManager::class.java)
|
||||
|
||||
@TestOnly
|
||||
fun updateScriptDependenciesSynchronously(virtualFile: VirtualFile, project: Project) {
|
||||
fun updateScriptDependenciesSynchronously(file: PsiFile, project: Project) {
|
||||
val loader = SyncScriptDependenciesLoader(project)
|
||||
val scriptDefinition = virtualFile.findScriptDefinition(project) ?: return
|
||||
loader.loadDependencies(virtualFile, scriptDefinition)
|
||||
val scriptDefinition = file.findScriptDefinition() ?: return
|
||||
assert(file is KtFile) {
|
||||
"PsiFile should be a KtFile, otherwise script dependencies cannot be loaded"
|
||||
}
|
||||
loader.loadDependencies(file as KtFile, scriptDefinition)
|
||||
loader.notifyRootsChanged()
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -28,9 +28,9 @@ fun VirtualFile.removeScriptDependenciesNotificationPanel(project: Project) {
|
||||
}
|
||||
|
||||
fun VirtualFile.addScriptDependenciesNotificationPanel(
|
||||
compilationConfigurationResult: ScriptCompilationConfigurationResult?,
|
||||
compilationConfigurationResult: ScriptCompilationConfigurationResult,
|
||||
project: Project,
|
||||
onClick: (ScriptCompilationConfigurationResult?) -> Unit
|
||||
onClick: (ScriptCompilationConfigurationResult) -> Unit
|
||||
) {
|
||||
withSelectedEditor(project) { manager ->
|
||||
val existingPanel = notificationPanel
|
||||
@@ -61,8 +61,8 @@ private fun VirtualFile.withSelectedEditor(project: Project, f: FileEditor.(File
|
||||
private var FileEditor.notificationPanel: NewScriptDependenciesNotificationPanel? by UserDataProperty<FileEditor, NewScriptDependenciesNotificationPanel>(Key.create("script.dependencies.panel"))
|
||||
|
||||
private class NewScriptDependenciesNotificationPanel(
|
||||
onClick: (ScriptCompilationConfigurationResult?) -> Unit,
|
||||
val compilationConfigurationResult: ScriptCompilationConfigurationResult?,
|
||||
onClick: (ScriptCompilationConfigurationResult) -> Unit,
|
||||
val compilationConfigurationResult: ScriptCompilationConfigurationResult,
|
||||
project: Project
|
||||
) : EditorNotificationPanel() {
|
||||
|
||||
|
||||
+68
-32
@@ -15,15 +15,19 @@ import com.intellij.openapi.roots.ModuleRootEvent
|
||||
import com.intellij.openapi.roots.ModuleRootListener
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiElementFinder
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.NonClasspathDirectoriesScope
|
||||
import com.intellij.util.containers.ConcurrentFactoryMap
|
||||
import com.intellij.util.containers.SLRUMap
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.kotlin.idea.caches.project.getAllProjectSdks
|
||||
import org.jetbrains.kotlin.idea.core.util.EDT
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
@@ -51,45 +55,79 @@ class ScriptsCompilationConfigurationCache(private val project: Project) {
|
||||
|
||||
private val cacheLock = ReentrantReadWriteLock()
|
||||
|
||||
private val scriptDependenciesCache = SLRUCacheWithLock<ScriptCompilationConfigurationResult>()
|
||||
private val scriptsModificationStampsCache = SLRUCacheWithLock<Long>()
|
||||
private val scriptDependenciesCache = SLRUCacheWithLock<VirtualFile, ScriptCompilationConfigurationResult>()
|
||||
private val scriptsModificationStampsCache = SLRUCacheWithLock<KtFile, Long>()
|
||||
|
||||
operator fun get(virtualFile: VirtualFile): ScriptCompilationConfigurationResult? = scriptDependenciesCache.get(virtualFile)
|
||||
operator fun get(file: VirtualFile): ScriptCompilationConfigurationResult? = scriptDependenciesCache.get(file)
|
||||
|
||||
fun shouldRunDependenciesUpdate(file: VirtualFile): Boolean {
|
||||
fun shouldRunDependenciesUpdate(file: KtFile): Boolean {
|
||||
return scriptsModificationStampsCache.replace(file, file.modificationStamp) != file.modificationStamp
|
||||
}
|
||||
|
||||
private val scriptsDependenciesClasspathScopeCache = SLRUCacheWithLock<GlobalSearchScope>()
|
||||
private val scriptsDependenciesClasspathScopeCache: MutableMap<VirtualFile, GlobalSearchScope> =
|
||||
ConcurrentFactoryMap.createWeakMap { file ->
|
||||
val compilationConfiguration = getCachedConfigurationOrLoad(file)
|
||||
?: return@createWeakMap null
|
||||
|
||||
fun scriptDependenciesClassFilesScope(file: VirtualFile): GlobalSearchScope {
|
||||
return scriptsDependenciesClasspathScopeCache.getOrPut(file) {
|
||||
val compilationConfiguration = scriptDependenciesCache.get(file)?.valueOrNull() ?: return@getOrPut GlobalSearchScope.EMPTY_SCOPE
|
||||
val roots = compilationConfiguration.dependenciesClassPath
|
||||
|
||||
val sdk = ScriptDependenciesManager.getScriptSdk(compilationConfiguration)
|
||||
val sdk = getScriptSdk(file)
|
||||
|
||||
@Suppress("FoldInitializerAndIfToElvis")
|
||||
if (sdk == null) {
|
||||
return@getOrPut NonClasspathDirectoriesScope.compose(ScriptDependenciesManager.toVfsRoots(roots))
|
||||
return@createWeakMap NonClasspathDirectoriesScope.compose(ScriptDependenciesManager.toVfsRoots(roots))
|
||||
}
|
||||
|
||||
return@getOrPut NonClasspathDirectoriesScope.compose(
|
||||
return@createWeakMap NonClasspathDirectoriesScope.compose(
|
||||
sdk.rootProvider.getFiles(OrderRootType.CLASSES).toList() +
|
||||
ScriptDependenciesManager.toVfsRoots(roots)
|
||||
)
|
||||
}
|
||||
|
||||
fun scriptDependenciesClassFilesScope(file: VirtualFile): GlobalSearchScope {
|
||||
return scriptsDependenciesClasspathScopeCache[file] ?: GlobalSearchScope.EMPTY_SCOPE
|
||||
}
|
||||
|
||||
private val scriptsSdksCache: MutableMap<VirtualFile, Sdk?> =
|
||||
ConcurrentFactoryMap.createWeakMap { file ->
|
||||
val compilationConfiguration = getCachedConfigurationOrLoad(file)
|
||||
return@createWeakMap getScriptSdk(compilationConfiguration) ?: ScriptDependenciesManager.getScriptDefaultSdk(project)
|
||||
}
|
||||
|
||||
fun getScriptSdk(file: VirtualFile): Sdk? {
|
||||
return scriptsSdksCache[file]
|
||||
}
|
||||
|
||||
private fun getCachedConfigurationOrLoad(file: VirtualFile): ScriptCompilationConfigurationWrapper? {
|
||||
val compilationConfiguration = scriptDependenciesCache.get(file)?.valueOrNull()
|
||||
|
||||
if (compilationConfiguration != null) {
|
||||
return compilationConfiguration
|
||||
}
|
||||
|
||||
val ktFile = PsiManager.getInstance(project).findFile(file) as? KtFile ?: return null
|
||||
return ScriptDependenciesManager.getInstance(project).getRefinedCompilationConfiguration(ktFile)?.valueOrNull()
|
||||
}
|
||||
|
||||
private fun getScriptSdk(compilationConfiguration: ScriptCompilationConfigurationWrapper?): Sdk? {
|
||||
// workaround for mismatched gradle wrapper and plugin version
|
||||
val javaHome = try {
|
||||
compilationConfiguration?.javaHome?.let { VfsUtil.findFileByIoFile(it, true) }
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
} ?: return null
|
||||
|
||||
return getAllProjectSdks().find { it.homeDirectory == javaHome }
|
||||
}
|
||||
|
||||
val allSdks by ClearableLazyValue(cacheLock) {
|
||||
scriptDependenciesCache.getAll()
|
||||
.mapNotNull { ScriptDependenciesManager.getInstance(project).getScriptSdk(it.key, project) }
|
||||
.mapNotNull { getScriptSdk(it.key) }
|
||||
.distinct()
|
||||
}
|
||||
|
||||
private val allNonIndexedSdks by ClearableLazyValue(cacheLock) {
|
||||
scriptDependenciesCache.getAll()
|
||||
.mapNotNull { ScriptDependenciesManager.getInstance(project).getScriptSdk(it.key, project) }
|
||||
.mapNotNull { getScriptSdk(it.key) }
|
||||
.filterNonModuleSdk()
|
||||
.distinct()
|
||||
}
|
||||
@@ -164,7 +202,7 @@ class ScriptsCompilationConfigurationCache(private val project: Project) {
|
||||
}
|
||||
|
||||
fun hasNotCachedRoots(compilationConfiguration: ScriptCompilationConfigurationWrapper): Boolean {
|
||||
val scriptSdk = ScriptDependenciesManager.getScriptSdk(compilationConfiguration) ?: ScriptDependenciesManager.getScriptDefaultSdk(project)
|
||||
val scriptSdk = getScriptSdk(compilationConfiguration) ?: ScriptDependenciesManager.getScriptDefaultSdk(project)
|
||||
return (scriptSdk != null && !allSdks.contains(scriptSdk)) ||
|
||||
!allDependenciesClassFiles.containsAll(ScriptDependenciesManager.toVfsRoots(compilationConfiguration.dependenciesClassPath)) ||
|
||||
!allDependenciesSources.containsAll(ScriptDependenciesManager.toVfsRoots(compilationConfiguration.dependenciesSources))
|
||||
@@ -176,23 +214,23 @@ class ScriptsCompilationConfigurationCache(private val project: Project) {
|
||||
scriptDependenciesCache.clear()
|
||||
scriptsModificationStampsCache.clear()
|
||||
|
||||
onChange(keys)
|
||||
updateHighlighting(keys)
|
||||
}
|
||||
|
||||
fun save(virtualFile: VirtualFile, new: ScriptCompilationConfigurationResult): Boolean {
|
||||
val old = scriptDependenciesCache.replace(virtualFile, new)
|
||||
fun save(file: VirtualFile, new: ScriptCompilationConfigurationResult): Boolean {
|
||||
val old = scriptDependenciesCache.replace(file, new)
|
||||
val changed = new != old
|
||||
if (changed) {
|
||||
onChange(listOf(virtualFile))
|
||||
onChange(listOf(file))
|
||||
}
|
||||
|
||||
return changed
|
||||
}
|
||||
|
||||
fun delete(virtualFile: VirtualFile): Boolean {
|
||||
val changed = scriptDependenciesCache.remove(virtualFile)
|
||||
fun delete(file: VirtualFile): Boolean {
|
||||
val changed = scriptDependenciesCache.remove(file)
|
||||
if (changed) {
|
||||
onChange(listOf(virtualFile))
|
||||
onChange(listOf(file))
|
||||
}
|
||||
return changed
|
||||
}
|
||||
@@ -226,20 +264,19 @@ private class ClearableLazyValue<in R, out T : Any>(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class SLRUCacheWithLock<T> {
|
||||
private class SLRUCacheWithLock<K, V> {
|
||||
private val lock = ReentrantReadWriteLock()
|
||||
|
||||
val cache = SLRUMap<VirtualFile, T>(
|
||||
val cache = SLRUMap<K, V>(
|
||||
ScriptsCompilationConfigurationCache.MAX_SCRIPTS_CACHED,
|
||||
ScriptsCompilationConfigurationCache.MAX_SCRIPTS_CACHED
|
||||
)
|
||||
|
||||
fun get(value: VirtualFile): T? = lock.write {
|
||||
fun get(value: K): V? = lock.write {
|
||||
cache[value]
|
||||
}
|
||||
|
||||
fun getOrPut(key: VirtualFile, defaultValue: () -> T): T = lock.write {
|
||||
fun getOrPut(key: K, defaultValue: () -> V): V = lock.write {
|
||||
val value = cache.get(key)
|
||||
return if (value == null) {
|
||||
val answer = defaultValue()
|
||||
@@ -250,11 +287,11 @@ private class SLRUCacheWithLock<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fun remove(file: VirtualFile) = lock.write {
|
||||
fun remove(file: K) = lock.write {
|
||||
cache.remove(file)
|
||||
}
|
||||
|
||||
fun getAll(): Collection<Map.Entry<VirtualFile, T>> = lock.write {
|
||||
fun getAll(): Collection<Map.Entry<K, V>> = lock.write {
|
||||
cache.entrySet()
|
||||
}
|
||||
|
||||
@@ -262,10 +299,9 @@ private class SLRUCacheWithLock<T> {
|
||||
cache.clear()
|
||||
}
|
||||
|
||||
fun replace(file: VirtualFile, value: T): T? = lock.write {
|
||||
fun replace(file: K, value: V): V? = lock.write {
|
||||
val old = get(file)
|
||||
cache.put(file, value)
|
||||
old
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+22
-36
@@ -39,13 +39,9 @@ import org.jetbrains.kotlin.idea.core.script.dependencies.SyncScriptDependencies
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.NotNullableUserDataProperty
|
||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.isNonScript
|
||||
import org.jetbrains.kotlin.scripting.resolve.LegacyResolverWrapper
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
|
||||
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
|
||||
|
||||
class ScriptsCompilationConfigurationUpdater(
|
||||
private val project: Project,
|
||||
@@ -65,8 +61,10 @@ class ScriptsCompilationConfigurationUpdater(
|
||||
listenForChangesInScripts()
|
||||
}
|
||||
|
||||
fun getCurrentCompilationConfiguration(file: VirtualFile): ScriptCompilationConfigurationResult? {
|
||||
cache[file]?.let { return it }
|
||||
fun getCurrentCompilationConfiguration(file: KtFile): ScriptCompilationConfigurationResult? {
|
||||
val virtualFile = file.originalFile.virtualFile
|
||||
|
||||
cache[virtualFile]?.let { return it }
|
||||
|
||||
// Try-catch block is a temporary workaround for ProcessCanceledException
|
||||
// that may happen during VirtualFile to PsiFile transformation
|
||||
@@ -76,10 +74,10 @@ class ScriptsCompilationConfigurationUpdater(
|
||||
makeRootsChangeIfNeeded()
|
||||
}
|
||||
|
||||
return cache[file]
|
||||
return cache[virtualFile]
|
||||
}
|
||||
|
||||
fun updateDependenciesIfNeeded(files: List<VirtualFile>): Boolean {
|
||||
fun updateDependenciesIfNeeded(files: List<KtFile>): Boolean {
|
||||
if (!ScriptDefinitionsManager.getInstance(project).isReady()) return false
|
||||
|
||||
var wasDependenciesUpdateStarted = false
|
||||
@@ -97,11 +95,11 @@ class ScriptsCompilationConfigurationUpdater(
|
||||
return wasDependenciesUpdateStarted
|
||||
}
|
||||
|
||||
private fun updateDependencies(file: VirtualFile) {
|
||||
private fun updateDependencies(file: KtFile) {
|
||||
if (!ScriptDefinitionsManager.getInstance(project).isReady()) return
|
||||
if (!cache.shouldRunDependenciesUpdate(file)) return
|
||||
|
||||
val scriptDefinition = file.findScriptDefinition(project) ?: return
|
||||
val scriptDefinition = file.findScriptDefinition() ?: return
|
||||
loaders.filter { it.isApplicable(file, scriptDefinition) }.forEach { it.loadDependencies(file, scriptDefinition) }
|
||||
}
|
||||
|
||||
@@ -122,24 +120,18 @@ class ScriptsCompilationConfigurationUpdater(
|
||||
}
|
||||
|
||||
private fun runScriptDependenciesUpdateIfNeeded(file: VirtualFile) {
|
||||
if (!shouldStartUpdate(file)) return
|
||||
val ktFile = getKtFileToStartConfigurationUpdate(file) ?: return
|
||||
|
||||
updateDependencies(file)
|
||||
updateDependencies(ktFile)
|
||||
makeRootsChangeIfNeeded()
|
||||
}
|
||||
})
|
||||
|
||||
EditorFactory.getInstance().eventMulticaster.addDocumentListener(object : DocumentListener {
|
||||
override fun documentChanged(event: DocumentEvent) {
|
||||
|
||||
val document = event.document
|
||||
val file = FileDocumentManager.getInstance().getFile(document)?.takeIf { it.isInLocalFileSystem } ?: return
|
||||
if (!file.isValid) {
|
||||
cache.delete(file)
|
||||
return
|
||||
}
|
||||
|
||||
if (!shouldStartUpdate(file)) return
|
||||
val ktFile = getKtFileToStartConfigurationUpdate(file) ?: return
|
||||
|
||||
// only update dependencies for scripts that were touched recently
|
||||
if (cache[file] == null) {
|
||||
@@ -151,7 +143,7 @@ class ScriptsCompilationConfigurationUpdater(
|
||||
scriptsQueue.addRequest(
|
||||
{
|
||||
FileDocumentManager.getInstance().saveDocument(document)
|
||||
updateDependencies(file)
|
||||
updateDependencies(ktFile)
|
||||
makeRootsChangeIfNeeded()
|
||||
},
|
||||
scriptChangesListenerDelay,
|
||||
@@ -161,31 +153,30 @@ class ScriptsCompilationConfigurationUpdater(
|
||||
}, project.messageBus.connect())
|
||||
}
|
||||
|
||||
private fun shouldStartUpdate(file: VirtualFile): Boolean {
|
||||
private fun getKtFileToStartConfigurationUpdate(file: VirtualFile): KtFile? {
|
||||
if (project.isDisposed || !file.isValid || file.isNonScript()) {
|
||||
return false
|
||||
return null
|
||||
}
|
||||
|
||||
if (
|
||||
ApplicationManager.getApplication().isUnitTestMode &&
|
||||
ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled == true
|
||||
) {
|
||||
return false
|
||||
return null
|
||||
}
|
||||
|
||||
val ktFile = PsiManager.getInstance(project).findFile(file) as? KtFile ?: return false
|
||||
return ProjectRootsUtil.isInProjectSource(ktFile, includeScriptsOutsideSourceRoots = true)
|
||||
val ktFile = PsiManager.getInstance(project).findFile(file) as? KtFile ?: return null
|
||||
if (ProjectRootsUtil.isInProjectSource(ktFile, includeScriptsOutsideSourceRoots = true)) {
|
||||
return ktFile
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun areDependenciesCached(file: VirtualFile): Boolean {
|
||||
return cache[file] != null || file.scriptDependencies != null
|
||||
return cache[file] != null || file.scriptDependencies != null || file.scriptCompilationConfiguration != null
|
||||
}
|
||||
|
||||
fun isAsyncDependencyResolver(scriptDef: ScriptDefinition): Boolean =
|
||||
scriptDef.asLegacyOrNull<KotlinScriptDefinition>()?.dependencyResolver?.let {
|
||||
it is AsyncDependenciesResolver || it is LegacyResolverWrapper
|
||||
} ?: false
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project): ScriptsCompilationConfigurationUpdater =
|
||||
@@ -194,11 +185,6 @@ class ScriptsCompilationConfigurationUpdater(
|
||||
fun areDependenciesCached(file: KtFile): Boolean {
|
||||
return getInstance(file.project).areDependenciesCached(file.virtualFile)
|
||||
}
|
||||
|
||||
fun isAsyncDependencyResolver(file: KtFile): Boolean {
|
||||
val scriptDefinition = file.virtualFile.findScriptDefinition(file.project) ?: return false
|
||||
return getInstance(file.project).isAsyncDependencyResolver(scriptDefinition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@@ -10,13 +10,12 @@ import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.progress.util.BackgroundTaskUtil
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import kotlinx.coroutines.Runnable
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater
|
||||
import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
|
||||
import org.jetbrains.kotlin.scripting.resolve.KtFileScriptSource
|
||||
import org.jetbrains.kotlin.scripting.resolve.refineScriptCompilationConfiguration
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
@@ -30,14 +29,14 @@ class AsyncScriptDependenciesLoader internal constructor(project: Project) : Scr
|
||||
private var backgroundTasksQueue: LoaderBackgroundTask? = null
|
||||
|
||||
override fun isApplicable(
|
||||
file: VirtualFile,
|
||||
file: KtFile,
|
||||
scriptDefinition: ScriptDefinition
|
||||
): Boolean {
|
||||
return ScriptsCompilationConfigurationUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition)
|
||||
return isAsyncDependencyResolver(scriptDefinition)
|
||||
}
|
||||
|
||||
override fun loadDependencies(
|
||||
file: VirtualFile,
|
||||
file: KtFile,
|
||||
scriptDefinition: ScriptDefinition
|
||||
) {
|
||||
lock.write {
|
||||
@@ -74,20 +73,20 @@ class AsyncScriptDependenciesLoader internal constructor(project: Project) : Scr
|
||||
return false
|
||||
}
|
||||
|
||||
private fun runDependenciesUpdate(file: VirtualFile) {
|
||||
val scriptDef = file.findScriptDefinition(project) ?: return
|
||||
private fun runDependenciesUpdate(file: KtFile) {
|
||||
val scriptDef = file.findScriptDefinition() ?: return
|
||||
|
||||
debug(file) { "start async dependencies loading" }
|
||||
|
||||
val result = refineScriptCompilationConfiguration(VirtualFileScriptSource(file), scriptDef, project)
|
||||
val result = refineScriptCompilationConfiguration(KtFileScriptSource(file), scriptDef, project)
|
||||
|
||||
debug(file) { "finish async dependencies loading" }
|
||||
|
||||
processRefinedConfiguration(result, file)
|
||||
processRefinedConfiguration(result, file.originalFile.virtualFile)
|
||||
}
|
||||
|
||||
private inner class LoaderBackgroundTask {
|
||||
private val sequenceOfFiles: ConcurrentLinkedQueue<VirtualFile> = ConcurrentLinkedQueue()
|
||||
private val sequenceOfFiles: ConcurrentLinkedQueue<KtFile> = ConcurrentLinkedQueue()
|
||||
private var forceStop : Boolean = false
|
||||
private var startedSilently : Boolean = false
|
||||
|
||||
@@ -124,7 +123,7 @@ class AsyncScriptDependenciesLoader internal constructor(project: Project) : Scr
|
||||
}.queue()
|
||||
}
|
||||
|
||||
fun addTask(file: VirtualFile) {
|
||||
fun addTask(file: KtFile) {
|
||||
if (sequenceOfFiles.contains(file)) return
|
||||
|
||||
debug(file) { "added to update queue" }
|
||||
|
||||
+15
-13
@@ -9,39 +9,41 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.scriptCompilationConfiguration
|
||||
import org.jetbrains.kotlin.idea.core.script.scriptDependencies
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.KtFileScriptSource
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
|
||||
import kotlin.script.experimental.api.asSuccess
|
||||
|
||||
// TODO: rename and provide alias for compatibility - this is not only about dependencies anymore
|
||||
class FromFileAttributeScriptDependenciesLoader(project: Project) : ScriptDependenciesLoader(project) {
|
||||
|
||||
override fun isApplicable(
|
||||
file: VirtualFile,
|
||||
file: KtFile,
|
||||
scriptDefinition: ScriptDefinition
|
||||
): Boolean {
|
||||
return file.scriptDependencies != null || file.scriptCompilationConfiguration != null
|
||||
}
|
||||
|
||||
override fun loadDependencies(
|
||||
file: VirtualFile,
|
||||
file: KtFile,
|
||||
scriptDefinition: ScriptDefinition
|
||||
) {
|
||||
file.scriptCompilationConfiguration?.let {
|
||||
ScriptCompilationConfigurationWrapper.FromCompilationConfiguration(VirtualFileScriptSource(file), it).apply {
|
||||
debug(file) { "refined configuration from fileAttributes = $it" }
|
||||
val virtualFile = file.originalFile.virtualFile
|
||||
virtualFile.scriptCompilationConfiguration?.let {
|
||||
ScriptCompilationConfigurationWrapper.FromCompilationConfiguration(KtFileScriptSource(file), it).apply {
|
||||
debug(virtualFile) { "refined configuration from fileAttributes = $it" }
|
||||
}
|
||||
} ?: file.scriptDependencies?.let {
|
||||
ScriptCompilationConfigurationWrapper.FromLegacy(VirtualFileScriptSource(file), it, scriptDefinition).apply {
|
||||
debug(file) { "dependencies from fileAttributes = $it" }
|
||||
} ?: virtualFile.scriptDependencies?.let {
|
||||
ScriptCompilationConfigurationWrapper.FromLegacy(KtFileScriptSource(file), it, scriptDefinition).apply {
|
||||
debug(virtualFile) { "dependencies from fileAttributes = $it" }
|
||||
}
|
||||
}?.let {
|
||||
if (areDependenciesValid(file, it)) {
|
||||
saveToCache(file, it.asSuccess(), skipSaveToAttributes = true)
|
||||
if (areDependenciesValid(virtualFile, it)) {
|
||||
saveToCache(virtualFile, it.asSuccess(), skipSaveToAttributes = true)
|
||||
} else {
|
||||
file.scriptCompilationConfiguration = null
|
||||
file.scriptDependencies = null
|
||||
virtualFile.scriptCompilationConfiguration = null
|
||||
virtualFile.scriptDependencies = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -18,10 +18,13 @@ package org.jetbrains.kotlin.idea.core.script.dependencies
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.ResolveScopeProvider
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager
|
||||
import org.jetbrains.kotlin.idea.core.script.StandardIdeScriptDefinition
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||
@@ -33,7 +36,10 @@ class KotlinScriptResolveScopeProvider : ResolveScopeProvider() {
|
||||
}
|
||||
|
||||
override fun getResolveScope(file: VirtualFile, project: Project): GlobalSearchScope? {
|
||||
val scriptDefinition = file.findScriptDefinition(project)
|
||||
if (file.fileType != KotlinFileType.INSTANCE) return null
|
||||
|
||||
val ktFile = PsiManager.getInstance(project).findFile(file) as? KtFile ?: return null
|
||||
val scriptDefinition = ktFile.findScriptDefinition()
|
||||
return when {
|
||||
scriptDefinition == null -> null
|
||||
// This is a workaround for completion in scripts and REPL to provide module dependencies
|
||||
|
||||
+12
-7
@@ -6,27 +6,32 @@
|
||||
package org.jetbrains.kotlin.idea.core.script.dependencies
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager
|
||||
import org.jetbrains.kotlin.idea.highlighter.OutsidersPsiFileSupportUtils
|
||||
import org.jetbrains.kotlin.idea.highlighter.OutsidersPsiFileSupportWrapper
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
|
||||
class OutsiderFileDependenciesLoader(project: Project) : ScriptDependenciesLoader(project) {
|
||||
override fun isApplicable(
|
||||
file: VirtualFile,
|
||||
file: KtFile,
|
||||
scriptDefinition: ScriptDefinition
|
||||
): Boolean {
|
||||
return OutsidersPsiFileSupportWrapper.isOutsiderFile(file)
|
||||
val virtualFile = file.virtualFile ?: return false
|
||||
return OutsidersPsiFileSupportWrapper.isOutsiderFile(virtualFile)
|
||||
}
|
||||
|
||||
override fun loadDependencies(
|
||||
file: VirtualFile,
|
||||
file: KtFile,
|
||||
scriptDefinition: ScriptDefinition
|
||||
) {
|
||||
val fileOrigin = OutsidersPsiFileSupportUtils.getOutsiderFileOrigin(project, file) ?: return
|
||||
val compilationConfiguration = ScriptDependenciesManager.getInstance(project).getRefinedCompilationConfiguration(fileOrigin) ?: return
|
||||
saveToCache(file, compilationConfiguration)
|
||||
val virtualFile = file.virtualFile ?: return
|
||||
val fileOrigin = OutsidersPsiFileSupportUtils.getOutsiderFileOrigin(project, virtualFile) ?: return
|
||||
val psiFileOrigin = PsiManager.getInstance(project).findFile(fileOrigin) as? KtFile ?: return
|
||||
val compilationConfiguration =
|
||||
ScriptDependenciesManager.getInstance(project).getRefinedCompilationConfiguration(psiFileOrigin) ?: return
|
||||
saveToCache(virtualFile, compilationConfiguration)
|
||||
}
|
||||
|
||||
override fun shouldShowNotification(): Boolean = false
|
||||
|
||||
+23
-9
@@ -15,18 +15,22 @@ import com.intellij.openapi.util.EmptyRunnable
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.*
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.LegacyResolverWrapper
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationWrapper
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.api.valueOrNull
|
||||
import kotlin.script.experimental.dependencies.AsyncDependenciesResolver
|
||||
|
||||
// TODO: rename and provide alias for compatibility - this is not only about dependencies anymore
|
||||
abstract class ScriptDependenciesLoader(protected val project: Project) {
|
||||
|
||||
abstract fun isApplicable(file: VirtualFile, scriptDefinition: ScriptDefinition): Boolean
|
||||
abstract fun loadDependencies(file: VirtualFile, scriptDefinition: ScriptDefinition)
|
||||
abstract fun isApplicable(file: KtFile, scriptDefinition: ScriptDefinition): Boolean
|
||||
abstract fun loadDependencies(file: KtFile, scriptDefinition: ScriptDefinition)
|
||||
|
||||
protected abstract fun shouldShowNotification(): Boolean
|
||||
|
||||
@@ -138,13 +142,23 @@ abstract class ScriptDependenciesLoader(protected val project: Project) {
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.script")
|
||||
protected fun isAsyncDependencyResolver(scriptDef: ScriptDefinition): Boolean =
|
||||
scriptDef.asLegacyOrNull<KotlinScriptDefinition>()?.dependencyResolver?.let {
|
||||
it is AsyncDependenciesResolver || it is LegacyResolverWrapper
|
||||
} ?: false
|
||||
|
||||
internal fun debug(file: VirtualFile? = null, message: () -> String) {
|
||||
if (LOG.isDebugEnabled) {
|
||||
LOG.debug("[KOTLIN SCRIPT] " + (file?.let { "file = ${file.path}, " } ?: "") + message())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.script")
|
||||
|
||||
internal fun debug(file: VirtualFile? = null, message: () -> String) {
|
||||
if (LOG.isDebugEnabled) {
|
||||
LOG.debug("[KOTLIN SCRIPT] ${file?.path} " + message())
|
||||
}
|
||||
}
|
||||
|
||||
internal fun debug(file: KtFile, message: () -> String) {
|
||||
if (LOG.isDebugEnabled) {
|
||||
LOG.debug("[KOTLIN SCRIPT] ${file.originalFile.virtualFile?.path} " + message())
|
||||
}
|
||||
}
|
||||
|
||||
+7
-8
@@ -6,28 +6,27 @@
|
||||
package org.jetbrains.kotlin.idea.core.script.dependencies
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptsCompilationConfigurationUpdater
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
|
||||
import org.jetbrains.kotlin.scripting.resolve.KtFileScriptSource
|
||||
import org.jetbrains.kotlin.scripting.resolve.refineScriptCompilationConfiguration
|
||||
|
||||
class SyncScriptDependenciesLoader(project: Project) : ScriptDependenciesLoader(project) {
|
||||
override fun isApplicable(
|
||||
file: VirtualFile,
|
||||
file: KtFile,
|
||||
scriptDefinition: ScriptDefinition
|
||||
): Boolean {
|
||||
return !ScriptsCompilationConfigurationUpdater.getInstance(project).isAsyncDependencyResolver(scriptDefinition)
|
||||
return !isAsyncDependencyResolver(scriptDefinition)
|
||||
}
|
||||
|
||||
override fun loadDependencies(
|
||||
file: VirtualFile,
|
||||
file: KtFile,
|
||||
scriptDefinition: ScriptDefinition
|
||||
) {
|
||||
debug(file) { "start sync dependencies loading" }
|
||||
val result = refineScriptCompilationConfiguration(VirtualFileScriptSource(file), scriptDefinition, project)
|
||||
val result = refineScriptCompilationConfiguration(KtFileScriptSource(file), scriptDefinition, project)
|
||||
debug(file) { "finish sync dependencies loading" }
|
||||
processRefinedConfiguration(result, file)
|
||||
processRefinedConfiguration(result, file.originalFile.virtualFile)
|
||||
}
|
||||
|
||||
override fun shouldShowNotification(): Boolean = false
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ class GradleBuildFileHighlightingTest : GradleImportingTestCase() {
|
||||
val psiFile = PsiManager.getInstance(myProject).findFile(file) as? KtFile
|
||||
?: error("Couldn't find psiFile for virtual file: ${file.canonicalPath}")
|
||||
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(file, myProject)
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(psiFile, myProject)
|
||||
|
||||
val bindingContext = psiFile.analyzeWithContent()
|
||||
|
||||
|
||||
+1
-1
@@ -166,7 +166,7 @@ class KtScratchExecutionSession(
|
||||
}
|
||||
|
||||
ScriptDependenciesManager.getInstance(originalFile.project)
|
||||
.getRefinedCompilationConfiguration(originalFile.virtualFile)?.valueOrNull()?.let {
|
||||
.getRefinedCompilationConfiguration(originalFile)?.valueOrNull()?.let {
|
||||
javaParameters.classPath.addAll(it.dependenciesClassPath.map { f -> f.absolutePath })
|
||||
}
|
||||
|
||||
|
||||
@@ -65,8 +65,9 @@ class ImportInsertHelperImpl(private val project: Project) : ImportInsertHelper(
|
||||
val allDefaultImports = platform.findAnalyzerServices.getDefaultImports(languageVersionSettings, includeLowPriorityImports = true)
|
||||
|
||||
val scriptExtraImports = contextFile.takeIf { it.isScript() }?.let { ktFile ->
|
||||
val scriptDependencies =
|
||||
ScriptDependenciesProvider.getInstance(ktFile.project)?.getScriptConfigurationResult(ktFile.originalFile)?.valueOrNull()
|
||||
val scriptDependencies = ScriptDependenciesProvider.getInstance(ktFile.project)
|
||||
?.getScriptConfigurationResult(ktFile.originalFile as KtFile)
|
||||
?.valueOrNull()
|
||||
scriptDependencies?.defaultImports?.map { ImportPath.fromString(it) }
|
||||
}.orEmpty()
|
||||
|
||||
|
||||
+5
-6
@@ -12,10 +12,7 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager
|
||||
import com.intellij.psi.codeStyle.VariableKind
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor.ConflictsInTestsException
|
||||
@@ -32,8 +29,8 @@ import com.intellij.testFramework.fixtures.CodeInsightTestFixture
|
||||
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager
|
||||
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
|
||||
import org.jetbrains.kotlin.idea.refactoring.chooseMembers
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractClass.ExtractSuperInfo
|
||||
@@ -325,7 +322,9 @@ abstract class AbstractExtractionTest : KotlinLightCodeInsightFixtureTestCase()
|
||||
PluginTestCaseBase.addJdk(myFixture.projectDisposable, PluginTestCaseBase::mockJdk)
|
||||
|
||||
if (mainFile.extension == KotlinParserDefinition.STD_SCRIPT_SUFFIX) {
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(VfsUtil.findFileByIoFile(mainFile, true)!!, project)
|
||||
val virtualFile = VfsUtil.findFileByIoFile(mainFile, true)!!
|
||||
val ktFile = PsiManager.getInstance(project).findFile(virtualFile)!!
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(ktFile, project)
|
||||
}
|
||||
|
||||
fixture.testDataPath = "${KotlinTestUtils.getHomeDirectory()}/${mainFile.parent}"
|
||||
|
||||
@@ -71,7 +71,7 @@ class StandaloneScriptRunConfigurationTest : KotlinCodeInsightTestCase() {
|
||||
fun testOnFileMoveWithDefaultWorkingDir() {
|
||||
configureByFile("move/script.kts")
|
||||
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(myFile.virtualFile, project)
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(myFile, project)
|
||||
|
||||
val script = KotlinScriptFqnIndex.instance.get("foo.Script", project, project.allScope()).single()
|
||||
val runConfiguration = createConfigurationFromElement(script, save = true) as KotlinStandaloneScriptRunConfiguration
|
||||
@@ -98,7 +98,7 @@ class StandaloneScriptRunConfigurationTest : KotlinCodeInsightTestCase() {
|
||||
fun testOnFileMoveWithNonDefaultWorkingDir() {
|
||||
configureByFile("move/script.kts")
|
||||
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(myFile.virtualFile, project)
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(myFile, project)
|
||||
|
||||
val script = KotlinScriptFqnIndex.instance.get("foo.Script", project, project.allScope()).single()
|
||||
val runConfiguration = createConfigurationFromElement(script, save = true) as KotlinStandaloneScriptRunConfiguration
|
||||
|
||||
@@ -204,7 +204,7 @@ abstract class AbstractScratchRunActionTest : FileEditorManagerTestCase() {
|
||||
|
||||
myFixture.openFileInEditor(scratchVirtualFile)
|
||||
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(scratchVirtualFile, project)
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(myFixture.file, project)
|
||||
|
||||
val scratchFileEditor = getScratchEditorForSelectedFile(myManager, myFixture.file.virtualFile)
|
||||
?: error("Couldn't find scratch file")
|
||||
@@ -217,7 +217,7 @@ abstract class AbstractScratchRunActionTest : FileEditorManagerTestCase() {
|
||||
protected fun configureWorksheetByText(name: String, text: String): ScratchFile {
|
||||
val worksheetFile = myFixture.configureByText(name, text).virtualFile
|
||||
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(worksheetFile, project)
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(myFixture.file, project)
|
||||
|
||||
val scratchFileEditor = getScratchEditorForSelectedFile(myManager, myFixture.file.virtualFile)
|
||||
?: error("Couldn't find scratch panel")
|
||||
|
||||
@@ -37,7 +37,7 @@ abstract class AbstractScratchLineMarkersTest : FileEditorManagerTestCase() {
|
||||
|
||||
myFixture.openFileInEditor(scratchVirtualFile)
|
||||
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(scratchVirtualFile, project)
|
||||
ScriptDependenciesManager.updateScriptDependenciesSynchronously(myFixture.file, project)
|
||||
|
||||
val scratchFileEditor = getScratchEditorForSelectedFile(FileEditorManager.getInstance(project), myFixture.file.virtualFile)
|
||||
?: error("Couldn't find scratch panel")
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingUtil
|
||||
import org.jetbrains.kotlin.idea.navigation.GotoCheck
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.MockLibraryUtil
|
||||
@@ -78,7 +79,7 @@ abstract class AbstractScriptConfigurationHighlightingTest : AbstractScriptConfi
|
||||
}
|
||||
})
|
||||
|
||||
updateScriptDependenciesSynchronously(myFile.virtualFile, project)
|
||||
updateScriptDependenciesSynchronously(myFile, project)
|
||||
checkHighlighting(editor, false, false)
|
||||
}
|
||||
}
|
||||
@@ -134,7 +135,7 @@ abstract class AbstractScriptDefinitionsOrderTest : AbstractScriptConfigurationT
|
||||
}
|
||||
|
||||
ScriptDefinitionsManager.getInstance(project).reorderScriptDefinitions()
|
||||
updateScriptDependenciesSynchronously(myFile.virtualFile, project)
|
||||
updateScriptDependenciesSynchronously(myFile, project)
|
||||
|
||||
checkHighlighting(editor, false, false)
|
||||
}
|
||||
@@ -345,13 +346,13 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() {
|
||||
if (script == null) error("Test file with script couldn't be found in test project")
|
||||
|
||||
configureByExistingFile(script)
|
||||
updateScriptDependenciesSynchronously(script, project)
|
||||
updateScriptDependenciesSynchronously(myFile, project)
|
||||
|
||||
VfsUtil.markDirtyAndRefresh(false, true, true, project.baseDir)
|
||||
// This is needed because updateScriptDependencies invalidates psiFile that was stored in myFile field
|
||||
myFile = psiManager.findFile(script)
|
||||
|
||||
val isFatalErrorPresent = IdeScriptReportSink.getReports(script).any { it.severity == ScriptDiagnostic.Severity.FATAL }
|
||||
val isFatalErrorPresent = IdeScriptReportSink.getReports(myFile as KtFile).any { it.severity == ScriptDiagnostic.Severity.FATAL }
|
||||
assert(isFatalErrorPresent || KotlinHighlightingUtil.shouldHighlight(myFile)) {
|
||||
"Highlighting is switched off for ${myFile.virtualFile.path}"
|
||||
}
|
||||
|
||||
+14
-9
@@ -9,24 +9,29 @@ import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
|
||||
import kotlin.script.experimental.api.valueOrNull
|
||||
import kotlin.script.experimental.dependencies.ScriptDependencies
|
||||
|
||||
interface ScriptDependenciesProvider {
|
||||
open class ScriptDependenciesProvider constructor(
|
||||
protected val project: Project
|
||||
) {
|
||||
|
||||
@Deprecated("Migrating to configuration refinement", level = DeprecationLevel.ERROR)
|
||||
fun getScriptDependencies(file: VirtualFile): ScriptDependencies? =
|
||||
getScriptConfigurationResult(file)?.valueOrNull()?.legacyDependencies
|
||||
fun getScriptDependencies(file: VirtualFile): ScriptDependencies? {
|
||||
val ktFile = PsiManager.getInstance(project).findFile(file) as? KtFile ?: return null
|
||||
return getScriptConfigurationResult(ktFile)?.valueOrNull()?.legacyDependencies
|
||||
}
|
||||
|
||||
@Deprecated("Migrating to configuration refinement", level = DeprecationLevel.ERROR)
|
||||
fun getScriptDependencies(file: PsiFile): ScriptDependencies? =
|
||||
getScriptConfigurationResult(file.virtualFile ?: file.originalFile.virtualFile)?.valueOrNull()?.legacyDependencies
|
||||
fun getScriptDependencies(file: PsiFile): ScriptDependencies? {
|
||||
if (file !is KtFile) return null
|
||||
return getScriptConfigurationResult(file)?.valueOrNull()?.legacyDependencies
|
||||
}
|
||||
|
||||
fun getScriptConfigurationResult(file: VirtualFile): ScriptCompilationConfigurationResult? = null
|
||||
|
||||
fun getScriptConfigurationResult(file: PsiFile): ScriptCompilationConfigurationResult? =
|
||||
getScriptConfigurationResult(file.virtualFile ?: file.originalFile.virtualFile)
|
||||
open fun getScriptConfigurationResult(file: KtFile): ScriptCompilationConfigurationResult? = null
|
||||
|
||||
companion object {
|
||||
fun getInstance(project: Project): ScriptDependenciesProvider? =
|
||||
|
||||
+6
-4
@@ -25,18 +25,20 @@ inline fun <T> runReadAction(crossinline runnable: () -> T): T {
|
||||
fun PsiFile.findScriptDefinition(): ScriptDefinition? {
|
||||
// Do not use psiFile.script, see comments in findScriptDefinition
|
||||
if (this !is KtFile/* || this.script == null*/) return null
|
||||
val file = virtualFile ?: originalFile.virtualFile ?: return null
|
||||
if (file.isNonScript()) return null
|
||||
|
||||
return findScriptDefinitionByFilePath(project, File(file.path))
|
||||
val virtualFile = this.virtualFile ?: this.originalFile.virtualFile ?: return null
|
||||
if (virtualFile.isNonScript()) return null
|
||||
|
||||
return findScriptDefinitionByFilePath(project, File(virtualFile.path))
|
||||
}
|
||||
|
||||
@Deprecated("Use PsiFile.findScriptDefinition() instead")
|
||||
fun VirtualFile.findScriptDefinition(project: Project): ScriptDefinition? {
|
||||
if (!isValid || isNonScript()) return null
|
||||
|
||||
// Do not use psiFile.script here because this method can be called during indexes access
|
||||
// and accessing stubs may cause deadlock
|
||||
// TODO: measure performance effect and if necessary consider detecting indexing here or using separate logic for non-IDE operations to speed up filtering
|
||||
|
||||
if (runReadAction { PsiManager.getInstance(project).findFile(this) as? KtFile }/*?.script*/ == null) return null
|
||||
|
||||
return findScriptDefinitionByFilePath(project, File(path))
|
||||
|
||||
-12
@@ -5,8 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.scripting.definitions
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
|
||||
@@ -27,16 +25,6 @@ fun PsiFile.findScriptCompilationConfiguration(): ScriptCompilationConfiguration
|
||||
?: findScriptDefinition()?.compilationConfiguration
|
||||
}
|
||||
|
||||
fun VirtualFile.findScriptCompilationConfiguration(project: Project): ScriptCompilationConfiguration? {
|
||||
if (!isValid || isNonScript()) return null
|
||||
// see comments in findScriptDefinition
|
||||
// we do not need expensive check as in findScriptDefinition here, since it is assumed that this function is called only for known scripts
|
||||
|
||||
val provider = ScriptDependenciesProvider.getInstance(project)
|
||||
return provider?.getScriptConfigurationResult(this)?.valueOrError()?.configuration
|
||||
?: findScriptDefinition(project)?.compilationConfiguration
|
||||
}
|
||||
|
||||
private fun ScriptCompilationConfigurationResult.valueOrError() = valueOr { failure ->
|
||||
val singleCause = failure.reports.singleOrNull { it.severity == ScriptDiagnostic.Severity.ERROR }
|
||||
if (singleCause != null)
|
||||
|
||||
+3
-1
@@ -29,7 +29,9 @@ class ScriptExtraImportsProviderExtension : ExtraImportsProviderExtension {
|
||||
|
||||
override fun getExtraImports(ktFile: KtFile): Collection<KtImportInfo> =
|
||||
ktFile.takeIf { it.isScript() }?.let { file ->
|
||||
val refinedConfiguration = ScriptDependenciesProvider.getInstance(file.project)?.getScriptConfigurationResult(file.originalFile)?.valueOrNull()
|
||||
val refinedConfiguration = ScriptDependenciesProvider.getInstance(file.project)
|
||||
?.getScriptConfigurationResult(file.originalFile as KtFile)
|
||||
?.valueOrNull()
|
||||
refinedConfiguration?.defaultImports?.map {
|
||||
ScriptExtraImportImpl(
|
||||
ImportPath.fromString(it)
|
||||
|
||||
+9
-10
@@ -8,38 +8,37 @@ package org.jetbrains.kotlin.scripting.compiler.plugin.definitions
|
||||
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.psi.KtFile
|
||||
import org.jetbrains.kotlin.scripting.definitions.ScriptDependenciesProvider
|
||||
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
|
||||
import org.jetbrains.kotlin.scripting.resolve.KtFileScriptSource
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptCompilationConfigurationResult
|
||||
import org.jetbrains.kotlin.scripting.resolve.ScriptReportSink
|
||||
import org.jetbrains.kotlin.scripting.resolve.VirtualFileScriptSource
|
||||
import org.jetbrains.kotlin.scripting.resolve.refineScriptCompilationConfiguration
|
||||
import java.io.File
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
import kotlin.script.experimental.api.ResultWithDiagnostics
|
||||
import kotlin.script.experimental.jvm.compat.mapToLegacyReports
|
||||
|
||||
class CliScriptDependenciesProvider(private val project: Project) : ScriptDependenciesProvider {
|
||||
class CliScriptDependenciesProvider(project: Project) : ScriptDependenciesProvider(project) {
|
||||
private val cacheLock = ReentrantReadWriteLock()
|
||||
private val cache = hashMapOf<String, ScriptCompilationConfigurationResult?>()
|
||||
|
||||
override fun getScriptConfigurationResult(file: VirtualFile): ScriptCompilationConfigurationResult? = cacheLock.read {
|
||||
override fun getScriptConfigurationResult(file: KtFile): ScriptCompilationConfigurationResult? = cacheLock.read {
|
||||
calculateRefinedConfiguration(file)
|
||||
}
|
||||
|
||||
private fun calculateRefinedConfiguration(file: VirtualFile): ScriptCompilationConfigurationResult? {
|
||||
val path = file.path
|
||||
private fun calculateRefinedConfiguration(file: KtFile): ScriptCompilationConfigurationResult? {
|
||||
val path = file.virtualFilePath
|
||||
val cached = cache[path]
|
||||
return if (cached != null) cached
|
||||
else {
|
||||
val scriptDef = file.findScriptDefinition(project)
|
||||
val scriptDef = file.findScriptDefinition()
|
||||
if (scriptDef != null) {
|
||||
val result = refineScriptCompilationConfiguration(VirtualFileScriptSource(file), scriptDef, project)
|
||||
val result = refineScriptCompilationConfiguration(KtFileScriptSource(file), scriptDef, project)
|
||||
|
||||
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports)
|
||||
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file.virtualFile, result.reports)
|
||||
|
||||
if (result is ResultWithDiagnostics.Success) {
|
||||
log.info("[kts] new cached deps for $path: ${result.value.dependenciesClassPath.joinToString(File.pathSeparator)}")
|
||||
|
||||
Reference in New Issue
Block a user