FIR IDE: implement KotlinExpressionTypeProvider for FIR

This commit is contained in:
Ilya Kirillov
2020-08-26 18:40:59 +03:00
parent fcc7db5224
commit 3cd445563e
6 changed files with 87 additions and 46 deletions
@@ -0,0 +1,24 @@
/*
* 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.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() {
override fun KtExpression.shouldShowStatementType(): Boolean {
return true /* TODO */
}
override fun getInformationHint(element: KtExpression): String = analyseInModalWindow(element, "Getting expression type") {
val rendered = element.getKtType().render()
StringUtil.escapeXmlEntities(rendered)
}
override fun getErrorHint(): String = "No expression found"
}
@@ -0,0 +1,55 @@
/*
* 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.codeInsight
import com.intellij.lang.ExpressionTypeProvider
import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
abstract class KotlinExpressionTypeProvider : ExpressionTypeProvider<KtExpression>() {
protected abstract fun KtExpression.shouldShowStatementType(): Boolean
override fun getExpressionsAt(elementAt: PsiElement): List<KtExpression> {
val candidates = elementAt.parentsWithSelf.filterIsInstance<KtExpression>().filter { it.shouldShowType() }.toList()
val fileEditor =
elementAt.containingFile?.virtualFile?.let { FileEditorManager.getInstance(elementAt.project).getSelectedEditor(it) }
val selectionTextRange = if (fileEditor is TextEditor) {
EditorUtil.getSelectionInAnyMode(fileEditor.editor)
} else {
TextRange.EMPTY_RANGE
}
val anchor =
candidates.firstOrNull { selectionTextRange.isEmpty || it.textRange.contains(selectionTextRange) } ?: return emptyList()
return candidates.filter { it.textRange.startOffset == anchor.textRange.startOffset }
}
private fun KtExpression.shouldShowType() = when (this) {
is KtFunctionLiteral -> false
is KtFunction -> !hasBlockBody() && !hasDeclaredReturnType()
is KtProperty -> typeReference == null
is KtPropertyAccessor -> false
is KtDestructuringDeclarationEntry -> true
is KtStatementExpression, is KtDestructuringDeclaration -> false
is KtIfExpression, is KtWhenExpression, is KtTryExpression -> shouldShowStatementType()
is KtLoopExpression -> false
is KtConstantExpression -> false
is KtThisExpression -> false
else -> getQualifiedExpressionForSelector() == null && parent !is KtCallableReferenceExpression && !isFunctionCallee()
}
private fun KtExpression.isFunctionCallee(): Boolean {
val callExpression = parent as? KtCallExpression ?: return false
if (callExpression.calleeExpression != this) return false
return mainReference?.resolve() is KtFunction
}
}
+3
View File
@@ -101,6 +101,9 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu
id="KotlinCompletionContributor"
order="first"
implementationClass="org.jetbrains.kotlin.idea.completion.KotlinFirCompletionContributor"/>
<codeInsight.typeInfo language="kotlin"
implementationClass="org.jetbrains.kotlin.idea.codeInsight.KotlinHighLevelExpressionTypeProvider"/>
</extensions>
<!-- scripts -->
+2 -1
View File
@@ -614,7 +614,8 @@
implementationClass="org.jetbrains.kotlin.idea.refactoring.pushDown.JavaToKotlinPushDownDelegate"/>
<codeInsight.typeInfo language="kotlin" implementationClass="org.jetbrains.kotlin.idea.codeInsight.KotlinExpressionTypeProvider"/>
<codeInsight.typeInfo language="kotlin"
implementationClass="org.jetbrains.kotlin.idea.codeInsight.KotlinExpressionTypeProviderDescriptorsImpl"/>
<testCreator language="kotlin" implementationClass="org.jetbrains.kotlin.idea.testIntegration.KotlinTestCreator"/>
<testFinder implementation="org.jetbrains.kotlin.idea.testIntegration.KotlinTestFinder"/>
@@ -5,12 +5,6 @@
package org.jetbrains.kotlin.idea.codeInsight
import com.intellij.lang.ExpressionTypeProvider
import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle
@@ -18,12 +12,9 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.resolve.getDataFlowValueFactory
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.RenderingFormat
@@ -38,7 +29,7 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo
class KotlinExpressionTypeProvider : ExpressionTypeProvider<KtExpression>() {
class KotlinExpressionTypeProviderDescriptorsImpl : KotlinExpressionTypeProvider() {
private val typeRenderer = DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.withOptions {
textFormat = RenderingFormat.HTML
classifierNamePolicy = object : ClassifierNamePolicy {
@@ -51,35 +42,7 @@ class KotlinExpressionTypeProvider : ExpressionTypeProvider<KtExpression>() {
}
}
override fun getExpressionsAt(elementAt: PsiElement): List<KtExpression> {
val candidates = elementAt.parentsWithSelf.filterIsInstance<KtExpression>().filter { it.shouldShowType() }.toList()
val fileEditor =
elementAt.containingFile?.virtualFile?.let { FileEditorManager.getInstance(elementAt.project).getSelectedEditor(it) }
val selectionTextRange = if (fileEditor is TextEditor) {
EditorUtil.getSelectionInAnyMode(fileEditor.editor)
} else {
TextRange.EMPTY_RANGE
}
val anchor =
candidates.firstOrNull { selectionTextRange.isEmpty || it.textRange.contains(selectionTextRange) } ?: return emptyList()
return candidates.filter { it.textRange.startOffset == anchor.textRange.startOffset }
}
private fun KtExpression.shouldShowType() = when (this) {
is KtFunctionLiteral -> false
is KtFunction -> !hasBlockBody() && !hasDeclaredReturnType()
is KtProperty -> typeReference == null
is KtPropertyAccessor -> false
is KtDestructuringDeclarationEntry -> true
is KtStatementExpression, is KtDestructuringDeclaration -> false
is KtIfExpression, is KtWhenExpression, is KtTryExpression -> shouldShowStatementType()
is KtLoopExpression -> false
is KtConstantExpression -> false
is KtThisExpression -> false
else -> getQualifiedExpressionForSelector() == null && parent !is KtCallableReferenceExpression && !isFunctionCallee()
}
private fun KtExpression.shouldShowStatementType(): Boolean {
override fun KtExpression.shouldShowStatementType(): Boolean {
if (parent !is KtBlockExpression) return true
if (parent.children.lastOrNull() == this) {
return isUsedAsExpression(analyze(BodyResolveMode.PARTIAL_WITH_CFA))
@@ -87,11 +50,6 @@ class KotlinExpressionTypeProvider : ExpressionTypeProvider<KtExpression>() {
return false
}
private fun KtExpression.isFunctionCallee(): Boolean {
val callExpression = parent as? KtCallExpression ?: return false
if (callExpression.calleeExpression != this) return false
return mainReference?.resolve() is KtFunction
}
override fun getInformationHint(element: KtExpression): String {
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
@@ -16,7 +16,7 @@ abstract class AbstractExpressionTypeTest : KotlinLightCodeInsightFixtureTestCas
protected fun doTest(path: String) {
myFixture.configureByFile(fileName())
val expressionTypeProvider = KotlinExpressionTypeProvider()
val expressionTypeProvider = KotlinExpressionTypeProviderDescriptorsImpl()
val elementAtCaret = myFixture.file.findElementAt(myFixture.editor.caretModel.offset)!!
val expressions = expressionTypeProvider.getExpressionsAt(elementAtCaret)
val types = expressions.map { "${it.text.replace('\n', ' ')} -> ${expressionTypeProvider.getInformationHint(it)}" }