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
 | 
			
		||||
 | 
			
		||||
	// Symbol is "*" or "_".
 | 
			
		||||
	Symbol  string
 | 
			
		||||
	Content string
 | 
			
		||||
	Symbol   string
 | 
			
		||||
	Children []Node
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*Bold) Type() NodeType {
 | 
			
		||||
 
 | 
			
		||||
@@ -53,8 +53,12 @@ func (p *BoldParser) Parse(tokens []*tokenizer.Token) (ast.Node, error) {
 | 
			
		||||
 | 
			
		||||
	prefixTokenType := tokens[0].Type
 | 
			
		||||
	contentTokens := tokens[2 : size-2]
 | 
			
		||||
	children, err := ParseInlineWithParsers(contentTokens, []InlineParser{NewLinkParser(), NewTextParser()})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &ast.Bold{
 | 
			
		||||
		Symbol:  prefixTokenType,
 | 
			
		||||
		Content: tokenizer.Stringify(contentTokens),
 | 
			
		||||
		Symbol:   prefixTokenType,
 | 
			
		||||
		Children: children,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,15 +21,23 @@ func TestBoldParser(t *testing.T) {
 | 
			
		||||
		{
 | 
			
		||||
			text: "**Hello**",
 | 
			
		||||
			bold: &ast.Bold{
 | 
			
		||||
				Symbol:  "*",
 | 
			
		||||
				Content: "Hello",
 | 
			
		||||
				Symbol: "*",
 | 
			
		||||
				Children: []ast.Node{
 | 
			
		||||
					&ast.Text{
 | 
			
		||||
						Content: "Hello",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			text: "** Hello **",
 | 
			
		||||
			bold: &ast.Bold{
 | 
			
		||||
				Symbol:  "*",
 | 
			
		||||
				Content: " Hello ",
 | 
			
		||||
				Symbol: "*",
 | 
			
		||||
				Children: []ast.Node{
 | 
			
		||||
					&ast.Text{
 | 
			
		||||
						Content: " Hello ",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
 
 | 
			
		||||
@@ -63,8 +63,12 @@ Hello World`,
 | 
			
		||||
				Level: 3,
 | 
			
		||||
				Children: []ast.Node{
 | 
			
		||||
					&ast.Bold{
 | 
			
		||||
						Symbol:  "*",
 | 
			
		||||
						Content: "Hello",
 | 
			
		||||
						Symbol: "*",
 | 
			
		||||
						Children: []ast.Node{
 | 
			
		||||
							&ast.Text{
 | 
			
		||||
								Content: "Hello",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					&ast.Text{
 | 
			
		||||
						Content: " World",
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,13 @@ func TestLinkParser(t *testing.T) {
 | 
			
		||||
			text: "[alte]( htt ps :/ /example.com)",
 | 
			
		||||
			link: nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			text: "[your/slash](https://example.com)",
 | 
			
		||||
			link: &ast.Link{
 | 
			
		||||
				Text: "your/slash",
 | 
			
		||||
				URL:  "https://example.com",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			text: "[hello world](https://example.com)",
 | 
			
		||||
			link: &ast.Link{
 | 
			
		||||
 
 | 
			
		||||
@@ -76,10 +76,14 @@ var defaultInlineParsers = []InlineParser{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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{}
 | 
			
		||||
	var prevNode ast.Node
 | 
			
		||||
	for len(tokens) > 0 {
 | 
			
		||||
		for _, inlineParser := range defaultInlineParsers {
 | 
			
		||||
		for _, inlineParser := range inlineParsers {
 | 
			
		||||
			size, matched := inlineParser.Match(tokens)
 | 
			
		||||
			if matched {
 | 
			
		||||
				node, err := inlineParser.Parse(tokens)
 | 
			
		||||
 
 | 
			
		||||
@@ -32,8 +32,12 @@ func TestParser(t *testing.T) {
 | 
			
		||||
				&ast.Paragraph{
 | 
			
		||||
					Children: []ast.Node{
 | 
			
		||||
						&ast.Bold{
 | 
			
		||||
							Symbol:  "*",
 | 
			
		||||
							Content: "Hello",
 | 
			
		||||
							Symbol: "*",
 | 
			
		||||
							Children: []ast.Node{
 | 
			
		||||
								&ast.Text{
 | 
			
		||||
									Content: "Hello",
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						&ast.Text{
 | 
			
		||||
							Content: " world!",
 | 
			
		||||
@@ -51,8 +55,12 @@ func TestParser(t *testing.T) {
 | 
			
		||||
							Content: "Hello ",
 | 
			
		||||
						},
 | 
			
		||||
						&ast.Bold{
 | 
			
		||||
							Symbol:  "*",
 | 
			
		||||
							Content: "world",
 | 
			
		||||
							Symbol: "*",
 | 
			
		||||
							Children: []ast.Node{
 | 
			
		||||
								&ast.Text{
 | 
			
		||||
									Content: "world",
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						&ast.Text{
 | 
			
		||||
							Content: "!",
 | 
			
		||||
@@ -78,8 +86,12 @@ func TestParser(t *testing.T) {
 | 
			
		||||
							Content: "Hello ",
 | 
			
		||||
						},
 | 
			
		||||
						&ast.Bold{
 | 
			
		||||
							Symbol:  "*",
 | 
			
		||||
							Content: "world",
 | 
			
		||||
							Symbol: "*",
 | 
			
		||||
							Children: []ast.Node{
 | 
			
		||||
								&ast.Text{
 | 
			
		||||
									Content: "world",
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
						&ast.Text{
 | 
			
		||||
							Content: "!",
 | 
			
		||||
@@ -150,7 +162,7 @@ func StringifyNode(node ast.Node) string {
 | 
			
		||||
	case *ast.Text:
 | 
			
		||||
		return "Text(" + n.Content + ")"
 | 
			
		||||
	case *ast.Bold:
 | 
			
		||||
		return "Bold(" + n.Symbol + n.Content + n.Symbol + ")"
 | 
			
		||||
		return "Bold(" + n.Symbol + StringifyNodes(n.Children) + n.Symbol + ")"
 | 
			
		||||
	case *ast.Italic:
 | 
			
		||||
		return "Italic(" + n.Symbol + n.Content + n.Symbol + ")"
 | 
			
		||||
	case *ast.BoldItalic:
 | 
			
		||||
 
 | 
			
		||||
@@ -35,8 +35,12 @@ func TestUnorderedListParser(t *testing.T) {
 | 
			
		||||
				Symbol: tokenizer.Asterisk,
 | 
			
		||||
				Children: []ast.Node{
 | 
			
		||||
					&ast.Bold{
 | 
			
		||||
						Symbol:  "*",
 | 
			
		||||
						Content: "Hello",
 | 
			
		||||
						Symbol: "*",
 | 
			
		||||
						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) {
 | 
			
		||||
	r.output.WriteString("<strong>")
 | 
			
		||||
	r.output.WriteString(node.Content)
 | 
			
		||||
	r.RenderNodes(node.Children)
 | 
			
		||||
	r.output.WriteString("</strong>")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -134,7 +134,7 @@ func (r *StringRender) renderText(node *ast.Text) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *StringRender) renderBold(node *ast.Bold) {
 | 
			
		||||
	r.output.WriteString(node.Content)
 | 
			
		||||
	r.RenderNodes(node.Children)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *StringRender) renderItalic(node *ast.Italic) {
 | 
			
		||||
@@ -151,12 +151,12 @@ func (r *StringRender) renderCode(node *ast.Code) {
 | 
			
		||||
	r.output.WriteString("`")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*StringRender) renderImage(*ast.Image) {
 | 
			
		||||
	// Do nothing.
 | 
			
		||||
func (r *StringRender) renderImage(node *ast.Image) {
 | 
			
		||||
	r.output.WriteString(node.AltText)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*StringRender) renderLink(*ast.Link) {
 | 
			
		||||
	// Do nothing.
 | 
			
		||||
func (r *StringRender) renderLink(node *ast.Link) {
 | 
			
		||||
	r.output.WriteString(node.Text)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *StringRender) renderTag(node *ast.Tag) {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,10 @@ func TestStringRender(t *testing.T) {
 | 
			
		||||
			text:     "**Hello** world!",
 | 
			
		||||
			expected: `Hello world!`,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			text:     "**[your/slash](https://example.com)** world!",
 | 
			
		||||
			expected: `your/slash world!`,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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