From 8de2ff21636b294e2ec6eb409d09c9b30a16c983 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 26 Dec 2018 16:43:40 +0300 Subject: [PATCH] Improve expect / actual markers on one line Before this commit, only one-line enums and annotation classes were considered. Now we can have enum header on one line and some entries on another lines, and it still works. Same with primary constructor parameters. #KT-22637 Fixed --- .../highlighter/markers/HasActualMarker.kt | 2 +- .../markers/KotlinLineMarkerProvider.kt | 55 ++++++++++++++++--- .../common/common.kt | 17 ++++++ .../jvm/jvm.kt | 6 ++ .../common/common.kt | 6 ++ .../jvm/jvm.kt | 31 +++++++++++ .../MultiModuleLineMarkerTestGenerated.java | 10 ++++ .../codeInsight/AbstractLineMarkersTest.kt | 7 ++- 8 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 idea/testData/multiModuleLineMarker/expectEnumWithEnumEntriesInOneLine/common/common.kt create mode 100644 idea/testData/multiModuleLineMarker/expectEnumWithEnumEntriesInOneLine/jvm/jvm.kt create mode 100644 idea/testData/multiModuleLineMarker/fromActualAnnotationWithParametersInOneLine/common/common.kt create mode 100644 idea/testData/multiModuleLineMarker/fromActualAnnotationWithParametersInOneLine/jvm/jvm.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/HasActualMarker.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/HasActualMarker.kt index 252264db1f7..739aab682ce 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/HasActualMarker.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/HasActualMarker.kt @@ -63,7 +63,7 @@ fun getPlatformActualTooltip(declaration: KtDeclaration): String? { } fun KtDeclaration.allNavigatableActualDeclarations(): Set = - actualsForExpected() + findMarkerBoundDeclarations().flatMap { it.actualsForExpected() } + actualsForExpected() + findMarkerBoundDeclarations().flatMap { it.actualsForExpected().asSequence() } class ActualExpectedPsiElementCellRenderer : DefaultPsiElementCellRenderer() { override fun getContainerText(element: PsiElement?, name: String?) = "" 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 0bcd843569c..d49322963f0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt @@ -385,22 +385,59 @@ private fun KtNamedDeclaration.requiresNoMarkers( return true } is KtParameter, - is KtEnumEntry -> if (document?.areAnchorsOnOneLine(this, containingClassOrObject) == true) { - return true + is KtEnumEntry -> { + if (document?.areAnchorsOnOneLine(this, containingClassOrObject) == true) { + return true + } + if (this is KtEnumEntry) { + val enumEntries = containingClassOrObject?.body?.enumEntries.orEmpty() + val previousEnumEntry = enumEntries.getOrNull(enumEntries.indexOf(this) - 1) + if (document?.areAnchorsOnOneLine(this, previousEnumEntry) == true) { + return true + } + } + if (this is KtParameter && hasValOrVar()) { + val parameters = containingClassOrObject?.primaryConstructorParameters.orEmpty() + val previousParameter = parameters.getOrNull(parameters.indexOf(this) - 1) + if (document?.areAnchorsOnOneLine(this, previousParameter) == true) { + return true + } + } } } return false } -internal fun KtDeclaration.findMarkerBoundDeclarations(): List { - if (this !is KtClass) return emptyList() - val result = mutableListOf() +internal fun KtDeclaration.findMarkerBoundDeclarations(): Sequence { + if (this !is KtClass && this !is KtParameter) return emptySequence() val document = PsiDocumentManager.getInstance(project).getDocument(containingFile) - result += primaryConstructor?.valueParameters?.filter { it.hasValOrVar() && it.requiresNoMarkers(document) }.orEmpty() - if (this.isEnum()) { - result += this.body?.enumEntries?.filter { it.requiresNoMarkers(document) }.orEmpty() + + fun Sequence.takeBound(bound: KtNamedDeclaration) = takeWhile { + document?.areAnchorsOnOneLine(bound, it) == true + } + + return when (this) { + is KtParameter -> { + val propertyParameters = takeIf { hasValOrVar() }?.containingClassOrObject?.primaryConstructorParameters + ?: return emptySequence() + propertyParameters.asSequence().dropWhile { + it !== this + }.drop(1).takeBound(this).filter { it.hasValOrVar() } + } + is KtEnumEntry -> { + val enumEntries = containingClassOrObject?.body?.enumEntries ?: return emptySequence() + enumEntries.asSequence().dropWhile { + it !== this + }.drop(1).takeBound(this) + } + is KtClass -> { + val boundParameters = + primaryConstructor?.valueParameters?.asSequence()?.takeBound(this)?.filter { it.hasValOrVar() }.orEmpty() + val boundEnumEntries = this.takeIf { isEnum() }?.body?.enumEntries?.asSequence()?.takeBound(this).orEmpty() + boundParameters + boundEnumEntries + } + else -> emptySequence() } - return result } private fun collectActualMarkers( diff --git a/idea/testData/multiModuleLineMarker/expectEnumWithEnumEntriesInOneLine/common/common.kt b/idea/testData/multiModuleLineMarker/expectEnumWithEnumEntriesInOneLine/common/common.kt new file mode 100644 index 00000000000..6c0f30ab284 --- /dev/null +++ b/idea/testData/multiModuleLineMarker/expectEnumWithEnumEntriesInOneLine/common/common.kt @@ -0,0 +1,17 @@ +package test + +expect enum class Enum { + A, B, C, D +} + +/* +LINEMARKER: Has actuals in JVM +TARGETS: +jvm.kt +actual enum class <1>Enum { +*//* +LINEMARKER: Has actuals in JVM +TARGETS: +jvm.kt + <1>A, <2>B, <3>C, <4>D +*/ diff --git a/idea/testData/multiModuleLineMarker/expectEnumWithEnumEntriesInOneLine/jvm/jvm.kt b/idea/testData/multiModuleLineMarker/expectEnumWithEnumEntriesInOneLine/jvm/jvm.kt new file mode 100644 index 00000000000..b8819e16464 --- /dev/null +++ b/idea/testData/multiModuleLineMarker/expectEnumWithEnumEntriesInOneLine/jvm/jvm.kt @@ -0,0 +1,6 @@ +// !CHECK_HIGHLIGHTING +package test + +actual enum class Enum { + A, B, C, D +} \ No newline at end of file diff --git a/idea/testData/multiModuleLineMarker/fromActualAnnotationWithParametersInOneLine/common/common.kt b/idea/testData/multiModuleLineMarker/fromActualAnnotationWithParametersInOneLine/common/common.kt new file mode 100644 index 00000000000..df4754e0c91 --- /dev/null +++ b/idea/testData/multiModuleLineMarker/fromActualAnnotationWithParametersInOneLine/common/common.kt @@ -0,0 +1,6 @@ +// !CHECK_HIGHLIGHTING + +expect annotation class Ann( + val x: Int, val y: String, + val z: Double, val b: Boolean +) \ No newline at end of file diff --git a/idea/testData/multiModuleLineMarker/fromActualAnnotationWithParametersInOneLine/jvm/jvm.kt b/idea/testData/multiModuleLineMarker/fromActualAnnotationWithParametersInOneLine/jvm/jvm.kt new file mode 100644 index 00000000000..e54dfb4587d --- /dev/null +++ b/idea/testData/multiModuleLineMarker/fromActualAnnotationWithParametersInOneLine/jvm/jvm.kt @@ -0,0 +1,31 @@ +actual annotation class Ann( + actual val x: Int, actual val y: String, + actual val z: Double, actual val b: Boolean +) + +// TODO: first marker on 'Ann' is generated twice here, see collectSlowLineMarkers main loop. +// Since it's fragile place, I don't want to fix it right now + + +/* +LINEMARKER: Has declaration in common module +TARGETS: +common.kt +expect annotation class <1>Ann( +*//* +LINEMARKER: Has declaration in common module +TARGETS: +common.kt +expect annotation class <1>Ann( +*//* +LINEMARKER: Has declaration in common module +TARGETS: +common.kt + val <1>x: Int, val <2>y: String, +*//* +LINEMARKER: Has declaration in common module +TARGETS: +common.kt + val <2>z: Double, val <1>b: Boolean +*/ + diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleLineMarkerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleLineMarkerTestGenerated.java index 5320d8757bd..5402f9773c4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleLineMarkerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleLineMarkerTestGenerated.java @@ -69,6 +69,11 @@ public class MultiModuleLineMarkerTestGenerated extends AbstractMultiModuleLineM runTest("idea/testData/multiModuleLineMarker/expectEnumEntriesInOneLine/"); } + @TestMetadata("expectEnumWithEnumEntriesInOneLine") + public void testExpectEnumWithEnumEntriesInOneLine() throws Exception { + runTest("idea/testData/multiModuleLineMarker/expectEnumWithEnumEntriesInOneLine/"); + } + @TestMetadata("expectWithOverload") public void testExpectWithOverload() throws Exception { runTest("idea/testData/multiModuleLineMarker/expectWithOverload/"); @@ -79,6 +84,11 @@ public class MultiModuleLineMarkerTestGenerated extends AbstractMultiModuleLineM runTest("idea/testData/multiModuleLineMarker/fromActualAnnotation/"); } + @TestMetadata("fromActualAnnotationWithParametersInOneLine") + public void testFromActualAnnotationWithParametersInOneLine() throws Exception { + runTest("idea/testData/multiModuleLineMarker/fromActualAnnotationWithParametersInOneLine/"); + } + @TestMetadata("fromActualCompanion") public void testFromActualCompanion() throws Exception { runTest("idea/testData/multiModuleLineMarker/fromActualCompanion/"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractLineMarkersTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractLineMarkersTest.kt index 5eda3ce7b8b..18d828f6ce4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractLineMarkersTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractLineMarkersTest.kt @@ -117,16 +117,17 @@ abstract class AbstractLineMarkersTest : KotlinLightCodeInsightFixtureTestCase() ) if (navigationDataComments.isEmpty()) return - for (navigationComment in navigationDataComments) { + for ((navigationCommentIndex, navigationComment) in navigationDataComments.reversed().withIndex()) { val description = getLineMarkerDescription(navigationComment) - val navigateMarker = markers.find { it.lineMarkerTooltip?.startsWith(description) == true }!! + val navigateMarkers = markers.filter { it.lineMarkerTooltip?.startsWith(description) == true } + val navigateMarker = navigateMarkers.singleOrNull() ?: navigateMarkers.getOrNull(navigationCommentIndex) TestCase.assertNotNull( String.format("Can't find marker for navigation check with description \"%s\"", description), navigateMarker ) - val handler = navigateMarker.navigationHandler + val handler = navigateMarker!!.navigationHandler if (handler is TestableLineMarkerNavigator) { val navigateElements = handler.getTargetsPopupDescriptor(navigateMarker.element)?.targets?.sortedBy { it.renderAsGotoImplementation()