diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt index 0b69fc19116..78ccc4a0ad5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/HeaderImplDeclarationChecker.kt @@ -131,7 +131,7 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null) } } - private fun CallableMemberDescriptor.findNamesakesFromTheSameModule(): Collection { + fun CallableMemberDescriptor.findNamesakesFromTheSameModule(): Collection { 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 { + fun ClassifierDescriptor.findClassifiersFromTheSameModule(): Collection { val myModule = moduleToCheck ?: module // TODO: support nested classes return myModule.getPackage(fqNameSafe.parent()).memberScope diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DeclaredHeaderMarker.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DeclaredHeaderMarker.kt new file mode 100644 index 00000000000..5b6fdacde08 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DeclaredHeaderMarker.kt @@ -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) +} diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/ImplementedHeaderMarker.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/ImplementedHeaderMarker.kt new file mode 100644 index 00000000000..6a4f0b7d615 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/ImplementedHeaderMarker.kt @@ -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) +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt index 241653b0088..aa2fb0f19d0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt @@ -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): Boolean { return descriptor.modality != Modality.ABSTRACT && overriddenMembers.all { it.modality == Modality.ABSTRACT } } @@ -196,6 +226,48 @@ private fun collectOverriddenPropertyAccessors(properties: Collection>) { + + 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>) { + + 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)