FIR IDE: implement completion for variables with functional types with receivers
This commit is contained in:
committed by
teamcityserver
parent
167917cf07
commit
915c8b7996
-1
@@ -6,4 +6,3 @@ fun C.test(foo: C.() -> Unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EXIST: { lookupString: "foo", itemText: "foo", tailText: null, typeText: "C.() -> Unit" }
|
// EXIST: { lookupString: "foo", itemText: "foo", tailText: null, typeText: "C.() -> Unit" }
|
||||||
// ABSENT: { itemText: "foo", typeText: "Unit" }
|
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
interface I
|
interface I
|
||||||
interface J
|
interface J
|
||||||
|
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
fun String.foo() {
|
fun String.foo() {
|
||||||
val v = ::xxx_<caret>
|
val v = ::xxx_<caret>
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
fun test(i: Int, foo: Int.() -> Char) {
|
fun test(i: Int, foo: Int.() -> Char) {
|
||||||
i.<caret>
|
i.<caret>
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
fun test(i: Int, foo: Int.() -> Char) {
|
fun test(i: Int, foo: Int.() -> Char) {
|
||||||
i.foo()<caret>
|
i.foo()<caret>
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
fun test(i: Int, foo: Int.(String) -> Char) {
|
fun test(i: Int, foo: Int.(String) -> Char) {
|
||||||
i.<caret>
|
i.<caret>
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
fun test(i: Int, foo: Int.(String) -> Char) {
|
fun test(i: Int, foo: Int.(String) -> Char) {
|
||||||
i.foo(<caret>)
|
i.foo(<caret>)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -6,8 +6,9 @@
|
|||||||
package org.jetbrains.kotlin.idea.completion.checkers
|
package org.jetbrains.kotlin.idea.completion.checkers
|
||||||
|
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtExtensionApplicabilityResult
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||||
|
|
||||||
internal fun interface ExtensionApplicabilityChecker {
|
internal fun interface ExtensionApplicabilityChecker {
|
||||||
fun KtAnalysisSession.isApplicable(symbol: KtCallableSymbol): Boolean
|
fun KtAnalysisSession.isApplicable(symbol: KtCallableSymbol): KtExtensionApplicabilityResult
|
||||||
}
|
}
|
||||||
+76
-20
@@ -10,24 +10,49 @@ import org.jetbrains.kotlin.idea.completion.checkers.ExtensionApplicabilityCheck
|
|||||||
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
||||||
import org.jetbrains.kotlin.idea.completion.context.FirNameReferencePositionContext
|
import org.jetbrains.kotlin.idea.completion.context.FirNameReferencePositionContext
|
||||||
import org.jetbrains.kotlin.idea.completion.contributors.helpers.insertSymbolAndInvokeCompletion
|
import org.jetbrains.kotlin.idea.completion.contributors.helpers.insertSymbolAndInvokeCompletion
|
||||||
|
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionOptions
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionStrategy
|
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionStrategy
|
||||||
import org.jetbrains.kotlin.idea.fir.HLIndexHelper
|
import org.jetbrains.kotlin.idea.fir.HLIndexHelper
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtExtensionApplicabilityResult
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtScopeContext
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtScopeContext
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.scopes.KtCompositeScope
|
import org.jetbrains.kotlin.idea.frontend.api.scopes.KtCompositeScope
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.scopes.KtScope
|
import org.jetbrains.kotlin.idea.frontend.api.scopes.KtScope
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType
|
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtNonErrorClassType
|
import org.jetbrains.kotlin.idea.frontend.api.types.KtNonErrorClassType
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.types.KtFunctionalType
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
import org.jetbrains.kotlin.idea.completion.lookups.detectImportStrategy
|
||||||
|
|
||||||
internal open class FirCallableCompletionContributor(
|
internal open class FirCallableCompletionContributor(
|
||||||
basicContext: FirBasicCompletionContext,
|
basicContext: FirBasicCompletionContext,
|
||||||
) : FirContextCompletionContributorBase<FirNameReferencePositionContext>(basicContext) {
|
) : FirContextCompletionContributorBase<FirNameReferencePositionContext>(basicContext) {
|
||||||
private val typeNamesProvider = TypeNamesProvider(indexHelper)
|
private val typeNamesProvider = TypeNamesProvider(indexHelper)
|
||||||
|
|
||||||
protected open val insertionStrategy: CallableInsertionStrategy = CallableInsertionStrategy.AsCall
|
protected open fun KtAnalysisSession.getInsertionStrategy(symbol: KtCallableSymbol): CallableInsertionStrategy = when (symbol) {
|
||||||
|
is KtFunctionLikeSymbol -> CallableInsertionStrategy.AsCall
|
||||||
|
else -> CallableInsertionStrategy.AsIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun KtAnalysisSession.getInsertionStrategyForExtensionFunction(
|
||||||
|
symbol: KtCallableSymbol,
|
||||||
|
applicabilityResult: KtExtensionApplicabilityResult
|
||||||
|
): CallableInsertionStrategy? = when (applicabilityResult) {
|
||||||
|
KtExtensionApplicabilityResult.ApplicableAsExtensionCallable -> getInsertionStrategy(symbol)
|
||||||
|
KtExtensionApplicabilityResult.ApplicableAsFunctionalVariableCall -> CallableInsertionStrategy.AsCall
|
||||||
|
KtExtensionApplicabilityResult.NonApplicable -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun KtAnalysisSession.getOptions(symbol: KtCallableSymbol): CallableInsertionOptions =
|
||||||
|
CallableInsertionOptions(detectImportStrategy(symbol), getInsertionStrategy(symbol))
|
||||||
|
|
||||||
|
private fun KtAnalysisSession.getExtensionOptions(
|
||||||
|
symbol: KtCallableSymbol,
|
||||||
|
applicability: KtExtensionApplicabilityResult
|
||||||
|
): CallableInsertionOptions? =
|
||||||
|
getInsertionStrategyForExtensionFunction(symbol, applicability)?.let { CallableInsertionOptions(detectImportStrategy(symbol), it) }
|
||||||
|
|
||||||
protected open fun KtAnalysisSession.filter(symbol: KtCallableSymbol): Boolean = true
|
protected open fun KtAnalysisSession.filter(symbol: KtCallableSymbol): Boolean = true
|
||||||
|
|
||||||
@@ -73,19 +98,21 @@ internal open class FirCallableCompletionContributor(
|
|||||||
val availableNonExtensions = collectNonExtensions(implicitScopes, visibilityChecker)
|
val availableNonExtensions = collectNonExtensions(implicitScopes, visibilityChecker)
|
||||||
val extensionsWhichCanBeCalled = collectSuitableExtensions(implicitScopes, extensionChecker, visibilityChecker)
|
val extensionsWhichCanBeCalled = collectSuitableExtensions(implicitScopes, extensionChecker, visibilityChecker)
|
||||||
|
|
||||||
availableNonExtensions.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
|
availableNonExtensions.forEach { addCallableSymbolToCompletion(expectedType, it, getOptions(it)) }
|
||||||
extensionsWhichCanBeCalled.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
|
extensionsWhichCanBeCalled.forEach { (symbol, applicable) ->
|
||||||
|
getExtensionOptions(symbol, applicable)?.let { addCallableSymbolToCompletion(expectedType, symbol, it) }
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldCompleteTopLevelCallablesFromIndex) {
|
if (shouldCompleteTopLevelCallablesFromIndex) {
|
||||||
val topLevelCallables = indexHelper.getTopLevelCallables(scopeNameFilter)
|
val topLevelCallables = indexHelper.getTopLevelCallables(scopeNameFilter)
|
||||||
topLevelCallables.asSequence()
|
topLevelCallables.asSequence()
|
||||||
.map { it.getSymbol() as KtCallableSymbol }
|
.map { it.getSymbol() as KtCallableSymbol }
|
||||||
.filter { with(visibilityChecker) { isVisible(it) } }
|
.filter { with(visibilityChecker) { isVisible(it) } }
|
||||||
.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
|
.forEach { addCallableSymbolToCompletion(expectedType, it, getOptions(it)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
collectTopLevelExtensionsFromIndices(implicitReceiversTypes, extensionChecker, visibilityChecker)
|
collectTopLevelExtensionsFromIndices(implicitReceiversTypes, extensionChecker, visibilityChecker)
|
||||||
.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
|
.forEach { addCallableSymbolToCompletion(expectedType, it, getOptions(it)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun KtAnalysisSession.collectDotCompletion(
|
protected open fun KtAnalysisSession.collectDotCompletion(
|
||||||
@@ -118,7 +145,7 @@ internal open class FirCallableCompletionContributor(
|
|||||||
.filter { with(visibilityChecker) { isVisible(it) } }
|
.filter { with(visibilityChecker) { isVisible(it) } }
|
||||||
.filter { filter(it) }
|
.filter { filter(it) }
|
||||||
.forEach { callable ->
|
.forEach { callable ->
|
||||||
addCallableSymbolToCompletion(expectedType, callable, insertionStrategy = insertionStrategy)
|
addCallableSymbolToCompletion(expectedType, callable, getOptions(callable))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,16 +163,15 @@ internal open class FirCallableCompletionContributor(
|
|||||||
val extensionNonMembers = collectSuitableExtensions(implicitScopes, extensionChecker, visibilityChecker)
|
val extensionNonMembers = collectSuitableExtensions(implicitScopes, extensionChecker, visibilityChecker)
|
||||||
|
|
||||||
nonExtensionMembers
|
nonExtensionMembers
|
||||||
.filter { filter(it) }
|
.forEach { addCallableSymbolToCompletion(expectedType, it, getOptions(it)) }
|
||||||
.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
|
|
||||||
|
|
||||||
extensionNonMembers
|
extensionNonMembers.forEach { (symbol, applicability) ->
|
||||||
.filter { filter(it) }
|
getExtensionOptions(symbol, applicability)?.let { addCallableSymbolToCompletion(expectedType, symbol, it) }
|
||||||
.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
|
}
|
||||||
|
|
||||||
collectTopLevelExtensionsFromIndices(listOf(typeOfPossibleReceiver), extensionChecker, visibilityChecker)
|
collectTopLevelExtensionsFromIndices(listOf(typeOfPossibleReceiver), extensionChecker, visibilityChecker)
|
||||||
.filter { filter(it) }
|
.filter { filter(it) }
|
||||||
.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
|
.forEach { addCallableSymbolToCompletion(expectedType, it, getOptions(it)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtAnalysisSession.collectTopLevelExtensionsFromIndices(
|
private fun KtAnalysisSession.collectTopLevelExtensionsFromIndices(
|
||||||
@@ -160,7 +186,7 @@ internal open class FirCallableCompletionContributor(
|
|||||||
.map { it.getSymbol() as KtCallableSymbol }
|
.map { it.getSymbol() as KtCallableSymbol }
|
||||||
.filter { filter(it) }
|
.filter { filter(it) }
|
||||||
.filter { with(visibilityChecker) { isVisible(it) } }
|
.filter { with(visibilityChecker) { isVisible(it) } }
|
||||||
.filter { with(extensionChecker) { isApplicable(it) } }
|
.filter { with(extensionChecker) { isApplicable(it).isApplicable } }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtAnalysisSession.collectNonExtensions(scope: KtScope, visibilityChecker: CompletionVisibilityChecker) =
|
private fun KtAnalysisSession.collectNonExtensions(scope: KtScope, visibilityChecker: CompletionVisibilityChecker) =
|
||||||
@@ -173,12 +199,17 @@ internal open class FirCallableCompletionContributor(
|
|||||||
scope: KtCompositeScope,
|
scope: KtCompositeScope,
|
||||||
hasSuitableExtensionReceiver: ExtensionApplicabilityChecker,
|
hasSuitableExtensionReceiver: ExtensionApplicabilityChecker,
|
||||||
visibilityChecker: CompletionVisibilityChecker,
|
visibilityChecker: CompletionVisibilityChecker,
|
||||||
): Sequence<KtCallableSymbol> =
|
): Sequence<Pair<KtCallableSymbol, KtExtensionApplicabilityResult>> =
|
||||||
scope.getCallableSymbols(scopeNameFilter)
|
scope.getCallableSymbols(scopeNameFilter)
|
||||||
.filter { it.isExtension }
|
.filter { it.isExtension || it is KtVariableLikeSymbol && (it.annotatedType.type as? KtFunctionalType)?.hasReceiver == true }
|
||||||
.filter { with(visibilityChecker) { isVisible(it) } }
|
.filter { with(visibilityChecker) { isVisible(it) } }
|
||||||
.filter { filter(it) }
|
.filter { filter(it) }
|
||||||
.filter { with(hasSuitableExtensionReceiver) { isApplicable(it) } }
|
.mapNotNull { callable ->
|
||||||
|
val applicabilityResult = with(hasSuitableExtensionReceiver) { isApplicable(callable) }
|
||||||
|
if (applicabilityResult.isApplicable) {
|
||||||
|
callable to applicabilityResult
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
|
||||||
private fun KtAnalysisSession.findAllNamesOfTypes(implicitReceiversTypes: List<KtType>) =
|
private fun KtAnalysisSession.findAllNamesOfTypes(implicitReceiversTypes: List<KtType>) =
|
||||||
implicitReceiversTypes.flatMapTo(hashSetOf()) { with(typeNamesProvider) { findAllNames(it) } }
|
implicitReceiversTypes.flatMapTo(hashSetOf()) { with(typeNamesProvider) { findAllNames(it) } }
|
||||||
@@ -187,7 +218,18 @@ internal open class FirCallableCompletionContributor(
|
|||||||
internal class FirCallableReferenceCompletionContributor(
|
internal class FirCallableReferenceCompletionContributor(
|
||||||
basicContext: FirBasicCompletionContext
|
basicContext: FirBasicCompletionContext
|
||||||
) : FirCallableCompletionContributor(basicContext) {
|
) : FirCallableCompletionContributor(basicContext) {
|
||||||
override val insertionStrategy: CallableInsertionStrategy = CallableInsertionStrategy.AsIdentifier
|
|
||||||
|
override fun KtAnalysisSession.getInsertionStrategy(symbol: KtCallableSymbol): CallableInsertionStrategy =
|
||||||
|
CallableInsertionStrategy.AsIdentifier
|
||||||
|
|
||||||
|
override fun KtAnalysisSession.getInsertionStrategyForExtensionFunction(
|
||||||
|
symbol: KtCallableSymbol,
|
||||||
|
applicabilityResult: KtExtensionApplicabilityResult
|
||||||
|
): CallableInsertionStrategy? = when (applicabilityResult) {
|
||||||
|
KtExtensionApplicabilityResult.ApplicableAsExtensionCallable -> CallableInsertionStrategy.AsIdentifier
|
||||||
|
KtExtensionApplicabilityResult.ApplicableAsFunctionalVariableCall -> null
|
||||||
|
KtExtensionApplicabilityResult.NonApplicable -> null
|
||||||
|
}
|
||||||
|
|
||||||
override fun KtAnalysisSession.collectDotCompletion(
|
override fun KtAnalysisSession.collectDotCompletion(
|
||||||
implicitScopes: KtCompositeScope,
|
implicitScopes: KtCompositeScope,
|
||||||
@@ -203,7 +245,7 @@ internal class FirCallableReferenceCompletionContributor(
|
|||||||
.getCallableSymbols(scopeNameFilter)
|
.getCallableSymbols(scopeNameFilter)
|
||||||
.filter { with(visibilityChecker) { isVisible(it) } }
|
.filter { with(visibilityChecker) { isVisible(it) } }
|
||||||
.forEach { symbol ->
|
.forEach { symbol ->
|
||||||
addCallableSymbolToCompletion(expectedType = null, symbol, insertionStrategy = insertionStrategy)
|
addCallableSymbolToCompletion(expectedType = null, symbol, getOptions(symbol))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -217,13 +259,27 @@ internal class FirCallableReferenceCompletionContributor(
|
|||||||
internal class FirInfixCallableCompletionContributor(
|
internal class FirInfixCallableCompletionContributor(
|
||||||
basicContext: FirBasicCompletionContext
|
basicContext: FirBasicCompletionContext
|
||||||
) : FirCallableCompletionContributor(basicContext) {
|
) : FirCallableCompletionContributor(basicContext) {
|
||||||
override val insertionStrategy: CallableInsertionStrategy = CallableInsertionStrategy.AsIdentifierCustom {
|
override fun KtAnalysisSession.getInsertionStrategy(symbol: KtCallableSymbol): CallableInsertionStrategy =
|
||||||
insertSymbolAndInvokeCompletion(" ")
|
insertionStrategy
|
||||||
|
|
||||||
|
override fun KtAnalysisSession.getInsertionStrategyForExtensionFunction(
|
||||||
|
symbol: KtCallableSymbol,
|
||||||
|
applicabilityResult: KtExtensionApplicabilityResult
|
||||||
|
): CallableInsertionStrategy? = when (applicabilityResult) {
|
||||||
|
KtExtensionApplicabilityResult.ApplicableAsExtensionCallable -> getInsertionStrategy(symbol)
|
||||||
|
KtExtensionApplicabilityResult.ApplicableAsFunctionalVariableCall -> null
|
||||||
|
KtExtensionApplicabilityResult.NonApplicable -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun KtAnalysisSession.filter(symbol: KtCallableSymbol): Boolean {
|
override fun KtAnalysisSession.filter(symbol: KtCallableSymbol): Boolean {
|
||||||
return symbol is KtFunctionSymbol && symbol.isInfix
|
return symbol is KtFunctionSymbol && symbol.isInfix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val insertionStrategy = CallableInsertionStrategy.AsIdentifierCustom {
|
||||||
|
insertSymbolAndInvokeCompletion(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TypeNamesProvider(private val indexHelper: HLIndexHelper) {
|
private class TypeNamesProvider(private val indexHelper: HLIndexHelper) {
|
||||||
|
|||||||
+4
-4
@@ -12,8 +12,9 @@ import com.intellij.openapi.project.Project
|
|||||||
import org.jetbrains.kotlin.idea.completion.LookupElementSink
|
import org.jetbrains.kotlin.idea.completion.LookupElementSink
|
||||||
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
||||||
import org.jetbrains.kotlin.idea.completion.context.FirRawPositionCompletionContext
|
import org.jetbrains.kotlin.idea.completion.context.FirRawPositionCompletionContext
|
||||||
|
import org.jetbrains.kotlin.idea.completion.lookups.*
|
||||||
|
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionOptions
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
|
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionStrategy
|
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.detectImportStrategy
|
import org.jetbrains.kotlin.idea.completion.lookups.detectImportStrategy
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.factories.KotlinFirLookupElementFactory
|
import org.jetbrains.kotlin.idea.completion.lookups.factories.KotlinFirLookupElementFactory
|
||||||
import org.jetbrains.kotlin.idea.completion.weighers.Weighers
|
import org.jetbrains.kotlin.idea.completion.weighers.Weighers
|
||||||
@@ -71,12 +72,11 @@ internal abstract class FirCompletionContributorBase<C : FirRawPositionCompletio
|
|||||||
protected fun KtAnalysisSession.addCallableSymbolToCompletion(
|
protected fun KtAnalysisSession.addCallableSymbolToCompletion(
|
||||||
expectedType: KtType?,
|
expectedType: KtType?,
|
||||||
symbol: KtCallableSymbol,
|
symbol: KtCallableSymbol,
|
||||||
importingStrategy: ImportStrategy = detectImportStrategy(symbol),
|
options: CallableInsertionOptions,
|
||||||
insertionStrategy: CallableInsertionStrategy,
|
|
||||||
) {
|
) {
|
||||||
if (symbol !is KtNamedSymbol) return
|
if (symbol !is KtNamedSymbol) return
|
||||||
val lookup = with(lookupElementFactory) {
|
val lookup = with(lookupElementFactory) {
|
||||||
createCallableLookupElement(symbol, importingStrategy, insertionStrategy)
|
createCallableLookupElement(symbol, options)
|
||||||
}
|
}
|
||||||
applyWeighers(lookup, symbol, expectedType)
|
applyWeighers(lookup, symbol, expectedType)
|
||||||
sink.addElement(lookup)
|
sink.addElement(lookup)
|
||||||
|
|||||||
+2
-2
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.idea.completion.checkers.CompletionVisibilityChecker
|
|||||||
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
||||||
import org.jetbrains.kotlin.idea.completion.context.FirImportDirectivePositionContext
|
import org.jetbrains.kotlin.idea.completion.context.FirImportDirectivePositionContext
|
||||||
import org.jetbrains.kotlin.idea.completion.contributors.helpers.getStaticScope
|
import org.jetbrains.kotlin.idea.completion.contributors.helpers.getStaticScope
|
||||||
|
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionOptions
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
|
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionStrategy
|
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionStrategy
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
@@ -31,8 +32,7 @@ internal class FirImportDirectivePackageMembersCompletionContributor(
|
|||||||
addCallableSymbolToCompletion(
|
addCallableSymbolToCompletion(
|
||||||
expectedType = null,
|
expectedType = null,
|
||||||
it,
|
it,
|
||||||
importingStrategy = ImportStrategy.DoNothing,
|
CallableInsertionOptions(ImportStrategy.DoNothing, CallableInsertionStrategy.AsIdentifier)
|
||||||
insertionStrategy = CallableInsertionStrategy.AsIdentifier
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.completion.lookups
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
|
||||||
|
|
||||||
|
internal data class CallableInsertionOptions(
|
||||||
|
val importingStrategy: ImportStrategy,
|
||||||
|
val insertionStrategy: CallableInsertionStrategy,
|
||||||
|
) {
|
||||||
|
fun withImportingStrategy(newImportStrategy: ImportStrategy): CallableInsertionOptions =
|
||||||
|
copy(importingStrategy = newImportStrategy)
|
||||||
|
|
||||||
|
fun withInsertionStrategy(newInsertionStrategy: CallableInsertionStrategy): CallableInsertionOptions =
|
||||||
|
copy(insertionStrategy = newInsertionStrategy)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun KtAnalysisSession.detectCallableOptions(symbol: KtCallableSymbol): CallableInsertionOptions = CallableInsertionOptions(
|
||||||
|
importingStrategy = detectImportStrategy(symbol),
|
||||||
|
insertionStrategy = when (symbol) {
|
||||||
|
is KtFunctionSymbol -> CallableInsertionStrategy.AsCall
|
||||||
|
else -> CallableInsertionStrategy.AsIdentifier
|
||||||
|
}
|
||||||
|
)
|
||||||
+16
-2
@@ -7,12 +7,26 @@ package org.jetbrains.kotlin.idea.completion.lookups
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeRendererOptions
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeRendererOptions
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtValueParameterSymbol
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtValueParameterSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtVariableLikeSymbol
|
||||||
|
|
||||||
internal object CompletionShortNamesRenderer {
|
internal object CompletionShortNamesRenderer {
|
||||||
fun KtAnalysisSession.renderFunctionParameters(function: KtFunctionSymbol): String =
|
fun KtAnalysisSession.renderFunctionParameters(function: KtFunctionSymbol): String {
|
||||||
function.valueParameters.joinToString(", ", "(", ")") { renderFunctionParameter(it) }
|
val receiver = renderReceiver(function)
|
||||||
|
val parameters = function.valueParameters.joinToString(", ", "(", ")") { renderFunctionParameter(it) }
|
||||||
|
return receiver + parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtAnalysisSession.renderVariable(function: KtVariableLikeSymbol): String {
|
||||||
|
return renderReceiver(function)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtAnalysisSession.renderReceiver(symbol: KtCallableSymbol): String {
|
||||||
|
val receiverType = symbol.receiverType?.type ?: return ""
|
||||||
|
return receiverType.render(TYPE_RENDERING_OPTIONS) + "."
|
||||||
|
}
|
||||||
|
|
||||||
private fun KtAnalysisSession.renderFunctionParameter(param: KtValueParameterSymbol): String =
|
private fun KtAnalysisSession.renderFunctionParameter(param: KtValueParameterSymbol): String =
|
||||||
"${if (param.isVararg) "vararg " else ""}${param.name.asString()}: ${param.annotatedType.type.render(TYPE_RENDERING_OPTIONS)}"
|
"${if (param.isVararg) "vararg " else ""}${param.name.asString()}: ${param.annotatedType.type.render(TYPE_RENDERING_OPTIONS)}"
|
||||||
|
|||||||
+3
-2
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.completion.lookups
|
package org.jetbrains.kotlin.idea.completion.lookups
|
||||||
|
|
||||||
abstract class KotlinCallableLookupObject: KotlinLookupObject {
|
internal abstract class KotlinCallableLookupObject : KotlinLookupObject {
|
||||||
abstract val renderedReceiverType: String?
|
abstract val renderedDeclaration: String
|
||||||
|
abstract val options: CallableInsertionOptions
|
||||||
}
|
}
|
||||||
@@ -31,4 +31,9 @@ internal object TailTextProvider {
|
|||||||
val singleParam = symbol.valueParameters.singleOrNull()
|
val singleParam = symbol.valueParameters.singleOrNull()
|
||||||
return singleParam != null && !singleParam.hasDefaultValue && singleParam.annotatedType.type is KtFunctionalType
|
return singleParam != null && !singleParam.hasDefaultValue && singleParam.annotatedType.type is KtFunctionalType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun KtAnalysisSession.insertLambdaBraces(symbol: KtFunctionalType): Boolean {
|
||||||
|
val singleParam = symbol.parameterTypes.singleOrNull()
|
||||||
|
return singleParam != null && singleParam is KtFunctionalType
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+16
-21
@@ -32,23 +32,22 @@ import org.jetbrains.kotlin.idea.completion.lookups.TailTextProvider.insertLambd
|
|||||||
import org.jetbrains.kotlin.idea.completion.lookups.CompletionShortNamesRenderer.renderFunctionParameters
|
import org.jetbrains.kotlin.idea.completion.lookups.CompletionShortNamesRenderer.renderFunctionParameters
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.CompletionShortNamesRenderer.TYPE_RENDERING_OPTIONS
|
import org.jetbrains.kotlin.idea.completion.lookups.CompletionShortNamesRenderer.TYPE_RENDERING_OPTIONS
|
||||||
import org.jetbrains.kotlin.idea.core.withRootPrefixIfNeeded
|
import org.jetbrains.kotlin.idea.core.withRootPrefixIfNeeded
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtDeclarationRendererOptions
|
||||||
|
|
||||||
internal class FunctionLookupElementFactory {
|
internal class FunctionLookupElementFactory {
|
||||||
fun KtAnalysisSession.createLookup(
|
fun KtAnalysisSession.createLookup(
|
||||||
symbol: KtFunctionSymbol,
|
symbol: KtFunctionSymbol,
|
||||||
importStrategy: ImportStrategy,
|
options: CallableInsertionOptions,
|
||||||
insertionStrategy: CallableInsertionStrategy
|
|
||||||
): LookupElementBuilder {
|
): LookupElementBuilder {
|
||||||
val lookupObject = FunctionLookupObject(
|
val lookupObject = FunctionCallLookupObject(
|
||||||
symbol.name,
|
symbol.name,
|
||||||
importStrategy = importStrategy,
|
options,
|
||||||
|
renderFunctionParameters(symbol),
|
||||||
inputValueArguments = symbol.valueParameters.isNotEmpty(),
|
inputValueArguments = symbol.valueParameters.isNotEmpty(),
|
||||||
insertEmptyLambda = insertLambdaBraces(symbol),
|
insertEmptyLambda = insertLambdaBraces(symbol),
|
||||||
renderedFunctionParameters = renderFunctionParameters(symbol),
|
|
||||||
renderedReceiverType = symbol.receiverType?.type?.render(TYPE_RENDERING_OPTIONS)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val insertionHandler = when (insertionStrategy) {
|
val insertionHandler = when (val insertionStrategy = options.insertionStrategy) {
|
||||||
CallableInsertionStrategy.AsCall -> FunctionInsertionHandler
|
CallableInsertionStrategy.AsCall -> FunctionInsertionHandler
|
||||||
CallableInsertionStrategy.AsIdentifier -> QuotedNamesAwareInsertionHandler()
|
CallableInsertionStrategy.AsIdentifier -> QuotedNamesAwareInsertionHandler()
|
||||||
is CallableInsertionStrategy.AsIdentifierCustom -> object : QuotedNamesAwareInsertionHandler() {
|
is CallableInsertionStrategy.AsIdentifierCustom -> object : QuotedNamesAwareInsertionHandler() {
|
||||||
@@ -61,28 +60,24 @@ internal class FunctionLookupElementFactory {
|
|||||||
|
|
||||||
return LookupElementBuilder.create(lookupObject, symbol.name.asString())
|
return LookupElementBuilder.create(lookupObject, symbol.name.asString())
|
||||||
.withTailText(getTailText(symbol), true)
|
.withTailText(getTailText(symbol), true)
|
||||||
.withTypeText(symbol.annotatedType.type.render(CompletionShortNamesRenderer.TYPE_RENDERING_OPTIONS))
|
.withTypeText(symbol.annotatedType.type.render(TYPE_RENDERING_OPTIONS))
|
||||||
.withInsertHandler(insertionHandler)
|
.withInsertHandler(insertionHandler)
|
||||||
.let { withSymbolInfo(symbol, it) }
|
.let { withSymbolInfo(symbol, it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Simplest lookup object so two lookup elements for the same function will clash.
|
internal data class FunctionCallLookupObject(
|
||||||
*/
|
|
||||||
private data class FunctionLookupObject(
|
|
||||||
override val shortName: Name,
|
override val shortName: Name,
|
||||||
val importStrategy: ImportStrategy,
|
override val options: CallableInsertionOptions,
|
||||||
|
override val renderedDeclaration: String,
|
||||||
val inputValueArguments: Boolean,
|
val inputValueArguments: Boolean,
|
||||||
val insertEmptyLambda: Boolean,
|
val insertEmptyLambda: Boolean,
|
||||||
// for distinction between different overloads
|
|
||||||
private val renderedFunctionParameters: String,
|
|
||||||
override val renderedReceiverType: String?
|
|
||||||
) : KotlinCallableLookupObject()
|
) : KotlinCallableLookupObject()
|
||||||
|
|
||||||
|
|
||||||
private object FunctionInsertionHandler : QuotedNamesAwareInsertionHandler() {
|
internal object FunctionInsertionHandler : QuotedNamesAwareInsertionHandler() {
|
||||||
private fun addArguments(context: InsertionContext, offsetElement: PsiElement, lookupObject: FunctionLookupObject) {
|
private fun addArguments(context: InsertionContext, offsetElement: PsiElement, lookupObject: FunctionCallLookupObject) {
|
||||||
val completionChar = context.completionChar
|
val completionChar = context.completionChar
|
||||||
if (completionChar == '(') { //TODO: more correct behavior related to braces type
|
if (completionChar == '(') { //TODO: more correct behavior related to braces type
|
||||||
context.setAddCompletionChar(false)
|
context.setAddCompletionChar(false)
|
||||||
@@ -152,7 +147,7 @@ private object FunctionInsertionHandler : QuotedNamesAwareInsertionHandler() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shouldPlaceCaretInBrackets(completionChar: Char, lookupObject: FunctionLookupObject): Boolean {
|
private fun shouldPlaceCaretInBrackets(completionChar: Char, lookupObject: FunctionCallLookupObject): Boolean {
|
||||||
if (completionChar == ',' || completionChar == '.' || completionChar == '=') return false
|
if (completionChar == ',' || completionChar == '.' || completionChar == '=') return false
|
||||||
if (completionChar == '(') return true
|
if (completionChar == '(') return true
|
||||||
return lookupObject.inputValueArguments || lookupObject.insertEmptyLambda
|
return lookupObject.inputValueArguments || lookupObject.insertEmptyLambda
|
||||||
@@ -163,14 +158,14 @@ private object FunctionInsertionHandler : QuotedNamesAwareInsertionHandler() {
|
|||||||
|
|
||||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||||
val targetFile = context.file as? KtFile ?: return
|
val targetFile = context.file as? KtFile ?: return
|
||||||
val lookupObject = item.`object` as FunctionLookupObject
|
val lookupObject = item.`object` as FunctionCallLookupObject
|
||||||
|
|
||||||
super.handleInsert(context, item)
|
super.handleInsert(context, item)
|
||||||
|
|
||||||
val startOffset = context.startOffset
|
val startOffset = context.startOffset
|
||||||
val element = context.file.findElementAt(startOffset) ?: return
|
val element = context.file.findElementAt(startOffset) ?: return
|
||||||
|
|
||||||
val importStrategy = lookupObject.importStrategy
|
val importStrategy = lookupObject.options.importingStrategy
|
||||||
if (importStrategy is ImportStrategy.InsertFqNameAndShorten) {
|
if (importStrategy is ImportStrategy.InsertFqNameAndShorten) {
|
||||||
context.document.replaceString(
|
context.document.replaceString(
|
||||||
context.startOffset,
|
context.startOffset,
|
||||||
|
|||||||
+6
-7
@@ -7,8 +7,9 @@ package org.jetbrains.kotlin.idea.completion.lookups.factories
|
|||||||
|
|
||||||
import com.intellij.codeInsight.lookup.LookupElement
|
import com.intellij.codeInsight.lookup.LookupElement
|
||||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||||
|
import org.jetbrains.kotlin.idea.completion.lookups.*
|
||||||
|
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionOptions
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
|
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionStrategy
|
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.detectImportStrategy
|
import org.jetbrains.kotlin.idea.completion.lookups.detectImportStrategy
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||||
@@ -26,8 +27,7 @@ internal class KotlinFirLookupElementFactory {
|
|||||||
return when (symbol) {
|
return when (symbol) {
|
||||||
is KtCallableSymbol -> createCallableLookupElement(
|
is KtCallableSymbol -> createCallableLookupElement(
|
||||||
symbol,
|
symbol,
|
||||||
importingStrategy = detectImportStrategy(symbol),
|
detectCallableOptions(symbol),
|
||||||
CallableInsertionStrategy.AsCall
|
|
||||||
)
|
)
|
||||||
is KtClassLikeSymbol -> with(classLookupElementFactory) { createLookup(symbol) }
|
is KtClassLikeSymbol -> with(classLookupElementFactory) { createLookup(symbol) }
|
||||||
is KtTypeParameterSymbol -> with(typeParameterLookupElementFactory) { createLookup(symbol) }
|
is KtTypeParameterSymbol -> with(typeParameterLookupElementFactory) { createLookup(symbol) }
|
||||||
@@ -37,12 +37,11 @@ internal class KotlinFirLookupElementFactory {
|
|||||||
|
|
||||||
fun KtAnalysisSession.createCallableLookupElement(
|
fun KtAnalysisSession.createCallableLookupElement(
|
||||||
symbol: KtCallableSymbol,
|
symbol: KtCallableSymbol,
|
||||||
importingStrategy: ImportStrategy,
|
options: CallableInsertionOptions,
|
||||||
insertionStrategy: CallableInsertionStrategy,
|
|
||||||
): LookupElementBuilder {
|
): LookupElementBuilder {
|
||||||
return when (symbol) {
|
return when (symbol) {
|
||||||
is KtFunctionSymbol -> with(functionLookupElementFactory) { createLookup(symbol, importingStrategy, insertionStrategy) }
|
is KtFunctionSymbol -> with(functionLookupElementFactory) { createLookup(symbol, options) }
|
||||||
is KtVariableLikeSymbol -> with(variableLookupElementFactory) { createLookup(symbol, importingStrategy) }
|
is KtVariableLikeSymbol -> with(variableLookupElementFactory) { createLookup(symbol, options) }
|
||||||
else -> throw IllegalArgumentException("Cannot create a lookup element for $symbol")
|
else -> throw IllegalArgumentException("Cannot create a lookup element for $symbol")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-16
@@ -11,16 +11,18 @@ import com.intellij.codeInsight.lookup.LookupElement
|
|||||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.*
|
import org.jetbrains.kotlin.idea.completion.lookups.*
|
||||||
|
import org.jetbrains.kotlin.idea.completion.lookups.CompletionShortNamesRenderer.renderVariable
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
|
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.TailTextProvider.getTailText
|
import org.jetbrains.kotlin.idea.completion.lookups.TailTextProvider.getTailText
|
||||||
|
import org.jetbrains.kotlin.idea.completion.lookups.TailTextProvider.insertLambdaBraces
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.addCallableImportIfRequired
|
import org.jetbrains.kotlin.idea.completion.lookups.addCallableImportIfRequired
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.detectImportStrategy
|
|
||||||
import org.jetbrains.kotlin.idea.completion.lookups.shortenReferencesForFirCompletion
|
import org.jetbrains.kotlin.idea.completion.lookups.shortenReferencesForFirCompletion
|
||||||
import org.jetbrains.kotlin.idea.core.withRootPrefixIfNeeded
|
import org.jetbrains.kotlin.idea.core.withRootPrefixIfNeeded
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeRendererOptions
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeRendererOptions
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSyntheticJavaPropertySymbol
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSyntheticJavaPropertySymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtVariableLikeSymbol
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtVariableLikeSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.types.KtFunctionalType
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.renderer.render
|
import org.jetbrains.kotlin.renderer.render
|
||||||
@@ -28,20 +30,42 @@ import org.jetbrains.kotlin.renderer.render
|
|||||||
internal class VariableLookupElementFactory {
|
internal class VariableLookupElementFactory {
|
||||||
fun KtAnalysisSession.createLookup(
|
fun KtAnalysisSession.createLookup(
|
||||||
symbol: KtVariableLikeSymbol,
|
symbol: KtVariableLikeSymbol,
|
||||||
importStrategy: ImportStrategy = detectImportStrategy(symbol)
|
options: CallableInsertionOptions,
|
||||||
): LookupElementBuilder {
|
): LookupElementBuilder {
|
||||||
val lookupObject = VariableLookupObject(
|
val rendered = renderVariable(symbol)
|
||||||
symbol.name,
|
val builder = when (options.insertionStrategy) {
|
||||||
importStrategy = importStrategy,
|
CallableInsertionStrategy.AsCall -> {
|
||||||
renderedReceiverType = symbol.receiverType?.type?.render(CompletionShortNamesRenderer.TYPE_RENDERING_OPTIONS),
|
val functionalType = symbol.annotatedType.type as KtFunctionalType
|
||||||
)
|
val lookupObject = FunctionCallLookupObject(
|
||||||
|
symbol.name,
|
||||||
|
options,
|
||||||
|
rendered,
|
||||||
|
inputValueArguments = functionalType.parameterTypes.isNotEmpty(),
|
||||||
|
insertEmptyLambda = insertLambdaBraces(functionalType),
|
||||||
|
)
|
||||||
|
|
||||||
return LookupElementBuilder.create(lookupObject, symbol.name.asString())
|
val tailText = functionalType.parameterTypes.joinToString(prefix = "(", postfix = ")") {
|
||||||
.withTypeText(symbol.annotatedType.type.render(KtTypeRendererOptions.SHORT_NAMES))
|
it.render(CompletionShortNamesRenderer.TYPE_RENDERING_OPTIONS)
|
||||||
.withTailText(getTailText(symbol), true)
|
}
|
||||||
.markIfSyntheticJavaProperty(symbol)
|
|
||||||
.withInsertHandler(VariableInsertionHandler)
|
val typeText = functionalType.returnType.render(CompletionShortNamesRenderer.TYPE_RENDERING_OPTIONS)
|
||||||
.let { withSymbolInfo(symbol, it) }
|
|
||||||
|
LookupElementBuilder.create(lookupObject, symbol.name.asString())
|
||||||
|
.withTailText(tailText, true)
|
||||||
|
.withTypeText(typeText)
|
||||||
|
.withInsertHandler(FunctionInsertionHandler)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val lookupObject = VariableLookupObject(symbol.name, options, rendered)
|
||||||
|
LookupElementBuilder.create(lookupObject, symbol.name.asString())
|
||||||
|
.withTypeText(symbol.annotatedType.type.render(KtTypeRendererOptions.SHORT_NAMES))
|
||||||
|
.withTailText(getTailText(symbol), true)
|
||||||
|
.markIfSyntheticJavaProperty(symbol)
|
||||||
|
.withInsertHandler(VariableInsertionHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return withSymbolInfo(symbol, builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LookupElementBuilder.markIfSyntheticJavaProperty(symbol: KtVariableLikeSymbol): LookupElementBuilder = when (symbol) {
|
private fun LookupElementBuilder.markIfSyntheticJavaProperty(symbol: KtVariableLikeSymbol): LookupElementBuilder = when (symbol) {
|
||||||
@@ -63,8 +87,8 @@ internal class VariableLookupElementFactory {
|
|||||||
*/
|
*/
|
||||||
private data class VariableLookupObject(
|
private data class VariableLookupObject(
|
||||||
override val shortName: Name,
|
override val shortName: Name,
|
||||||
val importStrategy: ImportStrategy,
|
override val options: CallableInsertionOptions,
|
||||||
override val renderedReceiverType: String?,
|
override val renderedDeclaration: String,
|
||||||
) : KotlinCallableLookupObject()
|
) : KotlinCallableLookupObject()
|
||||||
|
|
||||||
|
|
||||||
@@ -73,7 +97,7 @@ private object VariableInsertionHandler : InsertHandler<LookupElement> {
|
|||||||
val targetFile = context.file as? KtFile ?: return
|
val targetFile = context.file as? KtFile ?: return
|
||||||
val lookupObject = item.`object` as VariableLookupObject
|
val lookupObject = item.`object` as VariableLookupObject
|
||||||
|
|
||||||
when (val importStrategy = lookupObject.importStrategy) {
|
when (val importStrategy = lookupObject.options.importingStrategy) {
|
||||||
is ImportStrategy.AddImport -> {
|
is ImportStrategy.AddImport -> {
|
||||||
addCallableImportIfRequired(targetFile, importStrategy.nameToImport)
|
addCallableImportIfRequired(targetFile, importStrategy.nameToImport)
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-2
@@ -16,7 +16,13 @@ abstract class KtCompletionCandidateChecker : KtAnalysisSessionComponent() {
|
|||||||
originalFile: KtFile,
|
originalFile: KtFile,
|
||||||
nameExpression: KtSimpleNameExpression,
|
nameExpression: KtSimpleNameExpression,
|
||||||
possibleExplicitReceiver: KtExpression?,
|
possibleExplicitReceiver: KtExpression?,
|
||||||
): Boolean
|
): KtExtensionApplicabilityResult
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class KtExtensionApplicabilityResult(val isApplicable: Boolean) {
|
||||||
|
ApplicableAsExtensionCallable(isApplicable = true),
|
||||||
|
ApplicableAsFunctionalVariableCall(isApplicable = true),
|
||||||
|
NonApplicable(isApplicable = false),
|
||||||
}
|
}
|
||||||
|
|
||||||
interface KtCompletionCandidateCheckerMixIn : KtAnalysisSessionMixIn {
|
interface KtCompletionCandidateCheckerMixIn : KtAnalysisSessionMixIn {
|
||||||
@@ -24,7 +30,7 @@ interface KtCompletionCandidateCheckerMixIn : KtAnalysisSessionMixIn {
|
|||||||
originalPsiFile: KtFile,
|
originalPsiFile: KtFile,
|
||||||
psiFakeCompletionExpression: KtSimpleNameExpression,
|
psiFakeCompletionExpression: KtSimpleNameExpression,
|
||||||
psiReceiverExpression: KtExpression?,
|
psiReceiverExpression: KtExpression?,
|
||||||
): Boolean =
|
): KtExtensionApplicabilityResult =
|
||||||
analysisSession.completionCandidateChecker.checkExtensionFitsCandidate(
|
analysisSession.completionCandidateChecker.checkExtensionFitsCandidate(
|
||||||
this,
|
this,
|
||||||
originalPsiFile,
|
originalPsiFile,
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ abstract class KtFunctionalType : KtNonErrorClassType() {
|
|||||||
abstract val isSuspend: Boolean
|
abstract val isSuspend: Boolean
|
||||||
abstract val arity: Int
|
abstract val arity: Int
|
||||||
abstract val receiverType: KtType?
|
abstract val receiverType: KtType?
|
||||||
|
abstract val hasReceiver: Boolean
|
||||||
abstract val parameterTypes: List<KtType>
|
abstract val parameterTypes: List<KtType>
|
||||||
abstract val returnType: KtType
|
abstract val returnType: KtType
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-10
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.components
|
|||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||||
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
|
import org.jetbrains.kotlin.fir.resolve.calls.ImplicitReceiverValue
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.inference.receiverType
|
||||||
|
import org.jetbrains.kotlin.fir.types.coneType
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacadeForResolveOnAir.getTowerContextProvider
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacadeForResolveOnAir.getTowerContextProvider
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFirFile
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFirFile
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFirOfType
|
import org.jetbrains.kotlin.idea.fir.low.level.api.api.getOrBuildFirOfType
|
||||||
@@ -17,19 +19,15 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.resolver.SingleCandidateResol
|
|||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.util.getElementTextInContext
|
import org.jetbrains.kotlin.idea.fir.low.level.api.util.getElementTextInContext
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtCompletionCandidateChecker
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtCompletionCandidateChecker
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.components.KtExtensionApplicabilityResult
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirFunctionSymbol
|
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirKotlinPropertySymbol
|
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbol
|
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirValueParameterSymbol
|
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.firRef
|
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.weakRef
|
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.weakRef
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.withValidityAssertion
|
import org.jetbrains.kotlin.idea.frontend.api.withValidityAssertion
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
|
||||||
|
|
||||||
internal class KtFirCompletionCandidateChecker(
|
internal class KtFirCompletionCandidateChecker(
|
||||||
analysisSession: KtFirAnalysisSession,
|
analysisSession: KtFirAnalysisSession,
|
||||||
@@ -42,7 +40,7 @@ internal class KtFirCompletionCandidateChecker(
|
|||||||
originalFile: KtFile,
|
originalFile: KtFile,
|
||||||
nameExpression: KtSimpleNameExpression,
|
nameExpression: KtSimpleNameExpression,
|
||||||
possibleExplicitReceiver: KtExpression?,
|
possibleExplicitReceiver: KtExpression?,
|
||||||
): Boolean = withValidityAssertion {
|
): KtExtensionApplicabilityResult = withValidityAssertion {
|
||||||
require(firSymbolForCandidate is KtFirSymbol<*>)
|
require(firSymbolForCandidate is KtFirSymbol<*>)
|
||||||
return firSymbolForCandidate.firRef.withFir(phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) { declaration ->
|
return firSymbolForCandidate.firRef.withFir(phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) { declaration ->
|
||||||
check(declaration is FirCallableDeclaration<*>)
|
check(declaration is FirCallableDeclaration<*>)
|
||||||
@@ -55,7 +53,7 @@ internal class KtFirCompletionCandidateChecker(
|
|||||||
originalFile: KtFile,
|
originalFile: KtFile,
|
||||||
nameExpression: KtSimpleNameExpression,
|
nameExpression: KtSimpleNameExpression,
|
||||||
possibleExplicitReceiver: KtExpression?,
|
possibleExplicitReceiver: KtExpression?,
|
||||||
): Boolean {
|
): KtExtensionApplicabilityResult {
|
||||||
val file = originalFile.getOrBuildFirFile(firResolveState)
|
val file = originalFile.getOrBuildFirFile(firResolveState)
|
||||||
val explicitReceiverExpression = possibleExplicitReceiver?.getOrBuildFirOfType<FirExpression>(firResolveState)
|
val explicitReceiverExpression = possibleExplicitReceiver?.getOrBuildFirOfType<FirExpression>(firResolveState)
|
||||||
val resolver = SingleCandidateResolver(firResolveState.rootModuleSession, file)
|
val resolver = SingleCandidateResolver(firResolveState.rootModuleSession, file)
|
||||||
@@ -68,11 +66,17 @@ internal class KtFirCompletionCandidateChecker(
|
|||||||
explicitReceiver = explicitReceiverExpression
|
explicitReceiver = explicitReceiverExpression
|
||||||
)
|
)
|
||||||
resolver.resolveSingleCandidate(resolutionParameters)?.let {
|
resolver.resolveSingleCandidate(resolutionParameters)?.let {
|
||||||
// not null if resolved and completed successfully
|
return when {
|
||||||
return true
|
candidateSymbol is FirVariable<*> && candidateSymbol.returnTypeRef.coneType.receiverType(rootModuleSession) != null -> {
|
||||||
|
KtExtensionApplicabilityResult.ApplicableAsFunctionalVariableCall
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
KtExtensionApplicabilityResult.ApplicableAsExtensionCallable
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return KtExtensionApplicabilityResult.NonApplicable
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getImplicitReceivers(
|
private fun getImplicitReceivers(
|
||||||
|
|||||||
+6
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.idea.frontend.api.fir.types
|
package org.jetbrains.kotlin.idea.frontend.api.fir.types
|
||||||
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType
|
import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.inference.receiverType
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.*
|
import org.jetbrains.kotlin.idea.frontend.api.*
|
||||||
@@ -80,6 +81,11 @@ internal class KtFirFunctionalType(
|
|||||||
else null
|
else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val hasReceiver: Boolean
|
||||||
|
get() = withValidityAssertion {
|
||||||
|
coneType.receiverType(firBuilder.rootSession) != null
|
||||||
|
}
|
||||||
|
|
||||||
override val parameterTypes: List<KtType> by cached {
|
override val parameterTypes: List<KtType> by cached {
|
||||||
val parameterTypeArgs = if (coneType.isExtensionFunctionType) typeArguments.subList(1, typeArguments.lastIndex)
|
val parameterTypeArgs = if (coneType.isExtensionFunctionType) typeArguments.subList(1, typeArguments.lastIndex)
|
||||||
else typeArguments.subList(0, typeArguments.lastIndex)
|
else typeArguments.subList(0, typeArguments.lastIndex)
|
||||||
|
|||||||
Reference in New Issue
Block a user