FIR IDE: implement override completion
This commit is contained in:
committed by
TeamCityServer
parent
ae212f36f5
commit
8983bde73e
+2
-16
@@ -16,34 +16,21 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.ui.RowIcon
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.isSuspend
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.KotlinDescriptorIconProvider
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.indexOfSkippingSpace
|
||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
|
||||
import org.jetbrains.kotlin.idea.core.moveCaret
|
||||
import org.jetbrains.kotlin.idea.core.moveCaretIntoGeneratedElement
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMembersHandler
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.generateMember
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import javax.swing.Icon
|
||||
|
||||
class OverridesCompletion(
|
||||
private val collector: LookupElementsCollector,
|
||||
@@ -96,10 +83,9 @@ class OverridesCompletion(
|
||||
baseClassName,
|
||||
baseClassIcon,
|
||||
isConstructorParameter,
|
||||
classOrObject,
|
||||
memberObject.descriptor.isSuspend,
|
||||
memberObject::generateMember,
|
||||
ShortenReferences.DEFAULT::process,
|
||||
generateMember = { memberObject.generateMember(classOrObject, copyDoc = false) },
|
||||
shortenReferences = ShortenReferences.DEFAULT::process,
|
||||
)
|
||||
|
||||
lookupElement.assignPriority(if (isImplement) ItemPriority.IMPLEMENT else ItemPriority.OVERRIDE)
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface Interface {
|
||||
var field: Int
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface Interface {
|
||||
var field: Int
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
fun foo(p: Int)
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
fun foo(p: Int)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
val someVal: String?
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
val someVal: String?
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
var someVar: String
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
var someVar: String
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
class A {
|
||||
@Deprecated("")
|
||||
o<caret>
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
class A {
|
||||
@Deprecated("")
|
||||
override fun equals(other: Any?): Boolean {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
class A {
|
||||
@Deprecated("") // it is deprecated
|
||||
public o<caret>
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
class A {
|
||||
@Deprecated("") // it is deprecated
|
||||
public override fun equals(other: Any?): Boolean {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
class A {
|
||||
o<caret>
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
class A {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
<caret><selection>return super.equals(other)</selection>
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
val someVal: java.io.File?
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -1,5 +1,6 @@
|
||||
import java.io.File
|
||||
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
val someVal: java.io.File?
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
suspend fun foo()
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
suspend fun foo()
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
class A {
|
||||
equ<caret>
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
class A {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
<caret><selection>return super.equals(other)</selection>
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
protected var someVar: java.io.File?
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -1,5 +1,6 @@
|
||||
import java.io.File
|
||||
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
protected var someVar: java.io.File?
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
val someVal: java.io.File?
|
||||
}
|
||||
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
import java.io.File
|
||||
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
val someVal: java.io.File?
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
val p: Int
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
val p: Int
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
val p: Int
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface I {
|
||||
val p: Int
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
package ppp
|
||||
|
||||
interface I {
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
package ppp
|
||||
|
||||
interface I {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface Interface {
|
||||
fun funA()
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_COMPARISON
|
||||
interface Interface {
|
||||
fun funA()
|
||||
}
|
||||
|
||||
+18
-25
@@ -55,21 +55,15 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
|
||||
|
||||
val keywordContributor = FirKeywordCompletionContributor(basicContext)
|
||||
|
||||
if (positionContext is FirUnknownPositionContext) {
|
||||
// TODO unify when LL API will be able to handle more context for completion
|
||||
keywordContributor.completeDefaultKeywordsWithoutResolve(positionContext.position, expression = null)
|
||||
} else {
|
||||
FirPositionCompletionContextDetector.analyseInContext(basicContext, positionContext) {
|
||||
with(keywordContributor) { completeKeywords(positionContext) }
|
||||
when (positionContext) {
|
||||
is FirNameReferencePositionContext -> with(
|
||||
KotlinWithNameReferenceCompletionProvider(basicContext, indexHelper)
|
||||
) {
|
||||
addCompletions(positionContext)
|
||||
}
|
||||
is FirUnknownPositionContext -> {
|
||||
// TODO remove when hack when LL API will be able to handle more context for completion
|
||||
}
|
||||
FirPositionCompletionContextDetector.analyseInContext(basicContext, positionContext) {
|
||||
with(keywordContributor) { completeKeywords(positionContext) }
|
||||
when (positionContext) {
|
||||
is FirNameReferencePositionContext -> with(
|
||||
KotlinWithNameReferenceCompletionProvider(basicContext, indexHelper)
|
||||
) {
|
||||
addCompletions(positionContext)
|
||||
}
|
||||
is FirUnknownPositionContext -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,7 +131,6 @@ private class KotlinWithNameReferenceCompletionProvider(
|
||||
basicContext: FirBasicCompletionContext,
|
||||
private val indexHelper: IndexHelper
|
||||
) : FirCompletionContributorBase(basicContext) {
|
||||
private val lookupElementFactory = KotlinFirLookupElementFactory()
|
||||
private val typeNamesProvider = TypeNamesProvider(indexHelper)
|
||||
|
||||
private val scopeNameFilter: KtScopeNameFilter =
|
||||
@@ -198,13 +191,13 @@ private class KotlinWithNameReferenceCompletionProvider(
|
||||
.getClassifierSymbols(scopeNameFilter)
|
||||
.filter { visibilityChecker.isVisible(it) }
|
||||
|
||||
classesFromScopes.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||
classesFromScopes.forEach { addSymbolToCompletion(expectedType, it) }
|
||||
|
||||
val kotlinClassesFromIndices = indexHelper.getKotlinClasses(scopeNameFilter, psiFilter = { it !is KtEnumEntry })
|
||||
kotlinClassesFromIndices.asSequence()
|
||||
.map { it.getSymbol() as KtClassifierSymbol }
|
||||
.filter { visibilityChecker.isVisible(it) }
|
||||
.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||
.forEach { addSymbolToCompletion(expectedType, it) }
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.collectDotCompletion(
|
||||
@@ -220,11 +213,11 @@ private class KotlinWithNameReferenceCompletionProvider(
|
||||
val nonExtensionMembers = possibleReceiverScope.collectNonExtensions(visibilityChecker)
|
||||
val extensionNonMembers = implicitScopes.collectSuitableExtensions(extensionChecker, visibilityChecker)
|
||||
|
||||
nonExtensionMembers.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||
extensionNonMembers.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||
nonExtensionMembers.forEach { addSymbolToCompletion(expectedType, it) }
|
||||
extensionNonMembers.forEach { addSymbolToCompletion(expectedType, it) }
|
||||
|
||||
collectTopLevelExtensionsFromIndices(listOf(typeOfPossibleReceiver), extensionChecker, visibilityChecker)
|
||||
.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||
.forEach { addSymbolToCompletion(expectedType, it) }
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.collectDefaultCompletion(
|
||||
@@ -238,19 +231,19 @@ private class KotlinWithNameReferenceCompletionProvider(
|
||||
val availableNonExtensions = implicitScopes.collectNonExtensions(visibilityChecker)
|
||||
val extensionsWhichCanBeCalled = implicitScopes.collectSuitableExtensions(extensionChecker, visibilityChecker)
|
||||
|
||||
availableNonExtensions.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||
extensionsWhichCanBeCalled.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||
availableNonExtensions.forEach { addSymbolToCompletion(expectedType, it) }
|
||||
extensionsWhichCanBeCalled.forEach { addSymbolToCompletion(expectedType, it) }
|
||||
|
||||
if (shouldCompleteTopLevelCallablesFromIndex) {
|
||||
val topLevelCallables = indexHelper.getTopLevelCallables(scopeNameFilter)
|
||||
topLevelCallables.asSequence()
|
||||
.map { it.getSymbol() as KtCallableSymbol }
|
||||
.filter { visibilityChecker.isVisible(it) }
|
||||
.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||
.forEach { addSymbolToCompletion(expectedType, it) }
|
||||
}
|
||||
|
||||
collectTopLevelExtensionsFromIndices(implicitReceiversTypes, extensionChecker, visibilityChecker)
|
||||
.forEach { addSymbolToCompletion(result, expectedType, it) }
|
||||
.forEach { addSymbolToCompletion(expectedType, it) }
|
||||
|
||||
collectTypesCompletion(implicitScopes, expectedType, visibilityChecker)
|
||||
}
|
||||
|
||||
+2
@@ -9,6 +9,7 @@ import com.intellij.codeInsight.completion.CompletionParameters
|
||||
import com.intellij.codeInsight.completion.CompletionResultSet
|
||||
import com.intellij.codeInsight.completion.PrefixMatcher
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.completion.KotlinFirLookupElementFactory
|
||||
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
@@ -21,6 +22,7 @@ internal class FirBasicCompletionContext(
|
||||
val fakeKtFile: KtFile,
|
||||
val project: Project,
|
||||
val targetPlatform: TargetPlatform,
|
||||
val lookupElementFactory: KotlinFirLookupElementFactory = KotlinFirLookupElementFactory(),
|
||||
) {
|
||||
companion object {
|
||||
fun createFromParameters(parameters: CompletionParameters, result: CompletionResultSet): FirBasicCompletionContext? {
|
||||
|
||||
+2
-1
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.completion.context
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.analyse
|
||||
import org.jetbrains.kotlin.idea.frontend.api.analyseInFakeAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
@@ -60,7 +61,7 @@ internal object FirPositionCompletionContextDetector {
|
||||
action
|
||||
)
|
||||
is FirUnknownPositionContext -> {
|
||||
// TODO
|
||||
analyse(basicContext.originalKtFile, action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -29,9 +29,7 @@ internal abstract class FirCompletionContributorBase(protected val basicContext:
|
||||
protected val fakeKtFile: KtFile get() = basicContext.fakeKtFile
|
||||
protected val project: Project get() = basicContext.project
|
||||
protected val targetPlatform: TargetPlatform get() = basicContext.targetPlatform
|
||||
|
||||
|
||||
protected val lookupElementFactory = KotlinFirLookupElementFactory()
|
||||
protected val lookupElementFactory: KotlinFirLookupElementFactory get() = basicContext.lookupElementFactory
|
||||
|
||||
protected fun KtAnalysisSession.addSymbolToCompletion(expectedType: KtType?, symbol: KtSymbol) {
|
||||
if (symbol !is KtNamedSymbol) return
|
||||
|
||||
+8
-16
@@ -26,6 +26,8 @@ internal class FirKeywordCompletionContributor(basicContext: FirBasicCompletionC
|
||||
override fun getLanguageVersionSetting(module: Module) = LanguageVersionSettingsImpl.DEFAULT // TODO
|
||||
})
|
||||
|
||||
private val resolveDependentCompletionKeywordHandlers = ResolveDependentCompletionKeywordHandlers(basicContext)
|
||||
|
||||
fun KtAnalysisSession.completeKeywords(
|
||||
positionContext: FirPositionCompletionContext
|
||||
) {
|
||||
@@ -43,10 +45,10 @@ internal class FirKeywordCompletionContributor(basicContext: FirBasicCompletionC
|
||||
}
|
||||
|
||||
fun KtAnalysisSession.completeWithResolve(position: PsiElement, expression: KtExpression?) {
|
||||
complete(position, expression) { lookupElement, keyword ->
|
||||
complete(position) { lookupElement, keyword ->
|
||||
val lookups = DefaultCompletionKeywordHandlers.defaultHandlers.getHandlerForKeyword(keyword)
|
||||
?.createLookups(parameters, expression, lookupElement, project)
|
||||
?: ResolveDependentCompletionKeywordHandlers.handlers.getHandlerForKeyword(keyword)?.run {
|
||||
?: resolveDependentCompletionKeywordHandlers.handlers.getHandlerForKeyword(keyword)?.run {
|
||||
createLookups(parameters, expression, lookupElement, project)
|
||||
}
|
||||
?: listOf(lookupElement)
|
||||
@@ -54,16 +56,7 @@ internal class FirKeywordCompletionContributor(basicContext: FirBasicCompletionC
|
||||
}
|
||||
}
|
||||
|
||||
fun completeDefaultKeywordsWithoutResolve(position: PsiElement, expression: KtExpression?) {
|
||||
complete(position, expression) { lookupElement, keyword ->
|
||||
val lookups = DefaultCompletionKeywordHandlers.defaultHandlers.getHandlerForKeyword(keyword)
|
||||
?.createLookups(parameters, expression, lookupElement, project)
|
||||
?: listOf(lookupElement)
|
||||
result.addAllElements(lookups)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun complete(position: PsiElement, expression: KtExpression?, crossinline complete: (LookupElement, String) -> Unit) {
|
||||
private inline fun complete(position: PsiElement, crossinline complete: (LookupElement, String) -> Unit) {
|
||||
keywordCompletion.complete(position, prefixMatcher, targetPlatform.isJvm()) { lookupElement ->
|
||||
val keyword = lookupElement.lookupString
|
||||
complete(lookupElement, keyword)
|
||||
@@ -71,10 +64,9 @@ internal class FirKeywordCompletionContributor(basicContext: FirBasicCompletionC
|
||||
}
|
||||
}
|
||||
|
||||
private object ResolveDependentCompletionKeywordHandlers {
|
||||
private class ResolveDependentCompletionKeywordHandlers(basicContext: FirBasicCompletionContext) {
|
||||
val handlers = CompletionKeywordHandlers(
|
||||
ReturnKeywordHandler,
|
||||
OverrideKeywordHandler,
|
||||
OverrideKeywordHandler(basicContext),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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.CompletionParameters
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.ui.RowIcon
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.idea.completion.*
|
||||
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.*
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.KtClassMember
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.KtGenerateMembersHandler
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.KtOverrideMembersHandler
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.generateMember
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.analyse
|
||||
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.markers.KtSymbolWithModality
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtIconProvider.getIcon
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.nameOrAnonymous
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.HackToForceAllowRunningAnalyzeOnEDT
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.hackyAllowRunningOnEdt
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
|
||||
internal class OverrideKeywordHandler(
|
||||
private val basicContext: FirBasicCompletionContext
|
||||
) : CompletionKeywordHandler<KtAnalysisSession>(KtTokens.OVERRIDE_KEYWORD) {
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
override fun KtAnalysisSession.createLookups(
|
||||
parameters: CompletionParameters,
|
||||
expression: KtExpression?,
|
||||
lookup: LookupElement,
|
||||
project: Project
|
||||
): Collection<LookupElement> {
|
||||
val result = mutableListOf(lookup)
|
||||
val position = parameters.position
|
||||
val isConstructorParameter = position.getNonStrictParentOfType<KtPrimaryConstructor>() != null
|
||||
val classOrObject = position.getNonStrictParentOfType<KtClassOrObject>() ?: return result
|
||||
val members = collectMembers(classOrObject, isConstructorParameter)
|
||||
|
||||
for (member in members) {
|
||||
result += createLookupElementToGenerateSingleOverrideMember(member, classOrObject, isConstructorParameter, project)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.collectMembers(classOrObject: KtClassOrObject, isConstructorParameter: Boolean): List<KtClassMember> {
|
||||
val allMembers = KtOverrideMembersHandler().collectMembersToGenerate(classOrObject)
|
||||
return if (isConstructorParameter) {
|
||||
allMembers.mapNotNull { member ->
|
||||
if (member.memberInfo.isProperty) {
|
||||
member.copy(bodyType = BodyType.FROM_TEMPLATE, preferConstructorParameter = true)
|
||||
} else null
|
||||
}
|
||||
} else allMembers.toList()
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.createLookupElementToGenerateSingleOverrideMember(
|
||||
member: KtClassMember,
|
||||
classOrObject: KtClassOrObject,
|
||||
isConstructorParameter: Boolean,
|
||||
project: Project
|
||||
): OverridesCompletionLookupElementDecorator {
|
||||
val memberSymbol = member.symbol
|
||||
check(memberSymbol is KtNamedSymbol)
|
||||
|
||||
val text = getSymbolTextForLookupElement(memberSymbol)
|
||||
val baseIcon = getIcon(memberSymbol)
|
||||
val isImplement = (memberSymbol as? KtSymbolWithModality)?.modality == Modality.ABSTRACT
|
||||
val additionalIcon = if (isImplement) AllIcons.Gutter.ImplementingMethod else AllIcons.Gutter.OverridingMethod
|
||||
val icon = RowIcon(baseIcon, additionalIcon)
|
||||
val baseClass = classOrObject.getClassOrObjectSymbol()
|
||||
val baseClassIcon = getIcon(baseClass)
|
||||
val isSuspendFunction = (memberSymbol as? KtFunctionSymbol)?.isSuspend == true
|
||||
val baseClassName = baseClass.nameOrAnonymous.asString()
|
||||
|
||||
val memberPointer = memberSymbol.createPointer()
|
||||
|
||||
val baseLookupElement = with(basicContext.lookupElementFactory) { createLookupElement(memberSymbol) }
|
||||
?: error("Lookup element should be available for override completion")
|
||||
return OverridesCompletionLookupElementDecorator(
|
||||
baseLookupElement,
|
||||
declaration = null,
|
||||
text,
|
||||
isImplement,
|
||||
icon,
|
||||
baseClassName,
|
||||
baseClassIcon,
|
||||
isConstructorParameter,
|
||||
isSuspendFunction,
|
||||
generateMember = {
|
||||
generateMemberInNewAnalysisSession(classOrObject, memberPointer, member, project)
|
||||
},
|
||||
shortenReferences = { element ->
|
||||
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
|
||||
val shortenings = hackyAllowRunningOnEdt {
|
||||
analyse(classOrObject) {
|
||||
collectPossibleReferenceShortenings(element.containingKtFile, element.textRange)
|
||||
}
|
||||
}
|
||||
runWriteAction {
|
||||
shortenings.invokeShortening()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.getSymbolTextForLookupElement(memberSymbol: KtCallableSymbol): String = buildString {
|
||||
append(KtTokens.OVERRIDE_KEYWORD.value)
|
||||
.append(" ")
|
||||
.append(memberSymbol.render(renderingOptionsForLookupElementRendering))
|
||||
if (memberSymbol is KtFunctionSymbol) {
|
||||
append(" {...}")
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
|
||||
private fun generateMemberInNewAnalysisSession(
|
||||
classOrObject: KtClassOrObject,
|
||||
memberPointer: KtSymbolPointer<KtCallableSymbol>,
|
||||
member: KtClassMember,
|
||||
project: Project
|
||||
) = hackyAllowRunningOnEdt {
|
||||
analyse(classOrObject) {
|
||||
val memberInCorrectAnalysisSession = createCopyInCurrentAnalysisSession(memberPointer, member)
|
||||
generateMember(
|
||||
project,
|
||||
memberInCorrectAnalysisSession,
|
||||
classOrObject,
|
||||
copyDoc = false,
|
||||
mode = MemberGenerateMode.OVERRIDE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//todo temporary hack until KtSymbolPointer is properly implemented
|
||||
private fun KtAnalysisSession.createCopyInCurrentAnalysisSession(
|
||||
memberPointer: KtSymbolPointer<KtCallableSymbol>,
|
||||
member: KtClassMember
|
||||
) = KtClassMember(
|
||||
KtClassMemberInfo(
|
||||
memberPointer.restoreSymbol()
|
||||
?: error("Cannot restore symbol from $memberPointer"),
|
||||
member.memberInfo.memberText,
|
||||
member.memberInfo.memberIcon,
|
||||
member.memberInfo.containingSymbolText,
|
||||
member.memberInfo.containingSymbolIcon,
|
||||
),
|
||||
member.bodyType,
|
||||
member.preferConstructorParameter,
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val renderingOptionsForLookupElementRendering =
|
||||
KtGenerateMembersHandler.renderOption.copy(
|
||||
renderUnitReturnType = false,
|
||||
renderDeclarationHeader = true
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,8 @@ internal data class KtClassMemberInfo(
|
||||
val isProperty: Boolean get() = symbol is KtPropertySymbol
|
||||
}
|
||||
|
||||
internal class KtClassMember(
|
||||
private val memberInfo: KtClassMemberInfo,
|
||||
internal data class KtClassMember(
|
||||
val memberInfo: KtClassMemberInfo,
|
||||
val bodyType: BodyType,
|
||||
val preferConstructorParameter: Boolean
|
||||
) : MemberChooserObjectBase(
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ import org.jetbrains.kotlin.idea.frontend.api.tokens.hackyAllowRunningOnEdt
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
|
||||
internal class KtOverrideMembersHandler : KtGenerateMembersHandler() {
|
||||
internal open class KtOverrideMembersHandler : KtGenerateMembersHandler() {
|
||||
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
|
||||
override fun collectMembersToGenerate(classOrObject: KtClassOrObject): Collection<KtClassMember> {
|
||||
return hackyAllowRunningOnEdt {
|
||||
@@ -29,7 +29,7 @@ internal class KtOverrideMembersHandler : KtGenerateMembersHandler() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.collectMembers(classOrObject: KtClassOrObject): List<KtClassMember> {
|
||||
fun KtAnalysisSession.collectMembers(classOrObject: KtClassOrObject): List<KtClassMember> {
|
||||
val classOrObjectSymbol = classOrObject.getClassOrObjectSymbol()
|
||||
return getOverridableMembers(classOrObjectSymbol).map { (symbol, bodyType, containingSymbol) ->
|
||||
KtClassMember(
|
||||
|
||||
+2
-3
@@ -30,9 +30,8 @@ class OverridesCompletionLookupElementDecorator(
|
||||
private val baseClassName: String,
|
||||
private val baseClassIcon: Icon?,
|
||||
private val isConstructorParameter: Boolean,
|
||||
private val classOrObject: KtClassOrObject,
|
||||
private val isSuspend: Boolean,
|
||||
private val generateMember: (targetClass: KtClassOrObject, copyDoc: Boolean) -> KtCallableDeclaration,
|
||||
private val generateMember: () -> KtCallableDeclaration,
|
||||
private val shortenReferences: (KtElement) -> Unit,
|
||||
) : LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun getLookupString() =
|
||||
@@ -85,7 +84,7 @@ class OverridesCompletionLookupElementDecorator(
|
||||
// keep original modifiers
|
||||
val modifierList = KtPsiFactory(context.project).createModifierList(dummyMember.modifierList!!.text)
|
||||
|
||||
val prototype = generateMember(classOrObject, false)
|
||||
val prototype = generateMember()
|
||||
prototype.modifierList!!.replace(modifierList)
|
||||
val insertedMember = dummyMember.replaced(prototype)
|
||||
if (isSuspend) insertedMember.addModifier(KtTokens.SUSPEND_KEYWORD)
|
||||
|
||||
Reference in New Issue
Block a user