diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesCache.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesCache.kt index 705d14de896..b776cfe1cf2 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesCache.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesCache.kt @@ -27,10 +27,7 @@ import com.intellij.util.containers.SLRUMap import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.jetbrains.kotlin.idea.core.util.EDT -import org.jetbrains.kotlin.utils.addIfNotNull -import java.io.File import java.util.concurrent.locks.ReentrantReadWriteLock -import kotlin.concurrent.read import kotlin.concurrent.write import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty @@ -45,12 +42,18 @@ class ScriptDependenciesCache(private val project: Project) { } private val cacheLock = ReentrantReadWriteLock() - private val cache = SLRUMap(MAX_SCRIPTS_CACHED, MAX_SCRIPTS_CACHED) - operator fun get(virtualFile: VirtualFile): ScriptDependencies? = cacheLock.write { cache[virtualFile] } + private val scriptDependenciesCache = SLRUCacheWithLock() + private val scriptsModificationStampsCache = SLRUCacheWithLock() + + operator fun get(virtualFile: VirtualFile): ScriptDependencies? = scriptDependenciesCache.get(virtualFile) + + fun shouldRunDependenciesUpdate(file: VirtualFile): Boolean { + return scriptsModificationStampsCache.replace(file, file.modificationStamp) != file.modificationStamp + } val allScriptsClasspath by ClearableLazyValue(cacheLock) { - val files = cache.entrySet().flatMap { it.value.classpath }.distinct() + val files = scriptDependenciesCache.getAll().flatMap { it.value.classpath }.distinct() ScriptDependenciesManager.toVfsRoots(files) } @@ -59,7 +62,7 @@ class ScriptDependenciesCache(private val project: Project) { } val allLibrarySources by ClearableLazyValue(cacheLock) { - ScriptDependenciesManager.toVfsRoots(cache.entrySet().flatMap { it.value.sources }.distinct()) + ScriptDependenciesManager.toVfsRoots(scriptDependenciesCache.getAll().flatMap { it.value.sources }.distinct()) } val allLibrarySourcesScope by ClearableLazyValue(cacheLock) { @@ -99,21 +102,15 @@ class ScriptDependenciesCache(private val project: Project) { } fun clear() { - val keys = cacheLock.read { - val keys = mutableListOf() - cache.iterateKeys { keys.addIfNotNull(it) } - cacheLock.write(cache::clear) - keys - } + val keys = scriptDependenciesCache.getAll().map { it.key }.toList() + + scriptDependenciesCache.clear() + onChange(keys) } fun save(virtualFile: VirtualFile, new: ScriptDependencies): Boolean { - val old = cacheLock.write { - val old = cache[virtualFile] - cache.put(virtualFile, new) - old - } + val old = scriptDependenciesCache.replace(virtualFile, new) val changed = new != old if (changed) { onChange(listOf(virtualFile)) @@ -123,37 +120,12 @@ class ScriptDependenciesCache(private val project: Project) { } fun delete(virtualFile: VirtualFile): Boolean { - val changed = cacheLock.write { - cache.remove(virtualFile) - } + val changed = scriptDependenciesCache.remove(virtualFile) if (changed) { onChange(listOf(virtualFile)) } return changed } - - fun combineDependencies(filePredicate: (VirtualFile) -> Boolean): ScriptDependencies = cacheLock.read { - val sources = mutableListOf() - val binaries = mutableListOf() - val imports = mutableListOf() - val scripts = mutableListOf() - - val relevantEntries = cache.entrySet().filter { filePredicate(it.key) }.map { it.value } - relevantEntries.forEach { - sources += it.sources - binaries += it.classpath - imports += it.imports - scripts += it.scripts - } - - return ScriptDependencies( - classpath = binaries.distinct(), - sources = sources.distinct(), - imports = imports.distinct(), - scripts = scripts.distinct(), - javaHome = relevantEntries.map { it.javaHome }.firstOrNull() - ) - } } private fun KProperty0.clearValue() { @@ -184,3 +156,35 @@ private class ClearableLazyValue( } } + +private class SLRUCacheWithLock { + private val lock = ReentrantReadWriteLock() + + val cache = SLRUMap( + ScriptDependenciesCache.MAX_SCRIPTS_CACHED, + ScriptDependenciesCache.MAX_SCRIPTS_CACHED + ) + + fun get(value: VirtualFile): T? = lock.write { + cache[value] + } + + fun remove(file: VirtualFile) = lock.write { + cache.remove(file) + } + + fun getAll(): Collection> = lock.write { + cache.entrySet() + } + + fun clear() = lock.write { + cache.clear() + } + + fun replace(file: VirtualFile, value: T): T? = lock.write { + val old = get(file) + cache.put(file, value) + old + } +} + diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesManager.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesManager.kt index 3d79aec6dfd..206deae16eb 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesManager.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesManager.kt @@ -77,7 +77,7 @@ class ScriptDependenciesManager internal constructor( @TestOnly fun updateScriptDependenciesSynchronously(virtualFile: VirtualFile, project: Project) { val loader = SyncScriptDependenciesLoader(project) - loader.updateDependencies(virtualFile) + loader.loadDependencies(virtualFile) loader.notifyRootsChanged() } } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesUpdater.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesUpdater.kt index 9b5dac1e1de..6982d797a71 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesUpdater.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDependenciesUpdater.kt @@ -92,7 +92,9 @@ class ScriptDependenciesUpdater( } private fun updateDependencies(file: VirtualFile) { - loaders.filter { it.isApplicable(file) }.forEach { it.updateDependencies(file) } + if (!cache.shouldRunDependenciesUpdate(file)) return + + loaders.filter { it.isApplicable(file) }.forEach { it.loadDependencies(file) } } private fun makeRootsChangeIfNeeded() { diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/dependencies/ScriptDependenciesLoader.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/dependencies/ScriptDependenciesLoader.kt index c3ed5c2a2b5..ca8dd16190f 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/dependencies/ScriptDependenciesLoader.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/dependencies/ScriptDependenciesLoader.kt @@ -13,9 +13,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ex.ProjectRootManagerEx import com.intellij.openapi.util.EmptyRunnable import com.intellij.openapi.vfs.VirtualFile -import com.intellij.util.containers.SLRUMap import org.jetbrains.kotlin.idea.core.script.* -import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesCache.Companion.MAX_SCRIPTS_CACHED import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.resolve.ScriptContentLoader @@ -26,19 +24,9 @@ import kotlin.script.experimental.dependencies.ScriptDependencies abstract class ScriptDependenciesLoader(protected val project: Project) { - fun updateDependencies(file: VirtualFile) { - if (fileModificationStamps[file.path] != file.modificationStamp) { - fileModificationStamps.put(file.path, file.modificationStamp) - - loadDependencies(file) - } - } - - private val fileModificationStamps: SLRUMap = SLRUMap(MAX_SCRIPTS_CACHED, MAX_SCRIPTS_CACHED) - abstract fun isApplicable(file: VirtualFile): Boolean + abstract fun loadDependencies(file: VirtualFile) - protected abstract fun loadDependencies(file: VirtualFile) protected abstract fun shouldShowNotification(): Boolean protected var shouldNotifyRootsChanged = false