Respect imports when rendering class names in type hints

#KT-19524 Fixed
This commit is contained in:
Dmitry Jemerov
2018-01-04 18:25:59 +01:00
parent 45282af38b
commit 78682ac493
3 changed files with 52 additions and 11 deletions
@@ -29,7 +29,7 @@ fun provideLambdaImplicitHints(lambda: KtLambdaExpression): List<InlayInfo> {
val text = buildString { val text = buildString {
append(TYPE_INFO_PREFIX) append(TYPE_INFO_PREFIX)
append("this: ") append("this: ")
append(inlayHintsTypeRenderer.renderType(implicitReceiver.type)) append(getInlayHintsTypeRenderer(bindingContext, lambda).renderType(implicitReceiver.type))
} }
return listOf(InlayInfo(text, lbrace.textRange.endOffset)) return listOf(InlayInfo(text, lbrace.textRange.endOffset))
} }
@@ -41,7 +41,7 @@ fun provideLambdaImplicitHints(lambda: KtLambdaExpression): List<InlayInfo> {
val text = buildString { val text = buildString {
append(TYPE_INFO_PREFIX) append(TYPE_INFO_PREFIX)
append("it: ") append("it: ")
append(inlayHintsTypeRenderer.renderType(type)) append(getInlayHintsTypeRenderer(bindingContext, lambda).renderType(type))
} }
return listOf(InlayInfo(text, lbrace.textRange.endOffset)) return listOf(InlayInfo(text, lbrace.textRange.endOffset))
} }
@@ -8,28 +8,56 @@ package org.jetbrains.kotlin.idea.parameterInfo
import com.intellij.codeInsight.hints.InlayInfo import com.intellij.codeInsight.hints.InlayInfo
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.codeStyle.CodeStyleSettingsManager import com.intellij.psi.codeStyle.CodeStyleSettingsManager
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings import org.jetbrains.kotlin.idea.core.formatter.KotlinCodeStyleSettings
import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.sam.SamConstructorDescriptor import org.jetbrains.kotlin.load.java.sam.SamConstructorDescriptor
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.RenderingFormat import org.jetbrains.kotlin.renderer.RenderingFormat
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.containsError import org.jetbrains.kotlin.types.typeUtil.containsError
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
//hack to separate type presentation from param info presentation //hack to separate type presentation from param info presentation
const val TYPE_INFO_PREFIX = "@TYPE@" const val TYPE_INFO_PREFIX = "@TYPE@"
val inlayHintsTypeRenderer = DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.withOptions {
textFormat = RenderingFormat.PLAIN class ImportAwareClassifierNamePolicy(
renderUnabbreviatedType = false val bindingContext: BindingContext,
val context: KtElement
) : ClassifierNamePolicy {
override fun renderClassifier(classifier: ClassifierDescriptor, renderer: DescriptorRenderer): String {
if (classifier.containingDeclaration is ClassDescriptor) {
val resolutionFacade = context.getResolutionFacade()
val scope = context.getResolutionScope(bindingContext, resolutionFacade)
if (scope.findClassifier(classifier.name, NoLookupLocation.FROM_IDE) == classifier) {
return classifier.name.asString()
}
}
return ClassifierNamePolicy.SHORT.renderClassifier(classifier, renderer)
}
} }
fun getInlayHintsTypeRenderer(bindingContext: BindingContext, context: KtElement) =
DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.withOptions {
textFormat = RenderingFormat.PLAIN
renderUnabbreviatedType = false
classifierNamePolicy = ImportAwareClassifierNamePolicy(bindingContext, context)
}
fun providePropertyTypeHint(elem: PsiElement): List<InlayInfo> { fun providePropertyTypeHint(elem: PsiElement): List<InlayInfo> {
(elem as? KtCallableDeclaration)?.let { property -> (elem as? KtCallableDeclaration)?.let { property ->
property.nameIdentifier?.let { ident -> property.nameIdentifier?.let { ident ->
@@ -50,14 +78,13 @@ fun provideTypeHint(element: KtCallableDeclaration, offset: Int): List<InlayInfo
return emptyList() return emptyList()
} }
type = type.immediateSupertypes().singleOrNull() ?: return emptyList() type = type.immediateSupertypes().singleOrNull() ?: return emptyList()
} } else if (name?.isSpecial == true) {
else if (name?.isSpecial == true) {
return emptyList() return emptyList()
} }
return if (isUnclearType(type, element)) { return if (isUnclearType(type, element)) {
val settings = CodeStyleSettingsManager.getInstance(element.project).currentSettings val settings = CodeStyleSettingsManager.getInstance(element.project).currentSettings
.getCustomSettings(KotlinCodeStyleSettings::class.java) .getCustomSettings(KotlinCodeStyleSettings::class.java)
val declString = buildString { val declString = buildString {
append(TYPE_INFO_PREFIX) append(TYPE_INFO_PREFIX)
@@ -66,11 +93,10 @@ fun provideTypeHint(element: KtCallableDeclaration, offset: Int): List<InlayInfo
append(":") append(":")
if (settings.SPACE_AFTER_TYPE_COLON) if (settings.SPACE_AFTER_TYPE_COLON)
append(" ") append(" ")
append(inlayHintsTypeRenderer.renderType(type)) append(getInlayHintsTypeRenderer(element.analyze(), element).renderType(type))
} }
listOf(InlayInfo(declString, offset)) listOf(InlayInfo(declString, offset))
} } else {
else {
emptyList() emptyList()
} }
} }
@@ -110,4 +110,19 @@ class InlayTypeHintsTest : KotlinLightCodeInsightFixtureTestCase() {
HintType.PROPERTY_HINT.option.set(true) HintType.PROPERTY_HINT.option.set(true)
check("""val x = Runnable { }""") check("""val x = Runnable { }""")
} }
fun testNestedClassImports() {
HintType.PROPERTY_HINT.option.set(true)
check(
"""import kotlin.collections.Map.Entry
val entries<hint text=": Set<Entry<Int, String>>" /> = mapOf(1 to "1").entries"""
)
}
fun testNestedClassWithoutImport() {
HintType.PROPERTY_HINT.option.set(true)
check(
"""val entries<hint text=": Set<Map.Entry<Int, String>>" /> = mapOf(1 to "1").entries"""
)
}
} }