Fix override navigation from properties defined in primary constructor

EA-91185 - CCE: KotlinLineMarkerProviderKt$OVERRIDDEN_PROPERTY$.browse
This commit is contained in:
Dmitry Jemerov
2017-04-27 15:49:25 +02:00
parent c22e0d88f0
commit d49e9d7064
5 changed files with 65 additions and 35 deletions
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.idea.highlighter.markers package org.jetbrains.kotlin.idea.highlighter.markers
import com.intellij.codeHighlighting.Pass import com.intellij.codeHighlighting.Pass
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler
import com.intellij.codeInsight.daemon.LineMarkerInfo import com.intellij.codeInsight.daemon.LineMarkerInfo
import com.intellij.codeInsight.daemon.LineMarkerProvider import com.intellij.codeInsight.daemon.LineMarkerProvider
import com.intellij.codeInsight.daemon.impl.LineMarkerNavigator import com.intellij.codeInsight.daemon.impl.LineMarkerNavigator
@@ -132,23 +133,47 @@ private val SUBCLASSED_CLASS = MarkerType(
} }
}) })
private val OVERRIDDEN_FUNCTION = MarkerType( private val OVERRIDDEN_FUNCTION = object : MarkerType(
"OVERRIDDEN_FUNCTION", "OVERRIDDEN_FUNCTION",
{ getPsiMethod(it)?.let(::getOverriddenMethodTooltip) }, { getPsiMethod(it)?.let(::getOverriddenMethodTooltip) },
object : LineMarkerNavigator() { object : LineMarkerNavigator() {
override fun browse(e: MouseEvent?, element: PsiElement?) { override fun browse(e: MouseEvent?, element: PsiElement?) {
getPsiMethod(element)?.let { navigateToOverriddenMethod(e, it) } buildNavigateToOverriddenMethodPopup(e, element)?.showPopup(e)
} }
}) }) {
private val OVERRIDDEN_PROPERTY = MarkerType( override fun getNavigationHandler(): GutterIconNavigationHandler<PsiElement> {
val superHandler = super.getNavigationHandler()
return object : GutterIconNavigationHandler<PsiElement>, TestableLineMarkerNavigator {
override fun navigate(e: MouseEvent?, elt: PsiElement?) {
superHandler.navigate(e, elt)
}
override fun getTargetsPopupDescriptor(element: PsiElement?) = buildNavigateToOverriddenMethodPopup(null, element)
}
}
}
private val OVERRIDDEN_PROPERTY = object : MarkerType(
"OVERRIDDEN_PROPERTY", "OVERRIDDEN_PROPERTY",
{ it?.let { getOverriddenPropertyTooltip(it.parent as KtNamedDeclaration) } }, { it?.let { getOverriddenPropertyTooltip(it.parent as KtNamedDeclaration) } },
object : LineMarkerNavigator() { object : LineMarkerNavigator() {
override fun browse(e: MouseEvent?, element: PsiElement?) { override fun browse(e: MouseEvent?, element: PsiElement?) {
element?.let { navigateToPropertyOverriddenDeclarations(e, it.parent as KtProperty) } buildNavigateToPropertyOverriddenDeclarationsPopup(e, element)?.showPopup(e)
} }
}) }) {
override fun getNavigationHandler(): GutterIconNavigationHandler<PsiElement> {
val superHandler = super.getNavigationHandler()
return object : GutterIconNavigationHandler<PsiElement>, TestableLineMarkerNavigator {
override fun navigate(e: MouseEvent?, elt: PsiElement?) {
superHandler.navigate(e, elt)
}
override fun getTargetsPopupDescriptor(element: PsiElement?) = buildNavigateToPropertyOverriddenDeclarationsPopup(null, element)
}
}
}
private val PLATFORM_IMPLEMENTATION = MarkerType( private val PLATFORM_IMPLEMENTATION = MarkerType(
"PLATFORM_IMPLEMENTATION", "PLATFORM_IMPLEMENTATION",
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.idea.highlighter.markers
import com.intellij.codeInsight.daemon.DaemonBundle import com.intellij.codeInsight.daemon.DaemonBundle
import com.intellij.codeInsight.daemon.impl.GutterIconTooltipHelper import com.intellij.codeInsight.daemon.impl.GutterIconTooltipHelper
import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator
import com.intellij.codeInsight.navigation.ListBackgroundUpdaterTask import com.intellij.codeInsight.navigation.ListBackgroundUpdaterTask
import com.intellij.ide.util.MethodCellRenderer import com.intellij.ide.util.MethodCellRenderer
import com.intellij.ide.util.PsiElementListCellRenderer import com.intellij.ide.util.PsiElementListCellRenderer
@@ -93,10 +92,12 @@ fun getOverriddenMethodTooltip(method: PsiMethod): String? {
return GutterIconTooltipHelper.composeText(overridingJavaMethods, start, "&nbsp;&nbsp;&nbsp;&nbsp;{1}") return GutterIconTooltipHelper.composeText(overridingJavaMethods, start, "&nbsp;&nbsp;&nbsp;&nbsp;{1}")
} }
fun navigateToOverriddenMethod(e: MouseEvent?, method: PsiMethod) { fun buildNavigateToOverriddenMethodPopup(e: MouseEvent?, element: PsiElement?): NavigationPopupDescriptor? {
val method = getPsiMethod(element) ?: return null
if (DumbService.isDumb(method.project)) { if (DumbService.isDumb(method.project)) {
DumbService.getInstance(method.project)?.showDumbModeNotification("Navigation to overriding classes is not possible during index update") DumbService.getInstance(method.project)?.showDumbModeNotification("Navigation to overriding classes is not possible during index update")
return return null
} }
val processor = PsiElementProcessor.CollectElementsWithLimit<PsiMethod>(2, THashSet<PsiMethod>()) val processor = PsiElementProcessor.CollectElementsWithLimit<PsiMethod>(2, THashSet<PsiMethod>())
@@ -105,11 +106,11 @@ fun navigateToOverriddenMethod(e: MouseEvent?, method: PsiMethod) {
OverridingMethodsSearch.search(method, true).forEach(PsiElementProcessorAdapter(processor)) OverridingMethodsSearch.search(method, true).forEach(PsiElementProcessorAdapter(processor))
}, },
"Searching for overriding declarations", true, method.project, e?.component as JComponent?)) { "Searching for overriding declarations", true, method.project, e?.component as JComponent?)) {
return return null
} }
var overridingJavaMethods = processor.collection.filter { !it.isMethodWithDeclarationInOtherClass() } var overridingJavaMethods = processor.collection.filter { !it.isMethodWithDeclarationInOtherClass() }
if (overridingJavaMethods.isEmpty()) return if (overridingJavaMethods.isEmpty()) return null
val showMethodNames = !PsiUtil.allMethodsHaveSameSignature(overridingJavaMethods.toTypedArray()) val showMethodNames = !PsiUtil.allMethodsHaveSameSignature(overridingJavaMethods.toTypedArray())
@@ -117,13 +118,11 @@ fun navigateToOverriddenMethod(e: MouseEvent?, method: PsiMethod) {
overridingJavaMethods = overridingJavaMethods.sortedWith(renderer.comparator) overridingJavaMethods = overridingJavaMethods.sortedWith(renderer.comparator)
val methodsUpdater = OverridingMethodsUpdater(method, renderer) val methodsUpdater = OverridingMethodsUpdater(method, renderer)
PsiElementListNavigator.openTargets( return NavigationPopupDescriptor(overridingJavaMethods,
e, methodsUpdater.getCaption(overridingJavaMethods.size),
overridingJavaMethods.toTypedArray(), "Overriding declarations of " + method.name,
methodsUpdater.getCaption(overridingJavaMethods.size), renderer,
"Overriding declarations of " + method.name, methodsUpdater)
renderer,
methodsUpdater)
} }
private class OverridingMethodsUpdater( private class OverridingMethodsUpdater(
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.idea.highlighter.markers
import com.intellij.codeInsight.daemon.impl.GutterIconTooltipHelper import com.intellij.codeInsight.daemon.impl.GutterIconTooltipHelper
import com.intellij.codeInsight.daemon.impl.MarkerType import com.intellij.codeInsight.daemon.impl.MarkerType
import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator
import com.intellij.ide.util.DefaultPsiElementCellRenderer import com.intellij.ide.util.DefaultPsiElementCellRenderer
import com.intellij.ide.util.PsiClassListCellRenderer import com.intellij.ide.util.PsiClassListCellRenderer
import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.ProgressManager
@@ -76,18 +75,22 @@ fun getOverriddenPropertyTooltip(property: KtNamedDeclaration): String? {
return GutterIconTooltipHelper.composeText(collectedClasses.sortedWith(PsiClassListCellRenderer().comparator), start, pattern) return GutterIconTooltipHelper.composeText(collectedClasses.sortedWith(PsiClassListCellRenderer().comparator), start, pattern)
} }
fun navigateToPropertyOverriddenDeclarations(e: MouseEvent?, property: KtProperty) { fun buildNavigateToPropertyOverriddenDeclarationsPopup(e: MouseEvent?, element: PsiElement?): NavigationPopupDescriptor? {
val project = property.project val propertyOrParameter = element?.parent as? KtNamedDeclaration ?: return null
val project = propertyOrParameter.project
if (DumbService.isDumb(project)) { if (DumbService.isDumb(project)) {
DumbService.getInstance(project)?.showDumbModeNotification("Navigation to overriding classes is not possible during index update") DumbService.getInstance(project)?.showDumbModeNotification("Navigation to overriding classes is not possible during index update")
return return null
} }
val psiPropertyMethods = LightClassUtil.getLightClassPropertyMethods(property) val psiPropertyMethods = when(propertyOrParameter) {
is KtProperty -> LightClassUtil.getLightClassPropertyMethods(propertyOrParameter)
is KtParameter -> LightClassUtil.getLightClassPropertyMethods(propertyOrParameter)
else -> return null
}
val elementProcessor = CommonProcessors.CollectUniquesProcessor<PsiElement>() val elementProcessor = CommonProcessors.CollectUniquesProcessor<PsiElement>()
val jetPsiMethodProcessor = Runnable { val ktPsiMethodProcessor = Runnable {
KotlinDefinitionsSearcher.processPropertyImplementationsMethods( KotlinDefinitionsSearcher.processPropertyImplementationsMethods(
psiPropertyMethods, psiPropertyMethods,
GlobalSearchScope.allScope(project), GlobalSearchScope.allScope(project),
@@ -95,12 +98,12 @@ fun navigateToPropertyOverriddenDeclarations(e: MouseEvent?, property: KtPropert
} }
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously( if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(
/* runnable */ jetPsiMethodProcessor, /* runnable */ ktPsiMethodProcessor,
MarkerType.SEARCHING_FOR_OVERRIDING_METHODS, MarkerType.SEARCHING_FOR_OVERRIDING_METHODS,
/* can be canceled */ true, /* can be canceled */ true,
project, project,
e?.component as JComponent?)) { e?.component as JComponent?)) {
return return null
} }
val renderer = DefaultPsiElementCellRenderer() val renderer = DefaultPsiElementCellRenderer()
@@ -108,10 +111,9 @@ fun navigateToPropertyOverriddenDeclarations(e: MouseEvent?, property: KtPropert
.sortedWith(renderer.comparator) .sortedWith(renderer.comparator)
.filterIsInstance<NavigatablePsiElement>() .filterIsInstance<NavigatablePsiElement>()
PsiElementListNavigator.openTargets(e, return NavigationPopupDescriptor(navigatingOverrides,
navigatingOverrides.toTypedArray(), KotlinBundle.message("navigation.title.overriding.property", propertyOrParameter.name),
KotlinBundle.message("navigation.title.overriding.property", property.name), KotlinBundle.message("navigation.findUsages.title.overriding.property", propertyOrParameter.name), renderer)
KotlinBundle.message("navigation.findUsages.title.overriding.property", property.name), renderer)
} }
@@ -8,3 +8,10 @@ open class <lineMarker>C</lineMarker>(
class D : C("") { class D : C("") {
override val <lineMarker>s</lineMarker>: String get() = "q" override val <lineMarker>s</lineMarker>: String get() = "q"
} }
/*
LINEMARKER: <html><body>Is overridden in <br/>&nbsp;&nbsp;&nbsp;&nbsp;D</body></html>
TARGETS:
PrimaryConstructorOpen.kt
override val <1>s: String get() = "q"
*/
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.ReferenceUtils import org.jetbrains.kotlin.test.ReferenceUtils
@@ -115,9 +114,7 @@ abstract class AbstractLineMarkersTest : KotlinLightCodeInsightFixtureTestCase()
val handler = navigateMarker.navigationHandler val handler = navigateMarker.navigationHandler
if (handler is TestableLineMarkerNavigator) { if (handler is TestableLineMarkerNavigator) {
val element = navigateMarker.element as KtDeclaration val navigateElements = handler.getTargetsPopupDescriptor(navigateMarker.element)?.targets?.sortedBy { ReferenceUtils.renderAsGotoImplementation(it) }
val navigateElements = handler.getTargetsPopupDescriptor(element)?.targets?.sortedBy { ReferenceUtils.renderAsGotoImplementation(it) }
val actualNavigationData = NavigationTestUtils.getNavigateElementsText(myFixture.project, navigateElements) val actualNavigationData = NavigationTestUtils.getNavigateElementsText(myFixture.project, navigateElements)
UsefulTestCase.assertSameLines(getExpectedNavigationText(navigationComment), actualNavigationData) UsefulTestCase.assertSameLines(getExpectedNavigationText(navigationComment), actualNavigationData)