Add cache for top-level classes' virtual files
The reason is that searching for a class in a VirtualFile-based directory (i.e. in a package) happens for O(|amount-of-files-in-dir|), and it may be rather crucial for packages with a huge amount of classes Note that it was checked that the cache should not take a lot of heap since we call findClass only for really existing classes: see org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaPackageScope.kt:48 For example, there are no more than 10k classes for the whole intelliJ project (but the cache is flushed for each module)
This commit is contained in:
+27
-13
@@ -23,6 +23,7 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.file.PsiPackageImpl
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import gnu.trove.THashMap
|
||||
import org.jetbrains.kotlin.cli.jvm.index.JavaRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
@@ -38,17 +39,23 @@ import kotlin.properties.Delegates
|
||||
class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJavaFileManager(myPsiManager), KotlinCliJavaFileManager {
|
||||
private val perfCounter = PerformanceCounter.create("Find Java class")
|
||||
private var index: JvmDependenciesIndex by Delegates.notNull()
|
||||
private val allScope = GlobalSearchScope.allScope(myPsiManager.project)
|
||||
private val topLevelClassesCache: MutableMap<FqName, VirtualFile?> = THashMap()
|
||||
|
||||
fun initIndex(packagesCache: JvmDependenciesIndex) {
|
||||
this.index = packagesCache
|
||||
}
|
||||
|
||||
override fun findClass(classId: ClassId, searchScope: GlobalSearchScope): PsiClass? = perfCounter.time {
|
||||
findVirtualFileForTopLevelClass(classId, searchScope)?.findPsiClassInVirtualFile(classId.relativeClassName.asString())
|
||||
}
|
||||
|
||||
private fun findVirtualFileForTopLevelClass(classId: ClassId, searchScope: GlobalSearchScope): VirtualFile? {
|
||||
val relativeClassName = classId.relativeClassName.asString()
|
||||
index.findClass(classId) { dir, type ->
|
||||
findClassGivenPackage(allScope, dir, relativeClassName, type)
|
||||
}?.takeIf { it.containingFile.virtualFile in searchScope }
|
||||
return topLevelClassesCache.getOrPut(classId.packageFqName.child(classId.relativeClassName.pathSegments().first())) {
|
||||
index.findClass(classId) { dir, type ->
|
||||
findVirtualFileGivenPackage(dir, relativeClassName, type)
|
||||
}
|
||||
}?.takeIf { it in searchScope }
|
||||
}
|
||||
|
||||
// this method is called from IDEA to resolve dependencies in Java code
|
||||
@@ -89,7 +96,10 @@ class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJ
|
||||
forEachClassId(qName) { classId ->
|
||||
val relativeClassName = classId.relativeClassName.asString()
|
||||
index.traverseDirectoriesInPackage(classId.packageFqName) { dir, rootType ->
|
||||
val psiClass = findClassGivenPackage(scope, dir, relativeClassName, rootType)
|
||||
val psiClass =
|
||||
findVirtualFileGivenPackage(dir, relativeClassName, rootType)
|
||||
?.takeIf { it in scope }
|
||||
?.findPsiClassInVirtualFile(relativeClassName)
|
||||
if (psiClass != null) {
|
||||
result.add(psiClass)
|
||||
}
|
||||
@@ -118,10 +128,11 @@ class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJ
|
||||
return null
|
||||
}
|
||||
|
||||
private fun findClassGivenPackage(
|
||||
scope: GlobalSearchScope, packageDir: VirtualFile,
|
||||
classNameWithInnerClasses: String, rootType: JavaRoot.RootType
|
||||
): PsiClass? {
|
||||
private fun findVirtualFileGivenPackage(
|
||||
packageDir: VirtualFile,
|
||||
classNameWithInnerClasses: String,
|
||||
rootType: JavaRoot.RootType
|
||||
): VirtualFile? {
|
||||
val topLevelClassName = classNameWithInnerClasses.substringBefore('.')
|
||||
|
||||
val vFile = when (rootType) {
|
||||
@@ -133,11 +144,14 @@ class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJ
|
||||
LOG.error("Invalid child of valid parent: ${vFile.path}; ${packageDir.isValid} path=${packageDir.path}")
|
||||
return null
|
||||
}
|
||||
if (vFile !in scope) {
|
||||
return null
|
||||
}
|
||||
|
||||
val file = myPsiManager.findFile(vFile) as? PsiClassOwner ?: return null
|
||||
return vFile
|
||||
}
|
||||
|
||||
private fun VirtualFile.findPsiClassInVirtualFile(
|
||||
classNameWithInnerClasses: String
|
||||
): PsiClass? {
|
||||
val file = myPsiManager.findFile(this) as? PsiClassOwner ?: return null
|
||||
return findClassInPsiFile(classNameWithInnerClasses, file)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user