Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Description list support for JavaDocs (#2213) #2259

Merged
merged 3 commits into from Dec 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
Comment on lines +361 to +372
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about extracting it to a separate enum ListContentStyle, but it seems unlikely that we'll ever add anything else there, so probably not worth it

}

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")
)
)
}
}