FIR IDE: implement super qualifier completion
This commit is contained in:
committed by
TeamCityServer
parent
7d351ca6b1
commit
0a6243fc67
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
open class Base
|
||||
|
||||
open class A : Base
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
open class A
|
||||
|
||||
interface I
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
open class AA
|
||||
|
||||
interface AI
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// FIR_COMPARISON
|
||||
fun some() {
|
||||
|
||||
if (true) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
<caret>
|
||||
}
|
||||
|
||||
|
||||
// EXIST: do
|
||||
// EXIST: class
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: interface
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: return
|
||||
// EXIST: throw
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: typealias
|
||||
// EXIST: val
|
||||
// EXIST: var
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
fun some() {
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo() {
|
||||
try {
|
||||
|
||||
}
|
||||
catch(e: Exception) {
|
||||
|
||||
}
|
||||
<caret>
|
||||
}
|
||||
|
||||
// EXIST: catch
|
||||
// EXIST: finally
|
||||
// EXIST: class
|
||||
// EXIST: do
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: return
|
||||
// EXIST: throw
|
||||
// EXIST: interface
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: val
|
||||
// EXIST: var
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// EXIST: as
|
||||
// EXIST: typealias
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
fun foo() {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo() {
|
||||
try {
|
||||
|
||||
}
|
||||
finally {
|
||||
|
||||
}
|
||||
<caret>
|
||||
}
|
||||
|
||||
// EXIST: class
|
||||
// EXIST: do
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: return
|
||||
// EXIST: throw
|
||||
// EXIST: interface
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: val
|
||||
// EXIST: var
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// EXIST: typealias
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
fun foo() {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo(p: Int) {
|
||||
if (p > 0) {
|
||||
|
||||
}
|
||||
<caret>
|
||||
}
|
||||
|
||||
// EXIST: else
|
||||
// EXIST: class
|
||||
// EXIST: do
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: return
|
||||
// EXIST: throw
|
||||
// EXIST: interface
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: val
|
||||
// EXIST: var
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// EXIST: typealias
|
||||
// EXIST: as
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
fun foo(p: Int) {
|
||||
if (p > 0) {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo(p: Int) {
|
||||
if (p > 0)
|
||||
foo()
|
||||
<caret>
|
||||
}
|
||||
|
||||
// EXIST: else
|
||||
// EXIST: class
|
||||
// EXIST: do
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: return
|
||||
// EXIST: throw
|
||||
// EXIST: interface
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: val
|
||||
// EXIST: var
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// EXIST: typealias
|
||||
// EXIST: as
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
fun foo(p: Int) {
|
||||
if (p > 0)
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// FIR_COMPARISON
|
||||
// COMPILER_ARGUMENTS: -XXLanguage:+SealedInterfaces -XXLanguage:+MultiPlatformProjects
|
||||
|
||||
fun foo() {
|
||||
bar()
|
||||
}
|
||||
|
||||
fun bar() {
|
||||
foo()
|
||||
}
|
||||
|
||||
var a : Int
|
||||
<caret>
|
||||
|
||||
// EXIST: abstract
|
||||
// EXIST: by
|
||||
// EXIST: class
|
||||
// EXIST: class GlobalPropertyAccessors
|
||||
// EXIST: enum class
|
||||
// EXIST: enum class GlobalPropertyAccessors
|
||||
// EXIST: final
|
||||
// EXIST: fun
|
||||
// EXIST: { itemText: "get", tailText: null }
|
||||
// EXIST: { itemText: "get", tailText: "() = ..." }
|
||||
// EXIST: { itemText: "get", tailText: "() {...}" }
|
||||
// EXIST: internal
|
||||
// EXIST: object
|
||||
// EXIST: object GlobalPropertyAccessors
|
||||
// EXIST: open
|
||||
// EXIST: private
|
||||
// EXIST: public
|
||||
// EXIST: { itemText: "set", tailText: null }
|
||||
// EXIST: { itemText: "set", tailText: "(value) = ..." }
|
||||
// EXIST: { itemText: "set", tailText: "(value) {...}" }
|
||||
// EXIST: interface
|
||||
// EXIST: interface GlobalPropertyAccessors
|
||||
// EXIST: val
|
||||
// EXIST: var
|
||||
// EXIST: operator
|
||||
// EXIST: infix
|
||||
// EXIST: sealed class
|
||||
// EXIST: sealed class GlobalPropertyAccessors
|
||||
// EXIST: sealed interface GlobalPropertyAccessors
|
||||
// EXIST: sealed interface
|
||||
// EXIST: data class
|
||||
// EXIST: { "lookupString":"data class", "itemText":"data class", "tailText":" GlobalPropertyAccessors(...)", "attributes":"bold" }
|
||||
// EXIST: inline
|
||||
// EXIST: value
|
||||
// EXIST: tailrec
|
||||
// EXIST: external
|
||||
// EXIST: annotation class
|
||||
// EXIST: annotation class GlobalPropertyAccessors
|
||||
// EXIST: const val
|
||||
// EXIST: suspend fun
|
||||
// EXIST: typealias
|
||||
// EXIST: expect
|
||||
// EXIST: actual
|
||||
// EXIST: lateinit var
|
||||
// NOTHING_ELSE
|
||||
@@ -0,0 +1,33 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo(p: Int) {
|
||||
if (p > 0)
|
||||
try {
|
||||
}
|
||||
catch (e: Exception) {
|
||||
}
|
||||
<caret>
|
||||
}
|
||||
|
||||
// EXIST: catch
|
||||
// EXIST: finally
|
||||
// EXIST: else
|
||||
// EXIST: class
|
||||
// EXIST: do
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: return
|
||||
// EXIST: throw
|
||||
// EXIST: interface
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: val
|
||||
// EXIST: var
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// EXIST: typealias
|
||||
// EXIST: as
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
fun foo(p: Int) {
|
||||
if (p > 0)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo(p: Int) {
|
||||
x()
|
||||
y()
|
||||
val edge = a.b(<caret>)
|
||||
z()
|
||||
}
|
||||
|
||||
// EXIST: do
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: return
|
||||
// EXIST: throw
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
fun foo(p: Int) {
|
||||
x()
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo() {
|
||||
<caret>
|
||||
}
|
||||
|
||||
// EXIST: class
|
||||
// EXIST: do
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: return
|
||||
// EXIST: throw
|
||||
// EXIST: interface
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: val
|
||||
// EXIST: var
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// EXIST: typealias
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
fun foo() {
|
||||
<caret>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// FIR_COMPARISON
|
||||
fun some() {
|
||||
|
||||
if (true) {
|
||||
|
||||
} else {
|
||||
<caret>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// EXIST: do
|
||||
// EXIST: class
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: interface
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: return
|
||||
// EXIST: throw
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: typealias
|
||||
// EXIST: val
|
||||
// EXIST: var
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
fun some() {
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo() = <caret>
|
||||
|
||||
// EXIST: do
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: throw
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
fun foo() = <caret>
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// FIR_COMPARISON
|
||||
val prop: Int
|
||||
get() = <caret>
|
||||
|
||||
// EXIST: do
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: throw
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
val prop: Int
|
||||
get() = <caret>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// FIR_COMPARISON
|
||||
fun some() {
|
||||
|
||||
if (true) {
|
||||
<caret>
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// EXIST: do
|
||||
// EXIST: class
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: interface
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: return
|
||||
// EXIST: throw
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: typealias
|
||||
// EXIST: val
|
||||
// EXIST: var
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
fun some() {
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// FIR_COMPARISON
|
||||
fun foo(p: Int = <caret>)
|
||||
|
||||
// EXIST: do
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: return
|
||||
// EXIST: throw
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
fun foo(p: Int = <caret>)
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// FIR_COMPARISON
|
||||
var a : Int = <caret>
|
||||
|
||||
// EXIST: do
|
||||
// EXIST: false
|
||||
// EXIST: for
|
||||
// EXIST: fun
|
||||
// EXIST: if
|
||||
// EXIST: null
|
||||
// EXIST: object
|
||||
// EXIST: throw
|
||||
// EXIST: true
|
||||
// EXIST: try
|
||||
// EXIST: when
|
||||
// EXIST: while
|
||||
// NOTHING_ELSE
|
||||
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// FIR_COMPARISON
|
||||
var a : Int = <caret>
|
||||
|
||||
|
||||
+5
@@ -78,6 +78,7 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
|
||||
complete(callableContributor, positionContext)
|
||||
complete(classifierContributor, positionContext)
|
||||
}
|
||||
|
||||
is FirTypeNameReferencePositionContext -> {
|
||||
complete(keywordContributor, positionContext)
|
||||
complete(packageCompletionContributor, positionContext)
|
||||
@@ -90,6 +91,10 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
|
||||
complete(annotationsContributor, positionContext)
|
||||
}
|
||||
|
||||
is FirSuperTypeCallNameReferencePositionContext -> {
|
||||
complete(FirSuperEntryContributor(basicContext), positionContext)
|
||||
}
|
||||
|
||||
is FirUnknownPositionContext -> {
|
||||
complete(keywordContributor, positionContext)
|
||||
}
|
||||
|
||||
+4
-4
@@ -232,7 +232,7 @@ private object ClassifierInsertionHandler : InsertHandler<LookupElement> {
|
||||
context.document.replaceString(context.startOffset, context.tailOffset, fqName.render())
|
||||
context.commitDocument()
|
||||
|
||||
shortenReferences(targetFile, TextRange(context.startOffset, context.tailOffset))
|
||||
shortenReferencesForFirCompletion(targetFile, TextRange(context.startOffset, context.tailOffset))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,7 +338,7 @@ private object FunctionInsertionHandler : QuotedNamesAwareInsertionHandler() {
|
||||
addArguments(context, element, lookupObject)
|
||||
context.commitDocument()
|
||||
|
||||
shortenReferences(targetFile, TextRange(context.startOffset, context.tailOffset))
|
||||
shortenReferencesForFirCompletion(targetFile, TextRange(context.startOffset, context.tailOffset))
|
||||
} else {
|
||||
addArguments(context, element, lookupObject)
|
||||
context.commitDocument()
|
||||
@@ -368,7 +368,7 @@ private object VariableInsertionHandler : InsertHandler<LookupElement> {
|
||||
)
|
||||
|
||||
context.commitDocument()
|
||||
shortenReferences(targetFile, TextRange(context.startOffset, context.tailOffset))
|
||||
shortenReferencesForFirCompletion(targetFile, TextRange(context.startOffset, context.tailOffset))
|
||||
}
|
||||
|
||||
is CallableImportStrategy.DoNothing -> {}
|
||||
@@ -435,7 +435,7 @@ private fun CharSequence.indexOfSkippingSpace(c: Char, startIndex: Int): Int? {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun shortenReferences(targetFile: KtFile, textRange: TextRange) {
|
||||
internal fun shortenReferencesForFirCompletion(targetFile: KtFile, textRange: TextRange) {
|
||||
val shortenings = withAllowedResolve {
|
||||
analyse(targetFile) {
|
||||
collectPossibleReferenceShortenings(targetFile, textRange)
|
||||
|
||||
+25
-9
@@ -39,6 +39,14 @@ internal class FirAnnotationTypeNameReferencePositionContext(
|
||||
val annotationEntry: KtAnnotationEntry,
|
||||
) : FirNameReferencePositionContext()
|
||||
|
||||
internal class FirSuperTypeCallNameReferencePositionContext(
|
||||
override val position: PsiElement,
|
||||
override val reference: KtSimpleNameReference,
|
||||
override val nameExpression: KtSimpleNameExpression,
|
||||
override val explicitReceiver: KtExpression?,
|
||||
val superExpression: KtSuperExpression,
|
||||
) : FirNameReferencePositionContext()
|
||||
|
||||
|
||||
internal class FirExpressionNameReferencePositionContext(
|
||||
override val position: PsiElement,
|
||||
@@ -78,15 +86,23 @@ internal object FirPositionCompletionContextDetector {
|
||||
nameExpression: KtSimpleNameExpression,
|
||||
explicitReceiver: KtExpression?
|
||||
): FirNameReferencePositionContext {
|
||||
val annotationEntry = userType.annotationEntry()
|
||||
?: return FirTypeNameReferencePositionContext(position, reference, nameExpression, explicitReceiver)
|
||||
return FirAnnotationTypeNameReferencePositionContext(position, reference, nameExpression, explicitReceiver, annotationEntry)
|
||||
}
|
||||
|
||||
private fun KtUserType.annotationEntry(): KtAnnotationEntry? {
|
||||
val typeReference = (parent as? KtTypeReference)?.takeIf { it.typeElement == this }
|
||||
val constructorCall = (typeReference?.parent as? KtConstructorCalleeExpression)?.takeIf { it.typeReference == typeReference }
|
||||
return (constructorCall?.parent as? KtAnnotationEntry)?.takeIf { it.calleeExpression == constructorCall }
|
||||
val typeReference = (userType.parent as? KtTypeReference)?.takeIf { it.typeElement == userType }
|
||||
return when (val typeReferenceOwner = typeReference?.parent) {
|
||||
is KtConstructorCalleeExpression -> {
|
||||
val constructorCall = typeReferenceOwner.takeIf { it.typeReference == typeReference }
|
||||
val annotationEntry = (constructorCall?.parent as? KtAnnotationEntry)?.takeIf { it.calleeExpression == constructorCall }
|
||||
annotationEntry?.let {
|
||||
FirAnnotationTypeNameReferencePositionContext(position, reference, nameExpression, explicitReceiver, it)
|
||||
}
|
||||
}
|
||||
is KtSuperExpression -> {
|
||||
val superTypeCallEntry = typeReferenceOwner.takeIf { it.superTypeQualifier == typeReference }
|
||||
superTypeCallEntry?.let {
|
||||
FirSuperTypeCallNameReferencePositionContext(position, reference, nameExpression, explicitReceiver, it)
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
} ?: FirTypeNameReferencePositionContext(position, reference, nameExpression, explicitReceiver)
|
||||
}
|
||||
|
||||
inline fun analyseInContext(
|
||||
|
||||
+2
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.idea.completion.context.FirRawPositionCompletionCont
|
||||
import org.jetbrains.kotlin.idea.completion.context.FirUnknownPositionContext
|
||||
import org.jetbrains.kotlin.idea.completion.contributors.keywords.OverrideKeywordHandler
|
||||
import org.jetbrains.kotlin.idea.completion.contributors.keywords.ReturnKeywordHandler
|
||||
import org.jetbrains.kotlin.idea.completion.contributors.keywords.SuperKeywordHandler
|
||||
import org.jetbrains.kotlin.idea.completion.contributors.keywords.ThisKeywordHandler
|
||||
import org.jetbrains.kotlin.idea.completion.keywords.CompletionKeywordHandlerProvider
|
||||
import org.jetbrains.kotlin.idea.completion.keywords.CompletionKeywordHandlers
|
||||
@@ -76,5 +77,6 @@ private class ResolveDependentCompletionKeywordHandlerProvider(
|
||||
ReturnKeywordHandler,
|
||||
OverrideKeywordHandler(basicContext),
|
||||
ThisKeywordHandler(basicContext),
|
||||
SuperKeywordHandler,
|
||||
)
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.contributors
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
||||
import org.jetbrains.kotlin.idea.completion.context.FirSuperTypeCallNameReferenceRawPositionContext
|
||||
import org.jetbrains.kotlin.idea.completion.contributors.helpers.FirSuperEntriesProvider.getSuperClassesAvailableForSuperCall
|
||||
import org.jetbrains.kotlin.idea.completion.contributors.helpers.SuperCallLookupObject
|
||||
import org.jetbrains.kotlin.idea.completion.contributors.helpers.SuperCallInsertionHandler
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
|
||||
internal class FirSuperEntryContributor(
|
||||
basicContext: FirBasicCompletionContext,
|
||||
) : FirContextCompletionContributorBase<FirSuperTypeCallNameReferenceRawPositionContext>(basicContext) {
|
||||
override fun KtAnalysisSession.complete(positionContext: FirSuperTypeCallNameReferenceRawPositionContext) {
|
||||
getSuperClassesAvailableForSuperCall(positionContext.nameExpression).forEach { superType ->
|
||||
val tailText = superType.classIdIfNonLocal?.asString()?.let { "($it)" }
|
||||
LookupElementBuilder.create(SuperLookupObject(superType.name, superType.classIdIfNonLocal), superType.name.asString())
|
||||
.withTailText(tailText)
|
||||
.withInsertHandler(SuperCallInsertionHandler)
|
||||
.let { result.addElement(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SuperLookupObject(val className: Name, val classId: ClassId?) : SuperCallLookupObject {
|
||||
override val replaceTo: String
|
||||
get() = when {
|
||||
classId != null -> "${classId.asSingleFqName().asString()}>"
|
||||
else -> "${className.asString()}>"
|
||||
}
|
||||
|
||||
override val shortenReferencesInReplaced: Boolean
|
||||
get() = classId != null
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.contributors.helpers
|
||||
|
||||
import com.intellij.codeInsight.completion.CodeCompletionHandlerBase
|
||||
import com.intellij.codeInsight.completion.CompletionType
|
||||
import com.intellij.codeInsight.completion.InsertHandler
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.completion.shortenReferencesForFirCompletion
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtNamedClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
|
||||
internal object FirSuperEntriesProvider {
|
||||
fun KtAnalysisSession.getSuperClassesAvailableForSuperCall(context: PsiElement): List<KtNamedClassOrObjectSymbol> {
|
||||
val containingClass = context.getStrictParentOfType<KtClassOrObject>() ?: return emptyList()
|
||||
val containingClassSymbol = containingClass.getClassOrObjectSymbol()
|
||||
return containingClassSymbol.superTypes.mapNotNull { superType ->
|
||||
val classType = superType.type as? KtClassType ?: return@mapNotNull null
|
||||
classType.classSymbol as? KtNamedClassOrObjectSymbol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal object SuperCallInsertionHandler : InsertHandler<LookupElement> {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
val lookupObject = item.`object` as SuperCallLookupObject
|
||||
|
||||
replaceWithClassIdAndShorten(lookupObject, context)
|
||||
addDot(context)
|
||||
invokeCompletion(context)
|
||||
}
|
||||
|
||||
private fun replaceWithClassIdAndShorten(
|
||||
lookupObject: SuperCallLookupObject,
|
||||
context: InsertionContext
|
||||
) {
|
||||
val replaceTo = lookupObject.replaceTo ?: return
|
||||
context.document.replaceString(context.startOffset, context.tailOffset, replaceTo)
|
||||
context.commitDocument()
|
||||
|
||||
if (lookupObject.shortenReferencesInReplaced) {
|
||||
val targetFile = context.file as KtFile
|
||||
shortenReferencesForFirCompletion(targetFile, TextRange(context.startOffset, context.tailOffset))
|
||||
}
|
||||
}
|
||||
|
||||
private fun invokeCompletion(context: InsertionContext) {
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
CodeCompletionHandlerBase(CompletionType.BASIC, true, false, true)
|
||||
.invokeCompletion(context.project, context.editor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addDot(context: InsertionContext) {
|
||||
context.document.insertString(context.tailOffset, ".")
|
||||
context.commitDocument()
|
||||
context.editor.caretModel.moveToOffset(context.tailOffset)
|
||||
}
|
||||
}
|
||||
|
||||
internal interface SuperCallLookupObject {
|
||||
val replaceTo: String?
|
||||
val shortenReferencesInReplaced: Boolean
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.contributors.keywords
|
||||
|
||||
import com.intellij.codeInsight.completion.*
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.completion.KeywordLookupObject
|
||||
import org.jetbrains.kotlin.idea.completion.contributors.helpers.FirSuperEntriesProvider.getSuperClassesAvailableForSuperCall
|
||||
import org.jetbrains.kotlin.idea.completion.contributors.helpers.SuperCallLookupObject
|
||||
import org.jetbrains.kotlin.idea.completion.contributors.helpers.SuperCallInsertionHandler
|
||||
import org.jetbrains.kotlin.idea.completion.createKeywordElement
|
||||
import org.jetbrains.kotlin.idea.completion.keywords.CompletionKeywordHandler
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
|
||||
internal object SuperKeywordHandler : CompletionKeywordHandler<KtAnalysisSession>(KtTokens.SUPER_KEYWORD) {
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
override fun KtAnalysisSession.createLookups(
|
||||
parameters: CompletionParameters,
|
||||
expression: KtExpression?,
|
||||
lookup: LookupElement,
|
||||
project: Project
|
||||
): Collection<LookupElement> {
|
||||
val superClasses = getSuperClassesAvailableForSuperCall(parameters.position)
|
||||
|
||||
if (superClasses.isEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
if (expression == null) {
|
||||
// for completion in secondary constructor delegation call
|
||||
return listOf(lookup)
|
||||
}
|
||||
|
||||
return when {
|
||||
superClasses.size <= 1 -> listOf(lookup)
|
||||
else -> buildList {
|
||||
add(lookup)
|
||||
superClasses.mapTo(this) { symbol ->
|
||||
createKeywordElement("super", "<${symbol.name}>", SuperKeywordLookupObject(symbol.name, symbol.classIdIfNonLocal))
|
||||
.withInsertHandler(SuperCallInsertionHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SuperKeywordLookupObject(val className: Name, val classId: ClassId?) : KeywordLookupObject(), SuperCallLookupObject {
|
||||
override val replaceTo: String?
|
||||
get() = classId?.let { "super<${it.asSingleFqName().asString()}>" }
|
||||
|
||||
override val shortenReferencesInReplaced: Boolean
|
||||
get() = classId != null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user