Skip to content

Commit

Permalink
Merge pull request #2259 from Kotlin/2213-description-list-support
Browse files Browse the repository at this point in the history
Description list support for JavaDocs (#2213)
  • Loading branch information
IgnatBeresnev committed Dec 14, 2021
2 parents bf9d62a + 19b2a2d commit ed5d582
Show file tree
Hide file tree
Showing 12 changed files with 304 additions and 15 deletions.
8 changes: 8 additions & 0 deletions core/api/core.api
Expand Up @@ -3913,6 +3913,14 @@ public final class org/jetbrains/dokka/pages/HtmlContent : org/jetbrains/dokka/m
public abstract interface class org/jetbrains/dokka/pages/Kind {
}

public final class org/jetbrains/dokka/pages/ListStyle : java/lang/Enum, org/jetbrains/dokka/pages/Style {
public static final field DescriptionDetails Lorg/jetbrains/dokka/pages/ListStyle;
public static final field DescriptionList Lorg/jetbrains/dokka/pages/ListStyle;
public static final field DescriptionTerm Lorg/jetbrains/dokka/pages/ListStyle;
public static fun valueOf (Ljava/lang/String;)Lorg/jetbrains/dokka/pages/ListStyle;
public static fun values ()[Lorg/jetbrains/dokka/pages/ListStyle;
}

public abstract interface class org/jetbrains/dokka/pages/MemberPage : org/jetbrains/dokka/pages/ContentPage {
}

Expand Down
22 changes: 22 additions & 0 deletions core/src/main/kotlin/pages/ContentNodes.kt
Expand Up @@ -350,6 +350,28 @@ enum class ContentStyle : Style {
RowTitle, TabbedContent, WithExtraAttributes, RunnableSample, InDocumentationAnchor, Caption
}

enum class ListStyle : Style {
/**
* Represents a list of groups of [DescriptionTerm] and [DescriptionDetails].
* Common uses for this element are to implement a glossary or to display
* metadata (a list of key-value pairs). Formatting example: see `<dl>` html tag.
*/
DescriptionList,

/**
* If used within [DescriptionList] context, specifies a term in a description
* or definition list, usually followed by [DescriptionDetails] for one or more
* terms. Formatting example: see `<dt>` html tag
*/
DescriptionTerm,

/**
* If used within [DescriptionList] context, provides the definition or other
* related text associated with [DescriptionTerm]. Formatting example: see `<dd>` html tag
*/
DescriptionDetails
}

object CommentTable : Style

object MultimoduleTable : Style
Expand Down
2 changes: 2 additions & 0 deletions plugins/base/api/base.api
Expand Up @@ -1398,6 +1398,8 @@ public class org/jetbrains/dokka/base/translators/documentables/PageContentBuild
public final fun cover (Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;)V
public static synthetic fun cover$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
protected final fun createText (Ljava/lang/String;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)Lorg/jetbrains/dokka/pages/ContentText;
public final fun descriptionList (Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;)V
public static synthetic fun descriptionList$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public final fun divergentGroup (Lorg/jetbrains/dokka/pages/ContentDivergentGroup$GroupID;Ljava/util/Set;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ZLkotlin/jvm/functions/Function1;)V
public static synthetic fun divergentGroup$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/pages/ContentDivergentGroup$GroupID;Ljava/util/Set;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public final fun firstParagraphComment (Lorg/jetbrains/dokka/model/doc/DocTag;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)V
Expand Down
12 changes: 12 additions & 0 deletions plugins/base/base-test-utils/api/base-test-utils.api
Expand Up @@ -108,10 +108,22 @@ public final class utils/Br : utils/Tag {
public static final field INSTANCE Lutils/Br;
}

public final class utils/Dd : utils/Tag {
public fun <init> ([Ljava/lang/Object;)V
}

public final class utils/Div : utils/Tag {
public fun <init> ([Ljava/lang/Object;)V
}

public final class utils/Dl : utils/Tag {
public fun <init> ([Ljava/lang/Object;)V
}

public final class utils/Dt : utils/Tag {
public fun <init> ([Ljava/lang/Object;)V
}

public final class utils/I : utils/Tag {
public fun <init> ([Ljava/lang/Object;)V
}
Expand Down
Expand Up @@ -29,6 +29,9 @@ class A(vararg matchers: Any) : Tag("a", *matchers)
class B(vararg matchers: Any) : Tag("b", *matchers)
class I(vararg matchers: Any) : Tag("i", *matchers)
class STRIKE(vararg matchers: Any) : Tag("strike", *matchers)
class Dl(vararg matchers: Any) : Tag("dl", *matchers)
class Dt(vararg matchers: Any) : Tag("dt", *matchers)
class Dd(vararg matchers: Any) : Tag("dd", *matchers)
object Wbr : Tag("wbr")
object Br : Tag("br")
private fun Any.accepts(n: Node, ignoreSpan:Boolean = true) {
Expand Down
20 changes: 17 additions & 3 deletions plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt
Expand Up @@ -8,7 +8,6 @@ import org.jetbrains.dokka.base.DokkaBaseConfiguration
import org.jetbrains.dokka.base.DokkaBaseConfiguration.Companion.defaultFooterMessage
import org.jetbrains.dokka.base.renderers.*
import org.jetbrains.dokka.base.renderers.html.command.consumers.ImmediateResolutionTagConsumer
import org.jetbrains.dokka.base.renderers.pageId
import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint
import org.jetbrains.dokka.base.resolvers.local.DokkaBaseLocationProvider
import org.jetbrains.dokka.base.templating.*
Expand Down Expand Up @@ -112,6 +111,12 @@ open class HtmlRenderer(
) { childrenCallback() }
node.extra[InsertTemplateExtra] != null -> node.extra[InsertTemplateExtra]?.let { templateCommand(it.command) }
?: Unit
node.hasStyle(ListStyle.DescriptionTerm) -> DT(emptyMap(), consumer).visit {
this@wrapGroup.childrenCallback()
}
node.hasStyle(ListStyle.DescriptionDetails) -> DD(emptyMap(), consumer).visit {
this@wrapGroup.childrenCallback()
}
else -> childrenCallback()
}
}
Expand Down Expand Up @@ -295,8 +300,17 @@ open class HtmlRenderer(
node: ContentList,
pageContext: ContentPage,
sourceSetRestriction: Set<DisplaySourceSet>?
) = if (node.ordered) ol { buildListItems(node.children, pageContext, sourceSetRestriction) }
else ul { buildListItems(node.children, pageContext, sourceSetRestriction) }
) = when {
node.ordered -> {
ol { buildListItems(node.children, pageContext, sourceSetRestriction) }
}
node.hasStyle(ListStyle.DescriptionList) -> {
dl { node.children.forEach { it.build(this, pageContext, sourceSetRestriction) } }
}
else -> {
ul { buildListItems(node.children, pageContext, sourceSetRestriction) }
}
}

open fun OL.buildListItems(
items: List<ContentNode>,
Expand Down
Expand Up @@ -4,10 +4,10 @@ import org.intellij.markdown.MarkdownElementTypes
import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet
import org.jetbrains.dokka.model.doc.*
import org.jetbrains.dokka.model.properties.PropertyContainer
import org.jetbrains.dokka.model.properties.plus
import org.jetbrains.dokka.model.toDisplaySourceSets
import org.jetbrains.dokka.pages.*
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.dokka.model.properties.plus

open class DocTagToContentConverter : CommentsToContentConverter {
override fun buildContent(
Expand Down Expand Up @@ -39,14 +39,14 @@ open class DocTagToContentConverter : CommentsToContentConverter {
)
)

fun buildList(ordered: Boolean, start: Int = 1) =
fun buildList(ordered: Boolean, newStyles: Set<Style> = emptySet(), start: Int = 1) =
listOf(
ContentList(
buildChildren(docTag),
ordered,
dci,
sourceSets.toDisplaySourceSets(),
styles,
styles + newStyles,
((PropertyContainer.empty<ContentNode>()) + SimpleAttr("start", start.toString()))
)
)
Expand All @@ -68,10 +68,27 @@ open class DocTagToContentConverter : CommentsToContentConverter {
is H5 -> buildHeader(5)
is H6 -> buildHeader(6)
is Ul -> buildList(false)
is Ol -> buildList(true, docTag.params["start"]?.toInt() ?: 1)
is Ol -> buildList(true, start = docTag.params["start"]?.toInt() ?: 1)
is Li -> listOf(
ContentGroup(buildChildren(docTag), dci, sourceSets.toDisplaySourceSets(), styles, extra)
)
is Dl -> buildList(false, newStyles = setOf(ListStyle.DescriptionList))
is Dt -> listOf(
ContentGroup(
buildChildren(docTag),
dci,
sourceSets.toDisplaySourceSets(),
styles + ListStyle.DescriptionTerm
)
)
is Dd -> listOf(
ContentGroup(
buildChildren(docTag),
dci,
sourceSets.toDisplaySourceSets(),
styles + ListStyle.DescriptionDetails
)
)
is Br -> buildNewLine()
is B -> buildChildren(docTag, setOf(TextStyle.Strong))
is I -> buildChildren(docTag, setOf(TextStyle.Italic))
Expand Down
Expand Up @@ -173,6 +173,18 @@ open class PageContentBuilder(
contents += ListBuilder(true, mainDRI, sourceSets, kind, styles, extra).apply(operation).build()
}

fun descriptionList(
kind: Kind = ContentKind.Main,
sourceSets: Set<DokkaSourceSet> = mainSourcesetData,
styles: Set<Style> = mainStyles,
extra: PropertyContainer<ContentNode> = mainExtra,
operation: ListBuilder.() -> Unit = {}
) {
contents += ListBuilder(false, mainDRI, sourceSets, kind, styles + ListStyle.DescriptionList, extra)
.apply(operation)
.build()
}

internal fun headers(vararg label: String) = contentFor(mainDRI, mainSourcesetData) {
label.forEach { text(it) }
}
Expand Down
Expand Up @@ -14,17 +14,21 @@ import org.jetbrains.dokka.base.parsers.MarkdownParser
import org.jetbrains.dokka.base.translators.parseHtmlEncodedWithNormalisedSpaces
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.model.doc.*
import org.jetbrains.dokka.model.doc.Deprecated
import org.jetbrains.dokka.utilities.DokkaLogger
import org.jetbrains.dokka.utilities.enumValueOrNull
import org.jetbrains.dokka.utilities.htmlEscape
import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.util.CommentSaver.Companion.tokenType
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespace
import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jsoup.Jsoup
import org.jsoup.nodes.*
import org.jsoup.nodes.Comment
import org.jsoup.nodes.Element
import org.jsoup.nodes.Node
import org.jsoup.nodes.TextNode
import java.util.*
import org.jetbrains.dokka.utilities.htmlEscape

interface JavaDocumentationParser {
fun parseDocumentation(element: PsiNamedElement): DocumentationNode
Expand Down Expand Up @@ -396,6 +400,9 @@ class JavadocParser(
"ul" -> ifChildrenPresent { Ul(children) }
"ol" -> ifChildrenPresent { Ol(children) }
"li" -> listOf(Li(children))
"dl" -> ifChildrenPresent { Dl(children) }
"dt" -> listOf(Dt(children))
"dd" -> listOf(Dd(children))
"a" -> listOf(createLink(element, children))
"table" -> ifChildrenPresent { Table(children) }
"tr" -> ifChildrenPresent { Tr(children) }
Expand Down
104 changes: 102 additions & 2 deletions plugins/base/src/test/kotlin/parsers/JavadocParserTest.kt
Expand Up @@ -5,10 +5,9 @@ import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.model.DEnum
import org.jetbrains.dokka.model.DModule
import org.jetbrains.dokka.model.doc.*
import org.jetbrains.dokka.model.doc.P
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import utils.*
import utils.text

class JavadocParserTest : BaseAbstractTest() {

Expand Down Expand Up @@ -249,4 +248,105 @@ class JavadocParserTest : BaseAbstractTest() {
}
}
}

@Test
fun `description list tag`() {
val source = """
|/src/main/kotlin/test/Test.java
|package example
|
| /**
| * <dl>
| * <dt>
| * <code>name="<i>name</i>"</code>
| * </dt>
| * <dd>
| * A URI path segment. The subdirectory name for this value is contained in the
| * <code>path</code> attribute.
| * </dd>
| * <dt>
| * <code>path="<i>path</i>"</code>
| * </dt>
| * <dd>
| * The subdirectory you're sharing. While the <i>name</i> attribute is a URI path
| * segment, the <i>path</i> value is an actual subdirectory name.
| * </dd>
| * </dl>
| */
| public class Test {}
""".trimIndent()

val expected = listOf(
Dl(
listOf(
Dt(
listOf(
CodeInline(
listOf(
Text("name=\""),
I(
listOf(
Text("name")
)
),
Text("\"")
)
),
)
),
Dd(
listOf(
Text(" A URI path segment. The subdirectory name for this value is contained in the "),
CodeInline(
listOf(
Text("path")
)
),
Text(" attribute. ")
)
),

Dt(
listOf(
CodeInline(
listOf(
Text("path=\""),
I(
listOf(
Text("path")
)
),
Text("\"")
)
)
)
),
Dd(
listOf(
Text(" The subdirectory you're sharing. While the "),
I(
listOf(
Text("name")
)
),
Text(" attribute is a URI path segment, the "),
I(
listOf(
Text("path")
)
),
Text(" value is an actual subdirectory name. ")
)
)
)
)
)

testInline(source, configuration) {
documentablesCreationStage = { modules ->
val docs = modules.first().packages.first().classlikes.single().documentation.first().value
assertEquals(expected, docs.children.first().root.children)
}
}
}
}
41 changes: 41 additions & 0 deletions plugins/base/src/test/kotlin/renderers/html/ListStylesTest.kt
@@ -0,0 +1,41 @@
package renderers.html

import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
import org.jetbrains.dokka.pages.ListStyle
import org.junit.jupiter.api.Test
import renderers.testPage
import utils.Dd
import utils.Dl
import utils.Dt
import utils.match


class ListStylesTest : HtmlRenderingOnlyTestBase() {

@Test
fun `description list render`() {
val page = testPage {
descriptionList {
item(styles = setOf(ListStyle.DescriptionTerm)) {
text("Description term #1")
}
item(styles = setOf(ListStyle.DescriptionTerm)) {
text("Description term #2")
}
item(styles = setOf(ListStyle.DescriptionDetails)) {
text("Description details describing terms #1 and #2")
}
}
}


HtmlRenderer(context).render(page)
renderedContent.match(
Dl(
Dt("Description term #1"),
Dt("Description term #2"),
Dd("Description details describing terms #1 and #2")
)
)
}
}

0 comments on commit ed5d582

Please sign in to comment.