Navigation from header to implementation and vice versa #KT-15204 Fixed
This commit is contained in:
+2
-2
@@ -131,7 +131,7 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CallableMemberDescriptor.findNamesakesFromTheSameModule(): Collection<CallableMemberDescriptor> {
|
fun CallableMemberDescriptor.findNamesakesFromTheSameModule(): Collection<CallableMemberDescriptor> {
|
||||||
val packageFqName = (containingDeclaration as? PackageFragmentDescriptor)?.fqName ?: return emptyList()
|
val packageFqName = (containingDeclaration as? PackageFragmentDescriptor)?.fqName ?: return emptyList()
|
||||||
val myModule = moduleToCheck ?: module
|
val myModule = moduleToCheck ?: module
|
||||||
val scope = myModule.getPackage(packageFqName).memberScope
|
val scope = myModule.getPackage(packageFqName).memberScope
|
||||||
@@ -145,7 +145,7 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ClassifierDescriptor.findClassifiersFromTheSameModule(): Collection<ClassifierDescriptor> {
|
fun ClassifierDescriptor.findClassifiersFromTheSameModule(): Collection<ClassifierDescriptor> {
|
||||||
val myModule = moduleToCheck ?: module
|
val myModule = moduleToCheck ?: module
|
||||||
// TODO: support nested classes
|
// TODO: support nested classes
|
||||||
return myModule.getPackage(fqNameSafe.parent()).memberScope
|
return myModule.getPackage(fqNameSafe.parent()).memberScope
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.highlighter.markers
|
||||||
|
|
||||||
|
import com.intellij.psi.NavigatablePsiElement
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.core.toDescriptor
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker
|
||||||
|
|
||||||
|
fun ModuleDescriptor.commonModuleOrNull() = allDependencyModules.filter {
|
||||||
|
it.platformKind == PlatformKind.DEFAULT && it.sourceKind == sourceKind
|
||||||
|
}.firstOrNull()
|
||||||
|
|
||||||
|
fun ModuleDescriptor.hasDeclarationOf(descriptor: MemberDescriptor) = declarationOf(descriptor) != null
|
||||||
|
|
||||||
|
private fun ModuleDescriptor.declarationOf(descriptor: MemberDescriptor) =
|
||||||
|
with (HeaderImplDeclarationChecker(this)) {
|
||||||
|
when (descriptor) {
|
||||||
|
is CallableMemberDescriptor ->
|
||||||
|
descriptor.findNamesakesFromTheSameModule().filter { it.isHeader }.firstOrNull()
|
||||||
|
is ClassDescriptor ->
|
||||||
|
descriptor.findClassifiersFromTheSameModule().filter { it is ClassDescriptor && it.isHeader }.firstOrNull()
|
||||||
|
else ->
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getHeaderDeclarationTooltip(declaration: KtDeclaration): String? {
|
||||||
|
val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return null
|
||||||
|
val platformModuleDescriptor = declaration.containingKtFile.findModuleDescriptor()
|
||||||
|
|
||||||
|
val commonModuleDescriptor = platformModuleDescriptor.commonModuleOrNull() ?: return null
|
||||||
|
if (!commonModuleDescriptor.hasDeclarationOf(descriptor)) return null
|
||||||
|
|
||||||
|
return "Has declaration in common module"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun navigateToHeaderDeclaration(declaration: KtDeclaration) {
|
||||||
|
val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return
|
||||||
|
val platformModuleDescriptor = declaration.containingKtFile.findModuleDescriptor()
|
||||||
|
|
||||||
|
val commonModuleDescriptor = platformModuleDescriptor.commonModuleOrNull() ?: return
|
||||||
|
val headerDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(
|
||||||
|
commonModuleDescriptor.declarationOf(descriptor) ?: return
|
||||||
|
) as? NavigatablePsiElement ?: return
|
||||||
|
|
||||||
|
headerDeclaration.navigate(false)
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.highlighter.markers
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator
|
||||||
|
import com.intellij.ide.util.DefaultPsiElementCellRenderer
|
||||||
|
import com.intellij.psi.NavigatablePsiElement
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.core.toDescriptor
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker
|
||||||
|
import java.awt.event.MouseEvent
|
||||||
|
|
||||||
|
fun ModuleDescriptor.hasImplementationsOf(descriptor: MemberDescriptor) =
|
||||||
|
implementationsOf(descriptor).isNotEmpty()
|
||||||
|
|
||||||
|
private fun ModuleDescriptor.implementationsOf(descriptor: MemberDescriptor) =
|
||||||
|
with (HeaderImplDeclarationChecker(this)) {
|
||||||
|
when (descriptor) {
|
||||||
|
is CallableMemberDescriptor -> descriptor.findNamesakesFromTheSameModule().filter { it.isImpl }
|
||||||
|
is ClassDescriptor -> descriptor.findClassifiersFromTheSameModule().filter { it is ClassDescriptor && it.isImpl }
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPlatformImplementationTooltip(declaration: KtDeclaration): String? {
|
||||||
|
val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return null
|
||||||
|
val commonModuleDescriptor = declaration.containingKtFile.findModuleDescriptor()
|
||||||
|
|
||||||
|
val platformModulesWithImplementation = commonModuleDescriptor.allImplementingModules.filter {
|
||||||
|
it.hasImplementationsOf(descriptor)
|
||||||
|
}
|
||||||
|
if (platformModulesWithImplementation.isEmpty()) return null
|
||||||
|
|
||||||
|
return platformModulesWithImplementation.joinToString(prefix = "Has implementations in ") {
|
||||||
|
it.platformKind.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun navigateToPlatformImplementation(e: MouseEvent?, declaration: KtDeclaration) {
|
||||||
|
val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return
|
||||||
|
val commonModuleDescriptor = declaration.containingKtFile.findModuleDescriptor()
|
||||||
|
|
||||||
|
val implementations = commonModuleDescriptor.allImplementingModules.flatMap {
|
||||||
|
it.implementationsOf(descriptor)
|
||||||
|
}.mapNotNull { DescriptorToSourceUtils.descriptorToDeclaration(it) as? NavigatablePsiElement }
|
||||||
|
if (implementations.isEmpty()) return
|
||||||
|
|
||||||
|
val renderer = DefaultPsiElementCellRenderer()
|
||||||
|
PsiElementListNavigator.openTargets(e,
|
||||||
|
implementations.toTypedArray(),
|
||||||
|
"Choose implementation of ${declaration.name}",
|
||||||
|
"Implementations of ${declaration.name}",
|
||||||
|
renderer)
|
||||||
|
}
|
||||||
@@ -31,8 +31,9 @@ import com.intellij.psi.PsiNameIdentifierOwner
|
|||||||
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
import com.intellij.psi.search.searches.ClassInheritorsSearch
|
||||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||||
import org.jetbrains.kotlin.asJava.toLightClass
|
import org.jetbrains.kotlin.asJava.toLightClass
|
||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.core.toDescriptor
|
||||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -78,6 +79,15 @@ class KotlinLineMarkerProvider : LineMarkerProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (element is KtNamedDeclaration) {
|
||||||
|
if (element.hasModifier(KtTokens.HEADER_KEYWORD)) {
|
||||||
|
collectImplementationMarkers(element, result)
|
||||||
|
}
|
||||||
|
else if (element.hasModifier(KtTokens.IMPL_KEYWORD)) {
|
||||||
|
collectHeaderMarkers(element, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
collectOverriddenFunctions(functions, result)
|
collectOverriddenFunctions(functions, result)
|
||||||
@@ -114,6 +124,26 @@ private val OVERRIDDEN_PROPERTY = MarkerType(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
private val PLATFORM_IMPLEMENTATION = MarkerType(
|
||||||
|
"PLATFORM_IMPLEMENTATION",
|
||||||
|
{ it?.let { getPlatformImplementationTooltip(it.parent as KtDeclaration) } },
|
||||||
|
object : LineMarkerNavigator() {
|
||||||
|
override fun browse(e: MouseEvent?, element: PsiElement?) {
|
||||||
|
element?.let { navigateToPlatformImplementation(e, it.parent as KtDeclaration) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
private val HEADER_DECLARATION = MarkerType(
|
||||||
|
"HEADER_DECLARATION",
|
||||||
|
{ it?.let { getHeaderDeclarationTooltip(it.parent as KtDeclaration) } },
|
||||||
|
object : LineMarkerNavigator() {
|
||||||
|
override fun browse(e: MouseEvent?, element: PsiElement?) {
|
||||||
|
element?.let { navigateToHeaderDeclaration(it.parent as KtDeclaration) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
private fun isImplementsAndNotOverrides(descriptor: CallableMemberDescriptor, overriddenMembers: Collection<CallableMemberDescriptor>): Boolean {
|
private fun isImplementsAndNotOverrides(descriptor: CallableMemberDescriptor, overriddenMembers: Collection<CallableMemberDescriptor>): Boolean {
|
||||||
return descriptor.modality != Modality.ABSTRACT && overriddenMembers.all { it.modality == Modality.ABSTRACT }
|
return descriptor.modality != Modality.ABSTRACT && overriddenMembers.all { it.modality == Modality.ABSTRACT }
|
||||||
}
|
}
|
||||||
@@ -196,6 +226,48 @@ private fun collectOverriddenPropertyAccessors(properties: Collection<KtNamedDec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun collectImplementationMarkers(declaration: KtNamedDeclaration,
|
||||||
|
result: MutableCollection<LineMarkerInfo<*>>) {
|
||||||
|
|
||||||
|
val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return
|
||||||
|
val commonModuleDescriptor = declaration.containingKtFile.findModuleDescriptor()
|
||||||
|
|
||||||
|
if (commonModuleDescriptor.allImplementingModules.none { it.hasImplementationsOf(descriptor) }) return
|
||||||
|
|
||||||
|
val anchor = declaration.nameIdentifier ?: declaration
|
||||||
|
|
||||||
|
result.add(LineMarkerInfo(
|
||||||
|
anchor,
|
||||||
|
anchor.textRange,
|
||||||
|
IMPLEMENTED_MARK,
|
||||||
|
Pass.UPDATE_OVERRIDDEN_MARKERS,
|
||||||
|
PLATFORM_IMPLEMENTATION.tooltip,
|
||||||
|
PLATFORM_IMPLEMENTATION.navigationHandler,
|
||||||
|
GutterIconRenderer.Alignment.RIGHT
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectHeaderMarkers(declaration: KtNamedDeclaration,
|
||||||
|
result: MutableCollection<LineMarkerInfo<*>>) {
|
||||||
|
|
||||||
|
val descriptor = declaration.toDescriptor() as? MemberDescriptor ?: return
|
||||||
|
val platformModuleDescriptor = declaration.containingKtFile.findModuleDescriptor()
|
||||||
|
val commonModuleDescriptor = platformModuleDescriptor.commonModuleOrNull() ?: return
|
||||||
|
if (!commonModuleDescriptor.hasDeclarationOf(descriptor)) return
|
||||||
|
|
||||||
|
val anchor = declaration.nameIdentifier ?: declaration
|
||||||
|
|
||||||
|
result.add(LineMarkerInfo(
|
||||||
|
anchor,
|
||||||
|
anchor.textRange,
|
||||||
|
IMPLEMENTING_MARK,
|
||||||
|
Pass.UPDATE_OVERRIDDEN_MARKERS,
|
||||||
|
HEADER_DECLARATION.tooltip,
|
||||||
|
HEADER_DECLARATION.navigationHandler,
|
||||||
|
GutterIconRenderer.Alignment.RIGHT
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fun KtNamedDeclaration.getAccessorLightMethods(): LightClassUtil.PropertyAccessorsPsiMethods {
|
fun KtNamedDeclaration.getAccessorLightMethods(): LightClassUtil.PropertyAccessorsPsiMethods {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this)
|
is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this)
|
||||||
|
|||||||
Reference in New Issue
Block a user