mirror of
https://github.com/usememos/memos.git
synced 2024-11-30 22:14:19 +03:00
chore: implement part of nodes
This commit is contained in:
parent
dd83782522
commit
b20e0097cf
@ -9,10 +9,6 @@ type LineBreak struct {
|
|||||||
|
|
||||||
var NodeTypeLineBreak = NewNodeType("LineBreak")
|
var NodeTypeLineBreak = NewNodeType("LineBreak")
|
||||||
|
|
||||||
func NewLineBreak() *LineBreak {
|
|
||||||
return &LineBreak{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*LineBreak) Type() NodeType {
|
func (*LineBreak) Type() NodeType {
|
||||||
return NodeTypeLineBreak
|
return NodeTypeLineBreak
|
||||||
}
|
}
|
||||||
@ -25,12 +21,6 @@ type Paragraph struct {
|
|||||||
|
|
||||||
var NodeTypeParagraph = NewNodeType("Paragraph")
|
var NodeTypeParagraph = NewNodeType("Paragraph")
|
||||||
|
|
||||||
func NewParagraph(children []Node) *Paragraph {
|
|
||||||
return &Paragraph{
|
|
||||||
Children: children,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Paragraph) Type() NodeType {
|
func (*Paragraph) Type() NodeType {
|
||||||
return NodeTypeParagraph
|
return NodeTypeParagraph
|
||||||
}
|
}
|
||||||
@ -44,13 +34,19 @@ type CodeBlock struct {
|
|||||||
|
|
||||||
var NodeTypeCodeBlock = NewNodeType("CodeBlock")
|
var NodeTypeCodeBlock = NewNodeType("CodeBlock")
|
||||||
|
|
||||||
func NewCodeBlock(language, content string) *CodeBlock {
|
|
||||||
return &CodeBlock{
|
|
||||||
Language: language,
|
|
||||||
Content: content,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*CodeBlock) Type() NodeType {
|
func (*CodeBlock) Type() NodeType {
|
||||||
return NodeTypeCodeBlock
|
return NodeTypeCodeBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Heading struct {
|
||||||
|
BaseBlock
|
||||||
|
|
||||||
|
Level int
|
||||||
|
Children []Node
|
||||||
|
}
|
||||||
|
|
||||||
|
var NodeTypeHeading = NewNodeType("Heading")
|
||||||
|
|
||||||
|
func (*Heading) Type() NodeType {
|
||||||
|
return NodeTypeHeading
|
||||||
|
}
|
||||||
|
@ -10,12 +10,6 @@ type Text struct {
|
|||||||
|
|
||||||
var NodeTypeText = NewNodeType("Text")
|
var NodeTypeText = NewNodeType("Text")
|
||||||
|
|
||||||
func NewText(content string) *Text {
|
|
||||||
return &Text{
|
|
||||||
Content: content,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Text) Type() NodeType {
|
func (*Text) Type() NodeType {
|
||||||
return NodeTypeText
|
return NodeTypeText
|
||||||
}
|
}
|
||||||
@ -30,13 +24,70 @@ type Bold struct {
|
|||||||
|
|
||||||
var NodeTypeBold = NewNodeType("Bold")
|
var NodeTypeBold = NewNodeType("Bold")
|
||||||
|
|
||||||
func NewBold(symbol, content string) *Bold {
|
|
||||||
return &Bold{
|
|
||||||
Symbol: symbol,
|
|
||||||
Content: content,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Bold) Type() NodeType {
|
func (*Bold) Type() NodeType {
|
||||||
return NodeTypeBold
|
return NodeTypeBold
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Code struct {
|
||||||
|
BaseInline
|
||||||
|
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
var NodeTypeCode = NewNodeType("Code")
|
||||||
|
|
||||||
|
func (*Code) Type() NodeType {
|
||||||
|
return NodeTypeCode
|
||||||
|
}
|
||||||
|
|
||||||
|
type Image struct {
|
||||||
|
BaseInline
|
||||||
|
|
||||||
|
AltText string
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
var NodeTypeImage = NewNodeType("Image")
|
||||||
|
|
||||||
|
func (*Image) Type() NodeType {
|
||||||
|
return NodeTypeImage
|
||||||
|
}
|
||||||
|
|
||||||
|
type Link struct {
|
||||||
|
BaseInline
|
||||||
|
|
||||||
|
Text string
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
var NodeTypeLink = NewNodeType("Link")
|
||||||
|
|
||||||
|
func (*Link) Type() NodeType {
|
||||||
|
return NodeTypeLink
|
||||||
|
}
|
||||||
|
|
||||||
|
type Italic struct {
|
||||||
|
BaseInline
|
||||||
|
|
||||||
|
// Symbol is "*" or "_"
|
||||||
|
Symbol string
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
var NodeTypeItalic = NewNodeType("Italic")
|
||||||
|
|
||||||
|
func (*Italic) Type() NodeType {
|
||||||
|
return NodeTypeItalic
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tag struct {
|
||||||
|
BaseInline
|
||||||
|
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
var NodeTypeTag = NewNodeType("Tag")
|
||||||
|
|
||||||
|
func (*Tag) Type() NodeType {
|
||||||
|
return NodeTypeTag
|
||||||
|
}
|
||||||
|
@ -44,7 +44,6 @@ func TestBoldParser(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
tokens := tokenizer.Tokenize(test.text)
|
tokens := tokenizer.Tokenize(test.text)
|
||||||
parser := NewBoldParser()
|
require.Equal(t, test.bold, NewBoldParser().Parse(tokens))
|
||||||
require.Equal(t, test.bold, parser.Parse(tokens))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,51 @@
|
|||||||
package parser
|
package parser
|
||||||
|
|
||||||
import "github.com/usememos/memos/plugin/gomark/parser/tokenizer"
|
import (
|
||||||
|
"github.com/usememos/memos/plugin/gomark/ast"
|
||||||
|
"github.com/usememos/memos/plugin/gomark/parser/tokenizer"
|
||||||
|
)
|
||||||
|
|
||||||
type CodeParser struct {
|
type CodeParser struct{}
|
||||||
Content string
|
|
||||||
}
|
var defaultCodeParser = &CodeParser{}
|
||||||
|
|
||||||
func NewCodeParser() *CodeParser {
|
func NewCodeParser() *CodeParser {
|
||||||
return &CodeParser{}
|
return defaultCodeParser
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*CodeParser) Match(tokens []*tokenizer.Token) *CodeParser {
|
func (*CodeParser) Match(tokens []*tokenizer.Token) (int, bool) {
|
||||||
if len(tokens) < 3 {
|
if len(tokens) < 3 {
|
||||||
return nil
|
return 0, false
|
||||||
}
|
}
|
||||||
if tokens[0].Type != tokenizer.Backtick {
|
if tokens[0].Type != tokenizer.Backtick {
|
||||||
return nil
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
content, matched := "", false
|
contentTokens, matched := []*tokenizer.Token{}, false
|
||||||
for _, token := range tokens[1:] {
|
for _, token := range tokens[1:] {
|
||||||
if token.Type == tokenizer.Newline {
|
if token.Type == tokenizer.Newline {
|
||||||
return nil
|
return 0, false
|
||||||
}
|
}
|
||||||
if token.Type == tokenizer.Backtick {
|
if token.Type == tokenizer.Backtick {
|
||||||
matched = true
|
matched = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
content += token.Value
|
contentTokens = append(contentTokens, token)
|
||||||
}
|
}
|
||||||
if !matched || len(content) == 0 {
|
if !matched || len(contentTokens) == 0 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return len(contentTokens) + 2, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *CodeParser) Parse(tokens []*tokenizer.Token) ast.Node {
|
||||||
|
size, ok := p.Match(tokens)
|
||||||
|
if size == 0 || !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &CodeParser{
|
|
||||||
Content: content,
|
contentTokens := tokens[1 : size-1]
|
||||||
|
return &ast.Code{
|
||||||
|
Content: tokenizer.Stringify(contentTokens),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,6 @@ func TestCodeBlockParser(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
tokens := tokenizer.Tokenize(test.text)
|
tokens := tokenizer.Tokenize(test.text)
|
||||||
parser := NewCodeBlockParser()
|
require.Equal(t, test.codeBlock, NewCodeBlockParser().Parse(tokens))
|
||||||
require.Equal(t, test.codeBlock, parser.Parse(tokens))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,14 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/usememos/memos/plugin/gomark/ast"
|
||||||
"github.com/usememos/memos/plugin/gomark/parser/tokenizer"
|
"github.com/usememos/memos/plugin/gomark/parser/tokenizer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCodeParser(t *testing.T) {
|
func TestCodeParser(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
text string
|
text string
|
||||||
code *CodeParser
|
code ast.Node
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
text: "`Hello world!",
|
text: "`Hello world!",
|
||||||
@ -19,7 +20,7 @@ func TestCodeParser(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "`Hello world!`",
|
text: "`Hello world!`",
|
||||||
code: &CodeParser{
|
code: &ast.Code{
|
||||||
Content: "Hello world!",
|
Content: "Hello world!",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -31,7 +32,6 @@ func TestCodeParser(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
tokens := tokenizer.Tokenize(test.text)
|
tokens := tokenizer.Tokenize(test.text)
|
||||||
code := NewCodeParser()
|
require.Equal(t, test.code, NewCodeParser().Parse(tokens))
|
||||||
require.Equal(t, test.code, code.Match(tokens))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
package parser
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/usememos/memos/plugin/gomark/ast"
|
||||||
"github.com/usememos/memos/plugin/gomark/parser/tokenizer"
|
"github.com/usememos/memos/plugin/gomark/parser/tokenizer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HeadingParser struct {
|
type HeadingParser struct{}
|
||||||
Level int
|
|
||||||
ContentTokens []*tokenizer.Token
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHeadingParser() *HeadingParser {
|
func NewHeadingParser() *HeadingParser {
|
||||||
return &HeadingParser{}
|
return &HeadingParser{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*HeadingParser) Match(tokens []*tokenizer.Token) *HeadingParser {
|
func (*HeadingParser) Match(tokens []*tokenizer.Token) (int, bool) {
|
||||||
cursor := 0
|
cursor := 0
|
||||||
for _, token := range tokens {
|
for _, token := range tokens {
|
||||||
if token.Type == tokenizer.Hash {
|
if token.Type == tokenizer.Hash {
|
||||||
@ -23,14 +21,14 @@ func (*HeadingParser) Match(tokens []*tokenizer.Token) *HeadingParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(tokens) <= cursor+1 {
|
if len(tokens) <= cursor+1 {
|
||||||
return nil
|
return 0, false
|
||||||
}
|
}
|
||||||
if tokens[cursor].Type != tokenizer.Space {
|
if tokens[cursor].Type != tokenizer.Space {
|
||||||
return nil
|
return 0, false
|
||||||
}
|
}
|
||||||
level := cursor
|
level := cursor
|
||||||
if level == 0 || level > 6 {
|
if level == 0 || level > 6 {
|
||||||
return nil
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor++
|
cursor++
|
||||||
@ -43,11 +41,34 @@ func (*HeadingParser) Match(tokens []*tokenizer.Token) *HeadingParser {
|
|||||||
cursor++
|
cursor++
|
||||||
}
|
}
|
||||||
if len(contentTokens) == 0 {
|
if len(contentTokens) == 0 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return cursor, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *HeadingParser) Parse(tokens []*tokenizer.Token) ast.Node {
|
||||||
|
size, ok := p.Match(tokens)
|
||||||
|
if size == 0 || !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return &HeadingParser{
|
level := 0
|
||||||
|
for _, token := range tokens {
|
||||||
|
if token.Type == tokenizer.Hash {
|
||||||
|
level++
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contentTokens := tokens[level+1 : size]
|
||||||
|
children := ParseInline(contentTokens, []InlineParser{
|
||||||
|
NewBoldParser(),
|
||||||
|
NewCodeParser(),
|
||||||
|
NewTextParser(),
|
||||||
|
})
|
||||||
|
return &ast.Heading{
|
||||||
Level: level,
|
Level: level,
|
||||||
ContentTokens: contentTokens,
|
Children: children,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,14 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/usememos/memos/plugin/gomark/ast"
|
||||||
"github.com/usememos/memos/plugin/gomark/parser/tokenizer"
|
"github.com/usememos/memos/plugin/gomark/parser/tokenizer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHeadingParser(t *testing.T) {
|
func TestHeadingParser(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
text string
|
text string
|
||||||
heading *HeadingParser
|
heading ast.Node
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
text: "*Hello world",
|
text: "*Hello world",
|
||||||
@ -19,48 +20,22 @@ func TestHeadingParser(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "## Hello World",
|
text: "## Hello World",
|
||||||
heading: &HeadingParser{
|
heading: &ast.Heading{
|
||||||
Level: 2,
|
Level: 2,
|
||||||
ContentTokens: []*tokenizer.Token{
|
Children: []ast.Node{
|
||||||
{
|
&ast.Text{
|
||||||
Type: tokenizer.Text,
|
Content: "Hello World",
|
||||||
Value: "Hello",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: tokenizer.Space,
|
|
||||||
Value: " ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: tokenizer.Text,
|
|
||||||
Value: "World",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "# # Hello World",
|
text: "# # Hello World",
|
||||||
heading: &HeadingParser{
|
heading: &ast.Heading{
|
||||||
Level: 1,
|
Level: 1,
|
||||||
ContentTokens: []*tokenizer.Token{
|
Children: []ast.Node{
|
||||||
{
|
&ast.Text{
|
||||||
Type: tokenizer.Hash,
|
Content: "# Hello World",
|
||||||
Value: "#",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: tokenizer.Space,
|
|
||||||
Value: " ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: tokenizer.Text,
|
|
||||||
Value: "Hello",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: tokenizer.Space,
|
|
||||||
Value: " ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: tokenizer.Text,
|
|
||||||
Value: "World",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -72,16 +47,26 @@ func TestHeadingParser(t *testing.T) {
|
|||||||
{
|
{
|
||||||
text: `# 123
|
text: `# 123
|
||||||
Hello World`,
|
Hello World`,
|
||||||
heading: &HeadingParser{
|
heading: &ast.Heading{
|
||||||
Level: 1,
|
Level: 1,
|
||||||
ContentTokens: []*tokenizer.Token{
|
Children: []ast.Node{
|
||||||
{
|
&ast.Text{
|
||||||
Type: tokenizer.Text,
|
Content: "123 ",
|
||||||
Value: "123",
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: tokenizer.Space,
|
text: "### **Hello** World",
|
||||||
Value: " ",
|
heading: &ast.Heading{
|
||||||
|
Level: 3,
|
||||||
|
Children: []ast.Node{
|
||||||
|
&ast.Bold{
|
||||||
|
Symbol: "*",
|
||||||
|
Content: "Hello",
|
||||||
|
},
|
||||||
|
&ast.Text{
|
||||||
|
Content: " World",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -90,7 +75,6 @@ Hello World`,
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
tokens := tokenizer.Tokenize(test.text)
|
tokens := tokenizer.Tokenize(test.text)
|
||||||
heading := NewHeadingParser()
|
require.Equal(t, test.heading, NewHeadingParser().Parse(tokens))
|
||||||
require.Equal(t, test.heading, heading.Match(tokens))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,32 @@
|
|||||||
package parser
|
package parser
|
||||||
|
|
||||||
import "github.com/usememos/memos/plugin/gomark/parser/tokenizer"
|
import (
|
||||||
|
"github.com/usememos/memos/plugin/gomark/ast"
|
||||||
|
"github.com/usememos/memos/plugin/gomark/parser/tokenizer"
|
||||||
|
)
|
||||||
|
|
||||||
type ImageParser struct {
|
type ImageParser struct{}
|
||||||
AltText string
|
|
||||||
URL string
|
var defaultImageParser = &ImageParser{}
|
||||||
}
|
|
||||||
|
|
||||||
func NewImageParser() *ImageParser {
|
func NewImageParser() *ImageParser {
|
||||||
return &ImageParser{}
|
return defaultImageParser
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*ImageParser) Match(tokens []*tokenizer.Token) *ImageParser {
|
func (*ImageParser) Match(tokens []*tokenizer.Token) (int, bool) {
|
||||||
if len(tokens) < 5 {
|
if len(tokens) < 5 {
|
||||||
return nil
|
return 0, false
|
||||||
}
|
}
|
||||||
if tokens[0].Type != tokenizer.ExclamationMark {
|
if tokens[0].Type != tokenizer.ExclamationMark {
|
||||||
return nil
|
return 0, false
|
||||||
}
|
}
|
||||||
if tokens[1].Type != tokenizer.LeftSquareBracket {
|
if tokens[1].Type != tokenizer.LeftSquareBracket {
|
||||||
return nil
|
return 0, false
|
||||||
}
|
}
|
||||||
cursor, altText := 2, ""
|
cursor, altText := 2, ""
|
||||||
for ; cursor < len(tokens)-2; cursor++ {
|
for ; cursor < len(tokens)-2; cursor++ {
|
||||||
if tokens[cursor].Type == tokenizer.Newline {
|
if tokens[cursor].Type == tokenizer.Newline {
|
||||||
return nil
|
return 0, false
|
||||||
}
|
}
|
||||||
if tokens[cursor].Type == tokenizer.RightSquareBracket {
|
if tokens[cursor].Type == tokenizer.RightSquareBracket {
|
||||||
break
|
break
|
||||||
@ -32,24 +34,42 @@ func (*ImageParser) Match(tokens []*tokenizer.Token) *ImageParser {
|
|||||||
altText += tokens[cursor].Value
|
altText += tokens[cursor].Value
|
||||||
}
|
}
|
||||||
if tokens[cursor+1].Type != tokenizer.LeftParenthesis {
|
if tokens[cursor+1].Type != tokenizer.LeftParenthesis {
|
||||||
return nil
|
return 0, false
|
||||||
}
|
}
|
||||||
matched, url := false, ""
|
cursor += 2
|
||||||
for _, token := range tokens[cursor+2:] {
|
contentTokens, matched := []*tokenizer.Token{}, false
|
||||||
|
for _, token := range tokens[cursor:] {
|
||||||
if token.Type == tokenizer.Newline || token.Type == tokenizer.Space {
|
if token.Type == tokenizer.Newline || token.Type == tokenizer.Space {
|
||||||
return nil
|
return 0, false
|
||||||
}
|
}
|
||||||
if token.Type == tokenizer.RightParenthesis {
|
if token.Type == tokenizer.RightParenthesis {
|
||||||
matched = true
|
matched = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
url += token.Value
|
contentTokens = append(contentTokens, token)
|
||||||
}
|
}
|
||||||
if !matched || url == "" {
|
if !matched || len(contentTokens) == 0 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return cursor + len(contentTokens) + 1, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ImageParser) Parse(tokens []*tokenizer.Token) ast.Node {
|
||||||
|
size, ok := p.Match(tokens)
|
||||||
|
if size == 0 || !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &ImageParser{
|
|
||||||
AltText: altText,
|
altTextTokens := []*tokenizer.Token{}
|
||||||
URL: url,
|
for _, token := range tokens[2:] {
|
||||||
|
if token.Type == tokenizer.RightSquareBracket {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
altTextTokens = append(altTextTokens, token)
|
||||||
|
}
|
||||||
|
contentTokens := tokens[2+len(altTextTokens)+2 : size-1]
|
||||||
|
return &ast.Image{
|
||||||
|
AltText: tokenizer.Stringify(altTextTokens),
|
||||||
|
URL: tokenizer.Stringify(contentTokens),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,17 +5,18 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/usememos/memos/plugin/gomark/ast"
|
||||||
"github.com/usememos/memos/plugin/gomark/parser/tokenizer"
|
"github.com/usememos/memos/plugin/gomark/parser/tokenizer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestImageParser(t *testing.T) {
|
func TestImageParser(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
text string
|
text string
|
||||||
image *ImageParser
|
image ast.Node
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
text: "![](https://example.com)",
|
text: "![](https://example.com)",
|
||||||
image: &ImageParser{
|
image: &ast.Image{
|
||||||
AltText: "",
|
AltText: "",
|
||||||
URL: "https://example.com",
|
URL: "https://example.com",
|
||||||
},
|
},
|
||||||
@ -30,7 +31,7 @@ func TestImageParser(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "![al te](https://example.com)",
|
text: "![al te](https://example.com)",
|
||||||
image: &ImageParser{
|
image: &ast.Image{
|
||||||
AltText: "al te",
|
AltText: "al te",
|
||||||
URL: "https://example.com",
|
URL: "https://example.com",
|
||||||
},
|
},
|
||||||
@ -38,6 +39,6 @@ func TestImageParser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
tokens := tokenizer.Tokenize(test.text)
|
tokens := tokenizer.Tokenize(test.text)
|
||||||
require.Equal(t, test.image, NewImageParser().Match(tokens))
|
require.Equal(t, test.image, NewImageParser().Parse(tokens))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,6 @@ func TestItalicParser(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
tokens := tokenizer.Tokenize(test.text)
|
tokens := tokenizer.Tokenize(test.text)
|
||||||
italic := NewItalicParser()
|
require.Equal(t, test.italic, NewItalicParser().Match(tokens))
|
||||||
require.Equal(t, test.italic, italic.Match(tokens))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,5 +29,5 @@ func (p *LineBreakParser) Parse(tokens []*tokenizer.Token) ast.Node {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return ast.NewLineBreak()
|
return &ast.LineBreak{}
|
||||||
}
|
}
|
||||||
|
@ -42,5 +42,7 @@ func (p *ParagraphParser) Parse(tokens []*tokenizer.Token) ast.Node {
|
|||||||
NewBoldParser(),
|
NewBoldParser(),
|
||||||
NewTextParser(),
|
NewTextParser(),
|
||||||
})
|
})
|
||||||
return ast.NewParagraph(children)
|
return &ast.Paragraph{
|
||||||
|
Children: children,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ func TestParagraphParser(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
tokens := tokenizer.Tokenize(test.text)
|
tokens := tokenizer.Tokenize(test.text)
|
||||||
parser := NewParagraphParser()
|
require.Equal(t, test.paragraph, NewParagraphParser().Parse(tokens))
|
||||||
require.Equal(t, test.paragraph, parser.Parse(tokens))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,6 @@ func TestParser(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
tokens := tokenizer.Tokenize(test.text)
|
tokens := tokenizer.Tokenize(test.text)
|
||||||
nodes := Parse(tokens)
|
require.Equal(t, test.nodes, Parse(tokens))
|
||||||
require.Equal(t, test.nodes, nodes)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,9 @@ func (*TextParser) Match(tokens []*tokenizer.Token) (int, bool) {
|
|||||||
|
|
||||||
func (*TextParser) Parse(tokens []*tokenizer.Token) ast.Node {
|
func (*TextParser) Parse(tokens []*tokenizer.Token) ast.Node {
|
||||||
if len(tokens) == 0 {
|
if len(tokens) == 0 {
|
||||||
return ast.NewText("")
|
return &ast.Text{}
|
||||||
|
}
|
||||||
|
return &ast.Text{
|
||||||
|
Content: tokens[0].String(),
|
||||||
}
|
}
|
||||||
return ast.NewText(tokens[0].String())
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user