Workaround NonClasspathClassFinder not supporting inner classes
Explicitly split qualified names, can be removed once platform is fixed #KT-19310 Fixed
This commit is contained in:
+66
-16
@@ -22,9 +22,11 @@ import com.intellij.openapi.roots.impl.PackageDirectoryCache
|
|||||||
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.PsiClassOwner
|
||||||
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.util.containers.ConcurrentFactoryMap
|
import com.intellij.util.containers.ConcurrentFactoryMap
|
||||||
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.ScriptModuleSearchScope
|
import org.jetbrains.kotlin.idea.caches.resolve.ScriptModuleSearchScope
|
||||||
import org.jetbrains.kotlin.load.java.AbstractJavaClassFinder
|
import org.jetbrains.kotlin.load.java.AbstractJavaClassFinder
|
||||||
import org.jetbrains.kotlin.resolve.jvm.KotlinSafeClassFinder
|
import org.jetbrains.kotlin.resolve.jvm.KotlinSafeClassFinder
|
||||||
@@ -56,21 +58,69 @@ class KotlinScriptDependenciesClassFinder(project: Project,
|
|||||||
myCaches.clear()
|
myCaches.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findClass(qualifiedName: String, scope: GlobalSearchScope): PsiClass? =
|
override fun findClass(qualifiedName: String, scope: GlobalSearchScope): PsiClass? {
|
||||||
super.findClass(qualifiedName, scope)?.let { aClass ->
|
val psiClass = findClassInCache(qualifiedName, scope) ?: return null
|
||||||
when {
|
return when {
|
||||||
scope is ScriptModuleSearchScope ||
|
scope is ScriptModuleSearchScope ||
|
||||||
(scope as? AbstractJavaClassFinder.FilterOutKotlinSourceFilesScope)?.base is ScriptModuleSearchScope ||
|
(scope as? AbstractJavaClassFinder.FilterOutKotlinSourceFilesScope)?.base is ScriptModuleSearchScope ||
|
||||||
scope is EverythingGlobalScope ||
|
scope is EverythingGlobalScope ||
|
||||||
aClass.containingFile?.virtualFile.let { file ->
|
psiClass.containingFile?.virtualFile.let { file ->
|
||||||
file != null &&
|
file != null &&
|
||||||
with (ProjectFileIndex.SERVICE.getInstance(myProject)) {
|
with(ProjectFileIndex.SERVICE.getInstance(myProject)) {
|
||||||
!isInContent(file) &&
|
!isInContent(file) &&
|
||||||
!isInLibraryClasses(file) &&
|
!isInLibraryClasses(file) &&
|
||||||
!isInLibrarySource(file)
|
!isInLibrarySource(file)
|
||||||
}
|
}
|
||||||
} -> aClass
|
} -> psiClass
|
||||||
else -> null
|
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<String>, 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
|
||||||
|
}
|
||||||
|
return@process true
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findClassInDir(dir: VirtualFile, classNames: List<String>): 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<String, PsiClass?>(topLevelClass) { currentPsiClass, className ->
|
||||||
|
currentPsiClass?.findInnerClassByName(className, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun splitDotQualifiedName(qualifiedName: String): Sequence<Pair<String, List<String>>> {
|
||||||
|
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<String, String> {
|
||||||
|
return Pair(substringBeforeLast('.', missingDelimiterValue = ""), substringAfterLast('.'))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package lib;
|
||||||
|
|
||||||
|
public interface JavaInterface {
|
||||||
|
public interface F {
|
||||||
|
public interface I {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class G {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import lib.*
|
||||||
|
|
||||||
|
fun f(
|
||||||
|
<warning>g</warning>: JavaInterface.G,
|
||||||
|
<warning>f</warning>: JavaInterface.F,
|
||||||
|
<warning>i</warning>: JavaInterface.F.I
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package custom.scriptDefinition
|
||||||
|
|
||||||
|
import kotlin.script.dependencies.*
|
||||||
|
import kotlin.script.templates.*
|
||||||
|
import java.io.File
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.Future
|
||||||
|
|
||||||
|
class TestDependenciesResolver : ScriptDependenciesResolver {
|
||||||
|
override fun resolve(
|
||||||
|
script: ScriptContents,
|
||||||
|
environment: Map<String, Any?>?,
|
||||||
|
report: (ScriptDependenciesResolver.ReportSeverity, String, ScriptContents.Position?) -> Unit, previousDependencies: KotlinScriptExternalDependencies?
|
||||||
|
): Future<KotlinScriptExternalDependencies?> {
|
||||||
|
return CompletableFuture.completedFuture(
|
||||||
|
object : KotlinScriptExternalDependencies {
|
||||||
|
override val classpath: Iterable<File> = listOf(
|
||||||
|
environment?.get("lib-classes") as File
|
||||||
|
)
|
||||||
|
|
||||||
|
override val imports: Iterable<String> = listOf("custom.library.*")
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ScriptTemplateDefinition(TestDependenciesResolver::class, scriptFilePattern = "script.kts")
|
||||||
|
class Template
|
||||||
+6
@@ -84,6 +84,12 @@ public class ScriptConfigurationHighlightingTestGenerated extends AbstractScript
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("javaNestedClass")
|
||||||
|
public void testJavaNestedClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/script/definition/highlighting/javaNestedClass/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("noResolver")
|
@TestMetadata("noResolver")
|
||||||
public void testNoResolver() throws Exception {
|
public void testNoResolver() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/script/definition/highlighting/noResolver/");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/script/definition/highlighting/noResolver/");
|
||||||
|
|||||||
Reference in New Issue
Block a user