Navigation: Support "Navigate/Related Symbol" for expects/actuals
#KT-20952 Fixed
This commit is contained in:
@@ -858,6 +858,8 @@
|
||||
<library.type implementation="org.jetbrains.kotlin.idea.framework.JSLibraryType"/>
|
||||
<library.type implementation="org.jetbrains.kotlin.idea.framework.CommonLibraryType"/>
|
||||
|
||||
<gotoRelatedProvider implementation="org.jetbrains.kotlin.idea.goto.KotlinExpectOrActualGotoRelatedProvider"/>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention</className>
|
||||
<category>Kotlin</category>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.goto
|
||||
|
||||
import com.intellij.navigation.GotoRelatedItem
|
||||
import com.intellij.navigation.GotoRelatedProvider
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.highlighter.markers.actualsForExpected
|
||||
import org.jetbrains.kotlin.idea.highlighter.markers.expectedDeclarationIfAny
|
||||
import org.jetbrains.kotlin.idea.highlighter.markers.isActualDeclaration
|
||||
import org.jetbrains.kotlin.idea.highlighter.markers.isExpectDeclaration
|
||||
import org.jetbrains.kotlin.idea.util.module
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
|
||||
|
||||
class KotlinExpectOrActualGotoRelatedProvider : GotoRelatedProvider() {
|
||||
private class ActualOrExpectGotoRelatedItem(element: PsiElement): GotoRelatedItem(element) {
|
||||
override fun getCustomContainerName(): String? {
|
||||
val module = element?.module ?: return null
|
||||
return "(in module ${module.name})"
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItems(psiElement: PsiElement): List<GotoRelatedItem> {
|
||||
val declaration = psiElement.getParentOfTypeAndBranch<KtNamedDeclaration> { nameIdentifier } ?: return emptyList()
|
||||
val targets = when {
|
||||
declaration.isExpectDeclaration() -> declaration.actualsForExpected()
|
||||
declaration.isActualDeclaration() -> listOfNotNull(declaration.expectedDeclarationIfAny())
|
||||
else -> emptyList()
|
||||
}
|
||||
return targets.map(::ActualOrExpectGotoRelatedItem)
|
||||
}
|
||||
}
|
||||
@@ -65,12 +65,6 @@ class KotlinLineMarkerProvider : LineMarkerProvider {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun KtNamedDeclaration.isExpectDeclaration(): Boolean =
|
||||
(toDescriptor() as? MemberDescriptor)?.isExpect == true
|
||||
|
||||
private fun KtNamedDeclaration.isActualDeclaration(): Boolean =
|
||||
(toDescriptor() as? MemberDescriptor)?.isActual == true
|
||||
|
||||
override fun collectSlowLineMarkers(elements: List<PsiElement>, result: MutableCollection<LineMarkerInfo<*>>) {
|
||||
if (elements.isEmpty()) return
|
||||
|
||||
@@ -120,6 +114,12 @@ class KotlinLineMarkerProvider : LineMarkerProvider {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KtNamedDeclaration.isExpectDeclaration(): Boolean =
|
||||
(toDescriptor() as? MemberDescriptor)?.isExpect == true
|
||||
|
||||
internal fun KtNamedDeclaration.isActualDeclaration(): Boolean =
|
||||
(toDescriptor() as? MemberDescriptor)?.isActual == true
|
||||
|
||||
private val OVERRIDING_MARK: Icon = AllIcons.Gutter.OverridingMethod
|
||||
private val IMPLEMENTING_MARK: Icon = AllIcons.Gutter.ImplementingMethod
|
||||
private val OVERRIDDEN_MARK: Icon = AllIcons.Gutter.OverridenMethod
|
||||
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
expect class Foo {
|
||||
fun bar()
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
actual class Foo {
|
||||
actual fun bar() {}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package test
|
||||
|
||||
actual class Foo {
|
||||
actual fun <caret>bar() {}
|
||||
}
|
||||
|
||||
// REF: [common] (in test.Foo).bar()
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
expect class Foo {
|
||||
val bar: Int
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
actual class Foo {
|
||||
actual val bar: Int get() = 1
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package test
|
||||
|
||||
actual class Foo {
|
||||
actual val <caret>bar: Int get() = 1
|
||||
}
|
||||
|
||||
// REF: [common] (in test.Foo).bar
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
package test
|
||||
|
||||
expect class Foo {
|
||||
fun <caret>bar()
|
||||
}
|
||||
|
||||
// REF: [jvm] (in test.Foo).bar()
|
||||
// REF: [js] (in test.Foo).bar()
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
actual class Foo {
|
||||
actual fun bar() {}
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
actual class Foo {
|
||||
actual fun bar() {}
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
package test
|
||||
|
||||
expect class Foo {
|
||||
val <caret>bar: Int
|
||||
}
|
||||
|
||||
// REF: [jvm] (in test.Foo).bar
|
||||
// REF: [js] (in test.Foo).bar
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
actual class Foo {
|
||||
actual val bar: Int get() = 1
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
actual class Foo {
|
||||
actual val bar: Int get() = 1
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
expect class Foo {
|
||||
class Bar
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
actual class Foo {
|
||||
actual class Bar
|
||||
}
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
package test
|
||||
|
||||
actual class Foo {
|
||||
actual class <caret>Bar
|
||||
}
|
||||
|
||||
// REF: [common] (in test.Foo).Bar
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
package test
|
||||
|
||||
expect class Foo {
|
||||
class <caret>Bar
|
||||
}
|
||||
|
||||
// REF: [js] (in test.Foo).Bar
|
||||
// REF: [jvm] (in test.Foo).Bar
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
actual class Foo {
|
||||
actual class Bar
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
actual class Foo {
|
||||
actual class Bar
|
||||
}
|
||||
idea/testData/navigation/relatedSymbols/multiModule/fromTopLevelActualClassToExpect/common/common.kt
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
expect class Foo
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
actual class Foo
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
actual class <caret>Foo
|
||||
|
||||
// REF: [common] (test).Foo
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
expect fun foo()
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
actual fun foo() {}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
actual fun <caret>foo() {}
|
||||
|
||||
// REF: [common] (test).foo()
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
expect val foo: Int
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
actual val foo: Int get() = 1
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
actual val <caret>foo: Int get() = 1
|
||||
|
||||
// REF: [common] (test).foo
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package test
|
||||
|
||||
expect class <caret>Foo
|
||||
|
||||
// REF: [jvm] (test).Foo
|
||||
// REF: [js] (test).Foo
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
actual class Foo
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
actual class Foo
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
package test
|
||||
|
||||
expect fun <caret>foo()
|
||||
|
||||
// REF: [jvm] (test).foo()
|
||||
// REF: [js] (test).foo()
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
actual fun foo() {}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
actual fun foo() {}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
package test
|
||||
|
||||
expect val <caret>foo: Int
|
||||
|
||||
// REF: [jvm] (test).foo
|
||||
// REF: [js] (test).foo
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
actual val foo: Int get() = 1
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
actual val foo: Int get() = 1
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.codeInsight.navigation.GotoTargetHandler
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.EditorFactory
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.config.TargetPlatformKind
|
||||
import org.jetbrains.kotlin.idea.project.PluginJetFilesProvider
|
||||
import org.jetbrains.kotlin.idea.stubs.AbstractMultiModuleTest
|
||||
import org.jetbrains.kotlin.idea.stubs.createFacet
|
||||
import org.jetbrains.kotlin.idea.test.extractMarkerOffset
|
||||
|
||||
abstract class AbstractKotlinNavigationMultiModuleTest : AbstractMultiModuleTest() {
|
||||
protected abstract fun doNavigate(editor: Editor, file: PsiFile): GotoTargetHandler.GotoData
|
||||
|
||||
protected fun doMultiPlatformTest(
|
||||
testFileName: String,
|
||||
commonModuleName: String = "common",
|
||||
vararg actuals: Pair<String, TargetPlatformKind<*>> = arrayOf("jvm" to TargetPlatformKind.Jvm[JvmTarget.JVM_1_6])
|
||||
) {
|
||||
val commonModule = module(commonModuleName)
|
||||
commonModule.createFacet(TargetPlatformKind.Common, false)
|
||||
|
||||
actuals.forEach { (actualName, actualKind) ->
|
||||
val implModule = module(actualName)
|
||||
implModule.createFacet(actualKind, implementedModuleName = commonModuleName)
|
||||
implModule.enableMultiPlatform()
|
||||
implModule.addDependency(commonModule)
|
||||
}
|
||||
|
||||
val file = PluginJetFilesProvider.allFilesInProject(myProject).single { it.name == testFileName }
|
||||
val doc = PsiDocumentManager.getInstance(myProject).getDocument(file)!!
|
||||
val offset = doc.extractMarkerOffset(project, "<caret>")
|
||||
val editor = EditorFactory.getInstance().createEditor(doc, myProject)
|
||||
editor.caretModel.moveToOffset(offset)
|
||||
try {
|
||||
val gotoData = doNavigate(editor, file)
|
||||
NavigationTestUtils.assertGotoDataMatching(editor, gotoData, true)
|
||||
}
|
||||
finally {
|
||||
EditorFactory.getInstance().releaseEditor(editor)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun doMultiPlatformTestJvmJs(testFileName: String, commonModuleName: String = "common") {
|
||||
doMultiPlatformTest(
|
||||
testFileName,
|
||||
commonModuleName,
|
||||
*arrayOf("jvm" to TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], "js" to TargetPlatformKind.JavaScript)
|
||||
)
|
||||
}
|
||||
}
|
||||
+4
-35
@@ -16,50 +16,19 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.navigation
|
||||
|
||||
import com.intellij.openapi.editor.EditorFactory
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.config.TargetPlatformKind
|
||||
import org.jetbrains.kotlin.idea.project.PluginJetFilesProvider
|
||||
import org.jetbrains.kotlin.idea.stubs.AbstractMultiModuleTest
|
||||
import org.jetbrains.kotlin.idea.stubs.createFacet
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.test.extractMarkerOffset
|
||||
import java.io.File
|
||||
|
||||
class KotlinGotoImplementationMultiModuleTest : AbstractMultiModuleTest() {
|
||||
class KotlinGotoImplementationMultiModuleTest : AbstractKotlinNavigationMultiModuleTest() {
|
||||
override fun getTestDataPath(): String {
|
||||
return File(PluginTestCaseBase.getTestDataPathBase(), "/navigation/implementations/multiModule").path + File.separator
|
||||
}
|
||||
|
||||
private fun doMultiPlatformTest(
|
||||
testFileName: String,
|
||||
commonModuleName: String = "common",
|
||||
vararg actuals: Pair<String, TargetPlatformKind<*>> = arrayOf("jvm" to TargetPlatformKind.Jvm[JvmTarget.JVM_1_6])
|
||||
) {
|
||||
val commonModule = module(commonModuleName)
|
||||
commonModule.createFacet(TargetPlatformKind.Common, false)
|
||||
|
||||
actuals.forEach { (actualName, actualKind) ->
|
||||
val implModule = module(actualName)
|
||||
implModule.createFacet(actualKind, implementedModuleName = commonModuleName)
|
||||
implModule.enableMultiPlatform()
|
||||
implModule.addDependency(commonModule)
|
||||
}
|
||||
|
||||
val file = PluginJetFilesProvider.allFilesInProject(myProject).single { it.name == testFileName }
|
||||
val doc = PsiDocumentManager.getInstance(myProject).getDocument(file)!!
|
||||
val offset = doc.extractMarkerOffset(project, "<caret>")
|
||||
val editor = EditorFactory.getInstance().createEditor(doc, myProject)
|
||||
editor.caretModel.moveToOffset(offset)
|
||||
try {
|
||||
val gotoData = NavigationTestUtils.invokeGotoImplementations(editor, file)
|
||||
NavigationTestUtils.assertGotoDataMatching(editor, gotoData, true)
|
||||
}
|
||||
finally {
|
||||
EditorFactory.getInstance().releaseEditor(editor)
|
||||
}
|
||||
}
|
||||
override fun doNavigate(editor: Editor, file: PsiFile) = NavigationTestUtils.invokeGotoImplementations(editor, file)
|
||||
|
||||
fun testSuspendFunImpl() {
|
||||
doMultiPlatformTest(
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.codeInsight.navigation.GotoTargetHandler
|
||||
import com.intellij.codeInsight.navigation.NavigationUtil
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import java.io.File
|
||||
|
||||
class KotlinGotoRelatedSymbolMultiModuleTest : AbstractKotlinNavigationMultiModuleTest() {
|
||||
override fun getTestDataPath() =
|
||||
File(PluginTestCaseBase.getTestDataPathBase(), "/navigation/relatedSymbols/multiModule").path + File.separator
|
||||
|
||||
override fun doNavigate(editor: Editor, file: PsiFile): GotoTargetHandler.GotoData {
|
||||
val source = file.findElementAt(editor.caretModel.offset)!!
|
||||
val relatedItems = NavigationUtil.collectRelatedItems(source, null)
|
||||
return GotoTargetHandler.GotoData(source, relatedItems.map { it.element }.toTypedArray(), emptyList())
|
||||
}
|
||||
|
||||
fun testFromTopLevelExpectClassToActuals() {
|
||||
doMultiPlatformTestJvmJs("common.kt")
|
||||
}
|
||||
|
||||
fun testFromTopLevelActualClassToExpect() {
|
||||
doMultiPlatformTestJvmJs("jvm.kt")
|
||||
}
|
||||
|
||||
fun testFromTopLevelExpectFunToActuals() {
|
||||
doMultiPlatformTestJvmJs("common.kt")
|
||||
}
|
||||
|
||||
fun testFromTopLevelActualFunToExpect() {
|
||||
doMultiPlatformTestJvmJs("jvm.kt")
|
||||
}
|
||||
|
||||
fun testFromTopLevelExpectValToActuals() {
|
||||
doMultiPlatformTestJvmJs("common.kt")
|
||||
}
|
||||
|
||||
fun testFromTopLevelActualValToExpect() {
|
||||
doMultiPlatformTestJvmJs("jvm.kt")
|
||||
}
|
||||
|
||||
fun testFromNestedExpectClassToActuals() {
|
||||
doMultiPlatformTestJvmJs("common.kt")
|
||||
}
|
||||
|
||||
fun testFromNestedActualClassToExpect() {
|
||||
doMultiPlatformTestJvmJs("jvm.kt")
|
||||
}
|
||||
|
||||
fun testFromExpectMemberFunToActuals() {
|
||||
doMultiPlatformTestJvmJs("common.kt")
|
||||
}
|
||||
|
||||
fun testFromActualMemberFunToExpect() {
|
||||
doMultiPlatformTestJvmJs("jvm.kt")
|
||||
}
|
||||
|
||||
fun testFromExpectMemberValToActuals() {
|
||||
doMultiPlatformTestJvmJs("common.kt")
|
||||
}
|
||||
|
||||
fun testFromActualMemberValToExpect() {
|
||||
doMultiPlatformTestJvmJs("jvm.kt")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user