motion-markdown-it 0.4.0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +243 -0
- data/lib/motion-markdown-it.rb +71 -0
- data/lib/motion-markdown-it/common/entities.rb +1084 -0
- data/lib/motion-markdown-it/common/html_blocks.rb +60 -0
- data/lib/motion-markdown-it/common/html_re.rb +28 -0
- data/lib/motion-markdown-it/common/string.rb +14 -0
- data/lib/motion-markdown-it/common/url_schemas.rb +173 -0
- data/lib/motion-markdown-it/common/utils.rb +216 -0
- data/lib/motion-markdown-it/helpers/parse_link_destination.rb +75 -0
- data/lib/motion-markdown-it/helpers/parse_link_label.rb +51 -0
- data/lib/motion-markdown-it/helpers/parse_link_title.rb +48 -0
- data/lib/motion-markdown-it/index.rb +507 -0
- data/lib/motion-markdown-it/parser_block.rb +113 -0
- data/lib/motion-markdown-it/parser_core.rb +46 -0
- data/lib/motion-markdown-it/parser_inline.rb +121 -0
- data/lib/motion-markdown-it/presets/commonmark.rb +76 -0
- data/lib/motion-markdown-it/presets/default.rb +42 -0
- data/lib/motion-markdown-it/presets/zero.rb +59 -0
- data/lib/motion-markdown-it/renderer.rb +286 -0
- data/lib/motion-markdown-it/ruler.rb +327 -0
- data/lib/motion-markdown-it/rules_block/blockquote.rb +138 -0
- data/lib/motion-markdown-it/rules_block/code.rb +35 -0
- data/lib/motion-markdown-it/rules_block/fence.rb +94 -0
- data/lib/motion-markdown-it/rules_block/heading.rb +56 -0
- data/lib/motion-markdown-it/rules_block/hr.rb +45 -0
- data/lib/motion-markdown-it/rules_block/html_block.rb +73 -0
- data/lib/motion-markdown-it/rules_block/lheading.rb +54 -0
- data/lib/motion-markdown-it/rules_block/list.rb +242 -0
- data/lib/motion-markdown-it/rules_block/paragraph.rb +51 -0
- data/lib/motion-markdown-it/rules_block/reference.rb +161 -0
- data/lib/motion-markdown-it/rules_block/state_block.rb +184 -0
- data/lib/motion-markdown-it/rules_block/table.rb +161 -0
- data/lib/motion-markdown-it/rules_core/block.rb +20 -0
- data/lib/motion-markdown-it/rules_core/inline.rb +20 -0
- data/lib/motion-markdown-it/rules_core/linkify.rb +138 -0
- data/lib/motion-markdown-it/rules_core/normalize.rb +44 -0
- data/lib/motion-markdown-it/rules_core/replacements.rb +90 -0
- data/lib/motion-markdown-it/rules_core/smartquotes.rb +158 -0
- data/lib/motion-markdown-it/rules_core/state_core.rb +20 -0
- data/lib/motion-markdown-it/rules_inline/autolink.rb +74 -0
- data/lib/motion-markdown-it/rules_inline/backticks.rb +51 -0
- data/lib/motion-markdown-it/rules_inline/emphasis.rb +172 -0
- data/lib/motion-markdown-it/rules_inline/entity.rb +51 -0
- data/lib/motion-markdown-it/rules_inline/escape.rb +55 -0
- data/lib/motion-markdown-it/rules_inline/html_inline.rb +49 -0
- data/lib/motion-markdown-it/rules_inline/image.rb +158 -0
- data/lib/motion-markdown-it/rules_inline/link.rb +153 -0
- data/lib/motion-markdown-it/rules_inline/newline.rb +47 -0
- data/lib/motion-markdown-it/rules_inline/state_inline.rb +57 -0
- data/lib/motion-markdown-it/rules_inline/strikethrough.rb +130 -0
- data/lib/motion-markdown-it/rules_inline/text.rb +94 -0
- data/lib/motion-markdown-it/token.rb +134 -0
- data/lib/motion-markdown-it/version.rb +5 -0
- data/spec/motion-markdown-it/bench_mark_spec.rb +44 -0
- data/spec/motion-markdown-it/commonmark_spec.rb +16 -0
- data/spec/motion-markdown-it/markdown_it_spec.rb +18 -0
- data/spec/motion-markdown-it/misc_spec.rb +277 -0
- data/spec/motion-markdown-it/ruler_spec.rb +153 -0
- data/spec/motion-markdown-it/testgen_helper.rb +68 -0
- data/spec/motion-markdown-it/token_spec.rb +17 -0
- data/spec/motion-markdown-it/utils_spec.rb +82 -0
- data/spec/spec_helper.rb +6 -0
- metadata +158 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# Process html entity - {, ¯, ", ...
|
2
|
+
#------------------------------------------------------------------------------
|
3
|
+
module MarkdownIt
|
4
|
+
module RulesInline
|
5
|
+
class Entity
|
6
|
+
extend Common::Utils
|
7
|
+
|
8
|
+
DIGITAL_RE = /^&#((?:x[a-f0-9]{1,8}|[0-9]{1,8}));/i
|
9
|
+
NAMED_RE = /^&([a-z][a-z0-9]{1,31});/i
|
10
|
+
|
11
|
+
|
12
|
+
#------------------------------------------------------------------------------
|
13
|
+
def self.entity(state, silent)
|
14
|
+
pos = state.pos
|
15
|
+
max = state.posMax
|
16
|
+
|
17
|
+
return false if state.src.charCodeAt(pos) != 0x26 # &
|
18
|
+
|
19
|
+
if pos + 1 < max
|
20
|
+
ch = state.src.charCodeAt(pos + 1)
|
21
|
+
|
22
|
+
if ch == 0x23 # '#'
|
23
|
+
match = state.src.slice_to_end(pos).match(DIGITAL_RE)
|
24
|
+
if match
|
25
|
+
if !silent
|
26
|
+
code = match[1][0].downcase == 'x' ? match[1].slice_to_end(1).to_i(16) : match[1].to_i
|
27
|
+
state.pending += isValidEntityCode(code) ? fromCodePoint(code) : fromCodePoint(0xFFFD)
|
28
|
+
end
|
29
|
+
state.pos += match[0].length
|
30
|
+
return true
|
31
|
+
end
|
32
|
+
else
|
33
|
+
match = state.src.slice_to_end(pos).match(NAMED_RE)
|
34
|
+
if match
|
35
|
+
if HTMLEntities::MAPPINGS[match[1]]
|
36
|
+
state.pending += HTMLEntities::MAPPINGS[match[1]].chr(Encoding::UTF_8) if !silent
|
37
|
+
state.pos += match[0].length
|
38
|
+
return true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
state.pending += '&' if !silent
|
45
|
+
state.pos += 1
|
46
|
+
return true
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Proceess escaped chars and hardbreaks
|
2
|
+
#------------------------------------------------------------------------------
|
3
|
+
module MarkdownIt
|
4
|
+
module RulesInline
|
5
|
+
class Escape
|
6
|
+
|
7
|
+
ESCAPED = []
|
8
|
+
|
9
|
+
0.upto(255) { |i| ESCAPED.push(0) }
|
10
|
+
|
11
|
+
'\\!"#$%&\'()*+,./:;<=>?@[]^_`{|}~-'.split('').each { |ch| ESCAPED[ch.ord] = 1 }
|
12
|
+
|
13
|
+
|
14
|
+
#------------------------------------------------------------------------------
|
15
|
+
def self.escape(state, silent)
|
16
|
+
pos = state.pos
|
17
|
+
max = state.posMax
|
18
|
+
|
19
|
+
return false if state.src.charCodeAt(pos) != 0x5C # \
|
20
|
+
|
21
|
+
pos += 1
|
22
|
+
|
23
|
+
if pos < max
|
24
|
+
ch = state.src.charCodeAt(pos)
|
25
|
+
|
26
|
+
if ch < 256 && ESCAPED[ch] != 0
|
27
|
+
state.pending += state.src[pos] if !silent
|
28
|
+
state.pos += 2
|
29
|
+
return true
|
30
|
+
end
|
31
|
+
|
32
|
+
if ch == 0x0A
|
33
|
+
if !silent
|
34
|
+
state.push('hardbreak', 'br', 0)
|
35
|
+
end
|
36
|
+
|
37
|
+
pos += 1
|
38
|
+
# skip leading whitespaces from next line
|
39
|
+
while (pos < max && state.src.charCodeAt(pos) == 0x20)
|
40
|
+
pos += 1
|
41
|
+
end
|
42
|
+
|
43
|
+
state.pos = pos
|
44
|
+
return true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
state.pending += '\\' if !silent
|
49
|
+
state.pos += 1
|
50
|
+
return true
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Process html tags
|
2
|
+
#------------------------------------------------------------------------------
|
3
|
+
module MarkdownIt
|
4
|
+
|
5
|
+
module RulesInline
|
6
|
+
class HtmlInline
|
7
|
+
include MarkdownIt::Common::HtmlRe
|
8
|
+
|
9
|
+
#------------------------------------------------------------------------------
|
10
|
+
def self.isLetter(ch)
|
11
|
+
lc = ch | 0x20 # to lower case
|
12
|
+
return (lc >= 0x61) && (lc <= 0x7a) # >= a && <= z
|
13
|
+
end
|
14
|
+
|
15
|
+
#------------------------------------------------------------------------------
|
16
|
+
def self.html_inline(state, silent)
|
17
|
+
pos = state.pos
|
18
|
+
|
19
|
+
return false if !state.md.options[:html]
|
20
|
+
|
21
|
+
# Check start
|
22
|
+
max = state.posMax
|
23
|
+
if (state.src.charCodeAt(pos) != 0x3C || pos + 2 >= max) # <
|
24
|
+
return false
|
25
|
+
end
|
26
|
+
|
27
|
+
# Quick fail on second char
|
28
|
+
ch = state.src.charCodeAt(pos + 1)
|
29
|
+
if (ch != 0x21 && # !
|
30
|
+
ch != 0x3F && # ?
|
31
|
+
ch != 0x2F && # /
|
32
|
+
!isLetter(ch))
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
|
36
|
+
match = state.src.slice_to_end(pos).match(HTML_TAG_RE)
|
37
|
+
return false if !match
|
38
|
+
|
39
|
+
if !silent
|
40
|
+
token = state.push('html_inline', '', 0)
|
41
|
+
token.content = state.src.slice(pos...(pos + match[0].length))
|
42
|
+
end
|
43
|
+
state.pos += match[0].length
|
44
|
+
return true
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# Process 
|
2
|
+
#------------------------------------------------------------------------------
|
3
|
+
module MarkdownIt
|
4
|
+
module RulesInline
|
5
|
+
class Image
|
6
|
+
extend Helpers::ParseLinkDestination
|
7
|
+
extend Helpers::ParseLinkLabel
|
8
|
+
extend Helpers::ParseLinkTitle
|
9
|
+
extend Common::Utils
|
10
|
+
|
11
|
+
#------------------------------------------------------------------------------
|
12
|
+
def self.image(state, silent)
|
13
|
+
href = ''
|
14
|
+
oldPos = state.pos
|
15
|
+
max = state.posMax
|
16
|
+
|
17
|
+
return false if (state.src.charCodeAt(state.pos) != 0x21) # !
|
18
|
+
return false if (state.src.charCodeAt(state.pos + 1) != 0x5B) # [
|
19
|
+
|
20
|
+
labelStart = state.pos + 2
|
21
|
+
labelEnd = parseLinkLabel(state, state.pos + 1, false)
|
22
|
+
|
23
|
+
# parser failed to find ']', so it's not a valid link
|
24
|
+
return false if (labelEnd < 0)
|
25
|
+
|
26
|
+
pos = labelEnd + 1
|
27
|
+
if (pos < max && state.src.charCodeAt(pos) == 0x28) # (
|
28
|
+
#
|
29
|
+
# Inline link
|
30
|
+
#
|
31
|
+
|
32
|
+
# [link]( <href> "title" )
|
33
|
+
# ^^ skipping these spaces
|
34
|
+
pos += 1
|
35
|
+
while pos < max
|
36
|
+
code = state.src.charCodeAt(pos)
|
37
|
+
break if (code != 0x20 && code != 0x0A)
|
38
|
+
pos += 1
|
39
|
+
end
|
40
|
+
return false if (pos >= max)
|
41
|
+
|
42
|
+
# [link]( <href> "title" )
|
43
|
+
# ^^^^^^ parsing link destination
|
44
|
+
start = pos
|
45
|
+
res = parseLinkDestination(state.src, pos, state.posMax)
|
46
|
+
if (res[:ok])
|
47
|
+
href = state.md.normalizeLink.call(res[:str])
|
48
|
+
if (state.md.validateLink.call(href))
|
49
|
+
pos = res[:pos]
|
50
|
+
else
|
51
|
+
href = ''
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# [link]( <href> "title" )
|
56
|
+
# ^^ skipping these spaces
|
57
|
+
start = pos
|
58
|
+
while pos < max
|
59
|
+
code = state.src.charCodeAt(pos)
|
60
|
+
break if (code != 0x20 && code != 0x0A)
|
61
|
+
pos += 1
|
62
|
+
end
|
63
|
+
|
64
|
+
# [link]( <href> "title" )
|
65
|
+
# ^^^^^^^ parsing link title
|
66
|
+
res = parseLinkTitle(state.src, pos, state.posMax)
|
67
|
+
if (pos < max && start != pos && res[:ok])
|
68
|
+
title = res[:str]
|
69
|
+
pos = res[:pos]
|
70
|
+
|
71
|
+
# [link]( <href> "title" )
|
72
|
+
# ^^ skipping these spaces
|
73
|
+
while pos < max
|
74
|
+
code = state.src.charCodeAt(pos);
|
75
|
+
break if (code != 0x20 && code != 0x0A)
|
76
|
+
pos += 1
|
77
|
+
end
|
78
|
+
else
|
79
|
+
title = ''
|
80
|
+
end
|
81
|
+
|
82
|
+
if (pos >= max || state.src.charCodeAt(pos) != 0x29) # )
|
83
|
+
state.pos = oldPos
|
84
|
+
return false
|
85
|
+
end
|
86
|
+
pos += 1
|
87
|
+
else
|
88
|
+
#
|
89
|
+
# Link reference
|
90
|
+
#
|
91
|
+
return false if state.env[:references].nil?
|
92
|
+
|
93
|
+
# [foo] [bar]
|
94
|
+
# ^^ optional whitespace (can include newlines)
|
95
|
+
while pos < max
|
96
|
+
code = state.src.charCodeAt(pos)
|
97
|
+
break if (code != 0x20 && code != 0x0A)
|
98
|
+
pos += 1
|
99
|
+
end
|
100
|
+
|
101
|
+
if (pos < max && state.src.charCodeAt(pos) == 0x5B) # [
|
102
|
+
start = pos + 1
|
103
|
+
pos = parseLinkLabel(state, pos)
|
104
|
+
if (pos >= 0)
|
105
|
+
label = state.src.slice(start...pos)
|
106
|
+
pos += 1
|
107
|
+
else
|
108
|
+
pos = labelEnd + 1
|
109
|
+
end
|
110
|
+
else
|
111
|
+
pos = labelEnd + 1
|
112
|
+
end
|
113
|
+
|
114
|
+
# covers label === '' and label === undefined
|
115
|
+
# (collapsed reference link and shortcut reference link respectively)
|
116
|
+
label = state.src.slice(labelStart...labelEnd) if label.nil? || label.empty?
|
117
|
+
|
118
|
+
ref = state.env[:references][normalizeReference(label)]
|
119
|
+
if (!ref)
|
120
|
+
state.pos = oldPos
|
121
|
+
return false
|
122
|
+
end
|
123
|
+
href = ref[:href]
|
124
|
+
title = ref[:title]
|
125
|
+
end
|
126
|
+
|
127
|
+
#
|
128
|
+
# We found the end of the link, and know for a fact it's a valid link;
|
129
|
+
# so all that's left to do is to call tokenizer.
|
130
|
+
#
|
131
|
+
if (!silent)
|
132
|
+
state.pos = labelStart
|
133
|
+
state.posMax = labelEnd
|
134
|
+
|
135
|
+
newState = RulesInline::StateInline.new(
|
136
|
+
state.src.slice(labelStart...labelEnd),
|
137
|
+
state.md,
|
138
|
+
state.env,
|
139
|
+
tokens = []
|
140
|
+
)
|
141
|
+
newState.md.inline.tokenize(newState)
|
142
|
+
|
143
|
+
token = state.push('image', 'img', 0)
|
144
|
+
token.attrs = attrs = [ [ 'src', href ], [ 'alt', '' ] ]
|
145
|
+
token.children = tokens
|
146
|
+
unless (title.nil? || title.empty?)
|
147
|
+
attrs.push([ 'title', title ])
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
state.pos = pos
|
152
|
+
state.posMax = max
|
153
|
+
return true
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# Process [link](<to> "stuff")
|
2
|
+
#------------------------------------------------------------------------------
|
3
|
+
module MarkdownIt
|
4
|
+
module RulesInline
|
5
|
+
class Link
|
6
|
+
extend Helpers::ParseLinkDestination
|
7
|
+
extend Helpers::ParseLinkLabel
|
8
|
+
extend Helpers::ParseLinkTitle
|
9
|
+
extend Common::Utils
|
10
|
+
|
11
|
+
#------------------------------------------------------------------------------
|
12
|
+
def self.link(state, silent)
|
13
|
+
href = ''
|
14
|
+
oldPos = state.pos
|
15
|
+
max = state.posMax
|
16
|
+
start = state.pos
|
17
|
+
|
18
|
+
return false if (state.src.charCodeAt(state.pos) != 0x5B) # [
|
19
|
+
|
20
|
+
labelStart = state.pos + 1
|
21
|
+
labelEnd = parseLinkLabel(state, state.pos, true)
|
22
|
+
|
23
|
+
# parser failed to find ']', so it's not a valid link
|
24
|
+
return false if (labelEnd < 0)
|
25
|
+
|
26
|
+
pos = labelEnd + 1
|
27
|
+
if (pos < max && state.src.charCodeAt(pos) == 0x28) # (
|
28
|
+
#
|
29
|
+
# Inline link
|
30
|
+
#
|
31
|
+
|
32
|
+
# [link]( <href> "title" )
|
33
|
+
# ^^ skipping these spaces
|
34
|
+
pos += 1
|
35
|
+
while pos < max
|
36
|
+
code = state.src.charCodeAt(pos)
|
37
|
+
break if (code != 0x20 && code != 0x0A)
|
38
|
+
pos += 1
|
39
|
+
end
|
40
|
+
return false if (pos >= max)
|
41
|
+
|
42
|
+
# [link]( <href> "title" )
|
43
|
+
# ^^^^^^ parsing link destination
|
44
|
+
start = pos
|
45
|
+
res = parseLinkDestination(state.src, pos, state.posMax)
|
46
|
+
if (res[:ok])
|
47
|
+
href = state.md.normalizeLink.call(res[:str])
|
48
|
+
if (state.md.validateLink.call(href))
|
49
|
+
pos = res[:pos]
|
50
|
+
else
|
51
|
+
href = ''
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# [link]( <href> "title" )
|
56
|
+
# ^^ skipping these spaces
|
57
|
+
start = pos
|
58
|
+
while pos < max
|
59
|
+
code = state.src.charCodeAt(pos)
|
60
|
+
break if (code != 0x20 && code != 0x0A)
|
61
|
+
pos += 1
|
62
|
+
end
|
63
|
+
|
64
|
+
# [link]( <href> "title" )
|
65
|
+
# ^^^^^^^ parsing link title
|
66
|
+
res = parseLinkTitle(state.src, pos, state.posMax)
|
67
|
+
if (pos < max && start != pos && res[:ok])
|
68
|
+
title = res[:str]
|
69
|
+
pos = res[:pos]
|
70
|
+
|
71
|
+
# [link]( <href> "title" )
|
72
|
+
# ^^ skipping these spaces
|
73
|
+
while pos < max
|
74
|
+
code = state.src.charCodeAt(pos)
|
75
|
+
break if (code != 0x20 && code != 0x0A)
|
76
|
+
pos += 1
|
77
|
+
end
|
78
|
+
else
|
79
|
+
title = ''
|
80
|
+
end
|
81
|
+
|
82
|
+
if (pos >= max || state.src.charCodeAt(pos) != 0x29) # )
|
83
|
+
state.pos = oldPos
|
84
|
+
return false
|
85
|
+
end
|
86
|
+
pos += 1
|
87
|
+
else
|
88
|
+
#
|
89
|
+
# Link reference
|
90
|
+
#
|
91
|
+
return false if state.env[:references].nil?
|
92
|
+
|
93
|
+
# [foo] [bar]
|
94
|
+
# ^^ optional whitespace (can include newlines)
|
95
|
+
while pos < max
|
96
|
+
code = state.src.charCodeAt(pos);
|
97
|
+
break if (code != 0x20 && code != 0x0A)
|
98
|
+
pos += 1
|
99
|
+
end
|
100
|
+
|
101
|
+
if (pos < max && state.src.charCodeAt(pos) == 0x5B) # [
|
102
|
+
start = pos + 1
|
103
|
+
pos = parseLinkLabel(state, pos)
|
104
|
+
if (pos >= 0)
|
105
|
+
label = state.src.slice(start...pos)
|
106
|
+
pos += 1
|
107
|
+
else
|
108
|
+
pos = labelEnd + 1
|
109
|
+
end
|
110
|
+
else
|
111
|
+
pos = labelEnd + 1
|
112
|
+
end
|
113
|
+
|
114
|
+
# covers label === '' and label === undefined
|
115
|
+
# (collapsed reference link and shortcut reference link respectively)
|
116
|
+
label = state.src.slice(labelStart...labelEnd) if label.nil? || label.empty?
|
117
|
+
|
118
|
+
ref = state.env[:references][normalizeReference(label)]
|
119
|
+
if (!ref)
|
120
|
+
state.pos = oldPos
|
121
|
+
return false
|
122
|
+
end
|
123
|
+
href = ref[:href]
|
124
|
+
title = ref[:title]
|
125
|
+
end
|
126
|
+
|
127
|
+
#
|
128
|
+
# We found the end of the link, and know for a fact it's a valid link;
|
129
|
+
# so all that's left to do is to call tokenizer.
|
130
|
+
#
|
131
|
+
if (!silent)
|
132
|
+
state.pos = labelStart
|
133
|
+
state.posMax = labelEnd
|
134
|
+
|
135
|
+
token = state.push('link_open', 'a', 1)
|
136
|
+
token.attrs = attrs = [ [ 'href', href ] ]
|
137
|
+
unless title.nil? || title.empty?
|
138
|
+
attrs.push([ 'title', title ])
|
139
|
+
end
|
140
|
+
|
141
|
+
state.md.inline.tokenize(state)
|
142
|
+
|
143
|
+
token = state.push('link_close', 'a', -1)
|
144
|
+
end
|
145
|
+
|
146
|
+
state.pos = pos
|
147
|
+
state.posMax = max
|
148
|
+
return true
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|