Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
bb42042d
Commit
bb42042d
authored
Dec 16, 2023
by
Steven
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: implement task list parser
parent
1c7fb77e
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
235 additions
and
4 deletions
+235
-4
block.go
plugin/gomark/ast/block.go
+13
-0
node.go
plugin/gomark/ast/node.go
+1
-0
parser.go
plugin/gomark/parser/parser.go
+5
-4
parser_test.go
plugin/gomark/parser/parser_test.go
+54
-0
task_list.go
plugin/gomark/parser/task_list.go
+69
-0
task_list_test.go
plugin/gomark/parser/task_list_test.go
+57
-0
html.go
plugin/gomark/renderer/html/html.go
+20
-0
html_test.go
plugin/gomark/renderer/html/html_test.go
+8
-0
string.go
plugin/gomark/renderer/string/string.go
+8
-0
No files found.
plugin/gomark/ast/block.go
View file @
bb42042d
...
...
@@ -87,3 +87,16 @@ type UnorderedList struct {
func
(
*
UnorderedList
)
Type
()
NodeType
{
return
UnorderedListNode
}
type
TaskList
struct
{
BaseBlock
// Symbol is "*" or "-" or "+".
Symbol
string
Complete
bool
Children
[]
Node
}
func
(
*
TaskList
)
Type
()
NodeType
{
return
TaskListNode
}
plugin/gomark/ast/node.go
View file @
bb42042d
...
...
@@ -13,6 +13,7 @@ const (
BlockquoteNode
OrderedListNode
UnorderedListNode
TaskListNode
// Inline nodes.
TextNode
BoldNode
...
...
plugin/gomark/parser/parser.go
View file @
bb42042d
...
...
@@ -25,21 +25,22 @@ type BlockParser interface {
BaseParser
}
func
Parse
(
tokens
[]
*
tokenizer
.
Token
)
([]
ast
.
Node
,
error
)
{
return
ParseBlock
(
tokens
)
}
var
defaultBlockParsers
=
[]
BlockParser
{
NewCodeBlockParser
(),
NewHorizontalRuleParser
(),
NewHeadingParser
(),
NewBlockquoteParser
(),
NewTaskListParser
(),
NewUnorderedListParser
(),
NewOrderedListParser
(),
NewParagraphParser
(),
NewLineBreakParser
(),
}
func
Parse
(
tokens
[]
*
tokenizer
.
Token
)
([]
ast
.
Node
,
error
)
{
return
ParseBlock
(
tokens
)
}
func
ParseBlock
(
tokens
[]
*
tokenizer
.
Token
)
([]
ast
.
Node
,
error
)
{
return
ParseBlockWithParsers
(
tokens
,
defaultBlockParsers
)
}
...
...
plugin/gomark/parser/parser_test.go
View file @
bb42042d
package
parser
import
(
"strconv"
"testing"
"github.com/stretchr/testify/require"
...
...
@@ -151,6 +152,51 @@ func TestParser(t *testing.T) {
},
},
},
{
text
:
"1. hello
\n
- [ ] world"
,
nodes
:
[]
ast
.
Node
{
&
ast
.
OrderedList
{
Number
:
"1"
,
Children
:
[]
ast
.
Node
{
&
ast
.
Text
{
Content
:
"hello"
,
},
},
},
&
ast
.
TaskList
{
Symbol
:
tokenizer
.
Hyphen
,
Complete
:
false
,
Children
:
[]
ast
.
Node
{
&
ast
.
Text
{
Content
:
"world"
,
},
},
},
},
},
{
text
:
"- [ ] hello
\n
- [x] world"
,
nodes
:
[]
ast
.
Node
{
&
ast
.
TaskList
{
Symbol
:
tokenizer
.
Hyphen
,
Complete
:
false
,
Children
:
[]
ast
.
Node
{
&
ast
.
Text
{
Content
:
"hello"
,
},
},
},
&
ast
.
TaskList
{
Symbol
:
tokenizer
.
Hyphen
,
Complete
:
true
,
Children
:
[]
ast
.
Node
{
&
ast
.
Text
{
Content
:
"world"
,
},
},
},
},
},
}
for
_
,
test
:=
range
tests
{
...
...
@@ -184,6 +230,12 @@ func StringifyNode(node ast.Node) string {
return
"HorizontalRule("
+
n
.
Symbol
+
")"
case
*
ast
.
Blockquote
:
return
"Blockquote("
+
StringifyNodes
(
n
.
Children
)
+
")"
case
*
ast
.
OrderedList
:
return
"OrderedList("
+
n
.
Number
+
", "
+
StringifyNodes
(
n
.
Children
)
+
")"
case
*
ast
.
UnorderedList
:
return
"UnorderedList("
+
n
.
Symbol
+
", "
+
StringifyNodes
(
n
.
Children
)
+
")"
case
*
ast
.
TaskList
:
return
"TaskList("
+
n
.
Symbol
+
", "
+
strconv
.
FormatBool
(
n
.
Complete
)
+
", "
+
StringifyNodes
(
n
.
Children
)
+
")"
case
*
ast
.
Text
:
return
"Text("
+
n
.
Content
+
")"
case
*
ast
.
Bold
:
...
...
@@ -202,6 +254,8 @@ func StringifyNode(node ast.Node) string {
return
"Tag("
+
n
.
Content
+
")"
case
*
ast
.
Strikethrough
:
return
"Strikethrough("
+
n
.
Content
+
")"
case
*
ast
.
EscapingCharacter
:
return
"EscapingCharacter("
+
n
.
Symbol
+
")"
}
return
""
}
plugin/gomark/parser/task_list.go
0 → 100644
View file @
bb42042d
package
parser
import
(
"errors"
"github.com/usememos/memos/plugin/gomark/ast"
"github.com/usememos/memos/plugin/gomark/parser/tokenizer"
)
type
TaskListParser
struct
{}
func
NewTaskListParser
()
*
TaskListParser
{
return
&
TaskListParser
{}
}
func
(
*
TaskListParser
)
Match
(
tokens
[]
*
tokenizer
.
Token
)
(
int
,
bool
)
{
if
len
(
tokens
)
<
7
{
return
0
,
false
}
symbolToken
:=
tokens
[
0
]
if
symbolToken
.
Type
!=
tokenizer
.
Hyphen
&&
symbolToken
.
Type
!=
tokenizer
.
Asterisk
&&
symbolToken
.
Type
!=
tokenizer
.
PlusSign
{
return
0
,
false
}
if
tokens
[
1
]
.
Type
!=
tokenizer
.
Space
{
return
0
,
false
}
if
tokens
[
2
]
.
Type
!=
tokenizer
.
LeftSquareBracket
||
(
tokens
[
3
]
.
Type
!=
tokenizer
.
Space
&&
tokens
[
3
]
.
Value
!=
"x"
)
||
tokens
[
4
]
.
Type
!=
tokenizer
.
RightSquareBracket
{
return
0
,
false
}
if
tokens
[
5
]
.
Type
!=
tokenizer
.
Space
{
return
0
,
false
}
contentTokens
:=
[]
*
tokenizer
.
Token
{}
for
_
,
token
:=
range
tokens
[
6
:
]
{
contentTokens
=
append
(
contentTokens
,
token
)
if
token
.
Type
==
tokenizer
.
Newline
{
break
}
}
if
len
(
contentTokens
)
==
0
{
return
0
,
false
}
return
len
(
contentTokens
)
+
6
,
true
}
func
(
p
*
TaskListParser
)
Parse
(
tokens
[]
*
tokenizer
.
Token
)
(
ast
.
Node
,
error
)
{
size
,
ok
:=
p
.
Match
(
tokens
)
if
size
==
0
||
!
ok
{
return
nil
,
errors
.
New
(
"not matched"
)
}
symbolToken
:=
tokens
[
0
]
contentTokens
:=
tokens
[
6
:
size
]
if
contentTokens
[
len
(
contentTokens
)
-
1
]
.
Type
==
tokenizer
.
Newline
{
contentTokens
=
contentTokens
[
:
len
(
contentTokens
)
-
1
]
}
children
,
err
:=
ParseInline
(
contentTokens
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
ast
.
TaskList
{
Symbol
:
symbolToken
.
Type
,
Complete
:
tokens
[
3
]
.
Value
==
"x"
,
Children
:
children
,
},
nil
}
plugin/gomark/parser/task_list_test.go
0 → 100644
View file @
bb42042d
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
TestTaskListParser
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
text
string
node
ast
.
Node
}{
{
text
:
"*asd"
,
node
:
nil
,
},
{
text
:
"+ [ ] Hello World"
,
node
:
&
ast
.
TaskList
{
Symbol
:
tokenizer
.
PlusSign
,
Complete
:
false
,
Children
:
[]
ast
.
Node
{
&
ast
.
Text
{
Content
:
"Hello World"
,
},
},
},
},
{
text
:
"* [x] **Hello**"
,
node
:
&
ast
.
TaskList
{
Symbol
:
tokenizer
.
Asterisk
,
Complete
:
true
,
Children
:
[]
ast
.
Node
{
&
ast
.
Bold
{
Symbol
:
"*"
,
Children
:
[]
ast
.
Node
{
&
ast
.
Text
{
Content
:
"Hello"
,
},
},
},
},
},
},
}
for
_
,
test
:=
range
tests
{
tokens
:=
tokenizer
.
Tokenize
(
test
.
text
)
node
,
_
:=
NewTaskListParser
()
.
Parse
(
tokens
)
require
.
Equal
(
t
,
StringifyNodes
([]
ast
.
Node
{
test
.
node
}),
StringifyNodes
([]
ast
.
Node
{
node
}))
}
}
plugin/gomark/renderer/html/html.go
View file @
bb42042d
...
...
@@ -43,6 +43,8 @@ func (r *HTMLRenderer) RenderNode(node ast.Node) {
r
.
renderUnorderedList
(
n
)
case
*
ast
.
OrderedList
:
r
.
renderOrderedList
(
n
)
case
*
ast
.
TaskList
:
r
.
renderTaskList
(
n
)
case
*
ast
.
Bold
:
r
.
renderBold
(
n
)
case
*
ast
.
Italic
:
...
...
@@ -119,6 +121,24 @@ func (r *HTMLRenderer) renderBlockquote(node *ast.Blockquote) {
}
}
func
(
r
*
HTMLRenderer
)
renderTaskList
(
node
*
ast
.
TaskList
)
{
prevSibling
,
nextSibling
:=
node
.
PrevSibling
(),
node
.
NextSibling
()
if
prevSibling
==
nil
||
prevSibling
.
Type
()
!=
ast
.
TaskListNode
{
r
.
output
.
WriteString
(
"<ul>"
)
}
r
.
output
.
WriteString
(
"<li>"
)
r
.
output
.
WriteString
(
"<input type=
\"
checkbox
\"
"
)
if
node
.
Complete
{
r
.
output
.
WriteString
(
" checked"
)
}
r
.
output
.
WriteString
(
" disabled>"
)
r
.
RenderNodes
(
node
.
Children
)
r
.
output
.
WriteString
(
"</li>"
)
if
nextSibling
==
nil
||
nextSibling
.
Type
()
!=
ast
.
TaskListNode
{
r
.
output
.
WriteString
(
"</ul>"
)
}
}
func
(
r
*
HTMLRenderer
)
renderUnorderedList
(
node
*
ast
.
UnorderedList
)
{
prevSibling
,
nextSibling
:=
node
.
PrevSibling
(),
node
.
NextSibling
()
if
prevSibling
==
nil
||
prevSibling
.
Type
()
!=
ast
.
UnorderedListNode
{
...
...
plugin/gomark/renderer/html/html_test.go
View file @
bb42042d
...
...
@@ -54,6 +54,14 @@ func TestHTMLRenderer(t *testing.T) {
text
:
"1. Hello
\n
2. world
\n
* !"
,
expected
:
`<ol><li>Hello</li><li>world</li></ol><ul><li>!</li></ul>`
,
},
{
text
:
"- [ ] hello
\n
- [x] world"
,
expected
:
`<ul><li><input type="checkbox" disabled>hello</li><li><input type="checkbox" checked disabled>world</li></ul>`
,
},
{
text
:
"1. ordered
\n
* unorder
\n
- [ ] checkbox
\n
- [x] checked"
,
expected
:
`<ol><li>ordered</li></ol><ul><li>unorder</li></ul><ul><li><input type="checkbox" disabled>checkbox</li><li><input type="checkbox" checked disabled>checked</li></ul>`
,
},
}
for
_
,
test
:=
range
tests
{
...
...
plugin/gomark/renderer/string/string.go
View file @
bb42042d
...
...
@@ -39,6 +39,8 @@ func (r *StringRenderer) RenderNode(node ast.Node) {
r
.
renderHorizontalRule
(
n
)
case
*
ast
.
Blockquote
:
r
.
renderBlockquote
(
n
)
case
*
ast
.
TaskList
:
r
.
renderTaskList
(
n
)
case
*
ast
.
UnorderedList
:
r
.
renderUnorderedList
(
n
)
case
*
ast
.
OrderedList
:
...
...
@@ -109,6 +111,12 @@ func (r *StringRenderer) renderBlockquote(node *ast.Blockquote) {
r
.
output
.
WriteString
(
"
\n
"
)
}
func
(
r
*
StringRenderer
)
renderTaskList
(
node
*
ast
.
TaskList
)
{
r
.
output
.
WriteString
(
node
.
Symbol
)
r
.
RenderNodes
(
node
.
Children
)
r
.
output
.
WriteString
(
"
\n
"
)
}
func
(
r
*
StringRenderer
)
renderUnorderedList
(
node
*
ast
.
UnorderedList
)
{
r
.
output
.
WriteString
(
node
.
Symbol
)
r
.
RenderNodes
(
node
.
Children
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment