From 742ffb98ad34718c0ac94336ab808203bd008027 Mon Sep 17 00:00:00 2001 From: Alexander Podkhalyuzin Date: Fri, 29 Mar 2019 16:31:50 +0300 Subject: [PATCH] Use indexes instead of slow directory search for script dependencies #KT-30018 Comment --- .../KotlinScriptDependenciesClassFinder.kt | 106 ++++++------------ 1 file changed, 37 insertions(+), 69 deletions(-) diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/KotlinScriptDependenciesClassFinder.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/KotlinScriptDependenciesClassFinder.kt index 86618fa5f42..a7f2e745034 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/KotlinScriptDependenciesClassFinder.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/KotlinScriptDependenciesClassFinder.kt @@ -22,19 +22,17 @@ import com.intellij.openapi.roots.impl.PackageDirectoryCache import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.NonClasspathClassFinder import com.intellij.psi.PsiClass -import com.intellij.psi.PsiClassOwner import com.intellij.psi.search.EverythingGlobalScope import com.intellij.psi.search.GlobalSearchScope import com.intellij.util.containers.ConcurrentFactoryMap -import com.intellij.util.containers.ContainerUtil import org.jetbrains.kotlin.idea.caches.project.ScriptModuleSearchScope import org.jetbrains.kotlin.load.java.AbstractJavaClassFinder import org.jetbrains.kotlin.resolve.jvm.KotlinSafeClassFinder - -class KotlinScriptDependenciesClassFinder(project: Project, - private val scriptDependenciesManager: ScriptDependenciesManager +class KotlinScriptDependenciesClassFinder( + project: Project, + private val scriptDependenciesManager: ScriptDependenciesManager ) : NonClasspathClassFinder(project), KotlinSafeClassFinder { private val myCaches by lazy { @@ -47,11 +45,11 @@ class KotlinScriptDependenciesClassFinder(project: Project, override fun calcClassRoots(): List = scriptDependenciesManager.getAllScriptsClasspath().toList() override fun getCache(scope: GlobalSearchScope?): PackageDirectoryCache = - (scope as? ScriptModuleSearchScope ?: - (scope as? AbstractJavaClassFinder.FilterOutKotlinSourceFilesScope)?.base as? ScriptModuleSearchScope - )?.let { - myCaches[it.scriptFile] - } ?: super.getCache(scope) + (scope as? ScriptModuleSearchScope + ?: (scope as? AbstractJavaClassFinder.FilterOutKotlinSourceFilesScope)?.base as? ScriptModuleSearchScope + )?.let { + myCaches[it.scriptFile] + } ?: super.getCache(scope) override fun clearCache() { super.clearCache() @@ -59,68 +57,38 @@ class KotlinScriptDependenciesClassFinder(project: Project, } override fun findClass(qualifiedName: String, scope: GlobalSearchScope): PsiClass? { - val psiClass = findClassInCache(qualifiedName, scope) ?: return null - return when { - scope is ScriptModuleSearchScope || - (scope as? AbstractJavaClassFinder.FilterOutKotlinSourceFilesScope)?.base is ScriptModuleSearchScope || - scope is EverythingGlobalScope || - psiClass.containingFile?.virtualFile.let { file -> - file != null && - with(ProjectFileIndex.SERVICE.getInstance(myProject)) { - !isInContent(file) && - !isInLibraryClasses(file) && - !isInLibrarySource(file) - } - } -> psiClass - else -> null - } - } - - private fun findClassInCache(qualifiedName: String, scope: GlobalSearchScope): PsiClass? { - if (qualifiedName.isEmpty()) return null - - return splitDotQualifiedName(qualifiedName).map { (packageName, classNames) -> - findClassInPackage(packageName, classNames, scope) - }.find { it != null } - } - - private fun findClassInPackage(packageName: String, classNames: List, scope: GlobalSearchScope): PsiClass? { - var result: PsiClass? = null - ContainerUtil.process(getCache(scope).getDirectoriesByPackageName(packageName)) { dir -> - if (dir !in scope) return@process true - - findClassInDir(dir, classNames)?.let { - result = it - return@process false + tailrec fun findClassInner(parentQualifier: String, inners: List = emptyList()): PsiClass? { + if (parentQualifier.isEmpty()) return null + val parentClass = super.findClass(parentQualifier, scope) + if (parentClass != null) { + if (inners.isNotEmpty()) { + val innerClass = inners.fold(parentClass) { c: PsiClass?, name: String -> + c?.findInnerClassByName(name, false) + } + if (innerClass != null) return innerClass + } else return parentClass } - return@process true + return findClassInner( + parentQualifier.substringBeforeLast('.', ""), + listOf(parentQualifier.substringAfterLast('.')) + inners + ) } - return result - } - private fun findClassInDir(dir: VirtualFile, classNames: List): PsiClass? { - val firstClassName = classNames.first() - val virtualFile = dir.findChild("$firstClassName.class") ?: return null - val psiFile = psiManager.findFile(virtualFile) as? PsiClassOwner ?: return null - val topLevelClass = psiFile.classes.singleOrNull() ?: return null - return classNames.subList(1, classNames.size).fold(topLevelClass) { currentPsiClass, className -> - currentPsiClass?.findInnerClassByName(className, false) + return findClassInner(qualifiedName)?.let { aClass -> + when { + scope is ScriptModuleSearchScope || + (scope as? AbstractJavaClassFinder.FilterOutKotlinSourceFilesScope)?.base is ScriptModuleSearchScope || + scope is EverythingGlobalScope || + aClass.containingFile?.virtualFile.let { file -> + file != null && + with(ProjectFileIndex.SERVICE.getInstance(myProject)) { + !isInContent(file) && + !isInLibraryClasses(file) && + !isInLibrarySource(file) + } + } -> aClass + else -> null + } } } - - private fun splitDotQualifiedName(qualifiedName: String): Sequence>> { - val (packageName, className) = qualifiedName.splitByLastDot() - - return generateSequence(Pair(packageName, listOf(className))) { - (prevPackageName, prevClassNames) -> - if (prevPackageName == "") return@generateSequence null - - val (newPackageName, newTopLevelClassName) = prevPackageName.splitByLastDot() - Pair(newPackageName, listOf(newTopLevelClassName) + prevClassNames) - } - } - - private fun String.splitByLastDot(): Pair { - return Pair(substringBeforeLast('.', missingDelimiterValue = ""), substringAfterLast('.')) - } } \ No newline at end of file