diff --git a/app/src/main/kotlin/org/kotlinlsp/actions/Autocomplete.kt b/app/src/main/kotlin/org/kotlinlsp/actions/Autocomplete.kt index 7749f6b..205e3a9 100644 --- a/app/src/main/kotlin/org/kotlinlsp/actions/Autocomplete.kt +++ b/app/src/main/kotlin/org/kotlinlsp/actions/Autocomplete.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtValueArgumentList import org.kotlinlsp.actions.completions.autoCompletionDotExpression import org.kotlinlsp.actions.completions.autoCompletionGeneric +import org.kotlinlsp.common.info import org.kotlinlsp.index.Index import org.kotlinlsp.index.db.Declaration @@ -25,6 +26,8 @@ fun autocompleteAction(ktFile: KtFile, offset: Int, index: Index): List() ?: ktFile + info("Completing at $offset with prefix '$prefix', element type: ${completingElement.javaClass.simpleName}") + if (completingElement is KtNameReferenceExpression) { if (completingElement.parent is KtDotQualifiedExpression) { return completeDotQualified(ktFile, offset, index, completingElement.parent as KtDotQualifiedExpression, prefix) @@ -33,6 +36,11 @@ fun autocompleteAction(ktFile: KtFile, offset: Int, index: Index): List false + is Declaration.Function -> it.receiverFqName == receiverType + is Declaration.Field -> it.parentFqName == receiverType + is Declaration.EnumEntry -> false + } + } .mapNotNull { decl -> val additionalEdits = mutableListOf() diff --git a/app/src/main/kotlin/org/kotlinlsp/actions/completions/GenericCompletion.kt b/app/src/main/kotlin/org/kotlinlsp/actions/completions/GenericCompletion.kt index 0e4c8d9..31581d5 100644 --- a/app/src/main/kotlin/org/kotlinlsp/actions/completions/GenericCompletion.kt +++ b/app/src/main/kotlin/org/kotlinlsp/actions/completions/GenericCompletion.kt @@ -85,7 +85,15 @@ fun autoCompletionGeneric(ktFile: KtFile, offset: Int, index: Index, completingE } val importInsertionPosition = StringUtil.offsetToLineColumn(ktFile.text, importInsertionOffset).let { Position(it.line, it.column) } - val completions = index.getCompletions(prefix, "", "") // TODO: ThisRef + val completions = index.getCompletions(prefix) // TODO: ThisRef + .filter { + when (it) { + is Declaration.Class -> true + is Declaration.Field -> it.static + is Declaration.Function -> it.static + is Declaration.EnumEntry -> true + } + } .mapNotNull { decl -> val additionalEdits = mutableListOf() diff --git a/app/src/main/kotlin/org/kotlinlsp/index/db/Database.kt b/app/src/main/kotlin/org/kotlinlsp/index/db/Database.kt index 10b072a..c6dd927 100644 --- a/app/src/main/kotlin/org/kotlinlsp/index/db/Database.kt +++ b/app/src/main/kotlin/org/kotlinlsp/index/db/Database.kt @@ -9,7 +9,7 @@ import org.kotlinlsp.index.db.adapters.put import java.io.File import kotlin.io.path.absolutePathString -const val CURRENT_SCHEMA_VERSION = 4 // Increment on schema changes +const val CURRENT_SCHEMA_VERSION = 5 // Increment on schema changes const val VERSION_KEY = "__version" class Database(rootFolder: String) { diff --git a/app/src/main/kotlin/org/kotlinlsp/index/db/Declaration.kt b/app/src/main/kotlin/org/kotlinlsp/index/db/Declaration.kt index fc5f880..2e582f5 100644 --- a/app/src/main/kotlin/org/kotlinlsp/index/db/Declaration.kt +++ b/app/src/main/kotlin/org/kotlinlsp/index/db/Declaration.kt @@ -2,7 +2,6 @@ package org.kotlinlsp.index.db import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import org.kotlinlsp.common.info import org.kotlinlsp.index.db.adapters.put @Serializable @@ -24,6 +23,7 @@ sealed class Declaration() { val returnType: String, val parentFqName: String, val receiverFqName: String, + val static: Boolean, ) : Declaration() { @Serializable data class Parameter( @@ -73,6 +73,8 @@ sealed class Declaration() { override val endOffset: Int, val type: String, val parentFqName: String, + val receiverFqName: String, + val static: Boolean, ) : Declaration() } diff --git a/app/src/main/kotlin/org/kotlinlsp/index/queries/Completions.kt b/app/src/main/kotlin/org/kotlinlsp/index/queries/Completions.kt index 910abad..ee7bbff 100644 --- a/app/src/main/kotlin/org/kotlinlsp/index/queries/Completions.kt +++ b/app/src/main/kotlin/org/kotlinlsp/index/queries/Completions.kt @@ -7,15 +7,7 @@ import org.kotlinlsp.index.Index import org.kotlinlsp.index.db.Declaration @OptIn(ExperimentalSerializationApi::class) -fun Index.getCompletions(prefix: String, thisRef: String, receiver: String): Sequence = query { +fun Index.getCompletions(prefix: String): Sequence = query { it.declarationsDb.prefixSearch(prefix) .map { ProtoBuf.decodeFromByteArray(it.second) } - .filter { - when (it) { - is Declaration.Function -> it.receiverFqName == receiver || it.receiverFqName.isEmpty() - is Declaration.Class -> true - is Declaration.EnumEntry -> true - is Declaration.Field -> it.parentFqName == receiver || it.parentFqName.isEmpty() - } - } } diff --git a/app/src/main/kotlin/org/kotlinlsp/index/worker/IndexKtFile.kt b/app/src/main/kotlin/org/kotlinlsp/index/worker/IndexKtFile.kt index f915d9c..2dc3558 100644 --- a/app/src/main/kotlin/org/kotlinlsp/index/worker/IndexKtFile.kt +++ b/app/src/main/kotlin/org/kotlinlsp/index/worker/IndexKtFile.kt @@ -4,8 +4,13 @@ import com.intellij.openapi.project.Project import com.intellij.psi.util.parentOfType import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isAbstract +import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration +import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral import org.kotlinlsp.common.read import org.kotlinlsp.common.warn import org.kotlinlsp.index.db.* @@ -74,9 +79,13 @@ private fun KaSession.analyzeDeclaration(path: String, dcl: KtDeclaration): Decl return when (dcl) { is KtNamedFunction -> { - val parentFqName = if (dcl.parent is KtClassBody) { - (dcl.parent.parent as? KtClassOrObject)?.fqName?.asString() ?: "" - } else "" + // Local functions are not indexed, they are handled using the analysis API + if (dcl.isLocal) return null + + val container = if (dcl.parent is KtClassBody) { + dcl.parentOfType() + } else null + val parentFqName = container?.classSymbol?.defaultType?.toString() ?: "" Declaration.Function( name, @@ -92,7 +101,8 @@ private fun KaSession.analyzeDeclaration(path: String, dcl: KtDeclaration): Decl }, dcl.returnType.toString(), parentFqName, - dcl.receiverTypeReference?.type?.toString() ?: "" + dcl.receiverTypeReference?.type?.toString() ?: "", + container == null || container is KtObjectDeclaration ) } @@ -104,7 +114,7 @@ private fun KaSession.analyzeDeclaration(path: String, dcl: KtDeclaration): Decl path, startOffset, endOffset, - dcl.parentOfType()?.fqName?.asString() ?: "" + dcl.parentOfType()?.getClassId()?.asString() ?: "" ) } @@ -123,7 +133,7 @@ private fun KaSession.analyzeDeclaration(path: String, dcl: KtDeclaration): Decl Declaration.Class( name, type, - dcl.fqName?.asString() ?: "", + dcl.getClassId()?.asString() ?: "", path, startOffset, endOffset @@ -134,38 +144,52 @@ private fun KaSession.analyzeDeclaration(path: String, dcl: KtDeclaration): Decl if (!dcl.hasValOrVar()) return null val constructor = dcl.parentOfType() ?: return null val clazz = constructor.parentOfType() ?: return null + val classId = clazz.getClassId() ?: return null + val callableId = CallableId(classId, dcl.nameAsSafeName) Declaration.Field( name, - dcl.fqName?.asString() ?: "", + callableId.toString(), path, startOffset, endOffset, dcl.returnType.toString(), - clazz.fqName?.asString() ?: "" + clazz.getClassId()?.asString() ?: "", + "", + false, ) } is KtProperty -> { if (dcl.isLocal) return null - val clazz = dcl.parentOfType() ?: return Declaration.Field( + + val receiver = if (dcl.isExtensionDeclaration()) dcl.receiverTypeReference?.type?.toString() ?: "" else "" + + val clazz = dcl.parentOfType() ?: return Declaration.Field( name, - dcl.fqName?.asString() ?: "", + CallableId(dcl.containingKtFile.packageFqName, dcl.nameAsSafeName).toString(), path, startOffset, endOffset, dcl.returnType.toString(), - "" + "", + receiver, + true, ) + val classId = clazz.getClassId() ?: return null + val callableId = CallableId(classId, dcl.nameAsSafeName) + Declaration.Field( name, - dcl.fqName?.asString() ?: "", + callableId.toString(), path, startOffset, endOffset, dcl.returnType.toString(), - clazz.fqName?.asString() ?: "" + clazz.getClassId()?.asString() ?: "", + receiver, + clazz is KtObjectDeclaration ) } diff --git a/app/test-projects/basic/test.kt b/app/test-projects/basic/test.kt new file mode 100644 index 0000000..8c42293 --- /dev/null +++ b/app/test-projects/basic/test.kt @@ -0,0 +1,45 @@ +package app.ultradev.divineprison.util + +import app.ultradev.divineprison.util.TestClass + +class TestClass( + val name: String, +) { + val test: String = "test" + + class TestContainedClass( + val innerTest: String = "innerTest" + ) + inner class TestInnerClass( + val innerTest: String = "innerTest" + ) + + companion object { + val testCompanion: String = "testCompanion" + + val String.test get() = this + "test" + } +} + +val GLOBAL_TEST = "test" + +object Test { + val test: String = "test" +} + +fun test() { + val testList = listOf(1, 2, 3, 4, 5) + val testClass = TestClass("test") + + testList.forEach { + println(it) + } + + println(testClass.name) + + TestClass.TestInnerClass + + for (i in 1..10) { + println(i) + } +} \ No newline at end of file