Check also drop-box content in multi-module line marker tests
Related to KT-26957
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.highlighter.markers
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator
|
||||
import com.intellij.ide.util.DefaultPsiElementCellRenderer
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
@@ -64,19 +63,26 @@ fun getPlatformActualTooltip(declaration: KtDeclaration): String? {
|
||||
}
|
||||
}
|
||||
|
||||
fun navigateToPlatformActual(e: MouseEvent?, declaration: KtDeclaration) {
|
||||
val actualDeclarations =
|
||||
declaration.actualsForExpected() + declaration.findMarkerBoundDeclarations().flatMap { it.actualsForExpected() }
|
||||
if (actualDeclarations.isEmpty()) return
|
||||
fun KtDeclaration.allNavigatableActualDeclarations(): Set<KtDeclaration> =
|
||||
actualsForExpected() + findMarkerBoundDeclarations().flatMap { it.actualsForExpected() }
|
||||
|
||||
val renderer = object : DefaultPsiElementCellRenderer() {
|
||||
override fun getContainerText(element: PsiElement?, name: String?) = ""
|
||||
class ActualExpectedPsiElementCellRenderer : DefaultPsiElementCellRenderer() {
|
||||
override fun getContainerText(element: PsiElement?, name: String?) = ""
|
||||
}
|
||||
|
||||
fun KtDeclaration.navigateToActualTitle() = "Choose actual for $name"
|
||||
|
||||
fun KtDeclaration.navigateToActualUsagesTitle() = "Actuals for $name"
|
||||
|
||||
fun buildNavigateToActualDeclarationsPopup(e: MouseEvent?, element: PsiElement?): NavigationPopupDescriptor? {
|
||||
return element?.markerDeclaration?.let {
|
||||
val navigatableActualDeclarations = it.allNavigatableActualDeclarations()
|
||||
if (navigatableActualDeclarations.isEmpty()) return null
|
||||
return NavigationPopupDescriptor(
|
||||
navigatableActualDeclarations,
|
||||
it.navigateToActualTitle(),
|
||||
it.navigateToActualUsagesTitle(),
|
||||
ActualExpectedPsiElementCellRenderer()
|
||||
)
|
||||
}
|
||||
PsiElementListNavigator.openTargets(
|
||||
e,
|
||||
actualDeclarations.toTypedArray(),
|
||||
"Choose actual for ${declaration.name}",
|
||||
"Actuals for ${declaration.name}",
|
||||
renderer
|
||||
)
|
||||
}
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.highlighter.markers
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator
|
||||
import com.intellij.ide.util.DefaultPsiElementCellRenderer
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.MemberDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors
|
||||
@@ -38,20 +36,22 @@ fun getExpectedDeclarationTooltip(declaration: KtDeclaration): String? {
|
||||
return "Has declaration in common module"
|
||||
}
|
||||
|
||||
fun navigateToExpectedDeclaration(e: MouseEvent?, declaration: KtDeclaration) {
|
||||
val expectedDeclarations =
|
||||
listOfNotNull(declaration.expectedDeclarationIfAny()) +
|
||||
declaration.findMarkerBoundDeclarations().mapNotNull { it.expectedDeclarationIfAny() }
|
||||
if (expectedDeclarations.isEmpty()) return
|
||||
fun KtDeclaration.allNavigatableExpectedDeclarations(): List<KtDeclaration> =
|
||||
listOfNotNull(expectedDeclarationIfAny()) + findMarkerBoundDeclarations().mapNotNull { it.expectedDeclarationIfAny() }
|
||||
|
||||
val renderer = object : DefaultPsiElementCellRenderer() {
|
||||
override fun getContainerText(element: PsiElement?, name: String?) = ""
|
||||
fun KtDeclaration.navigateToExpectedTitle() = "Choose expected for $name"
|
||||
|
||||
fun KtDeclaration.navigateToExpectedUsagesTitle() = "Expected for $name"
|
||||
|
||||
fun buildNavigateToExpectedDeclarationsPopup(e: MouseEvent?, element: PsiElement?): NavigationPopupDescriptor? {
|
||||
return element?.markerDeclaration?.let {
|
||||
val navigatableExpectedDeclarations = it.allNavigatableExpectedDeclarations()
|
||||
if (navigatableExpectedDeclarations.isEmpty()) return null
|
||||
return NavigationPopupDescriptor(
|
||||
navigatableExpectedDeclarations,
|
||||
it.navigateToExpectedTitle(),
|
||||
it.navigateToExpectedUsagesTitle(),
|
||||
ActualExpectedPsiElementCellRenderer()
|
||||
)
|
||||
}
|
||||
PsiElementListNavigator.openTargets(
|
||||
e,
|
||||
expectedDeclarations.toTypedArray(),
|
||||
"Choose expected for ${declaration.name}",
|
||||
"Expected for ${declaration.name}",
|
||||
renderer
|
||||
)
|
||||
}
|
||||
@@ -198,28 +198,52 @@ private val OVERRIDDEN_PROPERTY = object : MarkerType(
|
||||
}
|
||||
}
|
||||
|
||||
private val PsiElement.markerDeclaration
|
||||
val PsiElement.markerDeclaration
|
||||
get() = (this as? KtDeclaration) ?: (parent as? KtDeclaration)
|
||||
|
||||
private val PLATFORM_ACTUAL = MarkerType(
|
||||
private val PLATFORM_ACTUAL = object : MarkerType(
|
||||
"PLATFORM_ACTUAL",
|
||||
{ element -> element?.markerDeclaration?.let { getPlatformActualTooltip(it) } },
|
||||
object : LineMarkerNavigator() {
|
||||
override fun browse(e: MouseEvent?, element: PsiElement?) {
|
||||
element?.markerDeclaration?.let { navigateToPlatformActual(e, it) }
|
||||
buildNavigateToActualDeclarationsPopup(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?): NavigationPopupDescriptor? {
|
||||
return buildNavigateToActualDeclarationsPopup(null, element)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private val EXPECTED_DECLARATION = MarkerType(
|
||||
private val EXPECTED_DECLARATION = object : MarkerType(
|
||||
"EXPECTED_DECLARATION",
|
||||
{ element -> element?.markerDeclaration?.let { getExpectedDeclarationTooltip(it) } },
|
||||
object : LineMarkerNavigator() {
|
||||
override fun browse(e: MouseEvent?, element: PsiElement?) {
|
||||
element?.markerDeclaration?.let { navigateToExpectedDeclaration(e, it) }
|
||||
buildNavigateToExpectedDeclarationsPopup(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?): NavigationPopupDescriptor? {
|
||||
return buildNavigateToExpectedDeclarationsPopup(null, element)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun isImplementsAndNotOverrides(
|
||||
descriptor: CallableMemberDescriptor,
|
||||
|
||||
+11
-1
@@ -1 +1,11 @@
|
||||
actual class <lineMarker>WithConstructor</lineMarker> actual constructor(actual val x: Int, actual val s: String)
|
||||
actual class <lineMarker descr="Has declaration in common module">WithConstructor</lineMarker> actual constructor(actual val x: Int, actual val s: String)
|
||||
|
||||
/*
|
||||
LINEMARKER: Has declaration in common module
|
||||
TARGETS:
|
||||
common.kt
|
||||
expect class <1>WithConstructor(x: Int, s: String) {
|
||||
val <3>x: Int
|
||||
|
||||
val <2>s: String
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
package test
|
||||
|
||||
actual enum class <lineMarker>Enum</lineMarker> { A, B, C }
|
||||
actual enum class <lineMarker descr="Has declaration in common module">Enum</lineMarker> { A, B, C }
|
||||
|
||||
/*
|
||||
LINEMARKER: Has declaration in common module
|
||||
TARGETS:
|
||||
common.kt
|
||||
expect enum class <4>Enum { <1>A, <2>B, <3>C }
|
||||
*/
|
||||
|
||||
+8
-1
@@ -1,3 +1,10 @@
|
||||
package test
|
||||
|
||||
expect enum class <lineMarker>Enum</lineMarker> { A, B, C, D }
|
||||
expect enum class <lineMarker descr="Has actuals in JVM">Enum</lineMarker> { A, B, C, D }
|
||||
|
||||
/*
|
||||
LINEMARKER: Has actuals in JVM
|
||||
TARGETS:
|
||||
jvm.kt
|
||||
actual enum class <5>Enum { <1>A, <2>B, <3>C, <4>D }
|
||||
*/
|
||||
|
||||
+10
-1
@@ -1,3 +1,12 @@
|
||||
interface <lineMarker>I</lineMarker> {
|
||||
suspend fun <lineMarker descr="<html><body>Is implemented in <br> KJs<br> KJvm</body></html>">foo</lineMarker>(s: String)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
LINEMARKER: <html><body>Is implemented in <br> KJs<br> KJvm</body></html>
|
||||
TARGETS:
|
||||
js.kt
|
||||
suspend override fun <1>foo(s: String) {
|
||||
jvm.kt
|
||||
suspend override fun <2>foo(s: String) {
|
||||
*/
|
||||
|
||||
+14
@@ -16,16 +16,30 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.caches.resolve
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.idea.codeInsight.AbstractLineMarkersTest
|
||||
import org.jetbrains.kotlin.idea.multiplatform.setupMppProjectFromDirStructure
|
||||
import org.jetbrains.kotlin.idea.stubs.AbstractMultiHighlightingTest
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.test.allJavaFiles
|
||||
import org.jetbrains.kotlin.idea.test.allKotlinFiles
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractMultiModuleHighlightingTest : AbstractMultiHighlightingTest() {
|
||||
|
||||
protected open fun checkLineMarkersInProject(
|
||||
findFiles: () -> List<PsiFile> = { project.allKotlinFiles().excludeByDirective() }
|
||||
) {
|
||||
checkFiles(findFiles) {
|
||||
checkHighlighting(myEditor, true, false)
|
||||
|
||||
val markers = DaemonCodeAnalyzerImpl.getLineMarkers(getDocument(file), project)
|
||||
AbstractLineMarkersTest.assertNavigationElements(project, myFile as KtFile, markers)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun checkHighlightingInProject(
|
||||
findFiles: () -> List<PsiFile> = { project.allKotlinFiles().excludeByDirective() }
|
||||
) {
|
||||
|
||||
+1
-1
@@ -32,6 +32,6 @@ abstract class AbstractMultiModuleLineMarkerTest : AbstractMultiModuleHighlighti
|
||||
|
||||
protected fun doTest(path: String) {
|
||||
setupMppProjectFromDirStructure(File(path))
|
||||
checkHighlightingInProject()
|
||||
checkLineMarkersInProject()
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings
|
||||
import com.intellij.codeInsight.daemon.LineMarkerInfo
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.rt.execution.junit.FileComparisonFailure
|
||||
@@ -93,7 +94,7 @@ abstract class AbstractLineMarkersTest : KotlinLightCodeInsightFixtureTestCase()
|
||||
|
||||
val markers = doAndCheckHighlighting(document, data, File(path))
|
||||
|
||||
assertNavigationElements(markers)
|
||||
assertNavigationElements(myFixture.project, myFixture.file as KtFile, markers)
|
||||
additionalCheck()
|
||||
} catch (exc: Exception) {
|
||||
throw RuntimeException(exc)
|
||||
@@ -104,45 +105,53 @@ abstract class AbstractLineMarkersTest : KotlinLightCodeInsightFixtureTestCase()
|
||||
|
||||
}
|
||||
|
||||
private fun assertNavigationElements(markers: List<LineMarkerInfo<*>>) {
|
||||
val navigationDataComments = KotlinTestUtils.getLastCommentsInFile(
|
||||
myFixture.file as KtFile, KotlinTestUtils.CommentType.BLOCK_COMMENT, false)
|
||||
if (navigationDataComments.isEmpty()) return
|
||||
|
||||
for (navigationComment in navigationDataComments) {
|
||||
val description = getLineMarkerDescription(navigationComment)
|
||||
val navigateMarker = markers.find { it.lineMarkerTooltip?.startsWith(description) == true }!!
|
||||
|
||||
TestCase.assertNotNull(
|
||||
String.format("Can't find marker for navigation check with description \"%s\"", description),
|
||||
navigateMarker)
|
||||
|
||||
val handler = navigateMarker.navigationHandler
|
||||
if (handler is TestableLineMarkerNavigator) {
|
||||
val navigateElements = handler.getTargetsPopupDescriptor(navigateMarker.element)?.targets?.sortedBy { it.renderAsGotoImplementation() }
|
||||
val actualNavigationData = NavigationTestUtils.getNavigateElementsText(myFixture.project, navigateElements)
|
||||
|
||||
UsefulTestCase.assertSameLines(getExpectedNavigationText(navigationComment), actualNavigationData)
|
||||
}
|
||||
else {
|
||||
Assert.fail("Only SuperDeclarationMarkerNavigationHandler are supported in navigate check")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val LINE_MARKER_PREFIX = "LINEMARKER:"
|
||||
private val TARGETS_PREFIX = "TARGETS"
|
||||
@Suppress("SpellCheckingInspection")
|
||||
private const val LINE_MARKER_PREFIX = "LINEMARKER:"
|
||||
private const val TARGETS_PREFIX = "TARGETS"
|
||||
|
||||
fun assertNavigationElements(project: Project, file: KtFile, markers: List<LineMarkerInfo<*>>) {
|
||||
val navigationDataComments = KotlinTestUtils.getLastCommentsInFile(
|
||||
file, KotlinTestUtils.CommentType.BLOCK_COMMENT, false
|
||||
)
|
||||
if (navigationDataComments.isEmpty()) return
|
||||
|
||||
for (navigationComment in navigationDataComments) {
|
||||
val description = getLineMarkerDescription(navigationComment)
|
||||
val navigateMarker = markers.find { it.lineMarkerTooltip?.startsWith(description) == true }!!
|
||||
|
||||
TestCase.assertNotNull(
|
||||
String.format("Can't find marker for navigation check with description \"%s\"", description),
|
||||
navigateMarker
|
||||
)
|
||||
|
||||
val handler = navigateMarker.navigationHandler
|
||||
if (handler is TestableLineMarkerNavigator) {
|
||||
val navigateElements = handler.getTargetsPopupDescriptor(navigateMarker.element)?.targets?.sortedBy {
|
||||
it.renderAsGotoImplementation()
|
||||
}
|
||||
val actualNavigationData = NavigationTestUtils.getNavigateElementsText(project, navigateElements)
|
||||
|
||||
UsefulTestCase.assertSameLines(getExpectedNavigationText(navigationComment), actualNavigationData)
|
||||
} else {
|
||||
Assert.fail("Only TestableLineMarkerNavigator are supported in navigate check")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLineMarkerDescription(navigationComment: String): String {
|
||||
val firstLineEnd = navigationComment.indexOf("\n")
|
||||
TestCase.assertTrue("The first line in block comment must contain description of marker for navigation check", firstLineEnd != -1)
|
||||
TestCase.assertTrue(
|
||||
"The first line in block comment must contain description of marker for navigation check", firstLineEnd != -1
|
||||
)
|
||||
|
||||
var navigationMarkerText = navigationComment.substring(0, firstLineEnd)
|
||||
|
||||
TestCase.assertTrue(String.format("Add %s directive in first line of comment", LINE_MARKER_PREFIX),
|
||||
navigationMarkerText.startsWith(LINE_MARKER_PREFIX))
|
||||
TestCase.assertTrue(
|
||||
String.format("Add %s directive in first line of comment", LINE_MARKER_PREFIX),
|
||||
navigationMarkerText.startsWith(LINE_MARKER_PREFIX)
|
||||
)
|
||||
|
||||
navigationMarkerText = navigationMarkerText.substring(LINE_MARKER_PREFIX.length)
|
||||
|
||||
@@ -155,8 +164,9 @@ abstract class AbstractLineMarkersTest : KotlinLightCodeInsightFixtureTestCase()
|
||||
var expectedNavigationText = navigationComment.substring(firstLineEnd + 1)
|
||||
|
||||
TestCase.assertTrue(
|
||||
String.format("Marker %s is expected before navigation data", TARGETS_PREFIX),
|
||||
expectedNavigationText.startsWith(TARGETS_PREFIX))
|
||||
String.format("Marker %s is expected before navigation data", TARGETS_PREFIX),
|
||||
expectedNavigationText.startsWith(TARGETS_PREFIX)
|
||||
)
|
||||
|
||||
expectedNavigationText = expectedNavigationText.substring(expectedNavigationText.indexOf("\n") + 1)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user