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 package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.util.SimpleModificationTracker import com.intellij.ide.highlighter.ArchiveFileType
import com.intellij.openapi.project.Project import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.project.Project
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.roots.ProjectFileIndex import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.codeInsight.ExternalAnnotationsManager import com.intellij.openapi.util.SimpleModificationTracker
import com.intellij.codeInsight.ExternalAnnotationsListener import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.psi.PsiModifierListOwner 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() { class LibraryModificationTracker(project: Project) : SimpleModificationTracker() {
companion object { companion object {
@JvmStatic @JvmStatic
fun getInstance(project: Project) = ServiceManager.getService(project, javaClass<LibraryModificationTracker>())!! fun getInstance(project: Project) = ServiceManager.getService(project, LibraryModificationTracker::class.java)!!
} }
init { init {
val connection = project.getMessageBus().connect() val connection = project.messageBus.connect()
connection.subscribe(VirtualFileManager.VFS_CHANGES, BulkVirtualFileListenerAdapter( connection.subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener.Adapter() {
object : VirtualFileAdapter() { override fun after(events: List<VFileEvent>) {
override fun fileCreated(event: VirtualFileEvent) = processEvent(event) events.filter(::isRelevantEvent).let { createEvents ->
override fun beforeFileMovement(event: VirtualFileMoveEvent) = processEvent(event) if (createEvents.isNotEmpty()) {
override fun beforeContentsChange(event: VirtualFileEvent) = processEvent(event) ApplicationManager.getApplication().invokeLater {
override fun beforeFileDeletion(event: VirtualFileEvent) = processEvent(event) 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) { private inline fun processBulk(events: List<VFileEvent>, check: (VirtualFile) -> Boolean) {
if (projectFileIndex.isInLibraryClasses(event.getFile())) { events.forEach { event ->
incModificationCount() 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