Merge remote-tracking branch 'upstream/java-layout-html-format' into devsite-with-java-layout-html
diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt
index 126a017..4b75f31 100644
--- a/core/src/main/kotlin/DokkaBootstrapImpl.kt
+++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt
@@ -69,7 +69,8 @@
languageVersion,
apiVersion,
cacheRoot,
- suppressedFiles.map { File(it) }
+ suppressedFiles.map { File(it) },
+ collectInheritedExtensionsFromLibraries
)
)
}
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt
index 5148097..494e05f 100644
--- a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormat.kt
@@ -63,7 +63,7 @@
interface JavaLayoutHtmlTemplateService {
fun composePage(
- nodes: List<DocumentationNode>,
+ page: JavaLayoutHtmlFormatOutputBuilder.Page,
tagConsumer: TagConsumer<Appendable>,
headContent: HEAD.() -> Unit,
bodyContent: BODY.() -> Unit
@@ -71,7 +71,7 @@
class Default : JavaLayoutHtmlTemplateService {
override fun composePage(
- nodes: List<DocumentationNode>,
+ page: JavaLayoutHtmlFormatOutputBuilder.Page,
tagConsumer: TagConsumer<Appendable>,
headContent: HEAD.() -> Unit,
bodyContent: BODY.() -> Unit
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt
index 0c95a41..a8b754f 100644
--- a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlFormatOutputBuilder.kt
@@ -12,19 +12,117 @@
open class JavaLayoutHtmlFormatOutputBuilder(
- val output: Appendable,
- val languageService: LanguageService,
- val uriProvider: JavaLayoutHtmlUriProvider,
- val templateService: JavaLayoutHtmlTemplateService,
- val logger: DokkaLogger,
- val uri: URI
+ val output: Appendable,
+ val languageService: LanguageService,
+ val uriProvider: JavaLayoutHtmlUriProvider,
+ val templateService: JavaLayoutHtmlTemplateService,
+ val logger: DokkaLogger,
+ val uri: URI
) {
val htmlConsumer = output.appendHTML()
- val contentToHtmlBuilder = ContentToHtmlBuilder(uriProvider, uri)
- open fun <T> FlowContent.summaryNodeGroup(nodes: Iterable<T>, header: String, headerAsRow: Boolean = false, row: TBODY.(T) -> Unit) {
+ private fun FlowContent.hN(
+ level: Int,
+ classes: String? = null,
+ block: CommonAttributeGroupFacadeFlowHeadingPhrasingContent.() -> Unit
+ ) {
+ when (level) {
+ 1 -> h1(classes, block)
+ 2 -> h2(classes, block)
+ 3 -> h3(classes, block)
+ 4 -> h4(classes, block)
+ 5 -> h5(classes, block)
+ 6 -> h6(classes, block)
+ }
+ }
+
+ protected open fun FlowContent.metaMarkup(content: List<ContentNode>) = contentNodesToMarkup(content)
+ protected open fun FlowContent.metaMarkup(content: ContentNode) = contentNodeToMarkup(content)
+
+ private fun FlowContent.contentNodesToMarkup(content: List<ContentNode>): Unit = content.forEach { contentNodeToMarkup(it) }
+ private fun FlowContent.contentNodeToMarkup(content: ContentNode) {
+ when (content) {
+ is ContentText -> +content.text
+ is ContentSymbol -> span("symbol") { +content.text }
+ is ContentKeyword -> span("keyword") { +content.text }
+ is ContentIdentifier -> span("identifier") {
+ content.signature?.let { id = it }
+ +content.text
+ }
+
+ is ContentHeading -> hN(level = content.level) { contentNodesToMarkup(content.children) }
+
+ is ContentEntity -> +content.text
+
+ is ContentStrong -> strong { contentNodesToMarkup(content.children) }
+ is ContentStrikethrough -> del { contentNodesToMarkup(content.children) }
+ is ContentEmphasis -> em { contentNodesToMarkup(content.children) }
+
+ is ContentOrderedList -> ol { contentNodesToMarkup(content.children) }
+ is ContentUnorderedList -> ul { contentNodesToMarkup(content.children) }
+ is ContentListItem -> consumer.li {
+ (content.children.singleOrNull() as? ContentParagraph)
+ ?.let { paragraph -> contentNodesToMarkup(paragraph.children) }
+ ?: contentNodesToMarkup(content.children)
+ }
+
+
+ is ContentCode -> contentInlineCode(content)
+ is ContentBlockSampleCode -> contentBlockSampleCode(content)
+ is ContentBlockCode -> contentBlockCode(content)
+
+
+ ContentNonBreakingSpace -> +nbsp
+ ContentSoftLineBreak, ContentIndentedSoftLineBreak -> {}
+ ContentHardLineBreak -> br
+
+ is ContentParagraph -> p { contentNodesToMarkup(content.children) }
+
+ is ContentNodeLink -> {
+ a(href = content.node) { contentNodesToMarkup(content.children) }
+ }
+ is ContentExternalLink -> contentExternalLink(content)
+ is ContentSection -> {}
+ is ContentBlock -> contentNodesToMarkup(content.children)
+ }
+ }
+
+ protected open fun FlowContent.contentInlineCode(content: ContentCode) {
+ code { contentNodesToMarkup(content.children) }
+ }
+
+ protected open fun FlowContent.contentBlockSampleCode(content: ContentBlockSampleCode) {
+ pre {
+ code {
+ attributes["data-language"] = content.language
+ contentNodesToMarkup(content.importsBlock.children)
+ +"\n\n"
+ contentNodesToMarkup(content.children)
+ }
+ }
+ }
+
+ protected open fun FlowContent.contentBlockCode(content: ContentBlockCode) {
+ pre {
+ code {
+ attributes["data-language"] = content.language
+ contentNodesToMarkup(content.children)
+ }
+ }
+ }
+
+ protected open fun FlowContent.contentExternalLink(content: ContentExternalLink) {
+ a(href = content.href) { contentNodesToMarkup(content.children) }
+ }
+
+ protected open fun <T> FlowContent.summaryNodeGroup(
+ nodes: Iterable<T>,
+ header: String,
+ headerAsRow: Boolean = true,
+ row: TBODY.(T) -> Unit
+ ) {
if (nodes.none()) return
if (!headerAsRow) {
h2 { +header }
@@ -39,38 +137,31 @@
}
}
- fun FlowContent.metaMarkup(content: ContentNode) = with(contentToHtmlBuilder) {
- appendContent(content)
- }
- fun FlowContent.metaMarkup(content: List<ContentNode>) = with(contentToHtmlBuilder) {
- appendContent(content)
- }
-
- open fun TBODY.classLikeRow(node: DocumentationNode) = tr {
+ protected open fun TBODY.classLikeRow(node: DocumentationNode) = tr {
td { a(href = uriProvider.linkTo(node, uri)) { +node.simpleName() } }
- td { metaMarkup(node.summary) }
+ td { contentNodeToMarkup(node.summary) }
}
- fun FlowContent.modifiers(node: DocumentationNode) {
+ protected fun FlowContent.modifiers(node: DocumentationNode) {
for (modifier in node.details(NodeKind.Modifier)) {
renderedSignature(modifier, SUMMARY)
}
}
- fun FlowContent.shortFunctionParametersList(func: DocumentationNode) {
+ protected fun FlowContent.shortFunctionParametersList(func: DocumentationNode) {
val params = func.details(NodeKind.Parameter)
- .map { languageService.render(it, FULL) }
- .run {
- drop(1).fold(listOfNotNull(firstOrNull())) { acc, node ->
- acc + ContentText(", ") + node
- }
+ .map { languageService.render(it, FULL) }
+ .run {
+ drop(1).fold(listOfNotNull(firstOrNull())) { acc, node ->
+ acc + ContentText(", ") + node
}
+ }
metaMarkup(listOf(ContentText("(")) + params + listOf(ContentText(")")))
}
- open fun TBODY.functionLikeSummaryRow(node: DocumentationNode) = tr {
+ protected open fun TBODY.functionLikeSummaryRow(node: DocumentationNode) = tr {
if (node.kind != NodeKind.Constructor) {
td {
modifiers(node)
@@ -85,16 +176,16 @@
renderedSignature(receiver.detail(NodeKind.Type), SUMMARY)
+"."
}
- a(href = uriProvider.linkTo(node, uri)) { +node.name }
+ a(href = node) { +node.name }
shortFunctionParametersList(node)
}
}
- metaMarkup(node.summary)
+ contentNodeToMarkup(node.summary)
}
}
- open fun TBODY.propertyLikeSummaryRow(node: DocumentationNode) = tr {
+ protected open fun TBODY.propertyLikeSummaryRow(node: DocumentationNode) = tr {
td {
modifiers(node)
renderedSignature(node.detail(NodeKind.Type), SUMMARY)
@@ -102,34 +193,37 @@
td {
div {
code {
- a(href = uriProvider.linkTo(node, uri)) { +node.name }
+ a(href = node) { +node.name }
}
}
- metaMarkup(node.summary)
+ contentNodeToMarkup(node.summary)
}
}
- open fun TBODY.nestedClassSummaryRow(node: DocumentationNode) = tr {
+ protected open fun TBODY.nestedClassSummaryRow(node: DocumentationNode) = tr {
td {
modifiers(node)
}
td {
div {
code {
- a(href = uriProvider.linkTo(node, uri)) { +node.name }
+ a(href = node) { +node.name }
}
}
- metaMarkup(node.summary)
+ contentNodeToMarkup(node.summary)
}
}
- open fun TBODY.inheritRow(entry: Map.Entry<DocumentationNode, List<DocumentationNode>>, summaryRow: TBODY.(DocumentationNode) -> Unit) = tr {
+ protected open fun TBODY.inheritRow(
+ entry: Map.Entry<DocumentationNode, List<DocumentationNode>>,
+ summaryRow: TBODY.(DocumentationNode) -> Unit
+ ) = tr {
td {
val (from, nodes) = entry
+"From class "
- a(href = uriProvider.linkTo(from.owner!!, uri)) { +from.qualifiedName() }
+ a(href = from.owner!!) { +from.qualifiedName() }
table {
tbody {
for (node in nodes) {
@@ -140,11 +234,14 @@
}
}
- open fun TBODY.extensionRow(entry: Map.Entry<DocumentationNode, List<DocumentationNode>>, summaryRow: TBODY.(DocumentationNode) -> Unit) = tr {
+ protected open fun TBODY.extensionRow(
+ entry: Map.Entry<DocumentationNode, List<DocumentationNode>>,
+ summaryRow: TBODY.(DocumentationNode) -> Unit
+ ) = tr {
td {
val (from, nodes) = entry
+"From "
- a(href = uriProvider.linkTo(from, uri)) { +from.qualifiedName() }
+ a(href = from) { +from.qualifiedName() }
table {
tbody {
for (node in nodes) {
@@ -155,16 +252,375 @@
}
}
- private fun FlowContent.renderedSignature(node: DocumentationNode, mode: LanguageService.RenderMode = SUMMARY) {
- metaMarkup(languageService.render(node, mode))
+ protected open fun FlowContent.a(href: DocumentationNode?, classes: String? = null, block: A.() -> Unit) {
+ if (href == null) {
+ return a(href = "#", classes = classes, block = block)
+ }
+
+ val hrefText =
+ href.name.takeIf { href.kind == NodeKind.ExternalLink }
+ ?: href.links.firstOrNull { it.kind == NodeKind.ExternalLink }?.name
+ ?: "#".takeIf { href.kind == NodeKind.ExternalClass } // When external class unresolved
+ ?: uriProvider.linkTo(href, uri)
+
+ a(href = hrefText, classes = classes, block = block)
}
- open fun FlowContent.memberDocs(node: DocumentationNode) {
+ protected open fun FlowContent.renderedSignature(node: DocumentationNode, mode: LanguageService.RenderMode = SUMMARY) {
+ contentNodeToMarkup(languageService.render(node, mode))
+ }
+
+ protected open fun generatePackage(page: Page.PackagePage) = templateService.composePage(
+ page,
+ htmlConsumer,
+ headContent = {
+
+ },
+ bodyContent = {
+ h1 { +page.node.name }
+ contentNodeToMarkup(page.node.content)
+ summaryNodeGroup(page.classes, "Classes", headerAsRow = false) { classLikeRow(it) }
+ summaryNodeGroup(page.exceptions, "Exceptions", headerAsRow = false) { classLikeRow(it) }
+ summaryNodeGroup(page.typeAliases, "Type-aliases", headerAsRow = false) { classLikeRow(it) }
+ summaryNodeGroup(page.annotations, "Annotations", headerAsRow = false) { classLikeRow(it) }
+ summaryNodeGroup(page.enums, "Enums", headerAsRow = false) { classLikeRow(it) }
+
+ summaryNodeGroup(
+ page.constants,
+ "Top-level constants summary",
+ headerAsRow = false
+ ) {
+ propertyLikeSummaryRow(it)
+ }
+
+ summaryNodeGroup(
+ page.functions,
+ "Top-level functions summary",
+ headerAsRow = false
+ ) {
+ functionLikeSummaryRow(it)
+ }
+
+ summaryNodeGroup(
+ page.properties,
+ "Top-level properties summary",
+ headerAsRow = false
+ ) {
+ propertyLikeSummaryRow(it)
+ }
+
+
+ fullMemberDocs(page.constants, "Top-level constants")
+ fullMemberDocs(page.functions, "Top-level functions")
+ fullMemberDocs(page.properties, "Top-level properties")
+ }
+ )
+
+ protected fun FlowContent.qualifiedTypeReference(node: DocumentationNode) {
+ if (node.kind in classLike) {
+ a(href = node) { +node.qualifiedName() }
+ return
+ }
+
+ val targetLink = node.links.singleOrNull()
+
+ if (targetLink?.kind == NodeKind.TypeParameter) {
+ +node.name
+ return
+ }
+
+ a(href = targetLink) {
+ +node.qualifiedNameFromType()
+ }
+ val typeParameters = node.details(NodeKind.Type)
+ if (typeParameters.isNotEmpty()) {
+ +"<"
+ typeParameters.forEach {
+ if (it != typeParameters.first()) {
+ +", "
+ }
+ qualifiedTypeReference(it)
+ }
+ +">"
+ }
+ }
+
+ protected open fun FlowContent.classHierarchy(superclasses: List<DocumentationNode>) {
+ table {
+ superclasses.forEach {
+ tr {
+ if (it != superclasses.first()) {
+ td {
+ +" ↳"
+ }
+ }
+ td {
+ qualifiedTypeReference(it)
+ }
+ }
+ }
+ }
+ }
+
+ protected open fun FlowContent.subclasses(inheritors: List<DocumentationNode>, direct: Boolean) {
+ if (inheritors.isEmpty()) return
+ div {
+ table {
+ thead {
+ tr {
+ td {
+ if (direct)
+ +"Known Direct Subclasses"
+ else
+ +"Known Indirect Subclasses"
+ }
+ }
+ }
+ tbody {
+ inheritors.forEach { inheritor ->
+ tr {
+ td {
+ a(href = inheritor) { +inheritor.classNodeNameWithOuterClass() }
+ }
+ td {
+ contentNodeToMarkup(inheritor.summary)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ protected open fun FlowContent.classLikeSummaries(page: Page.ClassPage) = with(page) {
+ summaryNodeGroup(
+ nestedClasses,
+ "Nested classes",
+ headerAsRow = true
+ ) {
+ nestedClassSummaryRow(it)
+ }
+
+ summaryNodeGroup(constants, "Constants") { propertyLikeSummaryRow(it) }
+
+ summaryNodeGroup(
+ constructors,
+ "Constructors",
+ headerAsRow = true
+ ) {
+ functionLikeSummaryRow(it)
+ }
+
+ summaryNodeGroup(functions, "Functions", headerAsRow = true) { functionLikeSummaryRow(it) }
+ summaryNodeGroup(
+ companionFunctions,
+ "Companion functions",
+ headerAsRow = true
+ ) {
+ functionLikeSummaryRow(it)
+ }
+ summaryNodeGroup(
+ inheritedFunctionsByReceiver.entries,
+ "Inherited functions",
+ headerAsRow = true
+ ) {
+ inheritRow(it) {
+ functionLikeSummaryRow(it)
+ }
+ }
+ summaryNodeGroup(
+ extensionFunctions.entries,
+ "Extension functions",
+ headerAsRow = true
+ ) {
+ extensionRow(it) {
+ functionLikeSummaryRow(it)
+ }
+ }
+ summaryNodeGroup(
+ inheritedExtensionFunctions.entries,
+ "Inherited extension functions",
+ headerAsRow = true
+ ) {
+ extensionRow(it) {
+ functionLikeSummaryRow(it)
+ }
+ }
+
+
+ summaryNodeGroup(properties, "Properties", headerAsRow = true) { propertyLikeSummaryRow(it) }
+ summaryNodeGroup(
+ companionProperties,
+ "Companion properties",
+ headerAsRow = true
+ ) {
+ propertyLikeSummaryRow(it)
+ }
+
+ summaryNodeGroup(
+ inheritedPropertiesByReceiver.entries,
+ "Inherited properties",
+ headerAsRow = true
+ ) {
+ inheritRow(it) {
+ propertyLikeSummaryRow(it)
+ }
+ }
+ summaryNodeGroup(
+ extensionProperties.entries,
+ "Extension properties",
+ headerAsRow = true
+ ) {
+ extensionRow(it) {
+ propertyLikeSummaryRow(it)
+ }
+ }
+ summaryNodeGroup(
+ inheritedExtensionProperties.entries,
+ "Inherited extension properties",
+ headerAsRow = true
+ ) {
+ extensionRow(it) {
+ propertyLikeSummaryRow(it)
+ }
+ }
+ }
+
+ protected open fun FlowContent.classLikeFullMemberDocs(page: Page.ClassPage) = with(page) {
+ fullMemberDocs(constants, "Constants")
+ fullMemberDocs(constructors, "Constructors")
+ fullMemberDocs(functions, "Functions")
+ fullMemberDocs(properties, "Properties")
+ if (!hasMeaningfulCompanion) {
+ fullMemberDocs(companionFunctions, "Companion functions")
+ fullMemberDocs(companionProperties, "Companion properties")
+ }
+ }
+
+ protected open fun generateClassLike(page: Page.ClassPage) = templateService.composePage(
+ page,
+ htmlConsumer,
+ headContent = {
+
+ },
+ bodyContent = {
+ val node = page.node
+ with(page) {
+ h1 { +node.name }
+ pre { renderedSignature(node, FULL) }
+ classHierarchy(page.superclasses)
+
+ subclasses(page.directInheritors, true)
+ subclasses(page.indirectInheritors, false)
+
+ contentNodeToMarkup(node.content)
+
+ h2 { +"Summary" }
+ classLikeSummaries(page)
+
+ classLikeFullMemberDocs(page)
+ }
+ }
+ )
+
+ protected open fun generateClassIndex(page: Page.ClassIndex) = templateService.composePage(
+ page,
+ htmlConsumer,
+ headContent = {
+
+ },
+ bodyContent = {
+ h1 { +"Class Index" }
+
+
+ ul {
+ page.classesByFirstLetter.forEach { (letter) ->
+ li { a(href = "#letter_$letter") { +letter } }
+ }
+ }
+
+ page.classesByFirstLetter.forEach { (letter, classes) ->
+ h2 {
+ id = "letter_$letter"
+ +letter
+ }
+ table {
+ tbody {
+ for (node in classes) {
+ tr {
+ td {
+ a(href = uriProvider.linkTo(node, uri)) { +node.classNodeNameWithOuterClass() }
+ }
+ td {
+ contentNodeToMarkup(node.content)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+
+ protected open fun generatePackageIndex(page: Page.PackageIndex) = templateService.composePage(
+ page,
+ htmlConsumer,
+ headContent = {
+
+ },
+ bodyContent = {
+ h1 { +"Package Index" }
+ table {
+ tbody {
+ for (node in page.packages) {
+ tr {
+ td {
+ a(href = uriProvider.linkTo(node, uri)) { +node.name }
+ }
+ td {
+ contentNodeToMarkup(node.content)
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+
+ fun generatePage(page: Page) {
+ when (page) {
+ is Page.PackageIndex -> generatePackageIndex(page)
+ is Page.ClassIndex -> generateClassIndex(page)
+ is Page.ClassPage -> generateClassLike(page)
+ is Page.PackagePage -> generatePackage(page)
+ }
+ }
+
+ protected fun FlowContent.fullMemberDocs(
+ nodes: List<DocumentationNode>,
+ header: String
+ ) {
+ if (nodes.none()) return
+ h2 {
+ +header
+ }
+ for (node in nodes) {
+ fullMemberDocs(node)
+ }
+ }
+
+ protected open fun FlowContent.fullMemberDocs(node: DocumentationNode) {
div {
id = node.signatureForAnchor(logger)
h3 { +node.name }
pre { renderedSignature(node, FULL) }
- metaMarkup(node.content)
+ contentNodeToMarkup(node.content)
+ node.constantValue()?.let { value ->
+ pre {
+ +"Value: "
+ code { +value }
+ }
+ }
for ((name, sections) in node.content.sections.groupBy { it.tag }) {
table {
thead { tr { td { h3 { +name } } } }
@@ -183,357 +639,144 @@
}
}
- fun appendPackage(node: DocumentationNode) = templateService.composePage(
- listOf(node),
- htmlConsumer,
- headContent = {
-
- },
- bodyContent = {
- h1 { +node.name }
- metaMarkup(node.content)
- summaryNodeGroup(node.members(NodeKind.Class), "Classes") { classLikeRow(it) }
- summaryNodeGroup(node.members(NodeKind.Exception), "Exceptions") { classLikeRow(it) }
- summaryNodeGroup(node.members(NodeKind.TypeAlias), "Type-aliases") { classLikeRow(it) }
- summaryNodeGroup(node.members(NodeKind.AnnotationClass), "Annotations") { classLikeRow(it) }
- summaryNodeGroup(node.members(NodeKind.Enum), "Enums") { classLikeRow(it) }
-
- summaryNodeGroup(node.members(NodeKind.Function), "Top-level functions summary") { functionLikeSummaryRow(it) }
- summaryNodeGroup(node.members(NodeKind.Property), "Top-level properties summary") { propertyLikeSummaryRow(it) }
-
-
- fullDocs(node.members(NodeKind.Function), "Top-level functions") { memberDocs(it) }
- fullDocs(node.members(NodeKind.Property), "Top-level properties") { memberDocs(it) }
+ sealed class Page {
+ class PackageIndex(packages: List<DocumentationNode>) : Page() {
+ init {
+ assert(packages.all { it.kind == NodeKind.Package })
}
- )
- fun FlowContent.qualifiedTypeReference(node: DocumentationNode) {
- if (node.kind in classLike) {
- a(href = uriProvider.linkTo(node, uri)) {
- +node.qualifiedName()
+ val packages = packages.sortedBy { it.name }
+ }
+
+ class ClassIndex(allTypesNode: DocumentationNode) : Page() {
+ init {
+ assert(allTypesNode.kind == NodeKind.AllTypes)
}
- return
- }
- val targetLink = node.links.single()
+ // Wide-collect all nested classes
+ val classes: List<DocumentationNode> =
+ generateSequence(listOf(allTypesNode)) { nodes ->
+ nodes
+ .flatMap { it.members.filter { it.kind in NodeKind.classLike } }
+ .takeUnless { it.isEmpty() }
+ }.drop(1)
+ .flatten()
+ .sortedBy { it.classNodeNameWithOuterClass() }
+ .toList()
- if (targetLink.kind == NodeKind.TypeParameter) {
- +node.name
- return
- }
- val href = if (targetLink.kind == NodeKind.ExternalLink)
- targetLink.name
- else
- uriProvider.linkTo(targetLink, uri)
-
- a(href = href) {
- +node.qualifiedNameFromType()
- }
- val typeParameters = node.details(NodeKind.Type)
- if (typeParameters.isNotEmpty()) {
- +"<"
- typeParameters.forEach {
- if (it != typeParameters.first()) {
- +", "
- }
- qualifiedTypeReference(it)
- }
- +">"
- }
- }
-
- open fun FlowContent.classHierarchy(node: DocumentationNode) {
-
- val superclasses = generateSequence(node.superclass) { it.links.single().superclass }.toList().asReversed() + node
- table {
- superclasses.forEach {
- tr {
- if (it != superclasses.first()) {
- td {
- +" ↳"
- }
+ // Group all classes by it's first letter and sort
+ val classesByFirstLetter =
+ classes
+ .groupBy {
+ it.classNodeNameWithOuterClass().first().toString()
}
- td {
- qualifiedTypeReference(it)
- }
- }
- }
+ .entries
+ .sortedBy { (letter) -> letter }
}
- }
- open fun FlowContent.subclasses(inheritors: List<DocumentationNode>, direct: Boolean) {
- if (inheritors.isEmpty()) return
- div {
- table {
- thead {
- tr {
- td {
- if (direct)
- +"Known Direct Subclasses"
- else
- +"Known Indirect Subclasses"
- }
- }
- }
- tbody {
- inheritors.forEach {
- tr {
- td {
- a(href = uriProvider.linkTo(it, uri)) { +it.classNodeNameWithOuterClass() }
- }
- td {
- metaMarkup(it.summary)
- }
- }
- }
- }
+ class ClassPage(val node: DocumentationNode) : Page() {
+
+ init {
+ assert(node.kind in NodeKind.classLike)
}
- }
- }
- fun appendClassLike(node: DocumentationNode) = templateService.composePage(
- listOf(node),
- htmlConsumer,
- headContent = {
+ val superclasses = (sequenceOf(node) + node.superclassTypeSequence).toList().asReversed()
- },
- bodyContent = {
- h1 { +node.name }
- pre { renderedSignature(node, FULL) }
- classHierarchy(node)
+ val directInheritors: List<DocumentationNode>
+ val indirectInheritors: List<DocumentationNode>
+
+ init {
+ // Wide-collect all inheritors
val inheritors = generateSequence(node.inheritors) { inheritors ->
inheritors
- .flatMap { it.inheritors }
- .takeUnless { it.isEmpty() }
+ .flatMap { it.inheritors }
+ .takeUnless { it.isEmpty() }
}
- subclasses(inheritors.first(), true)
- subclasses(inheritors.drop(1).flatten().toList(), false)
+ directInheritors = inheritors.first()
+ indirectInheritors = inheritors.drop(1).flatten().toList()
+ }
+
+ val isCompanion = node.details(NodeKind.Modifier).any { it.name == "companion" }
+ val hasMeaningfulCompanion = !isCompanion && node.companion != null
+
+ private fun DocumentationNode.thisTypeExtension() =
+ detail(NodeKind.Receiver).detail(NodeKind.Type).links.any { it == node }
+
+ val functionKind = if (!isCompanion) NodeKind.Function else NodeKind.CompanionObjectFunction
+ val propertyKind = if (!isCompanion) NodeKind.Property else NodeKind.CompanionObjectProperty
+
+ private fun DocumentationNode.isFunction() = kind == functionKind
+ private fun DocumentationNode.isProperty() = kind == propertyKind
- metaMarkup(node.content)
+ val nestedClasses = node.members.filter { it.kind in NodeKind.classLike }
- h2 { +"Summary" }
+ val constants = node.members.filter { it.constantValue() != null }
- val isCompanion = node.details(NodeKind.Modifier).any { it.name == "companion" }
- val hasMeaningfulCompanion = !isCompanion && node.companion != null
+ val constructors = node.members(NodeKind.Constructor)
+ val functions = node.members(functionKind)
+ val properties = node.members(propertyKind) - constants
+ val inheritedFunctionsByReceiver = node.inheritedMembers(functionKind).groupBy { it.owner!! }
+ val inheritedPropertiesByReceiver = node.inheritedMembers(propertyKind).groupBy { it.owner!! }
- fun DocumentationNode.thisTypeExtension() = detail(NodeKind.Receiver).detail(NodeKind.Type).links.any { it == node }
+ val originalExtensions = if (!isCompanion) node.extensions else node.owner!!.extensions
- val functionKind = if (!isCompanion) NodeKind.Function else NodeKind.CompanionObjectFunction
- val propertyKind = if (!isCompanion) NodeKind.Property else NodeKind.CompanionObjectProperty
+ val extensionFunctions: Map<DocumentationNode, List<DocumentationNode>>
+ val extensionProperties: Map<DocumentationNode, List<DocumentationNode>>
+ val inheritedExtensionFunctions: Map<DocumentationNode, List<DocumentationNode>>
+ val inheritedExtensionProperties: Map<DocumentationNode, List<DocumentationNode>>
- fun DocumentationNode.isFunction() = kind == functionKind
- fun DocumentationNode.isProperty() = kind == propertyKind
-
- val functions = node.members(functionKind)
- val properties = node.members(propertyKind)
- val inheritedFunctionsByReceiver = node.inheritedMembers(functionKind).groupBy { it.owner!! }
- val inheritedPropertiesByReceiver = node.inheritedMembers(propertyKind).groupBy { it.owner!! }
-
-
- val originalExtensions = if (!isCompanion) node.extensions else node.owner!!.extensions
+ init {
val (extensions, inheritedExtensions) = originalExtensions.partition { it.thisTypeExtension() }
- val extensionFunctions = extensions.filter(DocumentationNode::isFunction).groupBy { it.owner!! }
- val extensionProperties = extensions.filter(DocumentationNode::isProperty).groupBy { it.owner!! }
- val inheritedExtensionFunctions = inheritedExtensions.filter(DocumentationNode::isFunction).groupBy { it.owner!! }
- val inheritedExtensionProperties = inheritedExtensions.filter(DocumentationNode::isProperty).groupBy { it.owner!! }
-
- val companionFunctions = node.members(NodeKind.CompanionObjectFunction)
- val companionProperties = node.members(NodeKind.CompanionObjectProperty)
-
- summaryNodeGroup(node.members.filter { it.kind in NodeKind.classLike }, "Nested classes", headerAsRow = true) { nestedClassSummaryRow(it) }
-
- summaryNodeGroup(node.members(NodeKind.Constructor), "Constructors", headerAsRow = true) { functionLikeSummaryRow(it) }
-
- summaryNodeGroup(functions, "Functions", headerAsRow = true) { functionLikeSummaryRow(it) }
- if (!isCompanion) {
- summaryNodeGroup(companionFunctions, "Companion functions", headerAsRow = true) { functionLikeSummaryRow(it) }
- }
- summaryNodeGroup(inheritedFunctionsByReceiver.entries, "Inherited functions", headerAsRow = true) { inheritRow(it) { functionLikeSummaryRow(it) } }
- summaryNodeGroup(extensionFunctions.entries, "Extension functions", headerAsRow = true) { extensionRow(it) { functionLikeSummaryRow(it) } }
- summaryNodeGroup(inheritedExtensionFunctions.entries, "Inherited extension functions", headerAsRow = true) { extensionRow(it) { functionLikeSummaryRow(it) } }
-
-
- summaryNodeGroup(properties, "Properties", headerAsRow = true) { propertyLikeSummaryRow(it) }
- if (!isCompanion) {
- summaryNodeGroup(companionProperties, "Companion properties", headerAsRow = true) { propertyLikeSummaryRow(it) }
- }
- summaryNodeGroup(inheritedPropertiesByReceiver.entries, "Inherited properties", headerAsRow = true) { inheritRow(it) { propertyLikeSummaryRow(it) } }
- summaryNodeGroup(extensionProperties.entries, "Extension properties", headerAsRow = true) { extensionRow(it) { propertyLikeSummaryRow(it) } }
- summaryNodeGroup(inheritedExtensionProperties.entries, "Inherited extension properties", headerAsRow = true) { extensionRow(it) { propertyLikeSummaryRow(it) } }
-
-
- fullDocs(node.members(NodeKind.Constructor), "Constructors") { memberDocs(it) }
- fullDocs(functions, "Functions") { memberDocs(it) }
- fullDocs(properties, "Properties") { memberDocs(it) }
- if (!isCompanion && !hasMeaningfulCompanion) {
- fullDocs(companionFunctions, "Companion functions") { memberDocs(it) }
- fullDocs(companionProperties, "Companion properties") { memberDocs(it) }
- }
+ extensionFunctions = extensions.filter { it.isFunction() }.groupBy { it.owner!! }
+ extensionProperties = extensions.filter { it.isProperty() }.groupBy { it.owner!! }
+ inheritedExtensionFunctions =
+ inheritedExtensions.filter { it.isFunction() }.groupBy { it.owner!! }
+ inheritedExtensionProperties =
+ inheritedExtensions.filter { it.isProperty() }.groupBy { it.owner!! }
}
- )
- fun generateClassesIndex(allTypesNode: DocumentationNode) = templateService.composePage(
- listOf(allTypesNode),
- htmlConsumer,
- headContent = {
+ val companionFunctions = node.members(NodeKind.CompanionObjectFunction).takeUnless { isCompanion }.orEmpty()
+ val companionProperties =
+ node.members(NodeKind.CompanionObjectProperty).takeUnless { isCompanion }.orEmpty() - constants
- },
- bodyContent = {
- h1 { +"Class Index" }
- fun DocumentationNode.classWithNestedClasses(): List<DocumentationNode> =
- members.filter { it.kind in classLike }.flatMap(DocumentationNode::classWithNestedClasses) + this
-
- val classesByFirstLetter = allTypesNode.members
- .filterNot { it.kind == NodeKind.ExternalClass }
- .flatMap(DocumentationNode::classWithNestedClasses)
- .groupBy {
- it.classNodeNameWithOuterClass().first().toString()
- }
- .entries
- .sortedBy { (letter) -> letter }
-
- ul {
- classesByFirstLetter.forEach { (letter) ->
- li { a(href = "#letter_$letter") { +letter } }
- }
- }
-
- classesByFirstLetter.forEach { (letter, nodes) ->
- h2 {
- id = "letter_$letter"
- +letter
- }
- table {
- tbody {
- for (node in nodes.sortedBy { it.classNodeNameWithOuterClass() }) {
- tr {
- td {
- a(href = uriProvider.linkTo(node, uri)) { +node.classNodeNameWithOuterClass() }
- }
- td {
- metaMarkup(node.content)
- }
- }
- }
- }
- }
- }
- }
- )
-
- fun generatePackageIndex(nodes: List<DocumentationNode>) = templateService.composePage(nodes,
- htmlConsumer,
- headContent = {
-
- },
- bodyContent = {
- h1 { +"Package Index" }
- table {
- tbody {
- for (node in nodes.sortedBy { it.name }) {
- tr {
- td {
- a(href = uriProvider.linkTo(node, uri)) { +node.name }
- }
- td {
- metaMarkup(node.content)
- }
- }
- }
- }
- }
- }
- )
-
- private fun FlowContent.fullDocs(
- nodes: List<DocumentationNode>,
- header: String,
- renderNode: FlowContent.(DocumentationNode) -> Unit
- ) {
- if (nodes.none()) return
- h2 {
- +header
}
- for (node in nodes) {
- renderNode(node)
- }
- }
-}
-class ContentToHtmlBuilder(val uriProvider: JavaLayoutHtmlUriProvider, val uri: URI) {
- fun FlowContent.appendContent(content: List<ContentNode>): Unit = content.forEach { appendContent(it) }
+ class PackagePage(val node: DocumentationNode) : Page() {
- private fun FlowContent.hN(level: Int, classes: String? = null, block: CommonAttributeGroupFacadeFlowHeadingPhrasingContent.() -> Unit) {
- when (level) {
- 1 -> h1(classes, block)
- 2 -> h2(classes, block)
- 3 -> h3(classes, block)
- 4 -> h4(classes, block)
- 5 -> h5(classes, block)
- 6 -> h6(classes, block)
- }
- }
-
- fun FlowContent.appendContent(content: ContentNode) {
- when (content) {
- is ContentText -> +content.text
- is ContentSymbol -> span("symbol") { +content.text }
- is ContentKeyword -> span("keyword") { +content.text }
- is ContentIdentifier -> span("identifier") {
- content.signature?.let { id = it }
- +content.text
+ init {
+ assert(node.kind == NodeKind.Package)
}
- is ContentHeading -> hN(level = content.level) { appendContent(content.children) }
+ val classes = node.members(NodeKind.Class)
+ val exceptions = node.members(NodeKind.Exception)
+ val typeAliases = node.members(NodeKind.TypeAlias)
+ val annotations = node.members(NodeKind.AnnotationClass)
+ val enums = node.members(NodeKind.Enum)
- is ContentEntity -> +content.text
+ private val externalClassExtensionFunctions =
+ node.members(NodeKind.ExternalClass).flatMap { it.members(NodeKind.Function) }
+ private val externalClassExtensionProperties =
+ node.members(NodeKind.ExternalClass).flatMap { it.members(NodeKind.Property) }
- is ContentStrong -> strong { appendContent(content.children) }
- is ContentStrikethrough -> del { appendContent(content.children) }
- is ContentEmphasis -> em { appendContent(content.children) }
+ val constants = node.members(NodeKind.Property).filter { it.constantValue() != null }
- is ContentOrderedList -> ol { appendContent(content.children) }
- is ContentUnorderedList -> ul { appendContent(content.children) }
- is ContentListItem -> consumer.li {
- (content.children.singleOrNull() as? ContentParagraph)
- ?.let { paragraph -> appendContent(paragraph.children) }
- ?: appendContent(content.children)
- }
+ val functions = node.members(NodeKind.Function) + externalClassExtensionFunctions
+ val properties = node.members(NodeKind.Property) - constants + externalClassExtensionProperties
- is ContentCode -> pre { code { appendContent(content.children) } }
- is ContentBlockSampleCode -> pre { code {} }
- is ContentBlockCode -> pre { code {} }
-
-
- is ContentNonBreakingSpace -> +nbsp
- is ContentSoftLineBreak, is ContentIndentedSoftLineBreak -> {
- }
-
- is ContentParagraph -> p { appendContent(content.children) }
-
- is ContentNodeLink -> {
- a(href = content.node?.let { uriProvider.linkTo(it, uri) }
- ?: "#unresolved") { appendContent(content.children) }
- }
- is ContentExternalLink -> {
- a(href = content.href) { appendContent(content.children) }
- }
-
- is ContentBlock -> appendContent(content.children)
}
}
}
class JavaLayoutHtmlFormatOutputBuilderFactoryImpl @Inject constructor(
- val uriProvider: JavaLayoutHtmlUriProvider,
- val languageService: LanguageService,
- val templateService: JavaLayoutHtmlTemplateService,
- val logger: DokkaLogger
+ val uriProvider: JavaLayoutHtmlUriProvider,
+ val languageService: LanguageService,
+ val templateService: JavaLayoutHtmlTemplateService,
+ val logger: DokkaLogger
) : JavaLayoutHtmlFormatOutputBuilderFactory {
override fun createOutputBuilder(output: Appendable, node: DocumentationNode): JavaLayoutHtmlFormatOutputBuilder {
return createOutputBuilder(output, uriProvider.mainUri(node))
@@ -542,4 +785,9 @@
override fun createOutputBuilder(output: Appendable, uri: URI): JavaLayoutHtmlFormatOutputBuilder {
return JavaLayoutHtmlFormatOutputBuilder(output, languageService, uriProvider, templateService, logger, uri)
}
-}
\ No newline at end of file
+}
+
+fun DocumentationNode.constantValue(): String? =
+ detailOrNull(NodeKind.Value)?.name.takeIf {
+ kind == NodeKind.Property || kind == NodeKind.CompanionObjectProperty
+ }
\ No newline at end of file
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt
index 63668e7..270c912 100644
--- a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlGenerator.kt
@@ -3,6 +3,7 @@
import com.google.inject.Inject
import com.google.inject.name.Named
import org.jetbrains.dokka.*
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatOutputBuilder.Page
import org.jetbrains.dokka.NodeKind.Companion.classLike
import org.jetbrains.kotlin.preprocessor.mkdirsOrFail
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
@@ -13,10 +14,9 @@
class JavaLayoutHtmlFormatGenerator @Inject constructor(
@Named("outputDir") val root: File,
- val languageService: LanguageService,
- val templateService: JavaLayoutHtmlTemplateService,
val packageListService: PackageListService,
val outputBuilderFactoryService: JavaLayoutHtmlFormatOutputBuilderFactory,
+ private val options: DocumentationOptions,
val logger: DokkaLogger
) : Generator, JavaLayoutHtmlUriProvider {
@@ -63,7 +63,7 @@
fun buildClass(node: DocumentationNode, parentDir: File) {
val fileForClass = parentDir.resolve(node.classNodeNameWithOuterClass() + ".html")
fileForClass.bufferedWriter().use {
- createOutputBuilderForNode(node, it).appendClassLike(node)
+ createOutputBuilderForNode(node, it).generatePage(Page.ClassPage(node))
}
for (memberClass in node.members.filter { it.kind in NodeKind.classLike }) {
buildClass(memberClass, parentDir)
@@ -77,7 +77,7 @@
directoryForPackage.mkdirsOrFail()
directoryForPackage.resolve("package-summary.html").bufferedWriter().use {
- createOutputBuilderForNode(node, it).appendPackage(node)
+ createOutputBuilderForNode(node, it).generatePage(Page.PackagePage(node))
}
members.filter { it.kind in NodeKind.classLike }.forEach {
@@ -88,15 +88,16 @@
fun buildClassIndex(node: DocumentationNode, parentDir: File) {
val file = parentDir.resolve("classes.html")
file.bufferedWriter().use {
- createOutputBuilderForNode(node, it).generateClassesIndex(node)
+ createOutputBuilderForNode(node, it).generatePage(Page.ClassIndex(node))
}
}
- fun buildPackageIndex(nodes: List<DocumentationNode>, parentDir: File) {
+ fun buildPackageIndex(module: DocumentationNode, nodes: List<DocumentationNode>, parentDir: File) {
val file = parentDir.resolve("packages.html")
file.bufferedWriter().use {
- outputBuilderFactoryService.createOutputBuilder(it, containerUri(nodes.first().owner!!).resolve("packages.html"))
- .generatePackageIndex(nodes)
+ val uri = containerUri(module).resolve("packages.html")
+ outputBuilderFactoryService.createOutputBuilder(it, uri)
+ .generatePage(Page.PackageIndex(nodes))
}
}
@@ -107,8 +108,10 @@
val packages = module.members.filter { it.kind == NodeKind.Package }
packages.forEach { buildPackage(it, moduleRoot) }
- buildClassIndex(module.members.single { it.kind == NodeKind.AllTypes }, moduleRoot)
- buildPackageIndex(packages, moduleRoot)
+ if (options.generateIndexPages) {
+ buildClassIndex(module.members.single { it.kind == NodeKind.AllTypes }, moduleRoot)
+ buildPackageIndex(module, packages, moduleRoot)
+ }
}
override fun buildOutlines(nodes: Iterable<DocumentationNode>) {
diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt
index 09e5ced..33170f3 100644
--- a/core/src/main/kotlin/Generation/DokkaGenerator.kt
+++ b/core/src/main/kotlin/Generation/DokkaGenerator.kt
@@ -160,6 +160,8 @@
with(injector.getInstance(DocumentationBuilder::class.java)) {
documentationModule.appendFragments(fragments, packageDocs.packageContent,
injector.getInstance(PackageDocumentationBuilder::class.java))
+
+ propagateExtensionFunctionsToSubclasses(fragments, resolutionFacade)
}
val javaFiles = coreEnvironment.getJavaSourceFiles().filter(filesToDocumentFilter)
diff --git a/core/src/main/kotlin/Generation/configurationImpl.kt b/core/src/main/kotlin/Generation/configurationImpl.kt
index 34d4154..52e8446 100644
--- a/core/src/main/kotlin/Generation/configurationImpl.kt
+++ b/core/src/main/kotlin/Generation/configurationImpl.kt
@@ -36,27 +36,28 @@
override val suppress: Boolean = false) : DokkaConfiguration.PackageOptions
data class DokkaConfigurationImpl(
- override val moduleName: String,
- override val classpath: List<String>,
- override val sourceRoots: List<SourceRootImpl>,
- override val samples: List<String>,
- override val includes: List<String>,
- override val outputDir: String,
- override val format: String,
- override val includeNonPublic: Boolean,
- override val includeRootPackage: Boolean,
- override val reportUndocumented: Boolean,
- override val skipEmptyPackages: Boolean,
- override val skipDeprecated: Boolean,
- override val jdkVersion: Int,
- override val generateIndexPages: Boolean,
- override val sourceLinks: List<SourceLinkDefinitionImpl>,
- override val impliedPlatforms: List<String>,
- override val perPackageOptions: List<PackageOptionsImpl>,
- override val externalDocumentationLinks: List<ExternalDocumentationLinkImpl>,
- override val noStdlibLink: Boolean,
- override val cacheRoot: String?,
- override val suppressedFiles: List<String>,
- override val languageVersion: String?,
- override val apiVersion: String?
+ override val moduleName: String,
+ override val classpath: List<String>,
+ override val sourceRoots: List<SourceRootImpl>,
+ override val samples: List<String>,
+ override val includes: List<String>,
+ override val outputDir: String,
+ override val format: String,
+ override val includeNonPublic: Boolean,
+ override val includeRootPackage: Boolean,
+ override val reportUndocumented: Boolean,
+ override val skipEmptyPackages: Boolean,
+ override val skipDeprecated: Boolean,
+ override val jdkVersion: Int,
+ override val generateIndexPages: Boolean,
+ override val sourceLinks: List<SourceLinkDefinitionImpl>,
+ override val impliedPlatforms: List<String>,
+ override val perPackageOptions: List<PackageOptionsImpl>,
+ override val externalDocumentationLinks: List<ExternalDocumentationLinkImpl>,
+ override val noStdlibLink: Boolean,
+ override val cacheRoot: String?,
+ override val suppressedFiles: List<String>,
+ override val languageVersion: String?,
+ override val apiVersion: String?,
+ override val collectInheritedExtensionsFromLibraries: Boolean
) : DokkaConfiguration
\ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
index dd29cfc..88f517b 100644
--- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
@@ -2,6 +2,7 @@
import com.google.inject.Inject
import com.intellij.openapi.util.text.StringUtil
+import com.intellij.psi.PsiField
import com.intellij.psi.PsiJavaFile
import org.jetbrains.dokka.DokkaConfiguration.*
import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser
@@ -11,6 +12,9 @@
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
import org.jetbrains.kotlin.idea.kdoc.findKDoc
+import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
+import org.jetbrains.kotlin.idea.util.makeNotNullable
+import org.jetbrains.kotlin.idea.util.toFuzzyType
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
import org.jetbrains.kotlin.lexer.KtTokens
@@ -18,15 +22,17 @@
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.psi.KtParameter
+import org.jetbrains.kotlin.psi.KtVariableDeclaration
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
+import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
+import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
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 java.io.File
import java.nio.file.Path
@@ -51,7 +57,8 @@
val languageVersion: String?,
val apiVersion: String?,
cacheRoot: String? = null,
- val suppressedFiles: List<File> = emptyList()) {
+ val suppressedFiles: List<File> = emptyList(),
+ val collectInheritedExtensionsFromLibraries: Boolean = false) {
init {
if (perPackageOptions.any { it.prefix == "" })
throw IllegalArgumentException("Please do not register packageOptions with all match pattern, use global settings instead")
@@ -101,6 +108,10 @@
fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String>
}
+val ignoredSupertypes = setOf(
+ "kotlin.Annotation", "kotlin.Enum", "kotlin.Any"
+)
+
class DocumentationBuilder
@Inject constructor(val resolutionFacade: DokkaResolutionFacade,
val descriptorDocumentationParser: DescriptorDocumentationParser,
@@ -132,8 +143,20 @@
refGraph.register(descriptor.signature(), node)
}
- fun <T> nodeForDescriptor(descriptor: T, kind: NodeKind): DocumentationNode where T : DeclarationDescriptor, T : Named {
- val (doc, callback) = descriptorDocumentationParser.parseDocumentationAndDetails(descriptor, kind == NodeKind.Parameter)
+ fun <T> nodeForDescriptor(
+ descriptor: T,
+ kind: NodeKind,
+ external: Boolean = false
+ ): DocumentationNode where T : DeclarationDescriptor, T : Named {
+ val (doc, callback) =
+ if (external) {
+ Content.Empty to { node -> }
+ } else {
+ descriptorDocumentationParser.parseDocumentationAndDetails(
+ descriptor,
+ kind == NodeKind.Parameter
+ )
+ }
val node = DocumentationNode(descriptor.name.asString(), doc, kind).withModifiers(descriptor)
node.appendSignature(descriptor)
callback(node)
@@ -171,7 +194,7 @@
val unwrappedType = superType.unwrap()
if (unwrappedType is AbbreviatedType) {
appendSupertype(descriptor, unwrappedType.abbreviation)
- } else if (!ignoreSupertype(unwrappedType)) {
+ } else {
appendType(unwrappedType, NodeKind.Supertype)
val superclass = unwrappedType.constructor.declarationDescriptor
link(superclass, descriptor, RefKind.Inheritor)
@@ -179,15 +202,6 @@
}
}
- private fun ignoreSupertype(superType: KotlinType): Boolean {
- val superClass = superType.constructor.declarationDescriptor as? ClassDescriptor
- if (superClass != null) {
- val fqName = DescriptorUtils.getFqNameSafe(superClass).asString()
- return fqName == "kotlin.Annotation" || fqName == "kotlin.Enum" || fqName == "kotlin.Any"
- }
- return false
- }
-
fun DocumentationNode.appendProjection(projection: TypeProjection, kind: NodeKind = NodeKind.Type) {
if (projection.isStarProjection) {
appendTextNode("*", NodeKind.Type)
@@ -229,16 +243,33 @@
node.appendTextNode("?", NodeKind.NullabilityModifier)
}
if (classifierDescriptor != null) {
- val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
+ val externalLink =
+ linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
if (externalLink != null) {
- node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
- node.append(DocumentationNode(classifierDescriptor.fqNameUnsafe.asString(), Content.Empty, NodeKind.QualifiedName), RefKind.Detail)
+ if (classifierDescriptor !is TypeParameterDescriptor) {
+ val targetNode =
+ refGraph.lookup(classifierDescriptor.signature()) ?: classifierDescriptor.build(true)
+ node.append(targetNode, RefKind.ExternalType)
+ node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
} else {
- link(node, classifierDescriptor,
- if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link)
+ link(
+ node, classifierDescriptor,
+ if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link
+ )
+ }
+ if (classifierDescriptor !is TypeParameterDescriptor) {
+ node.append(
+ DocumentationNode(
+ classifierDescriptor.fqNameUnsafe.asString(),
+ Content.Empty,
+ NodeKind.QualifiedName
+ ), RefKind.Detail
+ )
}
}
+
append(node, RefKind.Detail)
node.appendAnnotations(kotlinType)
for (typeArgument in kotlinType.arguments) {
@@ -272,6 +303,17 @@
}
}
+ fun DocumentationNode.appendExternalLink(externalLink: String) {
+ append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
+
+ fun DocumentationNode.appendExternalLink(descriptor: DeclarationDescriptor) {
+ val target = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(descriptor)
+ if (target != null) {
+ appendExternalLink(target)
+ }
+ }
+
fun DocumentationNode.appendSinceKotlin(annotation: DocumentationNode) {
val kotlinVersion = annotation
.detail(NodeKind.Parameter)
@@ -418,15 +460,44 @@
declarations, allFqNames)
}
- propagateExtensionFunctionsToSubclasses(fragments)
}
- private fun propagateExtensionFunctionsToSubclasses(fragments: Collection<PackageFragmentDescriptor>) {
- val allDescriptors = fragments.flatMap { it.getMemberScope().getContributedDescriptors() }
- val allClasses = allDescriptors.filterIsInstance<ClassDescriptor>()
- val classHierarchy = buildClassHierarchy(allClasses)
+ fun propagateExtensionFunctionsToSubclasses(
+ fragments: Collection<PackageFragmentDescriptor>,
+ resolutionFacade: DokkaResolutionFacade
+ ) {
- val allExtensionFunctions = allDescriptors
+ val moduleDescriptor = resolutionFacade.moduleDescriptor
+
+ // Wide-collect all view descriptors
+ val allPackageViewDescriptors = generateSequence(listOf(moduleDescriptor.getPackage(FqName.ROOT))) { packages ->
+ packages
+ .flatMap { pkg ->
+ moduleDescriptor.getSubPackagesOf(pkg.fqName) { true }
+ }.map { fqName ->
+ moduleDescriptor.getPackage(fqName)
+ }.takeUnless { it.isEmpty() }
+ }.flatten()
+
+ val allDescriptors =
+ if (options.collectInheritedExtensionsFromLibraries) {
+ allPackageViewDescriptors.map { it.memberScope }
+ } else {
+ fragments.asSequence().map { it.getMemberScope() }
+ }.flatMap {
+ it.getDescriptorsFiltered(
+ DescriptorKindFilter.CALLABLES
+ ).asSequence()
+ }
+
+
+ val documentingDescriptors = fragments.flatMap { it.getMemberScope().getContributedDescriptors() }
+ val documentingClasses = documentingDescriptors.filterIsInstance<ClassDescriptor>()
+
+ val classHierarchy = buildClassHierarchy(documentingClasses)
+
+ val allExtensionFunctions =
+ allDescriptors
.filterIsInstance<CallableMemberDescriptor>()
.filter { it.extensionReceiverParameter != null }
val extensionFunctionsByName = allExtensionFunctions.groupBy { it.name }
@@ -434,15 +505,31 @@
for (extensionFunction in allExtensionFunctions) {
if (extensionFunction.dispatchReceiverParameter != null) continue
val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name]
- ?.filter { fn -> fn.canShadow(extensionFunction) }
+ ?.filter { fn -> fn.canShadow(extensionFunction) }
?: emptyList()
if (extensionFunction.extensionReceiverParameter?.type?.isDynamic() == true) continue
- val classDescriptor = extensionFunction.getExtensionClassDescriptor() ?: continue
- val subclasses = classHierarchy[classDescriptor] ?: continue
- subclasses.forEach { subclass ->
+ val subclasses =
+ classHierarchy.filter { (key) -> key.isExtensionApplicable(extensionFunction) }
+ if (subclasses.isEmpty()) continue
+ subclasses.values.flatten().forEach { subclass ->
if (subclass.isExtensionApplicable(extensionFunction) &&
- possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) {
+ possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) {
+
+ val hasExternalLink =
+ linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(
+ extensionFunction
+ ) != null
+ if (hasExternalLink) {
+ val containerDesc =
+ extensionFunction.containingDeclaration as? PackageFragmentDescriptor
+ if (containerDesc != null) {
+ val container = refGraph.lookup(containerDesc.signature())
+ ?: containerDesc.buildExternal()
+ container.append(extensionFunction.buildExternal(), RefKind.Member)
+ }
+ }
+
refGraph.link(subclass.signature(), extensionFunction.signature(), RefKind.Extension)
}
}
@@ -450,12 +537,9 @@
}
private fun ClassDescriptor.isExtensionApplicable(extensionFunction: CallableMemberDescriptor): Boolean {
- val receiverType = extensionFunction.extensionReceiverParameter!!.type
- if (receiverType.arguments.any { it.type.constructor.declarationDescriptor is TypeParameterDescriptor }) {
- val receiverClass = receiverType.constructor.declarationDescriptor
- return receiverClass is ClassDescriptor && DescriptorUtils.isSubclass(this, receiverClass)
- }
- return defaultType.isSubtypeOf(receiverType)
+ val receiverType = extensionFunction.fuzzyExtensionReceiverType()?.makeNotNullable()
+ val classType = defaultType.toFuzzyType(declaredTypeParameters)
+ return receiverType != null && classType.checkIsSubtypeOf(receiverType) != null
}
private fun buildClassHierarchy(classes: List<ClassDescriptor>): Map<ClassDescriptor, List<ClassDescriptor>> {
@@ -494,34 +578,60 @@
}
fun DeclarationDescriptor.build(): DocumentationNode = when (this) {
- is ClassDescriptor -> build()
+ is ClassifierDescriptor -> build()
is ConstructorDescriptor -> build()
is PropertyDescriptor -> build()
is FunctionDescriptor -> build()
- is TypeParameterDescriptor -> build()
is ValueParameterDescriptor -> build()
is ReceiverParameterDescriptor -> build()
- is TypeAliasDescriptor -> build()
else -> throw IllegalStateException("Descriptor $this is not known")
}
- fun TypeAliasDescriptor.build(): DocumentationNode {
+ fun PackageFragmentDescriptor.buildExternal(): DocumentationNode {
+ val node = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.Package)
+
+ val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(this)
+ if (externalLink != null) {
+ node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ }
+ register(this, node)
+ return node
+ }
+
+ fun CallableDescriptor.buildExternal(): DocumentationNode = when(this) {
+ is FunctionDescriptor -> build(true)
+ is PropertyDescriptor -> build(true)
+ else -> throw IllegalStateException("Descriptor $this is not known")
+ }
+
+
+ fun ClassifierDescriptor.build(external: Boolean = false): DocumentationNode = when (this) {
+ is ClassDescriptor -> build(external)
+ is TypeAliasDescriptor -> build(external)
+ is TypeParameterDescriptor -> build()
+ else -> throw IllegalStateException("Descriptor $this is not known")
+ }
+
+ fun TypeAliasDescriptor.build(external: Boolean = false): DocumentationNode {
val node = nodeForDescriptor(this, NodeKind.TypeAlias)
- node.appendAnnotations(this)
+ if (!external) {
+ node.appendAnnotations(this)
+ }
node.appendModifiers(this)
node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail)
node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType)
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
-
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ }
register(this, node)
return node
}
- fun ClassDescriptor.build(): DocumentationNode {
+ fun ClassDescriptor.build(external: Boolean = false): DocumentationNode {
val kind = when {
kind == ClassKind.OBJECT -> NodeKind.Object
kind == ClassKind.INTERFACE -> NodeKind.Interface
@@ -531,21 +641,25 @@
isSubclassOfThrowable() -> NodeKind.Exception
else -> NodeKind.Class
}
- val node = nodeForDescriptor(this, kind)
+ val node = nodeForDescriptor(this, kind, external)
+ register(this, node)
typeConstructor.supertypes.forEach {
node.appendSupertype(this, it)
}
if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) {
node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail)
}
- for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) {
- node.appendClassMember(descriptor, inheritedLinkKind, extraModifier)
+ if (!external) {
+ for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) {
+ node.appendClassMember(descriptor, inheritedLinkKind, extraModifier)
+ }
+ node.appendAnnotations(this)
}
- node.appendAnnotations(this)
node.appendModifiers(this)
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
- register(this, node)
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ }
return node
}
@@ -612,12 +726,12 @@
return (receiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor)?.isCompanionObject ?: false
}
- fun FunctionDescriptor.build(): DocumentationNode {
+ fun FunctionDescriptor.build(external: Boolean = false): DocumentationNode {
if (ErrorUtils.containsErrorType(this)) {
logger.warn("Found an unresolved type in ${signatureWithSourceLocation()}")
}
- val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function)
+ val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function, external)
node.appendInPageChildren(typeParameters, RefKind.Detail)
extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
@@ -625,8 +739,12 @@
node.appendType(returnType)
node.appendAnnotations(this)
node.appendModifiers(this)
- node.appendSourceLink(source)
- node.appendDefaultPlatforms(this)
+ if (!external) {
+ node.appendSourceLink(source)
+ node.appendDefaultPlatforms(this)
+ } else {
+ node.appendExternalLink(this)
+ }
overriddenDescriptors.forEach {
addOverrideLink(it, this)
@@ -647,32 +765,53 @@
}
}
- fun PropertyDescriptor.build(): DocumentationNode {
- val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property)
+ fun PropertyDescriptor.build(external: Boolean = false): DocumentationNode {
+ val node = nodeForDescriptor(
+ this,
+ if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property,
+ external
+ )
node.appendInPageChildren(typeParameters, RefKind.Detail)
extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) }
node.appendType(returnType)
node.appendAnnotations(this)
node.appendModifiers(this)
- node.appendSourceLink(source)
- if (isVar) {
- node.appendTextNode("var", NodeKind.Modifier)
- }
- getter?.let {
- if (!it.isDefault) {
- node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter")
+ if (!external) {
+ node.appendSourceLink(source)
+ if (isVar) {
+ node.appendTextNode("var", NodeKind.Modifier)
}
- }
- setter?.let {
- if (!it.isDefault) {
- node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter")
+
+ if (isConst) {
+ val psi = sourcePsi()
+ val valueText = when (psi) {
+ is KtVariableDeclaration -> psi.initializer?.text
+ is PsiField -> psi.initializer?.text
+ else -> null
+ }
+ valueText?.let { node.appendTextNode(it, NodeKind.Value) }
}
+
+
+ getter?.let {
+ if (!it.isDefault) {
+ node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter")
+ }
+ }
+ setter?.let {
+ if (!it.isDefault) {
+ node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter")
+ }
+ }
+ node.appendDefaultPlatforms(this)
+ }
+ if (external) {
+ node.appendExternalLink(this)
}
overriddenDescriptors.forEach {
addOverrideLink(it, this)
}
- node.appendDefaultPlatforms(this)
register(this, node)
return node
diff --git a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
index 108cee7..330e76a 100644
--- a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
+++ b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt
@@ -154,8 +154,8 @@
fun buildExternalDocumentationLink(symbol: DeclarationDescriptor): String? {
val packageFqName: FqName =
when (symbol) {
- is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
is PackageFragmentDescriptor -> symbol.fqName
+ is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null
else -> return null
}
diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
index b6f4471..aa185de 100644
--- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
+++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
@@ -291,7 +291,7 @@
}
private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) {
- val supertypes = node.details(NodeKind.Supertype)
+ val supertypes = node.details(NodeKind.Supertype).filterNot { it.qualifiedNameFromType() in ignoredSupertypes }
if (supertypes.any()) {
nbsp()
symbol(":")
diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt
index a145ae8..85ecdf8 100644
--- a/core/src/main/kotlin/Model/DocumentationNode.kt
+++ b/core/src/main/kotlin/Model/DocumentationNode.kt
@@ -104,12 +104,27 @@
get() = references(RefKind.Deprecation).singleOrNull()?.to
val platforms: List<String>
get() = references(RefKind.Platform).map { it.to.name }
+ val externalType: DocumentationNode?
+ get() = references(RefKind.ExternalType).map { it.to }.firstOrNull()
val supertypes: List<DocumentationNode>
get() = details(NodeKind.Supertype)
- val superclass: DocumentationNode?
- get() = supertypes.firstOrNull { it.links.any { it.kind in NodeKind.classLike } }
+ val superclassType: DocumentationNode?
+ get() = when (kind) {
+ NodeKind.Supertype -> (links.firstOrNull { it.kind in NodeKind.classLike } ?: externalType)?.superclassType
+ NodeKind.Interface -> null
+ in NodeKind.classLike -> supertypes.firstOrNull {
+ it.links.any { it.kind in NodeKind.classLike } ||
+ it.externalType != null
+ }
+ else -> null
+ }
+
+ val superclassTypeSequence: Sequence<DocumentationNode>
+ get() = generateSequence(superclassType) {
+ it.superclassType
+ }
// 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) {
@@ -194,6 +209,8 @@
fun DocumentationNode.qualifiedName(): String {
if (kind == NodeKind.Type) {
return qualifiedNameFromType()
+ } else if (kind == NodeKind.Package) {
+ return name
}
return path.drop(1).map { it.name }.filter { it.length > 0 }.joinToString(".")
}
diff --git a/core/src/main/kotlin/Model/DocumentationReference.kt b/core/src/main/kotlin/Model/DocumentationReference.kt
index a968f40..b0f011b 100644
--- a/core/src/main/kotlin/Model/DocumentationReference.kt
+++ b/core/src/main/kotlin/Model/DocumentationReference.kt
@@ -18,7 +18,8 @@
HiddenAnnotation,
Deprecation,
TopLevelPage,
- Platform
+ Platform,
+ ExternalType
}
data class DocumentationReference(val from: DocumentationNode, val to: DocumentationNode, val kind: RefKind) {
diff --git a/core/src/test/kotlin/TestAPI.kt b/core/src/test/kotlin/TestAPI.kt
index aa3eff4..aeff928 100644
--- a/core/src/test/kotlin/TestAPI.kt
+++ b/core/src/test/kotlin/TestAPI.kt
@@ -6,7 +6,6 @@
import com.intellij.openapi.util.io.FileUtil
import com.intellij.rt.execution.junit.FileComparisonFailure
import org.jetbrains.dokka.*
-import org.jetbrains.dokka.DokkaConfiguration.SourceLinkDefinition
import org.jetbrains.dokka.Utilities.DokkaAnalysisModule
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
@@ -25,22 +24,25 @@
format: String = "html",
includeNonPublic: Boolean = true,
perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(),
+ noStdlibLink: Boolean = true,
+ collectInheritedExtensionsFromLibraries: Boolean = false,
verifier: (DocumentationModule) -> Unit) {
val documentation = DocumentationModule("test")
val options = DocumentationOptions(
- "",
- format,
- includeNonPublic = includeNonPublic,
- skipEmptyPackages = false,
- includeRootPackage = true,
- sourceLinks = listOf(),
- perPackageOptions = perPackageOptions,
- generateIndexPages = false,
- noStdlibLink = true,
- cacheRoot = "default",
- languageVersion = null,
- apiVersion = null
+ "",
+ format,
+ includeNonPublic = includeNonPublic,
+ skipEmptyPackages = false,
+ includeRootPackage = true,
+ sourceLinks = listOf(),
+ perPackageOptions = perPackageOptions,
+ generateIndexPages = false,
+ noStdlibLink = noStdlibLink,
+ cacheRoot = "default",
+ languageVersion = null,
+ apiVersion = null,
+ collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
)
appendDocumentation(documentation, *roots,
@@ -161,13 +163,17 @@
withKotlinRuntime: Boolean = false,
format: String = "html",
includeNonPublic: Boolean = true,
+ noStdlibLink: Boolean = true,
+ collectInheritedExtensionsFromLibraries: Boolean = false,
outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
verifyModel(
- *roots,
- withJdk = withJdk,
- withKotlinRuntime = withKotlinRuntime,
- format = format,
- includeNonPublic = includeNonPublic
+ *roots,
+ withJdk = withJdk,
+ withKotlinRuntime = withKotlinRuntime,
+ format = format,
+ includeNonPublic = includeNonPublic,
+ noStdlibLink = noStdlibLink,
+ collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
) {
verifyModelOutput(it, outputExtension, roots.first().path, outputGenerator)
}
@@ -184,21 +190,27 @@
assertEqualsIgnoringSeparators(expectedFile, output.toString())
}
-fun verifyOutput(path: String,
- outputExtension: String,
- withJdk: Boolean = false,
- withKotlinRuntime: Boolean = false,
- format: String = "html",
- includeNonPublic: Boolean = true,
- outputGenerator: (DocumentationModule, StringBuilder) -> Unit) {
+fun verifyOutput(
+ path: String,
+ outputExtension: String,
+ withJdk: Boolean = false,
+ withKotlinRuntime: Boolean = false,
+ format: String = "html",
+ includeNonPublic: Boolean = true,
+ noStdlibLink: Boolean = true,
+ collectInheritedExtensionsFromLibraries: Boolean = false,
+ outputGenerator: (DocumentationModule, StringBuilder) -> Unit
+) {
verifyOutput(
- arrayOf(contentRootFromPath(path)),
- outputExtension,
- withJdk,
- withKotlinRuntime,
- format,
- includeNonPublic,
- outputGenerator
+ arrayOf(contentRootFromPath(path)),
+ outputExtension,
+ withJdk,
+ withKotlinRuntime,
+ format,
+ includeNonPublic,
+ noStdlibLink,
+ collectInheritedExtensionsFromLibraries,
+ outputGenerator
)
}
diff --git a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt
new file mode 100644
index 0000000..160b3cb
--- /dev/null
+++ b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTest.kt
@@ -0,0 +1,58 @@
+package org.jetbrains.dokka.tests
+
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptor
+import org.jetbrains.dokka.NodeKind
+import org.junit.Test
+
+class JavaLayoutHtmlFormatTest : JavaLayoutHtmlFormatTestCase() {
+ override val formatDescriptor = JavaLayoutHtmlFormatDescriptor()
+
+ @Test
+ fun simple() {
+ verifyNode("simple.kt")
+ }
+
+ @Test
+ fun topLevel() {
+ verifyPackageNode("topLevel.kt")
+ }
+
+ @Test
+ fun codeBlocks() {
+ verifyNode("codeBlocks.kt") { model ->
+ listOf(model.members.single().members.single { it.name == "foo" })
+ }
+ }
+
+ @Test
+ fun const() {
+ verifyPackageNode("const.kt", noStdlibLink = true)
+ verifyNode("const.kt", noStdlibLink = true) { model ->
+ model.members.single().members.filter { it.kind in NodeKind.classLike }
+ }
+ }
+
+ @Test
+ fun externalClassExtension() {
+ verifyPackageNode("externalClassExtension.kt")
+ }
+
+ @Test
+ fun unresolvedExternalClass() {
+ verifyNode("unresolvedExternalClass.kt", noStdlibLink = true) { model ->
+ listOf(model.members.single().members.single { it.name == "MyException" })
+ }
+ }
+
+ @Test
+ fun genericExtension() {
+ verifyNode("genericExtension.kt", noStdlibLink = true) { model ->
+ model.members.single().members(NodeKind.Class)
+ }
+ }
+
+ @Test
+ fun constJava() {
+ verifyNode("ConstJava.java", noStdlibLink = true)
+ }
+}
\ No newline at end of file
diff --git a/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt
new file mode 100644
index 0000000..50af8f2
--- /dev/null
+++ b/core/src/test/kotlin/format/JavaLayoutHtmlFormatTestCase.kt
@@ -0,0 +1,115 @@
+package org.jetbrains.dokka.tests
+
+import com.google.inject.Guice
+import com.google.inject.Injector
+import com.google.inject.Module
+import com.google.inject.name.Names
+import org.jetbrains.dokka.DocumentationNode
+import org.jetbrains.dokka.DocumentationOptions
+import org.jetbrains.dokka.DokkaLogger
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatDescriptorBase
+import org.jetbrains.dokka.Formats.JavaLayoutHtmlFormatGenerator
+import org.jetbrains.dokka.Generator
+import org.jetbrains.dokka.Utilities.bind
+import org.junit.Rule
+import org.junit.rules.TemporaryFolder
+import java.io.File
+import java.net.URI
+
+abstract class JavaLayoutHtmlFormatTestCase {
+
+ abstract val formatDescriptor: JavaLayoutHtmlFormatDescriptorBase
+
+ @get:Rule
+ var folder = TemporaryFolder()
+
+ var options =
+ DocumentationOptions(
+ "",
+ "java-layout-html",
+ apiVersion = null,
+ languageVersion = null,
+ generateIndexPages = false,
+ noStdlibLink = false,
+ collectInheritedExtensionsFromLibraries = true
+ )
+
+ val injector: Injector by lazy {
+ Guice.createInjector(Module { binder ->
+ binder.bind<File>().annotatedWith(Names.named("outputDir")).toInstance(folder.apply { create() }.root)
+
+ binder.bind<DocumentationOptions>().toProvider { options }
+ binder.bind<DokkaLogger>().toInstance(object : DokkaLogger {
+ override fun info(message: String) {
+ println(message)
+ }
+
+ override fun warn(message: String) {
+ println("WARN: $message")
+ }
+
+ override fun error(message: String) {
+ println("ERROR: $message")
+ }
+
+ })
+
+ formatDescriptor.configureOutput(binder)
+ })
+ }
+
+
+ protected fun buildPagesAndReadInto(model: DocumentationNode, nodes: List<DocumentationNode>, sb: StringBuilder) =
+ with(injector.getInstance(Generator::class.java)) {
+ this as JavaLayoutHtmlFormatGenerator
+ buildPages(listOf(model))
+ val byLocations = nodes.groupBy { mainUri(it) }
+ byLocations.forEach { (loc, _) ->
+ sb.appendln("<!-- File: $loc -->")
+ sb.append(folder.root.toURI().resolve(URI("/").relativize(loc)).toURL().readText())
+ }
+ }
+
+
+ protected fun verifyNode(
+ fileName: String,
+ noStdlibLink: Boolean = false,
+ fileExtension: String = ".html",
+ select: (model: DocumentationNode) -> List<DocumentationNode>
+ ) {
+ verifyOutput(
+ "testdata/format/java-layout-html/$fileName",
+ fileExtension,
+ format = "java-layout-html",
+ withKotlinRuntime = true,
+ noStdlibLink = noStdlibLink,
+ collectInheritedExtensionsFromLibraries = true
+ ) { model, output ->
+ buildPagesAndReadInto(
+ model,
+ select(model),
+ output
+ )
+ }
+ }
+
+ protected fun verifyNode(fileName: String, noStdlibLink: Boolean = false) {
+ verifyNode(fileName, noStdlibLink) { model -> listOf(model.members.single().members.single()) }
+ }
+
+ protected fun verifyPackageNode(fileName: String, noStdlibLink: Boolean = false) {
+ verifyOutput(
+ "testdata/format/java-layout-html/$fileName",
+ ".package-summary.html",
+ format = "java-layout-html",
+ withKotlinRuntime = true,
+ noStdlibLink = noStdlibLink
+ ) { model, output ->
+ buildPagesAndReadInto(
+ model,
+ listOf(model.members.single()),
+ output
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/test/kotlin/model/JavaTest.kt b/core/src/test/kotlin/model/JavaTest.kt
index e6c22ee..c2ede8f 100644
--- a/core/src/test/kotlin/model/JavaTest.kt
+++ b/core/src/test/kotlin/model/JavaTest.kt
@@ -193,7 +193,7 @@
@Test fun enumValues() {
verifyJavaPackageMember("testdata/java/enumValues.java") { cls ->
val superTypes = cls.details(NodeKind.Supertype)
- assertEquals(0, superTypes.size)
+ assertEquals(1, superTypes.size)
assertEquals(1, cls.members(NodeKind.EnumItem).size)
}
}
diff --git a/core/testdata/format/java-layout-html/ConstJava.html b/core/testdata/format/java-layout-html/ConstJava.html
new file mode 100644
index 0000000..26c9aa2
--- /dev/null
+++ b/core/testdata/format/java-layout-html/ConstJava.html
@@ -0,0 +1,93 @@
+<!-- File: /test/p/ConstJava.html# -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>ConstJava</h1>
+ <pre><span class="keyword">open</span> <span class="keyword">class </span><span class="identifier">ConstJava</span></pre>
+ <table>
+ <tr>
+ <td><a href="#">p.ConstJava</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constants</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><span class="keyword">static</span> <span class="identifier">String</span></td>
+ <td>
+ <div><code><a href="#myStringConst%3Akotlin.String">myStringConst</a></code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="keyword">static</span> <span class="identifier">Int</span></td>
+ <td>
+ <div><code><a href="#myIntConst%3Akotlin.Int">myIntConst</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constructors</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <div><code><a href="#%3Cinit%3E%28%29"><init></a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Properties</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><span class="keyword">static</span> <a href="#"><span class="identifier">ConstJava</span></a></td>
+ <td>
+ <div><code><a href="#myConstObjConst%3Ap.ConstJava">myConstObjConst</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Constants</h2>
+ <div id="myStringConst:kotlin.String">
+ <h3>myStringConst</h3>
+ <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myStringConst</span><span class="symbol">: </span><span class="identifier">String</span></pre>
+ <pre>Value: <code>""</code></pre>
+ </div>
+ <div id="myIntConst:kotlin.Int">
+ <h3>myIntConst</h3>
+ <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myIntConst</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+ <pre>Value: <code>0</code></pre>
+ </div>
+ <h2>Constructors</h2>
+ <div id="<init>()">
+ <h3><init></h3>
+ <pre><span class="identifier">ConstJava</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Properties</h2>
+ <div id="myConstObjConst:p.ConstJava">
+ <h3>myConstObjConst</h3>
+ <pre><span class="keyword">static</span> <span class="keyword">val </span><span class="identifier">myConstObjConst</span><span class="symbol">: </span><a href="#"><span class="identifier">ConstJava</span></a></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/ConstJava.java b/core/testdata/format/java-layout-html/ConstJava.java
new file mode 100644
index 0000000..eb5bb2b
--- /dev/null
+++ b/core/testdata/format/java-layout-html/ConstJava.java
@@ -0,0 +1,10 @@
+package p;
+
+
+public class ConstJava {
+
+ public static final String myStringConst = "";
+ public static final int myIntConst = 0;
+
+ public static final ConstJava myConstObjConst = new ConstJava(); // Not a constant, as it have not primitive type
+}
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/codeBlocks.html b/core/testdata/format/java-layout-html/codeBlocks.html
new file mode 100644
index 0000000..1d6bc6c
--- /dev/null
+++ b/core/testdata/format/java-layout-html/codeBlocks.html
@@ -0,0 +1,49 @@
+<!-- File: /test/p/package-summary.html#foo%28%29 -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>p</h1>
+ <h2>Top-level functions summary</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html"><span class="identifier">Int</span></a></td>
+ <td>
+ <div><code><a href="#foo%28%29">foo</a>()</code></div>
+ <p>See that <code>inline code</code> here</p>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+ <td>
+ <div><code><a href="#sample%28%29">sample</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level functions</h2>
+ <div id="foo()">
+ <h3>foo</h3>
+ <pre><span class="keyword">fun </span><span class="identifier">foo</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html"><span class="identifier">Int</span></a></pre>
+ <p>See that <code>inline code</code> here</p>
+ <p>Some full code-block</p>
+ <pre><code data-language="Kotlin">
+ println(foo()) // Prints 42
+ println(foo() - 10) // Prints 32
+</code></pre>
+ <p>Some indented code-block
+ fun ref() = foo()
+ val a = 2</p>
+ <pre><code data-language="kotlin">
+
+println(foo()) // Answer unlimate question of all
+println(foo() * 2) // 84!</code></pre>
+ </div>
+ <div id="sample()">
+ <h3>sample</h3>
+ <pre><span class="keyword">fun </span><span class="identifier">sample</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/codeBlocks.kt b/core/testdata/format/java-layout-html/codeBlocks.kt
new file mode 100644
index 0000000..de0c794
--- /dev/null
+++ b/core/testdata/format/java-layout-html/codeBlocks.kt
@@ -0,0 +1,26 @@
+package p
+
+/**
+ * See that `inline code` here
+ *
+ * Some full code-block
+ * ```Kotlin
+ * println(foo()) // Prints 42
+ * println(foo() - 10) // Prints 32
+ * ```
+ *
+ * Some indented code-block
+ * fun ref() = foo()
+ * val a = 2
+ *
+ * @sample p.sample
+ */
+fun foo(): Int {
+ return 42
+}
+
+
+fun sample() {
+ println(foo()) // Answer unlimate question of all
+ println(foo() * 2) // 84!
+}
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/const.html b/core/testdata/format/java-layout-html/const.html
new file mode 100644
index 0000000..89cb572
--- /dev/null
+++ b/core/testdata/format/java-layout-html/const.html
@@ -0,0 +1,99 @@
+<!-- File: /test/p/G.html# -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>G</h1>
+ <pre><span class="keyword">object </span><span class="identifier">G</span></pre>
+ <table>
+ <tr>
+ <td><a href="#">p.G</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constants</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><span class="keyword">const</span> <span class="identifier">Int</span></td>
+ <td>
+ <div><code><a href="#y%3Akotlin.Int">y</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Constants</h2>
+ <div id="y:kotlin.Int">
+ <h3>y</h3>
+ <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">y</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+ <pre>Value: <code>0</code></pre>
+ </div>
+ </body>
+</html>
+<!-- File: /test/p/D.html# -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>D</h1>
+ <pre><span class="keyword">class </span><span class="identifier">D</span></pre>
+ <table>
+ <tr>
+ <td><a href="#">p.D</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constants</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><span class="keyword">const</span> <span class="identifier">Int</span></td>
+ <td>
+ <div><code><a href="#Companion.z%3Akotlin.Int">z</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constructors</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <div><code><a href="#%3Cinit%3E%28%29"><init></a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Constants</h2>
+ <div id="Companion.z:kotlin.Int">
+ <h3>z</h3>
+ <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">z</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+ <pre>Value: <code>0</code></pre>
+ </div>
+ <h2>Constructors</h2>
+ <div id="<init>()">
+ <h3><init></h3>
+ <pre><span class="identifier">D</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/const.kt b/core/testdata/format/java-layout-html/const.kt
new file mode 100644
index 0000000..a6a0884
--- /dev/null
+++ b/core/testdata/format/java-layout-html/const.kt
@@ -0,0 +1,14 @@
+package p
+
+const val x = 0
+
+
+object G {
+ const val y = 0
+}
+
+class D {
+ companion object {
+ const val z = 0
+ }
+}
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/const.package-summary.html b/core/testdata/format/java-layout-html/const.package-summary.html
new file mode 100644
index 0000000..50f0653
--- /dev/null
+++ b/core/testdata/format/java-layout-html/const.package-summary.html
@@ -0,0 +1,35 @@
+<!-- File: /test/p/package-summary.html -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>p</h1>
+ <h2>Classes</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="D.html#">D</a></td>
+ <td></td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level constants summary</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><span class="keyword">const</span> <span class="identifier">Int</span></td>
+ <td>
+ <div><code><a href="#x%3Akotlin.Int">x</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level constants</h2>
+ <div id="x:kotlin.Int">
+ <h3>x</h3>
+ <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">x</span><span class="symbol">: </span><span class="identifier">Int</span></pre>
+ <pre>Value: <code>0</code></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/externalClassExtension.kt b/core/testdata/format/java-layout-html/externalClassExtension.kt
new file mode 100644
index 0000000..04415cc
--- /dev/null
+++ b/core/testdata/format/java-layout-html/externalClassExtension.kt
@@ -0,0 +1,5 @@
+package p
+
+fun String.ext() {
+ println(this)
+}
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/externalClassExtension.package-summary.html b/core/testdata/format/java-layout-html/externalClassExtension.package-summary.html
new file mode 100644
index 0000000..39db6c8
--- /dev/null
+++ b/core/testdata/format/java-layout-html/externalClassExtension.package-summary.html
@@ -0,0 +1,25 @@
+<!-- File: /test/p/package-summary.html -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>p</h1>
+ <h2>Top-level functions summary</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+ <td>
+ <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a>.<a href="#%28kotlin.String%29.ext%28%29">ext</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level functions</h2>
+ <div id="(kotlin.String).ext()">
+ <h3>ext</h3>
+ <pre><span class="keyword">fun </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a><span class="symbol">.</span><span class="identifier">ext</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/genericExtension.html b/core/testdata/format/java-layout-html/genericExtension.html
new file mode 100644
index 0000000..91b318e
--- /dev/null
+++ b/core/testdata/format/java-layout-html/genericExtension.html
@@ -0,0 +1,99 @@
+<!-- File: /test/p/Some.html# -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>Some</h1>
+ <pre><span class="keyword">class </span><span class="identifier">Some</span></pre>
+ <table>
+ <tr>
+ <td><a href="#">p.Some</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constructors</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <div><code><a href="#%3Cinit%3E%28%29"><init></a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Inherited extension functions</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>From <a href="package-summary.html">p</a>
+ <table>
+ <tbody>
+ <tr>
+ <td><span class="identifier">String</span></td>
+ <td>
+ <div><code><a href="package-summary.html#%28p.extFun.T%29.extFun%28%29%2FT"><span class="identifier">T</span></a>.<a href="package-summary.html#%28p.extFun.T%29.extFun%28%29">extFun</a>()</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">String</span></td>
+ <td>
+ <div><code><a href="package-summary.html#%28p.nullableExtFun.T%29.nullableExtFun%28%29%2FT"><span class="identifier">T</span></a>.<a href="package-summary.html#%28p.nullableExtFun.T%29.nullableExtFun%28%29">nullableExtFun</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Inherited extension properties</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>From <a href="package-summary.html">p</a>
+ <table>
+ <tbody>
+ <tr>
+ <td><span class="identifier">String</span></td>
+ <td>
+ <div><code><a href="package-summary.html#%28p.extVal.T%29.extVal%3Akotlin.String">extVal</a></code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">String</span></td>
+ <td>
+ <div><code><a href="package-summary.html#%28p.nullableExtVal.T%29.nullableExtVal%3Akotlin.String">nullableExtVal</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Constructors</h2>
+ <div id="<init>()">
+ <h3><init></h3>
+ <pre><span class="identifier">Some</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/genericExtension.kt b/core/testdata/format/java-layout-html/genericExtension.kt
new file mode 100644
index 0000000..0a9d74e
--- /dev/null
+++ b/core/testdata/format/java-layout-html/genericExtension.kt
@@ -0,0 +1,10 @@
+package p
+
+class Some
+
+
+fun <T : Some> T.extFun() = ""
+val <T : Some> T.extVal get() = ""
+
+fun <T : Some?> T.nullableExtFun() = ""
+val <T : Some?> T.nullableExtVal get() = ""
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/simple.html b/core/testdata/format/java-layout-html/simple.html
new file mode 100644
index 0000000..0b82c49
--- /dev/null
+++ b/core/testdata/format/java-layout-html/simple.html
@@ -0,0 +1,177 @@
+<!-- File: /test/p/Foo.html# -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>Foo</h1>
+ <pre><span class="keyword">class </span><span class="identifier">Foo</span></pre>
+ <table>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html">kotlin.Any</a></td>
+ </tr>
+ <tr>
+ <td> ↳</td>
+ <td><a href="#">p.Foo</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constructors</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <div><code><a href="#%3Cinit%3E%28%29"><init></a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Functions</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+ <td>
+ <div><code><a href="#s%28%29">s</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Inherited extension functions</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>From <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/index.html">kotlin</a>
+ <table>
+ <tbody>
+ <tr>
+ <td><span class="identifier">T</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/also.html">also</a>(<span class="identifier" id="kotlin$also(kotlin.also.T, kotlin.Function1((kotlin.also.T, kotlin.Unit)))/block">block</span><span class="symbol">:</span> <span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span> <span class="symbol">-></span> <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">T</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/apply.html">apply</a>(<span class="identifier" id="kotlin$apply(kotlin.apply.T, kotlin.Function1((kotlin.apply.T, kotlin.Unit)))/block">block</span><span class="symbol">:</span> <span class="identifier">T</span><span class="symbol">.</span><span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">-></span> <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">R</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html">let</a>(<span class="identifier" id="kotlin$let(kotlin.let.T, kotlin.Function1((kotlin.let.T, kotlin.let.R)))/block">block</span><span class="symbol">:</span> <span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span> <span class="symbol">-></span> <span class="identifier">R</span>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">R</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/run.html">run</a>(<span class="identifier" id="kotlin$run(kotlin.run.T, kotlin.Function1((kotlin.run.T, kotlin.run.R)))/block">block</span><span class="symbol">:</span> <span class="identifier">T</span><span class="symbol">.</span><span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">-></span> <span class="identifier">R</span>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">T</span><span class="symbol">?</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/take-if.html">takeIf</a>(<span class="identifier" id="kotlin$takeIf(kotlin.takeIf.T, kotlin.Function1((kotlin.takeIf.T, kotlin.Boolean)))/predicate">predicate</span><span class="symbol">:</span> <span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span> <span class="symbol">-></span> <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html"><span class="identifier">Boolean</span></a>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="identifier">T</span><span class="symbol">?</span></td>
+ <td>
+ <div><code><span class="identifier">T</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/take-unless.html">takeUnless</a>(<span class="identifier" id="kotlin$takeUnless(kotlin.takeUnless.T, kotlin.Function1((kotlin.takeUnless.T, kotlin.Boolean)))/predicate">predicate</span><span class="symbol">:</span> <span class="symbol">(</span><span class="identifier">T</span><span class="symbol">)</span> <span class="symbol">-></span> <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-boolean/index.html"><span class="identifier">Boolean</span></a>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-pair/index.html"><span class="identifier">Pair</span></a><span class="symbol"><</span><span class="identifier">A</span><span class="symbol">,</span> <span class="identifier">B</span><span class="symbol">></span></td>
+ <td>
+ <div><code><span class="identifier">A</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/to.html">to</a>(<span class="identifier" id="kotlin$to(kotlin.to.A, kotlin.to.B)/that">that</span><span class="symbol">:</span> <span class="identifier">B</span>)</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+ <td>
+ <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/index.html"><span class="identifier">Any</span></a><span class="symbol">?</span>.<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/to-string.html">toString</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Properties</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+ <td>
+ <div><code><a href="#g%3Akotlin.String">g</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Inherited extension properties</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>From <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/index.html">kotlin.jvm</a>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html"><span class="identifier">Class</span></a><span class="symbol"><</span><span class="identifier">T</span><span class="symbol">></span></td>
+ <td>
+ <div><code><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/java-class.html">javaClass</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Constructors</h2>
+ <div id="<init>()">
+ <h3><init></h3>
+ <pre><span class="identifier">Foo</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ <h2>Functions</h2>
+ <div id="s()">
+ <h3>s</h3>
+ <pre><span class="keyword">fun </span><span class="identifier">s</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+ </div>
+ <h2>Properties</h2>
+ <div id="g:kotlin.String">
+ <h3>g</h3>
+ <pre><span class="keyword">val </span><span class="identifier">g</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/simple.kt b/core/testdata/format/java-layout-html/simple.kt
new file mode 100644
index 0000000..89789c4
--- /dev/null
+++ b/core/testdata/format/java-layout-html/simple.kt
@@ -0,0 +1,8 @@
+package p
+
+
+class Foo {
+ fun s(): Unit {}
+
+ val g = ""
+}
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/topLevel.kt b/core/testdata/format/java-layout-html/topLevel.kt
new file mode 100644
index 0000000..85b1a43
--- /dev/null
+++ b/core/testdata/format/java-layout-html/topLevel.kt
@@ -0,0 +1,15 @@
+package p
+
+class Some
+
+fun topLevelFun() {}
+
+val topLevelVal = ""
+
+const val topLevelConst = ""
+
+val topLevelGetVal get() = ""
+
+val Some.topLevelExtVal get() = ""
+
+fun Some.topLevelExtFun() {}
\ No newline at end of file
diff --git a/core/testdata/format/java-layout-html/topLevel.package-summary.html b/core/testdata/format/java-layout-html/topLevel.package-summary.html
new file mode 100644
index 0000000..791c371
--- /dev/null
+++ b/core/testdata/format/java-layout-html/topLevel.package-summary.html
@@ -0,0 +1,90 @@
+<!-- File: /test/p/package-summary.html -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>p</h1>
+ <h2>Classes</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="Some.html#">Some</a></td>
+ <td></td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level functions summary</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+ <td>
+ <div><code><a href="#topLevelFun%28%29">topLevelFun</a>()</code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></td>
+ <td>
+ <div><code><a href="Some.html#"><span class="identifier">Some</span></a>.<a href="#%28p.Some%29.topLevelExtFun%28%29">topLevelExtFun</a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level properties summary</h2>
+ <table>
+ <tbody>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+ <td>
+ <div><code><a href="#topLevelVal%3Akotlin.String">topLevelVal</a></code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><span class="keyword">const</span> <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+ <td>
+ <div><code><a href="#topLevelConst%3Akotlin.String">topLevelConst</a></code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+ <td>
+ <div><code><a href="#topLevelGetVal%3Akotlin.String">topLevelGetVal</a></code></div>
+ </td>
+ </tr>
+ <tr>
+ <td><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></td>
+ <td>
+ <div><code><a href="#%28p.Some%29.topLevelExtVal%3Akotlin.String">topLevelExtVal</a></code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Top-level functions</h2>
+ <div id="topLevelFun()">
+ <h3>topLevelFun</h3>
+ <pre><span class="keyword">fun </span><span class="identifier">topLevelFun</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+ </div>
+ <div id="(p.Some).topLevelExtFun()">
+ <h3>topLevelExtFun</h3>
+ <pre><span class="keyword">fun </span><a href="Some.html#"><span class="identifier">Some</span></a><span class="symbol">.</span><span class="identifier">topLevelExtFun</span><span class="symbol">(</span><span class="symbol">)</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-unit/index.html"><span class="identifier">Unit</span></a></pre>
+ </div>
+ <h2>Top-level properties</h2>
+ <div id="topLevelVal:kotlin.String">
+ <h3>topLevelVal</h3>
+ <pre><span class="keyword">val </span><span class="identifier">topLevelVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+ </div>
+ <div id="topLevelConst:kotlin.String">
+ <h3>topLevelConst</h3>
+ <pre><span class="keyword">const</span> <span class="keyword">val </span><span class="identifier">topLevelConst</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+ </div>
+ <div id="topLevelGetVal:kotlin.String">
+ <h3>topLevelGetVal</h3>
+ <pre><span class="keyword">val </span><span class="identifier">topLevelGetVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+ </div>
+ <div id="(p.Some).topLevelExtVal:kotlin.String">
+ <h3>topLevelExtVal</h3>
+ <pre><span class="keyword">val </span><a href="Some.html#"><span class="identifier">Some</span></a><span class="symbol">.</span><span class="identifier">topLevelExtVal</span><span class="symbol">: </span><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/unresolvedExternalClass.html b/core/testdata/format/java-layout-html/unresolvedExternalClass.html
new file mode 100644
index 0000000..eca242c
--- /dev/null
+++ b/core/testdata/format/java-layout-html/unresolvedExternalClass.html
@@ -0,0 +1,63 @@
+<!-- File: /test/p/MyException.html# -->
+<html>
+ <head>
+ <meta charset="UTF-8">
+ </head>
+ <body>
+ <h1>MyException</h1>
+ <pre><span class="keyword">class </span><span class="identifier">MyException</span> <span class="symbol">:</span> <span class="identifier">Exception</span></pre>
+ <table>
+ <tr>
+ <td><a href="#">p.MyException</a></td>
+ </tr>
+ </table>
+ <h2>Summary</h2>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Constructors</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <div><code><a href="#%3Cinit%3E%28%29"><init></a>()</code></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ <thead>
+ <tr>
+ <td>
+ <h3>Inherited extension functions</h3>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>From <a href="#">p.java.lang.Exception</a>
+ <table>
+ <tbody>
+ <tr>
+ <td><span class="identifier">Unit</span></td>
+ <td>
+ <div><code><span class="identifier">Exception</span>.<a href="package-summary.html#%28kotlin.Exception%29.ext%28%29">ext</a>()</code></div>
+ <p>Some docs...</p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h2>Constructors</h2>
+ <div id="<init>()">
+ <h3><init></h3>
+ <pre><span class="identifier">MyException</span><span class="symbol">(</span><span class="symbol">)</span></pre>
+ </div>
+ </body>
+</html>
diff --git a/core/testdata/format/java-layout-html/unresolvedExternalClass.kt b/core/testdata/format/java-layout-html/unresolvedExternalClass.kt
new file mode 100644
index 0000000..0831dac
--- /dev/null
+++ b/core/testdata/format/java-layout-html/unresolvedExternalClass.kt
@@ -0,0 +1,13 @@
+package p
+
+// noStdlibLink set to true for that test
+
+/**
+ * Some docs...
+ */
+fun Exception.ext() {
+
+}
+
+
+class MyException: Exception()
\ No newline at end of file
diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt b/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
index 90e5b5f..46a5727 100644
--- a/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
+++ b/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt
@@ -41,6 +41,7 @@
val noStdlibLink: Boolean
val cacheRoot: String?
val suppressedFiles: List<String>
+ val collectInheritedExtensionsFromLibraries: Boolean
interface SourceRoot {
val path: String
@@ -82,29 +83,30 @@
}
data class SerializeOnlyDokkaConfiguration(
- override val moduleName: String,
- override val classpath: List<String>,
- override val sourceRoots: List<DokkaConfiguration.SourceRoot>,
- override val samples: List<String>,
- override val includes: List<String>,
- override val outputDir: String,
- override val format: String,
- override val includeNonPublic: Boolean,
- override val includeRootPackage: Boolean,
- override val reportUndocumented: Boolean,
- override val skipEmptyPackages: Boolean,
- override val skipDeprecated: Boolean,
- override val jdkVersion: Int,
- override val generateIndexPages: Boolean,
- override val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition>,
- override val impliedPlatforms: List<String>,
- override val perPackageOptions: List<DokkaConfiguration.PackageOptions>,
- override val externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink>,
- override val noStdlibLink: Boolean,
- override val cacheRoot: String?,
- override val suppressedFiles: List<String>,
- override val languageVersion: String?,
- override val apiVersion: String?
+ override val moduleName: String,
+ override val classpath: List<String>,
+ override val sourceRoots: List<DokkaConfiguration.SourceRoot>,
+ override val samples: List<String>,
+ override val includes: List<String>,
+ override val outputDir: String,
+ override val format: String,
+ override val includeNonPublic: Boolean,
+ override val includeRootPackage: Boolean,
+ override val reportUndocumented: Boolean,
+ override val skipEmptyPackages: Boolean,
+ override val skipDeprecated: Boolean,
+ override val jdkVersion: Int,
+ override val generateIndexPages: Boolean,
+ override val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition>,
+ override val impliedPlatforms: List<String>,
+ override val perPackageOptions: List<DokkaConfiguration.PackageOptions>,
+ override val externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink>,
+ override val noStdlibLink: Boolean,
+ override val cacheRoot: String?,
+ override val suppressedFiles: List<String>,
+ override val languageVersion: String?,
+ override val apiVersion: String?,
+ override val collectInheritedExtensionsFromLibraries: Boolean
) : DokkaConfiguration
diff --git a/runners/cli/src/main/kotlin/cli/main.kt b/runners/cli/src/main/kotlin/cli/main.kt
index fe945ed..111e142 100644
--- a/runners/cli/src/main/kotlin/cli/main.kt
+++ b/runners/cli/src/main/kotlin/cli/main.kt
@@ -62,6 +62,9 @@
@set:Argument(value = "apiVersion", description = "Kotlin Api Version to pass to Kotlin Analysis")
var apiVersion: String? = null
+ @set:Argument(value = "collectInheritedExtensionsFromLibraries", description = "Search for applicable extensions in libraries")
+ var collectInheritedExtensionsFromLibraries: Boolean = false
+
}
@@ -106,18 +109,19 @@
val classPath = arguments.classpath.split(File.pathSeparatorChar).toList()
val documentationOptions = DocumentationOptions(
- arguments.outputDir.let { if (it.endsWith('/')) it else it + '/' },
- arguments.outputFormat,
- skipDeprecated = arguments.nodeprecated,
- sourceLinks = sourceLinks,
- impliedPlatforms = arguments.impliedPlatforms.split(','),
- perPackageOptions = parsePerPackageOptions(arguments.packageOptions),
- jdkVersion = arguments.jdkVersion,
- externalDocumentationLinks = parseLinks(arguments.links),
- noStdlibLink = arguments.noStdlibLink,
- cacheRoot = arguments.cacheRoot,
- languageVersion = arguments.languageVersion,
- apiVersion = arguments.apiVersion
+ arguments.outputDir.let { if (it.endsWith('/')) it else it + '/' },
+ arguments.outputFormat,
+ skipDeprecated = arguments.nodeprecated,
+ sourceLinks = sourceLinks,
+ impliedPlatforms = arguments.impliedPlatforms.split(','),
+ perPackageOptions = parsePerPackageOptions(arguments.packageOptions),
+ jdkVersion = arguments.jdkVersion,
+ externalDocumentationLinks = parseLinks(arguments.links),
+ noStdlibLink = arguments.noStdlibLink,
+ cacheRoot = arguments.cacheRoot,
+ languageVersion = arguments.languageVersion,
+ apiVersion = arguments.apiVersion,
+ collectInheritedExtensionsFromLibraries = arguments.collectInheritedExtensionsFromLibraries
)
val generator = DokkaGenerator(
diff --git a/runners/gradle-plugin/src/main/kotlin/main.kt b/runners/gradle-plugin/src/main/kotlin/main.kt
index bdecc3f..5138fce 100644
--- a/runners/gradle-plugin/src/main/kotlin/main.kt
+++ b/runners/gradle-plugin/src/main/kotlin/main.kt
@@ -122,6 +122,9 @@
@Optional @Input
var apiVersion: String? = null
+ @Input
+ var collectInheritedExtensionsFromLibraries: Boolean = false
+
@get:Input
internal val kotlinCompileBasedClasspathAndSourceRoots: ClasspathAndSourceRoots by lazy { extractClasspathAndSourceRootsFromKotlinTasks() }
@@ -269,29 +272,31 @@
val bootstrapProxy: DokkaBootstrap = automagicTypedProxy(javaClass.classLoader, bootstrapInstance)
val configuration = SerializeOnlyDokkaConfiguration(
- moduleName,
- fullClasspath.map { it.absolutePath },
- sourceRoots,
- samples.filterNotNull().map { project.file(it).absolutePath },
- includes.filterNotNull().map { project.file(it).absolutePath },
- outputDirectory,
- outputFormat,
- includeNonPublic,
- false,
- reportNotDocumented,
- skipEmptyPackages,
- skipDeprecated,
- jdkVersion,
- true,
- linkMappings,
- impliedPlatforms,
- perPackageOptions,
- externalDocumentationLinks,
- noStdlibLink,
- cacheRoot,
- collectSuppressedFiles(sourceRoots),
- languageVersion,
- apiVersion)
+ moduleName,
+ fullClasspath.map { it.absolutePath },
+ sourceRoots,
+ samples.filterNotNull().map { project.file(it).absolutePath },
+ includes.filterNotNull().map { project.file(it).absolutePath },
+ outputDirectory,
+ outputFormat,
+ includeNonPublic,
+ false,
+ reportNotDocumented,
+ skipEmptyPackages,
+ skipDeprecated,
+ jdkVersion,
+ true,
+ linkMappings,
+ impliedPlatforms,
+ perPackageOptions,
+ externalDocumentationLinks,
+ noStdlibLink,
+ cacheRoot,
+ collectSuppressedFiles(sourceRoots),
+ languageVersion,
+ apiVersion,
+ collectInheritedExtensionsFromLibraries
+ )
bootstrapProxy.configure(