2025-10-28 00:47:41 +01:00
|
|
|
package textfmt
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
2025-10-31 20:24:37 +01:00
|
|
|
type mdEscapeState struct {
|
|
|
|
|
newLine bool
|
|
|
|
|
numberOnNewLine bool
|
|
|
|
|
linkValue bool
|
|
|
|
|
linkClosed bool
|
|
|
|
|
linkOpen bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func updateMDEscapeState(s mdEscapeState, r rune) mdEscapeState {
|
|
|
|
|
return mdEscapeState{
|
|
|
|
|
numberOnNewLine: (s.newLine || s.numberOnNewLine) && r >= '0' && r <= '9',
|
|
|
|
|
newLine: r == '\n',
|
|
|
|
|
linkValue: s.linkClosed && r == '(' || s.linkValue && r != ')',
|
|
|
|
|
linkClosed: s.linkOpen && r == ']',
|
|
|
|
|
linkOpen: !s.linkValue && r == '[' || s.linkOpen && r != ']',
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func escapeMarkdown(out writer, additional ...rune) writer {
|
|
|
|
|
esc := map[rune]string{
|
|
|
|
|
'\\': "\\\\",
|
|
|
|
|
'`': "\\`",
|
|
|
|
|
'*': "\\*",
|
|
|
|
|
'_': "\\_",
|
|
|
|
|
'[': "\\[",
|
|
|
|
|
']': "\\]",
|
|
|
|
|
'#': "\\#",
|
|
|
|
|
'<': "\\<",
|
|
|
|
|
'>': "\\>",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, a := range additional {
|
|
|
|
|
esc[a] = string([]rune{'\\', a})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &escape[mdEscapeState]{
|
|
|
|
|
out: out,
|
|
|
|
|
state: mdEscapeState{
|
|
|
|
|
newLine: true,
|
|
|
|
|
},
|
|
|
|
|
escape: esc,
|
|
|
|
|
conditionalEscape: map[rune]func(mdEscapeState, rune) (string, bool){
|
|
|
|
|
'+': func(s mdEscapeState, _ rune) (string, bool) {
|
|
|
|
|
if s.newLine {
|
|
|
|
|
return "\\+", true
|
2025-10-28 00:47:41 +01:00
|
|
|
}
|
2025-10-31 20:24:37 +01:00
|
|
|
|
|
|
|
|
return "", false
|
|
|
|
|
},
|
|
|
|
|
'-': func(s mdEscapeState, _ rune) (string, bool) {
|
|
|
|
|
if s.newLine {
|
|
|
|
|
return "\\-", true
|
2025-10-28 00:47:41 +01:00
|
|
|
}
|
2025-10-31 20:24:37 +01:00
|
|
|
|
|
|
|
|
return "", false
|
|
|
|
|
},
|
|
|
|
|
'.': func(s mdEscapeState, _ rune) (string, bool) {
|
|
|
|
|
if s.numberOnNewLine {
|
|
|
|
|
return "\\.", true
|
2025-10-28 00:47:41 +01:00
|
|
|
}
|
2025-10-31 20:24:37 +01:00
|
|
|
|
|
|
|
|
return "", false
|
|
|
|
|
},
|
|
|
|
|
'(': func(s mdEscapeState, _ rune) (string, bool) {
|
|
|
|
|
if s.linkClosed {
|
|
|
|
|
return "\\(", true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "", false
|
|
|
|
|
},
|
|
|
|
|
')': func(s mdEscapeState, _ rune) (string, bool) {
|
|
|
|
|
if s.linkValue {
|
|
|
|
|
return "\\)", true
|
2025-10-28 00:47:41 +01:00
|
|
|
}
|
2025-10-31 20:24:37 +01:00
|
|
|
|
|
|
|
|
return "", false
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
updateState: updateMDEscapeState,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func escapeMarkdownPrev(s string, additional ...rune) string {
|
|
|
|
|
var b bytes.Buffer
|
|
|
|
|
w := &textWriter{out: &b}
|
|
|
|
|
e := escapeMarkdown(w, additional...)
|
|
|
|
|
e.write(s)
|
|
|
|
|
return b.String()
|
|
|
|
|
/*
|
|
|
|
|
var (
|
|
|
|
|
rr []rune
|
|
|
|
|
isNumberOnNewLine bool
|
|
|
|
|
isLinkOpen, isLinkClosed, isLinkValue bool
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
isNewLine := true
|
|
|
|
|
r := []rune(s)
|
|
|
|
|
for _, ri := range r {
|
|
|
|
|
switch ri {
|
|
|
|
|
case '\\', '`', '*', '_', '[', ']', '#', '<', '>':
|
|
|
|
|
rr = append(rr, '\\', ri)
|
2025-10-28 00:47:41 +01:00
|
|
|
default:
|
2025-10-31 20:24:37 +01:00
|
|
|
switch {
|
|
|
|
|
case isNewLine:
|
|
|
|
|
switch ri {
|
|
|
|
|
case '+', '-':
|
|
|
|
|
rr = append(rr, '\\', ri)
|
|
|
|
|
default:
|
|
|
|
|
rr = append(rr, ri)
|
|
|
|
|
}
|
|
|
|
|
case isNumberOnNewLine:
|
|
|
|
|
switch ri {
|
|
|
|
|
case '.':
|
|
|
|
|
rr = append(rr, '\\', ri)
|
|
|
|
|
default:
|
|
|
|
|
rr = append(rr, ri)
|
|
|
|
|
}
|
|
|
|
|
case isLinkClosed:
|
|
|
|
|
switch ri {
|
|
|
|
|
case '(':
|
|
|
|
|
rr = append(rr, '\\', ri)
|
|
|
|
|
default:
|
|
|
|
|
rr = append(rr, ri)
|
|
|
|
|
}
|
|
|
|
|
case isLinkValue:
|
|
|
|
|
switch ri {
|
|
|
|
|
case ')':
|
|
|
|
|
rr = append(rr, '\\', ri)
|
|
|
|
|
default:
|
|
|
|
|
rr = append(rr, ri)
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
if slices.Contains(additional, ri) {
|
|
|
|
|
rr = append(rr, '\\', ri)
|
|
|
|
|
} else {
|
|
|
|
|
rr = append(rr, ri)
|
|
|
|
|
}
|
2025-10-28 00:47:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-31 20:24:37 +01:00
|
|
|
isNumberOnNewLine = (isNewLine || isNumberOnNewLine) && ri >= '0' && ri <= '9'
|
|
|
|
|
isNewLine = ri == '\n'
|
|
|
|
|
isLinkValue = isLinkClosed && ri == '(' || isLinkValue && ri != ')'
|
|
|
|
|
isLinkClosed = isLinkOpen && ri == ']'
|
|
|
|
|
isLinkOpen = !isLinkValue && ri == '[' || isLinkOpen && ri != ']'
|
|
|
|
|
}
|
2025-10-28 00:47:41 +01:00
|
|
|
|
2025-10-31 20:24:37 +01:00
|
|
|
return string(rr)
|
|
|
|
|
*/
|
2025-10-28 00:47:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func mdTextToString(text Txt) (string, error) {
|
|
|
|
|
var b bytes.Buffer
|
|
|
|
|
w := mdWriter{w: &b, internal: true}
|
|
|
|
|
renderMDText(&w, text)
|
|
|
|
|
if w.err != nil {
|
|
|
|
|
return "", w.err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return b.String(), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func mdCellTexts(rows []TableRow) ([][]string, error) {
|
|
|
|
|
var texts [][]string
|
|
|
|
|
for _, row := range rows {
|
|
|
|
|
var rowTexts []string
|
|
|
|
|
for _, cell := range row.cells {
|
|
|
|
|
txt, err := mdTextToString(cell.text)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rowTexts = append(rowTexts, txt)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
texts = append(texts, rowTexts)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return texts, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func mdEnsureHeaderTexts(h []string) []string {
|
|
|
|
|
var hh []string
|
|
|
|
|
for _, t := range h {
|
|
|
|
|
if strings.TrimSpace(t) == "" {
|
|
|
|
|
t = "\\-"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hh = append(hh, t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hh
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDText(w writer, text Txt) {
|
|
|
|
|
if len(text.cat) > 0 {
|
|
|
|
|
for i, tc := range text.cat {
|
|
|
|
|
if i > 0 {
|
|
|
|
|
w.write(" ")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderMDText(w, tc)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
text.text = singleLine(text.text)
|
2025-10-31 20:24:37 +01:00
|
|
|
text.text = escapeMarkdownPrev(text.text)
|
2025-10-28 00:47:41 +01:00
|
|
|
text.link = singleLine(text.link)
|
2025-10-31 20:24:37 +01:00
|
|
|
text.link = escapeMarkdownPrev(text.link)
|
2025-10-28 00:47:41 +01:00
|
|
|
if text.bold {
|
|
|
|
|
w.write("**")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if text.italic {
|
|
|
|
|
w.write("_")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
|
if text.italic {
|
|
|
|
|
w.write("_")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if text.bold {
|
|
|
|
|
w.write("**")
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
if text.link != "" {
|
|
|
|
|
if text.text != "" {
|
|
|
|
|
w.write("[")
|
|
|
|
|
w.write(text.text)
|
|
|
|
|
w.write("](")
|
|
|
|
|
w.write(text.link)
|
|
|
|
|
w.write(")")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.write(text.link)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.write(text.text)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDTitle(w writer, e Entry) {
|
|
|
|
|
hashes := e.titleLevel + 1
|
|
|
|
|
if hashes > 6 {
|
|
|
|
|
hashes = 6
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.write(timesn("#", hashes), " ")
|
|
|
|
|
renderMDText(w, e.text)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDParagraphIndent(w writer, e Entry) {
|
|
|
|
|
txt, err := mdTextToString(e.text)
|
|
|
|
|
if err != nil {
|
|
|
|
|
w.setErr(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
indentFirst := e.indent + e.indentFirst
|
|
|
|
|
if e.wrapWidth > 0 {
|
|
|
|
|
txt = wrap(txt, e.wrapWidth, indentFirst, e.indent)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writeLines(w, txt, indentFirst, e.indent)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDParagraph(w writer, e Entry) {
|
|
|
|
|
e.indent = 0
|
|
|
|
|
e.indentFirst = 0
|
|
|
|
|
renderMDParagraphIndent(w, e)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDList(w writer, e Entry) {
|
|
|
|
|
e.indent = 2
|
|
|
|
|
e.indentFirst = -2
|
|
|
|
|
if e.wrapWidth > 2 {
|
|
|
|
|
e.wrapWidth -= 2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i, item := range e.items {
|
|
|
|
|
if i > 0 {
|
|
|
|
|
w.write("\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.write("- ")
|
|
|
|
|
p := itemToParagraph(e, item.text)
|
|
|
|
|
renderMDParagraphIndent(w, p)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDNumberedList(w writer, e Entry) {
|
|
|
|
|
maxDigits := numDigits(len(e.items))
|
|
|
|
|
e.indent = maxDigits + 2
|
|
|
|
|
e.indentFirst = 0 - maxDigits - 2
|
|
|
|
|
if e.wrapWidth > maxDigits+2 {
|
|
|
|
|
e.wrapWidth -= maxDigits + 2
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i, item := range e.items {
|
|
|
|
|
if i > 0 {
|
|
|
|
|
w.write("\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.write(padRight(fmt.Sprintf("%d.", i+1), maxDigits+2))
|
|
|
|
|
p := itemToParagraph(e, item.text)
|
|
|
|
|
renderMDParagraphIndent(w, p)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDDefinitions(w writer, e Entry) {
|
|
|
|
|
for _, d := range e.definitions {
|
|
|
|
|
e.items = append(
|
|
|
|
|
e.items,
|
|
|
|
|
Item(Cat(Text(fmt.Sprintf("%s:", d.name.text)), d.value)),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderMDList(w, e)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDNumberedDefinitions(w writer, e Entry) {
|
|
|
|
|
for _, d := range e.definitions {
|
|
|
|
|
e.items = append(
|
|
|
|
|
e.items,
|
|
|
|
|
Item(Cat(Text(fmt.Sprintf("%s:", d.name.text)), d.value)),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderMDNumberedList(w, e)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDTable(w writer, e Entry) {
|
|
|
|
|
e.rows = normalizeTable(e.rows)
|
|
|
|
|
e.rows = ensureHeader(e.rows)
|
|
|
|
|
if len(e.rows) == 0 || len(e.rows[0].cells) == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
headerTexts, err := mdCellTexts(e.rows[:1])
|
|
|
|
|
if err != nil {
|
|
|
|
|
w.setErr(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cellTexts, err := mdCellTexts(e.rows[1:])
|
|
|
|
|
if err != nil {
|
|
|
|
|
w.setErr(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
headerTexts[0] = mdEnsureHeaderTexts(headerTexts[0])
|
|
|
|
|
columns := columnWidths(headerTexts)
|
|
|
|
|
cellColumns := columnWidths(cellTexts)
|
|
|
|
|
if len(cellColumns) > 0 {
|
|
|
|
|
for i := range columns {
|
|
|
|
|
if cellColumns[i] > columns[i] {
|
|
|
|
|
columns[i] = cellColumns[i]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.write("|")
|
|
|
|
|
for i, h := range headerTexts[0] {
|
|
|
|
|
w.write(" ", padRight(h, columns[i]))
|
|
|
|
|
w.write(" |")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.write("\n|")
|
|
|
|
|
for _, c := range columns {
|
|
|
|
|
w.write(timesn("-", c+1))
|
|
|
|
|
w.write("-|")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, row := range cellTexts {
|
|
|
|
|
w.write("\n|")
|
|
|
|
|
for i, cell := range row {
|
|
|
|
|
w.write(" ", padRight(cell, columns[i]))
|
|
|
|
|
w.write(" |")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDCode(w writer, e Entry) {
|
|
|
|
|
w.write("```\n")
|
|
|
|
|
w.write(e.text.text)
|
|
|
|
|
w.write("\n```")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDMultiple(w writer, s SyntaxItem) {
|
|
|
|
|
s.topLevel = false
|
|
|
|
|
s.multiple = false
|
|
|
|
|
renderMDSyntaxItem(w, s)
|
|
|
|
|
w.write("...")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDRequired(w writer, s SyntaxItem) {
|
|
|
|
|
s.delimited = true
|
|
|
|
|
s.topLevel = false
|
|
|
|
|
s.required = false
|
|
|
|
|
w.write("<")
|
|
|
|
|
renderMDSyntaxItem(w, s)
|
|
|
|
|
w.write(">")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDOptional(w writer, s SyntaxItem) {
|
|
|
|
|
s.delimited = true
|
|
|
|
|
s.topLevel = false
|
|
|
|
|
s.optional = false
|
|
|
|
|
w.write("[")
|
|
|
|
|
renderMDSyntaxItem(w, s)
|
|
|
|
|
w.write("]")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDSequence(w writer, s SyntaxItem) {
|
|
|
|
|
if !s.delimited && !s.topLevel {
|
|
|
|
|
w.write("(")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i, item := range s.sequence {
|
|
|
|
|
if i > 0 {
|
|
|
|
|
w.write(" ")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
item.delimited = false
|
|
|
|
|
renderMDSyntaxItem(w, item)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !s.delimited && !s.topLevel {
|
|
|
|
|
w.write(")")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDChoice(w writer, s SyntaxItem) {
|
|
|
|
|
if !s.delimited && !s.topLevel {
|
|
|
|
|
w.write("(")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i, item := range s.choice {
|
|
|
|
|
if i > 0 {
|
|
|
|
|
separator := "|"
|
|
|
|
|
if s.topLevel {
|
|
|
|
|
separator = "\n"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.write(separator)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
item.delimited = false
|
|
|
|
|
renderMDSyntaxItem(w, item)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !s.delimited && !s.topLevel {
|
|
|
|
|
w.write(")")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDSymbol(w writer, s SyntaxItem) {
|
2025-10-31 20:24:37 +01:00
|
|
|
w.write(escapeMarkdownPrev(s.symbol))
|
2025-10-28 00:47:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDSyntaxItem(w writer, s SyntaxItem) {
|
|
|
|
|
switch {
|
|
|
|
|
|
|
|
|
|
// foo...
|
|
|
|
|
case s.multiple:
|
|
|
|
|
renderMDMultiple(w, s)
|
|
|
|
|
|
|
|
|
|
// <foo>
|
|
|
|
|
case s.required:
|
|
|
|
|
renderMDRequired(w, s)
|
|
|
|
|
|
|
|
|
|
// [foo]
|
|
|
|
|
case s.optional:
|
|
|
|
|
renderMDOptional(w, s)
|
|
|
|
|
|
|
|
|
|
// foo bar baz or (foo bar baz)
|
|
|
|
|
case len(s.sequence) > 0:
|
|
|
|
|
renderMDSequence(w, s)
|
|
|
|
|
|
|
|
|
|
// foo|bar|baz or (foo|bar|baz)
|
|
|
|
|
case len(s.choice) > 0:
|
|
|
|
|
renderMDChoice(w, s)
|
|
|
|
|
|
|
|
|
|
// foo
|
|
|
|
|
default:
|
|
|
|
|
renderMDSymbol(w, s)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMDSyntax(w writer, e Entry) {
|
|
|
|
|
s := e.syntax
|
|
|
|
|
s.topLevel = true
|
|
|
|
|
w.write("```\n")
|
|
|
|
|
renderMDSyntaxItem(w, s)
|
|
|
|
|
w.write("\n```")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderMarkdown(out io.Writer, d Document) error {
|
|
|
|
|
w := mdWriter{w: out}
|
|
|
|
|
for i, e := range d.entries {
|
|
|
|
|
if i > 0 {
|
|
|
|
|
w.write("\n\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch e.typ {
|
|
|
|
|
case title:
|
|
|
|
|
renderMDTitle(&w, e)
|
|
|
|
|
case paragraph:
|
|
|
|
|
renderMDParagraph(&w, e)
|
|
|
|
|
case list:
|
|
|
|
|
renderMDList(&w, e)
|
|
|
|
|
case numberedList:
|
|
|
|
|
renderMDNumberedList(&w, e)
|
|
|
|
|
case definitions:
|
|
|
|
|
renderMDDefinitions(&w, e)
|
|
|
|
|
case numberedDefinitions:
|
|
|
|
|
renderMDNumberedDefinitions(&w, e)
|
|
|
|
|
case table:
|
|
|
|
|
renderMDTable(&w, e)
|
|
|
|
|
case code:
|
|
|
|
|
renderMDCode(&w, e)
|
|
|
|
|
case syntax:
|
|
|
|
|
renderMDSyntax(&w, e)
|
2025-10-28 02:48:55 +01:00
|
|
|
default:
|
|
|
|
|
return errors.New("invalid entry")
|
2025-10-28 00:47:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(d.entries) > 0 {
|
|
|
|
|
w.write("\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return w.err
|
|
|
|
|
}
|