Optimize and simplify script deps cache and appropriate index rebuilding
This commit is contained in:
+38
-55
@@ -28,8 +28,7 @@ import kotlin.concurrent.write
|
|||||||
class KotlinScriptExternalImportsProvider(val project: Project, private val scriptDefinitionProvider: KotlinScriptDefinitionProvider) {
|
class KotlinScriptExternalImportsProvider(val project: Project, private val scriptDefinitionProvider: KotlinScriptDefinitionProvider) {
|
||||||
|
|
||||||
private val cacheLock = ReentrantReadWriteLock()
|
private val cacheLock = ReentrantReadWriteLock()
|
||||||
private val cache = hashMapOf<String, KotlinScriptExternalDependencies>()
|
private val cache = hashMapOf<String, KotlinScriptExternalDependencies?>()
|
||||||
private val cacheOfNulls = hashSetOf<String>()
|
|
||||||
|
|
||||||
fun <TF: Any> getExternalImports(file: TF): KotlinScriptExternalDependencies? = cacheLock.read { calculateExternalDependencies(file) }
|
fun <TF: Any> getExternalImports(file: TF): KotlinScriptExternalDependencies? = cacheLock.read { calculateExternalDependencies(file) }
|
||||||
|
|
||||||
@@ -39,58 +38,62 @@ class KotlinScriptExternalImportsProvider(val project: Project, private val scri
|
|||||||
|
|
||||||
private fun <TF: Any> calculateExternalDependencies(file: TF): KotlinScriptExternalDependencies? {
|
private fun <TF: Any> calculateExternalDependencies(file: TF): KotlinScriptExternalDependencies? {
|
||||||
val path = getFilePath(file)
|
val path = getFilePath(file)
|
||||||
return cache[path]
|
val cached = cache[path]
|
||||||
?: if (cacheOfNulls.contains(path)) null
|
return if (cached != null) cached else {
|
||||||
else scriptDefinitionProvider.findScriptDefinition(file)?.getDependenciesFor(file, project, null)
|
val scriptDef = scriptDefinitionProvider.findScriptDefinition(file)
|
||||||
.apply {
|
if (scriptDef != null) {
|
||||||
if (this != null) {
|
val deps = scriptDef.getDependenciesFor(file, project, null)
|
||||||
log.info("[kts] new cached deps for $path: ${this.classpath.joinToString(File.pathSeparator)}")
|
if (deps != null) {
|
||||||
}
|
log.info("[kts] new cached deps for $path: ${deps.classpath.joinToString(File.pathSeparator)}")
|
||||||
cacheLock.write {
|
}
|
||||||
if (this == null) {
|
cacheLock.write {
|
||||||
cacheOfNulls.add(path)
|
cache.put(path, deps)
|
||||||
}
|
}
|
||||||
else {
|
deps
|
||||||
cache.put(path, this)
|
}
|
||||||
}
|
else null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// optimized for initial caching, additional handling of possible duplicates to save a call to distinct
|
// optimized for initial caching, additional handling of possible duplicates to save a call to distinct
|
||||||
fun <TF: Any> cacheExternalImports(files: Iterable<TF>): Unit = cacheLock.write {
|
// returns list of cached files
|
||||||
|
fun <TF: Any> cacheExternalImports(files: Iterable<TF>): Iterable<TF> = cacheLock.write {
|
||||||
val uncached = hashSetOf<String>()
|
val uncached = hashSetOf<String>()
|
||||||
files.forEach { file ->
|
files.mapNotNull { file ->
|
||||||
val path = getFilePath(file)
|
val path = getFilePath(file)
|
||||||
if (isValidFile(file) && !cache.containsKey(path) && !cacheOfNulls.contains(path) && !uncached.contains(path)) {
|
if (isValidFile(file) && !cache.containsKey(path) && !uncached.contains(path)) {
|
||||||
val scriptDef = scriptDefinitionProvider.findScriptDefinition(file)
|
val scriptDef = scriptDefinitionProvider.findScriptDefinition(file)
|
||||||
if (scriptDef != null) {
|
if (scriptDef != null) {
|
||||||
val deps = scriptDef.getDependenciesFor(file, project, null)
|
val deps = scriptDef.getDependenciesFor(file, project, null)
|
||||||
log.info("[kts] cached deps for $path: ${deps?.classpath?.joinToString(File.pathSeparator)}")
|
|
||||||
if (deps != null) {
|
if (deps != null) {
|
||||||
cache.put(path, deps)
|
log.info("[kts] cached deps for $path: ${deps.classpath.joinToString(File.pathSeparator)}")
|
||||||
}
|
|
||||||
else {
|
|
||||||
cacheOfNulls.add(path)
|
|
||||||
}
|
}
|
||||||
|
cache.put(path, deps)
|
||||||
|
file
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
uncached.add(path)
|
uncached.add(path)
|
||||||
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// optimized for update, no special duplicates handling
|
// optimized for update, no special duplicates handling
|
||||||
|
// returns files with valid script definition (or deleted from cache - which in fact should have script def too)
|
||||||
|
// TODO: this is the badly designed contract, since it mixes the entities, but these files are needed on the calling site now. Find out other solution
|
||||||
fun <TF: Any> updateExternalImportsCache(files: Iterable<TF>): Iterable<TF> = cacheLock.write {
|
fun <TF: Any> updateExternalImportsCache(files: Iterable<TF>): Iterable<TF> = cacheLock.write {
|
||||||
files.mapNotNull { file ->
|
files.mapNotNull { file ->
|
||||||
val path = getFilePath(file)
|
val path = getFilePath(file)
|
||||||
if (!isValidFile(file)) {
|
if (!isValidFile(file)) {
|
||||||
if (cache.remove(path) != null || cacheOfNulls.remove(path)) {
|
if (cache.remove(path) != null) {
|
||||||
log.info("[kts] removed deps for invalid file $path")
|
log.debug("[kts] removed deps for file $path")
|
||||||
file
|
file
|
||||||
} // cleared
|
} // cleared
|
||||||
else null // unknown
|
else {
|
||||||
|
null // unknown
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val scriptDef = scriptDefinitionProvider.findScriptDefinition(file)
|
val scriptDef = scriptDefinitionProvider.findScriptDefinition(file)
|
||||||
@@ -103,22 +106,17 @@ class KotlinScriptExternalImportsProvider(val project: Project, private val scri
|
|||||||
// changed or new
|
// changed or new
|
||||||
log.info("[kts] updated/new cached deps for $path: ${deps.classpath.joinToString(File.pathSeparator)}")
|
log.info("[kts] updated/new cached deps for $path: ${deps.classpath.joinToString(File.pathSeparator)}")
|
||||||
cache.put(path, deps)
|
cache.put(path, deps)
|
||||||
cacheOfNulls.remove(path)
|
|
||||||
file
|
|
||||||
}
|
}
|
||||||
deps != null -> {
|
deps != null -> {
|
||||||
// same as before
|
// same as before
|
||||||
log.info("[kts] unchanged deps for $path")
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
if (cache.remove(path) != null || cacheOfNulls.remove(path)) {
|
if (cache.remove(path) != null) {
|
||||||
log.info("[kts] removed deps for $path")
|
log.debug("[kts] removed deps for $path")
|
||||||
file
|
|
||||||
} // cleared
|
} // cleared
|
||||||
else null // same as before
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
file
|
||||||
}
|
}
|
||||||
else null // not a script
|
else null // not a script
|
||||||
}
|
}
|
||||||
@@ -126,30 +124,15 @@ class KotlinScriptExternalImportsProvider(val project: Project, private val scri
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun invalidateCaches() {
|
fun invalidateCaches() {
|
||||||
cacheLock.write {
|
cacheLock.write(cache::clear)
|
||||||
cache.clear()
|
|
||||||
cacheOfNulls.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <TF: Any> invalidateCachesFor(vararg files: TF) { invalidateCachesFor(files.asIterable()) }
|
|
||||||
|
|
||||||
fun <TF: Any> invalidateCachesFor(files: Iterable<TF>) {
|
|
||||||
cacheLock.write {
|
|
||||||
files.forEach { file ->
|
|
||||||
val path = getFilePath(file)
|
|
||||||
cache.remove(path)
|
|
||||||
cacheOfNulls.remove(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getKnownCombinedClasspath(): List<File> = cacheLock.read {
|
fun getKnownCombinedClasspath(): List<File> = cacheLock.read {
|
||||||
cache.values.flatMap { it.classpath }
|
cache.values.flatMap { it?.classpath ?: emptyList() }
|
||||||
}.distinct()
|
}.distinct()
|
||||||
|
|
||||||
fun getKnownSourceRoots(): List<File> = cacheLock.read {
|
fun getKnownSourceRoots(): List<File> = cacheLock.read {
|
||||||
cache.values.flatMap { it.sources }
|
cache.values.flatMap { it?.sources ?: emptyList() }
|
||||||
}.distinct()
|
}.distinct()
|
||||||
|
|
||||||
fun <TF: Any> getCombinedClasspathFor(files: Iterable<TF>): List<File> =
|
fun <TF: Any> getCombinedClasspathFor(files: Iterable<TF>): List<File> =
|
||||||
|
|||||||
+18
-27
@@ -50,7 +50,6 @@ import kotlin.concurrent.write
|
|||||||
@Suppress("SimplifyAssertNotNull")
|
@Suppress("SimplifyAssertNotNull")
|
||||||
class KotlinScriptConfigurationManager(
|
class KotlinScriptConfigurationManager(
|
||||||
private val project: Project,
|
private val project: Project,
|
||||||
private val dumbService: DumbService,
|
|
||||||
private val scriptDefinitionProvider: KotlinScriptDefinitionProvider,
|
private val scriptDefinitionProvider: KotlinScriptDefinitionProvider,
|
||||||
private val scriptExternalImportsProvider: KotlinScriptExternalImportsProvider
|
private val scriptExternalImportsProvider: KotlinScriptExternalImportsProvider
|
||||||
) {
|
) {
|
||||||
@@ -60,15 +59,16 @@ class KotlinScriptConfigurationManager(
|
|||||||
|
|
||||||
StartupManager.getInstance(project).runWhenProjectIsInitialized {
|
StartupManager.getInstance(project).runWhenProjectIsInitialized {
|
||||||
DumbService.getInstance(project).smartInvokeLater {
|
DumbService.getInstance(project).smartInvokeLater {
|
||||||
cacheAllScriptsExtraImports()
|
if (cacheAllScriptsExtraImports()) {
|
||||||
invalidateLocalCaches()
|
invalidateLocalCaches()
|
||||||
notifyRootsChanged()
|
notifyRootsChanged()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
project.messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener.Adapter() {
|
project.messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener.Adapter() {
|
||||||
override fun after(events: List<VFileEvent>) {
|
override fun after(events: List<VFileEvent>) {
|
||||||
updateExternalImportsCache(events.mapNotNull { it.file }) {
|
if (updateExternalImportsCache(events.mapNotNull { it.file })) {
|
||||||
invalidateLocalCaches()
|
invalidateLocalCaches()
|
||||||
notifyRootsChanged()
|
notifyRootsChanged()
|
||||||
}
|
}
|
||||||
@@ -100,9 +100,7 @@ class KotlinScriptConfigurationManager(
|
|||||||
if (project.isDisposed) return@runWriteAction
|
if (project.isDisposed) return@runWriteAction
|
||||||
|
|
||||||
ProjectRootManagerEx.getInstanceEx(project)?.makeRootsChange(EmptyRunnable.getInstance(), false, true)
|
ProjectRootManagerEx.getInstanceEx(project)?.makeRootsChange(EmptyRunnable.getInstance(), false, true)
|
||||||
dumbService.runWhenSmart {
|
ScriptDependenciesModificationTracker.getInstance(project).incModificationCount()
|
||||||
ScriptDependenciesModificationTracker.getInstance(project).incModificationCount()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,28 +126,21 @@ class KotlinScriptConfigurationManager(
|
|||||||
fun getAllLibrarySourcesScope() = allLibrarySourcesScope.get()
|
fun getAllLibrarySourcesScope() = allLibrarySourcesScope.get()
|
||||||
|
|
||||||
private fun reloadScriptDefinitions() {
|
private fun reloadScriptDefinitions() {
|
||||||
makeScriptDefsFromTemplatesProviderExtensions(project, { ep, ex -> log.warn("[kts] Error loading definition from ${ep.id}", ex) }).let {
|
val def = makeScriptDefsFromTemplatesProviderExtensions(project, { ep, ex -> log.warn("[kts] Error loading definition from ${ep.id}", ex) })
|
||||||
scriptDefinitionProvider.setScriptDefinitions(it)
|
scriptDefinitionProvider.setScriptDefinitions(def)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cacheAllScriptsExtraImports(): Boolean = runReadAction {
|
||||||
|
scriptExternalImportsProvider.run {
|
||||||
|
invalidateCaches()
|
||||||
|
cacheExternalImports(
|
||||||
|
scriptDefinitionProvider.getAllKnownFileTypes()
|
||||||
|
.flatMap { FileTypeIndex.getFiles(it, GlobalSearchScope.allScope(project)) }
|
||||||
|
).any()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cacheAllScriptsExtraImports() {
|
private fun updateExternalImportsCache(files: Iterable<VirtualFile>) = scriptExternalImportsProvider.updateExternalImportsCache(files).any()
|
||||||
runReadAction {
|
|
||||||
scriptExternalImportsProvider.apply {
|
|
||||||
invalidateCaches()
|
|
||||||
cacheExternalImports(
|
|
||||||
scriptDefinitionProvider.getAllKnownFileTypes()
|
|
||||||
.flatMap { FileTypeIndex.getFiles(it, GlobalSearchScope.allScope(project)) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateExternalImportsCache(files: Iterable<VirtualFile>, onChange: () -> Unit) {
|
|
||||||
val isChanged = scriptExternalImportsProvider.updateExternalImportsCache(files).any()
|
|
||||||
if (isChanged) {
|
|
||||||
onChange()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun invalidateLocalCaches() {
|
private fun invalidateLocalCaches() {
|
||||||
allScriptsClasspathCache.clear()
|
allScriptsClasspathCache.clear()
|
||||||
|
|||||||
Reference in New Issue
Block a user