Presentation: Do not use resolve to compute element description
Invoking analysis on EDT may degrade editor performance
This commit is contained in:
@@ -29,23 +29,72 @@ import com.intellij.usageView.UsageViewShortNameLocation
|
|||||||
import com.intellij.usageView.UsageViewTypeLocation
|
import com.intellij.usageView.UsageViewTypeLocation
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
||||||
import org.jetbrains.kotlin.asJava.unwrapped
|
import org.jetbrains.kotlin.asJava.unwrapped
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||||
import org.jetbrains.kotlin.idea.refactoring.rename.RenameJavaSyntheticPropertyHandler
|
import org.jetbrains.kotlin.idea.refactoring.rename.RenameJavaSyntheticPropertyHandler
|
||||||
import org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinPropertyProcessor
|
import org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinPropertyProcessor
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
|
||||||
import org.jetbrains.kotlin.idea.util.string.collapseSpaces
|
import org.jetbrains.kotlin.idea.util.string.collapseSpaces
|
||||||
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
|
||||||
|
|
||||||
class KotlinElementDescriptionProvider : ElementDescriptionProvider {
|
class KotlinElementDescriptionProvider : ElementDescriptionProvider {
|
||||||
companion object {
|
private tailrec fun KtNamedDeclaration.parentForFqName(): KtNamedDeclaration? {
|
||||||
val REFACTORING_RENDERER = DescriptorRenderer.ONLY_NAMES_WITH_SHORT_TYPES.withOptions {
|
val parent = getStrictParentOfType<KtNamedDeclaration>() ?: return null
|
||||||
withoutReturnType = true
|
if (parent is KtProperty && parent.isLocal) return parent.parentForFqName()
|
||||||
renderConstructorKeyword = false
|
return parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KtNamedDeclaration.fqName(): FqNameUnsafe {
|
||||||
|
val internalSegments = generateSequence(this) { it.parentForFqName() }
|
||||||
|
.filterIsInstance<KtNamedDeclaration>()
|
||||||
|
.map { it.name ?: "<no name provided>" }
|
||||||
|
.toList()
|
||||||
|
.asReversed()
|
||||||
|
val packageSegments = containingKtFile.packageFqName.pathSegments()
|
||||||
|
return FqNameUnsafe((packageSegments + internalSegments).joinToString("."))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtTypeReference.renderShort(): String {
|
||||||
|
return accept(
|
||||||
|
object : KtVisitor<String, Unit>() {
|
||||||
|
private val visitor get() = this
|
||||||
|
|
||||||
|
override fun visitTypeReference(typeReference: KtTypeReference, data: Unit): String {
|
||||||
|
val typeText = typeReference.typeElement?.accept(this, data) ?: "???"
|
||||||
|
return if (typeReference.hasParentheses()) "($typeText)" else typeText
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitDynamicType(type: KtDynamicType, data: Unit) = type.text
|
||||||
|
|
||||||
|
override fun visitFunctionType(type: KtFunctionType, data: Unit): String {
|
||||||
|
return buildString {
|
||||||
|
type.receiverTypeReference?.let { append(it.accept(visitor, data)).append('.') }
|
||||||
|
type.parameters.joinTo(this, prefix = "(", postfix = ")") { it.accept(visitor, data) }
|
||||||
|
append(" -> ")
|
||||||
|
append(type.returnTypeReference?.accept(visitor, data) ?: "???")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitNullableType(nullableType: KtNullableType, data: Unit): String {
|
||||||
|
val innerTypeText = nullableType.innerType?.accept(this, data) ?: return "???"
|
||||||
|
return "$innerTypeText?"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitSelfType(type: KtSelfType, data: Unit) = type.text
|
||||||
|
|
||||||
|
override fun visitUserType(type: KtUserType, data: Unit): String {
|
||||||
|
return buildString {
|
||||||
|
append(type.referencedName ?: "???")
|
||||||
|
|
||||||
|
val arguments = type.typeArgumentsAsTypes
|
||||||
|
if (arguments.isNotEmpty()) {
|
||||||
|
arguments.joinTo(this, prefix = "<", postfix = ">") { it.accept(visitor, data) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Unit
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getElementDescription(element: PsiElement, location: ElementDescriptionLocation): String? {
|
override fun getElementDescription(element: PsiElement, location: ElementDescriptionLocation): String? {
|
||||||
@@ -85,26 +134,32 @@ class KotlinElementDescriptionProvider : ElementDescriptionProvider {
|
|||||||
is UsageViewShortNameLocation, is UsageViewLongNameLocation -> namedElement.name
|
is UsageViewShortNameLocation, is UsageViewLongNameLocation -> namedElement.name
|
||||||
is RefactoringDescriptionLocation -> {
|
is RefactoringDescriptionLocation -> {
|
||||||
val kind = elementKind() ?: return null
|
val kind = elementKind() ?: return null
|
||||||
val descriptor = (namedElement as KtDeclaration).descriptor ?: return null
|
if (namedElement !is KtNamedDeclaration) return null
|
||||||
val renderFqName = location.includeParent() &&
|
val renderFqName = location.includeParent() &&
|
||||||
namedElement !is KtTypeParameter &&
|
namedElement !is KtTypeParameter &&
|
||||||
namedElement !is KtParameter &&
|
namedElement !is KtParameter &&
|
||||||
namedElement !is KtConstructor<*>
|
namedElement !is KtConstructor<*>
|
||||||
val desc = when (descriptor) {
|
val desc = when (namedElement) {
|
||||||
is FunctionDescriptor -> {
|
is KtFunction -> {
|
||||||
val baseText = REFACTORING_RENDERER.render(descriptor)
|
val baseText = buildString {
|
||||||
val parentFqName = if (renderFqName) descriptor.containingDeclaration.fqNameSafe else null
|
append(namedElement.name ?: "")
|
||||||
|
namedElement.valueParameters.joinTo(this, prefix = "(", postfix = ")") {
|
||||||
|
(if (it.isVarArg) "vararg " else "") + (it.typeReference?.renderShort() ?: "")
|
||||||
|
}
|
||||||
|
namedElement.receiverTypeReference?.let { append(" on ").append(it.renderShort()) }
|
||||||
|
}
|
||||||
|
val parentFqName = if (renderFqName) namedElement.fqName().parent() else null
|
||||||
if (parentFqName?.isRoot ?: true) baseText else "${parentFqName!!.asString()}.$baseText"
|
if (parentFqName?.isRoot ?: true) baseText else "${parentFqName!!.asString()}.$baseText"
|
||||||
}
|
}
|
||||||
else -> if (renderFqName) DescriptorUtils.getFqName(descriptor).asString() else descriptor.name.asString()
|
else -> (if (renderFqName) namedElement.fqName().asString() else namedElement.name) ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
"$kind ${CommonRefactoringUtil.htmlEmphasize(desc)}"
|
"$kind ${CommonRefactoringUtil.htmlEmphasize(desc)}"
|
||||||
}
|
}
|
||||||
is HighlightUsagesDescriptionLocation -> {
|
is HighlightUsagesDescriptionLocation -> {
|
||||||
val kind = elementKind() ?: return null
|
val kind = elementKind() ?: return null
|
||||||
val descriptor = (namedElement as KtDeclaration).descriptor ?: return null
|
if (namedElement !is KtNamedDeclaration) return null
|
||||||
"$kind ${descriptor.name.asString()}"
|
"$kind ${namedElement.name}"
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -11,11 +11,11 @@ Crumbs:
|
|||||||
Something().apply{…}
|
Something().apply{…}
|
||||||
Tooltips:
|
Tooltips:
|
||||||
function <b><code>foo()</code></b>
|
function <b><code>foo()</code></b>
|
||||||
lambda <b><code>foo.<anonymous>() on StringBuilder /* = StringBuilder */</code></b>
|
lambda <b><code>foo.<anonymous>()</code></b>
|
||||||
lambda <b><code>foo.<anonymous>.<anonymous>() on ???</code></b>
|
lambda <b><code>foo.<anonymous>.<anonymous>()</code></b>
|
||||||
lambda <b><code>foo.<anonymous>.<anonymous>.<anonymous>()</code></b>
|
lambda <b><code>foo.<anonymous>.<anonymous>.<anonymous>()</code></b>
|
||||||
lambda <b><code>foo.<anonymous>.<anonymous>.<anonymous>.<anonymous>()</code></b>
|
lambda <b><code>foo.<anonymous>.<anonymous>.<anonymous>.<anonymous>()</code></b>
|
||||||
lambda <b><code>foo.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>(???)</code></b>
|
lambda <b><code>foo.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>()</code></b>
|
||||||
lambda <b><code>foo.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>()</code></b>
|
lambda <b><code>foo.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>()</code></b>
|
||||||
lambda <b><code>foo.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>(Int)</code></b>
|
lambda <b><code>foo.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>(Int)</code></b>
|
||||||
lambda <b><code>foo.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>()</code></b>
|
lambda <b><code>foo.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>.<anonymous>()</code></b>
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// SHOULD_FAIL_WITH: 'foo' in class B will require class instance, 'foo' in function test() will require class instance, 'foo' in function test() will require class instance, 'foo' in lambda <anonymous>() on A.Companion will require class instance
|
// SHOULD_FAIL_WITH: 'foo' in class B will require class instance, 'foo' in function test() will require class instance, 'foo' in function test() will require class instance, 'foo' in lambda <anonymous>() will require class instance
|
||||||
class A {
|
class A {
|
||||||
companion object {
|
companion object {
|
||||||
class B {
|
class B {
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// SHOULD_FAIL_WITH: 'foo' in class B will require class instance, 'foo' in function test() will require class instance, 'foo' in function test() will require class instance, 'foo' in lambda <anonymous>() on A.Companion will require class instance
|
// SHOULD_FAIL_WITH: 'foo' in class B will require class instance, 'foo' in function test() will require class instance, 'foo' in function test() will require class instance, 'foo' in lambda <anonymous>() will require class instance
|
||||||
class A {
|
class A {
|
||||||
companion object {
|
companion object {
|
||||||
class B {
|
class B {
|
||||||
|
|||||||
Reference in New Issue
Block a user