Current scope: kotmark-renderer-kotlinx-html| all classes
|
io.heapy.kotmark.renderer.kotlinxhtml.internal
Coverage Summary for Class: CoreNodeRenderer (io.heapy.kotmark.renderer.kotlinxhtml.internal)
| Class | Method, % | Branch, % | Line, % | Instruction, % |
|---|---|---|---|---|
| CoreNodeRenderer | 100% (10/10) | 84.2% (96/114) | 100% (67/67) | 96.5% (791/820) |
| CoreNodeRenderer$altText$1$1 | 60% (3/5) | 50% (2/4) | 60% (3/5) | 56.2% (18/32) |
| Total | 86.7% (13/15) | 83.1% (98/118) | 97.2% (70/72) | 95% (809/852) |
package io.heapy.kotmark.renderer.kotlinxhtml.internal
import io.heapy.kotmark.node.*
import io.heapy.kotmark.renderer.NodeRenderer
import io.heapy.kotmark.renderer.kotlinxhtml.KotlinxHtmlNodeRendererContext
internal class CoreNodeRenderer(private val context: KotlinxHtmlNodeRendererContext) : NodeRenderer {
override val nodeTypes = setOf(
Document::class, Heading::class, Paragraph::class, BlockQuote::class, BulletList::class,
FencedCodeBlock::class, HtmlBlock::class, ThematicBreak::class, IndentedCodeBlock::class,
Link::class, ListItem::class, OrderedList::class, Image::class, Emphasis::class,
StrongEmphasis::class, Text::class, Code::class, HtmlInline::class, SoftLineBreak::class,
HardLineBreak::class, LinkReferenceDefinition::class,
)
override fun render(node: Node) {
when (node) {
is Document -> context.renderChildren(node)
is Heading -> {
require(node.level in 1..6) { "Heading level must be between 1 and 6" }
blockContainer(node, "h${node.level}")
}
is Paragraph -> {
val tight = (node.parent?.parent as? ListBlock)?.isTight == true
val single = context.omitSingleParagraphP && node.parent is Document && node.previous == null && node.next == null
if (tight || single) context.renderChildren(node) else blockContainer(node, "p")
}
is BlockQuote -> blockContainer(node, "blockquote", innerLines = true)
is BulletList -> blockContainer(node, "ul", innerLines = true)
is OrderedList -> {
val start = node.markerStartNumber ?: 1
blockContainer(node, "ol", if (start == 1) emptyMap() else mapOf("start" to start.toString()), innerLines = true)
}
is ListItem -> { container(node, "li"); context.line() }
is FencedCodeBlock -> {
val language = node.info.orEmpty().substringBefore(' ')
codeBlock(node, node.literal.orEmpty(), if (language.isEmpty()) emptyMap() else mapOf("class" to "language-$language"))
}
is IndentedCodeBlock -> codeBlock(node, node.literal.orEmpty(), emptyMap())
is ThematicBreak -> { context.line(); context.element(node, "hr"); context.line() }
is Emphasis -> container(node, "em")
is StrongEmphasis -> container(node, "strong")
is Text -> context.text(node.literal.orEmpty())
is Code -> context.element(node, "code") { context.text(node.literal.orEmpty()) }
is Link -> {
var url = context.resolveUrl(node, node.destination.orEmpty())
val attrs = linkedMapOf<String, String?>()
if (context.sanitizeUrls) {
url = context.urlSanitizer.sanitizeLinkUrl(url)
attrs["rel"] = "nofollow"
}
attrs["href"] = context.encodeUrl(url)
node.title?.let { attrs["title"] = it }
container(node, "a", attrs)
}
is Image -> {
var url = context.resolveUrl(node, node.destination.orEmpty())
if (context.sanitizeUrls) url = context.urlSanitizer.sanitizeImageUrl(url)
val attrs = linkedMapOf("src" to context.encodeUrl(url), "alt" to altText(node))
node.title?.let { attrs["title"] = it }
context.element(node, "img", attrs)
}
is HtmlInline -> if (context.escapeHtml) context.text(node.literal.orEmpty()) else context.raw(node.literal.orEmpty())
is HtmlBlock -> {
context.line()
if (context.escapeHtml) context.element(node, "p") { context.text(node.literal.orEmpty()) }
else context.raw(node.literal.orEmpty())
context.line()
}
is SoftLineBreak -> if (context.softbreak == "\n") context.text("\n") else context.raw(context.softbreak)
is HardLineBreak -> { context.element(node, "br"); context.text("\n") }
is LinkReferenceDefinition -> Unit
}
}
private fun container(node: Node, name: String, attributes: Map<String, String?> = emptyMap()) {
context.element(node, name, attributes) { context.renderChildren(node) }
}
private fun codeBlock(node: Node, literal: String, attributes: Map<String, String?>) {
context.line()
context.element(node, "pre") {
context.element(node, "code", attributes) { context.text(literal) }
}
context.line()
}
private fun blockContainer(node: Node, name: String, attributes: Map<String, String?> = emptyMap(), innerLines: Boolean = false) {
context.line()
context.element(node, name, attributes) {
if (innerLines) context.line()
context.renderChildren(node)
if (innerLines) context.line()
}
context.line()
}
private fun altText(node: Node): String = buildString {
node.accept(object : AbstractVisitor() {
override fun visit(text: Text) { append(text.literal.orEmpty()) }
override fun visit(code: Code) { append(code.literal.orEmpty()) }
override fun visit(softLineBreak: SoftLineBreak) { append('\n') }
override fun visit(hardLineBreak: HardLineBreak) { append('\n') }
})
}
}