improved markdown table support for go port

This commit is contained in:
Luxferre
2026-08-15 09:00:48 +03:00
parent 381cd3e9fb
commit e4140bce67
2 changed files with 228 additions and 9 deletions
+167 -9
View File
@@ -282,6 +282,117 @@ func isTableLine(line string) bool {
return strings.HasPrefix(trimmed, "|") || strings.HasSuffix(trimmed, "|")
}
func wrapCell(s string, width int) []string {
if width <= 0 { return []string{""} }
s = strings.ReplaceAll(s, "<br>", "\n")
s = strings.ReplaceAll(s, "<br/>", "\n")
s = strings.ReplaceAll(s, "<br />", "\n")
var lines []string
for _, p := range strings.Split(s, "\n") {
p = strings.TrimSpace(p)
if p == "" {
lines = append(lines, "")
continue
}
if visibleLen(p) <= width {
lines = append(lines, p)
continue
}
words := strings.Fields(p)
if len(words) == 0 {
lines = append(lines, "")
continue
}
var curLine string
var activeEsc string
updateActiveEsc := func(token string) {
for i := 0; i < len(token); {
if token[i] == 0x1b {
j := i + 1
if j < len(token) && token[j] == '[' {
j++
for j < len(token) && !(token[j] >= 0x40 && token[j] <= 0x7e) { j++ }
if j < len(token) { j++ }
}
esc := token[i:j]
if esc == "\033[0m" || esc == "\033[m" {
activeEsc = ""
} else {
activeEsc = esc
}
i = j
} else {
i++
}
}
}
flushLine := func() {
if curLine != "" {
out := curLine
if COL && activeEsc != "" && !strings.HasSuffix(out, "\033[0m") {
out += "\033[0m"
}
lines = append(lines, out)
curLine = ""
}
}
for _, word := range words {
wLen := visibleLen(word)
if wLen > width {
flushLine()
var chunk strings.Builder
cLen := 0
for i := 0; i < len(word); {
if word[i] == 0x1b {
j := i + 1
if j < len(word) && word[j] == '[' {
j++
for j < len(word) && !(word[j] >= 0x40 && word[j] <= 0x7e) { j++ }
if j < len(word) { j++ }
}
chunk.WriteString(word[i:j])
updateActiveEsc(word[i:j])
i = j
continue
}
r, size := utf8.DecodeRuneInString(word[i:])
if cLen >= width {
if COL && activeEsc != "" { chunk.WriteString("\033[0m") }
lines = append(lines, chunk.String())
chunk.Reset()
if COL && activeEsc != "" { chunk.WriteString(activeEsc) }
cLen = 0
}
chunk.WriteRune(r)
cLen++
i += size
}
if chunk.Len() > 0 { curLine = chunk.String() }
continue
}
cLen := visibleLen(curLine)
if curLine == "" {
curLine = word
updateActiveEsc(word)
} else if cLen+1+wLen <= width {
curLine += " " + word
updateActiveEsc(word)
} else {
flushLine()
if COL && activeEsc != "" {
curLine = activeEsc + word
} else {
curLine = word
}
updateActiveEsc(word)
}
}
flushLine()
}
if len(lines) == 0 { lines = []string{""} }
return lines
}
func renderTable(lines []string) []string {
if len(lines) == 0 { return nil }
var rows [][]string
@@ -334,6 +445,29 @@ func renderTable(lines []string) []string {
if colWidths[i] < 3 { colWidths[i] = 3 }
}
maxTableWidth := termWidth()
if maxTableWidth < 20 { maxTableWidth = 80 }
overhead := 3*numCols + 1
availContent := maxTableWidth - overhead
if availContent < numCols*3 { availContent = numCols * 3 }
tot := 0
for _, w := range colWidths { tot += w }
for tot > availContent {
maxIdx := 0
maxVal := colWidths[0]
for i := 1; i < numCols; i++ {
if colWidths[i] > maxVal {
maxVal = colWidths[i]
maxIdx = i
}
}
if maxVal <= 3 { break }
colWidths[maxIdx]--
tot--
}
var res []string
var topParts []string
@@ -341,13 +475,25 @@ func renderTable(lines []string) []string {
res = append(res, c("┌"+strings.Join(topParts, "┬")+"┐", 2))
if hasHeader {
var hCells []string
headerCols := make([][]string, numCols)
maxHeaderLines := 1
for i, h := range headerRow {
rh := c(h, 1, 36)
pad := strings.Repeat(" ", colWidths[i]-visibleLen(h))
hCells = append(hCells, " "+rh+pad+" ")
wrapped := wrapCell(h, colWidths[i])
if len(wrapped) > maxHeaderLines { maxHeaderLines = len(wrapped) }
headerCols[i] = wrapped
}
for lineIdx := 0; lineIdx < maxHeaderLines; lineIdx++ {
var hCells []string
for i := 0; i < numCols; i++ {
txt := ""
if lineIdx < len(headerCols[i]) { txt = headerCols[i][lineIdx] }
rh := c(txt, 1, 36)
pad := strings.Repeat(" ", colWidths[i]-visibleLen(txt))
hCells = append(hCells, " "+rh+pad+" ")
}
res = append(res, c("│", 2)+strings.Join(hCells, c("│", 2))+c("│", 2))
}
res = append(res, c("│", 2)+strings.Join(hCells, c("│", 2))+c("│", 2))
var midParts []string
for _, w := range colWidths { midParts = append(midParts, strings.Repeat("─", w+2)) }
@@ -355,13 +501,25 @@ func renderTable(lines []string) []string {
}
for _, r := range rows {
var rCells []string
rowCols := make([][]string, numCols)
maxRowLines := 1
for i, cell := range r {
rc := renderInline(cell)
pad := strings.Repeat(" ", colWidths[i]-visibleLen(rc))
rCells = append(rCells, " "+rc+pad+" ")
wrapped := wrapCell(rc, colWidths[i])
if len(wrapped) > maxRowLines { maxRowLines = len(wrapped) }
rowCols[i] = wrapped
}
for lineIdx := 0; lineIdx < maxRowLines; lineIdx++ {
var rCells []string
for i := 0; i < numCols; i++ {
txt := ""
if lineIdx < len(rowCols[i]) { txt = rowCols[i][lineIdx] }
pad := strings.Repeat(" ", colWidths[i]-visibleLen(txt))
rCells = append(rCells, " "+txt+pad+" ")
}
res = append(res, c("│", 2)+strings.Join(rCells, c("│", 2))+c("│", 2))
}
res = append(res, c("│", 2)+strings.Join(rCells, c("│", 2))+c("│", 2))
}
var botParts []string