mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
chore: update inline parser
This commit is contained in:
@ -18,8 +18,8 @@ type Bold struct {
|
|||||||
BaseInline
|
BaseInline
|
||||||
|
|
||||||
// Symbol is "*" or "_".
|
// Symbol is "*" or "_".
|
||||||
Symbol string
|
Symbol string
|
||||||
Content string
|
Children []Node
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Bold) Type() NodeType {
|
func (*Bold) Type() NodeType {
|
||||||
|
@ -53,8 +53,12 @@ func (p *BoldParser) Parse(tokens []*tokenizer.Token) (ast.Node, error) {
|
|||||||
|
|
||||||
prefixTokenType := tokens[0].Type
|
prefixTokenType := tokens[0].Type
|
||||||
contentTokens := tokens[2 : size-2]
|
contentTokens := tokens[2 : size-2]
|
||||||
|
children, err := ParseInlineWithParsers(contentTokens, []InlineParser{NewLinkParser(), NewTextParser()})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return &ast.Bold{
|
return &ast.Bold{
|
||||||
Symbol: prefixTokenType,
|
Symbol: prefixTokenType,
|
||||||
Content: tokenizer.Stringify(contentTokens),
|
Children: children,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -21,15 +21,23 @@ func TestBoldParser(t *testing.T) {
|
|||||||
{
|
{
|
||||||
text: "**Hello**",
|
text: "**Hello**",
|
||||||
bold: &ast.Bold{
|
bold: &ast.Bold{
|
||||||
Symbol: "*",
|
Symbol: "*",
|
||||||
Content: "Hello",
|
Children: []ast.Node{
|
||||||
|
&ast.Text{
|
||||||
|
Content: "Hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "** Hello **",
|
text: "** Hello **",
|
||||||
bold: &ast.Bold{
|
bold: &ast.Bold{
|
||||||
Symbol: "*",
|
Symbol: "*",
|
||||||
Content: " Hello ",
|
Children: []ast.Node{
|
||||||
|
&ast.Text{
|
||||||
|
Content: " Hello ",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -63,8 +63,12 @@ Hello World`,
|
|||||||
Level: 3,
|
Level: 3,
|
||||||
Children: []ast.Node{
|
Children: []ast.Node{
|
||||||
&ast.Bold{
|
&ast.Bold{
|
||||||
Symbol: "*",
|
Symbol: "*",
|
||||||
Content: "Hello",
|
Children: []ast.Node{
|
||||||
|
&ast.Text{
|
||||||
|
Content: "Hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&ast.Text{
|
&ast.Text{
|
||||||
Content: " World",
|
Content: " World",
|
||||||
|
@ -29,6 +29,13 @@ func TestLinkParser(t *testing.T) {
|
|||||||
text: "[alte]( htt ps :/ /example.com)",
|
text: "[alte]( htt ps :/ /example.com)",
|
||||||
link: nil,
|
link: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: "[your/slash](https://example.com)",
|
||||||
|
link: &ast.Link{
|
||||||
|
Text: "your/slash",
|
||||||
|
URL: "https://example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: "[hello world](https://example.com)",
|
text: "[hello world](https://example.com)",
|
||||||
link: &ast.Link{
|
link: &ast.Link{
|
||||||
|
@ -76,10 +76,14 @@ var defaultInlineParsers = []InlineParser{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ParseInline(tokens []*tokenizer.Token) ([]ast.Node, error) {
|
func ParseInline(tokens []*tokenizer.Token) ([]ast.Node, error) {
|
||||||
|
return ParseInlineWithParsers(tokens, defaultInlineParsers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInlineWithParsers(tokens []*tokenizer.Token, inlineParsers []InlineParser) ([]ast.Node, error) {
|
||||||
nodes := []ast.Node{}
|
nodes := []ast.Node{}
|
||||||
var prevNode ast.Node
|
var prevNode ast.Node
|
||||||
for len(tokens) > 0 {
|
for len(tokens) > 0 {
|
||||||
for _, inlineParser := range defaultInlineParsers {
|
for _, inlineParser := range inlineParsers {
|
||||||
size, matched := inlineParser.Match(tokens)
|
size, matched := inlineParser.Match(tokens)
|
||||||
if matched {
|
if matched {
|
||||||
node, err := inlineParser.Parse(tokens)
|
node, err := inlineParser.Parse(tokens)
|
||||||
|
@ -32,8 +32,12 @@ func TestParser(t *testing.T) {
|
|||||||
&ast.Paragraph{
|
&ast.Paragraph{
|
||||||
Children: []ast.Node{
|
Children: []ast.Node{
|
||||||
&ast.Bold{
|
&ast.Bold{
|
||||||
Symbol: "*",
|
Symbol: "*",
|
||||||
Content: "Hello",
|
Children: []ast.Node{
|
||||||
|
&ast.Text{
|
||||||
|
Content: "Hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&ast.Text{
|
&ast.Text{
|
||||||
Content: " world!",
|
Content: " world!",
|
||||||
@ -51,8 +55,12 @@ func TestParser(t *testing.T) {
|
|||||||
Content: "Hello ",
|
Content: "Hello ",
|
||||||
},
|
},
|
||||||
&ast.Bold{
|
&ast.Bold{
|
||||||
Symbol: "*",
|
Symbol: "*",
|
||||||
Content: "world",
|
Children: []ast.Node{
|
||||||
|
&ast.Text{
|
||||||
|
Content: "world",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&ast.Text{
|
&ast.Text{
|
||||||
Content: "!",
|
Content: "!",
|
||||||
@ -78,8 +86,12 @@ func TestParser(t *testing.T) {
|
|||||||
Content: "Hello ",
|
Content: "Hello ",
|
||||||
},
|
},
|
||||||
&ast.Bold{
|
&ast.Bold{
|
||||||
Symbol: "*",
|
Symbol: "*",
|
||||||
Content: "world",
|
Children: []ast.Node{
|
||||||
|
&ast.Text{
|
||||||
|
Content: "world",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
&ast.Text{
|
&ast.Text{
|
||||||
Content: "!",
|
Content: "!",
|
||||||
@ -150,7 +162,7 @@ func StringifyNode(node ast.Node) string {
|
|||||||
case *ast.Text:
|
case *ast.Text:
|
||||||
return "Text(" + n.Content + ")"
|
return "Text(" + n.Content + ")"
|
||||||
case *ast.Bold:
|
case *ast.Bold:
|
||||||
return "Bold(" + n.Symbol + n.Content + n.Symbol + ")"
|
return "Bold(" + n.Symbol + StringifyNodes(n.Children) + n.Symbol + ")"
|
||||||
case *ast.Italic:
|
case *ast.Italic:
|
||||||
return "Italic(" + n.Symbol + n.Content + n.Symbol + ")"
|
return "Italic(" + n.Symbol + n.Content + n.Symbol + ")"
|
||||||
case *ast.BoldItalic:
|
case *ast.BoldItalic:
|
||||||
|
@ -35,8 +35,12 @@ func TestUnorderedListParser(t *testing.T) {
|
|||||||
Symbol: tokenizer.Asterisk,
|
Symbol: tokenizer.Asterisk,
|
||||||
Children: []ast.Node{
|
Children: []ast.Node{
|
||||||
&ast.Bold{
|
&ast.Bold{
|
||||||
Symbol: "*",
|
Symbol: "*",
|
||||||
Content: "Hello",
|
Children: []ast.Node{
|
||||||
|
&ast.Text{
|
||||||
|
Content: "Hello",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -149,7 +149,7 @@ func (r *HTMLRender) renderText(node *ast.Text) {
|
|||||||
|
|
||||||
func (r *HTMLRender) renderBold(node *ast.Bold) {
|
func (r *HTMLRender) renderBold(node *ast.Bold) {
|
||||||
r.output.WriteString("<strong>")
|
r.output.WriteString("<strong>")
|
||||||
r.output.WriteString(node.Content)
|
r.RenderNodes(node.Children)
|
||||||
r.output.WriteString("</strong>")
|
r.output.WriteString("</strong>")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ func (r *StringRender) renderText(node *ast.Text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *StringRender) renderBold(node *ast.Bold) {
|
func (r *StringRender) renderBold(node *ast.Bold) {
|
||||||
r.output.WriteString(node.Content)
|
r.RenderNodes(node.Children)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *StringRender) renderItalic(node *ast.Italic) {
|
func (r *StringRender) renderItalic(node *ast.Italic) {
|
||||||
@ -151,12 +151,12 @@ func (r *StringRender) renderCode(node *ast.Code) {
|
|||||||
r.output.WriteString("`")
|
r.output.WriteString("`")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*StringRender) renderImage(*ast.Image) {
|
func (r *StringRender) renderImage(node *ast.Image) {
|
||||||
// Do nothing.
|
r.output.WriteString(node.AltText)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*StringRender) renderLink(*ast.Link) {
|
func (r *StringRender) renderLink(node *ast.Link) {
|
||||||
// Do nothing.
|
r.output.WriteString(node.Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *StringRender) renderTag(node *ast.Tag) {
|
func (r *StringRender) renderTag(node *ast.Tag) {
|
||||||
|
@ -22,6 +22,10 @@ func TestStringRender(t *testing.T) {
|
|||||||
text: "**Hello** world!",
|
text: "**Hello** world!",
|
||||||
expected: `Hello world!`,
|
expected: `Hello world!`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: "**[your/slash](https://example.com)** world!",
|
||||||
|
expected: `your/slash world!`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
INSERT INTO
|
|
||||||
user (
|
|
||||||
`id`,
|
|
||||||
`username`,
|
|
||||||
`role`,
|
|
||||||
`email`,
|
|
||||||
`nickname`,
|
|
||||||
`row_status`,
|
|
||||||
`avatar_url`,
|
|
||||||
`password_hash`
|
|
||||||
)
|
|
||||||
VALUES
|
|
||||||
(
|
|
||||||
101,
|
|
||||||
'memos-demo',
|
|
||||||
'HOST',
|
|
||||||
'demo@usememos.com',
|
|
||||||
'Derobot',
|
|
||||||
'NORMAL',
|
|
||||||
'',
|
|
||||||
-- raw password: secret
|
|
||||||
'$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK'
|
|
||||||
),
|
|
||||||
(
|
|
||||||
102,
|
|
||||||
'jack',
|
|
||||||
'USER',
|
|
||||||
'jack@usememos.com',
|
|
||||||
'Jack',
|
|
||||||
'NORMAL',
|
|
||||||
'',
|
|
||||||
-- raw password: secret
|
|
||||||
'$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK'
|
|
||||||
),
|
|
||||||
(
|
|
||||||
103,
|
|
||||||
'bob',
|
|
||||||
'USER',
|
|
||||||
'bob@usememos.com',
|
|
||||||
'Bob',
|
|
||||||
'ARCHIVED',
|
|
||||||
'',
|
|
||||||
-- raw password: secret
|
|
||||||
'$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK'
|
|
||||||
);
|
|
@ -1,51 +0,0 @@
|
|||||||
INSERT INTO
|
|
||||||
memo (`id`, `content`, `creator_id`)
|
|
||||||
VALUES
|
|
||||||
(
|
|
||||||
1,
|
|
||||||
"#Hello 👋 Welcome to memos.",
|
|
||||||
101
|
|
||||||
);
|
|
||||||
|
|
||||||
INSERT INTO
|
|
||||||
memo (
|
|
||||||
`id`,
|
|
||||||
`content`,
|
|
||||||
`creator_id`,
|
|
||||||
`visibility`
|
|
||||||
)
|
|
||||||
VALUES
|
|
||||||
(
|
|
||||||
2,
|
|
||||||
'#TODO
|
|
||||||
- [x] Take more photos about **🌄 sunset**
|
|
||||||
- [x] Clean the room
|
|
||||||
- [ ] Read *📖 The Little Prince*
|
|
||||||
(👆 click to toggle status)',
|
|
||||||
101,
|
|
||||||
'PROTECTED'
|
|
||||||
),
|
|
||||||
(
|
|
||||||
3,
|
|
||||||
"**[Slash](https://github.com/yourselfhosted/slash)**: A bookmarking and url shortener, save and share your links very easily.
|
|
||||||
**[SQL Chat](https://www.sqlchat.ai)**: Chat-based SQL Client",
|
|
||||||
101,
|
|
||||||
'PUBLIC'
|
|
||||||
),
|
|
||||||
(
|
|
||||||
4,
|
|
||||||
'#TODO
|
|
||||||
- [x] Take more photos about **🌄 sunset**
|
|
||||||
- [ ] Clean the classroom
|
|
||||||
- [ ] Watch *👦 The Boys*
|
|
||||||
(👆 click to toggle status)
|
|
||||||
',
|
|
||||||
102,
|
|
||||||
'PROTECTED'
|
|
||||||
),
|
|
||||||
(
|
|
||||||
5,
|
|
||||||
'三人行,必有我师焉!👨🏫',
|
|
||||||
102,
|
|
||||||
'PUBLIC'
|
|
||||||
);
|
|
@ -1,5 +0,0 @@
|
|||||||
INSERT INTO
|
|
||||||
memo_organizer (`memo_id`, `user_id`, `pinned`)
|
|
||||||
VALUES
|
|
||||||
(1, 101, 1),
|
|
||||||
(3, 101, 1);
|
|
@ -1,6 +0,0 @@
|
|||||||
INSERT INTO
|
|
||||||
tag (`name`, `creator_id`)
|
|
||||||
VALUES
|
|
||||||
('Hello', 101),
|
|
||||||
('TODO', 101),
|
|
||||||
('TODO', 102);
|
|
@ -1,44 +0,0 @@
|
|||||||
INSERT INTO "user" (
|
|
||||||
id,
|
|
||||||
username,
|
|
||||||
role,
|
|
||||||
email,
|
|
||||||
nickname,
|
|
||||||
row_status,
|
|
||||||
avatar_url,
|
|
||||||
password_hash
|
|
||||||
)
|
|
||||||
VALUES
|
|
||||||
(
|
|
||||||
101,
|
|
||||||
'memos-demo',
|
|
||||||
'HOST',
|
|
||||||
'demo@usememos.com',
|
|
||||||
'Derobot',
|
|
||||||
'NORMAL',
|
|
||||||
'',
|
|
||||||
-- raw password: secret
|
|
||||||
'$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK'
|
|
||||||
),
|
|
||||||
(
|
|
||||||
102,
|
|
||||||
'jack',
|
|
||||||
'USER',
|
|
||||||
'jack@usememos.com',
|
|
||||||
'Jack',
|
|
||||||
'NORMAL',
|
|
||||||
'',
|
|
||||||
-- raw password: secret
|
|
||||||
'$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK'
|
|
||||||
),
|
|
||||||
(
|
|
||||||
103,
|
|
||||||
'bob',
|
|
||||||
'USER',
|
|
||||||
'bob@usememos.com',
|
|
||||||
'Bob',
|
|
||||||
'ARCHIVED',
|
|
||||||
'',
|
|
||||||
-- raw password: secret
|
|
||||||
'$2a$14$ajq8Q7fbtFRQvXpdCq7Jcuy.Rx1h/L4J60Otx.gyNLbAYctGMJ9tK'
|
|
||||||
);
|
|
@ -1,34 +0,0 @@
|
|||||||
INSERT INTO memo (id, content, creator_id)
|
|
||||||
VALUES
|
|
||||||
(
|
|
||||||
1,
|
|
||||||
'#Hello 👋 Welcome to memos.',
|
|
||||||
101
|
|
||||||
);
|
|
||||||
|
|
||||||
INSERT INTO memo (id, content, creator_id, visibility)
|
|
||||||
VALUES
|
|
||||||
(
|
|
||||||
2,
|
|
||||||
E'#TODO\n- [x] Take more photos about **🌄 sunset**\n- [x] Clean the room\n- [ ] Read *📖 The Little Prince*\n(👆 click to toggle status)',
|
|
||||||
101,
|
|
||||||
'PROTECTED'
|
|
||||||
),
|
|
||||||
(
|
|
||||||
3,
|
|
||||||
E'**[Slash](https://github.com/yourselfhosted/slash)**: A bookmarking and url shortener, save and share your links very easily.\n**[SQL Chat](https://www.sqlchat.ai)**: Chat-based SQL Client',
|
|
||||||
101,
|
|
||||||
'PUBLIC'
|
|
||||||
),
|
|
||||||
(
|
|
||||||
4,
|
|
||||||
E'#TODO\n- [x] Take more photos about **🌄 sunset**\n- [ ] Clean the classroom\n- [ ] Watch *👦 The Boys*\n(👆 click to toggle status)',
|
|
||||||
102,
|
|
||||||
'PROTECTED'
|
|
||||||
),
|
|
||||||
(
|
|
||||||
5,
|
|
||||||
'三人行,必有我师焉!👨🏫',
|
|
||||||
102,
|
|
||||||
'PUBLIC'
|
|
||||||
);
|
|
@ -1,5 +0,0 @@
|
|||||||
INSERT INTO
|
|
||||||
memo_organizer (memo_id, user_id, pinned)
|
|
||||||
VALUES
|
|
||||||
(1, 101, 1),
|
|
||||||
(3, 101, 1);
|
|
@ -1,6 +0,0 @@
|
|||||||
INSERT INTO
|
|
||||||
tag (name, creator_id)
|
|
||||||
VALUES
|
|
||||||
('Hello', 101),
|
|
||||||
('TODO', 101),
|
|
||||||
('TODO', 102);
|
|
Reference in New Issue
Block a user