Skip to content

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 →