Coverage Summary for Class: TocHtmlNodeRenderer (io.heapy.kotmark.ext.toc.internal)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| TocHtmlNodeRenderer |
100%
(4/4)
|
90%
(18/20)
|
97%
(32/33)
|
95.4%
(248/260)
|
| TocHtmlNodeRenderer$beforeRoot$$inlined$filterIsInstance$1 |
0%
(0/1)
|
|
| Total |
80%
(4/5)
|
90%
(18/20)
|
97%
(32/33)
|
95.4%
(248/260)
|
package io.heapy.kotmark.ext.toc.internal
import io.heapy.kotmark.ext.heading.anchor.idGenerator
import io.heapy.kotmark.ext.toc.TableOfContents
import io.heapy.kotmark.node.Heading
import io.heapy.kotmark.node.Node
import io.heapy.kotmark.renderer.NodeRenderer
import io.heapy.kotmark.renderer.html.HtmlNodeRendererContext
import io.heapy.kotmark.renderer.html.renderChildren
internal class TocHtmlNodeRenderer(
private val context: HtmlNodeRendererContext,
private val levels: IntRange,
) : NodeRenderer {
override val nodeTypes = setOf(TableOfContents::class, Heading::class)
private val attributes = mutableMapOf<Heading, Map<String, String?>>()
private val fallback = idGenerator()
private val usedIds = mutableSetOf<String>()
private var entries: List<Entry> = emptyList()
override fun beforeRoot(rootNode: Node) {
val headings = documentNodes(rootNode).filterIsInstance<Heading>().toList()
// Invoke user providers exactly once, reserving all supplied IDs before filling gaps.
for (heading in headings) {
val attrs = context.extendAttributes(heading, "h${heading.level}", emptyMap()).toMap()
attributes[heading] = attrs
attrs["id"]?.let(usedIds::add)
}
entries = headings.mapNotNull { heading ->
val attrs = headingAttributes(heading)
if (heading.level in levels) Entry(heading.level, headingText(heading), fragment(attrs.getValue("id")!!))
else null
}
}
private fun headingAttributes(heading: Heading): Map<String, String?> {
val attrs = attributes.getOrPut(heading) {
context.extendAttributes(heading, "h${heading.level}", emptyMap()).toMap()
}
if (attrs["id"] != null) return attrs
var id: String
do { id = fallback.generateId(headingText(heading)) } while (!usedIds.add(id))
return (attrs + ("id" to id)).also { attributes[heading] = it }
}
override fun render(node: Node) {
when (node) {
is Heading -> {
val html = context.writer
val tag = "h${node.level}"
html.line()
html.tag(tag, headingAttributes(node))
context.renderChildren(node)
html.tag("/$tag")
html.line()
}
is TableOfContents -> if (entries.isNotEmpty()) context.render(outline(entries))
}
}
}