diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/codeInsight/KotlinHighLevelExpressionTypeProvider.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/codeInsight/KotlinHighLevelExpressionTypeProvider.kt index ac767e4feea..ba7914236e4 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/codeInsight/KotlinHighLevelExpressionTypeProvider.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/codeInsight/KotlinHighLevelExpressionTypeProvider.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.idea.codeInsight import com.intellij.openapi.util.text.StringUtil import org.jetbrains.kotlin.idea.frontend.api.analyseInModalWindow -import org.jetbrains.kotlin.idea.frontend.api.types.render import org.jetbrains.kotlin.psi.KtExpression class KotlinHighLevelExpressionTypeProvider : KotlinExpressionTypeProvider() { diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt index 8c676f0760e..b842a33a884 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt @@ -19,8 +19,6 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.symbols.* import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol -import org.jetbrains.kotlin.idea.frontend.api.types.KtType -import org.jetbrains.kotlin.idea.frontend.api.types.render import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtTypeArgumentList @@ -36,7 +34,7 @@ internal class KotlinFirLookupElementFactory { fun KtAnalysisSession.createLookupElement(symbol: KtNamedSymbol): LookupElement? { val elementBuilder = when (symbol) { is KtFunctionSymbol -> with(functionLookupElementFactory) { createLookup(symbol) } - is KtVariableLikeSymbol -> variableLookupElementFactory.createLookup(symbol) + is KtVariableLikeSymbol -> with(variableLookupElementFactory) { createLookup(symbol) } is KtClassLikeSymbol -> classLookupElementFactory.createLookup(symbol) is KtTypeParameterSymbol -> typeParameterLookupElementFactory.createLookup(symbol) else -> throw IllegalArgumentException("Cannot create a lookup element for $symbol") @@ -66,9 +64,9 @@ private class TypeParameterLookupElementFactory { } private class VariableLookupElementFactory { - fun createLookup(symbol: KtVariableLikeSymbol): LookupElementBuilder { + fun KtAnalysisSession.createLookup(symbol: KtVariableLikeSymbol): LookupElementBuilder { return LookupElementBuilder.create(UniqueLookupObject(), symbol.name.asString()) - .withTypeText(ShortNamesRenderer.renderType(symbol.type)) + .withTypeText(symbol.type.render()) .withInsertHandler(createInsertHandler(symbol)) } @@ -82,7 +80,7 @@ private class FunctionLookupElementFactory { return try { LookupElementBuilder.create(UniqueLookupObject(), symbol.name.asString()) .withTailText(getTailText(symbol), true) - .withTypeText(ShortNamesRenderer.renderType(symbol.type)) + .withTypeText(symbol.type.render()) .withInsertHandler(createInsertHandler(symbol)) } catch (e: Throwable) { if (e is ControlFlowException) throw e @@ -92,7 +90,7 @@ private class FunctionLookupElementFactory { } private fun KtAnalysisSession.getTailText(symbol: KtFunctionSymbol): String { - return if (insertLambdaBraces(symbol)) " {...}" else ShortNamesRenderer.renderFunctionParameters(symbol) + return if (insertLambdaBraces(symbol)) " {...}" else with(ShortNamesRenderer) { renderFunctionParameters(symbol) } } private fun KtAnalysisSession.insertLambdaBraces(symbol: KtFunctionSymbol): Boolean { @@ -215,13 +213,11 @@ private open class QuotedNamesAwareInsertionHandler(private val name: Name) : In private object ShortNamesRenderer { - fun renderFunctionParameters(function: KtFunctionSymbol): String = + fun KtAnalysisSession.renderFunctionParameters(function: KtFunctionSymbol): String = function.valueParameters.joinToString(", ", "(", ")") { renderFunctionParameter(it) } - fun renderType(ktType: KtType): String = ktType.render() - - private fun renderFunctionParameter(param: KtFunctionParameterSymbol): String = - "${if (param.isVararg) "vararg " else ""}${param.name.asString()}: ${renderType(param.type)}" + private fun KtAnalysisSession.renderFunctionParameter(param: KtFunctionParameterSymbol): String = + "${if (param.isVararg) "vararg " else ""}${param.name.asString()}: ${param.type.render()}" } private fun Document.isTextAt(offset: Int, text: String) = diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/FunctionCallHighlightingVisitor.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/FunctionCallHighlightingVisitor.kt index 868fcee900b..663aa28b565 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/FunctionCallHighlightingVisitor.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/fir/highlighter/visitors/FunctionCallHighlightingVisitor.kt @@ -51,7 +51,7 @@ internal class FunctionCallHighlightingVisitor( private fun getTextAttributesForCal(call: KtCall): TextAttributesKey? = when { call.isSuccessCallOf { it.isSuspend } -> Colors.SUSPEND_FUNCTION_CALL - call is KtFunctionCall -> when (val function = call.targetFunction) { + call is KtFunctionCall -> when (val function = call.targetFunction.getSuccessCallSymbolOrNull()) { is KtConstructorSymbol -> Colors.CONSTRUCTOR_CALL is KtAnonymousFunctionSymbol -> null is KtFunctionSymbol -> when { diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt index 466e6625e52..fcd06bf919e 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSession.kt @@ -33,7 +33,7 @@ import org.jetbrains.kotlin.psi.* * * To create analysis session consider using [analyze] */ -abstract class KtAnalysisSession(override val token: ValidityToken) : ValidityTokenOwner { +abstract class KtAnalysisSession(final override val token: ValidityToken) : ValidityTokenOwner { protected abstract val smartCastProvider: KtSmartCastProvider protected abstract val typeProvider: KtTypeProvider protected abstract val diagnosticProvider: KtDiagnosticProvider @@ -43,6 +43,8 @@ abstract class KtAnalysisSession(override val token: ValidityToken) : ValidityTo protected abstract val callResolver: KtCallResolver protected abstract val completionCandidateChecker: KtCompletionCandidateChecker protected abstract val symbolDeclarationOverridesProvider: KtSymbolDeclarationOverridesProvider + @Suppress("LeakingThis") + protected open val typeRenderer: KtTypeRenderer = KtDefaultTypeRenderer(this, token) /// TODO: get rid of @Deprecated("Used only in completion now, temporary") @@ -143,4 +145,7 @@ abstract class KtAnalysisSession(override val token: ValidityToken) : ValidityTo psiFakeCompletionExpression, psiReceiverExpression ) + + fun KtType.render(options: KtTypeRendererOptions = KtTypeRendererOptions.DEFAULT): String = + typeRenderer.render(this, options) } diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSessionProvider.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSessionProvider.kt index b19be1f6c09..e4e2d27bbe7 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSessionProvider.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/KtAnalysisSessionProvider.kt @@ -10,7 +10,6 @@ import com.intellij.openapi.components.service import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.Task -import org.jetbrains.kotlin.idea.frontend.api.types.render import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.utils.PrintingLogger diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtTypeRenderer.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtTypeRenderer.kt new file mode 100644 index 00000000000..451368ac759 --- /dev/null +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/components/KtTypeRenderer.kt @@ -0,0 +1,115 @@ +/* + * Copyright 2010-2020 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.frontend.api.components + +import org.jetbrains.kotlin.idea.frontend.api.* +import org.jetbrains.kotlin.idea.frontend.api.types.* +import org.jetbrains.kotlin.name.ClassId + +abstract class KtTypeRenderer : KtAnalysisSessionComponent() { + abstract fun render(type: KtType, options: KtTypeRendererOptions): String +} + +data class KtTypeRendererOptions( + val renderFqNames: Boolean, +) { + companion object { + val DEFAULT = KtTypeRendererOptions( + renderFqNames = true + ) + } +} + +class KtDefaultTypeRenderer(override val analysisSession: KtAnalysisSession, override val token: ValidityToken) : KtTypeRenderer() { + override fun render(type: KtType, options: KtTypeRendererOptions): String = type.withValidityAssertion { + buildString { render(type, options) } + } + + private fun StringBuilder.render(type: KtType, options: KtTypeRendererOptions) { + when (type) { + is KtDenotableType -> when (type) { + is KtClassType -> { + render(type.classId, options) + renderTypeArgumentsIfNotEmpty(type.typeArguments, options) + renderNullability(type.nullability) + } + is KtTypeParameterType -> { + append(type.name.asString()) + renderNullability(type.nullability) + } + } + is KtNonDenotableType -> when (type) { + is KtFlexibleType -> inParens { + render(type.lowerBound, options) + append("..") + render(type.upperBound, options) + } + is KtIntersectionType -> inParens { + type.conjuncts.forEachIndexed { index, conjunct -> + render(conjunct, options) + if (index != type.conjuncts.lastIndex) { + append("&") + } + } + } + } + is KtErrorType -> { + append(type.error) + } + else -> error("Unsupported type ${type::class}") + } + } + + private fun StringBuilder.renderTypeArgumentsIfNotEmpty(typeArguments: List, options: KtTypeRendererOptions) { + if (typeArguments.isNotEmpty()) { + append("<") + typeArguments.forEachIndexed { index, typeArgument -> + render(typeArgument, options) + if (index != typeArguments.lastIndex) { + append(", ") + } + } + append(">") + } + } + + private fun StringBuilder.render(typeArgument: KtTypeArgument, options: KtTypeRendererOptions) { + when (typeArgument) { + KtStarProjectionTypeArgument -> { + append("*") + } + is KtTypeArgumentWithVariance -> { + val varianceWithSpace = when (typeArgument.variance) { + KtTypeArgumentVariance.COVARIANT -> "out " + KtTypeArgumentVariance.CONTRAVARIANT -> "in " + KtTypeArgumentVariance.INVARIANT -> "" + } + append(varianceWithSpace) + render(typeArgument.type, options) + } + } + } + + private fun StringBuilder.renderNullability(nullability: KtTypeNullability) { + if (nullability == KtTypeNullability.NULLABLE) { + append("?") + } + } + + private fun StringBuilder.render(classId: ClassId, options: KtTypeRendererOptions) { + if (options.renderFqNames) { + append(classId.asString().replace('/', '.')) + } else { + append(classId.shortClassName.asString()) + } + } + + private inline fun StringBuilder.inParens(render: StringBuilder.() -> Unit) { + append("(") + render() + append(")") + } +} \ No newline at end of file diff --git a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/types/KtTypeRenderer.kt b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/types/KtTypeRenderer.kt index b488abec940..de1a270e6c7 100644 --- a/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/types/KtTypeRenderer.kt +++ b/idea/idea-frontend-api/src/org/jetbrains/kotlin/idea/frontend/api/types/KtTypeRenderer.kt @@ -7,109 +7,3 @@ package org.jetbrains.kotlin.idea.frontend.api.types import org.jetbrains.kotlin.idea.frontend.api.* import org.jetbrains.kotlin.name.ClassId - -private object KtTypeRenderer { - fun render(type: KtType, options: KtTypeRendererOptions = KtTypeRendererOptions.DEFAULT): String = type.withValidityAssertion { - buildString { render(type, options) } - } - - private fun StringBuilder.render(type: KtType, options: KtTypeRendererOptions) { - when (type) { - is KtDenotableType -> when (type) { - is KtClassType -> { - render(type.classId, options) - renderTypeArgumentsIfNotEmpty(type.typeArguments, options) - renderNullability(type.nullability) - } - is KtTypeParameterType -> { - append(type.name.asString()) - renderNullability(type.nullability) - } - } - is KtNonDenotableType -> when (type) { - is KtFlexibleType -> inParens { - render(type.lowerBound, options) - append("..") - render(type.upperBound, options) - } - is KtIntersectionType -> inParens { - type.conjuncts.forEachIndexed { index, conjunct -> - render(conjunct, options) - if (index != type.conjuncts.lastIndex) { - append("&") - } - } - } - } - is KtErrorType -> { - append(type.error) - } - else -> error("Unsupported type ${type::class}") - } - } - - private fun StringBuilder.renderTypeArgumentsIfNotEmpty(typeArguments: List, options: KtTypeRendererOptions) { - if (typeArguments.isNotEmpty()) { - append("<") - typeArguments.forEachIndexed { index, typeArgument -> - render(typeArgument, options) - if (index != typeArguments.lastIndex) { - append(", ") - } - } - append(">") - } - } - - private fun StringBuilder.render(typeArgument: KtTypeArgument, options: KtTypeRendererOptions) { - when (typeArgument) { - KtStarProjectionTypeArgument -> { - append("*") - } - is KtTypeArgumentWithVariance -> { - val varianceWithSpace = when (typeArgument.variance) { - KtTypeArgumentVariance.COVARIANT -> "out " - KtTypeArgumentVariance.CONTRAVARIANT -> "in " - KtTypeArgumentVariance.INVARIANT -> "" - } - append(varianceWithSpace) - render(typeArgument.type, options) - } - } - } - - private fun StringBuilder.renderNullability(nullability: KtTypeNullability) { - if (nullability == KtTypeNullability.NULLABLE) { - append("?") - } - } - - private fun StringBuilder.render(classId: ClassId, options: KtTypeRendererOptions) { - if (options.renderFqNames) { - append(classId.asString().replace('/', '.')) - } else { - append(classId.shortClassName.asString()) - } - } - - private inline fun StringBuilder.inParens(render: StringBuilder.() -> Unit) { - append("(") - render() - append(")") - - } -} - -data class KtTypeRendererOptions( - val renderFqNames: Boolean, -) { - companion object { - val DEFAULT = KtTypeRendererOptions( - renderFqNames = true - ) - } -} - -fun KtType.render(options: KtTypeRendererOptions = KtTypeRendererOptions.DEFAULT): String = - KtTypeRenderer.render(this, options) - diff --git a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/types/KtTypeRendererTest.kt b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/types/KtTypeRendererTest.kt index 8efd5c7d3a2..1f132b524fc 100644 --- a/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/types/KtTypeRendererTest.kt +++ b/idea/idea-frontend-fir/tests/org/jetbrains/kotlin/idea/frontend/api/types/KtTypeRendererTest.kt @@ -8,6 +8,8 @@ package org.jetbrains.kotlin.idea.frontend.api.types import com.intellij.testFramework.LightProjectDescriptor import org.jetbrains.kotlin.idea.executeOnPooledThreadInReadAction import org.jetbrains.kotlin.idea.frontend.api.analyze +import org.jetbrains.kotlin.idea.frontend.api.analyze +import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeRendererOptions import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor