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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user