mirror of https://github.com/usememos/memos
chore: implement html render
parent
3edce174d6
commit
242f64fa8e
@ -0,0 +1,171 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/usememos/memos/plugin/gomark/ast"
|
||||
)
|
||||
|
||||
// HTMLRender is a simple renderer that converts AST to HTML.
|
||||
type HTMLRender struct {
|
||||
output *bytes.Buffer
|
||||
context *RendererContext
|
||||
}
|
||||
|
||||
type RendererContext struct {
|
||||
}
|
||||
|
||||
// NewHTMLRender creates a new HTMLRender.
|
||||
func NewHTMLRender() *HTMLRender {
|
||||
return &HTMLRender{
|
||||
output: new(bytes.Buffer),
|
||||
context: &RendererContext{},
|
||||
}
|
||||
}
|
||||
|
||||
// RenderNode renders a single AST node to HTML.
|
||||
func (r *HTMLRender) 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.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.Text:
|
||||
r.renderText(n)
|
||||
default:
|
||||
// Handle other block types if needed.
|
||||
}
|
||||
}
|
||||
|
||||
// RenderNodes renders a slice of AST nodes to HTML.
|
||||
func (r *HTMLRender) RenderNodes(nodes []ast.Node) {
|
||||
for _, node := range nodes {
|
||||
r.RenderNode(node)
|
||||
}
|
||||
}
|
||||
|
||||
// Render renders the AST to HTML.
|
||||
func (r *HTMLRender) Render(astRoot []ast.Node) string {
|
||||
r.RenderNodes(astRoot)
|
||||
return r.output.String()
|
||||
}
|
||||
|
||||
func (r *HTMLRender) renderLineBreak(_ *ast.LineBreak) {
|
||||
r.output.WriteString("<br>")
|
||||
}
|
||||
|
||||
func (r *HTMLRender) renderParagraph(node *ast.Paragraph) {
|
||||
r.output.WriteString("<p>")
|
||||
r.RenderNodes(node.Children)
|
||||
r.output.WriteString("</p>")
|
||||
}
|
||||
|
||||
func (r *HTMLRender) renderCodeBlock(node *ast.CodeBlock) {
|
||||
r.output.WriteString("<pre><code>")
|
||||
r.output.WriteString(node.Content)
|
||||
r.output.WriteString("</code></pre>")
|
||||
}
|
||||
|
||||
func (r *HTMLRender) 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 *HTMLRender) renderHorizontalRule(_ *ast.HorizontalRule) {
|
||||
r.output.WriteString("<hr>")
|
||||
}
|
||||
|
||||
func (r *HTMLRender) renderBlockquote(node *ast.Blockquote) {
|
||||
prevSibling, nextSibling := node.PrevSibling(), node.NextSibling()
|
||||
if prevSibling == nil || prevSibling.Type() != ast.BlockquoteNode {
|
||||
r.output.WriteString("<blockquote>")
|
||||
}
|
||||
r.RenderNodes(node.Children)
|
||||
if nextSibling == nil || nextSibling.Type() != ast.BlockquoteNode {
|
||||
r.output.WriteString("</blockquote>")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *HTMLRender) renderText(node *ast.Text) {
|
||||
r.output.WriteString(node.Content)
|
||||
}
|
||||
|
||||
func (r *HTMLRender) renderBold(node *ast.Bold) {
|
||||
r.output.WriteString("<strong>")
|
||||
r.output.WriteString(node.Content)
|
||||
r.output.WriteString("</strong>")
|
||||
}
|
||||
|
||||
func (r *HTMLRender) renderItalic(node *ast.Italic) {
|
||||
r.output.WriteString("<em>")
|
||||
r.output.WriteString(node.Content)
|
||||
r.output.WriteString("</em>")
|
||||
}
|
||||
|
||||
func (r *HTMLRender) renderBoldItalic(node *ast.BoldItalic) {
|
||||
r.output.WriteString("<strong><em>")
|
||||
r.output.WriteString(node.Content)
|
||||
r.output.WriteString("</em></strong>")
|
||||
}
|
||||
|
||||
func (r *HTMLRender) renderCode(node *ast.Code) {
|
||||
r.output.WriteString("<code>")
|
||||
r.output.WriteString(node.Content)
|
||||
r.output.WriteString("</code>")
|
||||
}
|
||||
|
||||
func (r *HTMLRender) renderImage(node *ast.Image) {
|
||||
r.output.WriteString(`<img src="`)
|
||||
r.output.WriteString(node.URL)
|
||||
r.output.WriteString(`" alt="`)
|
||||
r.output.WriteString(node.AltText)
|
||||
r.output.WriteString(`" />`)
|
||||
}
|
||||
|
||||
func (r *HTMLRender) renderLink(node *ast.Link) {
|
||||
r.output.WriteString(`<a href="`)
|
||||
r.output.WriteString(node.URL)
|
||||
r.output.WriteString(`">`)
|
||||
r.output.WriteString(node.Text)
|
||||
r.output.WriteString("</a>")
|
||||
}
|
||||
|
||||
func (r *HTMLRender) renderTag(node *ast.Tag) {
|
||||
r.output.WriteString(`<span>`)
|
||||
r.output.WriteString(`# `)
|
||||
r.output.WriteString(node.Content)
|
||||
r.output.WriteString(`</span>`)
|
||||
}
|
||||
|
||||
func (r *HTMLRender) renderStrikethrough(node *ast.Strikethrough) {
|
||||
r.output.WriteString(`<del>`)
|
||||
r.output.WriteString(node.Content)
|
||||
r.output.WriteString(`</del>`)
|
||||
}
|
@ -0,0 +1 @@
|
||||
package render
|
@ -1,124 +0,0 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/usememos/memos/plugin/gomark/ast"
|
||||
)
|
||||
|
||||
// HTMLRenderer is a simple renderer that converts AST to HTML.
|
||||
// nolint
|
||||
type HTMLRenderer struct {
|
||||
output *bytes.Buffer
|
||||
context *RendererContext
|
||||
}
|
||||
|
||||
type RendererContext struct {
|
||||
}
|
||||
|
||||
// NewHTMLRenderer creates a new HTMLRenderer.
|
||||
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) {
|
||||
prevSibling, nextSibling := node.GetPrevSibling(), node.GetNextSibling()
|
||||
|
||||
switch n := node.(type) {
|
||||
case *ast.LineBreak:
|
||||
r.output.WriteString("<br>")
|
||||
case *ast.Paragraph:
|
||||
r.output.WriteString("<p>")
|
||||
r.RenderNodes(n.Children)
|
||||
r.output.WriteString("</p>")
|
||||
case *ast.CodeBlock:
|
||||
r.output.WriteString("<pre><code>")
|
||||
r.output.WriteString(n.Content)
|
||||
r.output.WriteString("</code></pre>")
|
||||
case *ast.Heading:
|
||||
r.output.WriteString(fmt.Sprintf("<h%d>", n.Level))
|
||||
r.RenderNodes(n.Children)
|
||||
r.output.WriteString(fmt.Sprintf("</h%d>", n.Level))
|
||||
case *ast.HorizontalRule:
|
||||
r.output.WriteString("<hr>")
|
||||
case *ast.Blockquote:
|
||||
if prevSibling == nil || prevSibling.Type() != ast.NodeTypeBlockquote {
|
||||
r.output.WriteString("<blockquote>")
|
||||
}
|
||||
r.RenderNodes(n.Children)
|
||||
if nextSibling != nil && nextSibling.Type() == ast.NodeTypeBlockquote {
|
||||
r.RenderNode(nextSibling)
|
||||
}
|
||||
if prevSibling == nil || prevSibling.Type() != ast.NodeTypeBlockquote {
|
||||
r.output.WriteString("</blockquote>")
|
||||
}
|
||||
case *ast.BoldItalic:
|
||||
r.output.WriteString("<strong><em>")
|
||||
r.output.WriteString(n.Content)
|
||||
r.output.WriteString("</em></strong>")
|
||||
case *ast.Bold:
|
||||
r.output.WriteString("<strong>")
|
||||
r.output.WriteString(n.Content)
|
||||
r.output.WriteString("</strong>")
|
||||
case *ast.Italic:
|
||||
r.output.WriteString("<em>")
|
||||
r.output.WriteString(n.Content)
|
||||
r.output.WriteString("</em>")
|
||||
case *ast.Code:
|
||||
r.output.WriteString("<code>")
|
||||
r.output.WriteString(n.Content)
|
||||
r.output.WriteString("</code>")
|
||||
case *ast.Link:
|
||||
r.output.WriteString(`<a href="`)
|
||||
r.output.WriteString(n.URL)
|
||||
r.output.WriteString(`">`)
|
||||
r.output.WriteString(n.Text)
|
||||
r.output.WriteString("</a>")
|
||||
case *ast.Image:
|
||||
r.output.WriteString(`<img src="`)
|
||||
r.output.WriteString(n.URL)
|
||||
r.output.WriteString(`" alt="`)
|
||||
r.output.WriteString(n.AltText)
|
||||
r.output.WriteString(`" />`)
|
||||
case *ast.Tag:
|
||||
r.output.WriteString(`<span>`)
|
||||
r.output.WriteString(`# `)
|
||||
r.output.WriteString(n.Content)
|
||||
r.output.WriteString(`</span>`)
|
||||
case *ast.Strikethrough:
|
||||
r.output.WriteString(`<del>`)
|
||||
r.output.WriteString(n.Content)
|
||||
r.output.WriteString(`</del>`)
|
||||
case *ast.Text:
|
||||
r.output.WriteString(n.Content)
|
||||
default:
|
||||
// Handle other block types if needed.
|
||||
}
|
||||
}
|
||||
|
||||
// RenderNodes renders a slice of AST nodes to HTML.
|
||||
func (r *HTMLRenderer) RenderNodes(nodes []ast.Node) {
|
||||
for _, node := range nodes {
|
||||
prevSibling := node.GetPrevSibling()
|
||||
if prevSibling != nil {
|
||||
if prevSibling.Type() == node.Type() {
|
||||
if node.Type() == ast.NodeTypeBlockquote {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.RenderNode(node)
|
||||
}
|
||||
}
|
||||
|
||||
// Render renders the AST to HTML.
|
||||
func (r *HTMLRenderer) Render(astRoot []ast.Node) string {
|
||||
r.RenderNodes(astRoot)
|
||||
return r.output.String()
|
||||
}
|
@ -1 +0,0 @@
|
||||
package renderer
|
Loading…
Reference in New Issue