User: joe Date: 08 May 25 13:11 Revision: 0b58959f04b911a69833c824248e5161c3b51d14 Summary: Fix full qualified name for local classes TeamCity URL: https://ci.mcdev.io/viewModification.html?tab=vcsModificationFiles&modId=9981&personal=false Index: src/main/kotlin/platform/mcp/at/completion/AtCompletionContributor.kt =================================================================== --- src/main/kotlin/platform/mcp/at/completion/AtCompletionContributor.kt (revision 63e48f813ba5c00a05dd04eb73b6cb93ee3645d4) +++ src/main/kotlin/platform/mcp/at/completion/AtCompletionContributor.kt (revision 0b58959f04b911a69833c824248e5161c3b51d14) @@ -31,6 +31,7 @@ import com.demonwav.mcdev.util.anonymousElements import com.demonwav.mcdev.util.fullQualifiedName import com.demonwav.mcdev.util.getSimilarity +import com.demonwav.mcdev.util.localClasses import com.demonwav.mcdev.util.nameAndParameterTypes import com.demonwav.mcdev.util.qualifiedMemberReference import com.demonwav.mcdev.util.simpleQualifiedMemberReference @@ -157,6 +158,17 @@ ), ) } + + for (localClass in currentClass.localClasses) { + val name = localClass.fullQualifiedName ?: continue + result.addElement( + PrioritizedLookupElement.withPriority( + LookupElementBuilder.create(name).withIcon(PlatformIcons.CLASS_ICON), + 1.0, + ), + ) + } + return } Index: src/main/kotlin/platform/mixin/util/AsmUtil.kt =================================================================== --- src/main/kotlin/platform/mixin/util/AsmUtil.kt (revision 63e48f813ba5c00a05dd04eb73b6cb93ee3645d4) +++ src/main/kotlin/platform/mixin/util/AsmUtil.kt (revision 0b58959f04b911a69833c824248e5161c3b51d14) @@ -31,7 +31,9 @@ import com.demonwav.mcdev.util.findQualifiedClass import com.demonwav.mcdev.util.fullQualifiedName import com.demonwav.mcdev.util.hasSyntheticMethod +import com.demonwav.mcdev.util.innerIndexAndName import com.demonwav.mcdev.util.isErasureEquivalentTo +import com.demonwav.mcdev.util.localClasses import com.demonwav.mcdev.util.lockedCached import com.demonwav.mcdev.util.loggerForTopLevel import com.demonwav.mcdev.util.mapToArray @@ -285,29 +287,65 @@ append(outerClassSimpleName) append(" {\n") var indent = " " + val closingBraces = mutableListOf("}") for ((index, innerClass) in innerClasses.withIndex()) { - val anonymousIndex = innerClass.toIntOrNull() - if (anonymousIndex != null) { - // add anonymous classes make the anonymous class index correct - if (anonymousIndex in 1..999) { - repeat(anonymousIndex - 1) { i -> + val innerIndexAndName = innerIndexAndName(innerClass) + if (innerIndexAndName != null) { + val (innerIndex, innerName) = innerIndexAndName + if (innerIndex in 1..999) { + if (innerName != null) { + // add local classes to make the local class index correct + repeat(innerIndex - 1) { i -> - append(indent) + append(indent) + append("static void method") + append(i) + append("() {\n") + append(indent) + append(" class ") + append(innerName) + append(" {}\n") + append(indent) + append("}\n") + } + } else { + // add anonymous classes to make the anonymous class index correct + repeat(innerIndex - 1) { i -> + append(indent) - append("Object inner") - append(i) - append(" = new Object() {};\n") - } - } + append("Object inner") + append(i) + append(" = new Object() {};\n") + } + } + } + append(indent) + if (innerName != null) { + append("static void method") + append(innerIndex) + append("() {\n") + closingBraces += "}" + indent += " " + append(indent) + append("class ") + append(innerName) + if (index == innerClasses.lastIndex) { + append("") + } + append(" {\n") + closingBraces += "}" + } else { - append("Object inner") + append("Object inner") - append(anonymousIndex) + append(innerIndex) - append(" = new ") - if (index == innerClasses.lastIndex) { - val superName = superName ?: "java/lang/Object" - append(superName.replace('/', '.').replace('$', '.')) - } else { - append("Object") - } + append(" = new ") + if (index == innerClasses.lastIndex) { + val superName = superName ?: "java/lang/Object" + append(superName.replace('/', '.').replace('$', '.')) + } else { + append("Object") + } - append("() {} {\n") + append("() {\n") + closingBraces += ");" + } } else { append(indent) if (index != innerClasses.lastIndex || hasAccess(Opcodes.ACC_STATIC)) { @@ -319,20 +357,17 @@ append("") } append(" {\n") + closingBraces += "}" } indent += " " } append(body.prependIndent(indent)) - repeat(innerClasses.size + 1) { i -> + for (i in closingBraces.lastIndex downTo 0) { append("\n") - append(" ".repeat(innerClasses.size - i)) - append("}") - // append ; after anonymous class declarations - if (i < innerClasses.size && innerClasses[innerClasses.size - 1 - i].toIntOrNull() != null) { - append(";") + append(" ".repeat(i)) + append(closingBraces[i]) - } - } + } + } - } val file = PsiFileFactory.getInstance(project).createFileFromText( "$outerClassSimpleName.java", JavaFileType.INSTANCE, @@ -355,6 +390,7 @@ while (true) { clazz = clazz.innerClasses.firstOrNull() ?: clazz.anonymousElements.lastOrNull { it !== clazz && it is PsiClass } as? PsiClass + ?: clazz.localClasses.lastOrNull() ?: break } Index: src/main/kotlin/util/class-utils.kt =================================================================== --- src/main/kotlin/util/class-utils.kt (revision 63e48f813ba5c00a05dd04eb73b6cb93ee3645d4) +++ src/main/kotlin/util/class-utils.kt (revision 0b58959f04b911a69833c824248e5161c3b51d14) @@ -25,8 +25,11 @@ import com.intellij.openapi.project.Project import com.intellij.psi.CommonClassNames import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.JavaRecursiveElementWalkingVisitor +import com.intellij.psi.PsiAnonymousClass import com.intellij.psi.PsiClass import com.intellij.psi.PsiClassType +import com.intellij.psi.PsiCompiledElement import com.intellij.psi.PsiElement import com.intellij.psi.PsiField import com.intellij.psi.PsiInvalidElementAccessException @@ -36,6 +39,7 @@ import com.intellij.psi.PsiPrimitiveType import com.intellij.psi.PsiTypeParameter import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.util.PsiUtil val PsiClass.packageName get() = (containingFile as? PsiJavaFile)?.packageName @@ -101,9 +105,15 @@ } else { parentClass = currentClass.parent.findContainingClass() ?: throw ClassNameResolutionFailedException() + if (currentClass is PsiAnonymousClass) { - // Add index of anonymous class to list - list.add(parentClass.getAnonymousIndex(currentClass).toString()) + // Add index of anonymous class to list + list.add(parentClass.getAnonymousIndex(currentClass).toString()) + } else { + // Add index and name of local class to list + val currentName = currentClass.name ?: throw ClassNameResolutionFailedException() + list.add(parentClass.getLocalIndex(currentClass).toString() + currentName) - } + } + } currentClass = parentClass name = getName(currentClass) @@ -157,21 +167,32 @@ } private fun PsiClass.findInnerClass(name: String): PsiClass? { - val anonymousIndex = name.toIntOrNull() - return if (anonymousIndex == null) { + val innerIndexAndName = innerIndexAndName(name) + return if (innerIndexAndName == null) { // Named inner class findInnerClassByName(name, false) } else { - if (anonymousIndex > 0 && anonymousIndex <= anonymousElements.size) { - anonymousElements[anonymousIndex - 1] as PsiClass + val (innerIndex, innerName) = innerIndexAndName + if (innerName != null) { + val localClasses = this.localClasses.filter { it.name == innerName } + if (innerIndex > 0 && innerIndex <= localClasses.size) { + localClasses[innerIndex - 1] - } else { - null - } + } else { + null + } + } else { + if (innerIndex > 0 && innerIndex <= anonymousElements.size) { + anonymousElements[innerIndex - 1] as PsiClass + } else { + null - } -} + } + } + } +} @Throws(ClassNameResolutionFailedException::class) -fun PsiElement.getAnonymousIndex(anonymousElement: PsiElement): Int { +@PublishedApi +internal fun PsiElement.getAnonymousIndex(anonymousElement: PsiElement): Int { // Attempt to find name for anonymous class for ((i, element) in anonymousElements.withIndex()) { if (element equivalentTo anonymousElement) { @@ -194,6 +215,51 @@ return emptyArray() } +@Throws(ClassNameResolutionFailedException::class) +@PublishedApi +internal fun PsiElement.getLocalIndex(localClass: PsiClass): Int { + // Attempt to find index for local class + var index = 0 + for (aLocalClass in localClasses) { + if (aLocalClass.name == localClass.name) { + index++ + if (aLocalClass equivalentTo localClass) { + return index + } + } + } + + throw ClassNameResolutionFailedException("Failed to determine local class index for $localClass") +} + +val PsiElement.localClasses: List + get() { + if (this is PsiCompiledElement) { + return emptyList() + } + + val list = mutableListOf() + accept(object : JavaRecursiveElementWalkingVisitor() { + override fun visitClass(clazz: PsiClass) { + if (clazz === this@localClasses) { + super.visitClass(clazz) + } else { + if (PsiUtil.isLocalClass(clazz)) { + list += clazz + } + } + } + }) + return list + } + +private val INNER_INDEX_AND_NAME_REGEX = "(\\d+)(\\w*)".toRegex() +fun innerIndexAndName(className: String): Pair? { + val (indexStr, name) = INNER_INDEX_AND_NAME_REGEX.matchEntire(className)?.destructured ?: return null + val index = indexStr.toIntOrNull() ?: return null + return index to name.ifEmpty { null } +} + // Inheritance fun PsiClass.extendsOrImplements(qualifiedClassName: String): Boolean {