2023-08-21 17:42:50 +08:00
|
|
|
//
|
|
|
|
// render_markdown.go
|
|
|
|
// Copyright (C) 2023 tiglog <me@tiglog.com>
|
|
|
|
//
|
|
|
|
// Distributed under terms of the MIT license.
|
|
|
|
//
|
|
|
|
|
2023-08-20 13:50:39 +08:00
|
|
|
package table
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// RenderMarkdown renders the Table in Markdown format. Example:
|
2023-08-21 17:42:50 +08:00
|
|
|
//
|
|
|
|
// | # | First Name | Last Name | Salary | |
|
|
|
|
// | ---:| --- | --- | ---:| --- |
|
|
|
|
// | 1 | Arya | Stark | 3000 | |
|
|
|
|
// | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
|
|
|
|
// | 300 | Tyrion | Lannister | 5000 | |
|
|
|
|
// | | | Total | 10000 | |
|
2023-08-20 13:50:39 +08:00
|
|
|
func (t *Table) RenderMarkdown() string {
|
|
|
|
t.initForRender()
|
|
|
|
|
|
|
|
var out strings.Builder
|
|
|
|
if t.numColumns > 0 {
|
|
|
|
t.markdownRenderTitle(&out)
|
|
|
|
t.markdownRenderRowsHeader(&out)
|
|
|
|
t.markdownRenderRows(&out, t.rows, renderHint{})
|
|
|
|
t.markdownRenderRowsFooter(&out)
|
|
|
|
t.markdownRenderCaption(&out)
|
|
|
|
}
|
|
|
|
return t.render(&out)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Table) markdownRenderCaption(out *strings.Builder) {
|
|
|
|
if t.caption != "" {
|
|
|
|
out.WriteRune('\n')
|
|
|
|
out.WriteRune('_')
|
|
|
|
out.WriteString(t.caption)
|
|
|
|
out.WriteRune('_')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Table) markdownRenderRow(out *strings.Builder, row rowStr, hint renderHint) {
|
|
|
|
// when working on line number 2 or more, insert a newline first
|
|
|
|
if out.Len() > 0 {
|
|
|
|
out.WriteRune('\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
// render each column up to the max. columns seen in all the rows
|
|
|
|
out.WriteRune('|')
|
|
|
|
for colIdx := 0; colIdx < t.numColumns; colIdx++ {
|
|
|
|
t.markdownRenderRowAutoIndex(out, colIdx, hint)
|
|
|
|
|
|
|
|
if hint.isSeparatorRow {
|
|
|
|
out.WriteString(t.getAlign(colIdx, hint).MarkdownProperty())
|
|
|
|
} else {
|
|
|
|
var colStr string
|
|
|
|
if colIdx < len(row) {
|
|
|
|
colStr = row[colIdx]
|
|
|
|
}
|
|
|
|
out.WriteRune(' ')
|
|
|
|
if strings.Contains(colStr, "|") {
|
|
|
|
colStr = strings.Replace(colStr, "|", "\\|", -1)
|
|
|
|
}
|
|
|
|
if strings.Contains(colStr, "\n") {
|
|
|
|
colStr = strings.Replace(colStr, "\n", "<br/>", -1)
|
|
|
|
}
|
|
|
|
out.WriteString(colStr)
|
|
|
|
out.WriteRune(' ')
|
|
|
|
}
|
|
|
|
out.WriteRune('|')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Table) markdownRenderRowAutoIndex(out *strings.Builder, colIdx int, hint renderHint) {
|
|
|
|
if colIdx == 0 && t.autoIndex {
|
|
|
|
out.WriteRune(' ')
|
|
|
|
if hint.isSeparatorRow {
|
|
|
|
out.WriteString("---:")
|
|
|
|
} else if hint.isRegularRow() {
|
|
|
|
out.WriteString(fmt.Sprintf("%d ", hint.rowNumber))
|
|
|
|
}
|
|
|
|
out.WriteRune('|')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Table) markdownRenderRows(out *strings.Builder, rows []rowStr, hint renderHint) {
|
|
|
|
if len(rows) > 0 {
|
|
|
|
for idx, row := range rows {
|
|
|
|
hint.rowNumber = idx + 1
|
|
|
|
t.markdownRenderRow(out, row, hint)
|
|
|
|
|
|
|
|
if idx == len(rows)-1 && hint.isHeaderRow {
|
|
|
|
t.markdownRenderRow(out, t.rowSeparator, renderHint{isSeparatorRow: true})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Table) markdownRenderRowsFooter(out *strings.Builder) {
|
|
|
|
t.markdownRenderRows(out, t.rowsFooter, renderHint{isFooterRow: true})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Table) markdownRenderRowsHeader(out *strings.Builder) {
|
|
|
|
if len(t.rowsHeader) > 0 {
|
|
|
|
t.markdownRenderRows(out, t.rowsHeader, renderHint{isHeaderRow: true})
|
|
|
|
} else if t.autoIndex {
|
|
|
|
t.markdownRenderRows(out, []rowStr{t.getAutoIndexColumnIDs()}, renderHint{isAutoIndexRow: true, isHeaderRow: true})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Table) markdownRenderTitle(out *strings.Builder) {
|
|
|
|
if t.title != "" {
|
|
|
|
out.WriteString("# ")
|
|
|
|
out.WriteString(t.title)
|
|
|
|
}
|
|
|
|
}
|