Skip to content

Commit

Permalink
Add nav icons tests and refactor file structure
Browse files Browse the repository at this point in the history
  • Loading branch information
IgnatBeresnev committed Jul 23, 2022
1 parent aaff06d commit d399547
Show file tree
Hide file tree
Showing 5 changed files with 380 additions and 121 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.jetbrains.dokka.base.renderers.html

import org.jetbrains.dokka.analysis.PsiDocumentableSource
import org.jetbrains.dokka.base.renderers.sourceSets
import org.jetbrains.dokka.base.transformers.documentables.isException
import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.transformers.pages.PageTransformer

open class NavigationPageInstaller(val context: DokkaContext) : NavigationDataProvider(), PageTransformer {

override fun invoke(input: RootPageNode): RootPageNode =
input.modified(
children = input.children + NavigationPage(
root = navigableChildren(input),
moduleName = context.configuration.moduleName,
context = context
)
)
}

abstract class NavigationDataProvider {
open fun navigableChildren(input: RootPageNode): NavigationNode = input.withDescendants()
.first { it is ModulePage || it is MultimoduleRootPage }.let { visit(it as ContentPage) }

open fun visit(page: ContentPage): NavigationNode =
NavigationNode(
name = page.displayableName(),
dri = page.dri.first(),
sourceSets = page.sourceSets(),
icon = chooseNavigationIcon(page),
children = page.navigableChildren()
)

/**
* Parenthesis is applied in 1 case:
* - page only contains functions (therefore documentable from this page is [DFunction])
*/
private fun ContentPage.displayableName(): String =
if (this is WithDocumentables && documentables.all { it is DFunction }) {
"$name()"
} else {
name
}

private fun chooseNavigationIcon(contentPage: ContentPage): NavigationNodeIcon? {
return if (contentPage is WithDocumentables) {
val documentable = contentPage.documentables.firstOrNull()
val isJava = (documentable as? WithSources)?.sources?.any { it.value is PsiDocumentableSource } ?: false

when (documentable) {
is DClass -> when {
documentable.isException -> NavigationNodeIcon.EXCEPTION
documentable.isAbstract() -> {
if (isJava) NavigationNodeIcon.ABSTRACT_CLASS else NavigationNodeIcon.ABSTRACT_CLASS_KT
}
else -> if (isJava) NavigationNodeIcon.CLASS else NavigationNodeIcon.CLASS_KT
}
is DFunction -> NavigationNodeIcon.FUNCTION
is DProperty -> {
val isVar = documentable.extra[IsVar] != null
if (isVar) NavigationNodeIcon.VAR else NavigationNodeIcon.VAL
}
is DInterface -> if (isJava) NavigationNodeIcon.INTERFACE else NavigationNodeIcon.INTERFACE_KT
is DEnum -> if (isJava) NavigationNodeIcon.ENUM_CLASS else NavigationNodeIcon.ENUM_CLASS_KT
is DAnnotation -> {
if (isJava) NavigationNodeIcon.ANNOTATION_CLASS else NavigationNodeIcon.ANNOTATION_CLASS_KT
}
is DObject -> NavigationNodeIcon.OBJECT
else -> null
}
} else {
null
}
}

private fun DClass.isAbstract(): Boolean {
return modifier.values.all { it is KotlinModifier.Abstract || it is JavaModifier.Abstract }
}

private fun ContentPage.navigableChildren(): List<NavigationNode> {
return if (this !is ClasslikePageNode) {
children
.filterIsInstance<ContentPage>()
.map { visit(it) }
.sortedBy { it.name.toLowerCase() }
} else {
emptyList()
}
}
}
57 changes: 26 additions & 31 deletions plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ import org.jetbrains.dokka.model.WithChildren
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.plugability.DokkaContext

class NavigationPage(val root: NavigationNode, val moduleName: String, val context: DokkaContext) :
RendererSpecificPage {
class NavigationPage(
val root: NavigationNode,
val moduleName: String,
val context: DokkaContext
) : RendererSpecificPage {

override val name = "navigation"

override val children = emptyList<PageNode>()
Expand Down Expand Up @@ -64,24 +68,6 @@ class NavigationPage(val root: NavigationNode, val moduleName: String, val conte
node.children.withIndex().forEach { (n, p) -> visit(p, "$navId-$n", renderer) }
}
}

private fun NavigationNodeIcon.style(): String = when(this) {
NavigationNodeIcon.CLASS -> "nav-icon class"
NavigationNodeIcon.CLASS_KT -> "nav-icon class-kt"
NavigationNodeIcon.ABSTRACT_CLASS -> "nav-icon abstract-class"
NavigationNodeIcon.ABSTRACT_CLASS_KT -> "nav-icon abstract-class-kt"
NavigationNodeIcon.ENUM_CLASS -> "nav-icon enum-class"
NavigationNodeIcon.ENUM_CLASS_KT -> "nav-icon enum-class-kt"
NavigationNodeIcon.ANNOTATION_CLASS -> "nav-icon annotation-class"
NavigationNodeIcon.ANNOTATION_CLASS_KT -> "nav-icon annotation-class-kt"
NavigationNodeIcon.FUNCTION -> "nav-icon function"
NavigationNodeIcon.INTERFACE -> "nav-icon interface"
NavigationNodeIcon.INTERFACE_KT -> "nav-icon interface-kt"
NavigationNodeIcon.EXCEPTION -> "nav-icon exception-class"
NavigationNodeIcon.OBJECT -> "nav-icon object"
NavigationNodeIcon.VAL -> "nav-icon val"
NavigationNodeIcon.VAR -> "nav-icon var"
}
}

data class NavigationNode(
Expand All @@ -96,17 +82,26 @@ data class NavigationNode(
* [CLASS] represents a neutral (a.k.a Java-style) icon,
* whereas [CLASS_KT] should be Kotlin-styled
*/
enum class NavigationNodeIcon {
CLASS, CLASS_KT,
ABSTRACT_CLASS, ABSTRACT_CLASS_KT,
ENUM_CLASS, ENUM_CLASS_KT,
ANNOTATION_CLASS, ANNOTATION_CLASS_KT,
INTERFACE, INTERFACE_KT,
FUNCTION,
EXCEPTION,
OBJECT,
VAL,
VAR
enum class NavigationNodeIcon(
private val cssClass: String
) {
CLASS("class"),
CLASS_KT("class-kt"),
ABSTRACT_CLASS("abstract-class"),
ABSTRACT_CLASS_KT("abstract-class-kt"),
ENUM_CLASS("enum-class"),
ENUM_CLASS_KT("enum-class-kt"),
ANNOTATION_CLASS("annotation-class"),
ANNOTATION_CLASS_KT("annotation-class-kt"),
INTERFACE("interface"),
INTERFACE_KT("interface-kt"),
FUNCTION("function"),
EXCEPTION("exception-class"),
OBJECT("object"),
VAL("val"),
VAR("var");

internal fun style(): String = "nav-icon $cssClass"
}

fun NavigationPage.transform(block: (NavigationNode) -> NavigationNode) =
Expand Down
91 changes: 3 additions & 88 deletions plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt
Original file line number Diff line number Diff line change
@@ -1,100 +1,15 @@
package org.jetbrains.dokka.base.renderers.html

import org.jetbrains.dokka.analysis.PsiDocumentableSource
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.base.DokkaBaseConfiguration
import org.jetbrains.dokka.base.renderers.sourceSets
import org.jetbrains.dokka.base.templating.AddToSourcesetDependencies
import org.jetbrains.dokka.base.templating.toJsonString
import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.pages.RendererSpecificResourcePage
import org.jetbrains.dokka.pages.RenderingStrategy
import org.jetbrains.dokka.pages.RootPageNode
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.configuration
import org.jetbrains.dokka.transformers.pages.PageTransformer
import org.jetbrains.dokka.base.transformers.documentables.isException


abstract class NavigationDataProvider {
open fun navigableChildren(input: RootPageNode): NavigationNode = input.withDescendants()
.first { it is ModulePage || it is MultimoduleRootPage }.let { visit(it as ContentPage) }

open fun visit(page: ContentPage): NavigationNode = NavigationNode(
name = page.displayableName(),
dri = page.dri.first(),
sourceSets = page.sourceSets(),
icon = chooseNavigationIcon(page),
children = page.navigableChildren()
)

/**
* Parenthesis is applied in 1 case:
* - page only contains functions (therefore documentable from this page is [DFunction])
*/
private fun ContentPage.displayableName(): String =
if (this is WithDocumentables && documentables.all { it is DFunction }) {
"$name()"
} else {
name
}

private fun chooseNavigationIcon(contentPage: ContentPage): NavigationNodeIcon? {
return if (contentPage is WithDocumentables) {
val documentable = contentPage.documentables.firstOrNull()
val isJava = (documentable as? WithSources)?.sources?.any { it.value is PsiDocumentableSource } ?: false

when (documentable) {
is DClass -> when {
documentable.isException -> NavigationNodeIcon.EXCEPTION
documentable.isAbstract() -> {
if (isJava) NavigationNodeIcon.ABSTRACT_CLASS else NavigationNodeIcon.ABSTRACT_CLASS_KT
}
else -> if (isJava) NavigationNodeIcon.CLASS else NavigationNodeIcon.CLASS_KT
}
is DFunction -> NavigationNodeIcon.FUNCTION
is DProperty -> {
val isVar = documentable.extra[IsVar] != null
if (isVar) NavigationNodeIcon.VAR else NavigationNodeIcon.VAL
}
is DInterface -> if (isJava) NavigationNodeIcon.INTERFACE else NavigationNodeIcon.INTERFACE_KT
is DEnum -> if (isJava) NavigationNodeIcon.ENUM_CLASS else NavigationNodeIcon.ENUM_CLASS_KT
is DAnnotation -> {
if (isJava) NavigationNodeIcon.ANNOTATION_CLASS else NavigationNodeIcon.ANNOTATION_CLASS_KT
}
is DObject -> NavigationNodeIcon.OBJECT
else -> null
}
} else {
null
}
}

private fun DClass.isAbstract(): Boolean {
return modifier.values.all { it is KotlinModifier.Abstract || it is JavaModifier.Abstract }
}

private fun ContentPage.navigableChildren(): List<NavigationNode> {
return if (this !is ClasslikePageNode) {
children
.filterIsInstance<ContentPage>()
.map { visit(it) }
.sortedBy { it.name.toLowerCase() }
} else {
emptyList()
}
}
}

open class NavigationPageInstaller(val context: DokkaContext) : NavigationDataProvider(), PageTransformer {

override fun invoke(input: RootPageNode): RootPageNode =
input.modified(
children = input.children + NavigationPage(
root = navigableChildren(input),
moduleName = context.configuration.moduleName,
context = context
)
)
}

class CustomResourceInstaller(val dokkaContext: DokkaContext) : PageTransformer {
private val configuration = configuration<DokkaBase, DokkaBaseConfiguration>(dokkaContext)
Expand Down
2 changes: 0 additions & 2 deletions plugins/base/src/test/kotlin/enums/KotlinEnumsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,6 @@ class KotlinEnumsTest : BaseAbstractTest() {
}
}

fun TestOutputWriter.navigationHtml(): Element = contents.getValue("navigation.html").let { Jsoup.parse(it) }

@Test
fun `should handle companion object within enum`() {
val configuration = dokkaConfiguration {
Expand Down

0 comments on commit d399547

Please sign in to comment.