diff options
Diffstat (limited to 'core')
105 files changed, 1761 insertions, 279 deletions
diff --git a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt index 3f7f616f..15d0a26b 100644 --- a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt +++ b/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.CompilerEnvironment +import org.jetbrains.kotlin.resolve.MultiTargetPlatform import org.jetbrains.kotlin.resolve.jvm.JvmAnalyzerFacade import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM @@ -133,7 +134,8 @@ class AnalysisEnvironment(val messageCollector: MessageCollector) : Disposable { info, content -> JvmPackagePartProvider(environment, content.moduleContentScope) }, - builtIns = builtIns + builtIns = { builtIns }, + modulePlatforms = { MultiTargetPlatform.Specific("JVM") } ) resolverForProject.resolverForModule(library) // Required before module to initialize library properly 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..eac053aa 100644 --- a/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt +++ b/core/src/main/kotlin/Formats/KotlinWebsiteFormatService.kt @@ -1,14 +1,18 @@ package org.jetbrains.dokka import com.google.inject.Inject +import com.google.inject.name.Named +import org.jetbrains.dokka.Utilities.impliedPlatformsName +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty 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 @@ -30,8 +34,8 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder, override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body) - protected fun div(to: StringBuilder, cssClass: String, markdown: Boolean = false, block: () -> Unit) { - to.append("<div class=\"$cssClass\"") + protected fun div(to: StringBuilder, cssClass: String, otherAttributes: String = "", markdown: Boolean = false, block: () -> Unit) { + to.append("<div class=\"$cssClass\"$otherAttributes") if (markdown) to.append(" markdown=\"1\"") to.append(">") if (!markdown) insideDiv++ @@ -53,8 +57,8 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder, } } - override fun appendAsOverloadGroup(to: StringBuilder, block: () -> Unit) { - div(to, "overload-group", true) { + override fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) { + div(to, "overload-group", calculateDataAttributes(platforms), true) { ensureParagraph() block() ensureParagraph() @@ -142,14 +146,39 @@ open class KotlinWebsiteOutputBuilder(to: StringBuilder, IdentifierKind.SummarizedTypeName -> "summarizedTypeName" else -> "identifier" } + + fun calculateDataAttributes(platforms: Set<String>): String { + fun String.isKotlinVersion() = this.startsWith("Kotlin") + fun String.isJREVersion() = this.startsWith("JRE") + val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion) + val jreVersion = platforms.singleOrNull(String::isJREVersion) + val targetPlatforms = platforms.filterNot { it.isKotlinVersion() || it.isJREVersion() } + + val kotlinVersionAttr = kotlinVersion?.let { " data-kotlin-version=\"$it\"" } ?: "" + val jreVersionAttr = jreVersion?.let { " data-jre-version=\"$it\"" } ?: "" + val platformsAttr = targetPlatforms.ifNotEmpty { " data-platform=\"${targetPlatforms.joinToString()}\"" } ?: "" + return "$platformsAttr$kotlinVersionAttr$jreVersionAttr" + } + + override fun appendIndexRow(platforms: Set<String>, block: () -> Unit) { + if (platforms.isNotEmpty()) + wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block) + else + appendTableRow(block) + } + + override fun appendPlatforms(platforms: Set<String>) { + + } } 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 +186,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) { + div(to, "sample", markdown = 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 +203,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 7896bcd8..84f91d9c 100644 --- a/core/src/main/kotlin/Formats/StructuredFormatService.kt +++ b/core/src/main/kotlin/Formats/StructuredFormatService.kt @@ -9,7 +9,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 +60,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) } @@ -144,12 +152,9 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, is ContentBlockSampleCode, is ContentBlockCode -> { content as ContentBlockCode fun ContentBlockCode.appendBlockCodeContent() { - for ((index, contentNode) in this.children.withIndex()) { - appendContent(contentNode) - if (index < this.children.size - 1) { - to.append("\n") - } - } + children + .dropWhile { it is ContentText && it.text.isBlank() } + .forEach { appendContent(it) } } when (content) { is ContentBlockSampleCode -> @@ -179,6 +184,12 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, to: DocumentationNode, extension: String, name: (DocumentationNode) -> String = DocumentationNode::name): FormatLink { + if (to.owner?.kind == NodeKind.GroupNode) + return link(from, to.owner!!, extension, name) + + if (from.owner?.kind == NodeKind.GroupNode) + return link(from.owner!!, to, extension, name) + return FormatLink(name(to), locationService.relativePathToLocation(from, to)) } @@ -198,15 +209,25 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, block() } - protected open fun appendAsOverloadGroup(to: StringBuilder, block: () -> Unit) { + protected open fun appendAsOverloadGroup(to: StringBuilder, platforms: Set<String>, block: () -> Unit) { block() } + protected open fun appendIndexRow(platforms: Set<String>, block: () -> Unit) { + appendTableRow(block) + } + + protected open fun appendPlatforms(platforms: Set<String>) { + if (platforms.isNotEmpty()) { + appendLine() + appendText(platforms.joinToString(prefix = "(", postfix = ")")) + } + } + protected open fun appendBreadcrumbs(path: Iterable<FormatLink>) { for ((index, item) in path.withIndex()) { if (index > 0) { appendBreadcrumbSeparator() - } appendLink(item) } @@ -225,16 +246,18 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, appendContent(signatureAsCode) } - open inner class PageBuilder(val nodes: Iterable<DocumentationNode>) { + open inner class PageBuilder(val nodes: Iterable<DocumentationNode>, val noHeader: Boolean = false) { open fun build() { val breakdownByLocation = nodes.groupBy { node -> - node.path.filterNot { it.name.isEmpty() }.map { link(node, it) } + node.path.filterNot { it.name.isEmpty() }.map { link(node, it) }.distinct() } for ((path, nodes) in breakdownByLocation) { - appendBreadcrumbs(path) - appendLine() - appendLine() + if (!noHeader) { + appendBreadcrumbs(path) + appendLine() + appendLine() + } appendLocation(nodes.filter { it.kind != NodeKind.ExternalClass }) } } @@ -246,11 +269,13 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, val packageName = if (singleNode.name.isEmpty()) "<root>" else singleNode.name appendHeader(2) { appendText("Package $packageName") } } + singleNode.appendPlatforms() appendContent(singleNode.content) } else { val breakdownByName = nodes.groupBy { node -> node.name } for ((name, items) in breakdownByName) { - appendHeader { appendText(name) } + if (!noHeader) + appendHeader { appendText(name) } appendDocumentation(items) } } @@ -262,9 +287,9 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, if (breakdownBySummary.size == 1) { formatOverloadGroup(breakdownBySummary.values.single()) } else { - for ((summary, items) in breakdownBySummary) { + for ((_, items) in breakdownBySummary) { ensureParagraph() - appendAsOverloadGroup(to) { + appendAsOverloadGroup(to, platformsOfItems(items)) { formatOverloadGroup(items) } } @@ -284,12 +309,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() } @@ -334,6 +361,37 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } + private fun DocumentationNode.appendPlatforms() { + val platforms = if (isModuleOrPackage()) + platformsToShow.toSet() + platformsOfItems(members) + else + platformsToShow + + if(platforms.isEmpty()) return + + appendParagraph { + appendStrong { to.append("Platform and version requirements:") } + to.append(" " + platforms.joinToString()) + } + } + + protected fun platformsOfItems(items: List<DocumentationNode>): Set<String> { + val platforms = items.asSequence().map { + when (it.kind) { + NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module, NodeKind.GroupNode -> platformsOfItems(it.members) + else -> it.platformsToShow.toSet() + } + } + // Calculating common platforms for items + return platforms.fold(platforms.first()) { + result, platforms -> + result.intersect(platforms) + } + } + + 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) @@ -367,8 +425,38 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } - inner class SingleNodePageBuilder(val node: DocumentationNode) - : PageBuilder(listOf(node)) { + inner class GroupNodePageBuilder(val node: DocumentationNode) : PageBuilder(listOf(node)) { + + override fun build() { + + val breakdownByLocation = node.path.filterNot { it.name.isEmpty() }.map { link(node, it) } + + appendBreadcrumbs(breakdownByLocation) + appendLine() + appendLine() + appendHeader { appendText(node.name) } + + fun DocumentationNode.priority(): Int = when (kind) { + NodeKind.TypeAlias -> 1 + NodeKind.Class -> 2 + else -> 3 + } + + for (member in node.members.sortedBy(DocumentationNode::priority)) { + ensureParagraph() + appendAsOverloadGroup(to, platformsOfItems(listOf(member))) { + formatSubNodeOfGroup(member) + } + } + } + + fun formatSubNodeOfGroup(member: DocumentationNode) { + SingleNodePageBuilder(member, true).build() + } + } + + inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false) + : PageBuilder(listOf(node), noHeader) { override fun build() { super.build() @@ -378,21 +466,29 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, return } - appendSection("Packages", node.members(NodeKind.Package)) - appendSection("Types", node.members.filter { it.kind in NodeKind.classLike && it.kind != NodeKind.TypeAlias && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception }) - appendSection("Annotations", node.members(NodeKind.AnnotationClass)) - appendSection("Exceptions", node.members(NodeKind.Exception)) - appendSection("Type Aliases", node.members(NodeKind.TypeAlias)) + fun DocumentationNode.membersOrGroupMembers(predicate: (DocumentationNode) -> Boolean): List<DocumentationNode> { + return members.filter(predicate) + members(NodeKind.GroupNode).flatMap { it.members.filter(predicate) } + } + + fun DocumentationNode.membersOrGroupMembers(kind: NodeKind): List<DocumentationNode> { + return membersOrGroupMembers { it.kind == kind } + } + + appendSection("Packages", node.members(NodeKind.Package), platformsBasedOnMembers = true) + appendSection("Types", node.membersOrGroupMembers { it.kind in NodeKind.classLike && it.kind != NodeKind.TypeAlias && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception }) + appendSection("Annotations", node.membersOrGroupMembers(NodeKind.AnnotationClass)) + appendSection("Exceptions", node.membersOrGroupMembers(NodeKind.Exception)) + appendSection("Type Aliases", node.membersOrGroupMembers(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( @@ -411,7 +507,8 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, NodeKind.CompanionObjectFunction, NodeKind.ExternalClass, NodeKind.EnumItem, - NodeKind.AllTypes + NodeKind.AllTypes, + NodeKind.GroupNode ) }) @@ -431,7 +528,10 @@ 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, + platformsBasedOnMembers: Boolean = false) { if (members.isEmpty()) return appendHeader(3) { appendText(caption) } @@ -439,12 +539,22 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, val children = if (sortMembers) members.sortedBy { it.name } else members val membersMap = children.groupBy { link(node, it) } + + appendTable("Name", "Summary") { - appendTableBody() { + appendTableBody { for ((memberLocation, members) in membersMap) { - appendTableRow() { + val elementPlatforms = platformsOfItems(members, omitSamePlatforms) + val platforms = if (platformsBasedOnMembers) + members.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms + else + elementPlatforms + appendIndexRow(platforms) { appendTableCell { appendLink(memberLocation) + if (members.singleOrNull()?.kind != NodeKind.ExternalClass) { + appendPlatforms(platforms) + } } appendTableCell { val breakdownBySummary = members.groupBy { it.summary } @@ -459,6 +569,14 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, } } + private fun platformsOfItems(items: List<DocumentationNode>, omitSamePlatforms: Boolean = true): Set<String> { + val platforms = platformsOfItems(items) + if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) { + return platforms + } + return emptySet() + } + private fun appendSummarySignatures(items: List<DocumentationNode>) { val summarySignature = languageService.summarizeSignatures(items) if (summarySignature != null) { @@ -488,9 +606,9 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, appendHeader(3) { to.append("All Types") } appendTable("Name", "Summary") { - appendTableBody() { + appendTableBody { for (type in node.members) { - appendTableRow() { + appendTableRow { appendTableCell { appendLink(link(node, type) { if (it.kind == NodeKind.ExternalClass) it.name else it.qualifiedName() @@ -514,14 +632,11 @@ abstract class StructuredOutputBuilder(val to: StringBuilder, override fun appendNodes(nodes: Iterable<DocumentationNode>) { val singleNode = nodes.singleOrNull() - if (singleNode != null) { - if (singleNode.kind == NodeKind.AllTypes) { - AllTypesNodeBuilder(singleNode).build() - } else { - SingleNodePageBuilder(singleNode).build() - } - } else { - PageBuilder(nodes).build() + when (singleNode?.kind) { + NodeKind.AllTypes -> AllTypesNodeBuilder(singleNode).build() + NodeKind.GroupNode -> GroupNodePageBuilder(singleNode).build() + null -> PageBuilder(nodes).build() + else -> SingleNodePageBuilder(singleNode).build() } } } 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/ContentBuilder.kt b/core/src/main/kotlin/Kotlin/ContentBuilder.kt index 53afebaf..9c081d17 100644 --- a/core/src/main/kotlin/Kotlin/ContentBuilder.kt +++ b/core/src/main/kotlin/Kotlin/ContentBuilder.kt @@ -148,7 +148,7 @@ fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: (Stri private fun MarkdownNode.getLabelText() = children.filter { it.type == MarkdownTokenTypes.TEXT || it.type == MarkdownTokenTypes.EMPH }.joinToString("") { it.text } -private fun keepWhitespace(node: ContentNode) = node is ContentParagraph || node is ContentSection +private fun keepWhitespace(node: ContentNode) = node is ContentParagraph || node is ContentSection || node is ContentBlockCode fun buildInlineContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: (String) -> ContentBlock) { val inlineContent = tree.children.singleOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree) 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 14d04397..6d7ff7ba 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..299ad477 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,35 @@ 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) { + val kotlinVersion = annotation + .detail(NodeKind.Parameter) + .detail(NodeKind.Value) + .name.removeSurrounding("\"") + + 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 +251,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 +270,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 +278,82 @@ 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 createGroupNode(signature: String, nodes: List<DocumentationNode>) = (nodes.find { it.kind == NodeKind.GroupNode } ?: + DocumentationNode(nodes.first().name, Content.Empty, NodeKind.GroupNode).apply { + appendTextNode(signature, NodeKind.Signature, RefKind.Detail) + }) + .also { groupNode -> + nodes.forEach { node -> + if (node != groupNode) { + node.owner?.let { owner -> + node.dropReferences { it.to == owner && it.kind == RefKind.Owner } + owner.dropReferences { it.to == node && it.kind == RefKind.Member } + owner.append(groupNode, RefKind.Member) + } + groupNode.append(node, RefKind.Member) + } } - null - } else { - val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original - appendChild(descriptorToUse, RefKind.Member) + } + + + fun DocumentationNode.appendOrUpdateMember(descriptor: DeclarationDescriptor) { + if (descriptor.isGenerated() || !descriptor.isDocumented(options)) return + + val existingNode = refGraph.lookup(descriptor.signature()) + if (existingNode != null) { + if (existingNode.kind == NodeKind.TypeAlias && descriptor is ClassDescriptor + || existingNode.kind == NodeKind.Class && descriptor is TypeAliasDescriptor) { + val node = createGroupNode(descriptor.signature(), listOf(existingNode, descriptor.build())) + register(descriptor, node) + return + } + + 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) + } + } + } + } + } + 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) } } - return nodes.filterNotNull() } fun DocumentationNode.appendInPageChildren(descriptors: Iterable<DeclarationDescriptor>, kind: RefKind) { @@ -272,6 +369,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 +380,6 @@ class DocumentationBuilder } propagateExtensionFunctionsToSubclasses(fragments) - if (options.generateIndexPages) { - generateAllTypesNode() - } } private fun propagateExtensionFunctionsToSubclasses(fragments: Collection<PackageFragmentDescriptor>) { @@ -297,13 +392,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 +454,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 +476,7 @@ class DocumentationBuilder node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType) node.appendSourceLink(source) + node.appendDefaultPlatforms(this) register(this, node) return node @@ -413,28 +498,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 +554,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 +583,7 @@ class DocumentationBuilder node.appendModifiers(this) node.appendSourceLink(source) node.appendSignature(this) + node.appendDefaultPlatforms(this) overriddenDescriptors.forEach { addOverrideLink(it, this) @@ -523,6 +630,7 @@ class DocumentationBuilder overriddenDescriptors.forEach { addOverrideLink(it, this) } + node.appendDefaultPlatforms(this) register(this, node) return node @@ -596,9 +704,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 +755,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 +768,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 +930,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 75a8a948..f33c8c96 100644 --- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt +++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt @@ -151,26 +151,47 @@ class KotlinLanguageService : LanguageService { } } - private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) { + private fun ContentBlock.renderFunctionalType(node: DocumentationNode, renderMode: RenderMode) { var typeArguments = node.details(NodeKind.Type) - if (node.name == "Function${typeArguments.count() - 1}") { - // lambda - val isExtension = node.annotations.any { it.name == "ExtensionFunctionType" } - if (isExtension) { - renderType(typeArguments.first(), renderMode) - symbol(".") - typeArguments = typeArguments.drop(1) - } - symbol("(") - renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) { - renderFunctionalTypeParameterName(it, renderMode) - renderType(it, renderMode) - } - symbol(")") - nbsp() - symbol("->") - nbsp() - renderType(typeArguments.last(), renderMode) + + if (node.name.startsWith("Suspend")) { + keyword("suspend ") + } + + // lambda + val isExtension = node.annotations.any { it.name == "ExtensionFunctionType" } + if (isExtension) { + renderType(typeArguments.first(), renderMode) + symbol(".") + typeArguments = typeArguments.drop(1) + } + symbol("(") + renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) { + renderFunctionalTypeParameterName(it, renderMode) + renderType(it, renderMode) + } + symbol(")") + nbsp() + symbol("->") + nbsp() + renderType(typeArguments.last(), renderMode) + + } + + private fun DocumentationNode.isFunctionalType(): Boolean { + val typeArguments = details(NodeKind.Type) + val functionalTypeName = "Function${typeArguments.count() - 1}" + val suspendFunctionalTypeName = "Suspend$functionalTypeName" + return name == functionalTypeName || name == suspendFunctionalTypeName + } + + private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) { + if (node.name == "dynamic") { + keyword("dynamic") + return + } + if (node.isFunctionalType()) { + renderFunctionalType(node, renderMode) return } if (renderMode == RenderMode.FULL) { @@ -178,7 +199,8 @@ class KotlinLanguageService : LanguageService { } renderModifiersForNode(node, renderMode, true) renderLinked(node) { identifier(it.name, IdentifierKind.TypeName) } - if (typeArguments.any()) { + val typeArguments = node.details(NodeKind.Type) + if (typeArguments.isNotEmpty()) { symbol("<") renderList(typeArguments, noWrap = true) { renderType(it, renderMode) @@ -392,7 +414,15 @@ class KotlinLanguageService : LanguageService { if (signatureMapper != null) { signatureMapper.renderReceiver(receiver, this) } else { - renderType(receiver.detail(NodeKind.Type), renderMode) + val type = receiver.detail(NodeKind.Type) + + if (type.isFunctionalType()) { + symbol("(") + renderFunctionalType(type, renderMode) + symbol(")") + } else { + renderType(type, renderMode) + } } symbol(".") } diff --git a/core/src/main/kotlin/Markdown/MarkdownProcessor.kt b/core/src/main/kotlin/Markdown/MarkdownProcessor.kt index 46b72c03..d1d40dd4 100644 --- a/core/src/main/kotlin/Markdown/MarkdownProcessor.kt +++ b/core/src/main/kotlin/Markdown/MarkdownProcessor.kt @@ -4,13 +4,14 @@ import org.intellij.markdown.IElementType import org.intellij.markdown.MarkdownElementTypes import org.intellij.markdown.ast.ASTNode import org.intellij.markdown.ast.LeafASTNode +import org.intellij.markdown.ast.getTextInNode import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor import org.intellij.markdown.parser.MarkdownParser class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val markdown: String) { val children: List<MarkdownNode> = node.children.map { MarkdownNode(it, this, markdown) } val type: IElementType get() = node.type - val text: String get() = markdown.substring(node.startOffset, node.endOffset) + val text: String get() = node.getTextInNode(markdown).toString() fun child(type: IElementType): MarkdownNode? = children.firstOrNull { it.type == type } override fun toString(): String = StringBuilder().apply { presentTo(this) }.toString() @@ -30,6 +31,7 @@ fun MarkdownNode.toTestString(): String { visit { node, visitChildren -> sb.append(" ".repeat(level * 2)) node.presentTo(sb) + sb.appendln() level++ visitChildren() level-- @@ -40,7 +42,6 @@ fun MarkdownNode.toTestString(): String { private fun MarkdownNode.presentTo(sb: StringBuilder) { sb.append(type.toString()) sb.append(":" + text.replace("\n", "\u23CE")) - sb.appendln() } fun parseMarkdown(markdown: String): MarkdownNode { diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt index def0f626..c38a6a9f 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, @@ -55,7 +56,9 @@ enum class NodeKind { * A note which is rendered once on a page documenting a group of overloaded functions. * Needs to be generated equally on all overloads. */ - OverloadGroupNote; + OverloadGroupNote, + + GroupNode; companion object { val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object, TypeAlias) @@ -97,12 +100,18 @@ 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) { references.add(DocumentationReference(this, to, kind)) } + fun dropReferences(predicate: (DocumentationReference) -> Boolean) { + references.removeAll(predicate) + } + fun addAllReferencesFrom(other: DocumentationNode) { references.addAll(other.references) } @@ -135,6 +144,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 6348c181..116a5c02 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 baf44904..7ac43184 100644 --- a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt +++ b/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt @@ -2,8 +2,10 @@ 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.psi.psiUtil.allChildren import org.jetbrains.kotlin.resolve.ImportPath open class KotlinWebsiteSampleProcessingService @@ -12,7 +14,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 +50,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) } } @@ -70,8 +69,9 @@ open class KotlinWebsiteSampleProcessingService val psiFile = psiElement.containingFile if (psiFile is KtFile) { return ContentBlockCode("kotlin").apply { + append(ContentText("\n")) psiFile.importList?.let { - it.children.filter { + it.allChildren.filter { it !is KtImportDirective || it.importPath !in importsToIgnore }.forEach { append(ContentText(it.text)) } } 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/main/resources/dokka/styles/style.css b/core/src/main/resources/dokka/styles/style.css index 09586237..914be69d 100644 --- a/core/src/main/resources/dokka/styles/style.css +++ b/core/src/main/resources/dokka/styles/style.css @@ -1,10 +1,13 @@ -@import url(https://fonts.googleapis.com/css?family=Lato:300italic,700italic,300,700); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:300i,400,700); body, table { padding:50px; - font:14px/1.5 Lato, "Helvetica Neue", Helvetica, Arial, sans-serif; + font:14px/1.5 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif; color:#555; font-weight:300; + margin-left: auto; + margin-right: auto; + max-width: 1440px; } .keyword { diff --git a/core/src/test/kotlin/TestAPI.kt b/core/src/test/kotlin/TestAPI.kt index 7197d2c4..19eb29d5 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,18 @@ 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 expectedOutput = File(sourcePath.replaceAfterLast(".", ext, sourcePath + "." + ext)).readText() assertEqualsIgnoringSeparators(expectedOutput, output.toString()) } @@ -158,7 +180,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 4b4eff59..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,14 @@ class HtmlFormatTest { verifyHtmlNode("functionalTypeWithNamedParameters") } + @Test fun sinceKotlin() { + verifyHtmlNode("sinceKotlin") + } + + @Test fun blankLineInsideCodeBlock() { + verifyHtmlNode("blankLineInsideCodeBlock") + } + private fun verifyHtmlNode(fileName: String, withKotlinRuntime: Boolean = false) { verifyHtmlNodes(fileName, withKotlinRuntime) { model -> model.members.single().members } } diff --git a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt index 3363b0a3..9565263f 100644 --- a/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt +++ b/core/src/test/kotlin/format/KotlinWebSiteFormatTest.kt @@ -1,11 +1,11 @@ package org.jetbrains.dokka.tests -import org.jetbrains.dokka.KotlinLanguageService -import org.jetbrains.dokka.KotlinWebsiteFormatService +import org.jetbrains.dokka.* +import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList 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") @@ -19,9 +19,39 @@ class KotlinWebSiteFormatTest { verifyKWSNodeByName("overloadGroup", "magic") } + @Test fun dataTags() { + val module = buildMultiplePlatforms("dataTags") + verifyMultiplatformPackage(module, "dataTags") + } + + @Test fun dataTagsInGroupNode() { + val path = "dataTagsInGroupNode" + val module = buildMultiplePlatforms(path) + verifyModelOutput(module, ".md", "testdata/format/website/$path/multiplatform.kt") { model, output -> + kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.find { it.kind == NodeKind.GroupNode }.singletonOrEmptyList()) + } + verifyMultiplatformPackage(module, path) + } + private fun verifyKWSNodeByName(fileName: String, name: String) { verifyOutput("testdata/format/website/$fileName.kt", ".md", format = "kotlin-website") { model, output -> kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members.filter { it.name == name }) } } + + private fun buildMultiplePlatforms(path: String): DocumentationModule { + val module = DocumentationModule("test") + val options = DocumentationOptions("", "html", generateIndexPages = false) + appendDocumentation(module, contentRootFromPath("testdata/format/website/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), options = options) + appendDocumentation(module, contentRootFromPath("testdata/format/website/$path/jre7.kt"), defaultPlatforms = listOf("JVM", "JRE7"), options = options) + appendDocumentation(module, contentRootFromPath("testdata/format/website/$path/js.kt"), defaultPlatforms = listOf("JS"), options = options) + return module + } + + private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) { + verifyModelOutput(module, ".package.md", "testdata/format/website/$path/multiplatform.kt") { model, output -> + kwsService.createOutputBuilder(output, tempLocation).appendNodes(model.members) + } + } + } diff --git a/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteRunnableSamplesFormatTest.kt index 742d2908..0d586814 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,14 @@ class KotlinWebSiteRunnableSamplesFormatTest { verifyKWSNodeByName("sampleWithAsserts", "a") } + @Test fun newLinesInSamples() { + verifyKWSNodeByName("newLinesInSamples", "foo") + } + + @Test fun newLinesInImportList() { + verifyKWSNodeByName("newLinesInImportList", "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 4f32fa53..21298520 100644 --- a/core/src/test/kotlin/format/MarkdownFormatTest.kt +++ b/core/src/test/kotlin/format/MarkdownFormatTest.kt @@ -1,14 +1,11 @@ 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.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList import org.junit.Test class MarkdownFormatTest { - private val markdownService = MarkdownFormatService(InMemoryLocationService, KotlinLanguageService()) + private val markdownService = MarkdownFormatService(InMemoryLocationService, KotlinLanguageService(), listOf()) @Test fun emptyDescription() { verifyMarkdownNode("emptyDescription") @@ -244,6 +241,124 @@ class MarkdownFormatTest { verifyMarkdownNode("sampleByShortName") } + + @Test fun suspendParam() { + verifyMarkdownNode("suspendParam") + 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 renderFunctionalTypeInParenthesisWhenItIsReceiver() { + verifyMarkdownNode("renderFunctionalTypeInParenthesisWhenItIsReceiver") + } + + @Test fun multiplePlatforms() { + verifyMultiplatformPackage(buildMultiplePlatforms("multiplatform/simple"), "multiplatform/simple") + } + + @Test fun multiplePlatformsMerge() { + verifyMultiplatformPackage(buildMultiplePlatforms("multiplatform/merge"), "multiplatform/merge") + } + + @Test fun multiplePlatformsMergeMembers() { + val module = buildMultiplePlatforms("multiplatform/mergeMembers") + verifyModelOutput(module, ".md", "testdata/format/multiplatform/mergeMembers/foo.kt") { model, output -> + markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members) + } + } + + @Test fun multiplePlatformsOmitRedundant() { + val module = buildMultiplePlatforms("multiplatform/omitRedundant") + verifyModelOutput(module, ".md", "testdata/format/multiplatform/omitRedundant/foo.kt") { model, output -> + markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members) + } + } + + @Test fun multiplePlatformsImplied() { + val module = buildMultiplePlatforms("multiplatform/implied") + verifyModelOutput(module, ".md", "testdata/format/multiplatform/implied/foo.kt") { model, output -> + MarkdownFormatService(InMemoryLocationService, KotlinLanguageService(), listOf("JVM", "JS")) + .createOutputBuilder(output, tempLocation).appendNodes(model.members.single().members) + } + } + + @Test fun packagePlatformsWithExtExtensions() { + val path = "multiplatform/packagePlatformsWithExtExtensions" + val module = DocumentationModule("test") + val options = DocumentationOptions("", "html", generateIndexPages = false) + appendDocumentation(module, contentRootFromPath("testdata/format/$path/jvm.kt"), defaultPlatforms = listOf("JVM"), withKotlinRuntime = true, options = options) + verifyMultiplatformIndex(module, path) + verifyMultiplatformPackage(module, path) + } + + @Test fun multiplePlatformsPackagePlatformFromMembers() { + val path = "multiplatform/packagePlatformsFromMembers" + val module = buildMultiplePlatforms(path) + verifyMultiplatformIndex(module, path) + verifyMultiplatformPackage(module, path) + } + + @Test fun multiplePlatformsGroupNode() { + val path = "multiplatform/groupNode" + val module = buildMultiplePlatforms(path) + verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.kt") { model, output -> + markdownService.createOutputBuilder(output, tempLocation) + .appendNodes(listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode })) + } + verifyMultiplatformPackage(module, path) + } + + @Test fun multiplePlatformsBreadcrumbsInMemberOfMemberOfGroupNode() { + val path = "multiplatform/breadcrumbsInMemberOfMemberOfGroupNode" + val module = buildMultiplePlatforms(path) + verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.kt") { model, output -> + markdownService.createOutputBuilder(output, tempLocation) + .appendNodes(listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }?.member(NodeKind.Class)?.member(NodeKind.Function))) + } + } + + 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) + } + } + + private fun verifyMultiplatformIndex(module: DocumentationModule, path: String) { + verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.index.kt") { + model, output -> + MarkdownFormatService(InMemoryLocationService, KotlinLanguageService(), listOf()) + .createOutputBuilder(output, tempLocation).appendNodes(listOf(model)) + } + } + + @Test fun blankLineInsideCodeBlock() { + verifyMarkdownNode("blankLineInsideCodeBlock") + } + private fun verifyMarkdownPackage(fileName: String, withKotlinRuntime: Boolean = false) { verifyOutput("testdata/format/$fileName.kt", ".package.md", withKotlinRuntime = withKotlinRuntime) { model, output -> markdownService.createOutputBuilder(output, tempLocation).appendNodes(model.members) diff --git a/core/src/test/kotlin/javadoc/JavadocTest.kt b/core/src/test/kotlin/javadoc/JavadocTest.kt index f7f86881..359c5fef 100644 --- a/core/src/test/kotlin/javadoc/JavadocTest.kt +++ b/core/src/test/kotlin/javadoc/JavadocTest.kt @@ -1,7 +1,9 @@ package org.jetbrains.dokka.javadoc +import com.sun.javadoc.Tag import com.sun.javadoc.Type import org.jetbrains.dokka.DokkaConsoleLogger +import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators import org.jetbrains.dokka.tests.verifyModel import org.junit.Assert.* import org.junit.Test @@ -132,6 +134,23 @@ class JavadocTest { } } + @Test + fun testBlankLineInsideCodeBlock() { + verifyJavadoc("testdata/javadoc/blankLineInsideCodeBlock.kt", withKotlinRuntime = true) { doc -> + val method = doc.classNamed("BlankLineInsideCodeBlockKt")!!.methods()[0] + val text = method.inlineTags().joinToString(separator = "", transform = Tag::text) + assertEqualsIgnoringSeparators(""" + <p><code><pre> + This is a test + of Dokka's code blocks. + Here is a blank line. + + The previous line was blank. + </pre></code></p> + """.trimIndent(), text) + } + } + private fun verifyJavadoc(name: String, withJdk: Boolean = false, withKotlinRuntime: Boolean = false, 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 25fea48d..3752bb8c 100644 --- a/core/src/test/kotlin/model/CommentTest.kt +++ b/core/src/test/kotlin/model/CommentTest.kt @@ -10,14 +10,18 @@ public class CommentTest { verifyModel("testdata/comments/codeBlockComment.kt") { model -> with(model.members.single().members.first()) { assertEqualsIgnoringSeparators("""[code lang=brainfuck] + | |++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. + | |[/code] |""".trimMargin(), content.toTestString()) } with(model.members.single().members.last()) { assertEqualsIgnoringSeparators("""[code] + | |a + b - c + | |[/code] |""".trimMargin(), content.toTestString()) @@ -153,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/blankLineInsideCodeBlock.html b/core/testdata/format/blankLineInsideCodeBlock.html new file mode 100644 index 00000000..168dd0dd --- /dev/null +++ b/core/testdata/format/blankLineInsideCodeBlock.html @@ -0,0 +1,18 @@ +<HTML> +<HEAD> +<meta charset="UTF-8"> +<title>u - test</title> +</HEAD> +<BODY> +<a href="test/index">test</a> / <a href="test/u">u</a><br/> +<br/> +<h1>u</h1> +<a name="$u()"></a> +<code><span class="keyword">fun </span><span class="identifier">u</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code><pre><code>This is a test + of Dokka's code blocks. +Here is a blank line. + +The previous line was blank. +</code></pre> +</BODY> +</HTML> diff --git a/core/testdata/format/blankLineInsideCodeBlock.kt b/core/testdata/format/blankLineInsideCodeBlock.kt new file mode 100644 index 00000000..9430f4d5 --- /dev/null +++ b/core/testdata/format/blankLineInsideCodeBlock.kt @@ -0,0 +1,12 @@ +/** + * ``` + * This is a test + * of Dokka's code blocks. + * Here is a blank line. + * + * The previous line was blank. + * ``` + */ +fun u() { + +}
\ No newline at end of file diff --git a/core/testdata/format/blankLineInsideCodeBlock.md b/core/testdata/format/blankLineInsideCodeBlock.md new file mode 100644 index 00000000..66f4d65f --- /dev/null +++ b/core/testdata/format/blankLineInsideCodeBlock.md @@ -0,0 +1,14 @@ +[test](test/index) / [u](test/u) + +# u + +`fun u(): Unit` + +``` +This is a test + of Dokka's code blocks. +Here is a blank line. + +The previous line was blank. +``` + diff --git a/core/testdata/format/codeBlock.html b/core/testdata/format/codeBlock.html index b3b65dba..48c2ffd2 100644 --- a/core/testdata/format/codeBlock.html +++ b/core/testdata/format/codeBlock.html @@ -10,11 +10,13 @@ <p>This annotation indicates what exceptions should be declared by a function when compiled to a JVM method.</p> <p>Example:</p> <pre><code>Throws(IOException::class) -fun readFile(name: String): String {...}</code></pre><a href="test/index">test</a> / <a href="test/-it-does-some-obfuscated-thing/index">ItDoesSomeObfuscatedThing</a><br/> +fun readFile(name: String): String {...} +</code></pre><a href="test/index">test</a> / <a href="test/-it-does-some-obfuscated-thing/index">ItDoesSomeObfuscatedThing</a><br/> <br/> <h1>ItDoesSomeObfuscatedThing</h1> <code><span class="keyword">class </span><span class="identifier">ItDoesSomeObfuscatedThing</span></code> <p>Check output of</p> -<pre><code class="lang-brainfuck">++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.</code></pre> +<pre><code class="lang-brainfuck">++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. +</code></pre> </BODY> </HTML> 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/breadcrumbsInMemberOfMemberOfGroupNode/js.kt b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt new file mode 100644 index 00000000..d7fbf924 --- /dev/null +++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/js.kt @@ -0,0 +1,7 @@ +package pack + +class Some { + fun magic() { + + } +}
\ No newline at end of file diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt new file mode 100644 index 00000000..57f36742 --- /dev/null +++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/jvm.kt @@ -0,0 +1,9 @@ +package pack + +class SomeCoolJvmClass { + fun magic() { + + } +} + +typealias Some = SomeCoolJvmClass
\ No newline at end of file diff --git a/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md new file mode 100644 index 00000000..e3d4c070 --- /dev/null +++ b/core/testdata/format/multiplatform/breadcrumbsInMemberOfMemberOfGroupNode/multiplatform.md @@ -0,0 +1,8 @@ +[test](test/index) / [pack](test/pack/index) / [Some](test/pack/-some/index) / [magic](test/pack/-some/-some/magic) + +# magic + +`fun magic(): Unit` + +**Platform and version requirements:** JS + diff --git a/core/testdata/format/multiplatform/groupNode/js.kt b/core/testdata/format/multiplatform/groupNode/js.kt new file mode 100644 index 00000000..045f3f0d --- /dev/null +++ b/core/testdata/format/multiplatform/groupNode/js.kt @@ -0,0 +1,8 @@ +package pack + +class Some { + + fun magic() { + + } +}
\ No newline at end of file diff --git a/core/testdata/format/multiplatform/groupNode/jvm.kt b/core/testdata/format/multiplatform/groupNode/jvm.kt new file mode 100644 index 00000000..57f36742 --- /dev/null +++ b/core/testdata/format/multiplatform/groupNode/jvm.kt @@ -0,0 +1,9 @@ +package pack + +class SomeCoolJvmClass { + fun magic() { + + } +} + +typealias Some = SomeCoolJvmClass
\ No newline at end of file diff --git a/core/testdata/format/multiplatform/groupNode/multiplatform.md b/core/testdata/format/multiplatform/groupNode/multiplatform.md new file mode 100644 index 00000000..c0ef14b1 --- /dev/null +++ b/core/testdata/format/multiplatform/groupNode/multiplatform.md @@ -0,0 +1,20 @@ +[test](test/index) / [pack](test/pack/index) / [Some](test/pack/-some/index) + +# Some + +`typealias Some = SomeCoolJvmClass` + +**Platform and version requirements:** JVM + +`class Some` + +**Platform and version requirements:** JS + +### Constructors + +| [<init>](test/pack/-some/-some/-init-) | `Some()` | + +### Functions + +| [magic](test/pack/-some/-some/magic) | `fun magic(): Unit` | + diff --git a/core/testdata/format/multiplatform/groupNode/multiplatform.package.md b/core/testdata/format/multiplatform/groupNode/multiplatform.package.md new file mode 100644 index 00000000..a9e2e404 --- /dev/null +++ b/core/testdata/format/multiplatform/groupNode/multiplatform.package.md @@ -0,0 +1,13 @@ +[test](test/index) / [pack](test/pack/index) + +## Package pack + +### Types + +| [Some](test/pack/-some/index)<br>(JS) | `class Some` | +| [SomeCoolJvmClass](test/pack/-some-cool-jvm-class/index)<br>(JVM) | `class SomeCoolJvmClass` | + +### Type Aliases + +| [Some](test/pack/-some/index)<br>(JVM) | `typealias Some = SomeCoolJvmClass` | + diff --git a/core/testdata/format/multiplatform/implied/foo.md b/core/testdata/format/multiplatform/implied/foo.md new file mode 100644 index 00000000..c615dd8e --- /dev/null +++ b/core/testdata/format/multiplatform/implied/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/multiplatform/implied/js.kt b/core/testdata/format/multiplatform/implied/js.kt new file mode 100644 index 00000000..dd2de5bc --- /dev/null +++ b/core/testdata/format/multiplatform/implied/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/multiplatform/implied/jvm.kt b/core/testdata/format/multiplatform/implied/jvm.kt new file mode 100644 index 00000000..8d73ce25 --- /dev/null +++ b/core/testdata/format/multiplatform/implied/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/multiplatform/merge/js.kt b/core/testdata/format/multiplatform/merge/js.kt new file mode 100644 index 00000000..bbf1dd7c --- /dev/null +++ b/core/testdata/format/multiplatform/merge/js.kt @@ -0,0 +1,7 @@ +package foo + +/** + * This is a foo. + */ +class Foo { +} diff --git a/core/testdata/format/multiplatform/merge/jvm.kt b/core/testdata/format/multiplatform/merge/jvm.kt new file mode 100644 index 00000000..cb77273f --- /dev/null +++ b/core/testdata/format/multiplatform/merge/jvm.kt @@ -0,0 +1,8 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + +} diff --git a/core/testdata/format/multiplatform/merge/multiplatform.package.md b/core/testdata/format/multiplatform/merge/multiplatform.package.md new file mode 100644 index 00000000..8e463327 --- /dev/null +++ b/core/testdata/format/multiplatform/merge/multiplatform.package.md @@ -0,0 +1,10 @@ +[test](test/index) / [foo](test/foo/index) + +## Package foo + +**Platform and version requirements:** JVM, JS + +### Types + +| [Foo](test/foo/-foo/index)<br>(JVM, JS) | `class Foo`<br>This is a foo. | + diff --git a/core/testdata/format/multiplatform/mergeMembers/foo.md b/core/testdata/format/multiplatform/mergeMembers/foo.md new file mode 100644 index 00000000..7490c878 --- /dev/null +++ b/core/testdata/format/multiplatform/mergeMembers/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/multiplatform/mergeMembers/js.kt b/core/testdata/format/multiplatform/mergeMembers/js.kt new file mode 100644 index 00000000..dd2de5bc --- /dev/null +++ b/core/testdata/format/multiplatform/mergeMembers/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/multiplatform/mergeMembers/jvm.kt b/core/testdata/format/multiplatform/mergeMembers/jvm.kt new file mode 100644 index 00000000..8d73ce25 --- /dev/null +++ b/core/testdata/format/multiplatform/mergeMembers/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/multiplatform/omitRedundant/foo.md b/core/testdata/format/multiplatform/omitRedundant/foo.md new file mode 100644 index 00000000..088ced2c --- /dev/null +++ b/core/testdata/format/multiplatform/omitRedundant/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/multiplatform/omitRedundant/js.kt b/core/testdata/format/multiplatform/omitRedundant/js.kt new file mode 100644 index 00000000..d1b1429c --- /dev/null +++ b/core/testdata/format/multiplatform/omitRedundant/js.kt @@ -0,0 +1,2 @@ +package foo + diff --git a/core/testdata/format/multiplatform/omitRedundant/jvm.kt b/core/testdata/format/multiplatform/omitRedundant/jvm.kt new file mode 100644 index 00000000..35e3c08d --- /dev/null +++ b/core/testdata/format/multiplatform/omitRedundant/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/multiplatform/packagePlatformsFromMembers/js.kt b/core/testdata/format/multiplatform/packagePlatformsFromMembers/js.kt new file mode 100644 index 00000000..86d09289 --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/js.kt @@ -0,0 +1,3 @@ +package foo.bar + +fun buz() {} diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt b/core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt new file mode 100644 index 00000000..86d09289 --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/jvm.kt @@ -0,0 +1,3 @@ +package foo.bar + +fun buz() {} diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md new file mode 100644 index 00000000..1dda25d4 --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.index.md @@ -0,0 +1,10 @@ +[test](test/index) + +**Platform and version requirements:** JVM, JS + +### Packages + +| [foo.bar](test/foo.bar/index)<br>(JVM, JS) | | + +### Index + diff --git a/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md new file mode 100644 index 00000000..2921cdd1 --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsFromMembers/multiplatform.package.md @@ -0,0 +1,10 @@ +[test](test/index) / [foo.bar](test/foo.bar/index) + +## Package foo.bar + +**Platform and version requirements:** JVM, JS + +### Functions + +| [buz](test/foo.bar/buz)<br>(JVM, JS) | `fun buz(): Unit` | + diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt new file mode 100644 index 00000000..27ab1b32 --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/jvm.kt @@ -0,0 +1,5 @@ +package some + +fun String.buz(): Unit { + +}
\ No newline at end of file diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md new file mode 100644 index 00000000..f6e770b2 --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.index.md @@ -0,0 +1,10 @@ +[test](test/index) + +**Platform and version requirements:** JVM + +### Packages + +| [some](test/some/index)<br>(JVM) | | + +### Index + diff --git a/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md new file mode 100644 index 00000000..22f778d6 --- /dev/null +++ b/core/testdata/format/multiplatform/packagePlatformsWithExtExtensions/multiplatform.package.md @@ -0,0 +1,10 @@ +[test](test/index) / [some](test/some/index) + +## Package some + +**Platform and version requirements:** JVM + +### Extensions for External Classes + +| [kotlin.String](test/some/kotlin.-string/index) | | + diff --git a/core/testdata/format/multiplatform/simple/js.kt b/core/testdata/format/multiplatform/simple/js.kt new file mode 100644 index 00000000..e6d66ffd --- /dev/null +++ b/core/testdata/format/multiplatform/simple/js.kt @@ -0,0 +1,7 @@ +package foo + +/** + * This is a bar. + */ +class Bar { +} diff --git a/core/testdata/format/multiplatform/simple/jvm.kt b/core/testdata/format/multiplatform/simple/jvm.kt new file mode 100644 index 00000000..cb77273f --- /dev/null +++ b/core/testdata/format/multiplatform/simple/jvm.kt @@ -0,0 +1,8 @@ +package foo + +/** + * This is a foo. + */ +class Foo { + +} diff --git a/core/testdata/format/multiplatform/simple/multiplatform.package.md b/core/testdata/format/multiplatform/simple/multiplatform.package.md new file mode 100644 index 00000000..3574942c --- /dev/null +++ b/core/testdata/format/multiplatform/simple/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/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt new file mode 100644 index 00000000..84f78dfb --- /dev/null +++ b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.kt @@ -0,0 +1,3 @@ +fun (suspend () -> Unit).foo() { + +}
\ No newline at end of file diff --git a/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md new file mode 100644 index 00000000..c9856976 --- /dev/null +++ b/core/testdata/format/renderFunctionalTypeInParenthesisWhenItIsReceiver.md @@ -0,0 +1,6 @@ +[test](test/index) / [kotlin.SuspendFunction0](test/kotlin.-suspend-function0/index) + +### Extensions for kotlin.SuspendFunction0 + +| [foo](test/kotlin.-suspend-function0/foo) | `fun (suspend () -> Unit).foo(): Unit` | + diff --git a/core/testdata/format/sinceKotlin.html b/core/testdata/format/sinceKotlin.html new file mode 100644 index 00000000..4788fcda --- /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..c811749c --- /dev/null +++ b/core/testdata/format/sinceKotlin.package.md @@ -0,0 +1,10 @@ +[test](test/index) + +## Package <root> + +**Platform and version requirements:** Kotlin 1.1 + +### Types + +| [Since1.1](test/-since1.1/index)<br>(Kotlin 1.1) | `class Since1.1`<br>Useful | + diff --git a/core/testdata/format/suspendParam.kt b/core/testdata/format/suspendParam.kt new file mode 100644 index 00000000..ea3f56f9 --- /dev/null +++ b/core/testdata/format/suspendParam.kt @@ -0,0 +1,3 @@ +fun takesSuspendParam(func: suspend () -> Unit) { + +}
\ No newline at end of file diff --git a/core/testdata/format/suspendParam.md b/core/testdata/format/suspendParam.md new file mode 100644 index 00000000..cf11f57b --- /dev/null +++ b/core/testdata/format/suspendParam.md @@ -0,0 +1,5 @@ +[test](test/index) / [takesSuspendParam](test/takes-suspend-param) + +# takesSuspendParam + +`fun takesSuspendParam(func: suspend () -> Unit): Unit`
\ No newline at end of file diff --git a/core/testdata/format/suspendParam.package.md b/core/testdata/format/suspendParam.package.md new file mode 100644 index 00000000..463ba356 --- /dev/null +++ b/core/testdata/format/suspendParam.package.md @@ -0,0 +1,8 @@ +[test](test/index) + +## Package <root> + +### Functions + +| [takesSuspendParam](test/takes-suspend-param) | `fun takesSuspendParam(func: suspend () -> Unit): Unit` | + diff --git a/core/testdata/format/tripleBackticks.html b/core/testdata/format/tripleBackticks.html index 5b3aef82..bac3385c 100644 --- a/core/testdata/format/tripleBackticks.html +++ b/core/testdata/format/tripleBackticks.html @@ -10,6 +10,7 @@ <a name="$f()"></a> <code><span class="keyword">fun </span><span class="identifier">f</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code> <p>Description</p> -<pre><code>code sample</code></pre> +<pre><code>code sample +</code></pre> </BODY> </HTML> 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/newLinesInImportList.kt b/core/testdata/format/website-samples/newLinesInImportList.kt new file mode 100644 index 00000000..836d9f6f --- /dev/null +++ b/core/testdata/format/website-samples/newLinesInImportList.kt @@ -0,0 +1,12 @@ +import same.* +import some.* + +/** + * @sample example1 + */ +fun foo() { +} + +fun example1() { + +}
\ No newline at end of file diff --git a/core/testdata/format/website-samples/newLinesInImportList.md b/core/testdata/format/website-samples/newLinesInImportList.md new file mode 100644 index 00000000..27d796f8 --- /dev/null +++ b/core/testdata/format/website-samples/newLinesInImportList.md @@ -0,0 +1,24 @@ +--- +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 +import same.* +import some.* + +fun main(args: Array<String>) { +//sampleStart + +//sampleEnd +} +``` + +</div> 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/format/website/dataTags/jre7.kt b/core/testdata/format/website/dataTags/jre7.kt new file mode 100644 index 00000000..d21b8d7b --- /dev/null +++ b/core/testdata/format/website/dataTags/jre7.kt @@ -0,0 +1,11 @@ +package foo + +@SinceKotlin("1.1") +fun jre7New() {} + +fun jre7() {} + +fun shared() {} + +@SinceKotlin("1.1") +fun sharedNew() {}
\ No newline at end of file diff --git a/core/testdata/format/website/dataTags/js.kt b/core/testdata/format/website/dataTags/js.kt new file mode 100644 index 00000000..b22d7088 --- /dev/null +++ b/core/testdata/format/website/dataTags/js.kt @@ -0,0 +1,11 @@ +package foo + +@SinceKotlin("1.1") +fun jsNew() {} + +fun js() {} + +fun shared() {} + +@SinceKotlin("1.1") +fun sharedNew() {}
\ No newline at end of file diff --git a/core/testdata/format/website/dataTags/jvm.kt b/core/testdata/format/website/dataTags/jvm.kt new file mode 100644 index 00000000..02d04226 --- /dev/null +++ b/core/testdata/format/website/dataTags/jvm.kt @@ -0,0 +1,11 @@ +package foo + +@SinceKotlin("1.1") +fun jvmNew() {} + +fun jvm() {} + +fun shared() {} + +@SinceKotlin("1.1") +fun sharedNew() {}
\ No newline at end of file diff --git a/core/testdata/format/website/dataTags/multiplatform.package.md b/core/testdata/format/website/dataTags/multiplatform.package.md new file mode 100644 index 00000000..1c7fbf66 --- /dev/null +++ b/core/testdata/format/website/dataTags/multiplatform.package.md @@ -0,0 +1,71 @@ +--- +title: foo - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/foo/index">foo</a></div> + +## Package foo + +### Functions + +<table class="api-docs-table"> +<tbody> +<tr data-platform="JVM" data-jre-version="JRE7"><td markdown="1"> +<a href="test/foo/jre7">jre7</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jre7</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td markdown="1"> +<a href="test/foo/jre7-new">jre7New</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jre7New</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JS"><td markdown="1"> +<a href="test/foo/js">js</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">js</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JS" data-kotlin-version="Kotlin 1.1"><td markdown="1"> +<a href="test/foo/js-new">jsNew</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jsNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM"><td markdown="1"> +<a href="test/foo/jvm">jvm</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jvm</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM" data-kotlin-version="Kotlin 1.1"><td markdown="1"> +<a href="test/foo/jvm-new">jvmNew</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">jvmNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM, JS" data-jre-version="JRE7"><td markdown="1"> +<a href="test/foo/shared">shared</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">shared</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr><tr data-platform="JVM, JS" data-kotlin-version="Kotlin 1.1" data-jre-version="JRE7"><td markdown="1"> +<a href="test/foo/shared-new">sharedNew</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">sharedNew</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr></tbody> +</table> diff --git a/core/testdata/format/website/dataTagsInGroupNode/jre7.kt b/core/testdata/format/website/dataTagsInGroupNode/jre7.kt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/core/testdata/format/website/dataTagsInGroupNode/jre7.kt diff --git a/core/testdata/format/website/dataTagsInGroupNode/js.kt b/core/testdata/format/website/dataTagsInGroupNode/js.kt new file mode 100644 index 00000000..045f3f0d --- /dev/null +++ b/core/testdata/format/website/dataTagsInGroupNode/js.kt @@ -0,0 +1,8 @@ +package pack + +class Some { + + fun magic() { + + } +}
\ No newline at end of file diff --git a/core/testdata/format/website/dataTagsInGroupNode/jvm.kt b/core/testdata/format/website/dataTagsInGroupNode/jvm.kt new file mode 100644 index 00000000..57f36742 --- /dev/null +++ b/core/testdata/format/website/dataTagsInGroupNode/jvm.kt @@ -0,0 +1,9 @@ +package pack + +class SomeCoolJvmClass { + fun magic() { + + } +} + +typealias Some = SomeCoolJvmClass
\ No newline at end of file diff --git a/core/testdata/format/website/dataTagsInGroupNode/multiplatform.md b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.md new file mode 100644 index 00000000..78f6adf2 --- /dev/null +++ b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.md @@ -0,0 +1,56 @@ +--- +title: pack.Some - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/pack/index">pack</a> / <a href="test/pack/-some/index">Some</a></div> + +# Some + +<div class="overload-group" data-platform="JVM" markdown="1"> + +<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span> <span class="symbol">=</span> <span class="identifier">SomeCoolJvmClass</span></code></div> + +**Platform and version requirements:** JVM + +</div> + +<div class="overload-group" data-platform="JS" markdown="1"> + +<div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div> + +**Platform and version requirements:** JS + +### Constructors + +<table class="api-docs-table"> +<tbody> +<tr> +<td markdown="1"> +<a href="test/pack/-some/-some/-init-"><init></a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="identifier">Some</span><span class="symbol">(</span><span class="symbol">)</span></code></div> + +</td> +</tr> +</tbody> +</table> + +### Functions + +<table class="api-docs-table"> +<tbody> +<tr> +<td markdown="1"> +<a href="test/pack/-some/-some/magic">magic</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">fun </span><span class="identifier">magic</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><span class="identifier">Unit</span></code></div> + +</td> +</tr> +</tbody> +</table> + +</div> diff --git a/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md new file mode 100644 index 00000000..a6e7d63b --- /dev/null +++ b/core/testdata/format/website/dataTagsInGroupNode/multiplatform.package.md @@ -0,0 +1,43 @@ +--- +title: pack - test +layout: api +--- + +<div class='api-docs-breadcrumbs'><a href="test/index">test</a> / <a href="test/pack/index">pack</a></div> + +## Package pack + +### Types + +<table class="api-docs-table"> +<tbody> +<tr data-platform="JS"><td markdown="1"> +<a href="test/pack/-some/index">Some</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">class </span><span class="identifier">Some</span></code></div> + +</td> +</tr><tr data-platform="JVM"><td markdown="1"> +<a href="test/pack/-some-cool-jvm-class/index">SomeCoolJvmClass</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">class </span><span class="identifier">SomeCoolJvmClass</span></code></div> + +</td> +</tr></tbody> +</table> + +### Type Aliases + +<table class="api-docs-table"> +<tbody> +<tr data-platform="JVM"><td markdown="1"> +<a href="test/pack/-some/index">Some</a> +</td> +<td markdown="1"> +<div class="signature"><code><span class="keyword">typealias </span><span class="identifier">Some</span> <span class="symbol">=</span> <span class="identifier">SomeCoolJvmClass</span></code></div> + +</td> +</tr></tbody> +</table> 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/javadoc/blankLineInsideCodeBlock.kt b/core/testdata/javadoc/blankLineInsideCodeBlock.kt new file mode 100644 index 00000000..9430f4d5 --- /dev/null +++ b/core/testdata/javadoc/blankLineInsideCodeBlock.kt @@ -0,0 +1,12 @@ +/** + * ``` + * This is a test + * of Dokka's code blocks. + * Here is a blank line. + * + * The previous line was blank. + * ``` + */ +fun u() { + +}
\ 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 |