Merge remote-tracking branch 'upstream/java-layout-html-format' into devsite-with-java-layout-html
diff --git a/core/src/main/kotlin/Formats/FormatDescriptor.kt b/core/src/main/kotlin/Formats/FormatDescriptor.kt
index aa00df9..a1120c0 100644
--- a/core/src/main/kotlin/Formats/FormatDescriptor.kt
+++ b/core/src/main/kotlin/Formats/FormatDescriptor.kt
@@ -3,6 +3,7 @@
import com.google.inject.Binder
import org.jetbrains.dokka.*
import org.jetbrains.dokka.Utilities.bind
+import org.jetbrains.dokka.Utilities.lazyBind
import org.jetbrains.dokka.Utilities.toOptional
import org.jetbrains.dokka.Utilities.toType
import kotlin.reflect.KClass
@@ -23,11 +24,12 @@
override fun configureOutput(binder: Binder): Unit = with(binder) {
bind<Generator>() toType NodeLocationAwareGenerator::class
-
- bind<OutlineFormatService>() toOptional (outlineServiceClass)
- bind<FormatService>() toOptional formatServiceClass
bind<NodeLocationAwareGenerator>() toType generatorServiceClass
- bind<PackageListService>() toOptional packageListServiceClass
+
+
+ lazyBind<OutlineFormatService>() toOptional (outlineServiceClass)
+ lazyBind<FormatService>() toOptional formatServiceClass
+ lazyBind<PackageListService>() toOptional packageListServiceClass
}
abstract val formatServiceClass: KClass<out FormatService>?
diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt b/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt
index 58a5b99..e1951ea 100644
--- a/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt
+++ b/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt
@@ -12,9 +12,11 @@
import org.jetbrains.dokka.NodeKind.Companion.classLike
import org.jetbrains.dokka.NodeKind.Companion.memberLike
import org.jetbrains.dokka.Utilities.bind
+import org.jetbrains.dokka.Utilities.lazyBind
import org.jetbrains.dokka.Utilities.toOptional
import org.jetbrains.dokka.Utilities.toType
import org.jetbrains.kotlin.preprocessor.mkdirsOrFail
+import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
import java.io.BufferedWriter
import java.io.File
import java.net.URI
@@ -29,7 +31,7 @@
bind<LanguageService>() toType languageServiceClass
bind<JavaLayoutHtmlTemplateService>() toType templateServiceClass
bind<JavaLayoutHtmlUriProvider>() toType generatorServiceClass
- bind<JavaLayoutHtmlFormatOutlineFactoryService>() toOptional outlineFactoryClass
+ lazyBind<JavaLayoutHtmlFormatOutlineFactoryService>() toOptional outlineFactoryClass
}
val generatorServiceClass = JavaLayoutHtmlFormatGenerator::class
@@ -61,7 +63,10 @@
bodyContent: BODY.() -> Unit
) {
tagConsumer.html {
- head(headContent)
+ head {
+ meta(charset = "UTF-8")
+ headContent()
+ }
body(block = bodyContent)
}
}
@@ -77,6 +82,7 @@
val languageService: LanguageService,
val uriProvider: JavaLayoutHtmlUriProvider,
val templateService: JavaLayoutHtmlTemplateService,
+ val logger: DokkaLogger,
val uri: URI
) {
@@ -84,11 +90,10 @@
val contentToHtmlBuilder = ContentToHtmlBuilder(uriProvider, uri)
- private fun FlowContent.summaryNodeGroup(nodes: Iterable<DocumentationNode>, header: String, headerAsRow: Boolean = false, row: TBODY.(DocumentationNode) -> Unit) {
+ private fun <T> FlowContent.summaryNodeGroup(nodes: Iterable<T>, header: String, headerAsRow: Boolean = false, row: TBODY.(T) -> Unit) {
if (nodes.none()) return
if (!headerAsRow) {
h2 { +header }
- hr()
}
table {
if (headerAsRow) thead { tr { td { h3 { +header } } } }
@@ -113,7 +118,7 @@
td { metaMarkup(node.summary) }
}
- private fun TBODY.formatFunctionSummaryRow(node: DocumentationNode) = tr {
+ private fun TBODY.functionSummaryRow(node: DocumentationNode) = tr {
td {
for (modifier in node.details(NodeKind.Modifier)) {
renderedSignature(modifier, SUMMARY)
@@ -129,12 +134,28 @@
}
}
+ private fun TBODY.formatInheritRow(entry: Map.Entry<DocumentationNode, List<DocumentationNode>>) = tr {
+ td {
+ val (from, nodes) = entry
+ +"From class "
+ a(href = uriProvider.linkTo(from.owner!!, uri)) { +from.qualifiedName() }
+ table {
+ tbody {
+ for (node in nodes) {
+ functionSummaryRow(node)
+ }
+ }
+ }
+ }
+ }
+
private fun FlowContent.renderedSignature(node: DocumentationNode, mode: LanguageService.RenderMode = SUMMARY) {
metaMarkup(languageService.render(node, mode))
}
private fun FlowContent.fullFunctionDocs(node: DocumentationNode) {
div {
+ id = node.signatureForAnchor(logger)
h3 { +node.name }
pre { renderedSignature(node, FULL) }
metaMarkup(node.content)
@@ -154,7 +175,10 @@
}
}
}
- a { id = node.signature() }
+ }
+
+ private fun FlowContent.fullPropertyDocs(node: DocumentationNode) {
+ fullFunctionDocs(node)
}
fun appendPackage(node: DocumentationNode) = templateService.composePage(
@@ -172,17 +196,33 @@
summaryNodeGroup(node.members(NodeKind.AnnotationClass), "Annotations") { formatClassLikeRow(it) }
summaryNodeGroup(node.members(NodeKind.Enum), "Enums") { formatClassLikeRow(it) }
- summaryNodeGroup(node.members(NodeKind.Function), "Top-level functions summary") { formatFunctionSummaryRow(it) }
+ summaryNodeGroup(node.members(NodeKind.Function), "Top-level functions summary") { functionSummaryRow(it) }
+ summaryNodeGroup(node.members(NodeKind.Property), "Top-level properties summary") { functionSummaryRow(it) }
- h2 { +"Top-level functions" }
- hr()
- for (function in node.members(NodeKind.Function)) {
- fullFunctionDocs(function)
- }
+ fullDocs(node.members(NodeKind.Function), { h2 { +"Top-level functions" } }) { fullFunctionDocs(it) }
+ fullDocs(node.members(NodeKind.Property), { h2 { +"Top-level properties" } }) { fullPropertyDocs(it) }
}
)
+ fun FlowContent.classHierarchy(node: DocumentationNode) {
+
+ val superclasses = generateSequence(node) { it.superclass }.toList().asReversed()
+ table {
+ superclasses.forEach {
+ tr {
+ if (it != superclasses.first()) {
+ td {
+ +" ↳"
+ }
+ }
+ td {
+ a(href = uriProvider.linkTo(it, uri)) { +it.qualifiedName() }
+ }
+ }
+ }
+ }
+ }
fun appendClassLike(node: DocumentationNode) = templateService.composePage(
listOf(node),
@@ -193,22 +233,116 @@
bodyContent = {
h1 { +node.name }
pre { renderedSignature(node, FULL) }
+ classHierarchy(node)
+
metaMarkup(node.content)
h2 { +"Summary" }
- hr()
- val functionsToDisplay = node.members(NodeKind.Function) + node.members(NodeKind.CompanionObjectFunction)
+ fun DocumentationNode.isFunction() = kind == NodeKind.Function || kind == NodeKind.CompanionObjectFunction
+ fun DocumentationNode.isProperty() = kind == NodeKind.Property || kind == NodeKind.CompanionObjectProperty
- summaryNodeGroup(functionsToDisplay, "Functions", headerAsRow = true) { formatFunctionSummaryRow(it) }
+ val functionsToDisplay = node.members.filter(DocumentationNode::isFunction)
+ val properties = node.members.filter(DocumentationNode::isProperty)
+ val inheritedFunctionsByReceiver = node.inheritedMembers.filter(DocumentationNode::isFunction).groupBy { it.owner!! }
+ val inheritedPropertiesByReceiver = node.inheritedMembers.filter(DocumentationNode::isProperty).groupBy { it.owner!! }
+ val extensionProperties = node.extensions.filter(DocumentationNode::isProperty)
+ val extensionFunctions = node.extensions.filter(DocumentationNode::isFunction)
- h2 { +"Functions" }
- hr()
- for (function in functionsToDisplay) {
- fullFunctionDocs(function)
+
+ summaryNodeGroup(functionsToDisplay, "Functions", headerAsRow = true) { functionSummaryRow(it) }
+ summaryNodeGroup(inheritedFunctionsByReceiver.entries, "Inherited functions", headerAsRow = true) { formatInheritRow(it) }
+ summaryNodeGroup(extensionFunctions, "Extension functions", headerAsRow = true) { functionSummaryRow(it) }
+
+
+ summaryNodeGroup(properties, "Properties", headerAsRow = true) { functionSummaryRow(it) }
+ summaryNodeGroup(inheritedPropertiesByReceiver.entries, "Inherited properties", headerAsRow = true) { formatInheritRow(it) }
+ summaryNodeGroup(extensionProperties, "Extension properties", headerAsRow = true) { functionSummaryRow(it) }
+
+
+ fullDocs(functionsToDisplay, { h2 { +"Functions" } }) { fullFunctionDocs(it) }
+ fullDocs(extensionFunctions, { h2 { +"Extension functions" } }) { fullFunctionDocs(it) }
+ fullDocs(properties, { h2 { +"Properties" } }) { fullPropertyDocs(it) }
+ fullDocs(extensionProperties, { h2 { +"Extension properties" } }) { fullPropertyDocs(it) }
+ }
+ )
+
+ fun generateClassesIndex(allTypesNode: DocumentationNode) = templateService.composePage(
+ listOf(allTypesNode),
+ htmlConsumer,
+ headContent = {
+
+ },
+ bodyContent = {
+ h1 { +"Class Index" }
+ val classesByFirstLetter = allTypesNode.members.groupBy {
+ it.name.first().toString()
+ }.entries.sortedBy { (letter) -> letter }
+
+ ul {
+ classesByFirstLetter.forEach { (letter) ->
+ li { a(href = "#letter_$letter") { +letter } }
+ }
+ }
+
+ classesByFirstLetter.forEach { (letter, nodes) ->
+ h2 {
+ id = "letter_$letter"
+ +letter
+ }
+ table {
+ tbody {
+ for (node in nodes) {
+ tr {
+ td {
+ a(href = uriProvider.linkTo(node, uri)) { +node.name }
+ }
+ td {
+ metaMarkup(node.content)
+ }
+ }
+ }
+ }
+ }
}
}
)
+
+ fun generatePackageIndex(nodes: List<DocumentationNode>) = templateService.composePage(nodes,
+ htmlConsumer,
+ headContent = {
+
+ },
+ bodyContent = {
+ h1 { +"Package Index" }
+ table {
+ tbody {
+ for (node in nodes.sortedBy { it.name }) {
+ tr {
+ td {
+ a(href = uriProvider.linkTo(node, uri)) { +node.name }
+ }
+ td {
+ metaMarkup(node.content)
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+
+ private fun FlowContent.fullDocs(
+ nodes: List<DocumentationNode>,
+ header: FlowContent.() -> Unit,
+ renderNode: FlowContent.(DocumentationNode) -> Unit
+ ) {
+ if (nodes.none()) return
+ header()
+ for (node in nodes) {
+ renderNode(node)
+ }
+ }
}
class ContentToHtmlBuilder(val uriProvider: JavaLayoutHtmlUriProvider, val uri: URI) {
@@ -295,11 +429,14 @@
@Named("outputDir") val root: File,
val languageService: LanguageService,
val templateService: JavaLayoutHtmlTemplateService,
- val outlineFactoryService: JavaLayoutHtmlFormatOutlineFactoryService
+ val logger: DokkaLogger
) : Generator, JavaLayoutHtmlUriProvider {
+ @set:Inject(optional = true)
+ var outlineFactoryService: JavaLayoutHtmlFormatOutlineFactoryService? = null
+
fun createOutputBuilderForNode(node: DocumentationNode, output: Appendable)
- = JavaLayoutHtmlFormatOutputBuilder(output, languageService, this, templateService, mainUri(node))
+ = JavaLayoutHtmlFormatOutputBuilder(output, languageService, this, templateService, logger, mainUri(node))
override fun tryGetContainerUri(node: DocumentationNode): URI? {
return when (node.kind) {
@@ -314,12 +451,15 @@
return when (node.kind) {
NodeKind.Package -> tryGetContainerUri(node)?.resolve("package-summary.html")
in classLike -> tryGetContainerUri(node)?.resolve("#")
- in memberLike -> tryGetMainUri(node.owner!!)?.resolve("#${node.signatureUrlEncoded()}")
- NodeKind.AllTypes -> tryGetContainerUri(node.owner!!)?.resolve("allclasses.html")
+ in memberLike -> tryGetMainUri(node.owner!!)?.resolveInPage(node)
+ NodeKind.TypeParameter -> node.path.asReversed().drop(1).firstNotNullResult(this::tryGetMainUri)?.resolveInPage(node)
+ NodeKind.AllTypes -> tryGetContainerUri(node.owner!!)?.resolve("classes.html")
else -> null
}
}
+ fun URI.resolveInPage(node: DocumentationNode): URI = resolve("#${node.signatureUrlEncoded(logger)}")
+
fun buildClass(node: DocumentationNode, parentDir: File) {
val fileForClass = parentDir.resolve(node.simpleName() + ".html")
fileForClass.bufferedWriter().use {
@@ -342,12 +482,30 @@
}
}
+ fun buildClassIndex(node: DocumentationNode, parentDir: File) {
+ val file = parentDir.resolve("classes.html")
+ file.bufferedWriter().use {
+ createOutputBuilderForNode(node, it).generateClassesIndex(node)
+ }
+ }
+
+ fun buildPackageIndex(nodes: List<DocumentationNode>, parentDir: File) {
+ val file = parentDir.resolve("packages.html")
+ file.bufferedWriter().use {
+ JavaLayoutHtmlFormatOutputBuilder(it, languageService, this, templateService, logger, containerUri(nodes.first().owner!!).resolve("packages.html"))
+ .generatePackageIndex(nodes)
+ }
+ }
override fun buildPages(nodes: Iterable<DocumentationNode>) {
val module = nodes.single()
val moduleRoot = root.resolve(module.name)
- module.members.filter { it.kind == NodeKind.Package }.forEach { buildPackage(it, moduleRoot) }
+ val packages = module.members.filter { it.kind == NodeKind.Package }
+ packages.forEach { buildPackage(it, moduleRoot) }
+
+ buildClassIndex(module.members.single { it.kind == NodeKind.AllTypes }, moduleRoot)
+ buildPackageIndex(packages, moduleRoot)
}
override fun buildOutlines(nodes: Iterable<DocumentationNode>) {
@@ -362,7 +520,7 @@
return writer
}
- outlineFactoryService.generateOutlines(::provideOutput, nodes)
+ outlineFactoryService?.generateOutlines(::provideOutput, nodes)
uriToWriter.values.forEach { it.close() }
}
@@ -374,44 +532,57 @@
}
}
-fun DocumentationNode.signature() = detail(NodeKind.Signature).name
-fun DocumentationNode.signatureUrlEncoded() = URLEncoder.encode(detail(NodeKind.Signature).name, "UTF-8")
-
-
-fun URI.relativeTo(base: URI): URI {
- var base = base
- var child = this
- // Normalize paths to remove . and .. segments
- base = base.normalize()
- child = child.normalize()
-
- // Split paths into segments
- var bParts = base.path.split('/').dropLastWhile { it.isEmpty() }
- val cParts = child.path.split('/').dropLastWhile { it.isEmpty() }
-
- // Discard trailing segment of base path
- if (bParts.isNotEmpty() && !base.path.endsWith("/")) {
- bParts = bParts.dropLast(1)
- }
-
- // Remove common prefix segments
- var i = 0
- while (i < bParts.size && i < cParts.size && bParts[i] == cParts[i]) {
- i++
- }
-
-
- // Construct the relative path
- val sb = StringBuilder()
- for (j in 0 until bParts.size - i) {
- sb.append("../")
- }
- for (j in i until cParts.size) {
- if (j != i) {
- sb.append("/")
+fun DocumentationNode.signatureForAnchor(logger: DokkaLogger): String = when (kind) {
+ NodeKind.Function -> buildString {
+ detailOrNull(NodeKind.Receiver)?.let {
+ append("(")
+ append(it.detail(NodeKind.Type).qualifiedNameFromType())
+ append(").")
}
- sb.append(cParts[j])
+ append(name)
+ details(NodeKind.Parameter).joinTo(this, prefix = "(", postfix = ")") { it.detail(NodeKind.Type).qualifiedNameFromType() }
+ }
+ NodeKind.Property ->
+ "$name:${detail(NodeKind.Type).qualifiedNameFromType()}"
+ NodeKind.TypeParameter, NodeKind.Parameter -> owner!!.signatureForAnchor(logger) + "/" + name
+ else -> "Not implemented signatureForAnchor $this".also { logger.warn(it) }
+}
+
+fun DocumentationNode.signatureUrlEncoded(logger: DokkaLogger) = URLEncoder.encode(signatureForAnchor(logger), "UTF-8")
+
+
+fun URI.relativeTo(uri: URI): URI {
+ // Normalize paths to remove . and .. segments
+ val base = uri.normalize()
+ val child = this.normalize()
+
+ fun StringBuilder.appendRelativePath() {
+ // Split paths into segments
+ var bParts = base.path.split('/').dropLastWhile { it.isEmpty() }
+ val cParts = child.path.split('/').dropLastWhile { it.isEmpty() }
+
+ // Discard trailing segment of base path
+ if (bParts.isNotEmpty() && !base.path.endsWith("/")) {
+ bParts = bParts.dropLast(1)
+ }
+
+ // Compute common prefix
+ val commonPartsSize = bParts.zip(cParts).count { (basePart, childPart) -> basePart == childPart }
+ bParts.drop(commonPartsSize).joinTo(this, separator = "") { "../" }
+ cParts.drop(commonPartsSize).joinTo(this, separator = "/")
}
- return URI.create(sb.toString())
+ return URI.create(buildString {
+ if (base.path != child.path) {
+ appendRelativePath()
+ }
+ child.rawQuery?.let {
+ append("?")
+ append(it)
+ }
+ child.rawFragment?.let {
+ append("#")
+ append(it)
+ }
+ })
}
\ No newline at end of file
diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
index 61bf50d..dd29cfc 100644
--- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
+++ b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt
@@ -232,6 +232,7 @@
val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor)
if (externalLink != null) {
node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link)
+ node.append(DocumentationNode(classifierDescriptor.fqNameUnsafe.asString(), Content.Empty, NodeKind.QualifiedName), RefKind.Detail)
} else {
link(node, classifierDescriptor,
if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link)
diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
index f33c8c9..6f8ff44 100644
--- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
+++ b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt
@@ -47,8 +47,7 @@
val typeParameter = functionWithTypeParameter.details(NodeKind.TypeParameter).first()
if (functionWithTypeParameter.kind == NodeKind.Function) {
renderFunction(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name))
- }
- else {
+ } else {
renderProperty(functionWithTypeParameter, RenderMode.SUMMARY, SummarizingMapper(receiverKind, typeParameter.name))
}
}
@@ -102,7 +101,7 @@
fun renderReceiver(receiver: DocumentationNode, to: ContentBlock)
}
- private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String): SignatureMapper {
+ private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String) : SignatureMapper {
override fun renderReceiver(receiver: DocumentationNode, to: ContentBlock) {
to.append(ContentIdentifier(kind.receiverName, IdentifierKind.SummarizedTypeName))
to.text("<$typeParameterName>")
@@ -116,7 +115,7 @@
}
private fun <T> ContentBlock.renderList(nodes: List<T>, separator: String = ", ",
- noWrap: Boolean = false, renderItem: (T) -> Unit) {
+ noWrap: Boolean = false, renderItem: (T) -> Unit) {
if (nodes.none())
return
renderItem(nodes.first())
@@ -131,7 +130,7 @@
}
}
- private fun ContentBlock.renderLinked(node: DocumentationNode, body: ContentBlock.(DocumentationNode)->Unit) {
+ private fun ContentBlock.renderLinked(node: DocumentationNode, body: ContentBlock.(DocumentationNode) -> Unit) {
val to = node.links.firstOrNull()
if (to == null)
body(node)
@@ -215,13 +214,13 @@
private fun ContentBlock.renderModifier(node: DocumentationNode, nowrap: Boolean = false) {
when (node.name) {
- "final", "public", "var" -> {}
+ "final", "public", "var" -> {
+ }
else -> {
keyword(node.name)
if (nowrap) {
nbsp()
- }
- else {
+ } else {
text(" ")
}
}
@@ -238,11 +237,12 @@
nbsp()
symbol(":")
nbsp()
- renderList(constraints, noWrap=true) {
+ renderList(constraints, noWrap = true) {
renderType(it, renderMode)
}
}
}
+
private fun ContentBlock.renderParameter(node: DocumentationNode, renderMode: RenderMode) {
if (renderMode == RenderMode.FULL) {
renderAnnotationsForNode(node)
@@ -401,8 +401,7 @@
symbol(")")
symbol(": ")
renderType(node.detail(NodeKind.Type), renderMode)
- }
- else {
+ } else {
symbol(")")
}
renderExtraTypeParameterConstraints(node, renderMode)
@@ -428,7 +427,7 @@
}
}
- private fun needReturnType(node: DocumentationNode) = when(node.kind) {
+ private fun needReturnType(node: DocumentationNode) = when (node.kind) {
NodeKind.Constructor -> false
else -> !node.isUnitReturnType()
}
@@ -475,4 +474,7 @@
}
}
-fun DocumentationNode.qualifiedNameFromType() = (links.firstOrNull() ?: hiddenLinks.firstOrNull())?.qualifiedName() ?: name
+fun DocumentationNode.qualifiedNameFromType() =
+ details.firstOrNull { it.kind == NodeKind.QualifiedName }?.name
+ ?: (links.firstOrNull() ?: hiddenLinks.firstOrNull())?.qualifiedName()
+ ?: name
diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt
index da85cab..4b10879 100644
--- a/core/src/main/kotlin/Model/DocumentationNode.kt
+++ b/core/src/main/kotlin/Model/DocumentationNode.kt
@@ -48,6 +48,7 @@
Signature,
ExternalLink,
+ QualifiedName,
Platform,
AllTypes,
@@ -104,6 +105,12 @@
val platforms: List<String>
get() = references(RefKind.Platform).map { it.to.name }
+ val supertypes: List<DocumentationNode>
+ get() = references(RefKind.Superclass).map { it.to }
+
+ val superclass: DocumentationNode?
+ get() = supertypes.find { it.kind == NodeKind.Class }
+
// TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice
fun addReferenceTo(to: DocumentationNode, kind: RefKind) {
references.add(DocumentationReference(this, to, kind))
diff --git a/core/src/main/kotlin/Utilities/DokkaModules.kt b/core/src/main/kotlin/Utilities/DokkaModules.kt
index 6b5e153..763e29a 100644
--- a/core/src/main/kotlin/Utilities/DokkaModules.kt
+++ b/core/src/main/kotlin/Utilities/DokkaModules.kt
@@ -73,7 +73,9 @@
inline fun <reified T: Any> Binder.bind(): AnnotatedBindingBuilder<T> = bind(T::class.java)
-inline infix fun <reified T: Any, TKClass: KClass<out T>> AnnotatedBindingBuilder<T>.toOptional(kClass: TKClass?) =
- kClass?.let { to(it.java) }
+inline fun <reified T: Any> Binder.lazyBind(): Lazy<AnnotatedBindingBuilder<T>> = lazy { bind(T::class.java) }
+
+inline infix fun <reified T: Any, TKClass: KClass<out T>> Lazy<AnnotatedBindingBuilder<T>>.toOptional(kClass: TKClass?) =
+ kClass?.let { value toType it }
inline infix fun <reified T: Any, TKClass: KClass<out T>> AnnotatedBindingBuilder<T>.toType(kClass: TKClass) = to(kClass.java)