[LL API] Invalidate code fragment sessions on PSI modifications

Code fragments depend not only on their context module, but also on
body of the context declaration.
This commit is contained in:
Yan Zhulanow
2023-09-20 16:56:32 +09:00
committed by Space Team
parent 1293eb0fab
commit 36df749697
3 changed files with 30 additions and 3 deletions
@@ -35,6 +35,7 @@ class LLFirSessionCache(private val project: Project) {
private val sourceCache: SessionStorage = CollectionFactory.createConcurrentSoftValueMap() private val sourceCache: SessionStorage = CollectionFactory.createConcurrentSoftValueMap()
private val binaryCache: SessionStorage = CollectionFactory.createConcurrentSoftValueMap() private val binaryCache: SessionStorage = CollectionFactory.createConcurrentSoftValueMap()
private val codeFragmentSessionCache: SessionStorage = CollectionFactory.createConcurrentSoftValueMap()
/** /**
* Returns the existing session if found, or creates a new session and caches it. * Returns the existing session if found, or creates a new session and caches it.
@@ -47,7 +48,12 @@ class LLFirSessionCache(private val project: Project) {
} }
} }
return getCachedSession(module, sourceCache, ::createSession) val targetCache = when (module) {
is KtCodeFragmentModule -> codeFragmentSessionCache
else -> sourceCache
}
return getCachedSession(module, targetCache, ::createSession)
} }
/** /**
@@ -79,9 +85,10 @@ class LLFirSessionCache(private val project: Project) {
ApplicationManager.getApplication().assertWriteAccessAllowed() ApplicationManager.getApplication().assertWriteAccessAllowed()
val didSourceSessionExist = removeSessionFrom(module, sourceCache) val didSourceSessionExist = removeSessionFrom(module, sourceCache)
val didBinarySessionExist = if (module is KtBinaryModule) removeSessionFrom(module, binaryCache) else false val didBinarySessionExist = module is KtBinaryModule && removeSessionFrom(module, binaryCache)
val didCodeFragmentSessionExist = module is KtCodeFragmentModule && removeSessionFrom(module, codeFragmentSessionCache)
return didSourceSessionExist || didBinarySessionExist return didSourceSessionExist || didBinarySessionExist || didCodeFragmentSessionExist
} }
private fun removeSessionFrom(module: KtModule, storage: SessionStorage): Boolean { private fun removeSessionFrom(module: KtModule, storage: SessionStorage): Boolean {
@@ -106,6 +113,12 @@ class LLFirSessionCache(private val project: Project) {
// `binaryCache` can only contain library modules, so we only need to remove sessions from `sourceCache`. // `binaryCache` can only contain library modules, so we only need to remove sessions from `sourceCache`.
removeAllMatchingSessionsFrom(sourceCache) { it !is KtBinaryModule && it !is KtLibrarySourceModule } removeAllMatchingSessionsFrom(sourceCache) { it !is KtBinaryModule && it !is KtLibrarySourceModule }
} }
removeAllCodeFragmentSessions()
}
fun removeAllCodeFragmentSessions() {
removeAllSessionsFrom(codeFragmentSessionCache)
} }
// Removing script sessions is only needed temporarily until KTIJ-25620 has been implemented. // Removing script sessions is only needed temporarily until KTIJ-25620 has been implemented.
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.sessions
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.util.PsiModificationTracker
import org.jetbrains.kotlin.analysis.project.structure.* import org.jetbrains.kotlin.analysis.project.structure.*
import org.jetbrains.kotlin.analysis.providers.KotlinAnchorModuleProvider import org.jetbrains.kotlin.analysis.providers.KotlinAnchorModuleProvider
import org.jetbrains.kotlin.analysis.providers.analysisMessageBus import org.jetbrains.kotlin.analysis.providers.analysisMessageBus
@@ -51,6 +52,10 @@ class LLFirSessionInvalidationService(private val project: Project) : Disposable
KotlinTopics.GLOBAL_SOURCE_OUT_OF_BLOCK_MODIFICATION, KotlinTopics.GLOBAL_SOURCE_OUT_OF_BLOCK_MODIFICATION,
KotlinGlobalSourceOutOfBlockModificationListener { invalidateAll(includeLibraryModules = false) }, KotlinGlobalSourceOutOfBlockModificationListener { invalidateAll(includeLibraryModules = false) },
) )
busConnection.subscribe(
PsiModificationTracker.TOPIC,
PsiModificationTracker.Listener { invalidateAllCodeFragments() }
)
} }
/** /**
@@ -79,6 +84,8 @@ class LLFirSessionInvalidationService(private val project: Project) : Disposable
if (module is KtScriptModule || module is KtScriptDependencyModule || module is KtLibraryModule) { if (module is KtScriptModule || module is KtScriptDependencyModule || module is KtLibraryModule) {
sessionCache.removeAllScriptSessions() sessionCache.removeAllScriptSessions()
} }
sessionCache.removeAllCodeFragmentSessions()
} }
private fun invalidateAll(includeLibraryModules: Boolean) { private fun invalidateAll(includeLibraryModules: Boolean) {
@@ -98,6 +105,10 @@ class LLFirSessionInvalidationService(private val project: Project) : Disposable
LLFirSessionCache.getInstance(project).removeAllSessions(includeLibraryModules) LLFirSessionCache.getInstance(project).removeAllSessions(includeLibraryModules)
} }
private fun invalidateAllCodeFragments() {
LLFirSessionCache.getInstance(project).removeAllCodeFragmentSessions()
}
override fun dispose() { override fun dispose() {
} }
@@ -15,6 +15,9 @@ import com.intellij.openapi.project.Project
* should keep this limitation in mind and handle it separately. For example, a global modification event should be published for builtins * should keep this limitation in mind and handle it separately. For example, a global modification event should be published for builtins
* and SDK changes. * and SDK changes.
* *
* An empty set is also returned for [KtCodeFragmentModule]s, as no other module can depend on a code fragment.
* Additionally, [KtCodeFragmentModule] are not returned from [getDirectDependents] for their context modules.
*
* Implementations of this provider should ensure that results are provided in reasonable time, for example by caching results, as its * Implementations of this provider should ensure that results are provided in reasonable time, for example by caching results, as its
* functions may be called frequently. * functions may be called frequently.
*/ */