diff --git a/core/test-api/src/main/kotlin/testApi/testRunner/TestRunner.kt b/core/test-api/src/main/kotlin/testApi/testRunner/TestRunner.kt index 4435353535..3a3eae40e1 100644 --- a/core/test-api/src/main/kotlin/testApi/testRunner/TestRunner.kt +++ b/core/test-api/src/main/kotlin/testApi/testRunner/TestRunner.kt @@ -68,7 +68,7 @@ abstract class AbstractTest, D : DokkaTestGe block: T.() -> Unit ) { val testMethods = testBuilder().apply(block).build() - val testDirPath = getTempDir(cleanupOutput).root.toPath() + val testDirPath = getTempDir(cleanupOutput).root.toPath().toAbsolutePath() val fileMap = query.toFileMap() fileMap.materializeFiles(testDirPath.toAbsolutePath()) if (!cleanupOutput) @@ -85,7 +85,7 @@ abstract class AbstractTest, D : DokkaTestGe }.toSet(), sourceLinks = sourceSet.sourceLinks.map { link -> link.copy( - localDirectory = testDirPath.toFile().resolve(link.localDirectory).canonicalPath + localDirectory = testDirPath.toFile().resolve(link.localDirectory).absolutePath ) }.toSet() ) diff --git a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt index 933050552a..50add45199 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt @@ -24,36 +24,47 @@ import java.io.File class SourceLinksTransformer(val context: DokkaContext) : PageTransformer { - private val builder : PageContentBuilder = PageContentBuilder( + private val builder : PageContentBuilder = PageContentBuilder( context.plugin().querySingle { commentsToContentConverter }, context.plugin().querySingle { signatureProvider }, context.logger ) - override fun invoke(input: RootPageNode) = - input.transformContentPagesTree { node -> + override fun invoke(input: RootPageNode): RootPageNode { + val sourceLinks = getSourceLinksFromConfiguration() + if (sourceLinks.isEmpty()) { + return input + } + return input.transformContentPagesTree { node -> when (node) { is WithDocumentables -> - node.documentables.filterIsInstance().flatMap { resolveSources(it) } - .takeIf { it.isNotEmpty() } - ?.let { node.addSourcesContent(it) } - ?: node + node.documentables + .filterIsInstance() + .flatMap { resolveSources(sourceLinks, it) } + .takeIf { it.isNotEmpty() } + ?.let { node.addSourcesContent(it) } + ?: node else -> node } } + } - private fun getSourceLinks() = context.configuration.sourceSets - .flatMap { it.sourceLinks.map { sl -> SourceLink(sl, it) } } + private fun getSourceLinksFromConfiguration(): List { + return context.configuration.sourceSets + .flatMap { it.sourceLinks.map { sl -> SourceLink(sl, it) } } + } - private fun resolveSources(documentable: WithSources) = documentable.sources - .mapNotNull { entry -> - getSourceLinks().find { File(entry.value.path).startsWith(it.path) && it.sourceSetData == entry.key }?.let { - Pair( - entry.key, - entry.value.toLink(it) - ) - } + private fun resolveSources( + sourceLinks: List, documentable: WithSources + ): List> { + return documentable.sources.mapNotNull { (sourceSet, documentableSource) -> + val sourceLink = sourceLinks.find { sourceLink -> + File(documentableSource.path).startsWith(sourceLink.path) && sourceLink.sourceSetData == sourceSet + } ?: return@mapNotNull null + + sourceSet to documentableSource.toLink(sourceLink) } + } private fun ContentPage.addSourcesContent(sources: List>) = builder .buildSourcesContent(this, sources) @@ -89,8 +100,8 @@ class SourceLinksTransformer(val context: DokkaContext) : PageTransformer { } private fun DocumentableSource.toLink(sourceLink: SourceLink): String { - val sourcePath = File(this.path).canonicalPath.replace("\\", "/") - val sourceLinkPath = File(sourceLink.path).canonicalPath.replace("\\", "/") + val sourcePath = File(this.path).invariantSeparatorsPath + val sourceLinkPath = File(sourceLink.path).invariantSeparatorsPath val lineNumber = when (this) { is DescriptorDocumentableSource -> this.descriptor @@ -134,6 +145,9 @@ class SourceLinksTransformer(val context: DokkaContext) : PageTransformer { } private fun PsiElement.lineNumber(): Int? { + // synthetic and some light methods might return null + val textRange = textRange ?: return null + val doc = PsiDocumentManager.getInstance(project).getDocument(containingFile) // IJ uses 0-based line-numbers; external source browsers use 1-based return doc?.getLineNumber(textRange.startOffset)?.plus(1) diff --git a/plugins/base/src/test/kotlin/enums/JavaEnumsTest.kt b/plugins/base/src/test/kotlin/enums/JavaEnumsTest.kt new file mode 100644 index 0000000000..6a59a57eaf --- /dev/null +++ b/plugins/base/src/test/kotlin/enums/JavaEnumsTest.kt @@ -0,0 +1,60 @@ +package enums + +import org.jetbrains.dokka.SourceLinkDefinitionImpl +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.junit.jupiter.api.Test +import signatures.renderedContent +import utils.TestOutputWriterPlugin +import java.net.URL +import kotlin.test.assertEquals + +class JavaEnumsTest : BaseAbstractTest() { + + // Shouldn't try to give source links to synthetic methods (values, valueOf) if any are present + // https://github.com/Kotlin/dokka/issues/2544 + @Test + fun `java enum with configured source links should not fail build due to any synthetic methods`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + sourceLinks = listOf( + SourceLinkDefinitionImpl( + localDirectory = "src/main/java", + remoteUrl = URL("https://github.com/user/repo/tree/master/src/main/java"), + remoteLineSuffix = "#L" + ) + ) + } + } + } + + val writerPlugin = TestOutputWriterPlugin() + + testInline( + """ + |/src/main/java/basic/JavaEnum.java + |package testpackage + | + |public enum JavaEnum { + | ONE, TWO, THREE + |} + """.trimMargin(), + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + val enumPage = writerPlugin.writer.renderedContent("root/testpackage/-java-enum/index.html") + val sourceLink = enumPage.select("div[data-togglable=Sources]") + .select("a[href]") + .attr("href") + + + assertEquals( + "https://github.com/user/repo/tree/master/src/main/java/basic/JavaEnum.java#L3", + sourceLink + ) + } + } + } +} diff --git a/plugins/base/src/test/kotlin/enums/EnumsTest.kt b/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt similarity index 86% rename from plugins/base/src/test/kotlin/enums/EnumsTest.kt rename to plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt index 8cb9f65499..f527b87ce5 100644 --- a/plugins/base/src/test/kotlin/enums/EnumsTest.kt +++ b/plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt @@ -1,6 +1,7 @@ package enums import matchers.content.* +import org.jetbrains.dokka.SourceLinkDefinitionImpl import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.model.* @@ -8,12 +9,15 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance import org.jsoup.Jsoup import org.jsoup.nodes.Element import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import signatures.renderedContent import utils.TestOutputWriter import utils.TestOutputWriterPlugin +import java.io.File +import java.net.URL -class EnumsTest : BaseAbstractTest() { +class KotlinEnumsTest : BaseAbstractTest() { @Test fun `should preserve enum source ordering for documentables`() { @@ -366,4 +370,52 @@ class EnumsTest : BaseAbstractTest() { } } } + + // Shouldn't try to give source links to synthetic methods (values, valueOf) if any are present + // Initially reported for Java, making sure it doesn't fail for Kotlin either + // https://github.com/Kotlin/dokka/issues/2544 + @Test + fun `kotlin enum with configured source links should not fail the build due to synthetic methods`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + sourceLinks = listOf( + SourceLinkDefinitionImpl( + localDirectory = "src/main/kotlin", + remoteUrl = URL("https://github.com/user/repo/tree/master/src/main/kotlin"), + remoteLineSuffix = "#L" + ) + ) + } + } + } + + val writerPlugin = TestOutputWriterPlugin() + + testInline( + """ + |/src/main/kotlin/basic/KotlinEnum.kt + |package testpackage + | + |enum class KotlinEnum { + | ONE, TWO, THREE + |} + """.trimMargin(), + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + val sourceLink = writerPlugin.writer.renderedContent("root/testpackage/-kotlin-enum/index.html") + .select("div[data-togglable=Sources]") + .select("a[href]") + .attr("href") + + assertEquals( + "https://github.com/user/repo/tree/master/src/main/kotlin/basic/KotlinEnum.kt#L3", + sourceLink + ) + } + } + } }