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,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
|