diff options
69 files changed, 963 insertions, 220 deletions
diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt index eb2b2a65..8038089f 100644 --- a/core/src/main/kotlin/DokkaBootstrapImpl.kt +++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt @@ -10,6 +10,11 @@ fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinition { urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#" + it }) } +fun parseSourceRoot(sourceRoot: String): SourceRoot { + val components = sourceRoot.split("::", limit = 2) + return SourceRoot(components.last(), if (components.size == 1) listOf() else components[0].split(',')) +} + class DokkaBootstrapImpl : DokkaBootstrap { class DokkaProxyLogger(val consumer: BiConsumer<String, String>) : DokkaLogger { @@ -37,6 +42,7 @@ class DokkaBootstrapImpl : DokkaBootstrap { outputDir: String, format: String, includeNonPublic: Boolean, + includeRootPackage: Boolean, reportUndocumented: Boolean, skipEmptyPackages: Boolean, skipDeprecated: Boolean, @@ -46,7 +52,7 @@ class DokkaBootstrapImpl : DokkaBootstrap { generator = DokkaGenerator( DokkaProxyLogger(logger), classpath, - sources, + sources.map(::parseSourceRoot), samples, includes, moduleName, @@ -54,6 +60,7 @@ class DokkaBootstrapImpl : DokkaBootstrap { outputDir, format, includeNonPublic, + includeRootPackage, reportUndocumented, skipEmptyPackages, skipDeprecated, diff --git a/core/src/main/kotlin/Formats/GFMFormatService.kt b/core/src/main/kotlin/Formats/GFMFormatService.kt index cfb7fc03..cb31a1d3 100644 --- a/core/src/main/kotlin/Formats/GFMFormatService.kt +++ b/core/src/main/kotlin/Formats/GFMFormatService.kt @@ -3,11 +3,12 @@ package org.jetbrains.dokka import com.google.inject.Inject open class GFMOutputBuilder(to: StringBuilder, - location: Location, - locationService: LocationService, - languageService: LanguageService, - extension: String) - : MarkdownOutputBuilder(to, location, locationService, languageService, extension) + location: Location, + locationService: LocationService, + languageService: LanguageService, + extension: String, + impliedPlatforms: List<String>) + : MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { override fun appendTable(vararg columns: String, body: () -> Unit) { to.appendln(columns.joinToString(" | ", "| ", " |")) @@ -45,12 +46,14 @@ open class GFMOutputBuilder(to: StringBuilder, open class GFMFormatService(locationService: LocationService, signatureGenerator: LanguageService, - linkExtension: String) -: MarkdownFormatService(locationService, signatureGenerator, linkExtension) { + linkExtension: String, + impliedPlatforms: List<String>) +: MarkdownFormatService(locationService, signatureGenerator, linkExtension, impliedPlatforms) { @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService) : this(locationService, signatureGenerator, "md") + signatureGenerator: LanguageService, + impliedPlatforms: List<String>) : this(locationService, signatureGenerator, "md", impliedPlatforms) override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - GFMOutputBuilder(to, location, locationService, languageService, extension) + GFMOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) } diff --git a/core/src/main/kotlin/Formats/HtmlFormatService.kt b/core/src/main/kotlin/Formats/HtmlFormatService.kt index d73e4e62..6819e652 100644 --- a/core/src/main/kotlin/Formats/HtmlFormatService.kt +++ b/core/src/main/kotlin/Formats/HtmlFormatService.kt @@ -2,6 +2,7 @@ package org.jetbrains.dokka import com.google.inject.Inject import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName import java.io.File import java.nio.file.Path import java.nio.file.Paths @@ -11,8 +12,9 @@ open class HtmlOutputBuilder(to: StringBuilder, locationService: LocationService, languageService: LanguageService, extension: String, + impliedPlatforms: List<String>, val templateService: HtmlTemplateService) - : StructuredOutputBuilder(to, location, locationService, languageService, extension) + : StructuredOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { override fun appendText(text: String) { to.append(text.htmlEscape()) @@ -95,7 +97,8 @@ open class HtmlOutputBuilder(to: StringBuilder, open class HtmlFormatService @Inject constructor(@Named("folders") locationService: LocationService, signatureGenerator: LanguageService, - val templateService: HtmlTemplateService) + val templateService: HtmlTemplateService, + @Named(impliedPlatformsName) val impliedPlatforms: List<String>) : StructuredFormatService(locationService, signatureGenerator, "html"), OutlineFormatService { override fun enumerateSupportFiles(callback: (String, String) -> Unit) { @@ -103,7 +106,7 @@ open class HtmlFormatService @Inject constructor(@Named("folders") locationServi } override fun createOutputBuilder(to: StringBuilder, location: Location) = - HtmlOutputBuilder(to, location, locationService, languageService, extension, templateService) + HtmlOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms, templateService) override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) { templateService.appendHeader(to, "Module Contents", locationService.calcPathToRoot(location)) diff --git a/core/src/main/kotlin/Formats/JekyllFormatService.kt b/core/src/main/kotlin/Formats/JekyllFormatService.kt index bab73379..d217bf38 100644 --- a/core/src/main/kotlin/Formats/JekyllFormatService.kt +++ b/core/src/main/kotlin/Formats/JekyllFormatService.kt @@ -1,13 +1,16 @@ package org.jetbrains.dokka import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName open class JekyllOutputBuilder(to: StringBuilder, location: Location, locationService: LocationService, languageService: LanguageService, - extension: String) - : MarkdownOutputBuilder(to, location, locationService, languageService, extension) + extension: String, + impliedPlatforms: List<String>) + : MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { override fun appendNodes(nodes: Iterable<DocumentationNode>) { to.appendln("---") @@ -25,14 +28,15 @@ open class JekyllOutputBuilder(to: StringBuilder, open class JekyllFormatService(locationService: LocationService, signatureGenerator: LanguageService, - linkExtension: String) -: MarkdownFormatService(locationService, signatureGenerator, linkExtension) { + linkExtension: String, + impliedPlatforms: List<String>) +: MarkdownFormatService(locationService, signatureGenerator, linkExtension, impliedPlatforms) { @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService): this(locationService, signatureGenerator, "md") { - } + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(locationService, signatureGenerator, "md", impliedPlatforms) override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - JekyllOutputBuilder(to, location, locationService, languageService, extension) + JekyllOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) } diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt index 007e9353..7ebc5f09 100644 --- a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt +++ b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt @@ -1,14 +1,17 @@ package org.jetbrains.dokka import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName open class KotlinWebsiteOutputBuilder(to: StringBuilder, location: Location, locationService: LocationService, languageService: LanguageService, - extension: String) - : JekyllOutputBuilder(to, location, locationService, languageService, extension) + extension: String, + impliedPlatforms: List<String>) + : JekyllOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { private var needHardLineBreaks = false private var insideDiv = 0 @@ -145,11 +148,12 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder, } class KotlinWebsiteFormatService @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService) - : JekyllFormatService(locationService, signatureGenerator, "html") + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>) + : JekyllFormatService(locationService, signatureGenerator, "html", impliedPlatforms) { override fun createOutputBuilder(to: StringBuilder, location: Location) = - KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension) + KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) } @@ -157,14 +161,15 @@ class KotlinWebsiteRunnableSamplesOutputBuilder(to: StringBuilder, location: Location, locationService: LocationService, languageService: LanguageService, - extension: String) - : KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension) { + extension: String, + impliedPlatforms: List<String>) + : KotlinWebsiteOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) { div(to, "sample", true) { appendBlockCode(language) { imports() - wrap("\nfun main(args: Array<String>) {", "}") { + wrap("\n\nfun main(args: Array<String>) {", "}") { wrap("\n//sampleStart\n", "\n//sampleEnd\n", body) } } @@ -173,9 +178,10 @@ class KotlinWebsiteRunnableSamplesOutputBuilder(to: StringBuilder, } class KotlinWebsiteRunnableSamplesFormatService @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService) - : JekyllFormatService(locationService, signatureGenerator, "html") { + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>) + : JekyllFormatService(locationService, signatureGenerator, "html", impliedPlatforms) { override fun createOutputBuilder(to: StringBuilder, location: Location) = - KotlinWebsiteRunnableSamplesOutputBuilder(to, location, locationService, languageService, extension) + KotlinWebsiteRunnableSamplesOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) } diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt index fc63b2f2..b9c9c04f 100644 --- a/core/src/main/kotlin/Formats/MarkdownFormatService.kt +++ b/core/src/main/kotlin/Formats/MarkdownFormatService.kt @@ -1,6 +1,8 @@ package org.jetbrains.dokka import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName import java.util.* enum class ListKind { @@ -14,8 +16,9 @@ open class MarkdownOutputBuilder(to: StringBuilder, location: Location, locationService: LocationService, languageService: LanguageService, - extension: String) - : StructuredOutputBuilder(to, location, locationService, languageService, extension) + extension: String, + impliedPlatforms: List<String>) + : StructuredOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { private val listKindStack = Stack<ListKind>() protected var inTableCell = false @@ -211,11 +214,13 @@ open class MarkdownOutputBuilder(to: StringBuilder, open class MarkdownFormatService(locationService: LocationService, signatureGenerator: LanguageService, - linkExtension: String) + linkExtension: String, + val impliedPlatforms: List<String>) : StructuredFormatService(locationService, signatureGenerator, "md", linkExtension) { @Inject constructor(locationService: LocationService, - signatureGenerator: LanguageService): this(locationService, signatureGenerator, "md") + signatureGenerator: LanguageService, + @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(locationService, signatureGenerator, "md", impliedPlatforms) override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - MarkdownOutputBuilder(to, location, locationService, languageService, extension) + MarkdownOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) } diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt index 0e2085c9..6e887c05 100644 --- a/core/src/main/kotlin/Formats/StructuredFormatService.kt +++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka import org.jetbrains.dokka.LanguageService.RenderMode +import org.jetbrains.kotlin.utils.ifEmpty import java.util.* data class FormatLink(val text: String, val href: String) @@ -9,7 +10,8 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, val location: Location, val locationService: LocationService, val languageService: LanguageService, - val extension: String) : FormattedOutputBuilder { + val extension: String, + val impliedPlatforms: List<String>) : FormattedOutputBuilder { protected fun wrap(prefix: String, suffix: String, body: () -> Unit) { to.append(prefix) @@ -59,6 +61,13 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, abstract fun appendText(text: String) + open fun appendSinceKotlin(version: String) { + appendParagraph { + appendText("Available since Kotlin: ") + appendCode { appendText(version) } + } + } + open fun appendSymbol(text: String) { appendText(text) } @@ -281,12 +290,14 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } item.appendOverrides() item.appendDeprecation() + item.appendPlatforms() } // All items have exactly the same documentation, so we can use any item to render it val item = items.first() item.details(NodeKind.OverloadGroupNote).forEach { appendContent(it.content) } + appendContent(item.content.summary) item.appendDescription() } @@ -331,6 +342,17 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } + private fun DocumentationNode.appendPlatforms() { + val platforms = platformsToShow.ifEmpty { return } + appendParagraph { + appendStrong { to.append("Platform and version requirements:") } + to.append(" " + platforms.joinToString()) + } + } + + val DocumentationNode.platformsToShow: List<String> + get() = platforms.let { if (it.containsAll(impliedPlatforms)) it - impliedPlatforms else it } + private fun DocumentationNode.appendDescription() { if (content.description != ContentEmpty) { appendContent(content.description) @@ -381,15 +403,15 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, appendSection("Exceptions", node.members(NodeKind.Exception)) appendSection("Type Aliases", node.members(NodeKind.TypeAlias)) appendSection("Extensions for External Classes", node.members(NodeKind.ExternalClass)) - appendSection("Enum Values", node.members(NodeKind.EnumItem), sortMembers = false) - appendSection("Constructors", node.members(NodeKind.Constructor)) - appendSection("Properties", node.members(NodeKind.Property)) + appendSection("Enum Values", node.members(NodeKind.EnumItem), sortMembers = false, omitSamePlatforms = true) + appendSection("Constructors", node.members(NodeKind.Constructor), omitSamePlatforms = true) + appendSection("Properties", node.members(NodeKind.Property), omitSamePlatforms = true) appendSection("Inherited Properties", node.inheritedMembers(NodeKind.Property)) - appendSection("Functions", node.members(NodeKind.Function)) + appendSection("Functions", node.members(NodeKind.Function), omitSamePlatforms = true) appendSection("Inherited Functions", node.inheritedMembers(NodeKind.Function)) - appendSection("Companion Object Properties", node.members(NodeKind.CompanionObjectProperty)) + appendSection("Companion Object Properties", node.members(NodeKind.CompanionObjectProperty), omitSamePlatforms = true) appendSection("Inherited Companion Object Properties", node.inheritedCompanionObjectMembers(NodeKind.Property)) - appendSection("Companion Object Functions", node.members(NodeKind.CompanionObjectFunction)) + appendSection("Companion Object Functions", node.members(NodeKind.CompanionObjectFunction), omitSamePlatforms = true) appendSection("Inherited Companion Object Functions", node.inheritedCompanionObjectMembers(NodeKind.Function)) appendSection("Other members", node.members.filter { it.kind !in setOf( @@ -428,7 +450,9 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } - private fun appendSection(caption: String, members: List<DocumentationNode>, sortMembers: Boolean = true) { + private fun appendSection(caption: String, members: List<DocumentationNode>, + sortMembers: Boolean = true, + omitSamePlatforms: Boolean = false) { if (members.isEmpty()) return appendHeader(3) { appendText(caption) } @@ -437,11 +461,12 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, val membersMap = children.groupBy { link(node, it) } appendTable("Name", "Summary") { - appendTableBody() { + appendTableBody { for ((memberLocation, members) in membersMap) { - appendTableRow() { + appendTableRow { appendTableCell { appendLink(memberLocation) + appendPlatforms(members, omitSamePlatforms) } appendTableCell { val breakdownBySummary = members.groupBy { it.summary } @@ -475,6 +500,16 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, renderedSignatures.last().appendSignature() } } + + private fun appendPlatforms(items: List<DocumentationNode>, omitSamePlatforms: Boolean) { + val platforms = items.foldRight(items.first().platformsToShow.toSet()) { + node, platforms -> platforms.intersect(node.platformsToShow) + } + if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) { + appendLine() + to.append("(${platforms.joinToString()})") + } + } } inner class AllTypesNodeBuilder(val node: DocumentationNode) diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt index 360965d5..6b5df7c9 100644 --- a/core/src/main/kotlin/Generation/DokkaGenerator.kt +++ b/core/src/main/kotlin/Generation/DokkaGenerator.kt @@ -7,7 +7,8 @@ import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.psi.PsiFile import com.intellij.psi.PsiJavaFile import com.intellij.psi.PsiManager -import org.jetbrains.dokka.Utilities.DokkaModule +import org.jetbrains.dokka.Utilities.DokkaAnalysisModule +import org.jetbrains.dokka.Utilities.DokkaOutputModule import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.MessageCollector @@ -15,47 +16,76 @@ import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot import org.jetbrains.kotlin.config.JVMConfigurationKeys +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer import org.jetbrains.kotlin.resolve.TopDownAnalysisMode import org.jetbrains.kotlin.utils.PathUtil import java.io.File import kotlin.system.measureTimeMillis +class SourceRoot(path: String, val defaultPlatforms: List<String> = emptyList()) { + val path: String = File(path).absolutePath +} + class DokkaGenerator(val logger: DokkaLogger, val classpath: List<String>, - val sources: List<String>, + val sources: List<SourceRoot>, val samples: List<String>, val includes: List<String>, val moduleName: String, val options: DocumentationOptions) { + + private val documentationModule = DocumentationModule(moduleName) + fun generate() { - val environment = createAnalysisEnvironment() + val sourcesGroupedByPlatform = sources.groupBy { it.defaultPlatforms.firstOrNull() } + for ((platform, roots) in sourcesGroupedByPlatform) { + appendSourceModule(platform, roots) + } + documentationModule.prepareForGeneration(options) + + val timeBuild = measureTimeMillis { + logger.info("Generating pages... ") + val outputInjector = Guice.createInjector(DokkaOutputModule(options, logger)) + outputInjector.getInstance(Generator::class.java).buildAll(documentationModule) + } + logger.info("done in ${timeBuild / 1000} secs") + } + + private fun appendSourceModule(defaultPlatform: String?, sourceRoots: List<SourceRoot>) { + val sourcePaths = sourceRoots.map { it.path } + val environment = createAnalysisEnvironment(sourcePaths) logger.info("Module: $moduleName") logger.info("Output: ${File(options.outputDir)}") - logger.info("Sources: ${environment.sources.joinToString()}") + logger.info("Sources: ${sourcePaths.joinToString()}") logger.info("Classpath: ${environment.classpath.joinToString()}") logger.info("Analysing sources and libraries... ") val startAnalyse = System.currentTimeMillis() - val injector = Guice.createInjector(DokkaModule(environment, options, logger)) + val defaultPlatformAsList = defaultPlatform?.let { listOf(it) }.orEmpty() + val defaultPlatformsProvider = object : DefaultPlatformsProvider { + override fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String> { + val containingFilePath = descriptor.sourcePsi()?.containingFile?.virtualFile?.canonicalPath + ?.let { File(it).absolutePath } + val sourceRoot = containingFilePath?.let { path -> sourceRoots.find { path.startsWith(it.path) } } + return sourceRoot?.defaultPlatforms ?: defaultPlatformAsList + } + } + + val injector = Guice.createInjector( + DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentationModule.nodeRefGraph, logger)) - val documentation = buildDocumentationModule(injector, moduleName, { isSample(it) }, includes) + buildDocumentationModule(injector, documentationModule, { isNotSample(it) }, includes) val timeAnalyse = System.currentTimeMillis() - startAnalyse logger.info("done in ${timeAnalyse / 1000} secs") - val timeBuild = measureTimeMillis { - logger.info("Generating pages... ") - injector.getInstance(Generator::class.java).buildAll(documentation) - } - logger.info("done in ${timeBuild / 1000} secs") - Disposer.dispose(environment) } - fun createAnalysisEnvironment(): AnalysisEnvironment { + fun createAnalysisEnvironment(sourcePaths: List<String>): AnalysisEnvironment { val environment = AnalysisEnvironment(DokkaMessageCollector(logger)) environment.apply { @@ -65,14 +95,14 @@ class DokkaGenerator(val logger: DokkaLogger, addClasspath(File(element)) } - addSources(this@DokkaGenerator.sources) + addSources(sourcePaths) addSources(this@DokkaGenerator.samples) } return environment } - fun isSample(file: PsiFile): Boolean { + fun isNotSample(file: PsiFile): Boolean { val sourceFile = File(file.virtualFile!!.path) return samples.none { sample -> val canonicalSample = File(sample).canonicalPath @@ -100,9 +130,9 @@ class DokkaMessageCollector(val logger: DokkaLogger): MessageCollector { } fun buildDocumentationModule(injector: Injector, - moduleName: String, + documentationModule: DocumentationModule, filesToDocumentFilter: (PsiFile) -> Boolean = { file -> true }, - includes: List<String> = listOf()): DocumentationModule { + includes: List<String> = listOf()) { val coreEnvironment = injector.getInstance(KotlinCoreEnvironment::class.java) val fragmentFiles = coreEnvironment.getSourceFiles().filter(filesToDocumentFilter) @@ -120,7 +150,13 @@ fun buildDocumentationModule(injector: Injector, for (include in includes) { packageDocs.parse(include, fragments.firstOrNull()) } - val documentationModule = DocumentationModule(moduleName, packageDocs.moduleContent) + if (documentationModule.content.isEmpty()) { + documentationModule.updateContent { + for (node in packageDocs.moduleContent.children) { + append(node) + } + } + } with(injector.getInstance(DocumentationBuilder::class.java)) { documentationModule.appendFragments(fragments, packageDocs.packageContent, @@ -131,10 +167,6 @@ fun buildDocumentationModule(injector: Injector, with(injector.getInstance(JavaDocumentationBuilder::class.java)) { javaFiles.map { appendFile(it, documentationModule, packageDocs.packageContent) } } - - injector.getInstance(NodeReferenceGraph::class.java).resolveReferences() - - return documentationModule } diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt index 806e9f92..b2f4aeaf 100644 --- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt +++ b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt @@ -44,10 +44,10 @@ class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { private val refGraph: NodeReferenceGraph private val docParser: JavaDocumentationParser - @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph) { + @Inject constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, logger: DokkaLogger) { this.options = options this.refGraph = refGraph - this.docParser = JavadocParser(refGraph) + this.docParser = JavadocParser(refGraph, logger) } constructor(options: DocumentationOptions, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) { diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt index 0fb98230..af45f150 100644 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ b/core/src/main/kotlin/Java/JavadocParser.kt @@ -20,7 +20,8 @@ interface JavaDocumentationParser { fun parseDocumentation(element: PsiNamedElement): JavadocParseResult } -class JavadocParser(private val refGraph: NodeReferenceGraph) : JavaDocumentationParser { +class JavadocParser(private val refGraph: NodeReferenceGraph, + private val logger: DokkaLogger) : JavaDocumentationParser { override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { val docComment = (element as? PsiDocCommentOwner)?.docComment if (docComment == null) return JavadocParseResult.Empty @@ -99,7 +100,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph) : JavaDocumentatio private fun createLink(element: Element): ContentBlock { val docref = element.attr("docref") if (docref != null) { - return ContentNodeLazyLink(docref, { -> refGraph.lookup(docref)}) + return ContentNodeLazyLink(docref, { -> refGraph.lookupOrWarn(docref, logger)}) } val href = element.attr("href") if (href != null) { @@ -118,7 +119,7 @@ class JavadocParser(private val refGraph: NodeReferenceGraph) : JavaDocumentatio val linkSignature = resolveLink(linkElement) val text = ContentText(linkElement.text) if (linkSignature != null) { - val linkNode = ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookup(linkSignature)}) + val linkNode = ContentNodeLazyLink(tag.valueElement!!.text, { -> refGraph.lookupOrWarn(linkSignature, logger)}) linkNode.append(text) seeSection.append(linkNode) } else { diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt index 4c46f7d6..71b636bf 100644 --- a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt +++ b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt @@ -31,7 +31,7 @@ class DeclarationLinkResolver if (jdkHref != null) { return ContentExternalLink(jdkHref) } - return ContentNodeLazyLink(href, { -> refGraph.lookup(symbol.signature()) }) + return ContentNodeLazyLink(href, { -> refGraph.lookupOrWarn(symbol.signature(), logger) }) } if ("/" in href) { return ContentExternalLink(href) diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt index d1f64eeb..06acf2e6 100644 --- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt +++ b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt @@ -116,7 +116,7 @@ class DescriptorDocumentationParser fun parseJavadoc(descriptor: DeclarationDescriptor): Pair<Content, (DocumentationNode) -> Unit> { val psi = ((descriptor as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi if (psi is PsiDocCommentOwner) { - val parseResult = JavadocParser(refGraph).parseDocumentation(psi as PsiNamedElement) + val parseResult = JavadocParser(refGraph, logger).parseDocumentation(psi as PsiNamedElement) return parseResult.content to { node -> parseResult.deprecatedContent?.let { val deprecationNode = DocumentationNode("", it, NodeKind.Modifier) diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt index afb95fe6..2ab43fa1 100644 --- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt +++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt @@ -28,16 +28,19 @@ import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.supertypes +import com.google.inject.name.Named as GuiceNamed data class DocumentationOptions(val outputDir: String, val outputFormat: String, val includeNonPublic: Boolean = false, + val includeRootPackage: Boolean = false, val reportUndocumented: Boolean = true, val skipEmptyPackages: Boolean = true, val skipDeprecated: Boolean = false, val jdkVersion: Int = 6, val generateIndexPages: Boolean = true, - val sourceLinks: List<SourceLinkDefinition>) + val sourceLinks: List<SourceLinkDefinition> = emptyList(), + val impliedPlatforms: List<String> = emptyList()) private fun isExtensionForExternalClass(extensionFunctionDescriptor: DeclarationDescriptor, extensionReceiverDescriptor: DeclarationDescriptor, @@ -57,13 +60,19 @@ interface PackageDocumentationBuilder { allFqNames: Collection<FqName>) } +interface DefaultPlatformsProvider { + fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String> +} + class DocumentationBuilder @Inject constructor(val resolutionFacade: DokkaResolutionFacade, val descriptorDocumentationParser: DescriptorDocumentationParser, val options: DocumentationOptions, val refGraph: NodeReferenceGraph, + val platformNodeRegistry: PlatformNodeRegistry, val logger: DokkaLogger, - val linkResolver: DeclarationLinkResolver) { + val linkResolver: DeclarationLinkResolver, + val defaultPlatformsProvider: DefaultPlatformsProvider) { val boringBuiltinClasses = setOf( "kotlin.Unit", "kotlin.Byte", "kotlin.Short", "kotlin.Int", "kotlin.Long", "kotlin.Char", "kotlin.Boolean", "kotlin.Float", "kotlin.Double", "kotlin.String", "kotlin.Array", "kotlin.Any") @@ -156,6 +165,11 @@ class DocumentationBuilder return appendType(it.abbreviation) } + if (kotlinType.isDynamic()) { + append(DocumentationNode("dynamic", Content.Empty, kind), RefKind.Detail) + return + } + val classifierDescriptor = kotlinType.constructor.declarationDescriptor val name = when (classifierDescriptor) { is ClassDescriptor -> { @@ -199,17 +213,34 @@ class DocumentationBuilder fun DocumentationNode.appendAnnotations(annotated: Annotated) { annotated.annotations.forEach { it.build()?.let { annotationNode -> - val refKind = when { - it.isDocumented() && annotationNode.isDeprecation() -> RefKind.Deprecation - it.isDocumented() -> RefKind.Annotation - it.isHiddenInDocumentation() -> RefKind.HiddenAnnotation - else -> return@forEach + if (annotationNode.isSinceKotlin()) { + appendSinceKotlin(annotationNode) } - append(annotationNode, refKind) + else { + val refKind = when { + it.isDocumented() -> + when { + annotationNode.isDeprecation() -> RefKind.Deprecation + else -> RefKind.Annotation + } + it.isHiddenInDocumentation() -> RefKind.HiddenAnnotation + else -> return@forEach + } + append(annotationNode, refKind) + } + } } } + fun DocumentationNode.appendSinceKotlin(annotation: DocumentationNode) { + var kotlinVersion = annotation.detail(NodeKind.Parameter).detail(NodeKind.Value).name + if (kotlinVersion.startsWith('\"') && kotlinVersion.endsWith('\"')) { + kotlinVersion = kotlinVersion.substring(1..kotlinVersion.length-2) + } + append(platformNodeRegistry["Kotlin " + kotlinVersion], RefKind.Platform) + } + fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) { val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return KtTokens.MODIFIER_KEYWORDS_ARRAY.filter { it !in knownModifiers }.forEach { @@ -219,8 +250,16 @@ class DocumentationBuilder } } + fun DocumentationNode.appendDefaultPlatforms(descriptor: DeclarationDescriptor) { + for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor)) { + append(platformNodeRegistry[platform], RefKind.Platform) + } + } + fun DocumentationNode.isDeprecation() = name == "Deprecated" || name == "deprecated" + fun DocumentationNode.isSinceKotlin() = name == "SinceKotlin" && kind == NodeKind.Annotation + fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) { appendSourceLink(sourceElement.getPsi(), options.sourceLinks) } @@ -230,11 +269,7 @@ class DocumentationBuilder } fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: RefKind): DocumentationNode? { - // do not include generated code - if (descriptor is CallableMemberDescriptor && descriptor.kind != CallableMemberDescriptor.Kind.DECLARATION) - return null - - if (descriptor.isDocumented(options)) { + if (!descriptor.isGenerated() && descriptor.isDocumented(options)) { val node = descriptor.build() append(node, kind) return node @@ -242,21 +277,57 @@ class DocumentationBuilder return null } - fun DocumentationNode.appendMembers(descriptors: Iterable<DeclarationDescriptor>, - inheritedLinkKind: RefKind = RefKind.InheritedMember): List<DocumentationNode> { - val nodes = descriptors.map { descriptor -> - if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - val baseDescriptor = descriptor.overriddenDescriptors.firstOrNull() - if (baseDescriptor != null) { - link(this, baseDescriptor, inheritedLinkKind) + fun DocumentationNode.appendOrUpdateMember(descriptor: DeclarationDescriptor) { + if (descriptor.isGenerated() || !descriptor.isDocumented(options)) return + + val existingNode = refGraph.lookup(descriptor.signature()) + if (existingNode != null) { + existingNode.updatePlatforms(descriptor) + + if (descriptor is ClassDescriptor) { + val membersToDocument = descriptor.collectMembersToDocument() + for ((memberDescriptor, inheritedLinkKind, extraModifier) in membersToDocument) { + if (memberDescriptor is ClassDescriptor) { + existingNode.appendOrUpdateMember(memberDescriptor) // recurse into nested classes + } + else { + val existingMemberNode = refGraph.lookup(memberDescriptor.signature()) + if (existingMemberNode != null) { + existingMemberNode.updatePlatforms(memberDescriptor) + } + else { + existingNode.appendClassMember(memberDescriptor, inheritedLinkKind, extraModifier) + } + } } - null - } else { - val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original - appendChild(descriptorToUse, RefKind.Member) } } - return nodes.filterNotNull() + else { + appendChild(descriptor, RefKind.Member) + } + } + + private fun DocumentationNode.updatePlatforms(descriptor: DeclarationDescriptor) { + for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor) - platforms) { + append(platformNodeRegistry[platform], RefKind.Platform) + } + } + + fun DocumentationNode.appendClassMember(descriptor: DeclarationDescriptor, + inheritedLinkKind: RefKind = RefKind.InheritedMember, + extraModifier: String?) { + if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { + val baseDescriptor = descriptor.overriddenDescriptors.firstOrNull() + if (baseDescriptor != null) { + link(this, baseDescriptor, inheritedLinkKind) + } + } else { + val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original + val child = appendChild(descriptorToUse, RefKind.Member) + if (extraModifier != null) { + child?.appendTextNode("static", NodeKind.Modifier) + } + } } fun DocumentationNode.appendInPageChildren(descriptors: Iterable<DeclarationDescriptor>, kind: RefKind) { @@ -272,6 +343,7 @@ class DocumentationBuilder val allFqNames = fragments.map { it.fqName }.distinct() for (packageName in allFqNames) { + if (packageName.isRoot && !options.includeRootPackage) continue val declarations = fragments.filter { it.fqName == packageName }.flatMap { it.getMemberScope().getContributedDescriptors() } if (options.skipEmptyPackages && declarations.none { it.isDocumented(options) }) continue @@ -282,9 +354,6 @@ class DocumentationBuilder } propagateExtensionFunctionsToSubclasses(fragments) - if (options.generateIndexPages) { - generateAllTypesNode() - } } private fun propagateExtensionFunctionsToSubclasses(fragments: Collection<PackageFragmentDescriptor>) { @@ -297,13 +366,15 @@ class DocumentationBuilder .filter { it.extensionReceiverParameter != null } val extensionFunctionsByName = allExtensionFunctions.groupBy { it.name } - allExtensionFunctions.forEach { extensionFunction -> + for (extensionFunction in allExtensionFunctions) { + if (extensionFunction.dispatchReceiverParameter != null) continue val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name] ?.filter { fn -> fn.canShadow(extensionFunction) } ?: emptyList() - val classDescriptor = extensionFunction.getExtensionClassDescriptor() ?: return@forEach - val subclasses = classHierarchy[classDescriptor] ?: return@forEach + if (extensionFunction.extensionReceiverParameter?.type?.isDynamic() == true) continue + val classDescriptor = extensionFunction.getExtensionClassDescriptor() ?: continue + val subclasses = classHierarchy[classDescriptor] ?: continue subclasses.forEach { subclass -> if (subclass.isExtensionApplicable(extensionFunction) && possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) { @@ -357,19 +428,6 @@ class DocumentationBuilder return false } - private fun DocumentationNode.generateAllTypesNode() { - val allTypes = members(NodeKind.Package) - .flatMap { it.members.filter { it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass } } - .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.') else it.name } - - val allTypesNode = DocumentationNode("alltypes", Content.Empty, NodeKind.AllTypes) - for (typeNode in allTypes) { - allTypesNode.addReferenceTo(typeNode, RefKind.Member) - } - - append(allTypesNode, RefKind.Member) - } - fun DeclarationDescriptor.build(): DocumentationNode = when (this) { is ClassDescriptor -> build() is ConstructorDescriptor -> build() @@ -392,6 +450,7 @@ class DocumentationBuilder node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType) node.appendSourceLink(source) + node.appendDefaultPlatforms(this) register(this, node) return node @@ -413,28 +472,48 @@ class DocumentationBuilder } if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) { node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail) - val constructorsToDocument = if (getKind() == ClassKind.ENUM_CLASS) + } + for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) { + node.appendClassMember(descriptor, inheritedLinkKind, extraModifier) + } + node.appendAnnotations(this) + node.appendModifiers(this) + node.appendSourceLink(source) + node.appendDefaultPlatforms(this) + register(this, node) + return node + } + + data class ClassMember(val descriptor: DeclarationDescriptor, + val inheritedLinkKind: RefKind = RefKind.InheritedMember, + val extraModifier: String? = null) + + fun ClassDescriptor.collectMembersToDocument(): List<ClassMember> { + val result = arrayListOf<ClassMember>() + if (kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) { + val constructorsToDocument = if (kind == ClassKind.ENUM_CLASS) constructors.filter { it.valueParameters.size > 0 } else constructors - node.appendMembers(constructorsToDocument) - } - val members = defaultType.memberScope.getContributedDescriptors().filter { it != companionObjectDescriptor } - node.appendMembers(members) - node.appendMembers(staticScope.getContributedDescriptors()).forEach { - it.appendTextNode("static", NodeKind.Modifier) + constructorsToDocument.mapTo(result) { ClassMember(it) } } + + defaultType.memberScope.getContributedDescriptors() + .filter { it != companionObjectDescriptor } + .mapTo(result) { ClassMember(it) } + + staticScope.getContributedDescriptors() + .mapTo(result) { ClassMember(it, extraModifier = "static") } + val companionObjectDescriptor = companionObjectDescriptor if (companionObjectDescriptor != null) { val descriptors = companionObjectDescriptor.defaultType.memberScope.getContributedDescriptors() val descriptorsToDocument = descriptors.filter { it !is CallableDescriptor || !it.isInheritedFromAny() } - node.appendMembers(descriptorsToDocument, RefKind.InheritedCompanionObjectMember) + descriptorsToDocument.mapTo(result) { + ClassMember(it, inheritedLinkKind = RefKind.InheritedCompanionObjectMember) + } } - node.appendAnnotations(this) - node.appendModifiers(this) - node.appendSourceLink(source) - register(this, node) - return node + return result } fun CallableDescriptor.isInheritedFromAny(): Boolean { @@ -449,6 +528,7 @@ class DocumentationBuilder fun ConstructorDescriptor.build(): DocumentationNode { val node = nodeForDescriptor(this, NodeKind.Constructor) node.appendInPageChildren(valueParameters, RefKind.Detail) + node.appendDefaultPlatforms(this) register(this, node) return node } @@ -477,6 +557,7 @@ class DocumentationBuilder node.appendModifiers(this) node.appendSourceLink(source) node.appendSignature(this) + node.appendDefaultPlatforms(this) overriddenDescriptors.forEach { addOverrideLink(it, this) @@ -523,6 +604,7 @@ class DocumentationBuilder overriddenDescriptors.forEach { addOverrideLink(it, this) } + node.appendDefaultPlatforms(this) register(this, node) return node @@ -596,9 +678,10 @@ class DocumentationBuilder receiverClass = upperBoundClass } } - link(receiverClass, - containingDeclaration, - RefKind.Extension) + + if ((containingDeclaration as? FunctionDescriptor)?.dispatchReceiverParameter == null) { + link(receiverClass, containingDeclaration, RefKind.Extension) + } val node = DocumentationNode(name.asString(), Content.Empty, NodeKind.Receiver) node.appendType(type) @@ -646,6 +729,8 @@ fun DeclarationDescriptor.isDocumented(options: DocumentationOptions): Boolean { (!options.skipDeprecated || !isDeprecated()) } +private fun DeclarationDescriptor.isGenerated() = this is CallableMemberDescriptor && kind != CallableMemberDescriptor.Kind.DECLARATION + class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder { override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, packageName: FqName, @@ -657,7 +742,7 @@ class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder { with(documentationBuilder) { if (descriptor.isDocumented(options)) { val parent = packageNode.getParentForPackageMember(descriptor, externalClassNodes, allFqNames) - parent.appendChild(descriptor, RefKind.Member) + parent.appendOrUpdateMember(descriptor) } } } @@ -819,3 +904,23 @@ fun DeclarationDescriptor.sourceLocation(): String? { } return null } + +fun DocumentationModule.prepareForGeneration(options: DocumentationOptions) { + if (options.generateIndexPages) { + generateAllTypesNode() + } + nodeRefGraph.resolveReferences() +} + +fun DocumentationNode.generateAllTypesNode() { + val allTypes = members(NodeKind.Package) + .flatMap { it.members.filter { it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass } } + .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.') else it.name } + + val allTypesNode = DocumentationNode("alltypes", Content.Empty, NodeKind.AllTypes) + for (typeNode in allTypes) { + allTypesNode.addReferenceTo(typeNode, RefKind.Member) + } + + append(allTypesNode, RefKind.Member) +} diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt index faac9f7d..e54772b3 100644 --- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt +++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt @@ -186,6 +186,10 @@ class KotlinLanguageService : LanguageService { } private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) { + if (node.name == "dynamic") { + keyword("dynamic") + return + } if (node.isFunctionalType()) { renderFunctionalType(node, renderMode) return diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt index def0f626..caacac14 100644 --- a/core/src/main/kotlin/Model/DocumentationNode.kt +++ b/core/src/main/kotlin/Model/DocumentationNode.kt @@ -48,6 +48,7 @@ enum class NodeKind { Signature, ExternalLink, + Platform, AllTypes, @@ -97,6 +98,8 @@ open class DocumentationNode(val name: String, get() = references(RefKind.Annotation).map { it.to } val deprecation: DocumentationNode? get() = references(RefKind.Deprecation).singleOrNull()?.to + val platforms: List<String> + get() = references(RefKind.Platform).map { it.to.name } // TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice fun addReferenceTo(to: DocumentationNode, kind: RefKind) { @@ -135,6 +138,7 @@ open class DocumentationNode(val name: String, class DocumentationModule(name: String, content: Content = Content.Empty) : DocumentationNode(name, content, NodeKind.Module) { + val nodeRefGraph = NodeReferenceGraph() } val DocumentationNode.path: List<DocumentationNode> diff --git a/core/src/main/kotlin/Model/DocumentationReference.kt b/core/src/main/kotlin/Model/DocumentationReference.kt index 0165b567..a968f400 100644 --- a/core/src/main/kotlin/Model/DocumentationReference.kt +++ b/core/src/main/kotlin/Model/DocumentationReference.kt @@ -1,6 +1,5 @@ package org.jetbrains.dokka -import com.google.inject.Inject import com.google.inject.Singleton enum class RefKind { @@ -18,7 +17,8 @@ enum class RefKind { Annotation, HiddenAnnotation, Deprecation, - TopLevelPage + TopLevelPage, + Platform } data class DocumentationReference(val from: DocumentationNode, val to: DocumentationNode, val kind: RefKind) { @@ -36,9 +36,7 @@ class PendingDocumentationReference(val lazyNodeFrom: () -> DocumentationNode?, } } -@Singleton -class NodeReferenceGraph - @Inject constructor(val logger: DokkaLogger) { +class NodeReferenceGraph() { private val nodeMap = hashMapOf<String, DocumentationNode>() val references = arrayListOf<PendingDocumentationReference>() @@ -58,7 +56,9 @@ class NodeReferenceGraph references.add(PendingDocumentationReference({ -> nodeMap[fromSignature]}, { -> nodeMap[toSignature]}, kind)) } - fun lookup(signature: String): DocumentationNode? { + fun lookup(signature: String) = nodeMap[signature] + + fun lookupOrWarn(signature: String, logger: DokkaLogger): DocumentationNode? { val result = nodeMap[signature] if (result == null) { logger.warn("Can't find node by signature $signature") @@ -70,3 +70,14 @@ class NodeReferenceGraph references.forEach { it.resolve() } } } + +@Singleton +class PlatformNodeRegistry { + private val platformNodes = hashMapOf<String, DocumentationNode>() + + operator fun get(platform: String): DocumentationNode { + return platformNodes.getOrPut(platform) { + DocumentationNode(platform, Content.Empty, NodeKind.Platform) + } + } +} diff --git a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt index e6539135..1eb0c114 100644 --- a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt +++ b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt @@ -42,10 +42,9 @@ open class DefaultSampleProcessingService return ContentBlockSampleCode().apply { append(ContentText("//Source not found: $functionName")) } } - val text = processSampleBody(psiElement) - - val lines = text.trimEnd().split("\n".toRegex()).toTypedArray().filterNot(String::isEmpty) - val indent = lines.map { it.takeWhile(Char::isWhitespace).count() }.min() ?: 0 + val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd() + val lines = text.split("\n") + val indent = lines.filter(String::isNotBlank).map { it.takeWhile(Char::isWhitespace).count() }.min() ?: 0 val finalText = lines.map { it.drop(indent) }.joinToString("\n") return ContentBlockSampleCode(importsBlock = processImports(psiElement)).apply { append(ContentText(finalText)) } diff --git a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt index ffc5c9c7..ca857aaa 100644 --- a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt +++ b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt @@ -2,6 +2,7 @@ package org.jetbrains.dokka.Samples import com.google.inject.Inject import com.intellij.psi.PsiElement +import com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.dokka.* import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.ImportPath @@ -12,7 +13,7 @@ open class KotlinWebsiteSampleProcessingService resolutionFacade: DokkaResolutionFacade) : DefaultSampleProcessingService(options, logger, resolutionFacade) { - private class SampleBuilder() : KtVisitorVoid() { + private class SampleBuilder : KtTreeVisitorVoid() { val builder = StringBuilder() val text: String get() = builder.toString() @@ -48,13 +49,10 @@ open class KotlinWebsiteSampleProcessingService } } - override fun visitElement(element: PsiElement?) { - if (element != null) { - if (element.children.isEmpty()) - builder.append(element.text) - else - element.acceptChildren(this) - } + override fun visitElement(element: PsiElement) { + if (element is LeafPsiElement) + builder.append(element.text) + super.visitElement(element) } } diff --git a/core/src/main/kotlin/Utilities/DokkaModule.kt b/core/src/main/kotlin/Utilities/DokkaModules.kt index e1ae829a..5982d7dd 100644 --- a/core/src/main/kotlin/Utilities/DokkaModule.kt +++ b/core/src/main/kotlin/Utilities/DokkaModules.kt @@ -3,6 +3,7 @@ package org.jetbrains.dokka.Utilities import com.google.inject.Binder import com.google.inject.Module import com.google.inject.Provider +import com.google.inject.TypeLiteral import com.google.inject.name.Names import org.jetbrains.dokka.* import org.jetbrains.dokka.Formats.FormatDescriptor @@ -10,10 +11,47 @@ import org.jetbrains.dokka.Samples.SampleProcessingService import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import java.io.File -class DokkaModule(val environment: AnalysisEnvironment, - val options: DocumentationOptions, - val logger: DokkaLogger) : Module { +const val impliedPlatformsName = "impliedPlatforms" + +class DokkaAnalysisModule(val environment: AnalysisEnvironment, + val options: DocumentationOptions, + val defaultPlatformsProvider: DefaultPlatformsProvider, + val nodeReferenceGraph: NodeReferenceGraph, + val logger: DokkaLogger) : Module { + override fun configure(binder: Binder) { + val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", options.outputFormat) + + binder.registerCategory<LanguageService>("language") + binder.bind<PackageDocumentationBuilder>().to(descriptor.packageDocumentationBuilderClass.java) + binder.bind<JavaDocumentationBuilder>().to(descriptor.javaDocumentationBuilderClass.java) + binder.bind<SampleProcessingService>().to(descriptor.sampleProcessingService.java) + + val coreEnvironment = environment.createCoreEnvironment() + binder.bind<KotlinCoreEnvironment>().toInstance(coreEnvironment) + + val dokkaResolutionFacade = environment.createResolutionFacade(coreEnvironment) + binder.bind<DokkaResolutionFacade>().toInstance(dokkaResolutionFacade) + + binder.bind<DocumentationOptions>().toInstance(options) + binder.bind<DokkaLogger>().toInstance(logger) + + binder.bind<DefaultPlatformsProvider>().toInstance(defaultPlatformsProvider) + + binder.bind<NodeReferenceGraph>().toInstance(nodeReferenceGraph) + } +} + +object StringListType : TypeLiteral<@JvmSuppressWildcards List<String>>() + +class DokkaOutputModule(val options: DocumentationOptions, + val logger: DokkaLogger) : Module { override fun configure(binder: Binder) { + binder.bind(LanguageService::class.java).to(KotlinLanguageService::class.java) + + binder.bind(HtmlTemplateService::class.java).toProvider(object : Provider<HtmlTemplateService> { + override fun get(): HtmlTemplateService = HtmlTemplateService.default("style.css") + }) + binder.bind(File::class.java).annotatedWith(Names.named("outputDir")).toInstance(File(options.outputDir)) binder.bindNameAnnotated<LocationService, SingleFolderLocationService>("singleFolder") @@ -24,13 +62,7 @@ class DokkaModule(val environment: AnalysisEnvironment, // defaults binder.bind(LocationService::class.java).to(FoldersLocationService::class.java) binder.bind(FileLocationService::class.java).to(FoldersLocationService::class.java) - binder.bind(LanguageService::class.java).to(KotlinLanguageService::class.java) - - binder.bind(HtmlTemplateService::class.java).toProvider(object : Provider<HtmlTemplateService> { - override fun get(): HtmlTemplateService = HtmlTemplateService.default("style.css") - }) - binder.registerCategory<LanguageService>("language") binder.registerCategory<OutlineFormatService>("outline") binder.registerCategory<FormatService>("format") binder.registerCategory<Generator>("generator") @@ -43,20 +75,12 @@ class DokkaModule(val environment: AnalysisEnvironment, descriptor.formatServiceClass?.let { clazz -> binder.bind(FormatService::class.java).to(clazz.java) } - binder.bind<PackageDocumentationBuilder>().to(descriptor.packageDocumentationBuilderClass.java) - binder.bind<JavaDocumentationBuilder>().to(descriptor.javaDocumentationBuilderClass.java) - binder.bind<SampleProcessingService>().to(descriptor.sampleProcessingService.java) binder.bind<Generator>().to(descriptor.generatorServiceClass.java) - val coreEnvironment = environment.createCoreEnvironment() - binder.bind<KotlinCoreEnvironment>().toInstance(coreEnvironment) - - val dokkaResolutionFacade = environment.createResolutionFacade(coreEnvironment) - binder.bind<DokkaResolutionFacade>().toInstance(dokkaResolutionFacade) - binder.bind<DocumentationOptions>().toInstance(options) binder.bind<DokkaLogger>().toInstance(logger) + binder.bind(StringListType).annotatedWith(Names.named(impliedPlatformsName)).toInstance(options.impliedPlatforms) } } diff --git a/core/src/test/kotlin/TestAPI.kt b/core/src/test/kotlin/TestAPI.kt index 7197d2c4..09a346cf 100644 --- a/core/src/test/kotlin/TestAPI.kt +++ b/core/src/test/kotlin/TestAPI.kt @@ -5,13 +5,14 @@ import com.intellij.openapi.application.PathManager import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.io.FileUtil import org.jetbrains.dokka.* -import org.jetbrains.dokka.Utilities.DokkaModule +import org.jetbrains.dokka.Utilities.DokkaAnalysisModule import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot import org.jetbrains.kotlin.config.ContentRoot import org.jetbrains.kotlin.config.KotlinSourceRoot +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.junit.Assert import org.junit.Assert.fail import java.io.File @@ -22,6 +23,30 @@ fun verifyModel(vararg roots: ContentRoot, format: String = "html", includeNonPublic: Boolean = true, verifier: (DocumentationModule) -> Unit) { + val documentation = DocumentationModule("test") + + val options = DocumentationOptions("", format, + includeNonPublic = includeNonPublic, + skipEmptyPackages = false, + includeRootPackage = true, + sourceLinks = listOf<SourceLinkDefinition>(), + generateIndexPages = false) + + appendDocumentation(documentation, *roots, + withJdk = withJdk, + withKotlinRuntime = withKotlinRuntime, + options = options) + documentation.prepareForGeneration(options) + + verifier(documentation) +} + +fun appendDocumentation(documentation: DocumentationModule, + vararg roots: ContentRoot, + withJdk: Boolean = false, + withKotlinRuntime: Boolean = false, + options: DocumentationOptions, + defaultPlatforms: List<String> = emptyList()) { val messageCollector = object : MessageCollector { override fun clear() { @@ -60,14 +85,12 @@ fun verifyModel(vararg roots: ContentRoot, } addRoots(roots.toList()) } - val options = DocumentationOptions("", format, - includeNonPublic = includeNonPublic, - skipEmptyPackages = false, - sourceLinks = listOf<SourceLinkDefinition>(), - generateIndexPages = false) - val injector = Guice.createInjector(DokkaModule(environment, options, DokkaConsoleLogger)) - val documentation = buildDocumentationModule(injector, "test") - verifier(documentation) + val defaultPlatformsProvider = object : DefaultPlatformsProvider { + override fun getDefaultPlatforms(descriptor: DeclarationDescriptor) = defaultPlatforms + } + val injector = Guice.createInjector( + DokkaAnalysisModule(environment, options, defaultPlatformsProvider, documentation.nodeRefGraph, DokkaConsoleLogger)) + buildDocumentationModule(injector, documentation) Disposer.dispose(environment) } @@ -128,19 +151,23 @@ fun verifyOutput(roots: Array<ContentRoot>, format: String = "html", outputGenerator: (DocumentationModule, StringBuilder) -> Unit) { verifyModel(*roots, withJdk = withJdk, withKotlinRuntime = withKotlinRuntime, format = format) { - verifyModelOutput(it, outputExtension, outputGenerator, roots.first().path) + verifyModelOutput(it, outputExtension, roots.first().path, outputGenerator) } } -private fun verifyModelOutput(it: DocumentationModule, - outputExtension: String, - outputGenerator: (DocumentationModule, StringBuilder) -> Unit, - sourcePath: String) { +fun verifyModelOutput(it: DocumentationModule, + outputExtension: String, + sourcePath: String, + outputGenerator: (DocumentationModule, StringBuilder) -> Unit) { val output = StringBuilder() outputGenerator(it, output) val ext = outputExtension.removePrefix(".") - val path = sourcePath - val expectedOutput = File(path.replaceAfterLast(".", ext, path + "." + ext)).readText() + val expectedFileContent = File(sourcePath.replaceAfterLast(".", ext, sourcePath + "." + ext)).readText() + val expectedOutput = + if (ext.equals("html", true)) + expectedFileContent.lines().joinToString(separator = "\n", transform = String::trim) + else + expectedFileContent assertEqualsIgnoringSeparators(expectedOutput, output.toString()) } @@ -158,7 +185,7 @@ fun verifyJavaOutput(path: String, withKotlinRuntime: Boolean = false, outputGenerator: (DocumentationModule, StringBuilder) -> Unit) { verifyJavaModel(path, withKotlinRuntime) { model -> - verifyModelOutput(model, outputExtension, outputGenerator, path) + verifyModelOutput(model, outputExtension, path, outputGenerator) } } diff --git a/core/src/test/kotlin/format/GFMFormatTest.kt b/core/src/test/kotlin/format/GFMFormatTest.kt index 5327c9dc..c097c5c8 100644 --- a/core/src/test/kotlin/format/GFMFormatTest.kt +++ b/core/src/test/kotlin/format/GFMFormatTest.kt @@ -5,7 +5,7 @@ import org.jetbrains.dokka.KotlinLanguageService import org.junit.Test class GFMFormatTest { - private val gfmService = GFMFormatService(InMemoryLocationService, KotlinLanguageService()) + private val gfmService = GFMFormatService(InMemoryLocationService, KotlinLanguageService(), listOf()) @Test fun sample() { verifyGFMNodeByName("sample", "Foo") diff --git a/core/src/test/kotlin/format/HtmlFormatTest.kt b/core/src/test/kotlin/format/HtmlFormatTest.kt index f2b8624d..cbea3209 100644 --- a/core/src/test/kotlin/format/HtmlFormatTest.kt +++ b/core/src/test/kotlin/format/HtmlFormatTest.kt @@ -7,7 +7,7 @@ import org.junit.Test import java.io.File class HtmlFormatTest { - private val htmlService = HtmlFormatService(InMemoryLocationService, KotlinLanguageService(), HtmlTemplateService.default()) + private val htmlService = HtmlFormatService(InMemoryLocationService, KotlinLanguageService(), HtmlTemplateService.default(), listOf()) @Test fun classWithCompanionObject() { verifyHtmlNode("classWithCompanionObject") @@ -138,6 +138,10 @@ class HtmlFormatTest { verifyHtmlNode("functionalTypeWithNamedParameters") } + @Test fun sinceKotlin() { + verifyHtmlNode("sinceKotlin") + } + @Test fun blankLineInsideCodeBlock() { verifyHtmlNode("blankLineInsideCodeBlock") } diff --git a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt index 3363b0a3..27c84aca 100644 --- a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt +++ b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt @@ -5,7 +5,7 @@ import org.jetbrains.dokka.KotlinWebsiteFormatService import org.junit.Test class KotlinWebSiteFormatTest { - private val kwsService = KotlinWebsiteFormatService(InMemoryLocationService, KotlinLanguageService()) + private val kwsService = KotlinWebsiteFormatService(InMemoryLocationService, KotlinLanguageService(), listOf()) @Test fun sample() { verifyKWSNodeByName("sample", "foo") diff --git a/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt index 742d2908..3e46ead7 100644 --- a/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt +++ b/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt @@ -5,7 +5,7 @@ import org.jetbrains.dokka.KotlinWebsiteRunnableSamplesFormatService import org.junit.Test class KotlinWebSiteRunnableSamplesFormatTest { - private val kwsService = KotlinWebsiteRunnableSamplesFormatService(InMemoryLocationService, KotlinLanguageService()) + private val kwsService = KotlinWebsiteRunnableSamplesFormatService(InMemoryLocationService, KotlinLanguageService(), listOf()) @Test fun dropImport() { @@ -20,6 +20,10 @@ class KotlinWebSiteRunnableSamplesFormatTest { verifyKWSNodeByName("sampleWithAsserts", "a") } + @Test fun newLinesInSamples() { + verifyKWSNodeByName("newLinesInSamples", "foo") + } + private fun verifyKWSNodeByName(fileName: String, name: String) { verifyOutput("testdata/format/website-samples/$fileName.kt", ".md", format = "kotlin-website-samples") { model, output -> kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.filter { it.name == name }) diff --git a/core/src/test/kotlin/format/MarkdownFormatTest.kt b/core/src/test/kotlin/format/MarkdownFormatTest.kt index d788c84d..4830d760 100644 --- a/core/src/test/kotlin/format/MarkdownFormatTest.kt +++ b/core/src/test/kotlin/format/MarkdownFormatTest.kt @@ -1,14 +1,10 @@ package org.jetbrains.dokka.tests -import org.jetbrains.dokka.DocumentationModule -import org.jetbrains.dokka.DocumentationNode -import org.jetbrains.dokka.KotlinLanguageService -import org.jetbrains.dokka.MarkdownFormatService -import org.junit.Ignore +import org.jetbrains.dokka.* import org.junit.Test class MarkdownFormatTest { - private val markdownService = MarkdownFormatService(InMemoryLocationService, KotlinLanguageService()) + private val markdownService = MarkdownFormatService(InMemoryLocationService, KotlinLanguageService(), listOf()) @Test fun emptyDescription() { verifyMarkdownNode("emptyDescription") @@ -241,6 +237,67 @@ class MarkdownFormatTest { verifyMarkdownPackage("suspendParam") } + @Test fun sinceKotlin() { + verifyMarkdownNode("sinceKotlin") + verifyMarkdownPackage("sinceKotlin") + } + + @Test fun dynamicType() { + verifyMarkdownNode("dynamicType") + } + + @Test fun dynamicExtension() { + verifyMarkdownNodes("dynamicExtension") { model -> model.members.single().members.filter { it.name == "Foo" } } + } + + @Test fun memberExtension() { + verifyMarkdownNodes("memberExtension") { model -> model.members.single().members.filter { it.name == "Foo" } } + } + + @Test fun multiplePlatforms() { + verifyMultiplatformPackage(buildMultiplePlatforms("multiplatform"), "multiplatform") + } + + @Test fun multiplePlatformsMerge() { + verifyMultiplatformPackage(buildMultiplePlatforms("multiplatformMerge"), "multiplatformMerge") + } + + @Test fun multiplePlatformsMergeMembers() { + val module = buildMultiplePlatforms("multiplatformMergeMembers") + verifyModelOutput(module, ".md", "testdata/format/multiplatformMergeMembers/foo.kt") { model, output -> + markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members) + } + } + + @Test fun multiplePlatformsOmitRedundant() { + val module = buildMultiplePlatforms("multiplatformOmitRedundant") + verifyModelOutput(module, ".md", "testdata/format/multiplatformOmitRedundant/foo.kt") { model, output -> + markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members) + } + } + + @Test fun multiplePlatformsImplied() { + val module = buildMultiplePlatforms("multiplatformImplied") + verifyModelOutput(module, ".md", "testdata/format/multiplatformImplied/foo.kt") { model, output -> + MarkdownFormatService(InMemoryLocationService, KotlinLanguageService(), listOf("JVM", "JS")) + .createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members) + } + } + + private fun buildMultiplePlatforms(path: String): DocumentationModule { + val module = DocumentationModule("test") + val options = DocumentationOptions("", "html", generateIndexPages = false) + appendDocumentation(module, contentRootFromPath("testdata/format/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), options = options) + appendDocumentation(module, contentRootFromPath("testdata/format/$path/js.kt"), defaultPlatforms = listOf("JS"), options = options) + return module + } + + private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) { + verifyModelOutput(module, ".package.md", "testdata/format/$path/multiplatform.kt") { model, output -> + markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members) + } + } + @Test fun blankLineInsideCodeBlock() { verifyMarkdownNode("blankLineInsideCodeBlock") } diff --git a/core/src/test/kotlin/model/ClassTest.kt b/core/src/test/kotlin/model/ClassTest.kt index d50a3624..fb225728 100644 --- a/core/src/test/kotlin/model/ClassTest.kt +++ b/core/src/test/kotlin/model/ClassTest.kt @@ -3,11 +3,11 @@ package org.jetbrains.dokka.tests import org.jetbrains.dokka.Content import org.jetbrains.dokka.NodeKind import org.jetbrains.dokka.RefKind -import org.junit.Test import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue +import org.junit.Test -public class ClassTest { +class ClassTest { @Test fun emptyClass() { verifyModel("testdata/classes/emptyClass.kt") { model -> with(model.members.single().members.single()) { @@ -272,4 +272,12 @@ public class ClassTest { } } } + + @Test fun sinceKotlin() { + verifyModel("testdata/classes/sinceKotlin.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(listOf("Kotlin 1.1"), platforms) + } + } + } } diff --git a/core/src/test/kotlin/model/CommentTest.kt b/core/src/test/kotlin/model/CommentTest.kt index 2b2bc87e..3752bb8c 100644 --- a/core/src/test/kotlin/model/CommentTest.kt +++ b/core/src/test/kotlin/model/CommentTest.kt @@ -157,7 +157,8 @@ line two""", toTestString()) with(model.members.single().members.first()) { assertEquals("Summary", content.summary.toTestString()) with (content.description) { - assertEqualsIgnoringSeparators("""[code lang=kotlin] + assertEqualsIgnoringSeparators(""" + |[code lang=kotlin] |if (true) { | println(property) |} diff --git a/core/src/test/kotlin/model/FunctionTest.kt b/core/src/test/kotlin/model/FunctionTest.kt index 4cced562..ddd33941 100644 --- a/core/src/test/kotlin/model/FunctionTest.kt +++ b/core/src/test/kotlin/model/FunctionTest.kt @@ -2,11 +2,11 @@ package org.jetbrains.dokka.tests import org.jetbrains.dokka.Content import org.jetbrains.dokka.NodeKind -import org.junit.Test import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue +import org.junit.Test -public class FunctionTest { +class FunctionTest { @Test fun function() { verifyModel("testdata/functions/function.kt") { model -> with(model.members.single().members.single()) { @@ -224,4 +224,12 @@ Documentation""", content.description.toTestString()) } } } + + @Test fun sinceKotlin() { + verifyModel("testdata/functions/sinceKotlin.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(listOf("Kotlin 1.1"), platforms) + } + } + } } diff --git a/core/src/test/kotlin/model/PropertyTest.kt b/core/src/test/kotlin/model/PropertyTest.kt index cdf44c03..0ee0e0f5 100644 --- a/core/src/test/kotlin/model/PropertyTest.kt +++ b/core/src/test/kotlin/model/PropertyTest.kt @@ -3,11 +3,11 @@ package org.jetbrains.dokka.tests import org.jetbrains.dokka.Content import org.jetbrains.dokka.NodeKind import org.jetbrains.dokka.RefKind -import org.junit.Test import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue +import org.junit.Test -public class PropertyTest { +class PropertyTest { @Test fun valueProperty() { verifyModel("testdata/properties/valueProperty.kt") { model -> with(model.members.single().members.single()) { @@ -100,4 +100,12 @@ public class PropertyTest { } } } + + @Test fun sinceKotlin() { + verifyModel("testdata/properties/sinceKotlin.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(listOf("Kotlin 1.1"), platforms) + } + } + } } diff --git a/core/src/test/kotlin/model/TypeAliasTest.kt b/core/src/test/kotlin/model/TypeAliasTest.kt index 812fd9dc..c653ac83 100644 --- a/core/src/test/kotlin/model/TypeAliasTest.kt +++ b/core/src/test/kotlin/model/TypeAliasTest.kt @@ -120,4 +120,13 @@ class TypeAliasTest { } } } + + @Test + fun sinceKotlin() { + verifyModel("testdata/typealias/sinceKotlin.kt") { model -> + with(model.members.single().members.single()) { + assertEquals(listOf("Kotlin 1.1"), platforms) + } + } + } }
\ No newline at end of file diff --git a/core/testdata/classes/sinceKotlin.kt b/core/testdata/classes/sinceKotlin.kt new file mode 100644 index 00000000..1025cf0d --- /dev/null +++ b/core/testdata/classes/sinceKotlin.kt @@ -0,0 +1,5 @@ +/** + * Useful + */ +@SinceKotlin("1.1") +class `Since1.1`
\ No newline at end of file diff --git a/core/testdata/format/dynamicExtension.kt b/core/testdata/format/dynamicExtension.kt new file mode 100644 index 00000000..5c83bf22 --- /dev/null +++ b/core/testdata/format/dynamicExtension.kt @@ -0,0 +1,3 @@ +class Foo + +fun dynamic.bar() {} diff --git a/core/testdata/format/dynamicExtension.md b/core/testdata/format/dynamicExtension.md new file mode 100644 index 00000000..2fd928f6 --- /dev/null +++ b/core/testdata/format/dynamicExtension.md @@ -0,0 +1,10 @@ +[test](test/index) / [Foo](test/-foo/index) + +# Foo + +`class Foo` + +### Constructors + +| [<init>](test/-foo/-init-) | `Foo()` | + diff --git a/core/testdata/format/dynamicType.kt b/core/testdata/format/dynamicType.kt new file mode 100644 index 00000000..9d557ac0 --- /dev/null +++ b/core/testdata/format/dynamicType.kt @@ -0,0 +1,2 @@ +fun foo(): dynamic = "" + diff --git a/core/testdata/format/dynamicType.md b/core/testdata/format/dynamicType.md new file mode 100644 index 00000000..a3d6696b --- /dev/null +++ b/core/testdata/format/dynamicType.md @@ -0,0 +1,5 @@ +[test](test/index) / [foo](test/foo) + +# foo + +`fun foo(): dynamic`
\ No newline at end of file diff --git a/core/testdata/format/memberExtension.kt b/core/testdata/format/memberExtension.kt new file mode 100644 index 00000000..955794d1 --- /dev/null +++ b/core/testdata/format/memberExtension.kt @@ -0,0 +1,8 @@ +open class X + +class Foo : X + +class Bar { + fun X.y() = "" + fun Foo.x() = "" +} diff --git a/core/testdata/format/memberExtension.md b/core/testdata/format/memberExtension.md new file mode 100644 index 00000000..b9db4e3d --- /dev/null +++ b/core/testdata/format/memberExtension.md @@ -0,0 +1,10 @@ +[test](test/index) / [Foo](test/-foo/index) + +# Foo + +`class Foo : `[`X`](test/-x/index) + +### Constructors + +| [<init>](test/-foo/-init-) | `Foo()` | + diff --git a/core/testdata/format/multiplatform/js.kt b/core/testdata/format/multiplatform/js.kt new file mode 100644 index 00000000..e6d66ffd --- /dev/null +++ b/core/testdata/format/multiplatform/js.kt @@ -0,0 +1,7 @@ +package foo + +/** + * This is a bar. + */ +class Bar { +} diff --git a/core/testdata/format/multiplatform/jvm.kt b/core/testdata/format/multiplatform/jvm.kt new file mode 100644 index 00000000..cb77273f --- /dev/null +++ b/core/testdata/format/multiplatform/jvm.kt @@ -0,0 +1,8 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + +} diff --git a/core/testdata/format/multiplatform/multiplatform.package.md b/core/testdata/format/multiplatform/multiplatform.package.md new file mode 100644 index 00000000..3574942c --- /dev/null +++ b/core/testdata/format/multiplatform/multiplatform.package.md @@ -0,0 +1,9 @@ +[test](test/index) / [foo](test/foo/index) + +## Package foo + +### Types + +| [Bar](test/foo/-bar/index)<br>(JS) | `class Bar`<br>This is a bar. | +| [Foo](test/foo/-foo/index)<br>(JVM) | `class Foo`<br>This is a foo. | + diff --git a/core/testdata/format/multiplatformImplied/foo.md b/core/testdata/format/multiplatformImplied/foo.md new file mode 100644 index 00000000..c615dd8e --- /dev/null +++ b/core/testdata/format/multiplatformImplied/foo.md @@ -0,0 +1,24 @@ +[test](test/index) / [foo](test/foo/index) / [Foo](test/foo/-foo/index) + +# Foo + +`class Foo` + +This is a foo. + +### Constructors + +| [<init>](test/foo/-foo/-init-) | `Foo()`<br>This is a foo. | + +### Properties + +| [propJs](test/foo/-foo/prop-js)<br>(JS) | `val propJs: String` | +| [propJvm](test/foo/-foo/prop-jvm)<br>(JVM) | `val propJvm: String` | +| [propJvmAndJs](test/foo/-foo/prop-jvm-and-js) | `val propJvmAndJs: Int` | + +### Functions + +| [bothJvmAndJs](test/foo/-foo/both-jvm-and-js) | `fun bothJvmAndJs(): Unit` | +| [js](test/foo/-foo/js)<br>(JS) | `fun js(): Unit` | +| [jvm](test/foo/-foo/jvm)<br>(JVM) | `fun jvm(): Unit` | + diff --git a/core/testdata/format/multiplatformImplied/js.kt b/core/testdata/format/multiplatformImplied/js.kt new file mode 100644 index 00000000..dd2de5bc --- /dev/null +++ b/core/testdata/format/multiplatformImplied/js.kt @@ -0,0 +1,16 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + fun bothJvmAndJs() { + } + + fun js() { + } + + val propJvmAndJs = 0 + + val propJs = "abc" +} diff --git a/core/testdata/format/multiplatformImplied/jvm.kt b/core/testdata/format/multiplatformImplied/jvm.kt new file mode 100644 index 00000000..8d73ce25 --- /dev/null +++ b/core/testdata/format/multiplatformImplied/jvm.kt @@ -0,0 +1,16 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + fun bothJvmAndJs() { + } + + fun jvm() { + } + + val propJvmAndJs = 0 + + val propJvm = "abc" +} diff --git a/core/testdata/format/multiplatformMerge/js.kt b/core/testdata/format/multiplatformMerge/js.kt new file mode 100644 index 00000000..bbf1dd7c --- /dev/null +++ b/core/testdata/format/multiplatformMerge/js.kt @@ -0,0 +1,7 @@ +package foo + +/** + * This is a foo. + */ +class Foo { +} diff --git a/core/testdata/format/multiplatformMerge/jvm.kt b/core/testdata/format/multiplatformMerge/jvm.kt new file mode 100644 index 00000000..cb77273f --- /dev/null +++ b/core/testdata/format/multiplatformMerge/jvm.kt @@ -0,0 +1,8 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + +} diff --git a/core/testdata/format/multiplatformMerge/multiplatform.package.md b/core/testdata/format/multiplatformMerge/multiplatform.package.md new file mode 100644 index 00000000..a423f603 --- /dev/null +++ b/core/testdata/format/multiplatformMerge/multiplatform.package.md @@ -0,0 +1,8 @@ +[test](test/index) / [foo](test/foo/index) + +## Package foo + +### Types + +| [Foo](test/foo/-foo/index)<br>(JVM, JS) | `class Foo`<br>This is a foo. | + diff --git a/core/testdata/format/multiplatformMergeMembers/foo.md b/core/testdata/format/multiplatformMergeMembers/foo.md new file mode 100644 index 00000000..7490c878 --- /dev/null +++ b/core/testdata/format/multiplatformMergeMembers/foo.md @@ -0,0 +1,26 @@ +[test](test/index) / [foo](test/foo/index) / [Foo](test/foo/-foo/index) + +# Foo + +`class Foo` + +**Platform and version requirements:** JVM, JS + +This is a foo. + +### Constructors + +| [<init>](test/foo/-foo/-init-) | `Foo()`<br>This is a foo. | + +### Properties + +| [propJs](test/foo/-foo/prop-js)<br>(JS) | `val propJs: String` | +| [propJvm](test/foo/-foo/prop-jvm)<br>(JVM) | `val propJvm: String` | +| [propJvmAndJs](test/foo/-foo/prop-jvm-and-js) | `val propJvmAndJs: Int` | + +### Functions + +| [bothJvmAndJs](test/foo/-foo/both-jvm-and-js) | `fun bothJvmAndJs(): Unit` | +| [js](test/foo/-foo/js)<br>(JS) | `fun js(): Unit` | +| [jvm](test/foo/-foo/jvm)<br>(JVM) | `fun jvm(): Unit` | + diff --git a/core/testdata/format/multiplatformMergeMembers/js.kt b/core/testdata/format/multiplatformMergeMembers/js.kt new file mode 100644 index 00000000..dd2de5bc --- /dev/null +++ b/core/testdata/format/multiplatformMergeMembers/js.kt @@ -0,0 +1,16 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + fun bothJvmAndJs() { + } + + fun js() { + } + + val propJvmAndJs = 0 + + val propJs = "abc" +} diff --git a/core/testdata/format/multiplatformMergeMembers/jvm.kt b/core/testdata/format/multiplatformMergeMembers/jvm.kt new file mode 100644 index 00000000..8d73ce25 --- /dev/null +++ b/core/testdata/format/multiplatformMergeMembers/jvm.kt @@ -0,0 +1,16 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + fun bothJvmAndJs() { + } + + fun jvm() { + } + + val propJvmAndJs = 0 + + val propJvm = "abc" +} diff --git a/core/testdata/format/multiplatformOmitRedundant/foo.md b/core/testdata/format/multiplatformOmitRedundant/foo.md new file mode 100644 index 00000000..088ced2c --- /dev/null +++ b/core/testdata/format/multiplatformOmitRedundant/foo.md @@ -0,0 +1,22 @@ +[test](test/index) / [foo](test/foo/index) / [Foo](test/foo/-foo/index) + +# Foo + +`class Foo` + +**Platform and version requirements:** JVM + +This is a foo. + +### Constructors + +| [<init>](test/foo/-foo/-init-) | `Foo()`<br>This is a foo. | + +### Properties + +| [propJvm](test/foo/-foo/prop-jvm) | `val propJvm: String` | + +### Functions + +| [jvm](test/foo/-foo/jvm) | `fun jvm(): Unit` | + diff --git a/core/testdata/format/multiplatformOmitRedundant/js.kt b/core/testdata/format/multiplatformOmitRedundant/js.kt new file mode 100644 index 00000000..d1b1429c --- /dev/null +++ b/core/testdata/format/multiplatformOmitRedundant/js.kt @@ -0,0 +1,2 @@ +package foo + diff --git a/core/testdata/format/multiplatformOmitRedundant/jvm.kt b/core/testdata/format/multiplatformOmitRedundant/jvm.kt new file mode 100644 index 00000000..35e3c08d --- /dev/null +++ b/core/testdata/format/multiplatformOmitRedundant/jvm.kt @@ -0,0 +1,11 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + fun jvm() { + } + + val propJvm = "abc" +} diff --git a/core/testdata/format/sinceKotlin.html b/core/testdata/format/sinceKotlin.html new file mode 100644 index 00000000..eef5ca66 --- /dev/null +++ b/core/testdata/format/sinceKotlin.html @@ -0,0 +1,27 @@ +<HTML> +<HEAD> + <meta charset="UTF-8"> + <title>Since1.1 - test</title> +</HEAD> +<BODY> +<a href="test/index">test</a> / <a href="test/-since1.1/index">Since1.1</a><br/> +<br/> +<h1>Since1.1</h1> +<code><span class="keyword">class </span><span class="identifier">Since1.1</span></code> +<p><strong>Platform and version requirements:</strong> Kotlin 1.1</p> +<p>Useful</p> +<h3>Constructors</h3> +<table> + <tbody> + <tr> + <td> + <a href="test/-since1.1/-init-"><init></a></td> + <td> + <code><span class="identifier">Since1.1</span><span class="symbol">(</span><span class="symbol">)</span></code> + <p>Useful</p> + </td> + </tr> + </tbody> +</table> +</BODY> +</HTML> diff --git a/core/testdata/format/sinceKotlin.kt b/core/testdata/format/sinceKotlin.kt new file mode 100644 index 00000000..1025cf0d --- /dev/null +++ b/core/testdata/format/sinceKotlin.kt @@ -0,0 +1,5 @@ +/** + * Useful + */ +@SinceKotlin("1.1") +class `Since1.1`
\ No newline at end of file diff --git a/core/testdata/format/sinceKotlin.md b/core/testdata/format/sinceKotlin.md new file mode 100644 index 00000000..a1abe5fd --- /dev/null +++ b/core/testdata/format/sinceKotlin.md @@ -0,0 +1,14 @@ +[test](test/index) / [Since1.1](test/-since1.1/index) + +# Since1.1 + +`class Since1.1` + +**Platform and version requirements:** Kotlin 1.1 + +Useful + +### Constructors + +| [<init>](test/-since1.1/-init-) | `Since1.1()`<br>Useful | + diff --git a/core/testdata/format/sinceKotlin.package.md b/core/testdata/format/sinceKotlin.package.md new file mode 100644 index 00000000..2af0c9ee --- /dev/null +++ b/core/testdata/format/sinceKotlin.package.md @@ -0,0 +1,8 @@ +[test](test/index) + +## Package <root> + +### Types + +| [Since1.1](test/-since1.1/index)<br>(Kotlin 1.1) | `class Since1.1`<br>Useful | + diff --git a/core/testdata/format/website-samples/dropImport.md b/core/testdata/format/website-samples/dropImport.md index 13c8fb79..1e05678b 100644 --- a/core/testdata/format/website-samples/dropImport.md +++ b/core/testdata/format/website-samples/dropImport.md @@ -12,6 +12,7 @@ layout: api ``` kotlin import some.* + fun main(args: Array<String>) { //sampleStart diff --git a/core/testdata/format/website-samples/newLinesInSamples.kt b/core/testdata/format/website-samples/newLinesInSamples.kt new file mode 100644 index 00000000..ee49aefc --- /dev/null +++ b/core/testdata/format/website-samples/newLinesInSamples.kt @@ -0,0 +1,19 @@ +fun groupBySample() { + val words = listOf("a", "abc", "ab", "def", "abcd") + val byLength = words.groupBy { it.length } + + assertPrints(byLength.keys, "[1, 3, 2, 4]") + assertPrints(byLength.values, "[[a], [abc, def], [ab], [abcd]]") + + val mutableByLength: MutableMap<Int, MutableList<String>> = words.groupByTo(mutableMapOf()) { it.length } + // same content as in byLength map, but the map is mutable + assertTrue(mutableByLength == byLength) +} + + +/** + * @sample groupBySample + */ +fun foo() { + +}
\ No newline at end of file diff --git a/core/testdata/format/website-samples/newLinesInSamples.md b/core/testdata/format/website-samples/newLinesInSamples.md new file mode 100644 index 00000000..5344b983 --- /dev/null +++ b/core/testdata/format/website-samples/newLinesInSamples.md @@ -0,0 +1,31 @@ +--- +title: foo - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo">foo</a></div> + +# foo + +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> +<div class="sample" markdown="1"> + +``` kotlin + + +fun main(args: Array<String>) { +//sampleStart +val words = listOf("a", "abc", "ab", "def", "abcd") +val byLength = words.groupBy { it.length } + +println(byLength.keys) // [1, 3, 2, 4] +println(byLength.values) // [[a], [abc, def], [ab], [abcd]] + +val mutableByLength: MutableMap<Int, MutableList<String>> = words.groupByTo(mutableMapOf()) { it.length } +// same content as in byLength map, but the map is mutable +println("mutableByLength == byLength is ${mutableByLength == byLength}") // true +//sampleEnd +} +``` + +</div> diff --git a/core/testdata/format/website-samples/sample.md b/core/testdata/format/website-samples/sample.md index 203f1b02..b29075a7 100644 --- a/core/testdata/format/website-samples/sample.md +++ b/core/testdata/format/website-samples/sample.md @@ -18,6 +18,7 @@ applied to each element and returns a map where each group key is associated wit ``` kotlin + fun main(args: Array<String>) { //sampleStart if (true) { diff --git a/core/testdata/format/website-samples/sampleWithAsserts.md b/core/testdata/format/website-samples/sampleWithAsserts.md index 98d7df33..c114468a 100644 --- a/core/testdata/format/website-samples/sampleWithAsserts.md +++ b/core/testdata/format/website-samples/sampleWithAsserts.md @@ -12,6 +12,7 @@ layout: api ``` kotlin + fun main(args: Array<String>) { //sampleStart println(a()) // Hello, Work diff --git a/core/testdata/functions/sinceKotlin.kt b/core/testdata/functions/sinceKotlin.kt new file mode 100644 index 00000000..cdcd3357 --- /dev/null +++ b/core/testdata/functions/sinceKotlin.kt @@ -0,0 +1,5 @@ +/** + * Quite useful [String] + */ +@SinceKotlin("1.1") +fun `availableSince1.1`(): String = "1.1 rulezz"
\ No newline at end of file diff --git a/core/testdata/properties/sinceKotlin.kt b/core/testdata/properties/sinceKotlin.kt new file mode 100644 index 00000000..e96f2349 --- /dev/null +++ b/core/testdata/properties/sinceKotlin.kt @@ -0,0 +1,5 @@ +/** + * Quite useful [String] + */ +@SinceKotlin("1.1") +val `availableSince1.1`: String = "1.1 rulezz"
\ No newline at end of file diff --git a/core/testdata/typealias/sinceKotlin.kt b/core/testdata/typealias/sinceKotlin.kt new file mode 100644 index 00000000..5b76f63a --- /dev/null +++ b/core/testdata/typealias/sinceKotlin.kt @@ -0,0 +1,5 @@ +/** + * Documentation + */ +@SinceKotlin("1.1") +typealias `Since 1.1` = String
\ No newline at end of file diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt b/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt index 50e8fb8d..845f86a6 100644 --- a/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt +++ b/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt @@ -13,6 +13,7 @@ interface DokkaBootstrap { outputDir: String, format: String, includeNonPublic: Boolean, + includeRootPackage: Boolean, reportUndocumented: Boolean, skipEmptyPackages: Boolean, skipDeprecated: Boolean, diff --git a/runners/ant/src/main/kotlin/ant/dokka.kt b/runners/ant/src/main/kotlin/ant/dokka.kt index 38dc543b..e9cb35a2 100644 --- a/runners/ant/src/main/kotlin/ant/dokka.kt +++ b/runners/ant/src/main/kotlin/ant/dokka.kt @@ -5,10 +5,7 @@ import org.apache.tools.ant.Project import org.apache.tools.ant.Task import org.apache.tools.ant.types.Path import org.apache.tools.ant.types.Reference -import org.jetbrains.dokka.DocumentationOptions -import org.jetbrains.dokka.DokkaGenerator -import org.jetbrains.dokka.DokkaLogger -import org.jetbrains.dokka.SourceLinkDefinition +import org.jetbrains.dokka.* import java.io.File class AntLogger(val task: Task): DokkaLogger { @@ -19,10 +16,17 @@ class AntLogger(val task: Task): DokkaLogger { class AntSourceLinkDefinition(var path: String? = null, var url: String? = null, var lineSuffix: String? = null) -class DokkaAntTask(): Task() { +class AntSourceRoot(var path: String? = null, var platforms: String? = null) + +fun AntSourceRoot.toSourceRoot(): SourceRoot? = path?.let { + path -> SourceRoot(path, platforms?.split(',').orEmpty()) +} + +class DokkaAntTask: Task() { var moduleName: String? = null var outputDir: String? = null var outputFormat: String = "html" + var impliedPlatforms: String = "" var jdkVersion: Int = 6 var skipDeprecated: Boolean = false @@ -33,6 +37,7 @@ class DokkaAntTask(): Task() { val includesPath: Path by lazy { Path(getProject()) } val antSourceLinks: MutableList<AntSourceLinkDefinition> = arrayListOf() + val antSourceRoots: MutableList<AntSourceRoot> = arrayListOf() fun setClasspath(classpath: Path) { compileClasspath.append(classpath) @@ -68,8 +73,10 @@ class DokkaAntTask(): Task() { return def } + fun createSourceRoot(): AntSourceRoot = AntSourceRoot().apply { antSourceRoots.add(this) } + override fun execute() { - if (sourcePath.list().size == 0) { + if (sourcePath.list().isEmpty() && antSourceRoots.isEmpty()) { throw BuildException("At least one source path needs to be specified") } if (moduleName == null) { @@ -87,14 +94,15 @@ class DokkaAntTask(): Task() { val generator = DokkaGenerator( AntLogger(this), compileClasspath.list().toList(), - sourcePath.list().toList(), + sourcePath.list().map { SourceRoot(it) } + antSourceRoots.mapNotNull { it.toSourceRoot() }, samplesPath.list().toList(), includesPath.list().toList(), moduleName!!, DocumentationOptions(outputDir!!, outputFormat, skipDeprecated = skipDeprecated, sourceLinks = sourceLinks, - jdkVersion = jdkVersion) + jdkVersion = jdkVersion, + impliedPlatforms = impliedPlatforms.split(',')) ) generator.generate() } diff --git a/runners/cli/src/main/kotlin/cli/main.kt b/runners/cli/src/main/kotlin/cli/main.kt index afc67e45..58bca522 100644 --- a/runners/cli/src/main/kotlin/cli/main.kt +++ b/runners/cli/src/main/kotlin/cli/main.kt @@ -44,6 +44,9 @@ class DokkaArguments { @set:Argument(value = "jdkVersion", description = "Version of JDK to use for linking to JDK JavaDoc") var jdkVersion: Int = 6 + + @set:Argument(value = "impliedPlatforms", description = "List of implied platforms (comma-separated)") + var impliedPlatforms: String = "" } @@ -72,13 +75,14 @@ object MainKt { arguments.outputDir.let { if (it.endsWith('/')) it else it + '/' }, arguments.outputFormat, skipDeprecated = arguments.nodeprecated, - sourceLinks = sourceLinks + sourceLinks = sourceLinks, + impliedPlatforms = arguments.impliedPlatforms.split(',') ) val generator = DokkaGenerator( DokkaConsoleLogger, classPath, - sources, + sources.map(::parseSourceRoot), samples, includes, arguments.moduleName, diff --git a/runners/gradle-plugin/src/main/kotlin/main.kt b/runners/gradle-plugin/src/main/kotlin/main.kt index 7ed83473..3a78080c 100644 --- a/runners/gradle-plugin/src/main/kotlin/main.kt +++ b/runners/gradle-plugin/src/main/kotlin/main.kt @@ -159,6 +159,7 @@ open class DokkaTask : DefaultTask() { outputDirectory, outputFormat, false, + false, reportNotDocumented, skipEmptyPackages, skipDeprecated, diff --git a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt index 899d2dde..c3cf7509 100644 --- a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt +++ b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt @@ -7,11 +7,12 @@ import org.apache.maven.plugin.AbstractMojo import org.apache.maven.plugins.annotations.* import org.apache.maven.project.MavenProject import org.apache.maven.project.MavenProjectHelper -import org.jetbrains.dokka.DokkaGenerator -import org.jetbrains.dokka.SourceLinkDefinition -import org.jetbrains.dokka.DocumentationOptions import org.codehaus.plexus.archiver.Archiver import org.codehaus.plexus.archiver.jar.JarArchiver +import org.jetbrains.dokka.DocumentationOptions +import org.jetbrains.dokka.DokkaGenerator +import org.jetbrains.dokka.SourceLinkDefinition +import org.jetbrains.dokka.SourceRoot import java.io.File class SourceLinkMapItem { @@ -66,7 +67,7 @@ abstract class AbstractDokkaMojo : AbstractMojo() { val gen = DokkaGenerator( MavenDokkaLogger(log), classpath, - sourceDirectories, + sourceDirectories.map { SourceRoot(it) }, samplesDirs, includeDirs + includes, moduleName, |