diff options
15 files changed, 669 insertions, 532 deletions
diff --git a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt index 2a242948..689f6db5 100644 --- a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt +++ b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt @@ -21,9 +21,6 @@ interface JvmSignatureUtils { fun <T : Documentable> WithExtraProperties<T>.annotations(): SourceSetDependent<List<Annotations.Annotation>> = extra[Annotations]?.content ?: emptyMap() - private fun Annotations.Annotation.toSignatureString(): String = - "@" + this.dri.classNames + "(" + this.params.entries.joinToString { it.key + "=" + it.value } + ")" - private fun PageContentBuilder.DocumentableContentBuilder.annotations( d: Documentable, ignored: Set<Annotations.Annotation>, diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt index 5a55e3ec..5e03be6e 100644 --- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt @@ -317,10 +317,15 @@ class DefaultPsiToDocumentableTranslator( is PsiClassReferenceType -> { val resolved: PsiClass = type.resolve() ?: return UnresolvedBound(type.presentableText) - if (resolved.qualifiedName == "java.lang.Object") { - JavaObject - } else { - TypeConstructor(DRI.from(resolved), type.parameters.map { getProjection(it) }) + when { + resolved.qualifiedName == "java.lang.Object" -> JavaObject + resolved is PsiTypeParameter && resolved.owner != null -> + OtherParameter( + declarationDRI = DRI.from(resolved.owner!!), + name = resolved.name.orEmpty() + ) + else -> + TypeConstructor(DRI.from(resolved), type.parameters.map { getProjection(it) }) } } is PsiArrayType -> TypeConstructor( @@ -443,7 +448,7 @@ class DefaultPsiToDocumentableTranslator( attributes.filter { it !is KtLightAbstractAnnotation }.mapNotNull { it.attributeName to it.toValue() } .toMap(), (psiElement as PsiClass).annotations.any { - hasQualifiedName("java.lang.annotation.Documented") + it.hasQualifiedName("java.lang.annotation.Documented") } ) } diff --git a/plugins/javadoc/src/main/kotlin/javadoc/JavadocPageCreator.kt b/plugins/javadoc/src/main/kotlin/javadoc/JavadocPageCreator.kt index ebe223e5..1bafa111 100644 --- a/plugins/javadoc/src/main/kotlin/javadoc/JavadocPageCreator.kt +++ b/plugins/javadoc/src/main/kotlin/javadoc/JavadocPageCreator.kt @@ -9,13 +9,12 @@ import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentCon import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.doc.Description +import org.jetbrains.dokka.model.doc.NamedTagWrapper +import org.jetbrains.dokka.model.doc.Param import org.jetbrains.dokka.model.doc.TagWrapper import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.model.properties.WithExtraProperties -import org.jetbrains.dokka.pages.ContentKind -import org.jetbrains.dokka.pages.ContentNode -import org.jetbrains.dokka.pages.ContentText -import org.jetbrains.dokka.pages.DCI +import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.utilities.DokkaLogger open class JavadocPageCreator( @@ -43,20 +42,20 @@ open class JavadocPageCreator( name = c.name.orEmpty(), content = contentForClasslike(c), dri = setOf(c.dri), - modifiers = listOfNotNull(c.visibility[jvm]?.name), - signature = signatureProvider.signature(c).nodeForJvm(jvm), + signature = signatureForNode(c, jvm), description = c.descriptionToContentNodes(), constructors = (c as? WithConstructors)?.constructors?.mapNotNull { it.toJavadocFunction() }.orEmpty(), methods = c.functions.mapNotNull { it.toJavadocFunction() }, entries = (c as? DEnum)?.entries?.map { JavadocEntryNode( - signatureProvider.signature(it).nodeForJvm(jvm), it.descriptionToContentNodes(jvm) + signatureForNode(it, jvm), + it.descriptionToContentNodes(jvm) ) }.orEmpty(), classlikes = c.classlikes.mapNotNull { pageForClasslike(it) }, properties = c.properties.map { JavadocPropertyNode( - signatureProvider.signature(it).nodeForJvm(jvm), + signatureForNode(it, jvm), it.descriptionToContentNodes(jvm) ) }, @@ -71,7 +70,7 @@ open class JavadocPageCreator( JavadocContentKind.OverviewSummary, m.jvmSourceSets.toSet() ) { - title(m.name, m.brief(),"0.0.1", dri = setOf(m.dri), kind = ContentKind.Main) + title(m.name, m.brief(), "0.0.1", dri = setOf(m.dri), kind = ContentKind.Main) list("Packages", "Package", setOf(m.dri), ContentKind.Packages, m.packages.sortedBy { it.name }.map { p -> RowJavadocListEntry( LinkJavadocListEntry(p.name, setOf(p.dri), JavadocContentKind.PackageSummary, sourceSets), @@ -111,42 +110,21 @@ open class JavadocPageCreator( ) } - private fun signatureForProjection(p: Projection): String = - when (p) { - is OtherParameter -> p.name - is TypeConstructor -> if (p.function) - "TODO" - else { - val other = if (p.projections.isNotEmpty()) { - p.projections.joinToString(prefix = "<", postfix = ">") { signatureForProjection(it) } - } else { - "" - } - "${p.dri.classNames.orEmpty()} $other" - } - - is Variance -> "${p.kind} ${signatureForProjection(p.inner)}" - is Star -> "*" - is Nullable -> "${signatureForProjection(p.inner)}?" - is JavaObject -> "Object" - is Void -> "Void" - is PrimitiveJavaType -> p.name - is Dynamic -> "dynamic" - is UnresolvedBound -> p.name - } - private fun DFunction.toJavadocFunction() = highestJvmSourceSet?.let { jvm -> JavadocFunctionNode( name = name, dri = dri, - signature = signatureProvider.signature(this).nodeForJvm(jvm), + signature = signatureForNode(this, jvm), brief = brief(jvm), - parameters = parameters.map { - JavadocParameterNode( - name = it.name.orEmpty(), - type = signatureForProjection(it.type), - description = it.brief() - ) + parameters = parameters.mapNotNull { + val signature = signatureForNode(it, jvm) + signature.modifiers?.let { type -> + JavadocParameterNode( + name = it.name.orEmpty(), + type = type, + description = it.brief() + ) + } }, extras = extra ) @@ -165,19 +143,28 @@ open class JavadocPageCreator( private inline fun <reified T : TagWrapper> Documentable.findNodeInDocumentation(sourceSetData: DokkaSourceSet?): T? = documentation[sourceSetData]?.firstChildOfTypeOrNull<T>() - private fun Documentable.descriptionToContentNodes(sourceSet: DokkaSourceSet? = highestJvmSourceSet) = findNodeInDocumentation<Description>(sourceSet)?.let { - DocTagToContentConverter.buildContent( - it.root, - DCI(setOf(dri), JavadocContentKind.OverviewSummary), - sourceSets.toSet() - ) - }.orEmpty() + private fun Documentable.descriptionToContentNodes(sourceSet: DokkaSourceSet? = highestJvmSourceSet) = + contentNodesFromType<Description>(sourceSet) + + private fun DParameter.paramsToContentNodes(sourceSet: DokkaSourceSet? = highestJvmSourceSet) = + contentNodesFromType<Param>(sourceSet) + + private inline fun <reified T : TagWrapper> Documentable.contentNodesFromType(sourceSet: DokkaSourceSet?) = + findNodeInDocumentation<T>(sourceSet)?.let { + DocTagToContentConverter.buildContent( + it.root, + DCI(setOf(dri), JavadocContentKind.OverviewSummary), + sourceSets.toSet() + ) + }.orEmpty() fun List<ContentNode>.nodeForJvm(jvm: DokkaSourceSet): ContentNode = first { it.sourceSets.contains(jvm) } - private fun Documentable.brief(sourceSet: DokkaSourceSet? = highestJvmSourceSet): List<ContentNode> { - val description = descriptionToContentNodes(sourceSet) + private fun Documentable.brief(sourceSet: DokkaSourceSet? = highestJvmSourceSet): List<ContentNode> = + briefFromContentNodes(descriptionToContentNodes(sourceSet)) + + private fun briefFromContentNodes(description: List<ContentNode>): List<ContentNode> { val contents = mutableListOf<ContentNode>() for (node in description) { if (node is ContentText && firstSentenceRegex.containsMatchIn(node.text)) { @@ -189,5 +176,14 @@ open class JavadocPageCreator( } return contents } + + private fun DParameter.brief(sourceSet: DokkaSourceSet? = highestJvmSourceSet): List<ContentNode> = + briefFromContentNodes(paramsToContentNodes(sourceSet).dropWhile { it is ContentDRILink }) + + private fun ContentNode.asJavadocNode(): JavadocSignatureContentNode = + (this as ContentGroup).firstChildOfTypeOrNull<JavadocSignatureContentNode>() ?: throw IllegalStateException("No content for javadoc signature found") + + private fun signatureForNode(documentable: Documentable, sourceSet: DokkaSourceSet): JavadocSignatureContentNode = + signatureProvider.signature(documentable).nodeForJvm(sourceSet).asJavadocNode() } diff --git a/plugins/javadoc/src/main/kotlin/javadoc/JavadocPlugin.kt b/plugins/javadoc/src/main/kotlin/javadoc/JavadocPlugin.kt index 7b4aa64d..1a14e97e 100644 --- a/plugins/javadoc/src/main/kotlin/javadoc/JavadocPlugin.kt +++ b/plugins/javadoc/src/main/kotlin/javadoc/JavadocPlugin.kt @@ -3,21 +3,24 @@ package org.jetbrains.dokka.javadoc import javadoc.JavadocDocumentableToPageTranslator import javadoc.location.JavadocLocationProviderFactory import javadoc.renderer.KorteJavadocRenderer +import javadoc.signatures.JavadocSignatureProvider import org.jetbrains.dokka.CoreExtensions import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.kotlinAsJava.KotlinAsJavaPlugin import org.jetbrains.dokka.plugability.DokkaPlugin import org.jetbrains.dokka.plugability.querySingle class JavadocPlugin : DokkaPlugin() { val dokkaBasePlugin by lazy { plugin<DokkaBase>() } + val kotinAsJavaPlugin by lazy { plugin<KotlinAsJavaPlugin>() } val locationProviderFactory by extensionPoint<JavadocLocationProviderFactory>() val dokkaJavadocPlugin by extending { (CoreExtensions.renderer providing { ctx -> KorteJavadocRenderer(dokkaBasePlugin.querySingle { outputWriter }, ctx, "views") } - applyIf { format == "javadoc" } + applyIf { format == javadocFormat } override dokkaBasePlugin.htmlRenderer) } @@ -28,13 +31,28 @@ class JavadocPlugin : DokkaPlugin() { dokkaBasePlugin.querySingle { signatureProvider }, context.logger ) - } override dokkaBasePlugin.documentableToPageTranslator + } override dokkaBasePlugin.documentableToPageTranslator applyIf { format == javadocFormat } } val javadocLocationProviderFactory by extending { locationProviderFactory providing { context -> JavadocLocationProviderFactory(context) - } + } applyIf { format == javadocFormat } + } + + val javadocSignatureProvider by extending { + val dokkaBasePlugin = plugin<DokkaBase>() + dokkaBasePlugin.signatureProvider providing { ctx -> + JavadocSignatureProvider( + ctx.single( + dokkaBasePlugin.commentsToContentConverter + ), ctx.logger + ) + } override kotinAsJavaPlugin.javaSignatureProvider applyIf { format == javadocFormat } + } + + companion object { + private val javadocFormat = "javadoc" } } diff --git a/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocContentNodes.kt b/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocContentNodes.kt index c8593498..5e24ce9e 100644 --- a/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocContentNodes.kt +++ b/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocContentNodes.kt @@ -86,13 +86,6 @@ fun JavaContentGroupBuilder.title( list.add(TitleNode(title, subtitle, version, parent, dri, kind, sourceSets)) } -data class TextNode( - val text: String, - override val sourceSets: Set<DokkaSourceSet> -) : JavadocContentNode(emptySet(), ContentKind.Main, sourceSets) { - override fun hasAnyContent(): Boolean = !text.isBlank() -} - class ListNode( val tabTitle: String, val colTitle: String, @@ -137,3 +130,14 @@ class LinkJavadocListEntry( data class RowJavadocListEntry(val link: LinkJavadocListEntry, val doc: List<ContentNode>) : JavadocListEntry { override val stringTag: String = "" } + +data class JavadocSignatureContentNode( + val dri: DRI, + val kind: Kind = ContentKind.Symbol, + val annotations: ContentNode?, + val modifiers: ContentNode?, + val signatureWithoutModifiers: ContentNode, + val supertypes: ContentNode? +): JavadocContentNode(setOf(dri), kind, signatureWithoutModifiers.sourceSets) { + override fun hasAnyContent(): Boolean = true +} diff --git a/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocPageNodes.kt b/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocPageNodes.kt index 24e481e8..02e4b2d6 100644 --- a/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocPageNodes.kt +++ b/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocPageNodes.kt @@ -26,8 +26,6 @@ class JavadocModulePageNode( RootPageNode(), JavadocPageNode { - val version: String = "0.0.1" - override val documentable: Documentable? = null override val embeddedResources: List<String> = emptyList() override fun modified(name: String, children: List<PageNode>): RootPageNode = @@ -82,44 +80,35 @@ class JavadocPackagePageNode( } data class JavadocEntryNode( - val signature: ContentNode, + val signature: JavadocSignatureContentNode, val brief: List<ContentNode> ) data class JavadocParameterNode( val name: String, - val type: String, + val type: ContentNode, val description: List<ContentNode> ) data class JavadocPropertyNode( - val signature: ContentNode, + val signature: JavadocSignatureContentNode, val brief: List<ContentNode> -) { - val modifiersAndSignature: Pair<ContentNode, ContentNode> - get() = (signature as ContentGroup).splitSignatureIntoModifiersAndName() -} +) data class JavadocFunctionNode( - val signature: ContentNode, + val signature: JavadocSignatureContentNode, val brief: List<ContentNode>, val parameters: List<JavadocParameterNode>, val name: String, val dri: DRI, val extras: PropertyContainer<DFunction> = PropertyContainer.empty() -) { - - val modifiersAndSignature: Pair<ContentNode, ContentNode> - get() = (signature as ContentGroup).splitSignatureIntoModifiersAndName() - -} +) class JavadocClasslikePageNode( override val name: String, override val content: JavadocContentNode, override val dri: Set<DRI>, - val modifiers: List<String>, - val signature: ContentNode, + val signature: JavadocSignatureContentNode, val description: List<ContentNode>, val constructors: List<JavadocFunctionNode>, val methods: List<JavadocFunctionNode>, @@ -142,7 +131,6 @@ class JavadocClasslikePageNode( name, content, dri, - modifiers, signature, description, constructors, @@ -167,7 +155,6 @@ class JavadocClasslikePageNode( name, content as JavadocContentNode, dri, - modifiers, signature, description, constructors, @@ -413,15 +400,6 @@ class TreeViewPage( } } -private fun ContentGroup.splitSignatureIntoModifiersAndName(): Pair<ContentNode, ContentNode> { - val signature = children.firstIsInstance<ContentGroup>() - val modifiers = signature.children.takeWhile { it !is ContentLink } - return Pair( - signature.copy(children = modifiers), - signature.copy(children = signature.children.drop(modifiers.size)) - ) -} - private fun Documentable.kind(): String? = when (this) { is DClass -> "class" diff --git a/plugins/javadoc/src/main/kotlin/javadoc/pages/htmlGeneration.kt b/plugins/javadoc/src/main/kotlin/javadoc/pages/htmlGeneration.kt deleted file mode 100644 index 98aa2344..00000000 --- a/plugins/javadoc/src/main/kotlin/javadoc/pages/htmlGeneration.kt +++ /dev/null @@ -1,391 +0,0 @@ -package javadoc.pages - -import kotlinx.html.html -import kotlinx.html.stream.createHTML -import org.jetbrains.dokka.pages.ClasslikePageNode -import org.jetbrains.dokka.pages.ModulePageNode -import org.jetbrains.dokka.pages.PackagePageNode -import org.jetbrains.dokka.pages.PageNode -import java.time.LocalDate - -class NavbarGenerator(val page: PageNode) { - val activeClass = "navBarCell1Rev" - fun pathToRoot() = "???" // TODO - val navItems = listOf("Overview", "Package", "Class", "Tree", "Deprecated", "Index", "Help") - - // private fun items = navItems.map {itemLink} -// fun navItemLink() - val x = createHTML().html {} - - private fun navList(content: String) = """<ul class="navList" title="Navigation"> - $content - </ul> - """.trimIndent() - -// private fun navList(): String { -// when (page) { -// is PackagePageNode -> -// } -// """ -//<li><a href="../index.html">Overview</a></li> -//<li><a href="package-summary.html">Package</a></li> -//<li class="navBarCell1Rev">Class</li> -//""" -// val classItem = if (page is ClasslikePageNode) { -// "Class".wrapInTag("li", mapOf("class" to activeClass)) -// } -// val treeItem = if (page is ModulePageNode) { -// "<li><a href=\"overview-tree.html\">Tree</a></li>\n" -// } else { -// "<li><a href=\"package-tree.html\">Tree</a></li>\n" -// } -// -// val navListEnd = """ -//<li><a href="${pathToRoot()}deprecated-list.html">Deprecated</a></li> -//<li><a href="${pathToRoot()}index-all.html">Index</a></li> -//<li><a href="${pathToRoot()}help-doc.html">Help</a></li> -//""".trimIndent() -// } - - private fun bottomNavbar(page: PageNode): String = - """ - |<nav role="navigation"> - |<!-- ======= START OF BOTTOM NAVBAR ====== --> - |<div class="bottomNav"><a id="navbar.bottom"> - |<!-- --> - |</a> - |<div class="skipNav"><a href="#skip.navbar.bottom" title="Skip navigation links">Skip navigation links</a></div> - |<a id="navbar.bottom.firstrow"> - |<!-- --> - |</a> - ${navList("????")} - |</div> - |<div class="subNav"> - |<div> - |<ul class="subNavList"> - |<li>Summary: </li> - |<li>Nested | </li> - |<li>Field | </li> - |<li><a href="#constructor.summary">Constr</a> | </li> - |<li><a href="#method.summary">Method</a></li> - |</ul> - |<ul class="subNavList"> - |<li>Detail: </li> - |<li>Field | </li> - |<li><a href="#constructor.detail">Constr</a> | </li> - |<li><a href="#method.detail">Method</a></li> - |</ul> - |</div> - |</div> - |<a id="skip.navbar.bottom"> - |<!-- --> - |</a> - |<!-- ======== END OF BOTTOM NAVBAR ======= --> - |</nav>""" -} - -internal fun pageStart(title: String, version: String, documentTitle: String, pathToRoot: String) = """ - |<!DOCTYPE HTML> - |<!-- NewPage --> - |<html lang="en"> - |<head> - |<title>$documentTitle ($title $version API)</title> - |<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> - |<meta name="dc.created" content="${LocalDate.now()}"> - |<link rel="stylesheet" type="text/css" href="${pathToRoot}stylesheet.css" title="Style"> - |<link rel="stylesheet" type="text/css" href="${pathToRoot}jquery/jquery-ui.css" title="Style"> - |<script type="text/javascript" src="${pathToRoot}script.js"></script> - |<script type="text/javascript" src="${pathToRoot}jquery/jszip/dist/jszip.min.js"></script> - |<script type="text/javascript" src="${pathToRoot}jquery/jszip-utils/dist/jszip-utils.min.js"></script> - |<!--[if IE]> - |<script type="text/javascript" src="${pathToRoot}jquery/jszip-utils/dist/jszip-utils-ie.min.js"></script> - |<![endif]--> - |<script type="text/javascript" src="${pathToRoot}jquery/jquery-$jQueryVersion.js"></script> - |<script type="text/javascript" src="${pathToRoot}jquery/jquery-migrate-$jQueryMigrateVersion.js"></script> - |<script type="text/javascript" src="${pathToRoot}jquery/jquery-ui.js"></script> - |</head> - |<body> - |<script type="text/javascript"><!-- - | try { - | if (location.href.indexOf('is-external=true') == -1) { - | parent.document.title="$documentTitle ($title $version API)"; - | } - | } - | catch(err) { - | } - |//--> - |var pathtoroot = "$pathToRoot"; - |var useModuleDirectories = true; - |loadScripts(document, 'script');</script> - |<noscript> - |<div>JavaScript is disabled on your browser.</div> - |</noscript> - |<header role="banner"> - |<nav role="navigation"> - |<div class="fixedNav">""".trimMargin("|") - -internal fun topNavbar(page: PageNode, pathToRoot: String): String = """ - |<!-- ========= START OF TOP NAVBAR ======= --> - |<div class="topNav"><a id="navbar.top"> - |<!-- --> - |</a> - |<div class="skipNav"><a href="#skip.navbar.top" title="Skip navigation links">Skip navigation links</a></div> - |<a id="navbar.top.firstrow"> - |<!-- --> - |</a> - |<ul class="navList" title="Navigation"> - ${/*if ((page is JavadocPageNode) && page.pageType == PageType.Class) - "<li><a href=\"${pathToRoot}index.html\">Overview</a></li>" - else if (page is RootIndexPage) - "<li class=\"navBarCell1Rev\">Overview</li>" - else - "<li><a href=\"package-summary.html\">Package</a></li>\n"} - ${if ((page is JavadocPageNode) && page.pageType == PageType.Package) - "<li class=\"navBarCell1Rev\">Package</li>" - else - "<li>Package</li>"*/ ""} - |<li navBarCell1Rev>Class</li> - |<li><a href="overview-tree.html">Tree</a></li> - |<li><a href="${pathToRoot}deprecated-list.html">Deprecated</a></li> - |<li><a href="${pathToRoot}index-all.html">Index</a></li> - |<li><a href="${pathToRoot}help-doc.html">Help</a></li> - |</ul> - |</div> - |<div class="subNav"> - |<ul class="navListSearch"> - |<li><label for="search">SEARCH:</label> - |<input type="text" id="search" value="search" disabled="disabled"> - |<input type="reset" id="reset" value="reset" disabled="disabled"> - |</li> - |</ul> - |</div> - |<a id="skip.navbar.top"> - |<!-- --> - |</a> - |<!-- ========= END OF TOP NAVBAR ========= -->""".trimMargin("|") - -internal fun indexPage(title: String, version: String, tabTitle: String, colTitle: String, packages: List<PageNode>) = """ - |</div> - |<div class="navPadding"> </div> - |<script type="text/javascript"><!-- - |${'$'}('.navPadding').css('padding-top', ${'$'}('.fixedNav').css("height")); - |//--> - |</script> - |</nav> - |</header> - |<main role="main"> - |<div class="header"> - |<h1 class="title">$title $version API</h1> - |</div> - |<div class="contentContainer"> - |<div class="overviewSummary"> - |<table> - |<caption><span>$tabTitle</span><span class="tabEnd"> </span></caption> - |<tr> - |<th class="colFirst" scope="col">$colTitle</th> - |<th class="colLast" scope="col">Description</th> - |</tr> - |<tbody> - ${packages.mapIndexed { i, e -> e.generateLink(i) }.joinToString("\n")} - |</tbody> - |</table> - |</div> - |</div> - |</main> - |<footer role="contentinfo"> - |<nav role="navigation"> - |<!-- ======= START OF BOTTOM NAVBAR ====== --> - |<div class="bottomNav"><a id="navbar.bottom"> - |<!-- --> - |</a> - |<div class="skipNav"><a href="#skip.navbar.bottom" title="Skip navigation links">Skip navigation links</a></div> - |<a id="navbar.bottom.firstrow"> - |<!-- --> - |</a> - |<ul class="navList" title="Navigation"> - |<li class="navBarCell1Rev">Overview</li> - |<li>Package</li> - |<li>Class</li> - |<li><a href="overview-tree.html">Tree</a></li> - |<li><a href="deprecated-list.html">Deprecated</a></li> - |<li><a href="index-all.html">Index</a></li> - |<li><a href="help-doc.html">Help</a></li> - |</ul> - |</div> - |<a id="skip.navbar.bottom"> - |<!-- --> - |</a> - |<!-- ======== END OF BOTTOM NAVBAR ======= --> - |</nav> - |</footer> - |</body> - |</html> - |""".trimMargin("|") - -fun classData(name: String, extends: String) = """ - |<!-- ======== START OF CLASS DATA ======== --> - |<div class="header"> - |<div class="subTitle">adaptation</div> - |<h2 title="Class $name" class="title">Class $name</h2> - |</div> - |<div class="contentContainer"> - ${classInheritance()} - |<div class="description"> - |<ul class="blockList"> - |<li class="blockList"> - |<hr> - |<br> - |<pre>public class <span class="typeNameLabel">$name</span> - |extends $name</pre> - |</li> - |</ul> - |</div> - |<div class="summary"> - |<ul class="blockList"> - |<li class="blockList"> - |<!-- ======== NESTED CLASS SUMMARY ======== --> - |<ul class="blockList"> - |<li class="blockList"><a name="nested.class.summary"> - |<!-- --> - |</a> - |<h3>Nested Class Summary</h3> - |<table class="memberSummary" border="0" cellpadding="3" cellspacing="0" summary="Nested Class Summary table, listing nested classes, and an explanation"> - |<caption><span>Nested Classes</span><span class="tabEnd"> </span></caption> - |<tr> - |<th class="colFirst" scope="col">Modifier and Type</th> - |<th class="colLast" scope="col">Class and Description</th> - |</tr> - |<tr class="altColor"> - |<td class="colFirst"><code>class </code></td> - |<td class="colLast"><code><span class="memberNameLink"><a href="../adaptation/Adaptation.AdaptationInternalClass.html" title="class in adaptation">Adaptation.AdaptationInternalClass</a></span></code> </td> - |</tr> - |<tr class="rowColor"> - |<td class="colFirst"><code>static class </code></td> - |<td class="colLast"><code><span class="memberNameLink"><a href="../adaptation/Adaptation.AdaptationInternalStaticClass.html" title="class in adaptation">Adaptation.AdaptationInternalStaticClass</a></span></code> </td> - |</tr> - |</table> - |</li> - |</ul> - |<!-- ======== CONSTRUCTOR SUMMARY ======== --> - |<ul class="blockList"> - |<li class="blockList"><a name="constructor.summary"> - |<!-- --> - |</a> - |<h3>Constructor Summary</h3> - |<table class="memberSummary" border="0" cellpadding="3" cellspacing="0" summary="Constructor Summary table, listing constructors, and an explanation"> - |<caption><span>Constructors</span><span class="tabEnd"> </span></caption> - |<tr> - |<th class="colOne" scope="col">Constructor and Description</th> - |</tr> - |<tr class="altColor"> - |<td class="colOne"><code><span class="memberNameLink"><a href="../adaptation/Adaptation.html#Adaptation--">Adaptation</a></span>()</code> </td> - |</tr> - |</table> - |</li> - |</ul> - |<!-- ========== METHOD SUMMARY =========== --> - |<ul class="blockList"> - |<li class="blockList"><a name="method.summary"> - |<!-- --> - |</a> - |<h3>Method Summary</h3> - |<table class="memberSummary" border="0" cellpadding="3" cellspacing="0" summary="Method Summary table, listing methods, and an explanation"> - |<caption><span id="t0" class="activeTableTab"><span>All Methods</span><span class="tabEnd"> </span></span><span id="t1" class="tableTab"><span><a href="javascript:show(1);">Static Methods</a></span><span class="tabEnd"> </span></span><span id="t4" class="tableTab"><span><a href="javascript:show(8);">Concrete Methods</a></span><span class="tabEnd"> </span></span></caption> - |<tr> - |<th class="colFirst" scope="col">Modifier and Type</th> - |<th class="colLast" scope="col">Method and Description</th> - |</tr> - |<tr id="i0" class="altColor"> - |<td class="colFirst"><code>static org.javatuples.Pair<<a href="../model/ModelGraph.html" title="class in model">ModelGraph</a>,java.lang.Boolean></code></td> - |<td class="colLast"><code><span class="memberNameLink"><a href="../adaptation/Adaptation.html#transform-model.ModelGraph-transformation.Transformation-">transform</a></span>(<a href="../model/ModelGraph.html" title="class in model">ModelGraph</a> graph, - | <a href="../transformation/Transformation.html" title="interface in transformation">Transformation</a> transformation)</code> </td> - |</tr> - |</table> - |<ul class="blockList"> - |<li class="blockList"><a name="methods.inherited.from.class.java.lang.Object"> - |<!-- --> - |</a> - |<h3>Methods inherited from class java.lang.Object</h3> - |<code>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait</code></li> - |</ul> - |</li> - |</ul> - |</li> - |</ul> - |</div> - |<div class="details"> - |<ul class="blockList"> - |<li class="blockList"> - |<!-- ========= CONSTRUCTOR DETAIL ======== --> - |<ul class="blockList"> - |<li class="blockList"><a name="constructor.detail"> - |<!-- --> - |</a> - |<h3>Constructor Detail</h3> - |<a name="Adaptation--"> - |<!-- --> - |</a> - |<ul class="blockListLast"> - |<li class="blockList"> - |<h4>Adaptation</h4> - |<pre>public Adaptation()</pre> - |</li> - |</ul> - |</li> - |</ul> - |<!-- ============ METHOD DETAIL ========== --> - |<ul class="blockList"> - |<li class="blockList"><a name="method.detail"> - |<!-- --> - |</a> - |<h3>Method Detail</h3> - |<a name="transform-model.ModelGraph-transformation.Transformation-"> - |<!-- --> - |</a> - |<ul class="blockListLast"> - |<li class="blockList"> - |<h4>transform</h4> - |<pre>public static org.javatuples.Pair<<a href="../model/ModelGraph.html" title="class in model">ModelGraph</a>,java.lang.Boolean> transform(<a href="../model/ModelGraph.html" title="class in model">ModelGraph</a> graph, - | <a href="../transformation/Transformation.html" title="interface in transformation">Transformation</a> transformation)</pre> - |</li> - |</ul> - |</li> - |</ul> - |</li> - |</ul> - |</div> - |</div> - |<!-- ========= END OF CLASS DATA ========= --> -""".trimIndent() - -fun classInheritance() = """ - |<ul class="inheritance"> - |<li>java.lang.Object</li> - |<li> - |<ul class="inheritance"> - |<li>adaptation.Adaptation</li> - |</ul> - |</li> - |</ul> - """ - -internal fun String.wrapInTag(tag: String, options: Map<String, String>) = - "<$tag ${options.map { it.key + "=\"" + it.value + '"' }.joinToString(" ")}>$this</$tag>" - -fun PageNode.generateLink(i: Int) = "\n<tr class=\"altColor\" id=\"i$i\">\n" + run { - val path = /*if (this is JavadocPageNode && this.pageType != PageType.Package) "$filename.html" else*/ "$name/package-summary.html" - - name.wrapInTag("a", mapOf("href" to path)) - .wrapInTag( - "th", - mapOf("class" to "colFirst", "scope" to "row") - ) + "<td class=\"colLast\"> </td>\n</tr>" -} - -internal enum class NavigableType { - Overview, Package, Class, Tree, Deprecated, Index, Help -} - -internal interface Navigable { - val type: NavigableType -}
\ No newline at end of file diff --git a/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToHtmlTranslator.kt b/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToHtmlTranslator.kt index 7d6f37c0..df6490cf 100644 --- a/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToHtmlTranslator.kt +++ b/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToHtmlTranslator.kt @@ -1,9 +1,10 @@ package javadoc.renderer -import javadoc.pages.TextNode +import javadoc.pages.JavadocSignatureContentNode import org.jetbrains.dokka.base.resolvers.local.LocationProvider import org.jetbrains.dokka.pages.* import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.utilities.htmlEscape internal class JavadocContentToHtmlTranslator( private val locationProvider: LocationProvider, @@ -13,14 +14,14 @@ internal class JavadocContentToHtmlTranslator( fun htmlForContentNode(node: ContentNode, relative: PageNode?): String = when (node) { is ContentGroup -> htmlForContentNodes(node.children, relative) - is ContentText -> node.text - is TextNode -> node.text + is ContentText -> node.text.htmlEscape() is ContentDRILink -> buildLink( locationProvider.resolve(node.address, node.sourceSets, relative), htmlForContentNodes(node.children, relative) ) is ContentResolvedLink -> buildLink(node.address, htmlForContentNodes(node.children, relative)) is ContentCode -> htmlForCode(node.children) + is JavadocSignatureContentNode -> htmlForSignature(node, relative) else -> "" } @@ -35,6 +36,14 @@ internal class JavadocContentToHtmlTranslator( } }.joinToString("<br>", """<span class="code">""", "</span>") { it } + private fun htmlForSignature(node: JavadocSignatureContentNode, relative: PageNode?): String = + listOfNotNull( + node.annotations, + node.modifiers, + node.signatureWithoutModifiers, + node.supertypes + ).joinToString(separator = " ") { htmlForContentNode(it, relative) } + companion object { fun buildLink(address: String, content: String) = diff --git a/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt b/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt index e60b27c6..760647fa 100644 --- a/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt +++ b/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt @@ -49,20 +49,18 @@ internal class JavadocContentToTemplateMapTranslator( } } - private fun String.toNormalized() = Paths.get(this).toNormalized() - private inner class InnerTranslator(val contextNode: PageNode) { private val htmlTranslator = JavadocContentToHtmlTranslator(locationProvider, context) - internal fun templateMapForAllClassesPage(node: AllClassesPage): TemplateMap { + fun templateMapForAllClassesPage(node: AllClassesPage): TemplateMap { return mapOf( "title" to "All Classes", "list" to node.classEntries ) } - internal fun templateMapForTreeViewPage(node: TreeViewPage): TemplateMap { + fun templateMapForTreeViewPage(node: TreeViewPage): TemplateMap { return mapOf( "title" to node.title, "name" to node.name, @@ -73,45 +71,49 @@ internal class JavadocContentToTemplateMapTranslator( ) } - internal fun templateMapForPackagePageNode(node: JavadocPackagePageNode): TemplateMap { + fun templateMapForPackagePageNode(node: JavadocPackagePageNode): TemplateMap { return mapOf( "kind" to "package" ) + templateMapForJavadocContentNode(node.content) } - internal fun templateMapForFunctionNode(node: JavadocFunctionNode): TemplateMap { - val (modifiers, signature) = node.modifiersAndSignature + fun templateMapForFunctionNode(node: JavadocFunctionNode): TemplateMap { return mapOf( - "signature" to htmlForContentNode(node.signature, contextNode), "brief" to htmlForContentNodes(node.brief, contextNode), "parameters" to node.parameters.map { templateMapForParameterNode(it) }, - "inlineParameters" to node.parameters.joinToString { "${it.type} ${it.name}" }, - "modifiers" to htmlForContentNode(modifiers, contextNode), - "signatureWithoutModifiers" to htmlForContentNode(signature, contextNode), + "inlineParameters" to node.parameters.joinToString { renderInlineParameter(it) }, + "signature" to templateMapForSignatureNode(node.signature), "name" to node.name ) } - internal fun templateMapForClasslikeNode(node: JavadocClasslikePageNode): TemplateMap = + fun templateMapForClasslikeNode(node: JavadocClasslikePageNode): TemplateMap = mapOf( "constructors" to node.constructors.map { templateMapForFunctionNode(it) }, - "signature" to htmlForContentNode(node.signature, node), + "signature" to templateMapForSignatureNode(node.signature), "methods" to templateMapForClasslikeMethods(node.methods), "classlikeDocumentation" to htmlForContentNodes(node.description, node), "entries" to node.entries.map { templateMapForEntryNode(it) }, "properties" to node.properties.map { templateMapForPropertyNode(it) }, "classlikes" to node.classlikes.map { templateMapForNestedClasslikeNode(it) }, - "implementedInterfaces" to templateMapForImplementedInterfaces(node), + "implementedInterfaces" to templateMapForImplementedInterfaces(node).sorted(), "kind" to node.kind, "packageName" to node.packageName, "name" to node.name ) + templateMapForJavadocContentNode(node.content) - internal fun templateMapForJavadocContentNode(node: JavadocContentNode): TemplateMap = + fun templateMapForSignatureNode(node: JavadocSignatureContentNode): TemplateMap = + mapOf( + "annotations" to node.annotations?.let { htmlForContentNode(it, contextNode) }, + "signatureWithoutModifiers" to htmlForContentNode(node.signatureWithoutModifiers, contextNode), + "modifiers" to node.modifiers?.let { htmlForContentNode(it, contextNode) }, + "supertypes" to node.supertypes?.let { htmlForContentNode(it, contextNode) } + ) + + fun templateMapForJavadocContentNode(node: JavadocContentNode): TemplateMap = when (node) { is TitleNode -> templateMapForTitleNode(node) is JavadocContentGroup -> templateMapForJavadocContentGroup(node) - is TextNode -> templateMapForTextNode(node) is ListNode -> templateMapForListNode(node) else -> emptyMap() } @@ -120,7 +122,7 @@ internal class JavadocContentToTemplateMapTranslator( mapOf( "description" to htmlForContentNodes(node.description, contextNode), "name" to node.name, - "type" to node.type + "type" to htmlForContentNode(node.type, contextNode) ) private fun templateMapForImplementedInterfaces(node: JavadocClasslikePageNode) = @@ -157,25 +159,24 @@ internal class JavadocContentToTemplateMapTranslator( private fun templateMapForNestedClasslikeNode(node: JavadocClasslikePageNode): TemplateMap { return mapOf( - "modifiers" to (node.modifiers + "static" + node.kind).joinToString(separator = " "), + "modifiers" to node.signature.modifiers?.let { htmlForContentNode(it, contextNode) }, "signature" to node.name, "description" to htmlForContentNodes(node.description, node) ) } private fun templateMapForPropertyNode(node: JavadocPropertyNode): TemplateMap { - val (modifiers, signature) = node.modifiersAndSignature return mapOf( - "modifiers" to htmlForContentNode(modifiers, contextNode), - "signature" to htmlForContentNode(signature, contextNode), + "modifiers" to node.signature.modifiers?.let { htmlForContentNode(it, contextNode) }, + "signature" to htmlForContentNode(node.signature.signatureWithoutModifiers, contextNode), "description" to htmlForContentNodes(node.brief, contextNode) ) } private fun templateMapForEntryNode(node: JavadocEntryNode): TemplateMap { return mapOf( - "signature" to htmlForContentNode(node.signature, contextNode), - "brief" to node.brief + "signature" to templateMapForSignatureNode(node.signature), + "brief" to htmlForContentNodes(node.brief, contextNode) ) } @@ -194,10 +195,6 @@ internal class JavadocContentToTemplateMapTranslator( } } - private fun templateMapForTextNode(node: TextNode): TemplateMap { - return mapOf("text" to node.text) - } - private fun templateMapForListNode(node: ListNode): TemplateMap { return mapOf( "tabTitle" to node.tabTitle, @@ -206,6 +203,9 @@ internal class JavadocContentToTemplateMapTranslator( ) } + private fun renderInlineParameter(parameter: JavadocParameterNode): String = + htmlForContentNode(parameter.type, contextNode) + " ${parameter.name}" + private fun htmlForContentNode(node: ContentNode, relativeNode: PageNode) = htmlTranslator.htmlForContentNode(node, relativeNode) diff --git a/plugins/javadoc/src/main/kotlin/javadoc/signatures/JavadocSignatureProvider.kt b/plugins/javadoc/src/main/kotlin/javadoc/signatures/JavadocSignatureProvider.kt new file mode 100644 index 00000000..11db9fe1 --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/signatures/JavadocSignatureProvider.kt @@ -0,0 +1,196 @@ +package javadoc.signatures + +import javadoc.translators.documentables.JavadocPageContentBuilder +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.signatures.JvmSignatureUtils +import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.kotlinAsJava.signatures.JavaSignatureUtils +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.utilities.DokkaLogger + +class JavadocSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLogger) : SignatureProvider, + JvmSignatureUtils by JavaSignatureUtils { + + private val contentBuilder = JavadocPageContentBuilder(ctcc, this, logger) + + private val ignoredVisibilities = setOf(JavaVisibility.Default) + + private val ignoredModifiers = + setOf(KotlinModifier.Open, JavaModifier.Empty, KotlinModifier.Empty, KotlinModifier.Sealed) + + override fun signature(documentable: Documentable): List<ContentNode> = when (documentable) { + is DFunction -> signature(documentable) + is DProperty -> signature(documentable) + is DClasslike -> signature(documentable) + is DEnumEntry -> signature(documentable) + is DTypeParameter -> signature(documentable) + is DParameter -> signature(documentable) + else -> throw NotImplementedError( + "Cannot generate signature for ${documentable::class.qualifiedName} ${documentable.name}" + ) + } + + private fun signature(c: DClasslike): List<ContentNode> = + javadocSignature(c) { + annotations { + annotationsBlock(c) + } + modifiers { + text(c.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.plus(" ") ?: "") + + if (c is DClass) { + text(c.modifier[it]?.takeIf { it !in ignoredModifiers }?.name?.plus(" ") ?: "") + text(c.modifiers()[it]?.toSignatureString() ?: "") + } + + when (c) { + is DClass -> text("class") + is DInterface -> text("interface") + is DEnum -> text("enum") + is DObject -> text("class") + is DAnnotation -> text("@interface") + } + } + signatureWithoutModifiers { + link(c.name!!, c.dri) + if (c is WithGenerics) { + list(c.generics, prefix = "<", suffix = ">") { + +buildSignature(it) + } + } + } + supertypes { + if (c is WithSupertypes) { + c.supertypes.map { (p, dris) -> + list(dris, sourceSets = setOf(p)) { + link(it.fqName(), it, sourceSets = setOf(p)) + } + } + } + } + } + + private fun signature(f: DFunction): List<ContentNode> = + javadocSignature(f) { + annotations { + annotationsBlock(f) + } + modifiers { + text(f.modifier[it]?.takeIf { it !in ignoredModifiers }?.name?.plus(" ") ?: "") + text(f.modifiers()[it]?.toSignatureString() ?: "") + list(f.generics, prefix = "<", suffix = "> ") { + +buildSignature(it) + } + signatureForProjection(f.type) + } + signatureWithoutModifiers { + link(f.name, f.dri) + text("(") + list(f.parameters) { + annotationsInline(it) + text(it.modifiers()[it]?.toSignatureString() ?: "") + signatureForProjection(it.type) + text(Typography.nbsp.toString()) + link(it.name!!, it.dri) + } + text(")") + } + } + + private fun signature(p: DProperty): List<ContentNode> = + javadocSignature(p) { + annotations { + annotationsBlock(p) + } + modifiers { + text(p.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.plus(" ") ?: "") + text(p.modifier[it]?.name + " ") + text(p.modifiers()[it]?.toSignatureString() ?: "") + signatureForProjection(p.type) + } + signatureWithoutModifiers { + link(p.name, p.dri) + } + } + + private fun signature(e: DEnumEntry): List<ContentNode> = + javadocSignature(e) { + annotations { + annotationsBlock(e) + } + modifiers { + text(e.modifiers()[it]?.toSignatureString() ?: "") + } + signatureWithoutModifiers { + link(e.name, e.dri) + } + } + + private fun signature(t: DTypeParameter): List<ContentNode> = + javadocSignature(t) { + annotations { + annotationsBlock(t) + } + signatureWithoutModifiers { + text(t.name) + } + supertypes { + list(t.bounds, prefix = "extends ") { + signatureForProjection(it) + } + } + } + + private fun signature(p: DParameter): List<ContentNode> = + javadocSignature(p) { + modifiers { + signatureForProjection(p.type) + } + signatureWithoutModifiers { + link(p.name.orEmpty(), p.dri) + } + } + + private fun javadocSignature( + d: Documentable, + extra: PropertyContainer<ContentNode> = PropertyContainer.empty(), + block: JavadocPageContentBuilder.JavadocContentBuilder.(DokkaConfiguration.DokkaSourceSet) -> Unit + ): List<ContentNode> = + d.sourceSets.map { sourceSet -> + contentBuilder.contentFor(d, ContentKind.Main) { + with(contentBuilder) { + javadocGroup(d.dri, d.sourceSets, extra) { + block(sourceSet) + } + } + } + } + + private fun PageContentBuilder.DocumentableContentBuilder.signatureForProjection(p: Projection): Unit = when (p) { + is OtherParameter -> link(p.name, p.declarationDRI) + is TypeConstructor -> group { + link(p.dri.fqName(), p.dri) + list(p.projections, prefix = "<", suffix = ">") { + signatureForProjection(it) + } + } + is Variance -> group { + text(p.kind.toString() + " ") + signatureForProjection(p.inner) + } + is Star -> text("?") + is Nullable -> signatureForProjection(p.inner) + is JavaObject, is Dynamic -> link("java.lang.Object", DRI("java.lang", "Object")) + is Void -> text("void") + is PrimitiveJavaType -> text(p.name) + is UnresolvedBound -> text(p.name) + } + + private fun DRI.fqName(): String = "${packageName.orEmpty()}.${classNames.orEmpty()}" +} diff --git a/plugins/javadoc/src/main/kotlin/javadoc/translators/documentables/JavadocPageContentBuilder.kt b/plugins/javadoc/src/main/kotlin/javadoc/translators/documentables/JavadocPageContentBuilder.kt new file mode 100644 index 00000000..17b474b0 --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/translators/documentables/JavadocPageContentBuilder.kt @@ -0,0 +1,80 @@ +package javadoc.translators.documentables + +import javadoc.pages.JavadocSignatureContentNode +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.utilities.DokkaLogger +import java.lang.IllegalStateException + +class JavadocPageContentBuilder( + commentsConverter: CommentsToContentConverter, + signatureProvider: SignatureProvider, + logger: DokkaLogger +) : PageContentBuilder(commentsConverter, signatureProvider, logger) { + + fun PageContentBuilder.DocumentableContentBuilder.javadocGroup( + dri: DRI = mainDRI.first(), + sourceSets: Set<DokkaConfiguration.DokkaSourceSet> = mainSourcesetData, + extra: PropertyContainer<ContentNode> = mainExtra, + block: JavadocContentBuilder.() -> Unit + ) { + +JavadocContentBuilder( + mainDri = dri, + mainExtra = extra, + mainSourceSet = sourceSets, + ).apply(block).build() + } + + open inner class JavadocContentBuilder( + private val mainDri: DRI, + private val mainExtra: PropertyContainer<ContentNode>, + private val mainSourceSet: Set<DokkaConfiguration.DokkaSourceSet>, + ) { + var annotations: ContentNode? = null + var modifiers: ContentNode? = null + var signatureWithoutModifiers: ContentNode? = null + var supertypes: ContentNode? = null + + fun annotations(block: PageContentBuilder.DocumentableContentBuilder.() -> Unit) { + val built = buildContentForBlock(block) + if(built.hasAnyContent()) annotations = built + } + + fun modifiers(block: PageContentBuilder.DocumentableContentBuilder.() -> Unit) { + val built = buildContentForBlock(block) + if(built.hasAnyContent()) modifiers = built + } + + fun signatureWithoutModifiers(block: PageContentBuilder.DocumentableContentBuilder.() -> Unit) { + signatureWithoutModifiers = buildContentForBlock(block) + } + + fun supertypes(block: PageContentBuilder.DocumentableContentBuilder.() -> Unit) { + val built = buildContentForBlock(block) + if(built.hasAnyContent()) supertypes = built + } + + private fun buildContentForBlock(block: PageContentBuilder.DocumentableContentBuilder.() -> Unit) = + contentFor( + dri = mainDri, + sourceSets = mainSourceSet, + kind = ContentKind.Symbol, + extra = mainExtra, + block = block + ) + + fun build(): JavadocSignatureContentNode = JavadocSignatureContentNode( + dri = mainDri, + annotations = annotations, + modifiers = modifiers, + signatureWithoutModifiers = signatureWithoutModifiers ?: throw IllegalStateException("JavadocSignatureContentNode should have at least a signature"), + supertypes = supertypes + ) + } +}
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/views/class.korte b/plugins/javadoc/src/main/resources/views/class.korte index 6585e6b4..22793d69 100644 --- a/plugins/javadoc/src/main/resources/views/class.korte +++ b/plugins/javadoc/src/main/resources/views/class.korte @@ -32,7 +32,11 @@ </dl> {% endif %} <hr> - <pre>{{ signature|raw }}</pre> + <pre> +{% if signature.annotations != null %}{{ signature.annotations|raw }} {% endif %} +{{ signature.modifiers }} <span class="typeNameLabel">{{ signature.signatureWithoutModifiers|raw }}</span> +{% if signature.supertypes != null %}extends {{signature.supertypes|raw}} {% endif %} + </pre> <div class="block">{{ classlikeDocumentation|raw }}</div> </li> </ul> @@ -114,7 +118,7 @@ {% for constructor in constructors %} <tr class="{{ rowColor(loop.index0) }}"> <th class="colConstructorName" scope="row"><code><span class="memberNameLink"><a - href="#%3Cinit%3E({{ constructor.inlineParameters }})">{{ constructor.name }}</a></span>({{ constructor.inlineParameters }})</code></th> + href="#%3Cinit%3E({{ constructor.inlineParameters }})">{{ constructor.name }}</a></span>({{ constructor.inlineParameters|raw }})</code></th> <td class="colLast">{{ constructor.brief|raw }}</td> </tr> {% endfor %} @@ -142,7 +146,7 @@ {% for entry in entries %} <tr class="{{ rowColor(loop.index0) }}"> <th class="colFirst" scope="row"><code><span class="memberNameLink"><a - href="TODO">{{ entry.signature|raw }}</a></span></code></th> + href="TODO">{{ entry.signature.signatureWithoutModifiers|raw }}</a></span></code></th> <td class="colLast">{{ entry.brief|raw }}</td> </tr> {% endfor %} @@ -173,9 +177,9 @@ </tr> {% for method in methods.own %} <tr id="i{{ loop.index0 }}" class="{{ rowColor(loop.index0) }}"> - <td class="colFirst"><code>{{ method.modifiers|raw }}</code> + <td class="colFirst"><code>{{ method.signature.modifiers|raw }}</code> </td> - <th class="colSecond" scope="row"><code>{{ method.signatureWithoutModifiers|raw }} </code> + <th class="colSecond" scope="row"><code>{{ method.signature.signatureWithoutModifiers|raw }} </code> </th> <td class="colLast">{{ method.brief|raw }}</td> </tr> @@ -222,14 +226,14 @@ <ul class="blockList"> <li class="blockList"> <h4>{{ constructor.name }}</h4> - <pre>{{ constructor.name }}({{ constructor.inlineParameters }})</pre> + <pre>{{ constructor.name }}({{ constructor.inlineParameters|raw }})</pre> <div class="block">{{ constructor.brief|raw}}</div> {% if constructor.parameters.size != 0 && hasAnyDescription(constructor.parameters) %} <dl> <dt><span class="paramLabel">Parameters:</span></dt> {% for parameter in constructor.parameters %} {% if parameter.description != "" %} - <dd><code>{{ parameter.name }}</code> - {{ parameter.description }}</dd> + <dd><code>{{ parameter.name }}</code> - {{ parameter.description|raw }}</dd> {% endif %} {% endfor %} </dl> @@ -256,8 +260,18 @@ <ul class="blockListLast"> <li class="blockList"> <h4>{{ method.name }}</h4> - <pre class="methodSignature">{{ method.signature|raw }}</pre> + <pre class="methodSignature">{{ method.signature.annotations|raw }} {{ method.signature.modifiers|raw }} {{ method.signature.signatureWithoutModifiers|raw}}</pre> <div class="block">{{ method.brief|raw }}</div> + {% if method.parameters.size != 0 && hasAnyDescription(method.parameters) %} + <dl> + <dt><span class="paramLabel">Parameters:</span></dt> + {% for parameter in method.parameters %} + {% if parameter.description != "" %} + <dd><code>{{ parameter.name }}</code> - {{ parameter.description|raw }}</dd> + {% endif %} + {% endfor %} + </dl> + {% endif %} <!-- TODO missing return annotation --> </li> </ul> diff --git a/plugins/javadoc/src/test/kotlin/javadoc/AbstractJavadocTemplateMapTest.kt b/plugins/javadoc/src/test/kotlin/javadoc/AbstractJavadocTemplateMapTest.kt index 4fa65c58..7cd42942 100644 --- a/plugins/javadoc/src/test/kotlin/javadoc/AbstractJavadocTemplateMapTest.kt +++ b/plugins/javadoc/src/test/kotlin/javadoc/AbstractJavadocTemplateMapTest.kt @@ -101,7 +101,7 @@ internal abstract class AbstractJavadocTemplateMapTest : AbstractCoreTest() { throw AssertionError( "Kotlin and Java Code failed assertions\n" + "Kotlin: ${kotlinException.message}\n" + - "Java: ${javaException.message}", + "Java : ${javaException.message}", kotlinException ) } diff --git a/plugins/javadoc/src/test/kotlin/javadoc/JavadocClasslikeTemplateMapTest.kt b/plugins/javadoc/src/test/kotlin/javadoc/JavadocClasslikeTemplateMapTest.kt index 0f95894b..dc1573e1 100644 --- a/plugins/javadoc/src/test/kotlin/javadoc/JavadocClasslikeTemplateMapTest.kt +++ b/plugins/javadoc/src/test/kotlin/javadoc/JavadocClasslikeTemplateMapTest.kt @@ -36,7 +36,7 @@ internal class JavadocClasslikeTemplateMapTest : AbstractJavadocTemplateMapTest( assertEquals("com.test.package0", map["packageName"]) assertEquals("Documentation for TestClass", map["classlikeDocumentation"]) assertEquals("Documentation for TestClass", map["subtitle"]) - assertEquals("public final class <a href=TestClass.html>TestClass</a>", map["signature"]) + assertEquals("public final class <a href=TestClass.html>TestClass</a>", map.signatureWithModifiers()) } } @@ -81,21 +81,252 @@ internal class JavadocClasslikeTemplateMapTest : AbstractJavadocTemplateMapTest( assertEquals("com.test.package0", map["packageName"]) assertEquals("Documentation for TestClass", map["classlikeDocumentation"]) assertEquals("Documentation for TestClass", map["subtitle"]) - assertEquals("public final class <a href=TestClass.html>TestClass</a>", map["signature"]) + assertEquals("public final class", map.modifiers()) + assertEquals("<a href=TestClass.html>TestClass</a>", map.signatureWithoutModifiers()) val methods = assertIsInstance<Map<Any, Any?>>(map["methods"]) val ownMethods = assertIsInstance<List<*>>(methods["own"]) assertEquals(1, ownMethods.size, "Expected only one method") - val method = assertIsInstance<Map<Any, Any?>>(ownMethods.single()) + val method = assertIsInstance<Map<String, Any?>>(ownMethods.single()) assertEquals("Documentation for testFunction", method["brief"]) assertEquals("testFunction", method["name"]) assertEquals( 0, assertIsInstance<List<*>>(method["parameters"]).size, "Expected no parameters" ) + assertEquals("final <a href=.html>java.lang.String</a>", method.modifiers()) + assertEquals("<a href=.html>testFunction</a>()", method.signatureWithoutModifiers()) + } + } + + @Test + fun `class with annotation`(){ + dualTestTemplateMapInline( + kotlin = + """ + /src/source0.kt + package com.test.package0 + @MustBeDocumented + annotation class Author(val name: String) + + @Author( + name = "Benjamin Franklin" + ) + class TestClass {` + + @Author( + name = "Franklin D. Roosevelt" + ) + fun testFunction(): String = "" + } + """, + java = + """ + /src/com/test/package0/Author.java + package com.test.package0 + import java.lang.annotation.Documented; + + @Documented + public @interface Author { + String name(); + } + /src/com/test/package0/TestClass.java + package com.test.package0 + + @Author( + name = "Benjamin Franklin" + ) + public final class TestClass { + + @Author( + name = "Franklin D. Roosevelt" + ) + public final String testFunction() { + return ""; + } + } + """ + ){ + val map = allPagesOfType<JavadocClasslikePageNode>().first { it.name == "TestClass" }.templateMap + assertEquals("TestClass", map["name"]) + val signature = assertIsInstance<Map<String, Any?>>(map["signature"]) + assertEquals("@<a href=Author.html>Author</a>(name = \"Benjamin Franklin\")", signature["annotations"]) + + val methods = assertIsInstance<Map<Any, Any?>>(map["methods"]) + val ownMethods = assertIsInstance<List<*>>(methods["own"]) + val method = assertIsInstance<Map<String, Any?>>(ownMethods.single()) + val methodSignature = assertIsInstance<Map<String, Any?>>(method["signature"]) + assertEquals("@<a href=Author.html>Author</a>(name = \"Franklin D. Roosevelt\")", methodSignature["annotations"]) + } + } + + @Test + fun `simple enum`(){ + dualTestTemplateMapInline( + kotlin = + """ + /src/source0.kt + package com.test.package0 + enum class ClockDays { + /** + * Sample docs for first + */ + FIRST, + /** + * Sample docs for second + */ + SECOND + } + """, + java = + """ + /src/com/test/package0/TestClass.java + package com.test.package0; + enum ClockDays { + /** + * Sample docs for first + */ + FIRST, + /** + * Sample docs for second + */ + SECOND + } + """ + ){ + val map = singlePageOfType<JavadocClasslikePageNode>().templateMap + assertEquals("ClockDays", map["name"]) + assertEquals("enum", map["kind"]) + val entries = assertIsInstance<List<Map<String, Any?>>>(map["entries"]) + assertEquals(2, entries.size) + + val (first, second) = entries + assertEquals("Sample docs for first", first["brief"]) + assertEquals("Sample docs for second", second["brief"]) + + assertEquals("<a href=.html>FIRST</a>", first.signatureWithoutModifiers()) + assertEquals("<a href=.html>SECOND</a>", second.signatureWithoutModifiers()) + } + } + + @Test + fun `documented function parameters`(){ + dualTestTemplateMapInline( + kotlin = + """ + /src/source0.kt + package com.test.package0 + class TestClass { + /** + * Simple parameters list to check out + * @param simple simple String parameter + * @param parameters simple Integer parameter + * @param list simple Boolean parameter + * @return just a String + */ + fun testFunction(simple: String?, parameters: Int?, list: Boolean?): String { + return "" + } + } + """, + java = + """ + /src/com/test/package0/TestClass.java + package com.test.package0; + public final class TestClass { + /** + * Simple parameters list to check out + * @param simple simple String parameter + * @param parameters simple Integer parameter + * @param list simple Boolean parameter + * @return just a String + */ + public final String testFunction(String simple, Integer parameters, Boolean list) { + return ""; + } + } + """ + ) { + val map = singlePageOfType<JavadocClasslikePageNode>().templateMap + assertEquals("TestClass", map["name"]) + + val methods = assertIsInstance<Map<String, Any?>>(map["methods"]) + val testFunction = assertIsInstance<List<Map<String, Any?>>>(methods["own"]).single() + assertEquals("Simple parameters list to check out", testFunction["brief"]) + + val (first, second, third) = assertIsInstance<List<Map<String, Any?>>>(testFunction["parameters"]) + assertParameterNode( + node = first, + expectedName = "simple", + expectedType = "<a href=.html>java.lang.String</a>", + expectedDescription = "simple String parameter" + ) + assertParameterNode( + node = second, + expectedName = "parameters", + expectedType = "<a href=.html>java.lang.Integer</a>", + expectedDescription = "simple Integer parameter" + ) + assertParameterNode( + node = third, + expectedName = "list", + expectedType = "<a href=.html>java.lang.Boolean</a>", + expectedDescription = "simple Boolean parameter" + ) + } + } + + @Test + fun `with generic parameters`(){ + dualTestTemplateMapInline( + kotlin = + """ + /src/source0.kt + package com.test.package0 + import java.io.Serializable + + class Generic<T : Serializable?> { + fun <D : T> sampleFunction(): D = TODO() + } + """, + java = + """ + /src/com/test/package0/Generic.java + package com.test.package0; + import java.io.Serializable; + + public final class Generic<T extends Serializable> { + public final <D extends T> D sampleFunction(){ + return null; + } + } + """ + ) { + val map = singlePageOfType<JavadocClasslikePageNode>().templateMap + assertEquals("Generic", map["name"]) + assertEquals( - "final <a href=.html>String</a> <a href=.html>testFunction</a>()", method["signature"] + "public final class <a href=Generic.html>Generic</a><T extends <a href=.html>java.io.Serializable</a>>", + map.signatureWithModifiers() ) + val methods = assertIsInstance<Map<Any, Any?>>(map["methods"]) + val ownMethods = assertIsInstance<List<*>>(methods["own"]).first() + val sampleFunction = assertIsInstance<Map<String, Any?>>(ownMethods) + + assertEquals("final <D extends <a href=Generic.html>T</a>> <a href=.html>D</a> <a href=.html>sampleFunction</a>()", sampleFunction.signatureWithModifiers()) } } + + private fun assertParameterNode(node: Map<String, Any?>, expectedName: String, expectedType: String, expectedDescription: String){ + assertEquals(expectedName, node["name"]) + assertEquals(expectedType, node["type"]) + assertEquals(expectedDescription, node["description"]) + } + + private fun Map<String, Any?>.signatureWithModifiers(): String = "${modifiers()} ${signatureWithoutModifiers()}" + + private fun Map<String, Any?>.signatureWithoutModifiers(): String = (get("signature") as Map<String, Any?>)["signatureWithoutModifiers"] as String + + private fun Map<String, Any?>.modifiers(): String = (get("signature") as Map<String, Any?>)["modifiers"] as String + } diff --git a/plugins/javadoc/src/test/kotlin/javadoc/location/JavadocLocationTest.kt b/plugins/javadoc/src/test/kotlin/javadoc/location/JavadocLocationTest.kt index a47f0142..92620f78 100644 --- a/plugins/javadoc/src/test/kotlin/javadoc/location/JavadocLocationTest.kt +++ b/plugins/javadoc/src/test/kotlin/javadoc/location/JavadocLocationTest.kt @@ -54,8 +54,8 @@ class JavadocTest : AbstractCoreTest() { val testClass = rootPageNode.firstChildOfType<JavadocPackagePageNode>() .firstChildOfType<JavadocClasslikePageNode>() assert( - "public final class <a href=javadoc/Test.html>Test</a> extends <a href=https://docs.oracle.com/javase/8/docs/api/java/lang/Cloneable.html>Cloneable</a>" - == transformer.htmlForContentNode(testClass.signature, null) + "<a href=https://docs.oracle.com/javase/8/docs/api/java/lang/Cloneable.html>java.lang.Cloneable</a>" + == transformer.htmlForContentNode(testClass.signature.supertypes!!, null) ) } } |