motion-markdown-it 0.4.0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,54 @@
|
|
1
|
+
# lheading (---, ===)
|
2
|
+
#------------------------------------------------------------------------------
|
3
|
+
module MarkdownIt
|
4
|
+
module RulesBlock
|
5
|
+
class Lheading
|
6
|
+
|
7
|
+
#------------------------------------------------------------------------------
|
8
|
+
def self.lheading(state, startLine, endLine, silent = true)
|
9
|
+
nextLine = startLine + 1
|
10
|
+
|
11
|
+
return false if (nextLine >= endLine)
|
12
|
+
return false if (state.tShift[nextLine] < state.blkIndent)
|
13
|
+
|
14
|
+
# Scan next line
|
15
|
+
|
16
|
+
return false if (state.tShift[nextLine] - state.blkIndent > 3)
|
17
|
+
|
18
|
+
pos = state.bMarks[nextLine] + state.tShift[nextLine]
|
19
|
+
max = state.eMarks[nextLine]
|
20
|
+
|
21
|
+
return false if (pos >= max)
|
22
|
+
|
23
|
+
marker = state.src.charCodeAt(pos)
|
24
|
+
|
25
|
+
return false if (marker != 0x2D && marker != 0x3D) # != '-' && != '='
|
26
|
+
|
27
|
+
pos = state.skipChars(pos, marker)
|
28
|
+
pos = state.skipSpaces(pos)
|
29
|
+
|
30
|
+
return false if (pos < max)
|
31
|
+
|
32
|
+
pos = state.bMarks[startLine] + state.tShift[startLine]
|
33
|
+
|
34
|
+
state.line = nextLine + 1
|
35
|
+
level = (marker == 0x3D ? 1 : 2) # =
|
36
|
+
|
37
|
+
token = state.push('heading_open', "h#{level.to_s}", 1)
|
38
|
+
token.markup = marker.chr
|
39
|
+
token.map = [ startLine, state.line ]
|
40
|
+
|
41
|
+
token = state.push('inline', '', 0)
|
42
|
+
token.content = state.src.slice(pos...state.eMarks[startLine]).strip
|
43
|
+
token.map = [ startLine, state.line - 1 ]
|
44
|
+
token.children = []
|
45
|
+
|
46
|
+
token = state.push('heading_close', "h#{level.to_s}", -1)
|
47
|
+
token.markup = marker.chr
|
48
|
+
|
49
|
+
return true
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
# Lists
|
2
|
+
#------------------------------------------------------------------------------
|
3
|
+
module MarkdownIt
|
4
|
+
module RulesBlock
|
5
|
+
class List
|
6
|
+
|
7
|
+
# Search `[-+*][\n ]`, returns next pos arter marker on success
|
8
|
+
# or -1 on fail.
|
9
|
+
#------------------------------------------------------------------------------
|
10
|
+
def self.skipBulletListMarker(state, startLine)
|
11
|
+
pos = state.bMarks[startLine] + state.tShift[startLine]
|
12
|
+
max = state.eMarks[startLine]
|
13
|
+
|
14
|
+
marker = state.src.charCodeAt(pos)
|
15
|
+
pos += 1
|
16
|
+
# Check bullet
|
17
|
+
if (marker != 0x2A && # *
|
18
|
+
marker != 0x2D && # -
|
19
|
+
marker != 0x2B) # +
|
20
|
+
return -1
|
21
|
+
end
|
22
|
+
|
23
|
+
if (pos < max && state.src.charCodeAt(pos) != 0x20)
|
24
|
+
# " 1.test " - is not a list item
|
25
|
+
return -1
|
26
|
+
end
|
27
|
+
|
28
|
+
return pos
|
29
|
+
end
|
30
|
+
|
31
|
+
# Search `\d+[.)][\n ]`, returns next pos after marker on success
|
32
|
+
# or -1 on fail.
|
33
|
+
#------------------------------------------------------------------------------
|
34
|
+
def self.skipOrderedListMarker(state, startLine)
|
35
|
+
pos = state.bMarks[startLine] + state.tShift[startLine]
|
36
|
+
max = state.eMarks[startLine]
|
37
|
+
|
38
|
+
# List marker should have at least 2 chars (digit + dot)
|
39
|
+
return -1 if (pos + 1 >= max)
|
40
|
+
|
41
|
+
ch = state.src.charCodeAt(pos)
|
42
|
+
pos += 1
|
43
|
+
|
44
|
+
return -1 if ch.nil?
|
45
|
+
return -1 if (ch < 0x30 || ch > 0x39) # < 0 || > 9
|
46
|
+
|
47
|
+
while true
|
48
|
+
# EOL -> fail
|
49
|
+
return -1 if (pos >= max)
|
50
|
+
|
51
|
+
ch = state.src.charCodeAt(pos)
|
52
|
+
pos += 1
|
53
|
+
|
54
|
+
if (ch >= 0x30 && ch <= 0x39) # >= 0 && <= 9
|
55
|
+
next
|
56
|
+
end
|
57
|
+
|
58
|
+
# found valid marker
|
59
|
+
if (ch === 0x29 || ch === 0x2e) # ')' || '.'
|
60
|
+
break
|
61
|
+
end
|
62
|
+
|
63
|
+
return -1
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
if (pos < max && state.src.charCodeAt(pos) != 0x20) # space
|
68
|
+
# " 1.test " - is not a list item
|
69
|
+
return -1
|
70
|
+
end
|
71
|
+
return pos
|
72
|
+
end
|
73
|
+
|
74
|
+
#------------------------------------------------------------------------------
|
75
|
+
def self.markTightParagraphs(state, idx)
|
76
|
+
level = state.level + 2
|
77
|
+
|
78
|
+
i = idx + 2
|
79
|
+
l = state.tokens.length
|
80
|
+
while i < l
|
81
|
+
if (state.tokens[i].level == level && state.tokens[i].type == 'paragraph_open')
|
82
|
+
state.tokens[i + 2].hidden = true
|
83
|
+
state.tokens[i].hidden = true
|
84
|
+
i += 2
|
85
|
+
end
|
86
|
+
i += 1
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
#------------------------------------------------------------------------------
|
92
|
+
def self.list(state, startLine, endLine, silent)
|
93
|
+
tight = true
|
94
|
+
|
95
|
+
# Detect list type and position after marker
|
96
|
+
if ((posAfterMarker = skipOrderedListMarker(state, startLine)) >= 0)
|
97
|
+
isOrdered = true
|
98
|
+
elsif ((posAfterMarker = skipBulletListMarker(state, startLine)) >= 0)
|
99
|
+
isOrdered = false
|
100
|
+
else
|
101
|
+
return false
|
102
|
+
end
|
103
|
+
|
104
|
+
# We should terminate list on style change. Remember first one to compare.
|
105
|
+
markerCharCode = state.src.charCodeAt(posAfterMarker - 1)
|
106
|
+
|
107
|
+
# For validation mode we can terminate immediately
|
108
|
+
return true if (silent)
|
109
|
+
|
110
|
+
# Start list
|
111
|
+
listTokIdx = state.tokens.length
|
112
|
+
|
113
|
+
if (isOrdered)
|
114
|
+
start = state.bMarks[startLine] + state.tShift[startLine]
|
115
|
+
markerValue = state.src[start, posAfterMarker - start - 1].to_i
|
116
|
+
token = state.push('ordered_list_open', 'ol', 1)
|
117
|
+
if (markerValue > 1)
|
118
|
+
token.attrs = [ [ 'start', markerValue ] ]
|
119
|
+
end
|
120
|
+
|
121
|
+
else
|
122
|
+
token = state.push('bullet_list_open', 'ul', 1)
|
123
|
+
end
|
124
|
+
|
125
|
+
token.map = listLines = [ startLine, 0 ]
|
126
|
+
token.markup = markerCharCode.chr
|
127
|
+
|
128
|
+
#
|
129
|
+
# Iterate list items
|
130
|
+
#
|
131
|
+
|
132
|
+
nextLine = startLine
|
133
|
+
prevEmptyEnd = false
|
134
|
+
terminatorRules = state.md.block.ruler.getRules('list')
|
135
|
+
|
136
|
+
while (nextLine < endLine)
|
137
|
+
contentStart = state.skipSpaces(posAfterMarker)
|
138
|
+
max = state.eMarks[nextLine]
|
139
|
+
|
140
|
+
if (contentStart >= max)
|
141
|
+
# trimming space in "- \n 3" case, indent is 1 here
|
142
|
+
indentAfterMarker = 1
|
143
|
+
else
|
144
|
+
indentAfterMarker = contentStart - posAfterMarker
|
145
|
+
end
|
146
|
+
|
147
|
+
# If we have more than 4 spaces, the indent is 1
|
148
|
+
# (the rest is just indented code block)
|
149
|
+
indentAfterMarker = 1 if (indentAfterMarker > 4)
|
150
|
+
|
151
|
+
# " - test"
|
152
|
+
# ^^^^^ - calculating total length of this thing
|
153
|
+
indent = (posAfterMarker - state.bMarks[nextLine]) + indentAfterMarker
|
154
|
+
|
155
|
+
# Run subparser & write tokens
|
156
|
+
token = state.push('list_item_open', 'li', 1)
|
157
|
+
token.markup = markerCharCode.chr
|
158
|
+
token.map = itemLines = [ startLine, 0 ]
|
159
|
+
|
160
|
+
oldIndent = state.blkIndent
|
161
|
+
oldTight = state.tight
|
162
|
+
oldTShift = state.tShift[startLine]
|
163
|
+
oldParentType = state.parentType
|
164
|
+
state.tShift[startLine] = contentStart - state.bMarks[startLine]
|
165
|
+
state.blkIndent = indent
|
166
|
+
state.tight = true
|
167
|
+
state.parentType = 'list'
|
168
|
+
|
169
|
+
state.md.block.tokenize(state, startLine, endLine, true)
|
170
|
+
|
171
|
+
# If any of list item is tight, mark list as tight
|
172
|
+
if (!state.tight || prevEmptyEnd)
|
173
|
+
tight = false
|
174
|
+
end
|
175
|
+
# Item become loose if finish with empty line,
|
176
|
+
# but we should filter last element, because it means list finish
|
177
|
+
prevEmptyEnd = (state.line - startLine) > 1 && state.isEmpty(state.line - 1)
|
178
|
+
|
179
|
+
state.blkIndent = oldIndent
|
180
|
+
state.tShift[startLine] = oldTShift
|
181
|
+
state.tight = oldTight
|
182
|
+
state.parentType = oldParentType
|
183
|
+
|
184
|
+
token = state.push('list_item_close', 'li', -1)
|
185
|
+
token.markup = markerCharCode.chr
|
186
|
+
|
187
|
+
nextLine = startLine = state.line
|
188
|
+
itemLines[1] = nextLine
|
189
|
+
contentStart = state.bMarks[startLine]
|
190
|
+
|
191
|
+
break if (nextLine >= endLine)
|
192
|
+
break if (state.isEmpty(nextLine))
|
193
|
+
|
194
|
+
#
|
195
|
+
# Try to check if list is terminated or continued.
|
196
|
+
#
|
197
|
+
break if (state.tShift[nextLine] < state.blkIndent)
|
198
|
+
|
199
|
+
# fail if terminating block found
|
200
|
+
terminate = false
|
201
|
+
(0...terminatorRules.length).each do |i|
|
202
|
+
if (terminatorRules[i].call(state, nextLine, endLine, true))
|
203
|
+
terminate = true
|
204
|
+
break
|
205
|
+
end
|
206
|
+
end
|
207
|
+
break if (terminate)
|
208
|
+
|
209
|
+
# fail if list has another type
|
210
|
+
if (isOrdered)
|
211
|
+
posAfterMarker = skipOrderedListMarker(state, nextLine)
|
212
|
+
break if (posAfterMarker < 0)
|
213
|
+
else
|
214
|
+
posAfterMarker = skipBulletListMarker(state, nextLine)
|
215
|
+
break if (posAfterMarker < 0)
|
216
|
+
end
|
217
|
+
|
218
|
+
break if (markerCharCode != state.src.charCodeAt(posAfterMarker - 1))
|
219
|
+
end
|
220
|
+
|
221
|
+
# Finilize list
|
222
|
+
if (isOrdered)
|
223
|
+
token = state.push('ordered_list_close', 'ol', -1)
|
224
|
+
else
|
225
|
+
token = state.push('bullet_list_close', 'ul', -1)
|
226
|
+
end
|
227
|
+
token.markup = markerCharCode.chr
|
228
|
+
|
229
|
+
listLines[1] = nextLine
|
230
|
+
state.line = nextLine
|
231
|
+
|
232
|
+
# mark paragraphs tight if needed
|
233
|
+
if (tight)
|
234
|
+
markTightParagraphs(state, listTokIdx)
|
235
|
+
end
|
236
|
+
|
237
|
+
return true
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Paragraph
|
2
|
+
#------------------------------------------------------------------------------
|
3
|
+
module MarkdownIt
|
4
|
+
module RulesBlock
|
5
|
+
class Paragraph
|
6
|
+
|
7
|
+
#------------------------------------------------------------------------------
|
8
|
+
def self.paragraph(state, startLine)
|
9
|
+
nextLine = startLine + 1
|
10
|
+
terminatorRules = state.md.block.ruler.getRules('paragraph')
|
11
|
+
endLine = state.lineMax
|
12
|
+
|
13
|
+
# jump line-by-line until empty one or EOF
|
14
|
+
# for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
|
15
|
+
while nextLine < endLine && !state.isEmpty(nextLine)
|
16
|
+
# this would be a code block normally, but after paragraph
|
17
|
+
# it's considered a lazy continuation regardless of what's there
|
18
|
+
(nextLine += 1) && next if (state.tShift[nextLine] - state.blkIndent > 3)
|
19
|
+
|
20
|
+
# Some tags can terminate paragraph without empty line.
|
21
|
+
terminate = false
|
22
|
+
0.upto(terminatorRules.length - 1) do |i|
|
23
|
+
if terminatorRules[i].call(state, nextLine, endLine, true)
|
24
|
+
terminate = true
|
25
|
+
break
|
26
|
+
end
|
27
|
+
end
|
28
|
+
break if terminate
|
29
|
+
nextLine += 1
|
30
|
+
end
|
31
|
+
|
32
|
+
content = state.getLines(startLine, nextLine, state.blkIndent, false).strip
|
33
|
+
|
34
|
+
state.line = nextLine
|
35
|
+
|
36
|
+
token = state.push('paragraph_open', 'p', 1)
|
37
|
+
token.map = [ startLine, state.line ]
|
38
|
+
|
39
|
+
token = state.push('inline', '', 0)
|
40
|
+
token.content = content
|
41
|
+
token.map = [ startLine, state.line ]
|
42
|
+
token.children = []
|
43
|
+
|
44
|
+
token = state.push('paragraph_close', 'p', -1)
|
45
|
+
|
46
|
+
return true
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module MarkdownIt
|
2
|
+
module RulesBlock
|
3
|
+
class Reference
|
4
|
+
extend Helpers::ParseLinkDestination
|
5
|
+
extend Helpers::ParseLinkTitle
|
6
|
+
extend Common::Utils
|
7
|
+
|
8
|
+
#------------------------------------------------------------------------------
|
9
|
+
def self.reference(state, startLine, _endLine, silent)
|
10
|
+
lines = 0
|
11
|
+
pos = state.bMarks[startLine] + state.tShift[startLine]
|
12
|
+
max = state.eMarks[startLine]
|
13
|
+
nextLine = startLine + 1
|
14
|
+
|
15
|
+
return false if (state.src.charCodeAt(pos) != 0x5B) # [
|
16
|
+
|
17
|
+
# Simple check to quickly interrupt scan on [link](url) at the start of line.
|
18
|
+
# Can be useful on practice: https://github.com/markdown-it/markdown-it/issues/54
|
19
|
+
pos += 1
|
20
|
+
while (pos < max)
|
21
|
+
if (state.src.charCodeAt(pos) == 0x5D && # ]
|
22
|
+
state.src.charCodeAt(pos - 1) != 0x5C) # \
|
23
|
+
return false if (pos + 1 === max)
|
24
|
+
return false if (state.src.charCodeAt(pos + 1) != 0x3A) # :
|
25
|
+
break
|
26
|
+
end
|
27
|
+
pos += 1
|
28
|
+
end
|
29
|
+
|
30
|
+
endLine = state.lineMax
|
31
|
+
|
32
|
+
# jump line-by-line until empty one or EOF
|
33
|
+
terminatorRules = state.md.block.ruler.getRules('reference')
|
34
|
+
|
35
|
+
while nextLine < endLine && !state.isEmpty(nextLine)
|
36
|
+
nextLine += 1
|
37
|
+
# this would be a code block normally, but after paragraph
|
38
|
+
# it's considered a lazy continuation regardless of what's there
|
39
|
+
next if (state.tShift[nextLine] - state.blkIndent > 3)
|
40
|
+
|
41
|
+
# Some tags can terminate paragraph without empty line.
|
42
|
+
terminate = false
|
43
|
+
(0...terminatorRules.length).each do |i|
|
44
|
+
if (terminatorRules[i].call(state, nextLine, endLine, true))
|
45
|
+
terminate = true
|
46
|
+
break
|
47
|
+
end
|
48
|
+
end
|
49
|
+
break if (terminate)
|
50
|
+
end
|
51
|
+
|
52
|
+
str = state.getLines(startLine, nextLine, state.blkIndent, false).strip
|
53
|
+
max = str.length
|
54
|
+
labelEnd = -1
|
55
|
+
|
56
|
+
pos = 1
|
57
|
+
while pos < max
|
58
|
+
ch = str.charCodeAt(pos)
|
59
|
+
if (ch == 0x5B ) # [
|
60
|
+
return false
|
61
|
+
elsif (ch == 0x5D) # ]
|
62
|
+
labelEnd = pos
|
63
|
+
break
|
64
|
+
elsif (ch == 0x0A) # \n
|
65
|
+
lines += 1
|
66
|
+
elsif (ch == 0x5C) # \
|
67
|
+
pos += 1
|
68
|
+
if (pos < max && str.charCodeAt(pos) == 0x0A)
|
69
|
+
lines += 1
|
70
|
+
end
|
71
|
+
end
|
72
|
+
pos += 1
|
73
|
+
end
|
74
|
+
|
75
|
+
return false if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) != 0x3A) # :
|
76
|
+
|
77
|
+
# [label]: destination 'title'
|
78
|
+
# ^^^ skip optional whitespace here
|
79
|
+
pos = labelEnd + 2
|
80
|
+
while pos < max
|
81
|
+
ch = str.charCodeAt(pos)
|
82
|
+
if (ch == 0x0A)
|
83
|
+
lines += 1
|
84
|
+
elsif (ch == 0x20)
|
85
|
+
else
|
86
|
+
break
|
87
|
+
end
|
88
|
+
pos += 1
|
89
|
+
end
|
90
|
+
|
91
|
+
# [label]: destination 'title'
|
92
|
+
# ^^^^^^^^^^^ parse this
|
93
|
+
res = parseLinkDestination(str, pos, max)
|
94
|
+
return false if (!res[:ok])
|
95
|
+
|
96
|
+
href = state.md.normalizeLink.call(res[:str])
|
97
|
+
return false if (!state.md.validateLink.call(href))
|
98
|
+
|
99
|
+
pos = res[:pos]
|
100
|
+
lines += res[:lines]
|
101
|
+
|
102
|
+
# save cursor state, we could require to rollback later
|
103
|
+
destEndPos = pos
|
104
|
+
destEndLineNo = lines
|
105
|
+
|
106
|
+
# [label]: destination 'title'
|
107
|
+
# ^^^ skipping those spaces
|
108
|
+
start = pos
|
109
|
+
while (pos < max)
|
110
|
+
ch = str.charCodeAt(pos)
|
111
|
+
if (ch == 0x0A)
|
112
|
+
lines += 1
|
113
|
+
elsif (ch == 0x20)
|
114
|
+
else
|
115
|
+
break
|
116
|
+
end
|
117
|
+
pos += 1
|
118
|
+
end
|
119
|
+
|
120
|
+
# [label]: destination 'title'
|
121
|
+
# ^^^^^^^ parse this
|
122
|
+
res = parseLinkTitle(str, pos, max)
|
123
|
+
if (pos < max && start != pos && res[:ok])
|
124
|
+
title = res[:str]
|
125
|
+
pos = res[:pos]
|
126
|
+
lines += res[:lines]
|
127
|
+
else
|
128
|
+
title = ''
|
129
|
+
pos = destEndPos
|
130
|
+
lines = destEndLineNo
|
131
|
+
end
|
132
|
+
|
133
|
+
# skip trailing spaces until the rest of the line
|
134
|
+
while (pos < max && str.charCodeAt(pos) == 0x20) # space
|
135
|
+
pos += 1
|
136
|
+
end
|
137
|
+
|
138
|
+
if (pos < max && str.charCodeAt(pos) != 0x0A)
|
139
|
+
# garbage at the end of the line
|
140
|
+
return false
|
141
|
+
end
|
142
|
+
|
143
|
+
# Reference can not terminate anything. This check is for safety only.
|
144
|
+
# istanbul ignore if
|
145
|
+
return true if (silent)
|
146
|
+
|
147
|
+
label = normalizeReference(str.slice(1...labelEnd))
|
148
|
+
if (state.env[:references].nil?)
|
149
|
+
state.env[:references] = {}
|
150
|
+
end
|
151
|
+
if state.env[:references][label].nil?
|
152
|
+
state.env[:references][label] = { title: title, href: href }
|
153
|
+
end
|
154
|
+
|
155
|
+
state.line = startLine + lines + 1
|
156
|
+
return true
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|