chore: implement escaping character node

This commit is contained in:
Steven 2023-12-16 11:47:29 +08:00
parent 1237643028
commit e43a445c34
10 changed files with 132 additions and 0 deletions

View file

@ -101,3 +101,13 @@ type Strikethrough struct {
func (*Strikethrough) Type() NodeType {
return StrikethroughNode
}
type EscapingCharacter struct {
BaseInline
Symbol string
}
func (*EscapingCharacter) Type() NodeType {
return EscapingCharacterNode
}

View file

@ -23,6 +23,7 @@ const (
LinkNode
TagNode
StrikethroughNode
EscapingCharacterNode
)
type Node interface {

View file

@ -0,0 +1,41 @@
package parser
import (
"errors"
"github.com/usememos/memos/plugin/gomark/ast"
"github.com/usememos/memos/plugin/gomark/parser/tokenizer"
)
type EscapingCharacterParser struct{}
func NewEscapingCharacterParser() *EscapingCharacterParser {
return &EscapingCharacterParser{}
}
func (*EscapingCharacterParser) Match(tokens []*tokenizer.Token) (int, bool) {
if len(tokens) == 0 {
return 0, false
}
if tokens[0].Type != tokenizer.Backslash {
return 0, false
}
if len(tokens) == 1 {
return 0, false
}
if tokens[1].Type == tokenizer.Newline || tokens[1].Type == tokenizer.Space || tokens[1].Type == tokenizer.Text || tokens[1].Type == tokenizer.Number {
return 0, false
}
return 2, true
}
func (p *EscapingCharacterParser) Parse(tokens []*tokenizer.Token) (ast.Node, error) {
size, ok := p.Match(tokens)
if size == 0 || !ok {
return nil, errors.New("not matched")
}
return &ast.EscapingCharacter{
Symbol: tokens[1].Value,
}, nil
}

View file

@ -0,0 +1,30 @@
package parser
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/usememos/memos/plugin/gomark/ast"
"github.com/usememos/memos/plugin/gomark/parser/tokenizer"
)
func TestEscapingCharacterParser(t *testing.T) {
tests := []struct {
text string
node ast.Node
}{
{
text: `\# 123`,
node: &ast.EscapingCharacter{
Symbol: "#",
},
},
}
for _, test := range tests {
tokens := tokenizer.Tokenize(test.text)
node, _ := NewEscapingCharacterParser().Parse(tokens)
require.Equal(t, StringifyNodes([]ast.Node{test.node}), StringifyNodes([]ast.Node{node}))
}
}

View file

@ -71,6 +71,7 @@ func ParseBlockWithParsers(tokens []*tokenizer.Token, blockParsers []BlockParser
}
var defaultInlineParsers = []InlineParser{
NewEscapingCharacterParser(),
NewBoldItalicParser(),
NewImageParser(),
NewLinkParser(),

View file

@ -26,6 +26,34 @@ func TestParser(t *testing.T) {
},
},
},
{
text: "# Hello world!",
nodes: []ast.Node{
&ast.Heading{
Level: 1,
Children: []ast.Node{
&ast.Text{
Content: "Hello world!",
},
},
},
},
},
{
text: "\\# Hello world!",
nodes: []ast.Node{
&ast.Paragraph{
Children: []ast.Node{
&ast.EscapingCharacter{
Symbol: "#",
},
&ast.Text{
Content: " Hello world!",
},
},
},
},
},
{
text: "**Hello** world!",
nodes: []ast.Node{

View file

@ -17,6 +17,7 @@ const (
PlusSign TokenType = "+"
Dot TokenType = "."
GreaterThan TokenType = ">"
Backslash TokenType = "\\"
Newline TokenType = "\n"
Space TokenType = " "
)
@ -70,6 +71,8 @@ func Tokenize(text string) []*Token {
tokens = append(tokens, NewToken(PlusSign, "+"))
case '.':
tokens = append(tokens, NewToken(Dot, "."))
case '\\':
tokens = append(tokens, NewToken(Backslash, `\`))
case '\n':
tokens = append(tokens, NewToken(Newline, "\n"))
case ' ':

View file

@ -59,6 +59,8 @@ func (r *HTMLRender) RenderNode(node ast.Node) {
r.renderTag(n)
case *ast.Strikethrough:
r.renderStrikethrough(n)
case *ast.EscapingCharacter:
r.renderEscapingCharacter(n)
case *ast.Text:
r.renderText(n)
default:
@ -199,3 +201,8 @@ func (r *HTMLRender) renderStrikethrough(node *ast.Strikethrough) {
r.output.WriteString(node.Content)
r.output.WriteString(`</del>`)
}
func (r *HTMLRender) renderEscapingCharacter(node *ast.EscapingCharacter) {
r.output.WriteString("\\")
r.output.WriteString(node.Symbol)
}

View file

@ -42,6 +42,10 @@ func TestHTMLRender(t *testing.T) {
text: "#article #memo",
expected: `<p><span>#article</span> <span>#memo</span></p>`,
},
{
text: "#article \\#memo",
expected: `<p><span>#article</span> \#memo</p>`,
},
{
text: "* Hello\n* world!",
expected: `<ul><li>Hello</li><li>world!</li></ul>`,

View file

@ -59,6 +59,8 @@ func (r *StringRender) RenderNode(node ast.Node) {
r.renderTag(n)
case *ast.Strikethrough:
r.renderStrikethrough(n)
case *ast.EscapingCharacter:
r.renderEscapingCharacter(n)
case *ast.Text:
r.renderText(n)
default:
@ -157,3 +159,8 @@ func (r *StringRender) renderTag(node *ast.Tag) {
func (r *StringRender) renderStrikethrough(node *ast.Strikethrough) {
r.output.WriteString(node.Content)
}
func (r *StringRender) renderEscapingCharacter(node *ast.EscapingCharacter) {
r.output.WriteString("\\")
r.output.WriteString(node.Symbol)
}