diff --git a/table/render_markdown.go b/table/render_markdown.go
index ff298b1..d7227d3 100644
--- a/table/render_markdown.go
+++ b/table/render_markdown.go
@@ -51,22 +51,49 @@ func (t *Table) markdownRenderRow(out *strings.Builder, row rowStr, hint renderH
if colIdx < len(row) {
colStr = row[colIdx]
}
- out.WriteRune(' ')
colStr = strings.ReplaceAll(colStr, "|", "\\|")
colStr = strings.ReplaceAll(colStr, "\n", "
")
- out.WriteString(colStr)
- out.WriteRune(' ')
+ if t.style.Markdown.PadContent {
+ out.WriteRune(' ')
+ align := t.getAlign(colIdx, hint)
+ out.WriteString(align.Apply(colStr, t.maxColumnLengths[colIdx]))
+ out.WriteRune(' ')
+ } else {
+ out.WriteRune(' ')
+ 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("---:")
+ if t.style.Markdown.PadContent {
+ out.WriteString(" " + strings.Repeat("-", t.autoIndexVIndexMaxLength) + ":")
+ } else {
+ out.WriteRune(' ')
+ out.WriteString("---:")
+ }
} else if hint.isRegularRow() {
- fmt.Fprintf(out, "%d ", hint.rowNumber)
+ if t.style.Markdown.PadContent {
+ rowNumStr := fmt.Sprint(hint.rowNumber)
+ out.WriteRune(' ')
+ fmt.Fprintf(out, "%*s", t.autoIndexVIndexMaxLength, rowNumStr)
+ out.WriteRune(' ')
+ } else {
+ out.WriteRune(' ')
+ fmt.Fprintf(out, "%d ", hint.rowNumber)
+ }
+ } else {
+ if t.style.Markdown.PadContent {
+ out.WriteRune(' ')
+ out.WriteString(strings.Repeat(" ", t.autoIndexVIndexMaxLength))
+ out.WriteRune(' ')
+ } else {
+ out.WriteRune(' ')
+ }
}
out.WriteRune('|')
}
@@ -107,7 +134,12 @@ func (t *Table) markdownRenderSeparator(out *strings.Builder, hint renderHint) {
for colIdx := 0; colIdx < t.numColumns; colIdx++ {
t.markdownRenderRowAutoIndex(out, colIdx, hint)
- out.WriteString(t.getAlign(colIdx, hint).MarkdownProperty())
+ align := t.getAlign(colIdx, hint)
+ if t.style.Markdown.PadContent {
+ out.WriteString(align.MarkdownProperty(t.maxColumnLengths[colIdx]))
+ } else {
+ out.WriteString(align.MarkdownProperty())
+ }
out.WriteRune('|')
}
}
diff --git a/table/render_markdown_test.go b/table/render_markdown_test.go
index cb8e8de..8c9c7e4 100644
--- a/table/render_markdown_test.go
+++ b/table/render_markdown_test.go
@@ -65,6 +65,66 @@ func TestTable_RenderMarkdown_AutoIndex(t *testing.T) {
| | AF | BF | CF | DF | EF | FF | GF | HF | IF | JF |`)
}
+func TestTable_RenderMarkdown_Padded(t *testing.T) {
+ tw := NewWriter()
+ tw.AppendHeader(testHeader)
+ tw.AppendRows(testRows)
+ tw.AppendRow(testRowNewLines)
+ tw.AppendRow(testRowPipes)
+ tw.AppendFooter(testFooter)
+ tw.SetCaption(testCaption)
+ tw.SetTitle(testTitle1)
+ tw.Style().Markdown.PadContent = true
+
+ compareOutput(t, tw.RenderMarkdown(), `
+# Game of Thrones
+| # | First Name | Last Name | Salary | |
+| ---:| ---------- | --------- | ------:| --------------------------- |
+| 1 | Arya | Stark | 3000 | |
+| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
+| 300 | Tyrion | Lannister | 5000 | |
+| 0 | Valar | Morghulis | 0 | Faceless
Men |
+| 0 | Valar | Morghulis | 0 | Faceless\|Men |
+| | | Total | 10000 | |
+_A Song of Ice and Fire_`)
+}
+
+func TestTable_RenderMarkdown_Padded_AutoIndex(t *testing.T) {
+ tw := NewWriter()
+ for rowIdx := 0; rowIdx < 10; rowIdx++ {
+ row := make(Row, 10)
+ for colIdx := 0; colIdx < 10; colIdx++ {
+ row[colIdx] = fmt.Sprintf("%s%d", AutoIndexColumnID(colIdx), rowIdx+1)
+ }
+ tw.AppendRow(row)
+ }
+ for rowIdx := 0; rowIdx < 1; rowIdx++ {
+ row := make(Row, 10)
+ for colIdx := 0; colIdx < 10; colIdx++ {
+ row[colIdx] = AutoIndexColumnID(colIdx) + "F"
+ }
+ tw.AppendFooter(row)
+ }
+ tw.SetAutoIndex(true)
+ tw.SetStyle(StyleLight)
+ tw.Style().Markdown.PadContent = true
+
+ compareOutput(t, tw.RenderMarkdown(), `
+| | A | B | C | D | E | F | G | H | I | J |
+| --:| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| 1 | A1 | B1 | C1 | D1 | E1 | F1 | G1 | H1 | I1 | J1 |
+| 2 | A2 | B2 | C2 | D2 | E2 | F2 | G2 | H2 | I2 | J2 |
+| 3 | A3 | B3 | C3 | D3 | E3 | F3 | G3 | H3 | I3 | J3 |
+| 4 | A4 | B4 | C4 | D4 | E4 | F4 | G4 | H4 | I4 | J4 |
+| 5 | A5 | B5 | C5 | D5 | E5 | F5 | G5 | H5 | I5 | J5 |
+| 6 | A6 | B6 | C6 | D6 | E6 | F6 | G6 | H6 | I6 | J6 |
+| 7 | A7 | B7 | C7 | D7 | E7 | F7 | G7 | H7 | I7 | J7 |
+| 8 | A8 | B8 | C8 | D8 | E8 | F8 | G8 | H8 | I8 | J8 |
+| 9 | A9 | B9 | C9 | D9 | E9 | F9 | G9 | H9 | I9 | J9 |
+| 10 | A10 | B10 | C10 | D10 | E10 | F10 | G10 | H10 | I10 | J10 |
+| | AF | BF | CF | DF | EF | FF | GF | HF | IF | JF |`)
+}
+
func TestTable_RenderMarkdown_Empty(t *testing.T) {
tw := NewWriter()
assert.Empty(t, tw.RenderMarkdown())
diff --git a/table/style.go b/table/style.go
index b5f5849..0b2473d 100644
--- a/table/style.go
+++ b/table/style.go
@@ -3,14 +3,15 @@ package table
// Style declares how to render the Table and provides very fine-grained control
// on how the Table gets rendered on the Console.
type Style struct {
- Name string // name of the Style
- Box BoxStyle // characters to use for the boxes
- Color ColorOptions // colors to use for the rows and columns
- Format FormatOptions // formatting options for the rows and columns
- HTML HTMLOptions // rendering options for HTML mode
- Options Options // misc. options for the table
- Size SizeOptions // size (width) options for the table
- Title TitleOptions // formation options for the title text
+ Name string // name of the Style
+ Box BoxStyle // characters to use for the boxes
+ Color ColorOptions // colors to use for the rows and columns
+ Format FormatOptions // formatting options for the rows and columns
+ HTML HTMLOptions // rendering options for HTML mode
+ Markdown MarkdownOptions // rendering options for Markdown mode
+ Options Options // misc. options for the table
+ Size SizeOptions // size (width) options for the table
+ Title TitleOptions // formation options for the title text
}
var (
diff --git a/table/style_markdown.go b/table/style_markdown.go
new file mode 100644
index 0000000..a4c6632
--- /dev/null
+++ b/table/style_markdown.go
@@ -0,0 +1,25 @@
+package table
+
+// MarkdownOptions defines options to control Markdown rendering.
+type MarkdownOptions struct {
+ // PadContent pads each column content to match the longest content in
+ // the column, and extends the separator dashes to match. This makes the
+ // raw Markdown source more readable without affecting the rendered
+ // output.
+ //
+ // When disabled (default):
+ // | # | First Name | Last Name | Salary | |
+ // | ---:| --- | --- | ---:| --- |
+ // | 1 | Arya | Stark | 3000 | |
+ //
+ // When enabled:
+ // | # | First Name | Last Name | Salary | |
+ // | ---:| ---------- | --------- | ------:| --------------------------- |
+ // | 1 | Arya | Stark | 3000 | |
+ PadContent bool
+}
+
+var (
+ // DefaultMarkdownOptions defines sensible Markdown rendering defaults.
+ DefaultMarkdownOptions = MarkdownOptions{}
+)
diff --git a/text/align.go b/text/align.go
index 189531d..33512ee 100644
--- a/text/align.go
+++ b/text/align.go
@@ -76,16 +76,24 @@ func (a Align) HTMLProperty() string {
}
// MarkdownProperty returns the equivalent Markdown horizontal-align separator.
-func (a Align) MarkdownProperty() string {
+// An optional minLength can be provided to extend the dashes to match the
+// column content width; the result will be max(minLength, 3)+2 wide (including
+// leading/trailing space or colon). Without minLength (or 0), it defaults to 3.
+func (a Align) MarkdownProperty(minLength ...int) string {
+ length := 3
+ if len(minLength) > 0 && minLength[0] > length {
+ length = minLength[0]
+ }
+ dashes := strings.Repeat("-", length)
switch a {
case AlignLeft:
- return ":--- "
+ return ":" + dashes + " "
case AlignCenter:
- return ":---:"
+ return ":" + dashes + ":"
case AlignRight:
- return " ---:"
+ return " " + dashes + ":"
default:
- return " --- "
+ return " " + dashes + " "
}
}
diff --git a/text/align_test.go b/text/align_test.go
index a148845..75b7425 100644
--- a/text/align_test.go
+++ b/text/align_test.go
@@ -151,3 +151,16 @@ func TestAlign_MarkdownProperty(t *testing.T) {
assert.Contains(t, align.MarkdownProperty(), markdownSeparator)
}
}
+
+func TestAlign_MarkdownProperty_WithMinLength(t *testing.T) {
+ assert.Equal(t, " ---------- ", AlignDefault.MarkdownProperty(10))
+ assert.Equal(t, ":---------- ", AlignLeft.MarkdownProperty(10))
+ assert.Equal(t, ":----------:", AlignCenter.MarkdownProperty(10))
+ assert.Equal(t, " ---------- ", AlignJustify.MarkdownProperty(10))
+ assert.Equal(t, " ----------:", AlignRight.MarkdownProperty(10))
+
+ // minimum width of 3
+ assert.Equal(t, " --- ", AlignDefault.MarkdownProperty(1))
+ assert.Equal(t, " --- ", AlignDefault.MarkdownProperty(3))
+ assert.Equal(t, " ---- ", AlignDefault.MarkdownProperty(4))
+}