Markdown Table Generator — Kotlin source
Turn pipe, CSV, tab, semicolon, or space-separated data into a clean GitHub-Flavored Markdown table. Auto-detects the delimiter, pads columns, escapes pipes, and supports per-column alignment - all in your browser.
This is the Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.
// markdown-table — Kotlin port: split delimiter-separated rows and render a padded GFM table, ported from src/lib/markdown-table.ts.
enum class Align { NONE, LEFT, CENTER, RIGHT }
// splitLine: literal-delimiter split (',', '\t', ';'), each cell trimmed.
private fun splitLine(line: String, delim: Char): List<String> = line.split(delim).map { it.trim() }
// parseTable: non-empty lines, each split by the delimiter.
fun parseTable(input: String, delim: Char): List<List<String>> =
input.split(Regex("\r?\n")).map { it.trim() }.filter { it.isNotEmpty() }.map { splitLine(it, delim) }
// escapeCell: newlines collapse to spaces, a literal '|' renders as '\|'.
private fun escapeCell(cell: String): String =
cell.replace(Regex("\r?\n"), " ").replace("|", "\\|")
// pad: fill to width honoring right/center/left alignment.
private fun pad(cell: String, width: Int, align: Align): String {
val diff = width - cell.length
if (diff <= 0) return cell
return when (align) {
Align.RIGHT -> " ".repeat(diff) + cell
Align.CENTER -> " ".repeat(diff / 2) + cell + " ".repeat(diff - diff / 2)
else -> cell + " ".repeat(diff) // LEFT, NONE -> left-aligned
}
}
// sepCell: ---, :--, --:, :-: — the alignment separator, min width 3.
private fun sepCell(align: Align, width: Int): String {
val w = maxOf(3, width)
return when (align) {
Align.CENTER -> ":" + "-".repeat(w - 2) + ":"
Align.RIGHT -> "-".repeat(w - 1) + ":"
Align.LEFT -> ":" + "-".repeat(w - 1)
Align.NONE -> "-".repeat(w)
}
}
// toMarkdown: pad every column to its widest (escaped) cell and lay rows out
// as '| a | b |', with the separator row under the header row.
fun toMarkdown(rows: List<List<String>>, header: Boolean = true, aligns: List<Align> = emptyList()): String {
if (rows.isEmpty()) return ""
val cols = rows.maxOf { it.size }
val grid = rows.map { row ->
val cells = row.map(::escapeCell)
cells + List(cols - cells.size) { "" }
}
val align = (0 until cols).map { aligns.getOrElse(it) { Align.NONE } }
val widths = (0 until cols).map { c -> maxOf(3, grid.maxOf { it[c].length }) }
fun line(cells: List<String>) =
"| " + cells.mapIndexed { i, c -> pad(c, widths[i], align[i]) }.joinToString(" | ") + " |"
val separator = "| " + align.mapIndexed { i, a -> sepCell(a, widths[i]) }.joinToString(" | ") + " |"
val head = if (header) grid.first() else List(cols) { "" }
val data = if (header) grid.drop(1) else grid
return (listOf(line(head), separator) + data.map { line(it) }).joinToString("\n")
}
fun main() {
// Demo: a CSV-ish grid with a literal pipe in one cell (escaped on output).
val rows = parseTable("Name,Price,Qty\nWire | cable,2 m,1\nLED,3 mm,25", ',')
println(toMarkdown(rows, aligns = listOf(Align.NONE, Align.RIGHT, Align.CENTER)))
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →