diff --git a/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt b/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt index 8bab690bb3..b5387b1da9 100644 --- a/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt +++ b/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt @@ -374,4 +374,42 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory") } + + @Test + fun `relative paths in configuraiton should work`() { + val resourcePath = + javaClass.getResource("/my-file.json")?.toURI() ?: throw IllegalStateException("No JSON found!") + val jsonPath = File(resourcePath) + + val dokkaOutputDir = File(projectDir, "output-relative") + assertTrue(dokkaOutputDir.mkdirs()) + jsonPath.writeText( + jsonBuilder( + outputPath = dokkaOutputDir.invariantSeparatorsPath, + pluginsClasspath = basePluginJarFile.absoluteFile.invariantSeparatorsPath, + projectPath = "src", // relative path + ) + ) + + ProcessBuilder( + "java", "-jar", cliJarFile.absolutePath, jsonPath.absolutePath + ).directory(projectDir).redirectErrorStream(true).start().also { process -> + val result = process.awaitProcessResult() + assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)") + } + + assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory") + + val htmlFiles = dokkaOutputDir.allHtmlFiles().map { it.relativeTo(dokkaOutputDir).path }.toList() + + // check that both Kotlin and Java sources are processed + + // kotlin: + assertContains(htmlFiles, "-dokka -example/it.basic/index.html") + assertContains(htmlFiles, "-dokka -example/it.basic/-public-class/public-documented-function.html") + + // java: + assertContains(htmlFiles, "-dokka -example/it.basic.java/index.html") + assertContains(htmlFiles, "-dokka -example/it.basic.java/-sample-java-class/public-documented-function.html") + } } diff --git a/dokka-subprojects/analysis-kotlin-descriptors-compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/DefaultDescriptorToDocumentableTranslator.kt b/dokka-subprojects/analysis-kotlin-descriptors-compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/DefaultDescriptorToDocumentableTranslator.kt index c488731f5c..1eb1165136 100644 --- a/dokka-subprojects/analysis-kotlin-descriptors-compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/DefaultDescriptorToDocumentableTranslator.kt +++ b/dokka-subprojects/analysis-kotlin-descriptors-compiler/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/descriptors/compiler/translator/DefaultDescriptorToDocumentableTranslator.kt @@ -168,9 +168,14 @@ private class DokkaDescriptorVisitor( private val syntheticDocProvider = SyntheticDescriptorDocumentationProvider(kDocFinder, sourceSet) private fun Collection.filterDescriptorsInSourceSet() = filter { - it.toSourceElement.containingFile.toString().let { path -> - path.isNotBlank() && sourceSet.sourceRoots.any { root -> - Paths.get(path).startsWith(root.toPath()) + val pathString = it.toSourceElement.containingFile.toString() + when { + pathString.isBlank() -> false + else -> { + val absolutePath = Paths.get(pathString).toRealPath() + sourceSet.sourceRoots.any { root -> + absolutePath.startsWith(root.toPath().toRealPath()) + } } } } diff --git a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt index dd5bcdf5ba..b763434a96 100644 --- a/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt +++ b/dokka-subprojects/analysis-kotlin-symbols/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/symbols/translators/DefaultSymbolToDocumentableTranslator.kt @@ -46,7 +46,6 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier -import java.nio.file.Paths internal class DefaultSymbolToDocumentableTranslator(context: DokkaContext) : AsyncSourceToDocumentableTranslator { private val kotlinAnalysis = context.plugin().querySingle { kotlinAnalysis } @@ -111,34 +110,31 @@ internal class DokkaSymbolVisitor( private val KtDeclarationSymbol.isExpect get() = (psi as? KtModifierListOwner)?.hasExpectModifier() == true - private fun Collection.filterSymbolsInSourceSet() = filter { - it.psi?.containingFile?.virtualFile?.path?.let { path -> - path.isNotBlank() && sourceSet.sourceRoots.any { root -> - Paths.get(path).startsWith(root.toPath()) - } - } == true + private fun List.filterSymbolsInSourceSet(moduleFiles: Set): List = filter { + when (val file = it.psi?.containingFile) { + is KtFile -> moduleFiles.contains(file) + else -> false + } } fun visitModule(): DModule { val sourceModule = analysisContext.getModule(sourceSet) - val ktFiles = analysisContext.modulesWithFiles[sourceModule]?.filterIsInstance() ?: throw IllegalStateException("No source files for a source module ${sourceModule.moduleName} of source set ${sourceSet.sourceSetID}") + val ktFiles = analysisContext.modulesWithFiles[sourceModule]?.filterIsInstance()?.toSet() + ?: throw IllegalStateException("No source files for a source module ${sourceModule.moduleName} of source set ${sourceSet.sourceSetID}") val processedPackages: MutableSet = mutableSetOf() return analyze(sourceModule) { - val packageSymbols: List = ktFiles - .mapNotNull { - if (processedPackages.contains(it.packageFqName)) - return@mapNotNull null - processedPackages.add(it.packageFqName) - getPackageSymbolIfPackageExists(it.packageFqName)?.let { it1 -> - visitPackageSymbol( - it1 - ) - } + val packages = ktFiles.mapNotNull { file -> + if (processedPackages.contains(file.packageFqName)) + return@mapNotNull null + processedPackages.add(file.packageFqName) + getPackageSymbolIfPackageExists(file.packageFqName)?.let { packageSymbol -> + visitPackageSymbol(packageSymbol, ktFiles) } + } DModule( name = moduleName, - packages = packageSymbols, + packages = packages, documentation = emptyMap(), expectPresentInSet = null, sourceSets = setOf(sourceSet) @@ -146,11 +142,14 @@ internal class DokkaSymbolVisitor( } } - private fun KtAnalysisSession.visitPackageSymbol(packageSymbol: KtPackageSymbol): DPackage { + private fun KtAnalysisSession.visitPackageSymbol( + packageSymbol: KtPackageSymbol, + moduleFiles: Set + ): DPackage { val dri = getDRIFromPackage(packageSymbol) val scope = packageSymbol.getPackageScope() - val callables = scope.getCallableSymbols().toList().filterSymbolsInSourceSet() - val classifiers = scope.getClassifierSymbols().toList().filterSymbolsInSourceSet() + val callables = scope.getCallableSymbols().toList().filterSymbolsInSourceSet(moduleFiles) + val classifiers = scope.getClassifierSymbols().toList().filterSymbolsInSourceSet(moduleFiles) val functions = callables.filterIsInstance().map { visitFunctionSymbol(it, dri) } val properties = callables.filterIsInstance().map { visitPropertySymbol(it, dri) } diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt index 245592ccc7..0fc4a618e8 100644 --- a/dokka-subprojects/plugin-base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt +++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt @@ -258,6 +258,11 @@ class ContentForInheritorsTest : BaseAbstractTest() { | |class Child : Parent() | + |/src/linuxX64Main/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class Child : Parent() + | """.trimMargin(), mppTestConfiguration ) {