Skip to content

Commit 8ea0cf1

Browse files
committed
fixes
1 parent ce1f8df commit 8ea0cf1

File tree

3 files changed

+68
-32
lines changed

3 files changed

+68
-32
lines changed

lsp/kotlin-core/src/main/java/org/appdevforall/codeonthego/lsp/kotlin/index/ClasspathIndexer.kt

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,8 @@ class ClasspathIndexer {
108108
index.addSourceJar(directory.absolutePath)
109109
}
110110

111-
private fun detectExtensionReceiver(
112-
extensionFunctionNames: Set<String>,
113-
method: MethodInfo
114-
): String? {
115-
if (method.name !in extensionFunctionNames) return null
111+
private fun detectExtensionReceiver(method: MethodInfo): String? {
112+
if (!method.isExtension) return null
116113
if (method.parameters.isEmpty()) return null
117114
return method.parameters.first().type
118115
}
@@ -160,7 +157,7 @@ class ClasspathIndexer {
160157
}
161158
if (method.name == "<clinit>") continue
162159

163-
val extensionReceiver = detectExtensionReceiver(classInfo.extensionFunctionNames, method)
160+
val extensionReceiver = detectExtensionReceiver(method)
164161

165162
val params = if (extensionReceiver != null && method.parameters.isNotEmpty()) {
166163
method.parameters.drop(1)
@@ -294,28 +291,40 @@ internal object ClassFileReader {
294291

295292
val attributesCount = readU2(bytes, offset)
296293
offset += 2
297-
val extensionNames = mutableSetOf<String>()
294+
var extensionFunctions = emptyMap<String, List<Int>>()
298295
for (i in 0 until attributesCount) {
299296
val attrNameIndex = readU2(bytes, offset)
300297
offset += 2
301298
val attrLength = readU4(bytes, offset)
302299
offset += 4
303300
val attrName = constantPool.getUtf8(attrNameIndex)
304301
if (attrName == "RuntimeVisibleAnnotations") {
305-
val parsed = parseKotlinMetadataExtensions(bytes, offset, attrLength, constantPool)
306-
extensionNames.addAll(parsed)
302+
extensionFunctions = parseKotlinMetadataExtensions(bytes, offset, attrLength, constantPool)
307303
}
308304
offset += attrLength
309305
}
310306

307+
val markedMethods = if (extensionFunctions.isNotEmpty()) {
308+
val remaining = extensionFunctions.mapValues { it.value.toMutableList() }.toMutableMap()
309+
methods.map { method ->
310+
val valueCounts = remaining[method.name] ?: return@map method
311+
if (method.parameters.isEmpty()) return@map method
312+
val idx = valueCounts.indexOf(method.parameters.size - 1)
313+
if (idx < 0) return@map method
314+
valueCounts.removeAt(idx)
315+
method.copy(isExtension = true)
316+
}
317+
} else {
318+
methods
319+
}
320+
311321
return ClassInfo(
312322
kind = parseClassKind(accessFlags),
313323
visibility = parseVisibility(accessFlags),
314324
typeParameters = emptyList(),
315325
superTypes = superTypes,
316-
methods = methods,
317-
fields = fields,
318-
extensionFunctionNames = extensionNames
326+
methods = markedMethods,
327+
fields = fields
319328
)
320329
}
321330

@@ -324,10 +333,10 @@ internal object ClassFileReader {
324333
offset: Int,
325334
length: Int,
326335
constantPool: ConstantPool
327-
): Set<String> {
336+
): Map<String, MutableList<Int>> {
328337
try {
329338
val endOffset = offset + length
330-
if (offset >= bytes.size) return emptySet()
339+
if (offset >= bytes.size) return emptyMap()
331340
val numAnnotations = readU2(bytes, offset)
332341
var pos = offset + 2
333342

@@ -373,12 +382,12 @@ internal object ClassFileReader {
373382

374383
if (d1Bytes.isNotEmpty() && d2Strings.isNotEmpty()) {
375384
val combined = d1Bytes.fold(ByteArray(0)) { acc, b -> acc + b }
376-
return extractExtensionFunctionNames(combined, d2Strings)
385+
return extractExtensionFunctions(combined, d2Strings)
377386
}
378387
}
379388
} catch (_: Exception) {
380389
}
381-
return emptySet()
390+
return emptyMap()
382391
}
383392

384393
private fun parseAnnotationArrayOfStrings(
@@ -438,8 +447,8 @@ internal object ClassFileReader {
438447
}
439448
}
440449

441-
private fun extractExtensionFunctionNames(d1: ByteArray, d2: List<String>): Set<String> {
442-
val extensionNames = mutableSetOf<String>()
450+
private fun extractExtensionFunctions(d1: ByteArray, d2: List<String>): Map<String, MutableList<Int>> {
451+
val extensionFunctions = mutableMapOf<String, MutableList<Int>>()
443452
try {
444453
var pos = 0
445454
while (pos < d1.size) {
@@ -464,6 +473,7 @@ internal object ClassFileReader {
464473
val msgEnd = pos + msgLen
465474
var nameIndex = -1
466475
var hasReceiver = false
476+
var valueParamCount = 0
467477
var innerPos = pos
468478

469479
while (innerPos < msgEnd) {
@@ -489,6 +499,7 @@ internal object ClassFileReader {
489499
2 -> {
490500
val (innerLen, np) = readVarInt(d1, innerPos)
491501
innerPos = np
502+
if (innerField == 5) valueParamCount++
492503
if (innerField == 6) hasReceiver = true
493504
innerPos += innerLen
494505
}
@@ -498,7 +509,9 @@ internal object ClassFileReader {
498509
}
499510

500511
if (hasReceiver && nameIndex >= 0 && nameIndex < d2.size) {
501-
extensionNames.add(d2[nameIndex])
512+
extensionFunctions
513+
.getOrPut(d2[nameIndex]) { mutableListOf() }
514+
.add(valueParamCount)
502515
}
503516
pos = msgEnd
504517
} else {
@@ -511,7 +524,7 @@ internal object ClassFileReader {
511524
}
512525
} catch (_: Exception) {
513526
}
514-
return extensionNames
527+
return extensionFunctions
515528
}
516529

517530
private fun readVarInt(bytes: ByteArray, startPos: Int): Pair<Int, Int> {
@@ -841,8 +854,7 @@ data class ClassInfo(
841854
val typeParameters: List<String>,
842855
val superTypes: List<String>,
843856
val methods: List<MethodInfo>,
844-
val fields: List<FieldInfo>,
845-
val extensionFunctionNames: Set<String> = emptySet()
857+
val fields: List<FieldInfo>
846858
) {
847859
companion object {
848860
val EMPTY = ClassInfo(
@@ -861,7 +873,8 @@ data class MethodInfo(
861873
val parameters: List<IndexedParameter>,
862874
val returnType: String,
863875
val visibility: Visibility,
864-
val typeParameters: List<String>
876+
val typeParameters: List<String>,
877+
val isExtension: Boolean = false
865878
)
866879

867880
data class FieldInfo(

lsp/kotlin-core/src/main/java/org/appdevforall/codeonthego/lsp/kotlin/index/ProjectIndex.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ class ProjectIndex : SymbolIndex {
6868
return results.distinctBy { it.fqName }
6969
}
7070

71+
fun findInProjectFiles(name: String): List<IndexedSymbol> {
72+
val results = mutableListOf<IndexedSymbol>()
73+
for (fgiveileIndex in fileIndexes.values) {
74+
results.addAll(fileIndex.findBySimpleName(name))
75+
}
76+
return results
77+
}
78+
7179
override fun findByPackage(packageName: String): List<IndexedSymbol> {
7280
val results = mutableListOf<IndexedSymbol>()
7381

lsp/kotlin-core/src/main/java/org/appdevforall/codeonthego/lsp/kotlin/server/providers/CompletionProvider.kt

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -656,13 +656,29 @@ class CompletionProvider(
656656
}
657657
}
658658

659-
val classpathIndex = projectIndex.getClasspathIndex()
660-
if (classpathIndex != null) {
661-
symbolTable?.imports?.filter { it.isStar }?.forEach { import ->
662-
val candidate = "${import.fqName}.$typeName"
663-
if (classpathIndex.findByFqName(candidate) != null) return candidate
659+
if (symbolTable != null) {
660+
val pkg = symbolTable.packageName
661+
val hasDeclaredType = symbolTable.classes.any { it.name == typeName }
662+
|| symbolTable.typeAliases.any { it.name == typeName }
663+
if (hasDeclaredType) {
664+
return if (pkg.isNotEmpty()) "$pkg.$typeName" else typeName
664665
}
666+
}
667+
668+
val projectMatches = projectIndex.findInProjectFiles(typeName).filter { it.kind.isClass }
669+
if (projectMatches.isNotEmpty()) {
670+
if (projectMatches.size == 1) return projectMatches.first().fqName
671+
val samePackage = projectMatches.firstOrNull { it.packageName == symbolTable?.packageName }
672+
return (samePackage ?: projectMatches.first()).fqName
673+
}
674+
675+
symbolTable?.imports?.filter { it.isStar }?.forEach { import ->
676+
val candidate = "${import.fqName}.$typeName"
677+
if (projectIndex.findByFqName(candidate) != null) return candidate
678+
}
665679

680+
val classpathIndex = projectIndex.getClasspathIndex()
681+
if (classpathIndex != null) {
666682
val matches = classpathIndex.findBySimpleName(typeName).filter { it.kind.isClass }
667683
if (matches.size == 1) return matches.first().fqName
668684
}
@@ -701,7 +717,7 @@ class CompletionProvider(
701717

702718
val seen = mutableSetOf<String>()
703719
return result.filter { member ->
704-
val key = member.name + "(" + member.parameters.joinToString(",") { it.type } + ")"
720+
val key = member.kind.name + ":" + member.name + "(" + member.parameters.joinToString(",") { it.type } + ")"
705721
seen.add(key)
706722
}
707723
}
@@ -729,7 +745,7 @@ class CompletionProvider(
729745

730746
val seen = mutableSetOf<String>()
731747
return result.filter { member ->
732-
val key = member.name + "(" + member.parameters.joinToString(",") { it.type } + ")"
748+
val key = member.kind.name + ":" + member.name + "(" + member.parameters.joinToString(",") { it.type } + ")"
733749
seen.add(key)
734750
}
735751
}
@@ -771,8 +787,7 @@ class CompletionProvider(
771787

772788
private fun isFilteredMember(name: String): Boolean {
773789
if (name == "<init>" || name == "<clinit>") return true
774-
if (name.length > 1 && name.all { it.isUpperCase() || it == '_' || it.isDigit() }) return true
775-
if (name.startsWith("access$")) return true
790+
if (name.startsWith("access$") || name.startsWith("$")) return true
776791
return false
777792
}
778793

0 commit comments

Comments
 (0)