Optimize AndroidPsiTreeChangePreprocessor counter updating

This commit is contained in:
Yan Zhulanow
2015-08-21 02:40:11 +03:00
parent ddd4c2ae7f
commit f3462513e2
@@ -21,55 +21,91 @@ import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.util.SimpleModificationTracker
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.PsiTreeChangeEventImpl
import com.intellij.psi.impl.PsiTreeChangePreprocessor
import com.intellij.psi.xml.XmlAttribute
import com.intellij.psi.xml.XmlAttributeValue
import com.intellij.psi.xml.XmlFile
import com.intellij.psi.xml.XmlToken
import org.jetbrains.kotlin.android.synthetic.res.AndroidLayoutXmlFileManager
public class AndroidPsiTreeChangePreprocessor : PsiTreeChangePreprocessor, SimpleModificationTracker() {
companion object {
override fun treeChanged(event: PsiTreeChangeEventImpl) {
if (event.code in HANDLED_EVENTS) {
val child = event.child
// We should get more precise event notification (not just "that file was changed somehow")
if (child == null && event.code == PsiTreeChangeEventImpl.PsiEventType.CHILDREN_CHANGED) {
return
}
if (checkIfLayoutFile(child)) {
incModificationCount()
return
}
val file = event.file ?: return
if (!checkIfLayoutFile(file)) return
val xmlAttribute = findXmlAttribute(child)
if (xmlAttribute != null) {
val name = xmlAttribute.name
if (name != "android:id" && name != "class") return
}
incModificationCount()
}
}
private companion object {
private val HANDLED_EVENTS = setOf(
PsiTreeChangeEventImpl.PsiEventType.CHILD_ADDED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_MOVED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_REMOVED,
PsiTreeChangeEventImpl.PsiEventType.CHILD_REPLACED,
PsiTreeChangeEventImpl.PsiEventType.CHILDREN_CHANGED)
}
override fun treeChanged(event: PsiTreeChangeEventImpl) {
if (event.getCode() in HANDLED_EVENTS) {
val file = event.getFile() ?: (event.getChild() as? XmlFile)
if (file != null) {
val project = file.getProject()
private fun checkIfLayoutFile(element: PsiElement): Boolean {
val xmlFile = element as? XmlFile ?: return false
val projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex()
val module = projectFileIndex.getModuleForFile(file.getVirtualFile())
if (module != null) {
val resourceManager = AndroidLayoutXmlFileManager.getInstance(module)
val resDirectories = resourceManager.getModuleResDirectories()
val baseDirectory = file.getParent()?.getParent()?.getVirtualFile()
val projectFileIndex = ProjectRootManager.getInstance(xmlFile.project).fileIndex
val module = projectFileIndex.getModuleForFile(xmlFile.virtualFile)
// File from the res/ directory was modified
if (baseDirectory in resDirectories && file.isLayoutXmlFile()) {
incModificationCount()
}
if (module != null) {
val resourceManager = AndroidLayoutXmlFileManager.getInstance(module)
val resDirectories = resourceManager.getModuleResDirectories()
val baseDirectory = xmlFile.parent?.parent?.virtualFile
if (baseDirectory in resDirectories && xmlFile.isLayoutXmlFile()) {
return true
}
}
return false
}
private fun findXmlAttribute(element: PsiElement?): XmlAttribute? {
return when (element) {
is XmlToken, is XmlAttributeValue -> findXmlAttribute(element.parent)
is XmlAttribute -> element
else -> null
}
}
private fun AndroidLayoutXmlFileManager.getModuleResDirectories(): List<VirtualFile> {
val info = androidModuleInfo ?: return listOf()
val fileManager = VirtualFileManager.getInstance()
return info.resDirectories.map { fileManager.findFileByUrl("file://$it") }.filterNotNull()
}
private fun PsiFile.isLayoutXmlFile(): Boolean {
if (fileType != XmlFileType.INSTANCE) return false
return parent?.name?.startsWith("layout") ?: false
}
}
private fun AndroidLayoutXmlFileManager.getModuleResDirectories(): List<VirtualFile> {
val info = androidModuleInfo ?: return listOf()
val fileManager = VirtualFileManager.getInstance()
return info.resDirectories.map { fileManager.findFileByUrl("file://" + it)!! }
}
private fun PsiFile.isLayoutXmlFile(): Boolean {
if (getFileType() != XmlFileType.INSTANCE) return false
return getParent()?.getName()?.startsWith("layout") ?: false
}
}