From 4fa9524e52d8ff422bb355336e3810ab28ae135c Mon Sep 17 00:00:00 2001 From: vmishenev Date: Wed, 25 Aug 2021 17:14:31 +0300 Subject: Manual highlighting webhelp (#2079) * Add manual code highlighting * Fix test * Add kotlinAsJava highlighting * Add runtime highlighting via Prism * Add copy-button for code block * Add tests and refactor * Replace `
` for prism.js * Parse trivial default values Co-authored-by: Marcin Aman --- plugins/base/api/base.api | 14 +- .../base/base-test-utils/api/base-test-utils.api | 3 +- .../src/main/kotlin/renderers/JsoupUtils.kt | 36 ++++- .../src/main/kotlin/renderers/html/HtmlRenderer.kt | 15 +- .../kotlin/renderers/html/htmlPreprocessors.kt | 4 +- .../main/kotlin/signatures/JvmSignatureUtils.kt | 25 ++-- .../kotlin/signatures/KotlinSignatureProvider.kt | 165 +++++++++++++-------- .../pages/comments/DocTagToContentConverter.kt | 2 +- .../DefaultDescriptorToDocumentableTranslator.kt | 31 ++-- .../documentables/PageContentBuilder.kt | 18 ++- .../base/src/main/resources/dokka/scripts/prism.js | 13 ++ .../base/src/main/resources/dokka/styles/prism.css | 92 ++++++++++++ .../base/src/main/resources/dokka/styles/style.css | 12 +- .../src/test/kotlin/content/HighlightingTest.kt | 79 ++++++++++ plugins/base/src/test/kotlin/enums/EnumsTest.kt | 8 +- .../base/src/test/kotlin/model/FunctionsTest.kt | 4 +- .../test/kotlin/renderers/html/TextStylesTest.kt | 17 ++- .../FunctionalTypeConstructorsSignatureTest.kt | 33 +++-- .../src/test/kotlin/signatures/SignatureTest.kt | 74 +++++---- .../kotlin/signatures/VarianceSignatureTest.kt | 12 +- 20 files changed, 492 insertions(+), 165 deletions(-) create mode 100644 plugins/base/src/main/resources/dokka/scripts/prism.js create mode 100644 plugins/base/src/main/resources/dokka/styles/prism.css create mode 100644 plugins/base/src/test/kotlin/content/HighlightingTest.kt (limited to 'plugins/base') diff --git a/plugins/base/api/base.api b/plugins/base/api/base.api index d4e2d067..9cd15875 100644 --- a/plugins/base/api/base.api +++ b/plugins/base/api/base.api @@ -1311,12 +1311,14 @@ public class org/jetbrains/dokka/base/translators/documentables/PageContentBuild public fun (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)V public final fun block (Ljava/lang/String;ILorg/jetbrains/dokka/pages/Kind;Ljava/lang/Iterable;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ZZLjava/util/List;ZLkotlin/jvm/functions/Function2;)V public static synthetic fun block$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/lang/String;ILorg/jetbrains/dokka/pages/Kind;Ljava/lang/Iterable;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ZZLjava/util/List;ZLkotlin/jvm/functions/Function2;ILjava/lang/Object;)V + public final fun booleanLiteral (Z)V public final fun build (Ljava/util/Set;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)Lorg/jetbrains/dokka/pages/ContentGroup; public final fun buildGroup (Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/dokka/pages/ContentGroup; public static synthetic fun buildGroup$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lorg/jetbrains/dokka/pages/ContentGroup; public final fun buildSignature (Lorg/jetbrains/dokka/model/Documentable;)Ljava/util/List; public final fun comment (Lorg/jetbrains/dokka/model/doc/DocTag;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)V public static synthetic fun comment$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/doc/DocTag;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ILjava/lang/Object;)V + public final fun constant (Ljava/lang/String;)V 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; @@ -1333,6 +1335,7 @@ public class org/jetbrains/dokka/base/translators/documentables/PageContentBuild public static synthetic fun group$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public final fun header (ILjava/lang/String;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 header$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;ILjava/lang/String;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 keyword (Ljava/lang/String;)V public final fun link (Ljava/lang/String;Ljava/lang/String;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)V public final fun link (Ljava/lang/String;Lorg/jetbrains/dokka/links/DRI;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)V public final fun link (Lorg/jetbrains/dokka/links/DRI;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;)V @@ -1341,14 +1344,17 @@ public class org/jetbrains/dokka/base/translators/documentables/PageContentBuild public static synthetic fun link$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/links/DRI;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 linkNode (Ljava/lang/String;Lorg/jetbrains/dokka/links/DRI;Lorg/jetbrains/dokka/pages/DCI;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)Lorg/jetbrains/dokka/pages/ContentDRILink; public static synthetic fun linkNode$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/lang/String;Lorg/jetbrains/dokka/links/DRI;Lorg/jetbrains/dokka/pages/DCI;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;ILjava/lang/Object;)Lorg/jetbrains/dokka/pages/ContentDRILink; - public final fun list (Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Lkotlin/jvm/functions/Function2;)V - public static synthetic fun list$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V + public final fun list (Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Lkotlin/jvm/functions/Function2;)V + public static synthetic fun list$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V + public final fun operator (Ljava/lang/String;)V + public final fun punctuation (Ljava/lang/String;)V public final fun sourceSetDependentHint (Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;)V public final fun sourceSetDependentHint (Lorg/jetbrains/dokka/links/DRI;Ljava/util/Set;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;)V public static synthetic fun sourceSetDependentHint$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V public static synthetic fun sourceSetDependentHint$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/links/DRI;Ljava/util/Set;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V - public final fun sourceSetDependentText (Ljava/util/Map;Ljava/util/Set;Lkotlin/jvm/functions/Function1;)V - public static synthetic fun sourceSetDependentText$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/util/Map;Ljava/util/Set;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V + public final fun sourceSetDependentText (Ljava/util/Map;Ljava/util/Set;Ljava/util/Set;Lkotlin/jvm/functions/Function1;)V + public static synthetic fun sourceSetDependentText$default (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Ljava/util/Map;Ljava/util/Set;Ljava/util/Set;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V + public final fun stringLiteral (Ljava/lang/String;)V public final fun table (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 table$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 text (Ljava/lang/String;Lorg/jetbrains/dokka/pages/Kind;Ljava/util/Set;Ljava/util/Set;Lorg/jetbrains/dokka/model/properties/PropertyContainer;)V diff --git a/plugins/base/base-test-utils/api/base-test-utils.api b/plugins/base/base-test-utils/api/base-test-utils.api index b5a9ef2f..844a1703 100644 --- a/plugins/base/base-test-utils/api/base-test-utils.api +++ b/plugins/base/base-test-utils/api/base-test-utils.api @@ -113,7 +113,8 @@ public final class utils/I : utils/Tag { } public final class utils/JsoupUtilsKt { - public static final fun match (Lorg/jsoup/nodes/Element;[Ljava/lang/Object;)V + public static final fun match (Lorg/jsoup/nodes/Element;[Ljava/lang/Object;Z)V + public static synthetic fun match$default (Lorg/jsoup/nodes/Element;[Ljava/lang/Object;ZILjava/lang/Object;)V } public final class utils/P : utils/Tag { diff --git a/plugins/base/base-test-utils/src/main/kotlin/renderers/JsoupUtils.kt b/plugins/base/base-test-utils/src/main/kotlin/renderers/JsoupUtils.kt index ea2d13a7..9e38df10 100644 --- a/plugins/base/base-test-utils/src/main/kotlin/renderers/JsoupUtils.kt +++ b/plugins/base/base-test-utils/src/main/kotlin/renderers/JsoupUtils.kt @@ -4,12 +4,22 @@ import org.jsoup.nodes.Element import org.jsoup.nodes.Node import org.jsoup.nodes.TextNode -fun Element.match(vararg matchers: Any): Unit = +fun Element.match(vararg matchers: Any, ignoreSpanWithTokenStyle:Boolean = false): Unit = childNodes() - .filter { it !is TextNode || it.text().isNotBlank() } + .let { list -> + if(ignoreSpanWithTokenStyle) { + list + .filterNot { it is Element && it.tagName() == "span" && it.attr("class").startsWith("token ") && it.childNodeSize() == 0} + .map { if(it is Element && it.tagName() == "span" + && it.attr("class").startsWith("token ") + && it.childNodeSize() == 1) it.childNode(0) else it } + .uniteConsecutiveTextNodes() + } else list + } + .filter { (it !is TextNode || it.text().isNotBlank())} .let { it.drop(it.size - matchers.size) } .zip(matchers) - .forEach { (n, m) -> m.accepts(n) } + .forEach { (n, m) -> m.accepts(n, ignoreSpan = ignoreSpanWithTokenStyle) } open class Tag(val name: String, vararg val matchers: Any) class Div(vararg matchers: Any) : Tag("div", *matchers) @@ -20,13 +30,27 @@ class B(vararg matchers: Any) : Tag("b", *matchers) class I(vararg matchers: Any) : Tag("i", *matchers) class STRIKE(vararg matchers: Any) : Tag("strike", *matchers) object Wbr : Tag("wbr") -private fun Any.accepts(n: Node) { +private fun Any.accepts(n: Node, ignoreSpan:Boolean = true) { when (this) { is String -> assert(n is TextNode && n.text().trim() == this.trim()) { "\"$this\" expected but found: $n" } is Tag -> { assert(n is Element && n.tagName() == name) { "Tag $name expected but found: $n" } - if (n is Element && matchers.isNotEmpty()) n.match(*matchers) + if (n is Element && matchers.isNotEmpty()) n.match(*matchers, ignoreSpanWithTokenStyle = ignoreSpan) } else -> throw IllegalArgumentException("$this is not proper matcher") } -} \ No newline at end of file +} +private fun List.uniteConsecutiveTextNodes(): MutableList { + val resList = mutableListOf() + var acc = StringBuilder() + forEachIndexed { index, item -> + if (item is TextNode) { + acc.append(item.text()) + if (!(index + 1 < size && this[index + 1] is TextNode)) { + resList.add(TextNode(acc.toString())) + acc = StringBuilder() + } + } else resList.add(item) + } + return resList + } \ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt index 3cf914ce..31753332 100644 --- a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -677,13 +677,15 @@ open class HtmlRenderer( pageContext: ContentPage ) { div("sample-container") { - val stylesWithBlock = code.style + TextStyle.Block - code(stylesWithBlock.joinToString(" ") { it.toString().toLowerCase() }) { - attributes["theme"] = "idea" - pre { + val codeLang = "lang-" + code.language.ifEmpty { "kotlin" } + val stylesWithBlock = code.style + TextStyle.Block + codeLang + pre { + code(stylesWithBlock.joinToString(" ") { it.toString().toLowerCase() }) { + attributes["theme"] = "idea" code.children.forEach { buildContentNode(it, pageContext) } } } + copyButton() } } @@ -691,7 +693,9 @@ open class HtmlRenderer( code: ContentCodeInline, pageContext: ContentPage ) { - code { + val codeLang = "lang-" + code.language.ifEmpty { "kotlin" } + val stylesWithBlock = code.style + codeLang + code(stylesWithBlock.joinToString(" ") { it.toString().toLowerCase() }) { code.children.forEach { buildContentNode(it, pageContext) } } } @@ -725,6 +729,7 @@ open class HtmlRenderer( TextStyle.Italic -> i { body() } TextStyle.Strikethrough -> strike { body() } TextStyle.Strong -> strong { body() } + is TokenStyle -> span("token " + styleToApply.toString().toLowerCase()) { body() } else -> body() } } diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt index 9faf4d17..347e16bf 100644 --- a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt +++ b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt @@ -87,6 +87,7 @@ class ScriptsInstaller(private val dokkaContext: DokkaContext) : PageTransformer "scripts/navigation-loader.js", "scripts/platform-content-handler.js", "scripts/main.js", + "scripts/prism.js" ) override fun invoke(input: RootPageNode): RootPageNode = @@ -104,7 +105,8 @@ class StylesInstaller(private val dokkaContext: DokkaContext) : PageTransformer private val stylesPages = listOf( "styles/style.css", "styles/jetbrains-mono.css", - "styles/main.css" + "styles/main.css", + "styles/prism.css" ) override fun invoke(input: RootPageNode): RootPageNode = diff --git a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt index d17fa276..94af96e2 100644 --- a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt +++ b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt @@ -8,7 +8,6 @@ import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.drisOfAllNestedBounds import org.jetbrains.dokka.model.AnnotationTarget -import org.jetbrains.dokka.model.doc.DocumentationNode interface JvmSignatureUtils { @@ -80,21 +79,22 @@ interface JvmSignatureUtils { when (renderAtStrategy) { is All, is OnlyOnce -> { - text("@") when(a.scope) { - Annotations.AnnotationScope.GETTER -> text("get:") - Annotations.AnnotationScope.SETTER -> text("set:") + Annotations.AnnotationScope.GETTER -> text("@get:", styles = mainStyles + TokenStyle.Annotation) + Annotations.AnnotationScope.SETTER -> text("@set:", styles = mainStyles + TokenStyle.Annotation) + else -> text("@", styles = mainStyles + TokenStyle.Annotation) } + link(a.dri.classNames!!, a.dri, styles = mainStyles + TokenStyle.Annotation) } - is Never -> Unit + is Never -> link(a.dri.classNames!!, a.dri) } - link(a.dri.classNames!!, a.dri) val isNoWrappedBrackets = a.params.entries.isEmpty() && renderAtStrategy is OnlyOnce listParams( a.params.entries, if (isNoWrappedBrackets) null else Pair('(', ')') ) { - text(it.key + " = ") + text(it.key) + text(" = ", styles = mainStyles + TokenStyle.Operator) when (renderAtStrategy) { is All -> All is Never, is OnlyOnce -> Never @@ -116,8 +116,9 @@ interface JvmSignatureUtils { } is EnumValue -> link(a.enumName, a.enumDri) is ClassValue -> link(a.className + classExtension, a.classDRI) - is StringValue -> group(styles = setOf(TextStyle.Breakable)) { text( "\"${a.text()}\"") } - is LiteralValue -> group(styles = setOf(TextStyle.Breakable)) { text(a.text()) } + is StringValue -> group(styles = setOf(TextStyle.Breakable)) { stringLiteral( "\"${a.text()}\"") } + is BooleanValue -> group(styles = setOf(TextStyle.Breakable)) { booleanLiteral(a.value) } + is LiteralValue -> group(styles = setOf(TextStyle.Breakable)) { constant(a.text()) } } private fun PageContentBuilder.DocumentableContentBuilder.listParams( @@ -125,14 +126,14 @@ interface JvmSignatureUtils { listBrackets: Pair?, outFn: PageContentBuilder.DocumentableContentBuilder.(T) -> Unit ) { - listBrackets?.let{ text(it.first.toString()) } + listBrackets?.let{ punctuation(it.first.toString()) } params.forEachIndexed { i, it -> group(styles = setOf(TextStyle.BreakableAfter)) { this.outFn(it) - if (i != params.size - 1) text(", ") + if (i != params.size - 1) punctuation(", ") } } - listBrackets?.let{ text(it.second.toString()) } + listBrackets?.let{ punctuation(it.second.toString()) } } fun PageContentBuilder.DocumentableContentBuilder.annotationsBlockWithIgnored( diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt index bd967518..8db37012 100644 --- a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt +++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt @@ -15,6 +15,7 @@ import org.jetbrains.dokka.model.properties.WithExtraProperties import org.jetbrains.dokka.pages.ContentKind import org.jetbrains.dokka.pages.ContentNode import org.jetbrains.dokka.pages.TextStyle +import org.jetbrains.dokka.pages.TokenStyle import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.plugability.plugin import org.jetbrains.dokka.plugability.querySingle @@ -60,7 +61,7 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog it !in ignoredExtraModifiers || entry.key.analysisPlatform in (platformSpecificModifiers[it] ?: emptySet()) } - } + }, styles = mainStyles + TokenStyle.Keyword ) { it.toSignatureString() } @@ -78,8 +79,15 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog annotationsBlock(e) link(e.name, e.dri, styles = emptySet()) e.extra[ConstructorValues]?.let { constructorValues -> - constructorValues.values[it] - text(constructorValues.values[it]?.joinToString(prefix = "(", postfix = ")") ?: "") + constructorValues.values[it]?.let { values -> + punctuation("(") + list( + elements = values, + separator = ", ", + separatorStyles = mainStyles + TokenStyle.Punctuation, + ) { highlightValue(it) } + punctuation(")") + } } } } @@ -93,9 +101,9 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog ?: emptySet()), sourceSets = setOf(sourceSet) ) { - text("typealias ") + keyword("typealias ") link(c.name.orEmpty(), c.dri) - text(" = ") + operator(" = ") signatureForProjection(aliasedType) } @@ -118,10 +126,9 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog sourceSets = setOf(sourceSet) ) { annotationsBlock(c) - text(c.visibility[sourceSet]?.takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "") + c.visibility[sourceSet]?.takeIf { it !in ignoredVisibilities }?.name?.let { keyword("$it ") } if (c is DClass) { - text( - if (c.modifier[sourceSet] !in ignoredModifiers) + val modifier = if (c.modifier[sourceSet] !in ignoredModifiers) when { c.extra[AdditionalModifiers]?.content?.get(sourceSet)?.contains(ExtraModifiers.KotlinOnlyModifiers.Data) == true -> "" c.modifier[sourceSet] is JavaModifier.Empty -> "${KotlinModifier.Open.name} " @@ -129,33 +136,35 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog } else "" - ) + modifier.takeIf { it.isNotEmpty() }?.let { keyword(it) } } when (c) { is DClass -> { processExtraModifiers(c) - text("class ") + keyword("class ") } is DInterface -> { processExtraModifiers(c) - text("interface ") + keyword("interface ") } is DEnum -> { processExtraModifiers(c) - text("enum ") + keyword("enum ") } is DObject -> { processExtraModifiers(c) - text("object ") + keyword("object ") } is DAnnotation -> { processExtraModifiers(c) - text("annotation class ") + keyword("annotation class ") } } link(c.name!!, c.dri) if (c is WithGenerics) { - list(c.generics, prefix = "<", suffix = ">") { + list(c.generics, prefix = "<", suffix = ">", + separatorStyles = mainStyles + TokenStyle.Punctuation, + surroundingCharactersStyle = mainStyles + TokenStyle.Operator) { annotationsInline(it) +buildSignature(it) } @@ -166,18 +175,20 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog if (pConstructor.annotations().values.any { it.isNotEmpty() }) { text(nbsp.toString()) annotationsInline(pConstructor) - text("constructor") + keyword("constructor") } list( - pConstructor.parameters, - "(", - ")", - ", ", - pConstructor.sourceSets.toSet() + elements = pConstructor.parameters, + prefix = "(", + suffix = ")", + separator = ", ", + separatorStyles = mainStyles + TokenStyle.Punctuation, + surroundingCharactersStyle = mainStyles + TokenStyle.Punctuation, + sourceSets = pConstructor.sourceSets.toSet() ) { annotationsInline(it) text(it.name.orEmpty()) - text(": ") + operator(": ") signatureForProjection(it.type) } } @@ -186,7 +197,9 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog c.supertypes.filter { it.key == sourceSet }.map { (s, typeConstructors) -> list(typeConstructors, prefix = " : ", sourceSets = setOf(s)) { link(it.typeConstructor.dri.sureClassNames, it.typeConstructor.dri, sourceSets = setOf(s)) - list(it.typeConstructor.projections, prefix = "<", suffix = "> ") { + list(it.typeConstructor.projections, prefix = "<", suffix = "> ", + separatorStyles = mainStyles + TokenStyle.Punctuation, + surroundingCharactersStyle = mainStyles + TokenStyle.Operator) { signatureForProjection(it) } } @@ -203,31 +216,42 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog sourceSets = setOf(it) ) { annotationsBlock(p) - text(p.visibility[it].takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "") - text( - p.modifier[it].takeIf { it !in ignoredModifiers }?.let { + p.visibility[it].takeIf { it !in ignoredVisibilities }?.name?.let { keyword("$it ") } + p.modifier[it].takeIf { it !in ignoredModifiers }?.let { if (it is JavaModifier.Empty) KotlinModifier.Open else it - }?.name?.let { "$it " } ?: "" - ) - text(p.modifiers()[it]?.toSignatureString() ?: "") - p.setter?.let { text("var ") } ?: text("val ") - list(p.generics, prefix = "<", suffix = "> ") { + }?.name?.let { keyword("$it ") } + p.modifiers()[it]?.toSignatureString()?.let { keyword(it) } + p.setter?.let { keyword("var ") } ?: keyword("val ") + list(p.generics, prefix = "<", suffix = "> ", + separatorStyles = mainStyles + TokenStyle.Punctuation, + surroundingCharactersStyle = mainStyles + TokenStyle.Operator) { annotationsInline(it) +buildSignature(it) } p.receiver?.also { signatureForProjection(it.type) - text(".") + punctuation(".") } link(p.name, p.dri) - text(": ") + operator(": ") signatureForProjection(p.type) p.extra[DefaultValue]?.run { - text(" = $value") + operator(" = ") + highlightValue(value) } } } + private fun PageContentBuilder.DocumentableContentBuilder.highlightValue(expr: Expression) = when (expr) { + is IntegerConstant -> constant(expr.value.toString()) + is FloatConstant -> constant(expr.value.toString() + "f") + is DoubleConstant -> constant(expr.value.toString()) + is BooleanConstant -> booleanLiteral(expr.value) + is StringConstant -> stringLiteral("\"${expr.value}\"") + is ComplexExpression -> text(expr.value) + else -> Unit + } + private fun functionSignature(f: DFunction) = f.sourceSets.map { contentBuilder.contentFor( @@ -237,37 +261,40 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog sourceSets = setOf(it) ) { annotationsBlock(f) - text(f.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "") - text(f.modifier[it]?.takeIf { it !in ignoredModifiers }?.let { + f.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.let { keyword("$it ") } + f.modifier[it]?.takeIf { it !in ignoredModifiers }?.let { if (it is JavaModifier.Empty) KotlinModifier.Open else it - }?.name?.let { "$it " } ?: "" - ) - text(f.modifiers()[it]?.toSignatureString() ?: "") - text("fun ") + }?.name?.let { keyword("$it ") } + f.modifiers()[it]?.toSignatureString()?.let { keyword(it) } + keyword("fun ") val usedGenerics = if (f.isConstructor) f.generics.filter { f uses it } else f.generics - list(usedGenerics, prefix = "<", suffix = "> ") { + list(usedGenerics, prefix = "<", suffix = "> ", + separatorStyles = mainStyles + TokenStyle.Punctuation, + surroundingCharactersStyle = mainStyles + TokenStyle.Operator) { annotationsInline(it) +buildSignature(it) } f.receiver?.also { signatureForProjection(it.type) - text(".") + punctuation(".") } - link(f.name, f.dri) - text("(") - list(f.parameters) { + link(f.name, f.dri, styles = mainStyles + TokenStyle.Function) + punctuation("(") + list(f.parameters, + separatorStyles = mainStyles + TokenStyle.Punctuation) { annotationsInline(it) processExtraModifiers(it) text(it.name!!) - text(": ") + operator(": ") signatureForProjection(it.type) it.extra[DefaultValue]?.run { - text(" = $value") + operator(" = ") + highlightValue(value) } } - text(")") + punctuation(")") if (f.documentReturnType()) { - text(": ") + operator(": ") signatureForProjection(f.type) } } @@ -291,11 +318,11 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog sourceSets = platforms.toSet() ) { annotationsBlock(t) - text(t.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "") + t.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.let { keyword("$it ") } processExtraModifiers(t) - text("typealias ") + keyword("typealias ") signatureForProjection(t.type) - text(" = ") + operator(" = ") signatureForTypealiasTarget(t, type) } } @@ -306,7 +333,8 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog t.sourceSets.map { contentBuilder.contentFor(t, styles = t.stylesIfDeprecated(it), sourceSets = setOf(it)) { signatureForProjection(t.variantTypeParameter.withDri(t.dri.withTargetToDeclaration())) - list(t.nontrivialBounds, prefix = " : ") { bound -> + list(t.nontrivialBounds, prefix = " : ", + surroundingCharactersStyle = mainStyles + TokenStyle.Operator) { bound -> signatureForProjection(bound) } } @@ -338,24 +366,29 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog val linkText = if (showFullyQualifiedName && p.dri.packageName != null) { "${p.dri.packageName}.${p.dri.classNames.orEmpty()}" } else p.dri.classNames.orEmpty() - if (p.presentableName != null) text(p.presentableName + ": ") + if (p.presentableName != null) { + text(p.presentableName!!) + operator(": ") + } annotationsInline(p) link(linkText, p.dri) - list(p.projections, prefix = "<", suffix = ">") { + list(p.projections, prefix = "<", suffix = ">", + separatorStyles = mainStyles + TokenStyle.Punctuation, + surroundingCharactersStyle = mainStyles + TokenStyle.Operator) { signatureForProjection(it, showFullyQualifiedName) } } is Variance<*> -> group(styles = emptySet()) { - text("$p ".takeIf { it.isNotBlank() } ?: "") + keyword("$p ".takeIf { it.isNotBlank() } ?: "") signatureForProjection(p.inner, showFullyQualifiedName) } - is Star -> text("*") + is Star -> operator("*") is Nullable -> group(styles = emptySet()) { signatureForProjection(p.inner, showFullyQualifiedName) - text("?") + operator("?") } is TypeAliased -> signatureForProjection(p.typeAlias) @@ -373,12 +406,15 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog private fun funType(dri: DRI, sourceSets: Set, type: FunctionalTypeConstructor) = contentBuilder.contentFor(dri, sourceSets, ContentKind.Main) { - if (type.presentableName != null) text(type.presentableName + ": ") - if (type.isSuspendable) text("suspend ") + if (type.presentableName != null) { + text(type.presentableName!!) + operator(": ") + } + if (type.isSuspendable) keyword("suspend ") if (type.isExtensionFunction) { signatureForProjection(type.projections.first()) - text(".") + punctuation(".") } val args = if (type.isExtensionFunction) @@ -386,12 +422,13 @@ class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLog else type.projections - text("(") + punctuation("(") args.subList(0, args.size - 1).forEachIndexed { i, arg -> signatureForProjection(arg) - if (i < args.size - 2) text(", ") + if (i < args.size - 2) punctuation(", ") } - text(") -> ") + punctuation(")") + operator(" -> ") signatureForProjection(args.last()) } } diff --git a/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt b/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt index a02f1b53..8c2e1c99 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt @@ -108,7 +108,7 @@ open class DocTagToContentConverter : CommentsToContentConverter { is BlockQuote, is Pre, is CodeBlock -> listOf( ContentCodeBlock( buildChildren(docTag), - "", + docTag.params.getOrDefault("lang", ""), dci, sourceSets.toDisplaySourceSets(), styles diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt index 3ff8ffc3..d986056c 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt @@ -1,6 +1,7 @@ package org.jetbrains.dokka.base.translators.descriptors import com.intellij.psi.PsiNamedElement +import com.intellij.psi.util.PsiLiteralUtil.* import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.runBlocking @@ -29,6 +30,8 @@ import org.jetbrains.dokka.transformers.sources.AsyncSourceToDocumentableTransla import org.jetbrains.dokka.utilities.DokkaLogger import org.jetbrains.dokka.utilities.parallelMap import org.jetbrains.dokka.utilities.parallelMapNotNull +import org.jetbrains.kotlin.KtNodeTypes +import org.jetbrains.dokka.model.BooleanConstant import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.builtins.isBuiltinExtensionFunctionalType import org.jetbrains.kotlin.builtins.isExtensionFunctionType @@ -38,7 +41,6 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor -import org.jetbrains.kotlin.idea.core.getDirectlyOverriddenDeclarations import org.jetbrains.kotlin.idea.kdoc.findKDoc import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi @@ -53,7 +55,6 @@ import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.LocalClass import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.NormalClass import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull -import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeUniqueAsSequence import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.kotlin.resolve.source.PsiSourceElement @@ -1011,25 +1012,33 @@ private class DokkaDescriptorVisitor( if (kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE) this else overriddenDescriptors.first().getConcreteDescriptor() as T - private fun ValueParameterDescriptor.getDefaultValue(): String? = - (source as? KotlinSourceElement)?.psi?.children?.find { it is KtExpression }?.text + private fun ValueParameterDescriptor.getDefaultValue(): Expression? = + ((source as? KotlinSourceElement)?.psi as? KtParameter)?.defaultValue?.toDefaultValueExpression() - private suspend fun PropertyDescriptor.getDefaultValue(): String? = - (source as? KotlinSourceElement)?.psi?.children?.find { it is KtConstantExpression }?.text + private suspend fun PropertyDescriptor.getDefaultValue(): Expression? = + (source as? KotlinSourceElement)?.psi?.children?.filterIsInstance()?.firstOrNull() + ?.toDefaultValueExpression() private suspend fun ClassDescriptor.getAppliedConstructorParameters() = (source as PsiSourceElement).psi?.children?.flatMap { - it.safeAs()?.initializersAsText().orEmpty() + it.safeAs()?.initializersAsExpression().orEmpty() }.orEmpty() - private suspend fun KtInitializerList.initializersAsText() = + private suspend fun KtInitializerList.initializersAsExpression() = initializers.firstIsInstanceOrNull() ?.getValueArgumentsInParentheses() - ?.flatMap { it.childrenAsText() } + ?.map { it.getArgumentExpression()?.toDefaultValueExpression() ?: ComplexExpression("") } .orEmpty() - private fun ValueArgument.childrenAsText() = - this.safeAs()?.children?.map { it.text }.orEmpty() + private fun KtExpression.toDefaultValueExpression(): Expression? = when (node?.elementType) { + KtNodeTypes.INTEGER_CONSTANT -> parseLong(node?.text)?.let { IntegerConstant(it) } + KtNodeTypes.FLOAT_CONSTANT -> if (node?.text?.toLowerCase()?.endsWith('f') == true) + parseFloat(node?.text)?.let { FloatConstant(it) } + else parseDouble(node?.text)?.let { DoubleConstant(it) } + KtNodeTypes.BOOLEAN_CONSTANT -> BooleanConstant(node?.text == "true") + KtNodeTypes.STRING_TEMPLATE -> StringConstant(node.findChildByType(KtNodeTypes.LITERAL_STRING_TEMPLATE_ENTRY)?.text.orEmpty()) + else -> node?.text?.let { ComplexExpression(it) } + } private data class ClassInfo(val ancestry: List, val docs: SourceSetDependent) { val supertypes: List diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt index f98e284f..cbab6273 100644 --- a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt +++ b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt @@ -122,6 +122,13 @@ open class PageContentBuilder( header(1, text, sourceSets = sourceSets, styles = styles, extra = extra, block = block) } + fun constant(text: String) = text(text, styles = mainStyles + TokenStyle.Constant) + fun keyword(text: String) = text(text, styles = mainStyles + TokenStyle.Keyword) + fun stringLiteral(text: String) = text(text, styles = mainStyles + TokenStyle.String) + fun booleanLiteral(value: Boolean) = text(value.toString(), styles = mainStyles + TokenStyle.Boolean) + fun punctuation(text: String) = text(text, styles = mainStyles + TokenStyle.Punctuation) + fun operator(text: String) = text(text, styles = mainStyles + TokenStyle.Operator) + fun text( text: String, kind: Kind = ContentKind.Main, @@ -194,16 +201,18 @@ open class PageContentBuilder( suffix: String = "", separator: String = ", ", sourceSets: Set = mainSourcesetData, // TODO: children should be aware of this platform data + surroundingCharactersStyle: Set