From 309fab222e11ac572a0423669a3e1021ebe5d337 Mon Sep 17 00:00:00 2001 From: Steven Date: Sat, 27 Jan 2024 21:38:07 +0800 Subject: [PATCH] chore: implement nested blockquote --- plugin/gomark/parser/blockquote.go | 41 ++++++++++++------- plugin/gomark/parser/blockquote_test.go | 34 ++++++++++++++- plugin/gomark/parser/bold.go | 2 +- plugin/gomark/parser/bold_italic.go | 2 +- plugin/gomark/parser/code_block.go | 4 +- plugin/gomark/parser/escaping_character.go | 2 +- plugin/gomark/parser/horizontal_rule.go | 2 +- plugin/gomark/parser/line_break.go | 2 +- plugin/gomark/parser/math_block.go | 4 +- plugin/gomark/parser/table.go | 2 +- plugin/gomark/parser/tokenizer/tokenizer.go | 6 +-- .../gomark/parser/tokenizer/tokenizer_test.go | 2 +- plugin/gomark/renderer/html/html.go | 9 +--- 13 files changed, 76 insertions(+), 36 deletions(-) diff --git a/plugin/gomark/parser/blockquote.go b/plugin/gomark/parser/blockquote.go index e146c1c0..2c419a88 100644 --- a/plugin/gomark/parser/blockquote.go +++ b/plugin/gomark/parser/blockquote.go @@ -12,25 +12,38 @@ func NewBlockquoteParser() *BlockquoteParser { } func (*BlockquoteParser) Match(tokens []*tokenizer.Token) (ast.Node, int) { - matchedTokens := tokenizer.GetFirstLine(tokens) - if len(matchedTokens) < 3 { - return nil, 0 + rows := tokenizer.Split(tokens, tokenizer.NewLine) + contentRows := [][]*tokenizer.Token{} + for _, row := range rows { + if len(row) < 3 || row[0].Type != tokenizer.GreaterThan || row[1].Type != tokenizer.Space { + break + } + contentRows = append(contentRows, row) } - if matchedTokens[0].Type != tokenizer.GreaterThan || matchedTokens[1].Type != tokenizer.Space { + if len(contentRows) == 0 { return nil, 0 } - contentTokens := matchedTokens[2:] - children, err := ParseInlineWithParsers(contentTokens, []InlineParser{NewLinkParser(), NewTextParser()}) - if err != nil { - return nil, 0 + children := []ast.Node{} + size := 0 + for index, row := range contentRows { + contentTokens := row[2:] + nodes, err := ParseBlockWithParsers(contentTokens, []BlockParser{NewBlockquoteParser(), NewParagraphParser()}) + if err != nil { + return nil, 0 + } + if len(nodes) != 1 { + return nil, 0 + } + + children = append(children, nodes[0]) + size += len(row) + if index != len(contentRows)-1 { + size += 1 // NewLine. + } } return &ast.Blockquote{ - Children: []ast.Node{ - &ast.Paragraph{ - Children: children, - }, - }, - }, len(matchedTokens) + Children: children, + }, size } diff --git a/plugin/gomark/parser/blockquote_test.go b/plugin/gomark/parser/blockquote_test.go index 8bac2848..4a26d178 100644 --- a/plugin/gomark/parser/blockquote_test.go +++ b/plugin/gomark/parser/blockquote_test.go @@ -48,7 +48,7 @@ func TestBlockquoteParser(t *testing.T) { }, }, { - text: "> Hello\nworld", + text: "> Hello\n> world", blockquote: &ast.Blockquote{ Children: []ast.Node{ &ast.Paragraph{ @@ -58,6 +58,38 @@ func TestBlockquoteParser(t *testing.T) { }, }, }, + &ast.Paragraph{ + Children: []ast.Node{ + &ast.Text{ + Content: "world", + }, + }, + }, + }, + }, + }, + { + text: "> Hello\n> > world", + blockquote: &ast.Blockquote{ + Children: []ast.Node{ + &ast.Paragraph{ + Children: []ast.Node{ + &ast.Text{ + Content: "Hello", + }, + }, + }, + &ast.Blockquote{ + Children: []ast.Node{ + &ast.Paragraph{ + Children: []ast.Node{ + &ast.Text{ + Content: "world", + }, + }, + }, + }, + }, }, }, }, diff --git a/plugin/gomark/parser/bold.go b/plugin/gomark/parser/bold.go index 401f217e..1210cc2e 100644 --- a/plugin/gomark/parser/bold.go +++ b/plugin/gomark/parser/bold.go @@ -29,7 +29,7 @@ func (*BoldParser) Match(tokens []*tokenizer.Token) (ast.Node, int) { cursor, matched := 2, false for ; cursor < len(matchedTokens)-1; cursor++ { token, nextToken := matchedTokens[cursor], matchedTokens[cursor+1] - if token.Type == tokenizer.Newline || nextToken.Type == tokenizer.Newline { + if token.Type == tokenizer.NewLine || nextToken.Type == tokenizer.NewLine { return nil, 0 } if token.Type == prefixTokenType && nextToken.Type == prefixTokenType { diff --git a/plugin/gomark/parser/bold_italic.go b/plugin/gomark/parser/bold_italic.go index d34d0ad2..9148976d 100644 --- a/plugin/gomark/parser/bold_italic.go +++ b/plugin/gomark/parser/bold_italic.go @@ -28,7 +28,7 @@ func (*BoldItalicParser) Match(tokens []*tokenizer.Token) (ast.Node, int) { cursor, matched := 3, false for ; cursor < len(matchedTokens)-2; cursor++ { token, nextToken, endToken := matchedTokens[cursor], matchedTokens[cursor+1], matchedTokens[cursor+2] - if token.Type == tokenizer.Newline || nextToken.Type == tokenizer.Newline || endToken.Type == tokenizer.Newline { + if token.Type == tokenizer.NewLine || nextToken.Type == tokenizer.NewLine || endToken.Type == tokenizer.NewLine { return nil, 0 } if token.Type == prefixTokenType && nextToken.Type == prefixTokenType && endToken.Type == prefixTokenType { diff --git a/plugin/gomark/parser/code_block.go b/plugin/gomark/parser/code_block.go index 36eb93f6..bf0fb953 100644 --- a/plugin/gomark/parser/code_block.go +++ b/plugin/gomark/parser/code_block.go @@ -17,7 +17,7 @@ func NewCodeBlockParser() *CodeBlockParser { } func (*CodeBlockParser) Match(tokens []*tokenizer.Token) (ast.Node, int) { - rows := tokenizer.Split(tokens, tokenizer.Newline) + rows := tokenizer.Split(tokens, tokenizer.NewLine) if len(rows) < 3 { return nil, 0 } @@ -59,7 +59,7 @@ func (*CodeBlockParser) Match(tokens []*tokenizer.Token) (ast.Node, int) { contentTokens = append(contentTokens, row...) if index != len(contentRows)-1 { contentTokens = append(contentTokens, &tokenizer.Token{ - Type: tokenizer.Newline, + Type: tokenizer.NewLine, Value: "\n", }) } diff --git a/plugin/gomark/parser/escaping_character.go b/plugin/gomark/parser/escaping_character.go index afe6fc37..c1d315cc 100644 --- a/plugin/gomark/parser/escaping_character.go +++ b/plugin/gomark/parser/escaping_character.go @@ -18,7 +18,7 @@ func (*EscapingCharacterParser) Match(tokens []*tokenizer.Token) (ast.Node, int) if tokens[0].Type != tokenizer.Backslash { return nil, 0 } - if tokens[1].Type == tokenizer.Newline || tokens[1].Type == tokenizer.Space || tokens[1].Type == tokenizer.Text || tokens[1].Type == tokenizer.Number { + if tokens[1].Type == tokenizer.NewLine || tokens[1].Type == tokenizer.Space || tokens[1].Type == tokenizer.Text || tokens[1].Type == tokenizer.Number { return nil, 0 } return &ast.EscapingCharacter{ diff --git a/plugin/gomark/parser/horizontal_rule.go b/plugin/gomark/parser/horizontal_rule.go index 4a9c0e79..8646ec51 100644 --- a/plugin/gomark/parser/horizontal_rule.go +++ b/plugin/gomark/parser/horizontal_rule.go @@ -16,7 +16,7 @@ func (*HorizontalRuleParser) Match(tokens []*tokenizer.Token) (ast.Node, int) { if len(matchedTokens) < 3 { return nil, 0 } - if len(matchedTokens) > 3 && matchedTokens[3].Type != tokenizer.Newline { + if len(matchedTokens) > 3 && matchedTokens[3].Type != tokenizer.NewLine { return nil, 0 } if matchedTokens[0].Type != matchedTokens[1].Type || matchedTokens[0].Type != matchedTokens[2].Type || matchedTokens[1].Type != matchedTokens[2].Type { diff --git a/plugin/gomark/parser/line_break.go b/plugin/gomark/parser/line_break.go index 7df34f54..34b376a8 100644 --- a/plugin/gomark/parser/line_break.go +++ b/plugin/gomark/parser/line_break.go @@ -15,7 +15,7 @@ func (*LineBreakParser) Match(tokens []*tokenizer.Token) (ast.Node, int) { if len(tokens) == 0 { return nil, 0 } - if tokens[0].Type != tokenizer.Newline { + if tokens[0].Type != tokenizer.NewLine { return nil, 0 } return &ast.LineBreak{}, 1 diff --git a/plugin/gomark/parser/math_block.go b/plugin/gomark/parser/math_block.go index 77fe080e..c0193f81 100644 --- a/plugin/gomark/parser/math_block.go +++ b/plugin/gomark/parser/math_block.go @@ -12,7 +12,7 @@ func NewMathBlockParser() *MathBlockParser { } func (*MathBlockParser) Match(tokens []*tokenizer.Token) (ast.Node, int) { - rows := tokenizer.Split(tokens, tokenizer.Newline) + rows := tokenizer.Split(tokens, tokenizer.NewLine) if len(rows) < 3 { return nil, 0 } @@ -42,7 +42,7 @@ func (*MathBlockParser) Match(tokens []*tokenizer.Token) (ast.Node, int) { contentTokens = append(contentTokens, row...) if index != len(contentRows)-1 { contentTokens = append(contentTokens, &tokenizer.Token{ - Type: tokenizer.Newline, + Type: tokenizer.NewLine, Value: "\n", }) } diff --git a/plugin/gomark/parser/table.go b/plugin/gomark/parser/table.go index 7d776d32..091a9041 100644 --- a/plugin/gomark/parser/table.go +++ b/plugin/gomark/parser/table.go @@ -12,7 +12,7 @@ func NewTableParser() *TableParser { } func (*TableParser) Match(tokens []*tokenizer.Token) (ast.Node, int) { - rawRows := tokenizer.Split(tokens, tokenizer.Newline) + rawRows := tokenizer.Split(tokens, tokenizer.NewLine) if len(rawRows) < 3 { return nil, 0 } diff --git a/plugin/gomark/parser/tokenizer/tokenizer.go b/plugin/gomark/parser/tokenizer/tokenizer.go index a317dd3b..77d9659c 100644 --- a/plugin/gomark/parser/tokenizer/tokenizer.go +++ b/plugin/gomark/parser/tokenizer/tokenizer.go @@ -26,7 +26,7 @@ const ( Colon TokenType = ":" Caret TokenType = "^" Backslash TokenType = "\\" - Newline TokenType = "\n" + NewLine TokenType = "\n" Space TokenType = " " ) @@ -97,7 +97,7 @@ func Tokenize(text string) []*Token { case '\\': tokens = append(tokens, NewToken(Backslash, `\`)) case '\n': - tokens = append(tokens, NewToken(Newline, "\n")) + tokens = append(tokens, NewToken(NewLine, "\n")) case ' ': tokens = append(tokens, NewToken(Space, " ")) default: @@ -175,7 +175,7 @@ func FindUnescaped(tokens []*Token, target TokenType) int { func GetFirstLine(tokens []*Token) []*Token { for i, token := range tokens { - if token.Type == Newline { + if token.Type == NewLine { return tokens[:i] } } diff --git a/plugin/gomark/parser/tokenizer/tokenizer_test.go b/plugin/gomark/parser/tokenizer/tokenizer_test.go index 82f7cec2..94a2bfe7 100644 --- a/plugin/gomark/parser/tokenizer/tokenizer_test.go +++ b/plugin/gomark/parser/tokenizer/tokenizer_test.go @@ -57,7 +57,7 @@ func TestTokenize(t *testing.T) { Value: " ", }, { - Type: Newline, + Type: NewLine, Value: "\n", }, { diff --git a/plugin/gomark/renderer/html/html.go b/plugin/gomark/renderer/html/html.go index 538b6f18..9f08c177 100644 --- a/plugin/gomark/renderer/html/html.go +++ b/plugin/gomark/renderer/html/html.go @@ -122,14 +122,9 @@ func (r *HTMLRenderer) renderHorizontalRule(_ *ast.HorizontalRule) { } func (r *HTMLRenderer) renderBlockquote(node *ast.Blockquote) { - prevSibling, nextSibling := ast.FindPrevSiblingExceptLineBreak(node), ast.FindNextSiblingExceptLineBreak(node) - if prevSibling == nil || prevSibling.Type() != ast.BlockquoteNode { - r.output.WriteString("
") - } + r.output.WriteString("
") r.RenderNodes(node.Children) - if nextSibling == nil || nextSibling.Type() != ast.BlockquoteNode { - r.output.WriteString("
") - } + r.output.WriteString("
") } func (r *HTMLRenderer) renderTaskList(node *ast.TaskList) {