Coverage Summary for Class: IdGenerator (io.heapy.kotmark.ext.heading.anchor)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| IdGenerator |
100%
(4/4)
|
88.5%
(23/26)
|
100%
(38/38)
|
98.7%
(234/237)
|
| IdGenerator$Companion |
|
| IdGenerator$Configuration |
100%
(2/2)
|
|
100%
(5/5)
|
100%
(31/31)
|
| Total |
100%
(6/6)
|
88.5%
(23/26)
|
100%
(43/43)
|
98.9%
(265/268)
|
package io.heapy.kotmark.ext.heading.anchor
import io.heapy.kotmark.KotmarkDsl
import io.heapy.kotmark.internal.util.UnicodeCategories
class IdGenerator private constructor(configuration: Configuration) {
private val identities = mutableMapOf<String, Int>()
private val usedIds = mutableSetOf<String>()
private val defaultIdentifier = configuration.defaultId
private val prefix = configuration.prefix
private val suffix = configuration.suffix
fun generateId(text: String?): String {
var normalized = text?.let(::normalizeText) ?: defaultIdentifier
if (normalized.isEmpty()) {
normalized = defaultIdentifier
}
var count = identities[normalized] ?: 0
var candidate: String
do {
candidate = "$prefix$normalized${if (count == 0) "" else "-$count"}$suffix"
count++
} while (!usedIds.add(candidate))
identities[normalized] = count
return candidate
}
private fun normalizeText(text: String): String =
buildString {
val normalized = text.lowercase().replace(' ', '-')
var index = 0
while (index < normalized.length) {
val first = normalized[index]
val paired = first.isHighSurrogate() && index + 1 < normalized.length &&
normalized[index + 1].isLowSurrogate()
val length = if (paired) 2 else 1
val codePoint = if (paired) {
0x10000 + ((first.code - 0xD800) shl 10) + normalized[index + 1].code - 0xDC00
} else {
first.code
}
val category = UnicodeCategories.category(codePoint)
if (first == '-' || category in ALLOWED_CATEGORIES) {
append(normalized, index, index + length)
}
index += length
}
}
@KotmarkDsl
class Configuration internal constructor() {
var defaultId = "id"
var prefix = ""
var suffix = ""
internal fun create(): IdGenerator = IdGenerator(this)
}
companion object {
private val ALLOWED_CATEGORIES = setOf(
CharCategory.UPPERCASE_LETTER, CharCategory.LOWERCASE_LETTER,
CharCategory.TITLECASE_LETTER, CharCategory.MODIFIER_LETTER,
CharCategory.OTHER_LETTER, CharCategory.DECIMAL_DIGIT_NUMBER,
CharCategory.NON_SPACING_MARK, CharCategory.COMBINING_SPACING_MARK,
CharCategory.ENCLOSING_MARK, CharCategory.CONNECTOR_PUNCTUATION,
)
}
}
/** Creates [IdGenerator] with the supplied configuration. */
fun idGenerator(configure: IdGenerator.Configuration.() -> Unit = {}): IdGenerator =
IdGenerator.Configuration().apply(configure).create()