Optimize KotlinScriptDependenciesClassFinder
Do not call processDirectories for all package prefixes checking inner classes ^KT-39796 Fixed
This commit is contained in:
+40
-26
@@ -5,14 +5,15 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.core.script
|
package org.jetbrains.kotlin.idea.core.script
|
||||||
|
|
||||||
import com.intellij.openapi.progress.ProgressManager
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.roots.ProjectFileIndex
|
import com.intellij.openapi.roots.ProjectFileIndex
|
||||||
import com.intellij.openapi.vfs.VirtualFile
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.NonClasspathClassFinder
|
import com.intellij.psi.NonClasspathClassFinder
|
||||||
import com.intellij.psi.PsiClass
|
import com.intellij.psi.PsiClass
|
||||||
|
import com.intellij.psi.impl.java.stubs.index.JavaFullClassNameIndex
|
||||||
import com.intellij.psi.search.EverythingGlobalScope
|
import com.intellij.psi.search.EverythingGlobalScope
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.psi.stubs.StubIndex
|
||||||
import org.jetbrains.kotlin.resolve.jvm.KotlinSafeClassFinder
|
import org.jetbrains.kotlin.resolve.jvm.KotlinSafeClassFinder
|
||||||
|
|
||||||
|
|
||||||
@@ -23,33 +24,46 @@ class KotlinScriptDependenciesClassFinder(
|
|||||||
.getAllScriptsDependenciesClassFiles().filter { it.isValid }.toList()
|
.getAllScriptsDependenciesClassFiles().filter { it.isValid }.toList()
|
||||||
|
|
||||||
override fun findClass(qualifiedName: String, scope: GlobalSearchScope): PsiClass? {
|
override fun findClass(qualifiedName: String, scope: GlobalSearchScope): PsiClass? {
|
||||||
tailrec fun findClassInner(parentQualifier: String, inners: List<String> = emptyList()): PsiClass? {
|
val scriptDependencies =
|
||||||
if (parentQualifier.isEmpty()) return null
|
ScriptConfigurationManager.getInstance(project).getAllScriptsDependenciesClassFiles()
|
||||||
ProgressManager.checkCanceled()
|
val firstJarInDependencies = scriptDependencies.firstOrNull() ?: return null
|
||||||
val parentClass = super.findClass(parentQualifier, scope)
|
|
||||||
if (parentClass != null) {
|
if (!scope.contains(firstJarInDependencies)) return null
|
||||||
if (inners.isNotEmpty()) {
|
|
||||||
val innerClass = inners.fold(parentClass) { c: PsiClass?, name: String ->
|
val classByFileName = super.findClass(qualifiedName, scope)
|
||||||
c?.findInnerClassByName(name, false)
|
if (classByFileName != null) {
|
||||||
}
|
return classByFileName.isInScope(scope)
|
||||||
if (innerClass != null) return innerClass
|
|
||||||
} else return parentClass
|
|
||||||
}
|
|
||||||
return findClassInner(
|
|
||||||
parentQualifier.substringBeforeLast('.', ""),
|
|
||||||
listOf(parentQualifier.substringAfterLast('.')) + inners
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return findClassInner(qualifiedName)?.let { aClass ->
|
// Following code is needed because NonClasspathClassFinder cannot find inner classes
|
||||||
if (scope is EverythingGlobalScope) return aClass
|
// JavaFullClassNameIndex cannot be used directly, because it filter only classes in source roots
|
||||||
|
val classes = StubIndex.getElements(
|
||||||
val file = aClass.containingFile?.virtualFile ?: return null
|
JavaFullClassNameIndex.getInstance().key,
|
||||||
val index = ProjectFileIndex.SERVICE.getInstance(myProject)
|
qualifiedName.hashCode(),
|
||||||
if (index.isInContent(file) || index.isInLibraryClasses(file) || index.isInLibrarySource(file)) {
|
project,
|
||||||
return null
|
scope,
|
||||||
}
|
PsiClass::class.java
|
||||||
return aClass
|
).filter {
|
||||||
|
it.qualifiedName == qualifiedName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val found = when (classes.size) {
|
||||||
|
0 -> null
|
||||||
|
1 -> classes.single()
|
||||||
|
else -> classes.first() // todo: check when this happens
|
||||||
|
}
|
||||||
|
|
||||||
|
return found?.isInScope(scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiClass.isInScope(scope: GlobalSearchScope): PsiClass? {
|
||||||
|
if (scope is EverythingGlobalScope) return this
|
||||||
|
|
||||||
|
val file = this.containingFile?.virtualFile ?: return null
|
||||||
|
val index = ProjectFileIndex.SERVICE.getInstance(myProject)
|
||||||
|
if (index.isInContent(file) || index.isInLibrary(file)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package lib
|
||||||
|
|
||||||
|
interface KotlinInterface {
|
||||||
|
interface F {
|
||||||
|
interface I
|
||||||
|
}
|
||||||
|
|
||||||
|
class G
|
||||||
|
}
|
||||||
+9
-1
@@ -1,6 +1,6 @@
|
|||||||
import lib.*
|
import lib.*
|
||||||
|
|
||||||
fun f(
|
fun javaFun(
|
||||||
<warning>g</warning>: JavaInterface.G,
|
<warning>g</warning>: JavaInterface.G,
|
||||||
<warning>f</warning>: JavaInterface.F,
|
<warning>f</warning>: JavaInterface.F,
|
||||||
<warning>i</warning>: JavaInterface.F.I
|
<warning>i</warning>: JavaInterface.F.I
|
||||||
@@ -8,4 +8,12 @@ fun f(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun kotlinFun(
|
||||||
|
<warning>g</warning>: KotlinInterface.G,
|
||||||
|
<warning>f</warning>: KotlinInterface.F,
|
||||||
|
<warning>i</warning>: KotlinInterface.F.I
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// DEPENDENCIES: classpath:lib-classes; imports:custom.library.*
|
// DEPENDENCIES: classpath:lib-classes; imports:custom.library.*
|
||||||
Generated
+5
-5
@@ -100,16 +100,16 @@ public class ScriptConfigurationHighlightingTestGenerated extends AbstractScript
|
|||||||
runTest("idea/testData/script/definition/highlighting/implicitReceiver/");
|
runTest("idea/testData/script/definition/highlighting/implicitReceiver/");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("javaNestedClass")
|
|
||||||
public void testJavaNestedClass() throws Exception {
|
|
||||||
runTest("idea/testData/script/definition/highlighting/javaNestedClass/");
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestMetadata("multiModule")
|
@TestMetadata("multiModule")
|
||||||
public void testMultiModule() throws Exception {
|
public void testMultiModule() throws Exception {
|
||||||
runTest("idea/testData/script/definition/highlighting/multiModule/");
|
runTest("idea/testData/script/definition/highlighting/multiModule/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("nestedClass")
|
||||||
|
public void testNestedClass() throws Exception {
|
||||||
|
runTest("idea/testData/script/definition/highlighting/nestedClass/");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("noResolver")
|
@TestMetadata("noResolver")
|
||||||
public void testNoResolver() throws Exception {
|
public void testNoResolver() throws Exception {
|
||||||
runTest("idea/testData/script/definition/highlighting/noResolver/");
|
runTest("idea/testData/script/definition/highlighting/noResolver/");
|
||||||
|
|||||||
Reference in New Issue
Block a user