Add additional options to DescriptorRender
1) renderContextNearLocalVariable - if true renderer add fq name for local variables after "@" symbol 2) fullContextForLocalVariable - if true then renderer add all fq names for local variables, else only last one 3) useBaseClassAsReceiver - if true then renderer uses dispatch receiver as extension receiver if the last is null
This commit is contained in:
committed by
Mikhail Glukhikh
parent
1879fd19eb
commit
6fa7435cd4
+79
-18
@@ -5,21 +5,22 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.compiler.visualizer
|
package org.jetbrains.kotlin.compiler.visualizer
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||||
|
import org.jetbrains.kotlin.builtins.*
|
||||||
import org.jetbrains.kotlin.compiler.visualizer.Annotator.annotate
|
import org.jetbrains.kotlin.compiler.visualizer.Annotator.annotate
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
|
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||||
import org.jetbrains.kotlin.renderer.render
|
import org.jetbrains.kotlin.renderer.render
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext.*
|
import org.jetbrains.kotlin.resolve.BindingContext.*
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.types.*
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
|
||||||
|
|
||||||
class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult): BaseRenderer {
|
class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult) : BaseRenderer {
|
||||||
val bindingContext = analysisResult.bindingContext
|
val bindingContext = analysisResult.bindingContext
|
||||||
private val annotations = mutableListOf<Annotator.AnnotationInfo>()
|
private val annotations = mutableListOf<Annotator.AnnotationInfo>()
|
||||||
|
|
||||||
@@ -31,6 +32,14 @@ class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult): Bas
|
|||||||
withSourceFileForTopLevel = false
|
withSourceFileForTopLevel = false
|
||||||
renderConstructorKeyword = false
|
renderConstructorKeyword = false
|
||||||
classifierNamePolicy = ClassifierNamePolicy.SOURCE_CODE_QUALIFIED
|
classifierNamePolicy = ClassifierNamePolicy.SOURCE_CODE_QUALIFIED
|
||||||
|
includeAdditionalModifiers = false
|
||||||
|
renderContextNearLocalVariable = true
|
||||||
|
fullContextForLocalVariable = false
|
||||||
|
useBaseClassAsReceiver = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addAnnotation(text: String, range: TextRange) {
|
||||||
|
annotations.add(Annotator.AnnotationInfo(text, range))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun render(): String {
|
override fun render(): String {
|
||||||
@@ -39,6 +48,18 @@ class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult): Bas
|
|||||||
}
|
}
|
||||||
|
|
||||||
inner class Renderer : KtVisitorVoid() {
|
inner class Renderer : KtVisitorVoid() {
|
||||||
|
private fun renderType(type: KotlinType?): String {
|
||||||
|
return type?.let { descriptorRenderer.renderType(it) } ?: "[ERROR: unknown type]"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderType(descriptor: CallableDescriptor?): String {
|
||||||
|
return renderType(descriptor?.returnType)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderType(expression: KtExpression?): String {
|
||||||
|
return renderType(expression?.let { bindingContext.getType(it) })
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitElement(element: PsiElement) {
|
override fun visitElement(element: PsiElement) {
|
||||||
element.acceptChildren(this)
|
element.acceptChildren(this)
|
||||||
}
|
}
|
||||||
@@ -47,27 +68,67 @@ class PsiRenderer(private val file: KtFile, analysisResult: AnalysisResult): Bas
|
|||||||
element.acceptChildren(this)
|
element.acceptChildren(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitProperty(property: KtProperty) {
|
private fun renderVariableType(variable: KtVariableDeclaration) {
|
||||||
val descriptor = bindingContext[VARIABLE, property]
|
val descriptor = bindingContext[VARIABLE, variable]
|
||||||
annotations.add(Annotator.AnnotationInfo(descriptor?.returnType.toString(), property.nameIdentifier!!.textRange))
|
addAnnotation(renderType(descriptor), variable.nameIdentifier!!.textRange)
|
||||||
super.visitProperty(property)
|
variable.acceptChildren(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitProperty(property: KtProperty) =
|
||||||
|
renderVariableType(property)
|
||||||
|
|
||||||
|
override fun visitDestructuringDeclarationEntry(multiDeclarationEntry: KtDestructuringDeclarationEntry) =
|
||||||
|
renderVariableType(multiDeclarationEntry)
|
||||||
|
|
||||||
|
override fun visitTypeReference(typeReference: KtTypeReference) {
|
||||||
|
val type = bindingContext[TYPE, typeReference]
|
||||||
|
addAnnotation(renderType(type), typeReference.textRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitConstantExpression(expression: KtConstantExpression) {
|
override fun visitConstantExpression(expression: KtConstantExpression) {
|
||||||
val type = bindingContext.getType(expression)
|
addAnnotation(renderType(expression), expression.textRange)
|
||||||
annotations.add(Annotator.AnnotationInfo(type.toString(), expression.textRange))
|
}
|
||||||
|
|
||||||
|
private fun renderCall(call: Call): String {
|
||||||
|
val resolvedCall = bindingContext[RESOLVED_CALL, call] ?: return "[ERROR: not resolved]"
|
||||||
|
|
||||||
|
val descriptor = resolvedCall.resultingDescriptor
|
||||||
|
var annotation = descriptorRenderer.render(descriptor)
|
||||||
|
if (descriptor.name.asString() == "<init>") annotation = annotation.replace("(", "<init>(")
|
||||||
|
resolvedCall.typeArguments.forEach { (type, value) ->
|
||||||
|
annotation = annotation.replaceFirst(type.name.toString(), "$value")
|
||||||
|
}
|
||||||
|
return annotation
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||||
val resolvedCall = bindingContext[RESOLVED_CALL, bindingContext[CALL, expression]]
|
val call = expression.getCall(bindingContext)
|
||||||
if (resolvedCall == null || resolvedCall.resultingDescriptor == null) {
|
if (call != null) {
|
||||||
|
addAnnotation(renderCall(call), expression.textRange)
|
||||||
super.visitSimpleNameExpression(expression)
|
super.visitSimpleNameExpression(expression)
|
||||||
return
|
} else {
|
||||||
|
val qualifierDescriptor = bindingContext[QUALIFIER, expression]?.descriptor
|
||||||
|
if (qualifierDescriptor != null) {
|
||||||
|
addAnnotation(descriptorRenderer.render(qualifierDescriptor), expression.textRange)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val annotation = descriptorRenderer.render(resolvedCall.resultingDescriptor)
|
override fun visitIfExpression(expression: KtIfExpression) {
|
||||||
val fqName = DescriptorUtils.getFqName(resolvedCall.resultingDescriptor.containingDeclaration)
|
addAnnotation(renderType(expression), expression.ifKeyword.textRange)
|
||||||
annotations.add(Annotator.AnnotationInfo("$fqName.$annotation", expression.textRange))
|
super.visitIfExpression(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitWhenExpression(expression: KtWhenExpression) {
|
||||||
|
addAnnotation(renderType(expression), expression.whenKeyword.textRange)
|
||||||
|
super.visitWhenExpression(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitWhenEntry(jetWhenEntry: KtWhenEntry) {
|
||||||
|
val arrowTextRange = jetWhenEntry.expression?.textRange?.shiftLeft(3)
|
||||||
|
?: jetWhenEntry.conditions.last().textRange.shiftRight(3)
|
||||||
|
addAnnotation(renderType(jetWhenEntry.expression), arrowTextRange)
|
||||||
|
super.visitWhenEntry(jetWhenEntry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,6 +246,10 @@ interface DescriptorRendererOptions {
|
|||||||
var parameterNamesInFunctionalTypes: Boolean
|
var parameterNamesInFunctionalTypes: Boolean
|
||||||
var renderFunctionContracts: Boolean
|
var renderFunctionContracts: Boolean
|
||||||
var presentableUnresolvedTypes: Boolean
|
var presentableUnresolvedTypes: Boolean
|
||||||
|
|
||||||
|
var renderContextNearLocalVariable: Boolean
|
||||||
|
var fullContextForLocalVariable: Boolean
|
||||||
|
var useBaseClassAsReceiver: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
object ExcludedTypeAnnotations {
|
object ExcludedTypeAnnotations {
|
||||||
|
|||||||
@@ -106,6 +106,17 @@ internal class DescriptorRendererImpl(
|
|||||||
|
|
||||||
private fun renderFqName(pathSegments: List<Name>) = escape(org.jetbrains.kotlin.renderer.renderFqName(pathSegments))
|
private fun renderFqName(pathSegments: List<Name>) = escape(org.jetbrains.kotlin.renderer.renderFqName(pathSegments))
|
||||||
|
|
||||||
|
private fun renderFqNameForLocalVariables(fqName: FqNameUnsafe): String =
|
||||||
|
if (fqName.isRoot) {
|
||||||
|
"<root>"
|
||||||
|
} else {
|
||||||
|
if (fullContextForLocalVariable) {
|
||||||
|
renderFqName(fqName)
|
||||||
|
} else {
|
||||||
|
renderFqName(fqName).substringAfterLast(".")
|
||||||
|
}
|
||||||
|
}.replace(".", "/")
|
||||||
|
|
||||||
override fun renderClassifierName(klass: ClassifierDescriptor): String = if (ErrorUtils.isError(klass)) {
|
override fun renderClassifierName(klass: ClassifierDescriptor): String = if (ErrorUtils.isError(klass)) {
|
||||||
klass.typeConstructor.toString()
|
klass.typeConstructor.toString()
|
||||||
} else
|
} else
|
||||||
@@ -709,6 +720,8 @@ internal class DescriptorRendererImpl(
|
|||||||
|
|
||||||
private fun renderReceiver(callableDescriptor: CallableDescriptor, builder: StringBuilder) {
|
private fun renderReceiver(callableDescriptor: CallableDescriptor, builder: StringBuilder) {
|
||||||
val receiver = callableDescriptor.extensionReceiverParameter
|
val receiver = callableDescriptor.extensionReceiverParameter
|
||||||
|
?: (if (useBaseClassAsReceiver) callableDescriptor.dispatchReceiverParameter else null)
|
||||||
|
val extensionIsNull = callableDescriptor.extensionReceiverParameter == null
|
||||||
if (receiver != null) {
|
if (receiver != null) {
|
||||||
builder.renderAnnotations(receiver, AnnotationUseSiteTarget.RECEIVER)
|
builder.renderAnnotations(receiver, AnnotationUseSiteTarget.RECEIVER)
|
||||||
|
|
||||||
@@ -717,7 +730,8 @@ internal class DescriptorRendererImpl(
|
|||||||
if (shouldRenderAsPrettyFunctionType(type) && !TypeUtils.isNullableType(type)) {
|
if (shouldRenderAsPrettyFunctionType(type) && !TypeUtils.isNullableType(type)) {
|
||||||
result = "($result)"
|
result = "($result)"
|
||||||
}
|
}
|
||||||
builder.append(result).append(".")
|
// @ symbol is used to distinguish extension receiver from dispatch
|
||||||
|
builder.append(result).append(if (extensionIsNull) "@" else ".")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -809,7 +823,7 @@ internal class DescriptorRendererImpl(
|
|||||||
builder: StringBuilder,
|
builder: StringBuilder,
|
||||||
topLevel: Boolean
|
topLevel: Boolean
|
||||||
) {
|
) {
|
||||||
if (topLevel) {
|
if (topLevel && !renderContextNearLocalVariable) {
|
||||||
builder.append(renderKeyword("value-parameter")).append(" ")
|
builder.append(renderKeyword("value-parameter")).append(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -862,6 +876,11 @@ internal class DescriptorRendererImpl(
|
|||||||
|
|
||||||
if (includeName) {
|
if (includeName) {
|
||||||
renderName(variable, builder, topLevel)
|
renderName(variable, builder, topLevel)
|
||||||
|
if (topLevel && renderContextNearLocalVariable) {
|
||||||
|
val containingDeclaration = variable.containingDeclaration
|
||||||
|
val fqName = DescriptorUtils.getFqName(containingDeclaration)
|
||||||
|
builder.append("@").append(renderFqNameForLocalVariables(fqName))
|
||||||
|
}
|
||||||
builder.append(": ")
|
builder.append(": ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -127,4 +127,8 @@ internal class DescriptorRendererOptionsImpl : DescriptorRendererOptions {
|
|||||||
override var presentableUnresolvedTypes: Boolean by property(false)
|
override var presentableUnresolvedTypes: Boolean by property(false)
|
||||||
|
|
||||||
override var boldOnlyForNamesInHtml: Boolean by property(false)
|
override var boldOnlyForNamesInHtml: Boolean by property(false)
|
||||||
|
|
||||||
|
override var renderContextNearLocalVariable by property(false)
|
||||||
|
override var fullContextForLocalVariable by property(false)
|
||||||
|
override var useBaseClassAsReceiver by property(false)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user