diff --git a/.golangci.yaml b/.golangci.yaml
index adcf3fd3f..c9dc4037d 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -69,6 +69,10 @@ linters-settings:
disabled: true
- name: use-any
disabled: true
+ - name: exported
+ disabled: true
+ - name: unhandled-error
+ disabled: true
gocritic:
disabled-checks:
- ifElseChain
diff --git a/plugin/gomark/ast/block.go b/plugin/gomark/ast/block.go
index ff254e523..b5390f3ea 100644
--- a/plugin/gomark/ast/block.go
+++ b/plugin/gomark/ast/block.go
@@ -1,7 +1,5 @@
package ast
-import "fmt"
-
type BaseBlock struct {
BaseNode
}
@@ -10,14 +8,8 @@ type LineBreak struct {
BaseBlock
}
-var NodeTypeLineBreak = NewNodeType("LineBreak")
-
func (*LineBreak) Type() NodeType {
- return NodeTypeLineBreak
-}
-
-func (n *LineBreak) String() string {
- return n.Type().String()
+ return LineBreakNode
}
type Paragraph struct {
@@ -26,18 +18,8 @@ type Paragraph struct {
Children []Node
}
-var NodeTypeParagraph = NewNodeType("Paragraph")
-
func (*Paragraph) Type() NodeType {
- return NodeTypeParagraph
-}
-
-func (n *Paragraph) String() string {
- str := n.Type().String()
- for _, child := range n.Children {
- str += " " + child.String()
- }
- return str
+ return ParagraphNode
}
type CodeBlock struct {
@@ -47,14 +29,8 @@ type CodeBlock struct {
Content string
}
-var NodeTypeCodeBlock = NewNodeType("CodeBlock")
-
func (*CodeBlock) Type() NodeType {
- return NodeTypeCodeBlock
-}
-
-func (n *CodeBlock) String() string {
- return n.Type().String() + " " + n.Language + " " + n.Content
+ return CodeBlockNode
}
type Heading struct {
@@ -64,18 +40,8 @@ type Heading struct {
Children []Node
}
-var NodeTypeHeading = NewNodeType("Heading")
-
func (*Heading) Type() NodeType {
- return NodeTypeHeading
-}
-
-func (n *Heading) String() string {
- str := n.Type().String() + " " + fmt.Sprintf("%d", n.Level)
- for _, child := range n.Children {
- str += " " + child.String()
- }
- return str
+ return HeadingNode
}
type HorizontalRule struct {
@@ -85,14 +51,8 @@ type HorizontalRule struct {
Symbol string
}
-var NodeTypeHorizontalRule = NewNodeType("HorizontalRule")
-
func (*HorizontalRule) Type() NodeType {
- return NodeTypeHorizontalRule
-}
-
-func (n *HorizontalRule) String() string {
- return n.Type().String()
+ return HorizontalRuleNode
}
type Blockquote struct {
@@ -101,16 +61,6 @@ type Blockquote struct {
Children []Node
}
-var NodeTypeBlockquote = NewNodeType("Blockquote")
-
func (*Blockquote) Type() NodeType {
- return NodeTypeBlockquote
-}
-
-func (n *Blockquote) String() string {
- str := n.Type().String()
- for _, child := range n.Children {
- str += " " + child.String()
- }
- return str
+ return BlockquoteNode
}
diff --git a/plugin/gomark/ast/inline.go b/plugin/gomark/ast/inline.go
index 582368649..6c4eb9112 100644
--- a/plugin/gomark/ast/inline.go
+++ b/plugin/gomark/ast/inline.go
@@ -10,14 +10,8 @@ type Text struct {
Content string
}
-var NodeTypeText = NewNodeType("Text")
-
func (*Text) Type() NodeType {
- return NodeTypeText
-}
-
-func (n *Text) String() string {
- return n.Type().String() + " " + n.Content
+ return TextNode
}
type Bold struct {
@@ -28,14 +22,8 @@ type Bold struct {
Content string
}
-var NodeTypeBold = NewNodeType("Bold")
-
func (*Bold) Type() NodeType {
- return NodeTypeBold
-}
-
-func (n *Bold) String() string {
- return n.Type().String() + " " + n.Symbol + " " + n.Content
+ return BoldNode
}
type Italic struct {
@@ -46,14 +34,8 @@ type Italic struct {
Content string
}
-var NodeTypeItalic = NewNodeType("Italic")
-
func (*Italic) Type() NodeType {
- return NodeTypeItalic
-}
-
-func (n *Italic) String() string {
- return n.Type().String() + " " + n.Symbol + " " + n.Content
+ return ItalicNode
}
type BoldItalic struct {
@@ -64,14 +46,8 @@ type BoldItalic struct {
Content string
}
-var NodeTypeBoldItalic = NewNodeType("BoldItalic")
-
func (*BoldItalic) Type() NodeType {
- return NodeTypeBoldItalic
-}
-
-func (n *BoldItalic) String() string {
- return n.Type().String() + " " + n.Symbol + " " + n.Content
+ return BoldItalicNode
}
type Code struct {
@@ -80,14 +56,8 @@ type Code struct {
Content string
}
-var NodeTypeCode = NewNodeType("Code")
-
func (*Code) Type() NodeType {
- return NodeTypeCode
-}
-
-func (n *Code) String() string {
- return n.Type().String() + " " + n.Content
+ return CodeNode
}
type Image struct {
@@ -97,14 +67,8 @@ type Image struct {
URL string
}
-var NodeTypeImage = NewNodeType("Image")
-
func (*Image) Type() NodeType {
- return NodeTypeImage
-}
-
-func (n *Image) String() string {
- return n.Type().String() + " " + n.AltText + " " + n.URL
+ return ImageNode
}
type Link struct {
@@ -114,14 +78,8 @@ type Link struct {
URL string
}
-var NodeTypeLink = NewNodeType("Link")
-
func (*Link) Type() NodeType {
- return NodeTypeLink
-}
-
-func (n *Link) String() string {
- return n.Type().String() + " " + n.Text + " " + n.URL
+ return LinkNode
}
type Tag struct {
@@ -130,14 +88,8 @@ type Tag struct {
Content string
}
-var NodeTypeTag = NewNodeType("Tag")
-
func (*Tag) Type() NodeType {
- return NodeTypeTag
-}
-
-func (n *Tag) String() string {
- return n.Type().String() + " " + n.Content
+ return TagNode
}
type Strikethrough struct {
@@ -146,12 +98,6 @@ type Strikethrough struct {
Content string
}
-var NodeTypeStrikethrough = NewNodeType("Strikethrough")
-
func (*Strikethrough) Type() NodeType {
- return NodeTypeStrikethrough
-}
-
-func (n *Strikethrough) String() string {
- return n.Type().String() + " " + n.Content
+ return StrikethroughNode
}
diff --git a/plugin/gomark/ast/node.go b/plugin/gomark/ast/node.go
index 4a7acad77..6fbb23b58 100644
--- a/plugin/gomark/ast/node.go
+++ b/plugin/gomark/ast/node.go
@@ -1,18 +1,37 @@
package ast
+type NodeType uint32
+
+const (
+ UnknownNode NodeType = iota
+ // Block nodes.
+ LineBreakNode
+ ParagraphNode
+ CodeBlockNode
+ HeadingNode
+ HorizontalRuleNode
+ BlockquoteNode
+ // Inline nodes.
+ TextNode
+ BoldNode
+ ItalicNode
+ BoldItalicNode
+ CodeNode
+ ImageNode
+ LinkNode
+ TagNode
+ StrikethroughNode
+)
+
type Node interface {
// Type returns a node type.
Type() NodeType
- // String returns a string representation of this node.
- // This method is used for debugging.
- String() string
+ // PrevSibling returns a previous sibling node of this node.
+ PrevSibling() Node
- // GetPrevSibling returns a previous sibling node of this node.
- GetPrevSibling() Node
-
- // GetNextSibling returns a next sibling node of this node.
- GetNextSibling() Node
+ // NextSibling returns a next sibling node of this node.
+ NextSibling() Node
// SetPrevSibling sets a previous sibling node to this node.
SetPrevSibling(Node)
@@ -21,32 +40,17 @@ type Node interface {
SetNextSibling(Node)
}
-type NodeType int
-
-func (t NodeType) String() string {
- return nodeTypeNames[t]
-}
-
-var nodeTypeIndex NodeType
-var nodeTypeNames = []string{""}
-
-func NewNodeType(name string) NodeType {
- nodeTypeNames = append(nodeTypeNames, name)
- nodeTypeIndex++
- return nodeTypeIndex
-}
-
type BaseNode struct {
prevSibling Node
nextSibling Node
}
-func (n *BaseNode) GetPrevSibling() Node {
+func (n *BaseNode) PrevSibling() Node {
return n.prevSibling
}
-func (n *BaseNode) GetNextSibling() Node {
+func (n *BaseNode) NextSibling() Node {
return n.nextSibling
}
diff --git a/plugin/gomark/parser/parser.go b/plugin/gomark/parser/parser.go
index 9d48852c1..3f97ef0db 100644
--- a/plugin/gomark/parser/parser.go
+++ b/plugin/gomark/parser/parser.go
@@ -88,7 +88,7 @@ func ParseInline(tokens []*tokenizer.Token) ([]ast.Node, error) {
tokens = tokens[size:]
if prevNode != nil {
// Merge text nodes if possible.
- if prevNode.Type() == ast.NodeTypeText && node.Type() == ast.NodeTypeText {
+ if prevNode.Type() == ast.TextNode && node.Type() == ast.TextNode {
prevNode.(*ast.Text).Content += node.(*ast.Text).Content
break
}
diff --git a/plugin/gomark/parser/parser_test.go b/plugin/gomark/parser/parser_test.go
index 22cc771f8..6d9086076 100644
--- a/plugin/gomark/parser/parser_test.go
+++ b/plugin/gomark/parser/parser_test.go
@@ -127,8 +127,44 @@ func StringifyNodes(nodes []ast.Node) string {
var result string
for _, node := range nodes {
if node != nil {
- result += node.String()
+ result += StringifyNode(node)
}
}
return result
}
+
+func StringifyNode(node ast.Node) string {
+ switch n := node.(type) {
+ case *ast.LineBreak:
+ return "LineBreak()"
+ case *ast.CodeBlock:
+ return "CodeBlock(" + n.Language + ", " + n.Content + ")"
+ case *ast.Paragraph:
+ return "Paragraph(" + StringifyNodes(n.Children) + ")"
+ case *ast.Heading:
+ return "Heading(" + StringifyNodes(n.Children) + ")"
+ case *ast.HorizontalRule:
+ return "HorizontalRule(" + n.Symbol + ")"
+ case *ast.Blockquote:
+ return "Blockquote(" + StringifyNodes(n.Children) + ")"
+ case *ast.Text:
+ return "Text(" + n.Content + ")"
+ case *ast.Bold:
+ return "Bold(" + n.Symbol + n.Content + n.Symbol + ")"
+ case *ast.Italic:
+ return "Italic(" + n.Symbol + n.Content + n.Symbol + ")"
+ case *ast.BoldItalic:
+ return "BoldItalic(" + n.Symbol + n.Content + n.Symbol + ")"
+ case *ast.Code:
+ return "Code(" + n.Content + ")"
+ case *ast.Image:
+ return "Image(" + n.URL + ", " + n.AltText + ")"
+ case *ast.Link:
+ return "Link(" + n.Text + ", " + n.URL + ")"
+ case *ast.Tag:
+ return "Tag(" + n.Content + ")"
+ case *ast.Strikethrough:
+ return "Strikethrough(" + n.Content + ")"
+ }
+ return ""
+}
diff --git a/plugin/gomark/parser/tokenizer/tokenizer.go b/plugin/gomark/parser/tokenizer/tokenizer.go
index f27ecf03b..ce3730014 100644
--- a/plugin/gomark/parser/tokenizer/tokenizer.go
+++ b/plugin/gomark/parser/tokenizer/tokenizer.go
@@ -68,14 +68,14 @@ func Tokenize(text string) []*Token {
case ' ':
tokens = append(tokens, NewToken(Space, " "))
default:
- var lastToken *Token
+ var prevToken *Token
if len(tokens) > 0 {
- lastToken = tokens[len(tokens)-1]
+ prevToken = tokens[len(tokens)-1]
}
- if lastToken == nil || lastToken.Type != Text {
+ if prevToken == nil || prevToken.Type != Text {
tokens = append(tokens, NewToken(Text, string(c)))
} else {
- lastToken.Value += string(c)
+ prevToken.Value += string(c)
}
}
}
diff --git a/plugin/gomark/render/html/html.go b/plugin/gomark/render/html/html.go
new file mode 100644
index 000000000..aedbd222b
--- /dev/null
+++ b/plugin/gomark/render/html/html.go
@@ -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("
")
+}
+
+func (r *HTMLRender) renderParagraph(node *ast.Paragraph) {
+ r.output.WriteString("
") + r.RenderNodes(node.Children) + r.output.WriteString("
") +} + +func (r *HTMLRender) renderCodeBlock(node *ast.CodeBlock) { + r.output.WriteString("")
+ r.output.WriteString(node.Content)
+ r.output.WriteString("
")
+}
+
+func (r *HTMLRender) renderHeading(node *ast.Heading) {
+ element := fmt.Sprintf("") + } + r.RenderNodes(node.Children) + if nextSibling == nil || nextSibling.Type() != ast.BlockquoteNode { + r.output.WriteString("") + } +} + +func (r *HTMLRender) renderText(node *ast.Text) { + r.output.WriteString(node.Content) +} + +func (r *HTMLRender) renderBold(node *ast.Bold) { + r.output.WriteString("") + r.output.WriteString(node.Content) + r.output.WriteString("") +} + +func (r *HTMLRender) renderItalic(node *ast.Italic) { + r.output.WriteString("") + r.output.WriteString(node.Content) + r.output.WriteString("") +} + +func (r *HTMLRender) renderBoldItalic(node *ast.BoldItalic) { + r.output.WriteString("") + r.output.WriteString(node.Content) + r.output.WriteString("") +} + +func (r *HTMLRender) renderCode(node *ast.Code) { + r.output.WriteString("
")
+ r.output.WriteString(node.Content)
+ r.output.WriteString("
")
+}
+
+func (r *HTMLRender) renderImage(node *ast.Image) {
+ r.output.WriteString(`") - r.RenderNodes(n.Children) - r.output.WriteString("
") - case *ast.CodeBlock: - r.output.WriteString("")
- r.output.WriteString(n.Content)
- r.output.WriteString("
")
- case *ast.Heading:
- r.output.WriteString(fmt.Sprintf("") - } - r.RenderNodes(n.Children) - if nextSibling != nil && nextSibling.Type() == ast.NodeTypeBlockquote { - r.RenderNode(nextSibling) - } - if prevSibling == nil || prevSibling.Type() != ast.NodeTypeBlockquote { - r.output.WriteString("") - } - case *ast.BoldItalic: - r.output.WriteString("") - r.output.WriteString(n.Content) - r.output.WriteString("") - case *ast.Bold: - r.output.WriteString("") - r.output.WriteString(n.Content) - r.output.WriteString("") - case *ast.Italic: - r.output.WriteString("") - r.output.WriteString(n.Content) - r.output.WriteString("") - case *ast.Code: - r.output.WriteString("
")
- r.output.WriteString(n.Content)
- r.output.WriteString("
")
- case *ast.Link:
- r.output.WriteString(``)
- r.output.WriteString(n.Text)
- r.output.WriteString("")
- case *ast.Image:
- r.output.WriteString(`