@depup/markdown-it 14.1.1-depup.1
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.
- package/LICENSE +22 -0
- package/README.md +31 -0
- package/bin/markdown-it.mjs +107 -0
- package/changes.json +10 -0
- package/dist/index.cjs.js +5547 -0
- package/dist/markdown-it.js +6969 -0
- package/dist/markdown-it.min.js +2 -0
- package/index.mjs +1 -0
- package/lib/common/html_blocks.mjs +67 -0
- package/lib/common/html_re.mjs +25 -0
- package/lib/common/utils.mjs +304 -0
- package/lib/helpers/index.mjs +11 -0
- package/lib/helpers/parse_link_destination.mjs +77 -0
- package/lib/helpers/parse_link_label.mjs +49 -0
- package/lib/helpers/parse_link_title.mjs +66 -0
- package/lib/index.mjs +565 -0
- package/lib/parser_block.mjs +134 -0
- package/lib/parser_core.mjs +62 -0
- package/lib/parser_inline.mjs +197 -0
- package/lib/presets/commonmark.mjs +88 -0
- package/lib/presets/default.mjs +47 -0
- package/lib/presets/zero.mjs +70 -0
- package/lib/renderer.mjs +322 -0
- package/lib/ruler.mjs +340 -0
- package/lib/rules_block/blockquote.mjs +209 -0
- package/lib/rules_block/code.mjs +30 -0
- package/lib/rules_block/fence.mjs +94 -0
- package/lib/rules_block/heading.mjs +51 -0
- package/lib/rules_block/hr.mjs +40 -0
- package/lib/rules_block/html_block.mjs +69 -0
- package/lib/rules_block/lheading.mjs +82 -0
- package/lib/rules_block/list.mjs +331 -0
- package/lib/rules_block/paragraph.mjs +46 -0
- package/lib/rules_block/reference.mjs +212 -0
- package/lib/rules_block/state_block.mjs +220 -0
- package/lib/rules_block/table.mjs +228 -0
- package/lib/rules_core/block.mjs +13 -0
- package/lib/rules_core/inline.mjs +11 -0
- package/lib/rules_core/linkify.mjs +134 -0
- package/lib/rules_core/normalize.mjs +17 -0
- package/lib/rules_core/replacements.mjs +101 -0
- package/lib/rules_core/smartquotes.mjs +193 -0
- package/lib/rules_core/state_core.mjs +17 -0
- package/lib/rules_core/text_join.mjs +43 -0
- package/lib/rules_inline/autolink.mjs +72 -0
- package/lib/rules_inline/backticks.mjs +60 -0
- package/lib/rules_inline/balance_pairs.mjs +124 -0
- package/lib/rules_inline/emphasis.mjs +123 -0
- package/lib/rules_inline/entity.mjs +51 -0
- package/lib/rules_inline/escape.mjs +69 -0
- package/lib/rules_inline/fragments_join.mjs +38 -0
- package/lib/rules_inline/html_inline.mjs +50 -0
- package/lib/rules_inline/image.mjs +138 -0
- package/lib/rules_inline/link.mjs +139 -0
- package/lib/rules_inline/linkify.mjs +63 -0
- package/lib/rules_inline/newline.mjs +42 -0
- package/lib/rules_inline/state_inline.mjs +123 -0
- package/lib/rules_inline/strikethrough.mjs +127 -0
- package/lib/rules_inline/text.mjs +86 -0
- package/lib/token.mjs +191 -0
- package/package.json +109 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
// Lists
|
|
2
|
+
|
|
3
|
+
import { isSpace } from '../common/utils.mjs'
|
|
4
|
+
|
|
5
|
+
// Search `[-+*][\n ]`, returns next pos after marker on success
|
|
6
|
+
// or -1 on fail.
|
|
7
|
+
function skipBulletListMarker (state, startLine) {
|
|
8
|
+
const max = state.eMarks[startLine]
|
|
9
|
+
let pos = state.bMarks[startLine] + state.tShift[startLine]
|
|
10
|
+
|
|
11
|
+
const marker = state.src.charCodeAt(pos++)
|
|
12
|
+
// Check bullet
|
|
13
|
+
if (marker !== 0x2A/* * */ &&
|
|
14
|
+
marker !== 0x2D/* - */ &&
|
|
15
|
+
marker !== 0x2B/* + */) {
|
|
16
|
+
return -1
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (pos < max) {
|
|
20
|
+
const ch = state.src.charCodeAt(pos)
|
|
21
|
+
|
|
22
|
+
if (!isSpace(ch)) {
|
|
23
|
+
// " -test " - is not a list item
|
|
24
|
+
return -1
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return pos
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Search `\d+[.)][\n ]`, returns next pos after marker on success
|
|
32
|
+
// or -1 on fail.
|
|
33
|
+
function skipOrderedListMarker (state, startLine) {
|
|
34
|
+
const start = state.bMarks[startLine] + state.tShift[startLine]
|
|
35
|
+
const max = state.eMarks[startLine]
|
|
36
|
+
let pos = start
|
|
37
|
+
|
|
38
|
+
// List marker should have at least 2 chars (digit + dot)
|
|
39
|
+
if (pos + 1 >= max) { return -1 }
|
|
40
|
+
|
|
41
|
+
let ch = state.src.charCodeAt(pos++)
|
|
42
|
+
|
|
43
|
+
if (ch < 0x30/* 0 */ || ch > 0x39/* 9 */) { return -1 }
|
|
44
|
+
|
|
45
|
+
for (;;) {
|
|
46
|
+
// EOL -> fail
|
|
47
|
+
if (pos >= max) { return -1 }
|
|
48
|
+
|
|
49
|
+
ch = state.src.charCodeAt(pos++)
|
|
50
|
+
|
|
51
|
+
if (ch >= 0x30/* 0 */ && ch <= 0x39/* 9 */) {
|
|
52
|
+
// List marker should have no more than 9 digits
|
|
53
|
+
// (prevents integer overflow in browsers)
|
|
54
|
+
if (pos - start >= 10) { return -1 }
|
|
55
|
+
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// found valid marker
|
|
60
|
+
if (ch === 0x29/* ) */ || ch === 0x2e/* . */) {
|
|
61
|
+
break
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return -1
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (pos < max) {
|
|
68
|
+
ch = state.src.charCodeAt(pos)
|
|
69
|
+
|
|
70
|
+
if (!isSpace(ch)) {
|
|
71
|
+
// " 1.test " - is not a list item
|
|
72
|
+
return -1
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return pos
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function markTightParagraphs (state, idx) {
|
|
79
|
+
const level = state.level + 2
|
|
80
|
+
|
|
81
|
+
for (let i = idx + 2, l = state.tokens.length - 2; i < l; i++) {
|
|
82
|
+
if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') {
|
|
83
|
+
state.tokens[i + 2].hidden = true
|
|
84
|
+
state.tokens[i].hidden = true
|
|
85
|
+
i += 2
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default function list (state, startLine, endLine, silent) {
|
|
91
|
+
let max, pos, start, token
|
|
92
|
+
let nextLine = startLine
|
|
93
|
+
let tight = true
|
|
94
|
+
|
|
95
|
+
// if it's indented more than 3 spaces, it should be a code block
|
|
96
|
+
if (state.sCount[nextLine] - state.blkIndent >= 4) { return false }
|
|
97
|
+
|
|
98
|
+
// Special case:
|
|
99
|
+
// - item 1
|
|
100
|
+
// - item 2
|
|
101
|
+
// - item 3
|
|
102
|
+
// - item 4
|
|
103
|
+
// - this one is a paragraph continuation
|
|
104
|
+
if (state.listIndent >= 0 &&
|
|
105
|
+
state.sCount[nextLine] - state.listIndent >= 4 &&
|
|
106
|
+
state.sCount[nextLine] < state.blkIndent) {
|
|
107
|
+
return false
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let isTerminatingParagraph = false
|
|
111
|
+
|
|
112
|
+
// limit conditions when list can interrupt
|
|
113
|
+
// a paragraph (validation mode only)
|
|
114
|
+
if (silent && state.parentType === 'paragraph') {
|
|
115
|
+
// Next list item should still terminate previous list item;
|
|
116
|
+
//
|
|
117
|
+
// This code can fail if plugins use blkIndent as well as lists,
|
|
118
|
+
// but I hope the spec gets fixed long before that happens.
|
|
119
|
+
//
|
|
120
|
+
if (state.sCount[nextLine] >= state.blkIndent) {
|
|
121
|
+
isTerminatingParagraph = true
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Detect list type and position after marker
|
|
126
|
+
let isOrdered
|
|
127
|
+
let markerValue
|
|
128
|
+
let posAfterMarker
|
|
129
|
+
if ((posAfterMarker = skipOrderedListMarker(state, nextLine)) >= 0) {
|
|
130
|
+
isOrdered = true
|
|
131
|
+
start = state.bMarks[nextLine] + state.tShift[nextLine]
|
|
132
|
+
markerValue = Number(state.src.slice(start, posAfterMarker - 1))
|
|
133
|
+
|
|
134
|
+
// If we're starting a new ordered list right after
|
|
135
|
+
// a paragraph, it should start with 1.
|
|
136
|
+
if (isTerminatingParagraph && markerValue !== 1) return false
|
|
137
|
+
} else if ((posAfterMarker = skipBulletListMarker(state, nextLine)) >= 0) {
|
|
138
|
+
isOrdered = false
|
|
139
|
+
} else {
|
|
140
|
+
return false
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// If we're starting a new unordered list right after
|
|
144
|
+
// a paragraph, first line should not be empty.
|
|
145
|
+
if (isTerminatingParagraph) {
|
|
146
|
+
if (state.skipSpaces(posAfterMarker) >= state.eMarks[nextLine]) return false
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// For validation mode we can terminate immediately
|
|
150
|
+
if (silent) { return true }
|
|
151
|
+
|
|
152
|
+
// We should terminate list on style change. Remember first one to compare.
|
|
153
|
+
const markerCharCode = state.src.charCodeAt(posAfterMarker - 1)
|
|
154
|
+
|
|
155
|
+
// Start list
|
|
156
|
+
const listTokIdx = state.tokens.length
|
|
157
|
+
|
|
158
|
+
if (isOrdered) {
|
|
159
|
+
token = state.push('ordered_list_open', 'ol', 1)
|
|
160
|
+
if (markerValue !== 1) {
|
|
161
|
+
token.attrs = [['start', markerValue]]
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
token = state.push('bullet_list_open', 'ul', 1)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const listLines = [nextLine, 0]
|
|
168
|
+
token.map = listLines
|
|
169
|
+
token.markup = String.fromCharCode(markerCharCode)
|
|
170
|
+
|
|
171
|
+
//
|
|
172
|
+
// Iterate list items
|
|
173
|
+
//
|
|
174
|
+
|
|
175
|
+
let prevEmptyEnd = false
|
|
176
|
+
const terminatorRules = state.md.block.ruler.getRules('list')
|
|
177
|
+
|
|
178
|
+
const oldParentType = state.parentType
|
|
179
|
+
state.parentType = 'list'
|
|
180
|
+
|
|
181
|
+
while (nextLine < endLine) {
|
|
182
|
+
pos = posAfterMarker
|
|
183
|
+
max = state.eMarks[nextLine]
|
|
184
|
+
|
|
185
|
+
const initial = state.sCount[nextLine] + posAfterMarker - (state.bMarks[nextLine] + state.tShift[nextLine])
|
|
186
|
+
let offset = initial
|
|
187
|
+
|
|
188
|
+
while (pos < max) {
|
|
189
|
+
const ch = state.src.charCodeAt(pos)
|
|
190
|
+
|
|
191
|
+
if (ch === 0x09) {
|
|
192
|
+
offset += 4 - (offset + state.bsCount[nextLine]) % 4
|
|
193
|
+
} else if (ch === 0x20) {
|
|
194
|
+
offset++
|
|
195
|
+
} else {
|
|
196
|
+
break
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
pos++
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const contentStart = pos
|
|
203
|
+
let indentAfterMarker
|
|
204
|
+
|
|
205
|
+
if (contentStart >= max) {
|
|
206
|
+
// trimming space in "- \n 3" case, indent is 1 here
|
|
207
|
+
indentAfterMarker = 1
|
|
208
|
+
} else {
|
|
209
|
+
indentAfterMarker = offset - initial
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// If we have more than 4 spaces, the indent is 1
|
|
213
|
+
// (the rest is just indented code block)
|
|
214
|
+
if (indentAfterMarker > 4) { indentAfterMarker = 1 }
|
|
215
|
+
|
|
216
|
+
// " - test"
|
|
217
|
+
// ^^^^^ - calculating total length of this thing
|
|
218
|
+
const indent = initial + indentAfterMarker
|
|
219
|
+
|
|
220
|
+
// Run subparser & write tokens
|
|
221
|
+
token = state.push('list_item_open', 'li', 1)
|
|
222
|
+
token.markup = String.fromCharCode(markerCharCode)
|
|
223
|
+
const itemLines = [nextLine, 0]
|
|
224
|
+
token.map = itemLines
|
|
225
|
+
if (isOrdered) {
|
|
226
|
+
token.info = state.src.slice(start, posAfterMarker - 1)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// change current state, then restore it after parser subcall
|
|
230
|
+
const oldTight = state.tight
|
|
231
|
+
const oldTShift = state.tShift[nextLine]
|
|
232
|
+
const oldSCount = state.sCount[nextLine]
|
|
233
|
+
|
|
234
|
+
// - example list
|
|
235
|
+
// ^ listIndent position will be here
|
|
236
|
+
// ^ blkIndent position will be here
|
|
237
|
+
//
|
|
238
|
+
const oldListIndent = state.listIndent
|
|
239
|
+
state.listIndent = state.blkIndent
|
|
240
|
+
state.blkIndent = indent
|
|
241
|
+
|
|
242
|
+
state.tight = true
|
|
243
|
+
state.tShift[nextLine] = contentStart - state.bMarks[nextLine]
|
|
244
|
+
state.sCount[nextLine] = offset
|
|
245
|
+
|
|
246
|
+
if (contentStart >= max && state.isEmpty(nextLine + 1)) {
|
|
247
|
+
// workaround for this case
|
|
248
|
+
// (list item is empty, list terminates before "foo"):
|
|
249
|
+
// ~~~~~~~~
|
|
250
|
+
// -
|
|
251
|
+
//
|
|
252
|
+
// foo
|
|
253
|
+
// ~~~~~~~~
|
|
254
|
+
state.line = Math.min(state.line + 2, endLine)
|
|
255
|
+
} else {
|
|
256
|
+
state.md.block.tokenize(state, nextLine, endLine, true)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// If any of list item is tight, mark list as tight
|
|
260
|
+
if (!state.tight || prevEmptyEnd) {
|
|
261
|
+
tight = false
|
|
262
|
+
}
|
|
263
|
+
// Item become loose if finish with empty line,
|
|
264
|
+
// but we should filter last element, because it means list finish
|
|
265
|
+
prevEmptyEnd = (state.line - nextLine) > 1 && state.isEmpty(state.line - 1)
|
|
266
|
+
|
|
267
|
+
state.blkIndent = state.listIndent
|
|
268
|
+
state.listIndent = oldListIndent
|
|
269
|
+
state.tShift[nextLine] = oldTShift
|
|
270
|
+
state.sCount[nextLine] = oldSCount
|
|
271
|
+
state.tight = oldTight
|
|
272
|
+
|
|
273
|
+
token = state.push('list_item_close', 'li', -1)
|
|
274
|
+
token.markup = String.fromCharCode(markerCharCode)
|
|
275
|
+
|
|
276
|
+
nextLine = state.line
|
|
277
|
+
itemLines[1] = nextLine
|
|
278
|
+
|
|
279
|
+
if (nextLine >= endLine) { break }
|
|
280
|
+
|
|
281
|
+
//
|
|
282
|
+
// Try to check if list is terminated or continued.
|
|
283
|
+
//
|
|
284
|
+
if (state.sCount[nextLine] < state.blkIndent) { break }
|
|
285
|
+
|
|
286
|
+
// if it's indented more than 3 spaces, it should be a code block
|
|
287
|
+
if (state.sCount[nextLine] - state.blkIndent >= 4) { break }
|
|
288
|
+
|
|
289
|
+
// fail if terminating block found
|
|
290
|
+
let terminate = false
|
|
291
|
+
for (let i = 0, l = terminatorRules.length; i < l; i++) {
|
|
292
|
+
if (terminatorRules[i](state, nextLine, endLine, true)) {
|
|
293
|
+
terminate = true
|
|
294
|
+
break
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (terminate) { break }
|
|
298
|
+
|
|
299
|
+
// fail if list has another type
|
|
300
|
+
if (isOrdered) {
|
|
301
|
+
posAfterMarker = skipOrderedListMarker(state, nextLine)
|
|
302
|
+
if (posAfterMarker < 0) { break }
|
|
303
|
+
start = state.bMarks[nextLine] + state.tShift[nextLine]
|
|
304
|
+
} else {
|
|
305
|
+
posAfterMarker = skipBulletListMarker(state, nextLine)
|
|
306
|
+
if (posAfterMarker < 0) { break }
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (markerCharCode !== state.src.charCodeAt(posAfterMarker - 1)) { break }
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Finalize list
|
|
313
|
+
if (isOrdered) {
|
|
314
|
+
token = state.push('ordered_list_close', 'ol', -1)
|
|
315
|
+
} else {
|
|
316
|
+
token = state.push('bullet_list_close', 'ul', -1)
|
|
317
|
+
}
|
|
318
|
+
token.markup = String.fromCharCode(markerCharCode)
|
|
319
|
+
|
|
320
|
+
listLines[1] = nextLine
|
|
321
|
+
state.line = nextLine
|
|
322
|
+
|
|
323
|
+
state.parentType = oldParentType
|
|
324
|
+
|
|
325
|
+
// mark paragraphs tight if needed
|
|
326
|
+
if (tight) {
|
|
327
|
+
markTightParagraphs(state, listTokIdx)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return true
|
|
331
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Paragraph
|
|
2
|
+
|
|
3
|
+
export default function paragraph (state, startLine, endLine) {
|
|
4
|
+
const terminatorRules = state.md.block.ruler.getRules('paragraph')
|
|
5
|
+
const oldParentType = state.parentType
|
|
6
|
+
let nextLine = startLine + 1
|
|
7
|
+
state.parentType = 'paragraph'
|
|
8
|
+
|
|
9
|
+
// jump line-by-line until empty one or EOF
|
|
10
|
+
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
|
|
11
|
+
// this would be a code block normally, but after paragraph
|
|
12
|
+
// it's considered a lazy continuation regardless of what's there
|
|
13
|
+
if (state.sCount[nextLine] - state.blkIndent > 3) { continue }
|
|
14
|
+
|
|
15
|
+
// quirk for blockquotes, this line should already be checked by that rule
|
|
16
|
+
if (state.sCount[nextLine] < 0) { continue }
|
|
17
|
+
|
|
18
|
+
// Some tags can terminate paragraph without empty line.
|
|
19
|
+
let terminate = false
|
|
20
|
+
for (let i = 0, l = terminatorRules.length; i < l; i++) {
|
|
21
|
+
if (terminatorRules[i](state, nextLine, endLine, true)) {
|
|
22
|
+
terminate = true
|
|
23
|
+
break
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (terminate) { break }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const content = state.getLines(startLine, nextLine, state.blkIndent, false).trim()
|
|
30
|
+
|
|
31
|
+
state.line = nextLine
|
|
32
|
+
|
|
33
|
+
const token_o = state.push('paragraph_open', 'p', 1)
|
|
34
|
+
token_o.map = [startLine, state.line]
|
|
35
|
+
|
|
36
|
+
const token_i = state.push('inline', '', 0)
|
|
37
|
+
token_i.content = content
|
|
38
|
+
token_i.map = [startLine, state.line]
|
|
39
|
+
token_i.children = []
|
|
40
|
+
|
|
41
|
+
state.push('paragraph_close', 'p', -1)
|
|
42
|
+
|
|
43
|
+
state.parentType = oldParentType
|
|
44
|
+
|
|
45
|
+
return true
|
|
46
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { isSpace, normalizeReference } from '../common/utils.mjs'
|
|
2
|
+
|
|
3
|
+
export default function reference (state, startLine, _endLine, silent) {
|
|
4
|
+
let pos = state.bMarks[startLine] + state.tShift[startLine]
|
|
5
|
+
let max = state.eMarks[startLine]
|
|
6
|
+
let nextLine = startLine + 1
|
|
7
|
+
|
|
8
|
+
// if it's indented more than 3 spaces, it should be a code block
|
|
9
|
+
if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
|
|
10
|
+
|
|
11
|
+
if (state.src.charCodeAt(pos) !== 0x5B/* [ */) { return false }
|
|
12
|
+
|
|
13
|
+
function getNextLine (nextLine) {
|
|
14
|
+
const endLine = state.lineMax
|
|
15
|
+
|
|
16
|
+
if (nextLine >= endLine || state.isEmpty(nextLine)) {
|
|
17
|
+
// empty line or end of input
|
|
18
|
+
return null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let isContinuation = false
|
|
22
|
+
|
|
23
|
+
// this would be a code block normally, but after paragraph
|
|
24
|
+
// it's considered a lazy continuation regardless of what's there
|
|
25
|
+
if (state.sCount[nextLine] - state.blkIndent > 3) { isContinuation = true }
|
|
26
|
+
|
|
27
|
+
// quirk for blockquotes, this line should already be checked by that rule
|
|
28
|
+
if (state.sCount[nextLine] < 0) { isContinuation = true }
|
|
29
|
+
|
|
30
|
+
if (!isContinuation) {
|
|
31
|
+
const terminatorRules = state.md.block.ruler.getRules('reference')
|
|
32
|
+
const oldParentType = state.parentType
|
|
33
|
+
state.parentType = 'reference'
|
|
34
|
+
|
|
35
|
+
// Some tags can terminate paragraph without empty line.
|
|
36
|
+
let terminate = false
|
|
37
|
+
for (let i = 0, l = terminatorRules.length; i < l; i++) {
|
|
38
|
+
if (terminatorRules[i](state, nextLine, endLine, true)) {
|
|
39
|
+
terminate = true
|
|
40
|
+
break
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
state.parentType = oldParentType
|
|
45
|
+
if (terminate) {
|
|
46
|
+
// terminated by another block
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const pos = state.bMarks[nextLine] + state.tShift[nextLine]
|
|
52
|
+
const max = state.eMarks[nextLine]
|
|
53
|
+
|
|
54
|
+
// max + 1 explicitly includes the newline
|
|
55
|
+
return state.src.slice(pos, max + 1)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let str = state.src.slice(pos, max + 1)
|
|
59
|
+
|
|
60
|
+
max = str.length
|
|
61
|
+
let labelEnd = -1
|
|
62
|
+
|
|
63
|
+
for (pos = 1; pos < max; pos++) {
|
|
64
|
+
const ch = str.charCodeAt(pos)
|
|
65
|
+
if (ch === 0x5B /* [ */) {
|
|
66
|
+
return false
|
|
67
|
+
} else if (ch === 0x5D /* ] */) {
|
|
68
|
+
labelEnd = pos
|
|
69
|
+
break
|
|
70
|
+
} else if (ch === 0x0A /* \n */) {
|
|
71
|
+
const lineContent = getNextLine(nextLine)
|
|
72
|
+
if (lineContent !== null) {
|
|
73
|
+
str += lineContent
|
|
74
|
+
max = str.length
|
|
75
|
+
nextLine++
|
|
76
|
+
}
|
|
77
|
+
} else if (ch === 0x5C /* \ */) {
|
|
78
|
+
pos++
|
|
79
|
+
if (pos < max && str.charCodeAt(pos) === 0x0A) {
|
|
80
|
+
const lineContent = getNextLine(nextLine)
|
|
81
|
+
if (lineContent !== null) {
|
|
82
|
+
str += lineContent
|
|
83
|
+
max = str.length
|
|
84
|
+
nextLine++
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3A/* : */) { return false }
|
|
91
|
+
|
|
92
|
+
// [label]: destination 'title'
|
|
93
|
+
// ^^^ skip optional whitespace here
|
|
94
|
+
for (pos = labelEnd + 2; pos < max; pos++) {
|
|
95
|
+
const ch = str.charCodeAt(pos)
|
|
96
|
+
if (ch === 0x0A) {
|
|
97
|
+
const lineContent = getNextLine(nextLine)
|
|
98
|
+
if (lineContent !== null) {
|
|
99
|
+
str += lineContent
|
|
100
|
+
max = str.length
|
|
101
|
+
nextLine++
|
|
102
|
+
}
|
|
103
|
+
} else if (isSpace(ch)) {
|
|
104
|
+
/* eslint no-empty:0 */
|
|
105
|
+
} else {
|
|
106
|
+
break
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// [label]: destination 'title'
|
|
111
|
+
// ^^^^^^^^^^^ parse this
|
|
112
|
+
const destRes = state.md.helpers.parseLinkDestination(str, pos, max)
|
|
113
|
+
if (!destRes.ok) { return false }
|
|
114
|
+
|
|
115
|
+
const href = state.md.normalizeLink(destRes.str)
|
|
116
|
+
if (!state.md.validateLink(href)) { return false }
|
|
117
|
+
|
|
118
|
+
pos = destRes.pos
|
|
119
|
+
|
|
120
|
+
// save cursor state, we could require to rollback later
|
|
121
|
+
const destEndPos = pos
|
|
122
|
+
const destEndLineNo = nextLine
|
|
123
|
+
|
|
124
|
+
// [label]: destination 'title'
|
|
125
|
+
// ^^^ skipping those spaces
|
|
126
|
+
const start = pos
|
|
127
|
+
for (; pos < max; pos++) {
|
|
128
|
+
const ch = str.charCodeAt(pos)
|
|
129
|
+
if (ch === 0x0A) {
|
|
130
|
+
const lineContent = getNextLine(nextLine)
|
|
131
|
+
if (lineContent !== null) {
|
|
132
|
+
str += lineContent
|
|
133
|
+
max = str.length
|
|
134
|
+
nextLine++
|
|
135
|
+
}
|
|
136
|
+
} else if (isSpace(ch)) {
|
|
137
|
+
/* eslint no-empty:0 */
|
|
138
|
+
} else {
|
|
139
|
+
break
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// [label]: destination 'title'
|
|
144
|
+
// ^^^^^^^ parse this
|
|
145
|
+
let titleRes = state.md.helpers.parseLinkTitle(str, pos, max)
|
|
146
|
+
while (titleRes.can_continue) {
|
|
147
|
+
const lineContent = getNextLine(nextLine)
|
|
148
|
+
if (lineContent === null) break
|
|
149
|
+
str += lineContent
|
|
150
|
+
pos = max
|
|
151
|
+
max = str.length
|
|
152
|
+
nextLine++
|
|
153
|
+
titleRes = state.md.helpers.parseLinkTitle(str, pos, max, titleRes)
|
|
154
|
+
}
|
|
155
|
+
let title
|
|
156
|
+
|
|
157
|
+
if (pos < max && start !== pos && titleRes.ok) {
|
|
158
|
+
title = titleRes.str
|
|
159
|
+
pos = titleRes.pos
|
|
160
|
+
} else {
|
|
161
|
+
title = ''
|
|
162
|
+
pos = destEndPos
|
|
163
|
+
nextLine = destEndLineNo
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// skip trailing spaces until the rest of the line
|
|
167
|
+
while (pos < max) {
|
|
168
|
+
const ch = str.charCodeAt(pos)
|
|
169
|
+
if (!isSpace(ch)) { break }
|
|
170
|
+
pos++
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (pos < max && str.charCodeAt(pos) !== 0x0A) {
|
|
174
|
+
if (title) {
|
|
175
|
+
// garbage at the end of the line after title,
|
|
176
|
+
// but it could still be a valid reference if we roll back
|
|
177
|
+
title = ''
|
|
178
|
+
pos = destEndPos
|
|
179
|
+
nextLine = destEndLineNo
|
|
180
|
+
while (pos < max) {
|
|
181
|
+
const ch = str.charCodeAt(pos)
|
|
182
|
+
if (!isSpace(ch)) { break }
|
|
183
|
+
pos++
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (pos < max && str.charCodeAt(pos) !== 0x0A) {
|
|
189
|
+
// garbage at the end of the line
|
|
190
|
+
return false
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const label = normalizeReference(str.slice(1, labelEnd))
|
|
194
|
+
if (!label) {
|
|
195
|
+
// CommonMark 0.20 disallows empty labels
|
|
196
|
+
return false
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Reference can not terminate anything. This check is for safety only.
|
|
200
|
+
/* istanbul ignore if */
|
|
201
|
+
if (silent) { return true }
|
|
202
|
+
|
|
203
|
+
if (typeof state.env.references === 'undefined') {
|
|
204
|
+
state.env.references = {}
|
|
205
|
+
}
|
|
206
|
+
if (typeof state.env.references[label] === 'undefined') {
|
|
207
|
+
state.env.references[label] = { title, href }
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
state.line = nextLine
|
|
211
|
+
return true
|
|
212
|
+
}
|