package html
import (
"bytes"
"fmt"
"github.com/usememos/memos/plugin/gomark/ast"
)
// HTMLRenderer is a simple renderer that converts AST to HTML.
type HTMLRenderer struct {
output *bytes.Buffer
context *RendererContext
}
type RendererContext struct {
}
// NewHTMLRenderer creates a new HTMLRender.
func NewHTMLRenderer() *HTMLRenderer {
return &HTMLRenderer{
output: new(bytes.Buffer),
context: &RendererContext{},
}
}
// RenderNode renders a single AST node to HTML.
func (r *HTMLRenderer) RenderNode(node ast.Node) {
switch n := node.(type) {
case *ast.LineBreak:
r.renderLineBreak(n)
case *ast.Paragraph:
r.renderParagraph(n)
case *ast.CodeBlock:
r.renderCodeBlock(n)
case *ast.Heading:
r.renderHeading(n)
case *ast.HorizontalRule:
r.renderHorizontalRule(n)
case *ast.Blockquote:
r.renderBlockquote(n)
case *ast.UnorderedList:
r.renderUnorderedList(n)
case *ast.OrderedList:
r.renderOrderedList(n)
case *ast.TaskList:
r.renderTaskList(n)
case *ast.Bold:
r.renderBold(n)
case *ast.Italic:
r.renderItalic(n)
case *ast.BoldItalic:
r.renderBoldItalic(n)
case *ast.Code:
r.renderCode(n)
case *ast.Image:
r.renderImage(n)
case *ast.Link:
r.renderLink(n)
case *ast.Tag:
r.renderTag(n)
case *ast.Strikethrough:
r.renderStrikethrough(n)
case *ast.EscapingCharacter:
r.renderEscapingCharacter(n)
case *ast.Text:
r.renderText(n)
default:
// Handle other block types if needed.
}
}
// RenderNodes renders a slice of AST nodes to HTML.
func (r *HTMLRenderer) RenderNodes(nodes []ast.Node) {
var prevNode ast.Node
var skipNextLineBreakFlag bool
for _, node := range nodes {
if node.Type() == ast.LineBreakNode && skipNextLineBreakFlag {
if prevNode != nil && ast.IsBlockNode(prevNode) {
skipNextLineBreakFlag = false
continue
}
}
r.RenderNode(node)
prevNode = node
skipNextLineBreakFlag = true
}
}
// Render renders the AST to HTML.
func (r *HTMLRenderer) Render(astRoot []ast.Node) string {
r.RenderNodes(astRoot)
return r.output.String()
}
func (r *HTMLRenderer) renderLineBreak(*ast.LineBreak) {
r.output.WriteString("
")
}
func (r *HTMLRenderer) renderParagraph(node *ast.Paragraph) {
r.output.WriteString("
") r.RenderNodes(node.Children) r.output.WriteString("
") } func (r *HTMLRenderer) renderCodeBlock(node *ast.CodeBlock) { r.output.WriteString("")
r.output.WriteString(node.Content)
r.output.WriteString("
")
}
func (r *HTMLRenderer) renderHeading(node *ast.Heading) {
element := fmt.Sprintf("h%d", node.Level)
r.output.WriteString(fmt.Sprintf("<%s>", element))
r.RenderNodes(node.Children)
r.output.WriteString(fmt.Sprintf("%s>", element))
}
func (r *HTMLRenderer) renderHorizontalRule(_ *ast.HorizontalRule) {
r.output.WriteString("") } r.RenderNodes(node.Children) if nextSibling == nil || nextSibling.Type() != ast.BlockquoteNode { r.output.WriteString("") } } func (r *HTMLRenderer) renderTaskList(node *ast.TaskList) { prevSibling, nextSibling := ast.FindPrevSiblingExceptLineBreak(node), ast.FindNextSiblingExceptLineBreak(node) if prevSibling == nil || prevSibling.Type() != ast.TaskListNode { r.output.WriteString("
")
r.output.WriteString(node.Content)
r.output.WriteString("
")
}
func (r *HTMLRenderer) renderImage(node *ast.Image) {
r.output.WriteString(`