LibraryModificationTracker

Do not track external annotations
Make use of BulkFileListener to avoid processing extra files
Defer library classes check for newly created files: fixes a problem of indices not being updated at the time
Correctly check if the file is a jar file root of a library
Ther latter 2 changes fix a problem where resolve caches were not recomputed in case of deleting and readding jar files of a library
This commit is contained in:
Pavel V. Talanov
2015-10-19 19:24:26 +03:00
parent b8b98b4eaa
commit 55006872a1
@@ -16,51 +16,69 @@
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.util.SimpleModificationTracker
import com.intellij.openapi.project.Project
import com.intellij.ide.highlighter.ArchiveFileType
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.impl.BulkVirtualFileListenerAdapter
import com.intellij.openapi.vfs.VirtualFileAdapter
import com.intellij.openapi.vfs.VirtualFileEvent
import com.intellij.openapi.vfs.VirtualFileMoveEvent
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.codeInsight.ExternalAnnotationsManager
import com.intellij.codeInsight.ExternalAnnotationsListener
import com.intellij.psi.PsiModifierListOwner
import com.intellij.openapi.util.SimpleModificationTracker
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.newvfs.BulkFileListener
import com.intellij.openapi.vfs.newvfs.events.VFileCopyEvent
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
import com.intellij.openapi.vfs.newvfs.events.VFileMoveEvent
class LibraryModificationTracker(project: Project) : SimpleModificationTracker() {
companion object {
@JvmStatic
fun getInstance(project: Project) = ServiceManager.getService(project, javaClass<LibraryModificationTracker>())!!
fun getInstance(project: Project) = ServiceManager.getService(project, LibraryModificationTracker::class.java)!!
}
init {
val connection = project.getMessageBus().connect()
connection.subscribe(VirtualFileManager.VFS_CHANGES, BulkVirtualFileListenerAdapter(
object : VirtualFileAdapter() {
override fun fileCreated(event: VirtualFileEvent) = processEvent(event)
override fun beforeFileMovement(event: VirtualFileMoveEvent) = processEvent(event)
override fun beforeContentsChange(event: VirtualFileEvent) = processEvent(event)
override fun beforeFileDeletion(event: VirtualFileEvent) = processEvent(event)
val connection = project.messageBus.connect()
connection.subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener.Adapter() {
override fun after(events: List<VFileEvent>) {
events.filter(::isRelevantEvent).let { createEvents ->
if (createEvents.isNotEmpty()) {
ApplicationManager.getApplication().invokeLater {
processBulk(createEvents) {
projectFileIndex.isInLibraryClasses(it) || isLibraryArchiveRoot(it)
}
}
}
}
))
connection.subscribe(ExternalAnnotationsManager.TOPIC, object : ExternalAnnotationsListener {
override fun afterExternalAnnotationChanging(owner: PsiModifierListOwner, annotationFQName: String, successful: Boolean) {
if (successful) incModificationCount()
}
override fun externalAnnotationsChangedExternally() = incModificationCount()
override fun before(events: List<VFileEvent>) {
processBulk(events) {
projectFileIndex.isInLibraryClasses(it)
}
}
})
}
val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)
private val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)
fun processEvent(event: VirtualFileEvent) {
if (projectFileIndex.isInLibraryClasses(event.getFile())) {
incModificationCount()
private inline fun processBulk(events: List<VFileEvent>, check: (VirtualFile) -> Boolean) {
events.forEach { event ->
val file = event.file
if (file != null && check(file)) {
incModificationCount()
return
}
}
}
// if library points to a jar, the jar does not pass isInLibraryClasses check, so we have to perform additional check for this case
private fun isLibraryArchiveRoot(virtualFile: VirtualFile): Boolean {
if (virtualFile.fileType != ArchiveFileType.INSTANCE) return false
val archiveRoot = StandardFileSystems.getJarRootForLocalFile(virtualFile) ?: return false
return projectFileIndex.isInLibraryClasses(archiveRoot)
}
}
private fun isRelevantEvent(vFileEvent: VFileEvent) = vFileEvent is VFileCreateEvent || vFileEvent is VFileMoveEvent || vFileEvent is VFileCopyEvent