Navigate to lambda declaration from generated 'it' (KT-16992)

#KT-16992 Fixed
 #KT-13013 In Progress
This commit is contained in:
Nikolay Krasko
2017-03-20 17:26:19 +03:00
parent c976c3d909
commit 9dfc92c55e
9 changed files with 203 additions and 7 deletions
@@ -24,9 +24,9 @@ import org.jetbrains.kotlin.allopen.AbstractBytecodeListingTestForAllOpen
import org.jetbrains.kotlin.android.*
import org.jetbrains.kotlin.android.configure.AbstractConfigureProjectTest
import org.jetbrains.kotlin.android.folding.AbstractAndroidResourceFoldingTest
import org.jetbrains.kotlin.android.quickfix.AbstractAndroidLintQuickfixTest
import org.jetbrains.kotlin.android.intention.AbstractAndroidResourceIntentionTest
import org.jetbrains.kotlin.android.lint.AbstractKotlinLintTest
import org.jetbrains.kotlin.android.quickfix.AbstractAndroidLintQuickfixTest
import org.jetbrains.kotlin.android.quickfix.AbstractAndroidQuickFixMultiFileTest
import org.jetbrains.kotlin.annotation.AbstractAnnotationProcessorBoxTest
import org.jetbrains.kotlin.annotation.processing.test.sourceRetention.AbstractBytecodeListingTestForSourceRetention
@@ -104,10 +104,7 @@ import org.jetbrains.kotlin.idea.kdoc.AbstractKDocHighlightingTest
import org.jetbrains.kotlin.idea.kdoc.AbstractKDocTypingTest
import org.jetbrains.kotlin.idea.maven.AbstractKotlinMavenInspectionTest
import org.jetbrains.kotlin.idea.maven.configuration.AbstractMavenConfigureProjectByChangingFileTest
import org.jetbrains.kotlin.idea.navigation.AbstractGotoSuperTest
import org.jetbrains.kotlin.idea.navigation.AbstractGotoTypeDeclarationTest
import org.jetbrains.kotlin.idea.navigation.AbstractKotlinGotoImplementationTest
import org.jetbrains.kotlin.idea.navigation.AbstractKotlinGotoTest
import org.jetbrains.kotlin.idea.navigation.*
import org.jetbrains.kotlin.idea.parameterInfo.AbstractParameterInfoTest
import org.jetbrains.kotlin.idea.quickfix.AbstractQuickFixMultiFileTest
import org.jetbrains.kotlin.idea.quickfix.AbstractQuickFixTest
@@ -506,6 +503,10 @@ fun main(args: Array<String>) {
model("navigation/gotoTypeDeclaration", extension = "test")
}
testClass<AbstractGotoDeclarationTest> {
model("navigation/gotoDeclaration", extension = "test")
}
testClass<AbstractParameterInfoTest> {
model("parameterInfo", recursive = true, excludeDirs = listOf("withLib1/sharedLib", "withLib2/sharedLib", "withLib3/sharedLib"))
}
@@ -22,16 +22,39 @@ import com.intellij.codeInsight.TargetElementUtil
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiReference
import com.intellij.util.BitUtil
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.idea.intentions.isAutoCreatedItUsage
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.psi.psiUtil.isAbstract
import org.jetbrains.kotlin.resolve.source.getPsi
class KotlinTargetElementEvaluator : TargetElementEvaluatorEx {
companion object {
// Place caret after the open curly brace in lambda for generated 'it'
fun findLambdaOpenLBraceForGeneratedIt(ref: PsiReference): PsiElement? {
val element: PsiElement = ref.element
if (element.text != "it") return null
if (element !is KtNameReferenceExpression || !isAutoCreatedItUsage(element)) return null
val itDescriptor = element.resolveMainReferenceToDescriptors().singleOrNull() ?: return null
val descriptorWithSource = itDescriptor.containingDeclaration as? DeclarationDescriptorWithSource ?: return null
val lambdaExpression = descriptorWithSource.source.getPsi()?.parent as? KtLambdaExpression ?: return null
return lambdaExpression.leftCurlyBrace.treeNext?.psi
}
}
override fun includeSelfInGotoImplementation(element: PsiElement): Boolean = !(element is KtClass && element.isAbstract())
override fun getElementByReference(ref: PsiReference, flags: Int): PsiElement? {
// prefer destructing declaration entry to its target if element name is accepted
if (ref is KtDestructuringDeclarationReference && flags.and(TargetElementUtil.ELEMENT_NAME_ACCEPTED) != 0) {
if (ref is KtDestructuringDeclarationReference && BitUtil.isSet(flags, TargetElementUtil.ELEMENT_NAME_ACCEPTED)) {
return ref.element
}
@@ -43,6 +66,10 @@ class KotlinTargetElementEvaluator : TargetElementEvaluatorEx {
}
}
if (BitUtil.isSet(flags, TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED)) {
return findLambdaOpenLBraceForGeneratedIt(ref)
}
return null
}
@@ -0,0 +1,23 @@
// FILE: before.kt
interface Foo
interface Bar
fun foo(a: Foo.(Bar) -> Unit) {}
fun bar() {
foo {
<caret>it
}
}
// FILE: after.kt
interface Foo
interface Bar
fun foo(a: Foo.(Bar) -> Unit) {}
fun bar() {
foo {<caret>
it
}
}
@@ -0,0 +1,23 @@
// FILE: before.kt
interface Foo
interface Bar
fun foo(b: Int, a: Foo.(Bar) -> Unit) {}
fun bar() {
foo(12, {
<caret>it
})
}
// FILE: after.kt
interface Foo
interface Bar
fun foo(b: Int, a: Foo.(Bar) -> Unit) {}
fun bar() {
foo(12, {<caret>
it
})
}
@@ -0,0 +1,5 @@
// FILE: before.kt
fun test(a: (Int) -> Unit = { <caret>it }) {}
// FILE: after.kt
fun test(a: (Int) -> Unit = {<caret> it }) {}
@@ -0,0 +1,5 @@
// FILE: before.kt
val a: (Int) -> Unit = { <caret>it }
// FILE: after.kt
val a: (Int) -> Unit = {<caret> it }
@@ -0,0 +1,23 @@
// FILE: before.kt
class Foo
fun <T> some(a: T, f: (T) -> Unit) {}
fun foo(a: Any) {}
fun baz() {
some(Foo()) {
foo(it<caret>)
}
}
// FILE: after.kt
class Foo
fun <T> some(a: T, f: (T) -> Unit) {}
fun foo(a: Any) {}
fun baz() {
some(Foo()) {<caret>
foo(it)
}
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.navigation
abstract class AbstractGotoDeclarationTest : AbstractGotoActionTest() {
override val actionName: String get() = "GotoDeclaration"
}
@@ -0,0 +1,68 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.navigation;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/testData/navigation/gotoDeclaration")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class GotoDeclarationTestGenerated extends AbstractGotoDeclarationTest {
public void testAllFilesPresentInGotoDeclaration() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/navigation/gotoDeclaration"), Pattern.compile("^(.+)\\.test$"), TargetBackend.ANY, true);
}
@TestMetadata("itExtensionLambda.test")
public void testItExtensionLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/itExtensionLambda.test");
doTest(fileName);
}
@TestMetadata("itExtensionLambdaInBrackets.test")
public void testItExtensionLambdaInBrackets() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/itExtensionLambdaInBrackets.test");
doTest(fileName);
}
@TestMetadata("itInLambdaAsDefaultArgument.test")
public void testItInLambdaAsDefaultArgument() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/itInLambdaAsDefaultArgument.test");
doTest(fileName);
}
@TestMetadata("itInLambdaWithoutCall.test")
public void testItInLambdaWithoutCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/itInLambdaWithoutCall.test");
doTest(fileName);
}
@TestMetadata("itParameterInLambda.test")
public void testItParameterInLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/gotoDeclaration/itParameterInLambda.test");
doTest(fileName);
}
}