@maxmedvedev I’ve tried it, the extension doesn’t work, the returned PsiClass
instance doesn’t appear in the hint list, com.intellij.psi.JavaPsiFacade#findClass
doesn’t find the PsiClass
, and writing out the fully qualified class name doesn’t resolve the PsiClass
instance.
class PsiProvider(private val project: Project) : JvmElementProvider {
@Suppress("UnstableApiUsage")
override fun getClasses(qualifiedName: String, scope: GlobalSearchScope): MutableList<out JvmClass> {
val classes = project.service<GeneratedClassCache>()
.classes
.toList()
.filter { it.first.startsWith(qualifiedName) }
.mapTo(mutableListOf()) { it.second }
println("getClasses in PsiProvider for `$qualifiedName`: $classes")
return classes
}
}
Here’s some of the rest of my code 
@Service(Service.Level.PROJECT)
class GeneratedClassCache(private val project: Project) {
private val classesCachedValue = CachedValuesManager.getManager(project).createCachedValue {
@Language("Java")
val java = """
package top.fallingangel;
public class GenerateTest {
public static void staticMethod1() {}
public static void staticMethod2() {}
}
""".trimIndent()
val javaClass = PsiFileFactory.getInstance(project)
.createFileFromText("GenerateTest.java", JavaLanguage.INSTANCE, java)
.getChildOfType<PsiClass>()!!
CachedValueProvider.Result.create(
mapOf("top.fallingangel.GenerateTest" to javaClass),
DumbService.getInstance(project).modificationTracker,
ProjectRootModificationTracker.getInstance(project),
)
}
val classes = classesCachedValue.value ?: emptyMap()
}
class DTOShortNamesCache(private val project: Project) : PsiShortNamesCache() {
override fun getClassesByName(name: String, scope: GlobalSearchScope): Array<PsiClass> {
val classes = project.service<GeneratedClassCache>()
.classes
.toList()
.filter { name in (it.second.name ?: return@filter false) }
.map { it.second }
.toTypedArray()
return classes
}
override fun getAllClassNames(): Array<String> {
val names = project.service<GeneratedClassCache>()
.classes
.values
.mapNotNull { it.name }
.toTypedArray()
return names
}
// Omit the rest of the methods
}
But I found com.intellij.psi.PsiElementFinder#PsiElementFinder
in the comments of JvmElementProvider
, and this works… Combined with the PsiShortNamesCache(DTOShortNamesCache)
that I found earlier, the code’s completion hints show the PsiClass that I generated, which hints at the class members, and clicking on the jumps also works!
class PsiFinder(private val project: Project) : PsiElementFinder() {
override fun findClass(qualifiedName: String, scope: GlobalSearchScope): PsiClass? {
return project.service<GeneratedClassCache>().classes[qualifiedName]
}
override fun findClasses(qualifiedName: String, scope: GlobalSearchScope): Array<PsiClass> {
return project.service<GeneratedClassCache>()
.classes
.toList()
.filter { it.first.startsWith(qualifiedName) }
.map { it.second }
.toTypedArray()
}
}
However, there is another problem, as shown in the screenshot, after the completion prompt, the class name is fully qualified, not import + class name. After using ‘Replace qualified name with import’, it will only remove the package name, but will not generate the import statement, and then it will report an error, and after fixing it, it will become fully qualified again.
If you manually write out the import statement, it resolves to the PsiClass instance, but it prompts that the import statement is not being used and can be optimized away… What is this due to? Need to implement another extension point related to import? Is it com.intellij.lang.importOptimizer
? But when I look at the implementation of JavaImportOptimizer
, there is parsing of import statements in it, and when I test it under the kotlin file, the import and the parsing of import statements are fine, so… I don’t think the importOptimizer
is missing, is it?
Also one more question, under Java this effect is done through com.intellij.java.shortNamesCache
and com.intellij.java.elementFinder
, which extension point should be for Kotlin?