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("", node.Level) + r.output.WriteString(fmt.Sprintf("<%s>", element)) + r.RenderNodes(node.Children) + r.output.WriteString(fmt.Sprintf("", element)) +} + +func (r *HTMLRender) renderHorizontalRule(_ *ast.HorizontalRule) { + r.output.WriteString("
") +} + +func (r *HTMLRender) renderBlockquote(node *ast.Blockquote) { + prevSibling, nextSibling := node.PrevSibling(), node.NextSibling() + if prevSibling == nil || prevSibling.Type() != ast.BlockquoteNode { + r.output.WriteString("
") + } + 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.output.WriteString(node.AltText)
+	r.output.WriteString(``) +} + +func (r *HTMLRender) renderLink(node *ast.Link) { + r.output.WriteString(``) + r.output.WriteString(node.Text) + r.output.WriteString("") +} + +func (r *HTMLRender) renderTag(node *ast.Tag) { + r.output.WriteString(``) + r.output.WriteString(`# `) + r.output.WriteString(node.Content) + r.output.WriteString(``) +} + +func (r *HTMLRender) renderStrikethrough(node *ast.Strikethrough) { + r.output.WriteString(``) + r.output.WriteString(node.Content) + r.output.WriteString(``) +} diff --git a/plugin/gomark/renderer/html/html_test.go b/plugin/gomark/render/html/html_test.go similarity index 90% rename from plugin/gomark/renderer/html/html_test.go rename to plugin/gomark/render/html/html_test.go index 30b78ee04..9c22097ac 100644 --- a/plugin/gomark/renderer/html/html_test.go +++ b/plugin/gomark/render/html/html_test.go @@ -9,7 +9,7 @@ import ( "github.com/usememos/memos/plugin/gomark/parser/tokenizer" ) -func TestHTMLRenderer(t *testing.T) { +func TestHTMLRender(t *testing.T) { tests := []struct { text string expected string @@ -36,7 +36,7 @@ func TestHTMLRenderer(t *testing.T) { tokens := tokenizer.Tokenize(test.text) nodes, err := parser.Parse(tokens) require.NoError(t, err) - actual := NewHTMLRenderer().Render(nodes) + actual := NewHTMLRender().Render(nodes) require.Equal(t, test.expected, actual) } } diff --git a/plugin/gomark/render/renderer.go b/plugin/gomark/render/renderer.go new file mode 100644 index 000000000..da4cad77d --- /dev/null +++ b/plugin/gomark/render/renderer.go @@ -0,0 +1 @@ +package render diff --git a/plugin/gomark/renderer/html/html.go b/plugin/gomark/renderer/html/html.go deleted file mode 100644 index ce84207cd..000000000 --- a/plugin/gomark/renderer/html/html.go +++ /dev/null @@ -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("
") - case *ast.Paragraph: - 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("", n.Level)) - r.RenderNodes(n.Children) - r.output.WriteString(fmt.Sprintf("", n.Level)) - case *ast.HorizontalRule: - r.output.WriteString("
") - case *ast.Blockquote: - if prevSibling == nil || prevSibling.Type() != ast.NodeTypeBlockquote { - r.output.WriteString("
") - } - 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(``)
-		r.output.WriteString(n.AltText)
-		r.output.WriteString(``) - case *ast.Tag: - r.output.WriteString(``) - r.output.WriteString(`# `) - r.output.WriteString(n.Content) - r.output.WriteString(``) - case *ast.Strikethrough: - r.output.WriteString(``) - r.output.WriteString(n.Content) - r.output.WriteString(``) - 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() -} diff --git a/plugin/gomark/renderer/renderer.go b/plugin/gomark/renderer/renderer.go deleted file mode 100644 index 35803f645..000000000 --- a/plugin/gomark/renderer/renderer.go +++ /dev/null @@ -1 +0,0 @@ -package renderer