mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-06-12 00:13:17 +00:00
bdb60de7ae
- Update go.mod and Dockerfile to Go 1.26 - Apply `go fix ./...` stdlib modernizations across 170+ files - Add `go fix` to post-implementation checklist in CLAUDE.md - Fix go fix misapplied rewrite in loop_history.go
252 lines
5.0 KiB
Go
252 lines
5.0 KiB
Go
package tools
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"golang.org/x/net/html"
|
|
"golang.org/x/net/html/atom"
|
|
)
|
|
|
|
func (c *converter) handleText(n *html.Node) {
|
|
text := n.Data
|
|
if c.inPre {
|
|
c.buf.WriteString(text)
|
|
return
|
|
}
|
|
text = collapseWhitespace(text)
|
|
if text == "" {
|
|
return
|
|
}
|
|
if text == " " && c.buf.Len() > 0 {
|
|
c.buf.WriteByte(' ')
|
|
return
|
|
}
|
|
c.buf.WriteString(text)
|
|
}
|
|
|
|
func (c *converter) handleHeading(n *html.Node) {
|
|
c.ensureDoubleNewline()
|
|
if c.mode == modeMarkdown && len(n.Data) == 2 && n.Data[0] == 'h' {
|
|
level := int(n.Data[1] - '0')
|
|
for range level {
|
|
c.buf.WriteByte('#')
|
|
}
|
|
c.buf.WriteByte(' ')
|
|
}
|
|
c.walkChildren(n)
|
|
c.buf.WriteByte('\n')
|
|
}
|
|
|
|
func (c *converter) handleParagraph(n *html.Node) {
|
|
c.ensureDoubleNewline()
|
|
c.walkChildren(n)
|
|
c.buf.WriteByte('\n')
|
|
}
|
|
|
|
func (c *converter) handleLink(n *html.Node) {
|
|
href := getAttr(n, "href")
|
|
if c.mode == modeText || c.inLink || href == "" || strings.HasPrefix(href, "javascript:") {
|
|
c.walkChildren(n)
|
|
return
|
|
}
|
|
c.inLink = true
|
|
c.buf.WriteByte('[')
|
|
c.walkChildren(n)
|
|
c.buf.WriteString("](")
|
|
c.buf.WriteString(href)
|
|
c.buf.WriteByte(')')
|
|
c.inLink = false
|
|
}
|
|
|
|
func (c *converter) handleImage(n *html.Node) {
|
|
alt := getAttr(n, "alt")
|
|
src := getAttr(n, "src")
|
|
if c.mode == modeMarkdown {
|
|
c.buf.WriteString("![")
|
|
c.buf.WriteString(alt)
|
|
c.buf.WriteByte(']')
|
|
if src != "" {
|
|
c.buf.WriteByte('(')
|
|
c.buf.WriteString(src)
|
|
c.buf.WriteByte(')')
|
|
}
|
|
} else if alt != "" {
|
|
c.buf.WriteString(alt)
|
|
}
|
|
}
|
|
|
|
func (c *converter) handlePre(n *html.Node) {
|
|
c.ensureDoubleNewline()
|
|
if c.mode == modeMarkdown {
|
|
lang := ""
|
|
if code := findChild(n, atom.Code); code != nil {
|
|
cls := getAttr(code, "class")
|
|
for part := range strings.FieldsSeq(cls) {
|
|
if rest, ok := strings.CutPrefix(part, "language-"); ok {
|
|
lang = rest
|
|
break
|
|
}
|
|
if rest, ok := strings.CutPrefix(part, "lang-"); ok {
|
|
lang = rest
|
|
break
|
|
}
|
|
}
|
|
}
|
|
c.buf.WriteString("```")
|
|
c.buf.WriteString(lang)
|
|
c.buf.WriteByte('\n')
|
|
}
|
|
c.inPre = true
|
|
c.walkChildren(n)
|
|
c.inPre = false
|
|
if c.mode == modeMarkdown {
|
|
c.ensureNewline()
|
|
c.buf.WriteString("```\n")
|
|
} else {
|
|
c.buf.WriteByte('\n')
|
|
}
|
|
}
|
|
|
|
func (c *converter) handleCode(n *html.Node) {
|
|
if c.inPre {
|
|
c.walkChildren(n)
|
|
return
|
|
}
|
|
if c.mode == modeMarkdown {
|
|
c.buf.WriteByte('`')
|
|
c.walkChildren(n)
|
|
c.buf.WriteByte('`')
|
|
} else {
|
|
c.walkChildren(n)
|
|
}
|
|
}
|
|
|
|
func (c *converter) handleBlockquote(n *html.Node) {
|
|
c.ensureDoubleNewline()
|
|
if c.mode == modeMarkdown {
|
|
sub := &converter{mode: c.mode, inPre: c.inPre}
|
|
sub.walkChildren(n)
|
|
for i, line := range strings.Split(strings.TrimSpace(sub.buf.String()), "\n") {
|
|
if i > 0 {
|
|
c.buf.WriteByte('\n')
|
|
}
|
|
c.buf.WriteString("> ")
|
|
c.buf.WriteString(line)
|
|
}
|
|
c.buf.WriteByte('\n')
|
|
} else {
|
|
c.walkChildren(n)
|
|
}
|
|
}
|
|
|
|
func (c *converter) handleStrong(n *html.Node) {
|
|
if c.mode == modeMarkdown {
|
|
c.buf.WriteString("**")
|
|
c.walkChildren(n)
|
|
c.buf.WriteString("**")
|
|
} else {
|
|
c.walkChildren(n)
|
|
}
|
|
}
|
|
|
|
func (c *converter) handleEmphasis(n *html.Node) {
|
|
if c.mode == modeMarkdown {
|
|
c.buf.WriteByte('*')
|
|
c.walkChildren(n)
|
|
c.buf.WriteByte('*')
|
|
} else {
|
|
c.walkChildren(n)
|
|
}
|
|
}
|
|
|
|
func (c *converter) handleList(n *html.Node) {
|
|
c.ensureNewline()
|
|
c.listDepth++
|
|
c.listType = append(c.listType, n.DataAtom)
|
|
c.listIndex = append(c.listIndex, 0)
|
|
c.walkChildren(n)
|
|
c.listDepth--
|
|
c.listType = c.listType[:len(c.listType)-1]
|
|
c.listIndex = c.listIndex[:len(c.listIndex)-1]
|
|
c.ensureNewline()
|
|
}
|
|
|
|
func (c *converter) handleListItem(n *html.Node) {
|
|
c.ensureNewline()
|
|
indent := strings.Repeat(" ", max(0, c.listDepth-1))
|
|
c.buf.WriteString(indent)
|
|
|
|
if len(c.listType) > 0 && c.listType[len(c.listType)-1] == atom.Ol {
|
|
idx := len(c.listIndex) - 1
|
|
c.listIndex[idx]++
|
|
fmt.Fprintf(&c.buf, "%d. ", c.listIndex[idx])
|
|
} else {
|
|
c.buf.WriteString("- ")
|
|
}
|
|
c.walkChildren(n)
|
|
}
|
|
|
|
func (c *converter) handleTable(n *html.Node) {
|
|
c.ensureDoubleNewline()
|
|
rows := collectTableRows(n, c.mode)
|
|
if len(rows) == 0 {
|
|
return
|
|
}
|
|
colCount := 0
|
|
for _, row := range rows {
|
|
if len(row) > colCount {
|
|
colCount = len(row)
|
|
}
|
|
}
|
|
if c.mode == modeMarkdown {
|
|
for i, row := range rows {
|
|
c.buf.WriteByte('|')
|
|
for j := 0; j < colCount; j++ {
|
|
cell := ""
|
|
if j < len(row) {
|
|
cell = row[j]
|
|
}
|
|
c.buf.WriteByte(' ')
|
|
c.buf.WriteString(cell)
|
|
c.buf.WriteString(" |")
|
|
}
|
|
c.buf.WriteByte('\n')
|
|
if i == 0 {
|
|
c.buf.WriteByte('|')
|
|
for j := 0; j < colCount; j++ {
|
|
c.buf.WriteString(" --- |")
|
|
}
|
|
c.buf.WriteByte('\n')
|
|
}
|
|
}
|
|
} else {
|
|
for _, row := range rows {
|
|
c.buf.WriteString(strings.Join(row, " | "))
|
|
c.buf.WriteByte('\n')
|
|
}
|
|
}
|
|
c.buf.WriteByte('\n')
|
|
}
|
|
|
|
func (c *converter) handleDefinitionTerm(n *html.Node) {
|
|
c.ensureDoubleNewline()
|
|
if c.mode == modeMarkdown {
|
|
c.buf.WriteString("**")
|
|
c.walkChildren(n)
|
|
c.buf.WriteString("**")
|
|
} else {
|
|
c.walkChildren(n)
|
|
}
|
|
c.buf.WriteByte('\n')
|
|
}
|
|
|
|
func (c *converter) handleDefinitionDesc(n *html.Node) {
|
|
c.ensureNewline()
|
|
if c.mode == modeMarkdown {
|
|
c.buf.WriteString(": ")
|
|
}
|
|
c.walkChildren(n)
|
|
c.buf.WriteByte('\n')
|
|
}
|