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 myModule = moduleToCheck ?: module
|
||||
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
|
||||
// TODO: support nested classes
|
||||
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 org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
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.lexer.KtTokens
|
||||
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)
|
||||
@@ -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 {
|
||||
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 {
|
||||
return when (this) {
|
||||
is KtProperty -> LightClassUtil.getLightClassPropertyMethods(this)
|
||||
|
||||
Reference in New Issue
Block a user