diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt index 09950ffdf75..83129670e14 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt @@ -85,7 +85,7 @@ private object KotlinFirCompletionProvider : CompletionProvider !name.isSpecial && prefixMatcher.prefixMatches(name.identifier) } + private val shouldCompleteTopLevelCallablesFromIndex: Boolean + get() = prefixMatcher.prefix.isNotEmpty() + private fun KtAnalysisSession.addSymbolToCompletion(completionResultSet: CompletionResultSet, expectedType: KtType?, symbol: KtSymbol) { if (symbol !is KtNamedSymbol) return with(lookupElementFactory) { @@ -199,6 +202,14 @@ private class KotlinCommonCompletionProvider( availableNonExtensions.forEach { addSymbolToCompletion(result, expectedType, it) } extensionsWhichCanBeCalled.forEach { addSymbolToCompletion(result, expectedType, it) } + if (shouldCompleteTopLevelCallablesFromIndex) { + val topLevelCallables = indexHelper.getTopLevelCallables(scopeNameFilter) + topLevelCallables.asSequence() + .map { it.getSymbol() } + .forEach { addSymbolToCompletion(result, expectedType, it) } + } + + collectTypesCompletion(result, implicitScopes, expectedType) } } \ No newline at end of file diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/IndexHelper.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/IndexHelper.kt index ede4ffcb5e8..4f70196afe2 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/IndexHelper.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/IndexHelper.kt @@ -9,6 +9,7 @@ import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import com.intellij.psi.search.GlobalSearchScope +import com.intellij.psi.stubs.StringStubIndexExtension import com.intellij.psi.stubs.StubIndex import com.intellij.psi.stubs.StubIndexKey import org.jetbrains.kotlin.name.CallableId @@ -16,10 +17,7 @@ import org.jetbrains.kotlin.idea.stubindex.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtClassOrObject -import org.jetbrains.kotlin.psi.KtFunction -import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.* /* * Move to another module @@ -86,6 +84,20 @@ public class IndexHelper(val project: Project, private val scope: GlobalSearchSc .toList() } + fun getTopLevelCallables(nameFilter: (Name) -> Boolean): Collection { + fun sequenceOfElements(index: StringStubIndexExtension): Sequence = + index.getAllKeys(project).asSequence() + .onEach { ProgressManager.checkCanceled() } + .filter { fqName -> nameFilter(getShortName(fqName)) } + .flatMap { fqName -> index[fqName, project, scope] } + .filter { it.receiverTypeReference == null } + + val functions = sequenceOfElements(KotlinTopLevelFunctionFqnNameIndex.getInstance()) + val properties = sequenceOfElements(KotlinTopLevelPropertyFqnNameIndex.getInstance()) + + return (functions + properties).toList() + } + companion object { private fun CallableId.asStringForIndexes(): String = (if (packageName.isRoot) callableName.asString() else toString()).replace('/', '.')