ScriptDependenciesCache: limit simultaneously cached script count
Do not schedule updates for dependencies of scripts that were not requested (opened in the editor or such) for some time This fixes a problem when vcs updates changing scripts can trigger undesired script dependencies updates (probable cause of script files never highlighted correctly in some cases) Refactor: use VirtualFile instead of String and move code from GradleScriptTemplateProvider
This commit is contained in:
+34
-10
@@ -24,8 +24,10 @@ import com.intellij.openapi.vfs.VirtualFile
|
|||||||
import com.intellij.psi.PsiElementFinder
|
import com.intellij.psi.PsiElementFinder
|
||||||
import com.intellij.psi.PsiManager
|
import com.intellij.psi.PsiManager
|
||||||
import com.intellij.psi.search.NonClasspathDirectoriesScope
|
import com.intellij.psi.search.NonClasspathDirectoriesScope
|
||||||
|
import com.intellij.util.containers.SLRUMap
|
||||||
import kotlinx.coroutines.experimental.launch
|
import kotlinx.coroutines.experimental.launch
|
||||||
import org.jetbrains.kotlin.idea.core.util.EDT
|
import org.jetbrains.kotlin.idea.core.util.EDT
|
||||||
|
import java.io.File
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
import kotlin.concurrent.read
|
import kotlin.concurrent.read
|
||||||
import kotlin.concurrent.write
|
import kotlin.concurrent.write
|
||||||
@@ -35,14 +37,16 @@ import kotlin.reflect.KProperty0
|
|||||||
import kotlin.reflect.jvm.isAccessible
|
import kotlin.reflect.jvm.isAccessible
|
||||||
import kotlin.script.experimental.dependencies.ScriptDependencies
|
import kotlin.script.experimental.dependencies.ScriptDependencies
|
||||||
|
|
||||||
|
private const val MAX_SCRIPTS_CACHED = 15
|
||||||
|
|
||||||
class ScriptDependenciesCache(private val project: Project) {
|
class ScriptDependenciesCache(private val project: Project) {
|
||||||
private val cacheLock = ReentrantReadWriteLock()
|
private val cacheLock = ReentrantReadWriteLock()
|
||||||
private val cache = hashMapOf<String, ScriptDependencies>()
|
private val cache = SLRUMap<VirtualFile, ScriptDependencies>(0, MAX_SCRIPTS_CACHED)
|
||||||
|
|
||||||
operator fun get(virtualFile: VirtualFile): ScriptDependencies? = cacheLock.read { cache[virtualFile.path] }
|
operator fun get(virtualFile: VirtualFile): ScriptDependencies? = cacheLock.read { cache[virtualFile] }
|
||||||
|
|
||||||
val allScriptsClasspath by ClearableLazyValue(cacheLock) {
|
val allScriptsClasspath by ClearableLazyValue(cacheLock) {
|
||||||
val files = cache.values.flatMap { it.classpath }.distinct()
|
val files = cache.entrySet().flatMap { it.value.classpath }.distinct()
|
||||||
ScriptDependenciesManager.toVfsRoots(files)
|
ScriptDependenciesManager.toVfsRoots(files)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,15 +55,13 @@ class ScriptDependenciesCache(private val project: Project) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val allLibrarySources by ClearableLazyValue(cacheLock) {
|
val allLibrarySources by ClearableLazyValue(cacheLock) {
|
||||||
ScriptDependenciesManager.toVfsRoots(cache.values.flatMap { it.sources }.distinct())
|
ScriptDependenciesManager.toVfsRoots(cache.entrySet().flatMap { it.value.sources }.distinct())
|
||||||
}
|
}
|
||||||
|
|
||||||
val allLibrarySourcesScope by ClearableLazyValue(cacheLock) {
|
val allLibrarySourcesScope by ClearableLazyValue(cacheLock) {
|
||||||
NonClasspathDirectoriesScope(allLibrarySources)
|
NonClasspathDirectoriesScope(allLibrarySources)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> inspectCache(body: (Map<String, ScriptDependencies>) -> T): T = cacheLock.read { body(cache) }
|
|
||||||
|
|
||||||
private fun onChange(file: VirtualFile?) {
|
private fun onChange(file: VirtualFile?) {
|
||||||
this::allScriptsClasspath.clearValue()
|
this::allScriptsClasspath.clearValue()
|
||||||
this::allScriptsClasspathScope.clearValue()
|
this::allScriptsClasspathScope.clearValue()
|
||||||
@@ -102,10 +104,9 @@ class ScriptDependenciesCache(private val project: Project) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun save(virtualFile: VirtualFile, new: ScriptDependencies): Boolean {
|
fun save(virtualFile: VirtualFile, new: ScriptDependencies): Boolean {
|
||||||
val path = virtualFile.path
|
|
||||||
val old = cacheLock.write {
|
val old = cacheLock.write {
|
||||||
val old = cache[path]
|
val old = cache[virtualFile]
|
||||||
cache[path] = new
|
cache.put(virtualFile, new)
|
||||||
old
|
old
|
||||||
}
|
}
|
||||||
val changed = new != old
|
val changed = new != old
|
||||||
@@ -118,13 +119,36 @@ class ScriptDependenciesCache(private val project: Project) {
|
|||||||
|
|
||||||
fun delete(virtualFile: VirtualFile): Boolean {
|
fun delete(virtualFile: VirtualFile): Boolean {
|
||||||
val changed = cacheLock.write {
|
val changed = cacheLock.write {
|
||||||
cache.remove(virtualFile.path) != null
|
cache.remove(virtualFile) != null
|
||||||
}
|
}
|
||||||
if (changed) {
|
if (changed) {
|
||||||
onChange(virtualFile)
|
onChange(virtualFile)
|
||||||
}
|
}
|
||||||
return changed
|
return changed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun combineDependencies(filePredicate: (VirtualFile) -> Boolean): ScriptDependencies = cacheLock.read {
|
||||||
|
val sources = mutableListOf<File>()
|
||||||
|
val binaries = mutableListOf<File>()
|
||||||
|
val imports = mutableListOf<String>()
|
||||||
|
val scripts = mutableListOf<File>()
|
||||||
|
|
||||||
|
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 <R> KProperty0<R>.clearValue() {
|
private fun <R> KProperty0<R>.clearValue() {
|
||||||
|
|||||||
+8
-5
@@ -84,7 +84,7 @@ internal class ScriptDependenciesUpdater(
|
|||||||
tryUsingDefault(file)
|
tryUsingDefault(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCache(listOf(file))
|
performUpdate(file)
|
||||||
|
|
||||||
return cache[file] ?: ScriptDependencies.Empty
|
return cache[file] ?: ScriptDependencies.Empty
|
||||||
}
|
}
|
||||||
@@ -108,17 +108,20 @@ internal class ScriptDependenciesUpdater(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCache(files: Iterable<VirtualFile>) =
|
private fun requestUpdate(files: Iterable<VirtualFile>) =
|
||||||
files.map { file ->
|
files.map { file ->
|
||||||
if (!file.isValid) {
|
if (!file.isValid) {
|
||||||
return cache.delete(file)
|
return cache.delete(file)
|
||||||
}
|
}
|
||||||
|
else if (cache[file] != null) { // only update dependencies for scripts that were touched recently
|
||||||
|
performUpdate(file)
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
updateForFile(file)
|
false
|
||||||
}
|
}
|
||||||
}.contains(true)
|
}.contains(true)
|
||||||
|
|
||||||
private fun updateForFile(file: VirtualFile): Boolean {
|
private fun performUpdate(file: VirtualFile): Boolean {
|
||||||
val scriptDef = scriptDefinitionProvider.findScriptDefinition(file) ?: return false
|
val scriptDef = scriptDefinitionProvider.findScriptDefinition(file) ?: return false
|
||||||
|
|
||||||
return when (scriptDef.dependencyResolver) {
|
return when (scriptDef.dependencyResolver) {
|
||||||
@@ -263,7 +266,7 @@ internal class ScriptDependenciesUpdater(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateCache(events.mapNotNull {
|
if (requestUpdate(events.mapNotNull {
|
||||||
// The check is partly taken from the BuildManager.java
|
// The check is partly taken from the BuildManager.java
|
||||||
it.file?.takeIf {
|
it.file?.takeIf {
|
||||||
// the isUnitTestMode check fixes ScriptConfigurationHighlighting & Navigation tests, since they are not trigger proper update mechanims
|
// the isUnitTestMode check fixes ScriptConfigurationHighlighting & Navigation tests, since they are not trigger proper update mechanims
|
||||||
|
|||||||
+2
-19
@@ -181,23 +181,6 @@ class GradleScriptDefaultDependenciesProvider(
|
|||||||
return previouslyAnalyzedScriptsCombinedDependencies().takeUnless { it.classpath.isEmpty() }
|
return previouslyAnalyzedScriptsCombinedDependencies().takeUnless { it.classpath.isEmpty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun previouslyAnalyzedScriptsCombinedDependencies(): ScriptDependencies {
|
private fun previouslyAnalyzedScriptsCombinedDependencies() =
|
||||||
val sources = mutableListOf<File>()
|
scriptDependenciesCache.combineDependencies { it.name == KOTLIN_BUILD_FILE_NAME }
|
||||||
val binaries = mutableListOf<File>()
|
|
||||||
val imports = mutableListOf<String>()
|
|
||||||
|
|
||||||
scriptDependenciesCache.inspectCache { cache ->
|
|
||||||
cache.entries.filter { it.key.endsWith(KOTLIN_BUILD_FILE_NAME) }
|
|
||||||
.map { it.value }.forEach {
|
|
||||||
sources += it.sources
|
|
||||||
binaries += it.classpath
|
|
||||||
imports += it.imports
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ScriptDependencies(
|
|
||||||
classpath = binaries.distinct(),
|
|
||||||
sources = sources.distinct(),
|
|
||||||
imports = imports.distinct()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user