Navigate to receiver from this in extension function (KT-16991)
#KT-16991 Fixed #KT-13013 In Progress
This commit is contained in:
+18
-1
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiElement
|
|||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.psi.PsiReference
|
import com.intellij.psi.PsiReference
|
||||||
import com.intellij.util.BitUtil
|
import com.intellij.util.BitUtil
|
||||||
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
||||||
import org.jetbrains.kotlin.idea.intentions.isAutoCreatedItUsage
|
import org.jetbrains.kotlin.idea.intentions.isAutoCreatedItUsage
|
||||||
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
|
||||||
@@ -32,6 +33,7 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
|||||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.isAbstract
|
import org.jetbrains.kotlin.psi.psiUtil.isAbstract
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||||
|
|
||||||
class KotlinTargetElementEvaluator : TargetElementEvaluatorEx {
|
class KotlinTargetElementEvaluator : TargetElementEvaluatorEx {
|
||||||
@@ -48,6 +50,20 @@ class KotlinTargetElementEvaluator : TargetElementEvaluatorEx {
|
|||||||
val lambdaExpression = descriptorWithSource.source.getPsi()?.parent as? KtLambdaExpression ?: return null
|
val lambdaExpression = descriptorWithSource.source.getPsi()?.parent as? KtLambdaExpression ?: return null
|
||||||
return lambdaExpression.leftCurlyBrace.treeNext?.psi
|
return lambdaExpression.leftCurlyBrace.treeNext?.psi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Navigate to receiver element for this in extension declaration
|
||||||
|
fun findReceiverForThisInExtensionFunction(ref: PsiReference): PsiElement? {
|
||||||
|
val element: PsiElement = ref.element
|
||||||
|
if (element.text != "this") return null
|
||||||
|
|
||||||
|
if (element !is KtNameReferenceExpression) return null
|
||||||
|
val callableDescriptor = element.resolveMainReferenceToDescriptors().singleOrNull() as? CallableDescriptor ?: return null
|
||||||
|
|
||||||
|
if (!callableDescriptor.isExtension) return null
|
||||||
|
val callableDeclaration = callableDescriptor.source.getPsi() as? KtCallableDeclaration ?: return null
|
||||||
|
|
||||||
|
return callableDeclaration.receiverTypeReference
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun includeSelfInGotoImplementation(element: PsiElement): Boolean = !(element is KtClass && element.isAbstract())
|
override fun includeSelfInGotoImplementation(element: PsiElement): Boolean = !(element is KtClass && element.isAbstract())
|
||||||
@@ -67,7 +83,8 @@ class KotlinTargetElementEvaluator : TargetElementEvaluatorEx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (BitUtil.isSet(flags, TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED)) {
|
if (BitUtil.isSet(flags, TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED)) {
|
||||||
return findLambdaOpenLBraceForGeneratedIt(ref)
|
return findLambdaOpenLBraceForGeneratedIt(ref) ?:
|
||||||
|
findReceiverForThisInExtensionFunction(ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
class A {
|
||||||
|
fun String.foo() {
|
||||||
|
this<caret>@A
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
class <caret>A {
|
||||||
|
fun String.foo() {
|
||||||
|
this@A
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
class A {
|
||||||
|
fun String.foo() {
|
||||||
|
this<caret>@foo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
class A {
|
||||||
|
fun <caret>String.foo() {
|
||||||
|
this@foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
interface Foo
|
||||||
|
|
||||||
|
fun foo(a: Any) {}
|
||||||
|
|
||||||
|
fun Foo.bar() {
|
||||||
|
foo(this<caret>)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
interface Foo
|
||||||
|
|
||||||
|
fun foo(a: Any) {}
|
||||||
|
|
||||||
|
fun <caret>Foo.bar() {
|
||||||
|
foo(this)
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
interface Foo
|
||||||
|
|
||||||
|
fun foo(a: Foo.() -> Unit) {}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo {
|
||||||
|
<caret>this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
interface Foo
|
||||||
|
|
||||||
|
fun foo(a: Foo.() -> Unit) {}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo <caret>{
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// FILE: before.kt
|
||||||
|
val String.test: String
|
||||||
|
get() { return <caret>this }
|
||||||
|
|
||||||
|
// FILE: after.kt
|
||||||
|
val <caret>String.test: String
|
||||||
|
get() { return this }
|
||||||
@@ -65,4 +65,34 @@ public class GotoDeclarationTestGenerated extends AbstractGotoDeclarationTest {
|
|||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/itParameterInLambda.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/itParameterInLambda.test");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("labeledThisToClass.test")
|
||||||
|
public void testLabeledThisToClass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/labeledThisToClass.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("labeledThisToMemberExtension.test")
|
||||||
|
public void testLabeledThisToMemberExtension() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/labeledThisToMemberExtension.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("thisExtensionFunction.test")
|
||||||
|
public void testThisExtensionFunction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/thisExtensionFunction.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("thisExtensionLambda.test")
|
||||||
|
public void testThisExtensionLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/thisExtensionLambda.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("thisInExtensionPropertyAccessor.test")
|
||||||
|
public void testThisInExtensionPropertyAccessor() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/thisInExtensionPropertyAccessor.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user