FIR IDE API: add preliminary control of file in-block modifications

In this commit, we build FirFile from the beginning if in-block modifications are detected.
However, better way would be to rebuild only part of FirFile in this case
This commit is contained in:
Mikhail Glukhikh
2019-12-06 18:03:00 +03:00
parent 83ccd7dd1c
commit 718a679b72
3 changed files with 40 additions and 8 deletions
@@ -142,6 +142,7 @@ fun KtFile.getOrBuildFirWithDiagnostics(state: FirResolveState): FirFile {
}
}
ProgressIndicatorProvider.checkCanceled() // ???
if (state.hasDiagnosticsForFile(this)) return firFile
FirIdeDiagnosticsCollector(state).collectDiagnostics(firFile)
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.idea.fir
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.FirFile
@@ -75,7 +77,7 @@ class FirResolveStateImpl(override val sessionProvider: FirSessionProvider) : Fi
private val diagnosticCache = mutableMapOf<KtElement, MutableList<Diagnostic>>()
private val diagnosedFiles = mutableSetOf<KtFile>()
private val diagnosedFiles = mutableMapOf<KtFile, Long>()
override fun get(psi: KtElement): FirElement? = cache[psi]
@@ -84,7 +86,20 @@ class FirResolveStateImpl(override val sessionProvider: FirSessionProvider) : Fi
}
override fun hasDiagnosticsForFile(file: KtFile): Boolean {
return file in diagnosedFiles
val previousStamp = diagnosedFiles[file] ?: return false
if (file.modificationStamp == previousStamp) {
return true
}
diagnosedFiles.remove(file)
file.accept(object : PsiElementVisitor() {
override fun visitElement(element: PsiElement) {
cache.remove(element)
diagnosticCache.remove(element)
element.acceptChildren(this)
super.visitElement(element)
}
})
return false
}
override fun record(psi: KtElement, fir: FirElement) {
@@ -101,7 +116,7 @@ class FirResolveStateImpl(override val sessionProvider: FirSessionProvider) : Fi
(diagnostic.source.psi as? KtElement)?.let { record(it, diagnostic.diagnostic) }
}
diagnosedFiles += file
diagnosedFiles[file] = file.modificationStamp
}
}
@@ -32,8 +32,10 @@ class IdeFirProvider(
val session: FirSession
) : FirProvider() {
private val cacheProvider = FirProviderImpl(session)
// TODO: invalidation?
private val files = mutableMapOf<KtFile, FirFile>()
data class FirFileWithStamp(val file: FirFile, val stamp: Long)
private val files = mutableMapOf<KtFile, FirFileWithStamp>()
override fun getFirClassifierByFqName(classId: ClassId): FirClassLikeDeclaration<*>? {
return cacheProvider.getFirClassifierByFqName(classId) ?: run {
@@ -54,17 +56,31 @@ class IdeFirProvider(
}
fun getOrBuildFile(ktFile: KtFile): FirFile {
files[ktFile]?.let { return it }
val modificationStamp = ktFile.modificationStamp
files[ktFile]?.let { (firFile, stamp) ->
if (stamp == modificationStamp) {
return firFile
}
}
return synchronized(ktFile) {
files.getOrPut(ktFile) {
var fileWithStamp = files[ktFile]
if (fileWithStamp != null && fileWithStamp.stamp == modificationStamp) {
fileWithStamp.file
} else {
val file = RawFirBuilder(session, stubMode = false).buildFirFile(ktFile)
cacheProvider.recordFile(file)
fileWithStamp = FirFileWithStamp(file, modificationStamp)
files[ktFile] = fileWithStamp
file
}
}
}
fun getFile(ktFile: KtFile): FirFile? = files[ktFile]
fun getFile(ktFile: KtFile): FirFile? {
val (firFile, stamp) = files[ktFile] ?: return null
if (stamp == ktFile.modificationStamp) return firFile
return null
}
override fun getClassLikeSymbolByFqName(classId: ClassId): FirClassLikeSymbol<*>? {
return getFirClassifierByFqName(classId)?.symbol