Files
renovate-sh-app[bot]GitHubrenovate-sh-app[bot] <219655108+renovate-sh-app[bot]@users.noreply.github.com>
e1a79277fa fix(deps): update module github.com/jedib0t/go-pretty/v6 to v6.7.8 (#6152)
| datasource | package                         | from   | to     |
| ---------- | ------------------------------- | ------ | ------ |
| go         | github.com/jedib0t/go-pretty/v6 | v6.7.5 | v6.7.8 |

Signed-off-by: renovate-sh-app[bot] <219655108+renovate-sh-app[bot]@users.noreply.github.com>
Co-authored-by: renovate-sh-app[bot] <219655108+renovate-sh-app[bot]@users.noreply.github.com>
2026-01-07 23:39:01 +00:00

281 lines
7.1 KiB
Go

package table
import (
"fmt"
"html"
"strings"
"github.com/jedib0t/go-pretty/v6/text"
)
const (
// DefaultHTMLCSSClass stores the css-class to use when none-provided via
// SetHTMLCSSClass(cssClass string).
DefaultHTMLCSSClass = "go-pretty-table"
)
// RenderHTML renders the Table in HTML format. Example:
//
// <table class="go-pretty-table">
// <thead>
// <tr>
// <th align="right">#</th>
// <th>First Name</th>
// <th>Last Name</th>
// <th align="right">Salary</th>
// <th>&nbsp;</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td align="right">1</td>
// <td>Arya</td>
// <td>Stark</td>
// <td align="right">3000</td>
// <td>&nbsp;</td>
// </tr>
// <tr>
// <td align="right">20</td>
// <td>Jon</td>
// <td>Snow</td>
// <td align="right">2000</td>
// <td>You know nothing, Jon Snow!</td>
// </tr>
// <tr>
// <td align="right">300</td>
// <td>Tyrion</td>
// <td>Lannister</td>
// <td align="right">5000</td>
// <td>&nbsp;</td>
// </tr>
// </tbody>
// <tfoot>
// <tr>
// <td align="right">&nbsp;</td>
// <td>&nbsp;</td>
// <td>Total</td>
// <td align="right">10000</td>
// <td>&nbsp;</td>
// </tr>
// </tfoot>
// </table>
func (t *Table) RenderHTML() string {
t.initForRender(renderModeHTML)
var out strings.Builder
if t.numColumns > 0 {
out.WriteString("<table class=\"")
if t.htmlCSSClass != "" {
out.WriteString(t.htmlCSSClass)
} else {
out.WriteString(t.style.HTML.CSSClass)
}
out.WriteString("\">\n")
t.htmlRenderTitle(&out)
t.htmlRenderRowsHeader(&out)
t.htmlRenderRows(&out, t.rows, renderHint{})
t.htmlRenderRowsFooter(&out)
t.htmlRenderCaption(&out)
out.WriteString("</table>")
}
return t.render(&out)
}
func (t *Table) htmlGetColStrAndTag(row rowStr, colIdx int, hint renderHint) (string, string) {
// get the column contents
var colStr string
if colIdx < len(row) {
colStr = row[colIdx]
}
// header uses "th" instead of "td"
colTagName := "td"
if hint.isHeaderRow {
colTagName = "th"
}
return colStr, colTagName
}
func (t *Table) htmlRenderCaption(out *strings.Builder) {
if t.caption != "" {
out.WriteString(" <caption class=\"caption\" style=\"caption-side: bottom;\">")
out.WriteString(t.caption)
out.WriteString("</caption>\n")
}
}
func (t *Table) htmlRenderColumn(out *strings.Builder, colStr string) {
// convertEscSequencesToSpans already escapes text content, so skip
// EscapeText if ConvertColorsToSpans is true
if t.style.HTML.ConvertColorsToSpans {
colStr = convertEscSequencesToSpans(colStr)
} else if t.style.HTML.EscapeText {
colStr = html.EscapeString(colStr)
}
if t.style.HTML.Newline != "\n" {
colStr = strings.ReplaceAll(colStr, "\n", t.style.HTML.Newline)
}
out.WriteString(colStr)
}
func (t *Table) htmlRenderColumnAttributes(out *strings.Builder, colIdx int, hint renderHint, alignOverride text.Align) {
// determine the HTML "align"/"valign" property values
align := alignOverride.HTMLProperty()
vAlign := t.getVAlign(colIdx, hint).HTMLProperty()
// determine the HTML "class" property values for the colors
class := t.getColumnColors(colIdx, hint).HTMLProperty()
if align != "" {
out.WriteRune(' ')
out.WriteString(align)
}
if class != "" {
out.WriteRune(' ')
out.WriteString(class)
}
if vAlign != "" {
out.WriteRune(' ')
out.WriteString(vAlign)
}
}
func (t *Table) htmlRenderColumnAutoIndex(out *strings.Builder, hint renderHint) {
if hint.isHeaderRow {
out.WriteString(" <th>")
out.WriteString(t.style.HTML.EmptyColumn)
out.WriteString("</th>\n")
} else if hint.isFooterRow {
out.WriteString(" <td>")
out.WriteString(t.style.HTML.EmptyColumn)
out.WriteString("</td>\n")
} else {
out.WriteString(" <td align=\"right\">")
fmt.Fprint(out, hint.rowNumber)
out.WriteString("</td>\n")
}
}
func (t *Table) htmlRenderRow(out *strings.Builder, row rowStr, hint renderHint) {
out.WriteString(" <tr>\n")
for colIdx := 0; colIdx < t.numColumns; colIdx++ {
// auto-index column
if colIdx == 0 && t.autoIndex {
t.htmlRenderColumnAutoIndex(out, hint)
}
// auto-merged columns should be skipped
if t.shouldMergeCellsVerticallyAbove(colIdx, hint) {
continue
}
align := t.getAlign(colIdx, hint)
rowConfig := t.getRowConfig(hint)
extraColumnsRendered := 0
if rowConfig.AutoMerge && !hint.isSeparatorRow {
// get the real row to consider all lines in each column instead of just
// looking at the current "line"
rowUnwrapped := t.getRow(hint.rowNumber-1, hint)
for idx := colIdx + 1; idx < len(rowUnwrapped); idx++ {
if rowUnwrapped[colIdx] != rowUnwrapped[idx] {
break
}
align = rowConfig.getAutoMergeAlign()
extraColumnsRendered++
}
}
colStr, colTagName := t.htmlGetColStrAndTag(row, colIdx, hint)
// write the row
out.WriteString(" <")
out.WriteString(colTagName)
t.htmlRenderColumnAttributes(out, colIdx, hint, align)
if extraColumnsRendered > 0 {
out.WriteString(" colspan=")
fmt.Fprint(out, extraColumnsRendered+1)
} else if rowSpan := t.shouldMergeCellsVerticallyBelow(colIdx, hint); rowSpan > 1 {
out.WriteString(" rowspan=")
fmt.Fprint(out, rowSpan)
}
out.WriteString(">")
if len(colStr) == 0 {
out.WriteString(t.style.HTML.EmptyColumn)
} else {
t.htmlRenderColumn(out, colStr)
}
out.WriteString("</")
out.WriteString(colTagName)
out.WriteString(">\n")
colIdx += extraColumnsRendered
}
out.WriteString(" </tr>\n")
}
func (t *Table) htmlRenderRows(out *strings.Builder, rows []rowStr, hint renderHint) {
if len(rows) > 0 {
// determine that tag to use based on the type of the row
rowsTag := "tbody"
if hint.isHeaderRow {
rowsTag = "thead"
} else if hint.isFooterRow {
rowsTag = "tfoot"
}
var renderedTagOpen, shouldRenderTagClose bool
for idx, row := range rows {
hint.rowNumber = idx + 1
if len(row) > 0 {
if !renderedTagOpen {
out.WriteString(" <")
out.WriteString(rowsTag)
out.WriteString(">\n")
renderedTagOpen = true
}
t.htmlRenderRow(out, row, hint)
shouldRenderTagClose = true
}
t.firstRowOfPage = false
}
if shouldRenderTagClose {
out.WriteString(" </")
out.WriteString(rowsTag)
out.WriteString(">\n")
}
}
}
func (t *Table) htmlRenderRowsFooter(out *strings.Builder) {
if len(t.rowsFooter) > 0 {
t.htmlRenderRows(out, t.rowsFooter, renderHint{isFooterRow: true})
}
}
func (t *Table) htmlRenderRowsHeader(out *strings.Builder) {
if len(t.rowsHeader) > 0 {
t.htmlRenderRows(out, t.rowsHeader, renderHint{isHeaderRow: true})
} else if t.autoIndex {
hint := renderHint{isAutoIndexRow: true, isHeaderRow: true}
t.htmlRenderRows(out, []rowStr{t.getAutoIndexColumnIDs()}, hint)
}
}
func (t *Table) htmlRenderTitle(out *strings.Builder) {
if t.title != "" {
align := t.style.Title.Align.HTMLProperty()
colors := t.style.Title.Colors.HTMLProperty()
title := t.style.Title.Format.Apply(t.title)
out.WriteString(" <caption class=\"title\"")
if align != "" {
out.WriteRune(' ')
out.WriteString(align)
}
if colors != "" {
out.WriteRune(' ')
out.WriteString(colors)
}
out.WriteRune('>')
out.WriteString(title)
out.WriteString("</caption>\n")
}
}