samemark 0.0.0-stage → 0.1.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.
- package/CHANGELOG.md +13 -0
- package/KNOWN_GAPS.md +84 -0
- package/LICENSE +21 -0
- package/README.md +676 -2
- package/core.d.ts +10 -0
- package/core.js +10 -0
- package/dist/LICENSES.txt +217 -0
- package/dist/core.cjs +7178 -0
- package/dist/stream.cjs +6967 -0
- package/examples/react-chat/StreamMarkdown.jsx +67 -0
- package/index.d.ts +10 -0
- package/index.js +14 -0
- package/lib/api.js +155 -0
- package/lib/probe.js +109 -0
- package/lib/rows.js +47 -0
- package/lib/signature.js +77 -0
- package/lib/types.d.ts +46 -0
- package/lib/verify.js +38 -0
- package/package.json +140 -4
- package/src/block.js +1807 -0
- package/src/inline.js +1597 -0
- package/src/stream.js +178 -0
- package/stream.d.ts +26 -0
- package/stream.js +7 -0
package/src/block.js
ADDED
|
@@ -0,0 +1,1807 @@
|
|
|
1
|
+
// Hand-written CommonMark + GFM block parser producing mdast (block structure only).
|
|
2
|
+
// Phrasing containers (paragraph, heading, tableCell) get children: [].
|
|
3
|
+
import {normalizeIdentifier} from 'micromark-util-normalize-identifier'
|
|
4
|
+
import {decodeString} from 'micromark-util-decode-string'
|
|
5
|
+
import {htmlBlockNames} from 'micromark-util-html-tag-name'
|
|
6
|
+
import {inlineSingle, inlineMulti, setSource, setDefinitions, setMath, setTrackDefs, takeNeeds} from './inline.js'
|
|
7
|
+
|
|
8
|
+
const BLOCK_NAMES = new Set(htmlBlockNames)
|
|
9
|
+
|
|
10
|
+
const ROOT = 0, BQ = 1, LIST = 2, ITEM = 3, FN = 4
|
|
11
|
+
const NONE = 0, PARA = 1, ICODE = 2, FCODE = 3, HTML = 4, TABLE = 5
|
|
12
|
+
|
|
13
|
+
let src = '', len = 0, hasCR = false
|
|
14
|
+
let ls = 0, le = 0, eolLen = 0, lineNo = 0
|
|
15
|
+
let pos = 0, col = 0, vleft = 0
|
|
16
|
+
let stk = [], sp = 0
|
|
17
|
+
let ns = 0, indent = 0, blank = false
|
|
18
|
+
let e1Line = 1, e1Ls = 0, e1Off = 0
|
|
19
|
+
let lineTok = false, lineCont = false, lastBq = 0, bqIdx = 0
|
|
20
|
+
let e2Line = 1, e2Ls = 0, e2Off = 0, ebLine = 0, ebLs = 0, ebOff = 0, ebBi = 0
|
|
21
|
+
let pendingBlank = false, pbCi = 0, pbBq = false
|
|
22
|
+
let failKind = -1
|
|
23
|
+
let root = null
|
|
24
|
+
|
|
25
|
+
// leaf state
|
|
26
|
+
let leaf = NONE
|
|
27
|
+
// paragraph
|
|
28
|
+
let pL = 0, pLs = 0, pOff = 0, eL = 0, eLs = 0, eOff = 0, pCand = false
|
|
29
|
+
let pLines = [], pPos = [], pVl = []
|
|
30
|
+
// phrasing containers whose children are filled in after the block pass (definitions may follow use)
|
|
31
|
+
let emptyItemLine = -1
|
|
32
|
+
// streaming bookkeeping (parseStream)
|
|
33
|
+
let streamOn = false, streamSafe = null, streamEntries = []
|
|
34
|
+
const defLog = [], fnLog = [], rcLog = []
|
|
35
|
+
let initDefs = null, initFns = null
|
|
36
|
+
function topNow() { return root.children.length - 1 }
|
|
37
|
+
let mathOn = false
|
|
38
|
+
// indented code that started on a line that closed containers (micromark: its flow is not `interrupt`ible)
|
|
39
|
+
let icClosed = false
|
|
40
|
+
let pend = [], defIds = new Set(), fnIds = new Set(), doInline = true
|
|
41
|
+
// code / html
|
|
42
|
+
let cL = 0, cLs = 0, cOff = 0, cVal = '', cN = 0, cLastL = 0, cLastLs = 0, cLastLe = 0, cEol = ''
|
|
43
|
+
let fEolCR = false
|
|
44
|
+
let fch = 0, flen = 0, findent = 0, flang = null, fmeta = null
|
|
45
|
+
let hkind = 0
|
|
46
|
+
let icPend = ''
|
|
47
|
+
// table
|
|
48
|
+
let tNode = null, tState = 0, tSize = 0
|
|
49
|
+
|
|
50
|
+
function P(line, lsv, off) {
|
|
51
|
+
return {line, column: off - lsv + 1, offset: off}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function cont(kind, node) {
|
|
55
|
+
return {kind, node, cc: 0, sb: false, fb: false, ord: false, mk: 0}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ---- cursor helpers ----
|
|
59
|
+
function findNS() {
|
|
60
|
+
let i = pos
|
|
61
|
+
let c = col + vleft
|
|
62
|
+
const base = col
|
|
63
|
+
while (i < le) {
|
|
64
|
+
const ch = src.charCodeAt(i)
|
|
65
|
+
if (ch === 32) { i++; c++ } else if (ch === 9) { c += 4 - (c & 3); i++ } else break
|
|
66
|
+
}
|
|
67
|
+
ns = i
|
|
68
|
+
indent = c - base
|
|
69
|
+
blank = i >= le
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// like findNS but stops once `lim` columns of whitespace were seen (keeps per-container
|
|
73
|
+
// matching O(consumed columns) instead of rescanning the whole indentation run).
|
|
74
|
+
// When truncated, `blank` is reported false even if only whitespace follows.
|
|
75
|
+
function findNSL(lim) {
|
|
76
|
+
let i = pos
|
|
77
|
+
let c = col + vleft
|
|
78
|
+
const base = col
|
|
79
|
+
while (i < le && c - base < lim) {
|
|
80
|
+
const ch = src.charCodeAt(i)
|
|
81
|
+
if (ch === 32) { i++; c++ } else if (ch === 9) { c += 4 - (c & 3); i++ } else break
|
|
82
|
+
}
|
|
83
|
+
ns = i
|
|
84
|
+
indent = c - base
|
|
85
|
+
blank = i >= le
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// consume n virtual columns
|
|
89
|
+
function advance(n) {
|
|
90
|
+
while (n > 0) {
|
|
91
|
+
if (vleft > 0) {
|
|
92
|
+
const t = vleft < n ? vleft : n
|
|
93
|
+
vleft -= t; col += t; n -= t
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
if (src.charCodeAt(pos) === 9) {
|
|
97
|
+
const w = 4 - (col & 3)
|
|
98
|
+
pos++
|
|
99
|
+
if (n >= w) { col += w; n -= w } else { vleft = w - n; col += n; n = 0 }
|
|
100
|
+
} else {
|
|
101
|
+
pos++; col++; n--
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function eolStr(at, n) {
|
|
107
|
+
if (n === 1) return src.charCodeAt(at) === 10 ? '\n' : '\r'
|
|
108
|
+
return n === 2 ? '\r\n' : ''
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function restOfLine() {
|
|
112
|
+
return vleft > 0 ? ' '.repeat(vleft) + src.slice(pos, le) : src.slice(pos, le)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ---- container closing ----
|
|
116
|
+
let listSeen = false
|
|
117
|
+
function closeContainers(to) {
|
|
118
|
+
listSeen = false
|
|
119
|
+
while (sp > to) {
|
|
120
|
+
const c = stk[--sp]
|
|
121
|
+
stk[sp] = null
|
|
122
|
+
if (c.kind === ITEM) {
|
|
123
|
+
const ch = c.node.children
|
|
124
|
+
const last = ch.length ? ch[ch.length - 1].type : ''
|
|
125
|
+
// closed right after a container prefix: an item that is empty or ends in a container exits there too
|
|
126
|
+
c.node.position.end = closeOver && (closeFn || last === 'list' || last === 'blockquote' || (e12Line === lineNo && (last === 'code' || last === 'html' || last === 'math'))) ? closeOver() : last === 'list' && e1Line > e2Line && bqRun > 0 ? P(e1Line, e1Ls, e1Off) : pickE2(sp)
|
|
127
|
+
} else {
|
|
128
|
+
c.node.position.end = P(e1Line, e1Ls, e1Off)
|
|
129
|
+
if (c.kind === LIST) { if (bqRun >= 2 && !listSeen) c.node.spread = true; listSeen = true }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function setE1(l, lsv, off) { e1Line = l; e1Ls = lsv; e1Off = off }
|
|
135
|
+
// the last token of an unclosed fence / html block is the code itself: containers end where it ends
|
|
136
|
+
let cRealL = 0, cRealLs = 0, cRealLe = 0
|
|
137
|
+
function setE12(end) {
|
|
138
|
+
if (end.line > cRealL) e12Line = lineNo
|
|
139
|
+
if (cRealLe > end.offset) end = P(cRealL, cRealLs, cRealLe)
|
|
140
|
+
const lsv = end.offset - end.column + 1
|
|
141
|
+
e1Line = end.line; e1Ls = lsv; e1Off = end.offset
|
|
142
|
+
e2Line = end.line; e2Ls = lsv; e2Off = end.offset
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ---- leaf closing ----
|
|
146
|
+
function parentChildren() { return stk[sp - 1].node.children }
|
|
147
|
+
|
|
148
|
+
function closeLeaf(eolIncluded) {
|
|
149
|
+
switch (leaf) {
|
|
150
|
+
case NONE: return
|
|
151
|
+
case PARA: leaf = NONE; finishParagraph(-1); return
|
|
152
|
+
case ICODE: leaf = NONE; icPend = ''; finishICode(); return
|
|
153
|
+
case FCODE: leaf = NONE; finishFCode(false, eolIncluded); return
|
|
154
|
+
case HTML: leaf = NONE; finishHtml(eolIncluded); return
|
|
155
|
+
case TABLE: leaf = NONE; finishTable(); return
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function finishICode() {
|
|
160
|
+
let v = cVal
|
|
161
|
+
const lc = v.charCodeAt(v.length - 1)
|
|
162
|
+
if (lc === 10) v = v.slice(0, v.length - (v.charCodeAt(v.length - 2) === 13 ? 2 : 1))
|
|
163
|
+
else if (lc === 13) v = v.slice(0, -1)
|
|
164
|
+
const node = {
|
|
165
|
+
type: 'code', lang: null, meta: null, value: v,
|
|
166
|
+
position: {start: P(cL, cLs, cOff), end: P(cLastL, cLastLs, cLastLe)}
|
|
167
|
+
}
|
|
168
|
+
parentChildren().push(node)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function endOfCode(eolIncluded, eof) {
|
|
172
|
+
if (eolIncluded && eof) {
|
|
173
|
+
// end of document
|
|
174
|
+
if (eolLenAtEof > 0) return P(lineNo + 1, len, len)
|
|
175
|
+
return P(cLastL, cLastLs, cLastLe)
|
|
176
|
+
}
|
|
177
|
+
if (eolIncluded) return P(lineNo, ls, ls)
|
|
178
|
+
return P(cLastL, cLastLs, cLastLe)
|
|
179
|
+
}
|
|
180
|
+
let eolLenAtEof = 0
|
|
181
|
+
|
|
182
|
+
function finishFCode(closed, eolIncluded, eof) {
|
|
183
|
+
let val = cVal
|
|
184
|
+
let end
|
|
185
|
+
if (closed) {
|
|
186
|
+
end = P(cLastL, cLastLs, cLastLe)
|
|
187
|
+
} else {
|
|
188
|
+
end = endOfCode(eolIncluded, eof)
|
|
189
|
+
if (!eolIncluded) {
|
|
190
|
+
const c = val.charCodeAt(val.length - 1)
|
|
191
|
+
if (c === 10) val = val.slice(0, val.length - (val.charCodeAt(val.length - 2) === 13 ? 2 : 1))
|
|
192
|
+
else if (c === 13) val = val.slice(0, -1)
|
|
193
|
+
}
|
|
194
|
+
setE12(end)
|
|
195
|
+
}
|
|
196
|
+
// micromark strips `^(\r?\n|\r)` from the data: a lone-CR opening EOL followed by an LF-terminated empty first line
|
|
197
|
+
// reads as one CRLF; the same at the end (content ending in CR, then the LF before the closing fence)
|
|
198
|
+
if (fEolCR && val.charCodeAt(0) === 10) val = val.slice(1)
|
|
199
|
+
if (closed && val.charCodeAt(val.length - 1) === 13 && cEol === '\n') val = val.slice(0, -1)
|
|
200
|
+
if (fch === 36) {
|
|
201
|
+
parentChildren().push({
|
|
202
|
+
type: 'math', meta: fmeta, value: val,
|
|
203
|
+
data: {hName: 'pre', hChildren: [{type: 'element', tagName: 'code', properties: {className: ['language-math', 'math-display']}, children: [{type: 'text', value: val}]}]},
|
|
204
|
+
position: {start: P(cL, cLs, cOff), end}
|
|
205
|
+
})
|
|
206
|
+
return
|
|
207
|
+
}
|
|
208
|
+
parentChildren().push({
|
|
209
|
+
type: 'code', lang: flang, meta: fmeta, value: val,
|
|
210
|
+
position: {start: P(cL, cLs, cOff), end}
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function finishHtml(eolIncluded, eof) {
|
|
215
|
+
let val = cVal
|
|
216
|
+
let end
|
|
217
|
+
if (eolIncluded && hkind <= 5) {
|
|
218
|
+
end = endOfCode(true, eof)
|
|
219
|
+
if (end.offset !== cLastLe) {
|
|
220
|
+
val += cEolHtml()
|
|
221
|
+
setE12(end)
|
|
222
|
+
}
|
|
223
|
+
} else end = P(cLastL, cLastLs, cLastLe)
|
|
224
|
+
parentChildren().push({
|
|
225
|
+
type: 'html', value: val,
|
|
226
|
+
position: {start: P(cL, cLs, cOff), end}
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function finishTable() {
|
|
231
|
+
tNode = null
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ---- paragraph / definitions ----
|
|
235
|
+
function isWs(c) { return c === 32 || c === 9 }
|
|
236
|
+
|
|
237
|
+
function paragraphHasContentAfterDefs() {
|
|
238
|
+
if (!pCand) return true
|
|
239
|
+
return finishParagraph(-2)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Is there a ']:' in [from, to)? Cached next-occurrence so the total scan is
|
|
243
|
+
// linear over the document (paragraphs are visited in increasing offset order).
|
|
244
|
+
let dm = -2
|
|
245
|
+
function defMarkerIn(from, to) {
|
|
246
|
+
if (dm === -2 || (dm !== -1 && dm < from)) dm = src.indexOf(']:', from)
|
|
247
|
+
return dm !== -1 && dm < to
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// mode -1: emit; -2: dry-run returning whether paragraph content remains
|
|
251
|
+
function finishParagraph(mode, heading) {
|
|
252
|
+
let startL = pL, startLs = pLs, startOff = pOff
|
|
253
|
+
const out = mode === -2 ? null : parentChildren()
|
|
254
|
+
let remainingFrom = 0 // index of first remaining line in pLines (4 numbers per line)
|
|
255
|
+
const nBefore = out === null ? 0 : out.length
|
|
256
|
+
if (pCand && defMarkerIn(pOff, eOff)) {
|
|
257
|
+
const r = extractDefs(out)
|
|
258
|
+
if (r === -1) { // all consumed
|
|
259
|
+
if (mode === -2) return false
|
|
260
|
+
pLines.length = 0; pPos.length = 0; pVl.length = 0
|
|
261
|
+
return false
|
|
262
|
+
}
|
|
263
|
+
remainingFrom = r
|
|
264
|
+
if (r > 0) {
|
|
265
|
+
startL = pLines[r]; startLs = pLines[r + 1]; startOff = pLines[r + 2]
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (mode === -2) return true
|
|
269
|
+
const posn = {start: P(startL, startLs, startOff), end: undefined}
|
|
270
|
+
let node
|
|
271
|
+
let task = false
|
|
272
|
+
if (heading) {
|
|
273
|
+
posn.start = P(pL, pLs, pOff)
|
|
274
|
+
posn.end = heading.end
|
|
275
|
+
node = {type: 'heading', depth: heading.depth, children: [], position: posn}
|
|
276
|
+
out.push(node)
|
|
277
|
+
} else {
|
|
278
|
+
posn.end = P(eL, eLs, eOff)
|
|
279
|
+
const parent = stk[sp - 1]
|
|
280
|
+
// extra whitespace before the content on a later line is a linePrefix token: micromark then does not
|
|
281
|
+
// treat the text as the first content of the item
|
|
282
|
+
const fi = remainingFrom >> 2
|
|
283
|
+
const prefixed = parent.kind === ITEM && startL > parent.node.position.start.line && (startOff > pPos[fi] || pVl[fi] > 0)
|
|
284
|
+
// (definitions at the start of the same content do not count: the text after them still starts the chunk)
|
|
285
|
+
if (parent.kind === ITEM && nBefore === 0 && src.charCodeAt(startOff) === 91 && !prefixed) {
|
|
286
|
+
const t = taskCheck(startOff, startLs)
|
|
287
|
+
if (t !== 0) {
|
|
288
|
+
parent.node.checked = t === 1
|
|
289
|
+
task = taskLenOut === 4 ? 11 : 1
|
|
290
|
+
} else task = 3 // candidate: micromark attempts a task check here (matters for EOL positions)
|
|
291
|
+
} else if (startL === emptyItemLine + 1 && src.charCodeAt(startOff) === 91 && startOff === pPos[fi] && pVl[fi] === 0 &&
|
|
292
|
+
!(parent.node.position && parent.node.position.start.line === startL) && !(parent.kind === ITEM && parent.node.position.start.line === emptyItemLine)) {
|
|
293
|
+
// micromark quirk: right after an empty list item (marker directly followed by the EOL) the next
|
|
294
|
+
// paragraph is tokenised as if it were the first content of that item; `checked` lands on its parent
|
|
295
|
+
const t = taskCheck(startOff, startLs)
|
|
296
|
+
if (t !== 0) {
|
|
297
|
+
parent.node.checked = t === 1
|
|
298
|
+
if (streamOn && parent.kind === ROOT) rcLog.push(root.children.length, t === 1)
|
|
299
|
+
task = taskLenOut === 4 ? 12 : 2
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
node = {type: 'paragraph', children: [], position: posn}
|
|
303
|
+
out.push(node)
|
|
304
|
+
}
|
|
305
|
+
if (doInline) {
|
|
306
|
+
const nl = (pLines.length - remainingFrom) >> 2
|
|
307
|
+
if (nl === 1) {
|
|
308
|
+
pend.push(node, task, pLines[remainingFrom], pLines[remainingFrom + 1], pLines[remainingFrom + 2], pLines[remainingFrom + 3], null, topNow())
|
|
309
|
+
} else {
|
|
310
|
+
const lines = new Array(nl * 5)
|
|
311
|
+
for (let i = 0; i < nl; i++) {
|
|
312
|
+
const a = remainingFrom + i * 4
|
|
313
|
+
lines[i * 5] = pLines[a]; lines[i * 5 + 1] = pLines[a + 1]
|
|
314
|
+
lines[i * 5 + 2] = i === 0 ? pLines[a + 2] : pPos[(a >> 2)]
|
|
315
|
+
lines[i * 5 + 3] = pLines[a + 3]
|
|
316
|
+
lines[i * 5 + 4] = i === 0 ? 0 : pVl[(a >> 2)]
|
|
317
|
+
}
|
|
318
|
+
pend.push(node, task, 0, 0, 0, 0, lines, topNow())
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
pLines.length = 0; pPos.length = 0; pVl.length = 0
|
|
322
|
+
return true
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// returns 0 none, 1 checked, 2 unchecked
|
|
326
|
+
let taskLenOut = 3
|
|
327
|
+
function taskCheck(o, ls0) {
|
|
328
|
+
const c1 = src.charCodeAt(o + 1)
|
|
329
|
+
let checked
|
|
330
|
+
taskLenOut = 3
|
|
331
|
+
if (c1 === 120 || c1 === 88) checked = 1
|
|
332
|
+
else if (c1 === 32 || c1 === 10 || c1 === 13) checked = 2
|
|
333
|
+
else if (c1 === 9) {
|
|
334
|
+
// a tab as the value is followed by its remaining virtual spaces unless it is one column wide
|
|
335
|
+
let col = 0
|
|
336
|
+
for (let q = ls0; q < o + 1; q++) col += src.charCodeAt(q) === 9 ? 4 - (col & 3) : 1
|
|
337
|
+
if (4 - (col & 3) !== 1) return 0
|
|
338
|
+
checked = 2
|
|
339
|
+
} else return 0
|
|
340
|
+
let close = o + 2
|
|
341
|
+
if (c1 === 13 && src.charCodeAt(o + 2) === 10) { close = o + 3; taskLenOut = 4 }
|
|
342
|
+
if (src.charCodeAt(close) !== 93) return 0
|
|
343
|
+
const q = close + 1 // after ]
|
|
344
|
+
const c = src.charCodeAt(q)
|
|
345
|
+
if (c === 10 || c === 13) return q < eOff ? checked : 0
|
|
346
|
+
if (c !== 32 && c !== 9) return 0
|
|
347
|
+
// whitespace then non-end
|
|
348
|
+
let i = q + 1
|
|
349
|
+
while (i < len) {
|
|
350
|
+
const d = src.charCodeAt(i)
|
|
351
|
+
if (d === 32 || d === 9) i++
|
|
352
|
+
else break
|
|
353
|
+
}
|
|
354
|
+
if (i >= eOff) return 0
|
|
355
|
+
return checked
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// returns index into pLines of first remaining line, or -1 if all consumed
|
|
359
|
+
function extractDefs(out) {
|
|
360
|
+
const n = pLines.length / 4
|
|
361
|
+
// build text
|
|
362
|
+
let text = ''
|
|
363
|
+
const starts = new Array(n)
|
|
364
|
+
for (let i = 0; i < n; i++) {
|
|
365
|
+
starts[i] = text.length
|
|
366
|
+
const a = i === 0 ? pLines[2] : pPos[i] // continuation lines keep their indentation (label text)
|
|
367
|
+
text += (i ? '\n' : '') + src.slice(a, pLines[i * 4 + 3])
|
|
368
|
+
starts[i] = i ? text.length - (pLines[i * 4 + 3] - a) : 0
|
|
369
|
+
}
|
|
370
|
+
let li = 0 // current line index
|
|
371
|
+
const tl = text.length
|
|
372
|
+
while (li < n) {
|
|
373
|
+
let from = starts[li]
|
|
374
|
+
while (from < tl && isWs(text.charCodeAt(from))) from++
|
|
375
|
+
const r = parseDef(text, from, tl, starts, n, li)
|
|
376
|
+
if (!r) break
|
|
377
|
+
// r: {endIdx, endLine, label, ident, url, title}
|
|
378
|
+
const el = r.endLine
|
|
379
|
+
if (out) {
|
|
380
|
+
defIds.add(r.idU)
|
|
381
|
+
out.push({
|
|
382
|
+
type: 'definition', identifier: r.ident, label: r.label, title: r.title, url: r.url,
|
|
383
|
+
position: {
|
|
384
|
+
start: P(pLines[li * 4], pLines[li * 4 + 1], pLines[li * 4 + 2]),
|
|
385
|
+
end: P(pLines[el * 4], pLines[el * 4 + 1], pLines[el * 4 + 3])
|
|
386
|
+
}
|
|
387
|
+
})
|
|
388
|
+
if (streamOn) defLog.push(r.idU, topNow())
|
|
389
|
+
}
|
|
390
|
+
li = el + 1
|
|
391
|
+
}
|
|
392
|
+
if (li >= n) return -1
|
|
393
|
+
return li * 4
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// with CR / CRLF line endings the definition text is joined with '\n'; the mdast values keep the raw EOLs
|
|
397
|
+
function lineOf(starts, n, idx) { let l = 0; while (l + 1 < n && starts[l + 1] <= idx) l++; return l }
|
|
398
|
+
function eolOfLine(L) {
|
|
399
|
+
const e = pLines[L * 4 + 3]
|
|
400
|
+
const c = src.charCodeAt(e)
|
|
401
|
+
return c === 13 ? (src.charCodeAt(e + 1) === 10 ? '\r\n' : '\r') : '\n'
|
|
402
|
+
}
|
|
403
|
+
function rawEols(str, starts, n, fromIdx) {
|
|
404
|
+
let l = lineOf(starts, n, fromIdx), out = '', a = 0
|
|
405
|
+
for (;;) {
|
|
406
|
+
const k = str.indexOf('\n', a)
|
|
407
|
+
if (k === -1) return out + str.slice(a)
|
|
408
|
+
out += str.slice(a, k) + eolOfLine(l++)
|
|
409
|
+
a = k + 1
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
function parseDef(text, from, tl, starts, n, li) {
|
|
413
|
+
let i = from
|
|
414
|
+
if (text.charCodeAt(i) !== 91) return null
|
|
415
|
+
// label
|
|
416
|
+
i++
|
|
417
|
+
const labelStart = i
|
|
418
|
+
let size = 0, hasData = false
|
|
419
|
+
for (;;) {
|
|
420
|
+
if (i >= tl) return null
|
|
421
|
+
const c = text.charCodeAt(i)
|
|
422
|
+
if (c === 91) return null
|
|
423
|
+
if (c === 93) break
|
|
424
|
+
if (size > 999) return null
|
|
425
|
+
if (c !== 32 && c !== 9 && c !== 10) hasData = true
|
|
426
|
+
size++
|
|
427
|
+
i++
|
|
428
|
+
if (c === 92) {
|
|
429
|
+
const d = text.charCodeAt(i)
|
|
430
|
+
if (d === 91 || d === 92 || d === 93) { i++; size++ }
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (!hasData) return null
|
|
434
|
+
const rawLabel = text.slice(labelStart, i)
|
|
435
|
+
i++
|
|
436
|
+
if (text.charCodeAt(i) !== 58) return null
|
|
437
|
+
i++
|
|
438
|
+
// whitespace with optional single eol
|
|
439
|
+
i = skipWsEol(text, i, tl)
|
|
440
|
+
// destination
|
|
441
|
+
let url
|
|
442
|
+
const c0 = text.charCodeAt(i)
|
|
443
|
+
if (c0 === 60) {
|
|
444
|
+
i++
|
|
445
|
+
const s = i
|
|
446
|
+
for (;;) {
|
|
447
|
+
if (i >= tl) return null
|
|
448
|
+
const c = text.charCodeAt(i)
|
|
449
|
+
if (c === 10 || c === 60) return null
|
|
450
|
+
if (c === 62) break
|
|
451
|
+
i++
|
|
452
|
+
if (c === 92) { const d = text.charCodeAt(i); if (d === 60 || d === 62 || d === 92) i++ }
|
|
453
|
+
}
|
|
454
|
+
url = text.slice(s, i)
|
|
455
|
+
i++
|
|
456
|
+
} else {
|
|
457
|
+
const s = i
|
|
458
|
+
let depth = 0
|
|
459
|
+
for (;;) {
|
|
460
|
+
if (i >= tl) break
|
|
461
|
+
const c = text.charCodeAt(i)
|
|
462
|
+
if (c === 40) { if (++depth > 32) return null }
|
|
463
|
+
else if (c === 41) { if (depth === 0) break; depth-- }
|
|
464
|
+
else if (c === 32 || c === 9 || c === 10 || c < 32 || c === 127) break
|
|
465
|
+
else if (c === 92) { const d = text.charCodeAt(i + 1); if (d === 40 || d === 41 || d === 92) i++ }
|
|
466
|
+
i++
|
|
467
|
+
}
|
|
468
|
+
if (depth !== 0) return null
|
|
469
|
+
if (i === s) return null
|
|
470
|
+
url = text.slice(s, i)
|
|
471
|
+
}
|
|
472
|
+
const afterDest = i
|
|
473
|
+
// try title
|
|
474
|
+
let title = null, titleStart = 0
|
|
475
|
+
let endIdx = -1
|
|
476
|
+
{
|
|
477
|
+
let j = afterDest
|
|
478
|
+
const j0 = j
|
|
479
|
+
j = skipWsEol(text, j, tl)
|
|
480
|
+
if (j > j0) {
|
|
481
|
+
const q = text.charCodeAt(j)
|
|
482
|
+
if (q === 34 || q === 39 || q === 40) {
|
|
483
|
+
const close = q === 40 ? 41 : q
|
|
484
|
+
let k = j + 1
|
|
485
|
+
let ok = false
|
|
486
|
+
for (;;) {
|
|
487
|
+
if (k >= tl) break
|
|
488
|
+
const c = text.charCodeAt(k)
|
|
489
|
+
if (c === close) { ok = true; break }
|
|
490
|
+
k++
|
|
491
|
+
if (c === 92) { const d = text.charCodeAt(k); if (d === q || d === close || d === 92) k++ }
|
|
492
|
+
}
|
|
493
|
+
if (ok) {
|
|
494
|
+
let m = k + 1
|
|
495
|
+
while (m < tl && isWs(text.charCodeAt(m))) m++
|
|
496
|
+
if (m >= tl || text.charCodeAt(m) === 10) {
|
|
497
|
+
title = k > j + 1 ? text.slice(j + 1, k) : null
|
|
498
|
+
titleStart = j + 1
|
|
499
|
+
endIdx = m
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (endIdx === -1) {
|
|
506
|
+
let m = afterDest
|
|
507
|
+
while (m < tl && isWs(text.charCodeAt(m))) m++
|
|
508
|
+
if (m >= tl || text.charCodeAt(m) === 10) endIdx = m
|
|
509
|
+
else return null
|
|
510
|
+
}
|
|
511
|
+
// find line of endIdx
|
|
512
|
+
let el = li
|
|
513
|
+
while (el + 1 < n && starts[el + 1] <= endIdx) el++
|
|
514
|
+
let label = rawLabel.indexOf('\\') === -1 && rawLabel.indexOf('&') === -1 ? rawLabel : decodeString(rawLabel)
|
|
515
|
+
if (hasCR && label.indexOf('\n') !== -1) label = rawEols(label, starts, n, labelStart)
|
|
516
|
+
const idU = normalizeIdentifier(rawLabel)
|
|
517
|
+
const ident = idU.toLowerCase()
|
|
518
|
+
if (url.indexOf('\\') !== -1 || url.indexOf('&') !== -1) url = decodeString(url)
|
|
519
|
+
if (title !== null) {
|
|
520
|
+
if (title.indexOf('\n') !== -1) { title = title.replace(/\n[ \t]*/g, '\n'); if (hasCR) title = rawEols(title, starts, n, titleStart) }
|
|
521
|
+
if (title.indexOf('\\') !== -1 || title.indexOf('&') !== -1) title = decodeString(title)
|
|
522
|
+
}
|
|
523
|
+
return {endIdx, endLine: el, label, ident, idU, url, title}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function skipWsEol(text, i, tl) {
|
|
527
|
+
while (i < tl && isWs(text.charCodeAt(i))) i++
|
|
528
|
+
if (text.charCodeAt(i) === 10) {
|
|
529
|
+
i++
|
|
530
|
+
while (i < tl && isWs(text.charCodeAt(i))) i++
|
|
531
|
+
}
|
|
532
|
+
return i
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// ---- main ----
|
|
536
|
+
// remark-frontmatter / micromark-extension-frontmatter options -> [{type, open, close}] (same rules as toMatters)
|
|
537
|
+
const MARKERS = {yaml: '-', toml: '+'}
|
|
538
|
+
export function normalizeMatters(options) {
|
|
539
|
+
const list = Array.isArray(options) ? options : options ? [options] : ['yaml']
|
|
540
|
+
return list.map(o => {
|
|
541
|
+
let m = o
|
|
542
|
+
if (typeof m === 'string') {
|
|
543
|
+
if (!Object.prototype.hasOwnProperty.call(MARKERS, m)) throw new Error('Missing matter definition for `' + m + '`')
|
|
544
|
+
m = {type: m, marker: MARKERS[m]}
|
|
545
|
+
} else if (typeof m !== 'object' || m === null) throw new Error('Expected matter to be an object')
|
|
546
|
+
if (!('type' in m)) throw new Error('Missing `type` in matter')
|
|
547
|
+
const hasFence = 'fence' in m, hasMarker = 'marker' in m
|
|
548
|
+
if (!hasFence && !hasMarker) throw new Error('Missing `marker` or `fence` in matter')
|
|
549
|
+
const pick = (schema, prop) => typeof schema === 'string' ? schema : schema[prop]
|
|
550
|
+
const open = m.marker ? pick(m.marker, 'open').repeat(3) : pick(m.fence, 'open')
|
|
551
|
+
const close = m.marker ? pick(m.marker, 'close').repeat(3) : pick(m.fence, 'close')
|
|
552
|
+
return {type: m.type, open, close, anywhere: Boolean(m.anywhere)}
|
|
553
|
+
})
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// true when samemark can parse these remark-frontmatter / micromark-extension-frontmatter options natively
|
|
557
|
+
// (everything except `anywhere: true`, which needs the real tokenizer)
|
|
558
|
+
export function frontmatterSupported(options) {
|
|
559
|
+
try {
|
|
560
|
+
const ms = normalizeMatters(options)
|
|
561
|
+
return ms.length > 0 && ms.every(m => !m.anywhere && m.open.length > 0 && m.close.length > 0)
|
|
562
|
+
} catch { return false }
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// frontmatter at the very start of the document (micromark: flow construct, line 1 column 1, concrete).
|
|
566
|
+
// Returns true (state advanced to the closing fence line) or false.
|
|
567
|
+
let noContainers = false
|
|
568
|
+
function tryFrontmatter(matters) {
|
|
569
|
+
for (let mi = 0; mi < matters.length; mi++) {
|
|
570
|
+
const m = matters[mi]
|
|
571
|
+
const open = m.open
|
|
572
|
+
if (open.length === 0 || !src.startsWith(open, 0)) continue
|
|
573
|
+
let i = open.length
|
|
574
|
+
while (i < len) { const c = src.charCodeAt(i); if (c === 32 || c === 9) i++; else break }
|
|
575
|
+
if (i >= len) continue
|
|
576
|
+
let c = src.charCodeAt(i)
|
|
577
|
+
if (c !== 10 && c !== 13) continue
|
|
578
|
+
const eol1 = c === 13 && src.charCodeAt(i + 1) === 10 ? 2 : 1
|
|
579
|
+
const bodyStart = i + eol1
|
|
580
|
+
const close = m.close
|
|
581
|
+
// an open fence that never closes is still a pending `concrete` construct while the document is fed:
|
|
582
|
+
// no container (quote, list item, footnote definition) can start on any later line
|
|
583
|
+
noContainers = true
|
|
584
|
+
let pos = bodyStart, line = 2, prevEolLen = eol1
|
|
585
|
+
for (;;) {
|
|
586
|
+
if (pos >= len) break // the line after a trailing EOL is the end of input: not closed
|
|
587
|
+
// end of this line
|
|
588
|
+
let e = pos, eolL = 0
|
|
589
|
+
for (; e < len; e++) {
|
|
590
|
+
const d = src.charCodeAt(e)
|
|
591
|
+
if (d === 10) { eolL = 1; break }
|
|
592
|
+
if (d === 13) { eolL = src.charCodeAt(e + 1) === 10 ? 2 : 1; break }
|
|
593
|
+
}
|
|
594
|
+
// closing fence?
|
|
595
|
+
if (src.startsWith(close, pos)) {
|
|
596
|
+
let j = pos + close.length
|
|
597
|
+
while (j < e) { const d = src.charCodeAt(j); if (d === 32 || d === 9) j++; else break }
|
|
598
|
+
if (j >= e) {
|
|
599
|
+
const value = pos === bodyStart ? '' : src.slice(bodyStart, pos - prevEolLen)
|
|
600
|
+
const node = {type: m.type, value, position: {start: P(1, 0, 0), end: P(line, pos, e)}}
|
|
601
|
+
root.children.push(node)
|
|
602
|
+
lineNo = line; ls = pos; le = e; eolLen = eolL
|
|
603
|
+
lineTok = true; lineCont = true
|
|
604
|
+
setE1(lineNo, ls, le)
|
|
605
|
+
e2Line = lineNo; e2Ls = ls; e2Off = le
|
|
606
|
+
noContainers = false
|
|
607
|
+
return true
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (eolL === 0) break // content line without EOL at the end of input: not closed
|
|
611
|
+
pos = e + eolL; prevEolLen = eolL; line++
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return false
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// a Set-like view over a parse's own ids plus ids from earlier (finished) parts of a stream
|
|
618
|
+
function composite(a, b) {
|
|
619
|
+
return {has: id => a.has(id) || b.has(id), get size() { return a.size + b.size }}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
export function parse(source, opts) {
|
|
623
|
+
doInline = !opts || opts.inline !== false
|
|
624
|
+
mathOn = Boolean(opts && opts.math)
|
|
625
|
+
setMath(mathOn, !opts || !opts.math || opts.math.single !== false)
|
|
626
|
+
streamOn = Boolean(opts && opts.stream)
|
|
627
|
+
if (!(opts && opts.tail)) {
|
|
628
|
+
if (source.charCodeAt(0) === 0xfeff) source = source.slice(1)
|
|
629
|
+
if (source.indexOf('\0') !== -1) source = source.replaceAll('\0', '\uFFFD')
|
|
630
|
+
}
|
|
631
|
+
initDefs = (opts && opts.defs) || null; initFns = (opts && opts.fnDefs) || null
|
|
632
|
+
streamSafe = null; streamEntries = []; defLog.length = 0; fnLog.length = 0; rcLog.length = 0
|
|
633
|
+
src = source; len = source.length
|
|
634
|
+
hasCR = source.indexOf('\r') !== -1
|
|
635
|
+
root = {type: 'root', children: [], position: undefined}
|
|
636
|
+
stk = [cont(ROOT, root)]; sp = 1
|
|
637
|
+
leaf = NONE; pendingBlank = false; pCand = false; pLines.length = 0
|
|
638
|
+
e1Line = 1; e1Ls = 0; e1Off = 0
|
|
639
|
+
e2Line = 1; e2Ls = 0; e2Off = 0; ebLine = 0; ebLs = 0; ebOff = 0; ebBi = 0
|
|
640
|
+
quirkItem = null; lateBlank = false; icPend = ''; cVal = ''; tState = 0; dm = -2
|
|
641
|
+
tNode = null
|
|
642
|
+
pend.length = 0; defIds = new Set(); fnIds = new Set(); pPos.length = 0; emptyItemLine = -1; e12Line = -2; bqRun = 0; lastBqBlank = false
|
|
643
|
+
lineNo = 0
|
|
644
|
+
noContainers = false
|
|
645
|
+
let p = 0
|
|
646
|
+
if (opts && opts.frontmatter) {
|
|
647
|
+
if (opts.frontmatter.some(m => m.anywhere)) throw new Error('samemark: frontmatter `anywhere` is not supported natively')
|
|
648
|
+
if (tryFrontmatter(opts.frontmatter)) p = le + eolLen
|
|
649
|
+
}
|
|
650
|
+
while (p < len) {
|
|
651
|
+
ls = p
|
|
652
|
+
if (!hasCR) {
|
|
653
|
+
le = src.indexOf('\n', p)
|
|
654
|
+
if (le === -1) { le = len; eolLen = 0 } else eolLen = 1
|
|
655
|
+
} else {
|
|
656
|
+
let i = p
|
|
657
|
+
for (;;) {
|
|
658
|
+
if (i >= len) { le = len; eolLen = 0; break }
|
|
659
|
+
const c = src.charCodeAt(i)
|
|
660
|
+
if (c === 10) { le = i; eolLen = 1; break }
|
|
661
|
+
if (c === 13) { le = i; eolLen = src.charCodeAt(i + 1) === 10 ? 2 : 1; break }
|
|
662
|
+
i++
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
lineNo++
|
|
666
|
+
processLine()
|
|
667
|
+
p = le + eolLen
|
|
668
|
+
}
|
|
669
|
+
// EOF
|
|
670
|
+
eolLenAtEof = eolLen
|
|
671
|
+
const endsEol = len > 0 && (src.charCodeAt(len - 1) === 10 || src.charCodeAt(len - 1) === 13)
|
|
672
|
+
eolLenAtEof = endsEol ? 1 : 0
|
|
673
|
+
let hasBq = false
|
|
674
|
+
for (let i = 1; i < sp; i++) if (stk[i].kind === BQ) hasBq = true
|
|
675
|
+
eofClose(!hasBq)
|
|
676
|
+
if (eolLen === 0 && bqLastLine) { closeOver = () => P(e1Line, e1Ls, e1Off); closeContainers(1); closeOver = null } else closeContainers(1)
|
|
677
|
+
let endP
|
|
678
|
+
if (len === 0) endP = P(1, 0, 0)
|
|
679
|
+
else if (endsEol) endP = P(lineNo + 1, len, len)
|
|
680
|
+
else endP = P(lineNo, ls, len)
|
|
681
|
+
root.position = {start: P(1, 0, 0), end: endP}
|
|
682
|
+
if (doInline && pend.length) {
|
|
683
|
+
setSource(src)
|
|
684
|
+
setDefinitions(initDefs === null ? defIds : composite(defIds, initDefs), initFns === null ? fnIds : composite(fnIds, initFns))
|
|
685
|
+
setTrackDefs(streamOn)
|
|
686
|
+
for (let i = 0; i < pend.length; i += 8) {
|
|
687
|
+
const node = pend[i], task = pend[i + 1], ml = pend[i + 6]
|
|
688
|
+
const tk = task === 1 || task === 2 || task === 3 || task === 11 || task === 12 ? task : 0
|
|
689
|
+
const startLocal = streamOn ? Object.assign({}, node.position.start) : null
|
|
690
|
+
if (ml !== null) inlineMulti(node, ml, tk)
|
|
691
|
+
else inlineSingle(node, pend[i + 2], pend[i + 3], pend[i + 4], pend[i + 5], task === 'table', tk)
|
|
692
|
+
if (streamOn) {
|
|
693
|
+
const nd = takeNeeds()
|
|
694
|
+
if (nd !== null) streamEntries.push({top: pend[i + 7], node, task: tk, table: task === 'table', line: pend[i + 2], ls: pend[i + 3], cs: pend[i + 4], ce: pend[i + 5], ml, startLocal, neg: nd.neg, pos: nd.pos})
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
setTrackDefs(false)
|
|
698
|
+
pend.length = 0
|
|
699
|
+
}
|
|
700
|
+
const r = root
|
|
701
|
+
root = null; src = ''
|
|
702
|
+
return r
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function eofClose(eolOk) {
|
|
706
|
+
switch (leaf) {
|
|
707
|
+
case NONE: return
|
|
708
|
+
case FCODE: leaf = NONE; finishFCode(false, eolOk && eolLenAtEof > 0, true); return
|
|
709
|
+
case HTML: leaf = NONE; finishHtml(eolOk && eolLenAtEof > 0, true); return
|
|
710
|
+
default: closeLeaf(false)
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function prep(ci, container, eolInc) {
|
|
715
|
+
// close open leaf, pop unmatched containers; pop LIST if non-item child
|
|
716
|
+
closeLeaf(eolInc)
|
|
717
|
+
if (stk[ci].kind === LIST && container) { /* handled by caller */ }
|
|
718
|
+
closeContainers(ci + 1)
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
let lateBlank = false
|
|
722
|
+
function attribBlank(ci) {
|
|
723
|
+
let i = ci
|
|
724
|
+
while (i > 0 && stk[i].kind === FN) i--
|
|
725
|
+
const c = stk[i]
|
|
726
|
+
if (c.kind === ITEM) c.node.spread = true
|
|
727
|
+
else if (c.kind === LIST) lateBlank = true
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function processLine() {
|
|
731
|
+
if (!lastBqBlank) bqRun = 0
|
|
732
|
+
lastBqBlank = false
|
|
733
|
+
pos = ls; col = 0; vleft = 0
|
|
734
|
+
lineTok = false; lineCont = false; lastBq = 0
|
|
735
|
+
lateBlank = false
|
|
736
|
+
failKind = -1
|
|
737
|
+
let d = 1
|
|
738
|
+
let bqLine = false
|
|
739
|
+
let fnLine = false, fnTail = false
|
|
740
|
+
for (; d < sp; d++) {
|
|
741
|
+
const c = stk[d]
|
|
742
|
+
const k = c.kind
|
|
743
|
+
if (k === BQ) {
|
|
744
|
+
findNSL(4)
|
|
745
|
+
if (indent <= 3 && ns < le && src.charCodeAt(ns) === 62) {
|
|
746
|
+
advance(indent + 1)
|
|
747
|
+
if (pos < le) { const ch = src.charCodeAt(pos); if (ch === 32 || ch === 9) advance(1) }
|
|
748
|
+
bqLine = true; bqIdx = d; fnTail = false
|
|
749
|
+
continue
|
|
750
|
+
}
|
|
751
|
+
failKind = BQ
|
|
752
|
+
break
|
|
753
|
+
}
|
|
754
|
+
if (k === LIST) continue
|
|
755
|
+
if (k === ITEM && !c.sb && !c.fb) findNSL(c.cc)
|
|
756
|
+
else findNS()
|
|
757
|
+
if (k === ITEM) {
|
|
758
|
+
fnTail = false
|
|
759
|
+
if (blank) { c.fb = c.fb || c.sb; advance(indent < c.cc ? indent : c.cc); continue }
|
|
760
|
+
if (c.fb || indent === 0) { c.fb = false; c.sb = false; failKind = ITEM; break }
|
|
761
|
+
c.fb = false; c.sb = false
|
|
762
|
+
if (indent >= c.cc) { advance(c.cc); continue }
|
|
763
|
+
failKind = ITEM
|
|
764
|
+
break
|
|
765
|
+
}
|
|
766
|
+
// FN
|
|
767
|
+
if (blank) continue
|
|
768
|
+
if (indent >= 4) { advance(4); fnLine = true; fnTail = true; continue }
|
|
769
|
+
// micromark quirk: a nested footnote definition sees the previous footnote indent token as its own
|
|
770
|
+
if (indent === 0 && fnTail) continue
|
|
771
|
+
failKind = FN
|
|
772
|
+
break
|
|
773
|
+
}
|
|
774
|
+
const allMatched = d >= sp
|
|
775
|
+
closeAtPos = bqLine || fnLine ? pos : -1
|
|
776
|
+
matchedPos = pos
|
|
777
|
+
closeFn = fnLine
|
|
778
|
+
bqLastLine = bqLine
|
|
779
|
+
if (bqLine) { lineTok = true; lastBq = bqIdx }
|
|
780
|
+
findNS()
|
|
781
|
+
|
|
782
|
+
if (allMatched) {
|
|
783
|
+
if (leaf === FCODE) { fcodeLine(); finishLine(); return }
|
|
784
|
+
if (leaf === HTML) { if (htmlLine()) { finishLine(); return } }
|
|
785
|
+
else if (leaf === ICODE) {
|
|
786
|
+
if (icClosed) { closeLeaf(false); icClosed = false } // micromark: such code is one line, the next starts a new block
|
|
787
|
+
else if (indent >= 4 || blank) {
|
|
788
|
+
icodeLine(); finishLine(); return
|
|
789
|
+
}
|
|
790
|
+
} else if (leaf === TABLE) {
|
|
791
|
+
if (tState === 1) { tState = 2; lineTok = true; lineCont = true; setE1(lineNo, ls, le); e2Line = lineNo; e2Ls = ls; e2Off = le; tNode.position.end = P(lineNo, ls, le); return }
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
let ci = d - 1
|
|
796
|
+
|
|
797
|
+
if (blank) {
|
|
798
|
+
// blank line
|
|
799
|
+
if (bqLine) { bqRun++; lastBqBlank = true }
|
|
800
|
+
if (leaf !== NONE) closeLeaf(failKind === ITEM)
|
|
801
|
+
if (bqLine && eolLen === 0 && pos === le && vleft === 0 && sp > ci + 1) {
|
|
802
|
+
// last line without EOL: unmatched inner containers close right after the matched prefix
|
|
803
|
+
closeAt(ci + 1, pos)
|
|
804
|
+
} else if (bqLine && eolLen > 0 && sp > ci + 1) {
|
|
805
|
+
const r = bqRun; bqRun = r - 1 // (one line ending less is inside the list when the blank line has its own EOL)
|
|
806
|
+
closeContainers(ci + 1)
|
|
807
|
+
bqRun = r
|
|
808
|
+
} else closeContainers(ci + 1)
|
|
809
|
+
pendingBlank = true; pbCi = ci; pbBq = stk[ci].kind === BQ
|
|
810
|
+
// streaming: a complete blank line at the top level with nothing open is a point after which the past can no longer change
|
|
811
|
+
if (streamOn && sp === 1 && leaf === NONE && eolLen > 0 && !noContainers && !(src.charCodeAt(le) === 13 && le + 1 >= len)) streamSafe = {off: le + eolLen, line: lineNo, n: root.children.length}
|
|
812
|
+
finishLine()
|
|
813
|
+
return
|
|
814
|
+
}
|
|
815
|
+
// a blank line inside a container that has been closed since (e.g. an empty `>` line) is not a blank
|
|
816
|
+
// between the children of an outer item
|
|
817
|
+
if (pendingBlank) { if (!(pbBq && pbCi > ci)) attribBlank(ci); pendingBlank = false }
|
|
818
|
+
|
|
819
|
+
let paraTip = leaf === PARA
|
|
820
|
+
const interrupt = allMatched && (paraTip || (leaf === ICODE && !icClosed))
|
|
821
|
+
|
|
822
|
+
for (;;) {
|
|
823
|
+
findNS()
|
|
824
|
+
if (blank) {
|
|
825
|
+
// rest of line blank after container start
|
|
826
|
+
finishLine(); return
|
|
827
|
+
}
|
|
828
|
+
if (indent >= 4) {
|
|
829
|
+
if (!paraTip) {
|
|
830
|
+
const sp0 = sp
|
|
831
|
+
ci = prepCI(ci, true, false)
|
|
832
|
+
icClosed = sp < sp0
|
|
833
|
+
startICode()
|
|
834
|
+
finishLine(); return
|
|
835
|
+
}
|
|
836
|
+
break
|
|
837
|
+
}
|
|
838
|
+
const ch = src.charCodeAt(ns)
|
|
839
|
+
if (ch === 62 && !noContainers) { // >
|
|
840
|
+
ci = prepCI(ci, true, true, true)
|
|
841
|
+
paraTip = false
|
|
842
|
+
const node = {type: 'blockquote', children: [], position: {start: P(lineNo, ls, ns), end: undefined}}
|
|
843
|
+
stk[ci].node.children.push(node)
|
|
844
|
+
stk[sp++] = cont(BQ, node)
|
|
845
|
+
ci = sp - 1
|
|
846
|
+
advance(indent + 1)
|
|
847
|
+
if (pos < le) { const c2 = src.charCodeAt(pos); if (c2 === 32 || c2 === 9) advance(1) }
|
|
848
|
+
lineTok = true; lastBq = sp - 1
|
|
849
|
+
continue
|
|
850
|
+
}
|
|
851
|
+
if (ch === 35) { // #
|
|
852
|
+
let n = 1
|
|
853
|
+
while (src.charCodeAt(ns + n) === 35) n++
|
|
854
|
+
if (n <= 6) {
|
|
855
|
+
const nx = ns + n
|
|
856
|
+
const c2 = nx < le ? src.charCodeAt(nx) : 10
|
|
857
|
+
if (nx >= le || c2 === 32 || c2 === 9) {
|
|
858
|
+
ci = prepCI(ci, true, false)
|
|
859
|
+
const hnode = {
|
|
860
|
+
type: 'heading', depth: n, children: [],
|
|
861
|
+
position: {start: P(lineNo, ls, ns), end: P(lineNo, ls, le)}
|
|
862
|
+
}
|
|
863
|
+
stk[ci].node.children.push(hnode)
|
|
864
|
+
if (doInline) atxContent(hnode, ns + n)
|
|
865
|
+
lineTok = true; lineCont = true
|
|
866
|
+
finishLine(); return
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
break
|
|
870
|
+
}
|
|
871
|
+
if (ch === 96 || ch === 126 || (ch === 36 && mathOn)) {
|
|
872
|
+
if (startFence(ch, ci)) { finishLine(); return }
|
|
873
|
+
break
|
|
874
|
+
}
|
|
875
|
+
if (ch === 60) {
|
|
876
|
+
const k = htmlStart(paraTip && allMatched)
|
|
877
|
+
if (k) {
|
|
878
|
+
if (k === 7 && paraTip && !allMatched && eolLen > 0) {
|
|
879
|
+
// micromark: an html (kind 7) start on a lazy line is allowed and stays in the open containers
|
|
880
|
+
closeLeaf(false)
|
|
881
|
+
ci = sp - 1
|
|
882
|
+
startHtml(k)
|
|
883
|
+
finishLine(); return
|
|
884
|
+
}
|
|
885
|
+
ci = prepCI(ci, true, false)
|
|
886
|
+
startHtml(k)
|
|
887
|
+
finishLine(); return
|
|
888
|
+
}
|
|
889
|
+
break
|
|
890
|
+
}
|
|
891
|
+
if (ch === 61 && paraTip && allMatched && isSetext(ch)) {
|
|
892
|
+
if (paragraphHasContentAfterDefs()) {
|
|
893
|
+
doSetext(1, ci)
|
|
894
|
+
finishLine(); return
|
|
895
|
+
}
|
|
896
|
+
// only definitions so far: this line starts a new paragraph (micromark)
|
|
897
|
+
closeLeaf(false); paraTip = false
|
|
898
|
+
break
|
|
899
|
+
}
|
|
900
|
+
if (ch === 45 || ch === 42 || ch === 95) {
|
|
901
|
+
const thematic = isThematic(ch)
|
|
902
|
+
if (ch !== 95 && !thematic) {
|
|
903
|
+
if (tryItem(ci, ch, interrupt)) { paraTip = false; ci = sp - 1; if (blank2) { finishLine(); return } continue }
|
|
904
|
+
}
|
|
905
|
+
if (ch === 45 && paraTip && allMatched && isSetext(45)) {
|
|
906
|
+
if (paragraphHasContentAfterDefs()) {
|
|
907
|
+
doSetext(2, ci)
|
|
908
|
+
finishLine(); return
|
|
909
|
+
}
|
|
910
|
+
if (!thematic) { closeLeaf(false); paraTip = false; break }
|
|
911
|
+
}
|
|
912
|
+
if (thematic) {
|
|
913
|
+
ci = prepCI(ci, true, false)
|
|
914
|
+
stk[ci].node.children.push({type: 'thematicBreak', position: {start: P(lineNo, ls, ns), end: P(lineNo, ls, le)}})
|
|
915
|
+
lineTok = true; lineCont = true
|
|
916
|
+
finishLine(); return
|
|
917
|
+
}
|
|
918
|
+
break
|
|
919
|
+
}
|
|
920
|
+
if (ch === 43 || (ch >= 48 && ch <= 57)) {
|
|
921
|
+
if (tryItem(ci, ch, interrupt)) { paraTip = false; ci = sp - 1; if (blank2) { finishLine(); return } continue }
|
|
922
|
+
break
|
|
923
|
+
}
|
|
924
|
+
if (ch === 91 && src.charCodeAt(ns + 1) === 94) {
|
|
925
|
+
if (tryFootnote(ci)) { paraTip = false; ci = sp - 1; continue }
|
|
926
|
+
break
|
|
927
|
+
}
|
|
928
|
+
break
|
|
929
|
+
}
|
|
930
|
+
// paragraph / table / continuation
|
|
931
|
+
// table head?
|
|
932
|
+
if (leaf === TABLE && allMatched) {
|
|
933
|
+
addTableRow()
|
|
934
|
+
finishLine(); return
|
|
935
|
+
}
|
|
936
|
+
if (allMatched || !paraTip || true) {
|
|
937
|
+
if (!paraTip || (allMatched && leaf === PARA) || !allMatched) {
|
|
938
|
+
// table head detection
|
|
939
|
+
if ((allMatched || !paraTip) && tryTableHead(ci)) { finishLine(); return }
|
|
940
|
+
// micromark: a lazy line may be the head row of a table when the delimiter row line matches all containers
|
|
941
|
+
if (!allMatched && paraTip && tryTableHead(sp - 1)) { finishLine(); return }
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
if (paraTip) {
|
|
945
|
+
// continuation (lazy or not)
|
|
946
|
+
findNS()
|
|
947
|
+
paraLine(false)
|
|
948
|
+
finishLine(); return
|
|
949
|
+
}
|
|
950
|
+
// new paragraph
|
|
951
|
+
ci = prepCI(ci, true, false)
|
|
952
|
+
findNS()
|
|
953
|
+
startPara()
|
|
954
|
+
finishLine()
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
let blank2 = false
|
|
958
|
+
|
|
959
|
+
// ATX heading content range: port of micromark resolveHeadingAtx over the token list
|
|
960
|
+
let atxT = [], atxS = [], atxE = []
|
|
961
|
+
function atxContent(node, from) {
|
|
962
|
+
// token types: 0 sequence, 1 whitespace, 2 text
|
|
963
|
+
let nt = 0
|
|
964
|
+
atxT[0] = 0; atxS[0] = ns; atxE[0] = from
|
|
965
|
+
nt = 1
|
|
966
|
+
let i = from
|
|
967
|
+
while (i < le) {
|
|
968
|
+
const c = src.charCodeAt(i)
|
|
969
|
+
let ty
|
|
970
|
+
const st = i
|
|
971
|
+
if (c === 35) { while (i < le && src.charCodeAt(i) === 35) i++; ty = 0 }
|
|
972
|
+
else if (c === 32 || c === 9) { while (i < le && (src.charCodeAt(i) === 32 || src.charCodeAt(i) === 9)) i++; ty = 1 }
|
|
973
|
+
else { while (i < le) { const d = src.charCodeAt(i); if (d === 35 || d === 32 || d === 9) break; i++ } ty = 2 }
|
|
974
|
+
atxT[nt] = ty; atxS[nt] = st; atxE[nt] = i; nt++
|
|
975
|
+
}
|
|
976
|
+
// events: 0 enter heading, enter token j = 1 + 2j, exit token j = 2 + 2j, last = exit heading
|
|
977
|
+
const evLen = 2 + 2 * nt
|
|
978
|
+
const typeAt = idx => (idx <= 0 || idx >= evLen - 1) ? -1 : atxT[(idx - 1) >> 1]
|
|
979
|
+
let contentEnd = evLen - 2
|
|
980
|
+
let contentStart = 3
|
|
981
|
+
if (typeAt(contentStart) === 1) contentStart += 2
|
|
982
|
+
if (contentEnd - 2 > contentStart && typeAt(contentEnd) === 1) contentEnd -= 2
|
|
983
|
+
if (typeAt(contentEnd) === 0 && (contentStart === contentEnd - 1 || (contentEnd - 4 > contentStart && typeAt(contentEnd - 2) === 1))) {
|
|
984
|
+
contentEnd -= contentStart + 1 === contentEnd ? 2 : 4
|
|
985
|
+
}
|
|
986
|
+
if (contentEnd > contentStart) {
|
|
987
|
+
const a = atxS[(contentStart - 1) >> 1], b = atxE[(contentEnd - 1) >> 1]
|
|
988
|
+
pend.push(node, false, lineNo, ls, a, b, null, topNow())
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// micromark: when unmatched containers are closed by a NEW container starting on the same line and a
|
|
993
|
+
// blockquote prefix was consumed on that line, their exit sits right after that prefix (the `_container`
|
|
994
|
+
// fix cannot swap it before the previous line ending)
|
|
995
|
+
let closeAtPos = -1, closeFn = false, closeEol12 = false, matchedPos = 0
|
|
996
|
+
// last line on which an unclosed fence / html block included its EOL (its container exits sit after the next line's prefix)
|
|
997
|
+
let e12Line = -2
|
|
998
|
+
// micromark: a list that ends in 2+ blockquote-prefix-only lines is spread (those line endings stay inside it)
|
|
999
|
+
let bqRun = 0, lastBqBlank = false
|
|
1000
|
+
let bqLastLine = false
|
|
1001
|
+
let closeOver = null
|
|
1002
|
+
function closeAt(to, at) {
|
|
1003
|
+
const a = e1Line, b = e1Ls, c = e1Off
|
|
1004
|
+
setE1(lineNo, ls, at)
|
|
1005
|
+
closeOver = () => P(lineNo, ls, at)
|
|
1006
|
+
closeContainers(to)
|
|
1007
|
+
closeOver = null
|
|
1008
|
+
setE1(a, b, c)
|
|
1009
|
+
}
|
|
1010
|
+
function closeForNew(to) {
|
|
1011
|
+
closeEol12 = false
|
|
1012
|
+
let at = closeAtPos
|
|
1013
|
+
if (at < 0 && e12Line === lineNo) { at = matchedPos; closeEol12 = true }
|
|
1014
|
+
if (at >= 0 && sp > to) {
|
|
1015
|
+
if (!closeEol12) bqRun++ // the exit sits after this line's prefix: one more trailing line ending counts
|
|
1016
|
+
closeAt(to, at)
|
|
1017
|
+
if (!closeEol12) bqRun--
|
|
1018
|
+
} else closeContainers(to)
|
|
1019
|
+
}
|
|
1020
|
+
function prepCI(ci, isBlock, eolInc, newContainer) {
|
|
1021
|
+
closeLeaf(eolInc)
|
|
1022
|
+
if (ci < sp - 1 && true) {
|
|
1023
|
+
// pop unmatched
|
|
1024
|
+
if (newContainer) closeForNew(ci + 1); else closeContainers(ci + 1)
|
|
1025
|
+
}
|
|
1026
|
+
if (stk[ci].kind === LIST) {
|
|
1027
|
+
if (newContainer) closeForNew(ci); else closeContainers(ci)
|
|
1028
|
+
ci--
|
|
1029
|
+
if (lateBlank) { lateBlank = false; attribBlank(ci) }
|
|
1030
|
+
}
|
|
1031
|
+
return ci
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
function finishLine() {
|
|
1035
|
+
if (!lastBqBlank) bqRun = 0
|
|
1036
|
+
if (lineTok) {
|
|
1037
|
+
setE1(lineNo, ls, le)
|
|
1038
|
+
if (lineCont) { e2Line = lineNo; e2Ls = ls; e2Off = le }
|
|
1039
|
+
else { ebLine = lineNo; ebLs = ls; ebOff = le; ebBi = lastBq }
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
// end point for an item at stack index k closed by a following sibling item
|
|
1043
|
+
function pickE2(k) {
|
|
1044
|
+
if (ebLine > (e2Off === e2Ls ? e2Line - 1 : e2Line) && ebBi > k) return P(ebLine, ebLs, ebOff)
|
|
1045
|
+
return P(e2Line, e2Ls, e2Off)
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
function startPara() {
|
|
1049
|
+
leaf = PARA
|
|
1050
|
+
pL = lineNo; pLs = ls; pOff = ns
|
|
1051
|
+
eL = lineNo; eLs = ls; eOff = le
|
|
1052
|
+
pCand = src.charCodeAt(ns) === 91
|
|
1053
|
+
pLines.length = 0; pPos.length = 0; pVl.length = 0
|
|
1054
|
+
pLines.push(lineNo, ls, ns, le); pPos.push(pos); pVl.push(vleft)
|
|
1055
|
+
lineTok = true; lineCont = true
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
function paraLine() {
|
|
1059
|
+
eL = lineNo; eLs = ls; eOff = le
|
|
1060
|
+
pLines.push(lineNo, ls, ns, le); pPos.push(pos); pVl.push(vleft)
|
|
1061
|
+
lineTok = true; lineCont = true
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
function startICode() {
|
|
1065
|
+
leaf = ICODE
|
|
1066
|
+
cL = lineNo; cLs = ls; cOff = pos
|
|
1067
|
+
advance(4)
|
|
1068
|
+
cVal = restOfLine()
|
|
1069
|
+
cLastL = lineNo; cLastLs = ls; cLastLe = le
|
|
1070
|
+
cEol = eolStr(le, eolLen)
|
|
1071
|
+
icPend = ''
|
|
1072
|
+
lineTok = true; lineCont = true
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
function icodeLine() {
|
|
1076
|
+
// continuing indented code (allMatched) with indent>=4 or blank
|
|
1077
|
+
if (blank) {
|
|
1078
|
+
const n = indent
|
|
1079
|
+
advance(n > 4 ? 4 : n)
|
|
1080
|
+
if (n >= 4) {
|
|
1081
|
+
cVal += icPend + cEol + restOfLine()
|
|
1082
|
+
icPend = ''
|
|
1083
|
+
cEol = eolStr(le, eolLen)
|
|
1084
|
+
cLastL = lineNo; cLastLs = ls; cLastLe = le
|
|
1085
|
+
lineTok = true; lineCont = true
|
|
1086
|
+
pendingBlank = false
|
|
1087
|
+
return
|
|
1088
|
+
}
|
|
1089
|
+
icPend += cEol + restOfLine()
|
|
1090
|
+
cEol = eolStr(le, eolLen)
|
|
1091
|
+
pendingBlank = true; pbBq = false
|
|
1092
|
+
return
|
|
1093
|
+
}
|
|
1094
|
+
advance(4)
|
|
1095
|
+
cVal += icPend + cEol + restOfLine()
|
|
1096
|
+
icPend = ''
|
|
1097
|
+
cEol = eolStr(le, eolLen)
|
|
1098
|
+
cLastL = lineNo; cLastLs = ls; cLastLe = le
|
|
1099
|
+
pendingBlank = false
|
|
1100
|
+
lineTok = true; lineCont = true
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// ---- fenced code ----
|
|
1104
|
+
function startFence(ch, ci) {
|
|
1105
|
+
let n = 1
|
|
1106
|
+
while (src.charCodeAt(ns + n) === ch) n++
|
|
1107
|
+
if (n < (ch === 36 ? 2 : 3)) return false
|
|
1108
|
+
const m = ns + n
|
|
1109
|
+
if (ch === 36) {
|
|
1110
|
+
// math flow: the rest of the line is the meta (raw, may not contain `$`)
|
|
1111
|
+
let k = m
|
|
1112
|
+
while (k < le && isWs(src.charCodeAt(k))) k++
|
|
1113
|
+
for (let q = k; q < le; q++) if (src.charCodeAt(q) === 36) return false
|
|
1114
|
+
let mm = k < le ? src.slice(k, le) : null
|
|
1115
|
+
if (mm !== null && (mm.indexOf('\\') !== -1 || mm.indexOf('&') !== -1)) mm = decodeString(mm)
|
|
1116
|
+
ci = prepCI(ci, true, false)
|
|
1117
|
+
leaf = FCODE
|
|
1118
|
+
cL = lineNo; cLs = ls; cOff = ns
|
|
1119
|
+
fch = ch; flen = n; findent = indent
|
|
1120
|
+
flang = null; fmeta = mm
|
|
1121
|
+
fEolCR = src.charCodeAt(le) === 13 && src.charCodeAt(le + 1) !== 10
|
|
1122
|
+
cVal = ''; cN = 0
|
|
1123
|
+
cLastL = lineNo; cLastLs = ls; cLastLe = le
|
|
1124
|
+
cRealL = lineNo; cRealLs = ls; cRealLe = le
|
|
1125
|
+
lineTok = true; lineCont = true
|
|
1126
|
+
return true
|
|
1127
|
+
}
|
|
1128
|
+
if (ch === 96) {
|
|
1129
|
+
for (let i = m; i < le; i++) if (src.charCodeAt(i) === 96) return false
|
|
1130
|
+
}
|
|
1131
|
+
// info
|
|
1132
|
+
let i = m
|
|
1133
|
+
while (i < le && isWs(src.charCodeAt(i))) i++
|
|
1134
|
+
let lang = null, meta = null
|
|
1135
|
+
if (i < le) {
|
|
1136
|
+
let j = i
|
|
1137
|
+
while (j < le && !isWs(src.charCodeAt(j))) j++
|
|
1138
|
+
lang = src.slice(i, j)
|
|
1139
|
+
let k = j
|
|
1140
|
+
while (k < le && isWs(src.charCodeAt(k))) k++
|
|
1141
|
+
if (k < le) {
|
|
1142
|
+
meta = src.slice(k, le)
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
if (lang !== null && (lang.indexOf('\\') !== -1 || lang.indexOf('&') !== -1)) lang = decodeString(lang)
|
|
1146
|
+
if (meta !== null && (meta.indexOf('\\') !== -1 || meta.indexOf('&') !== -1)) meta = decodeString(meta)
|
|
1147
|
+
ci = prepCI(ci, true, false)
|
|
1148
|
+
leaf = FCODE
|
|
1149
|
+
cL = lineNo; cLs = ls; cOff = ns
|
|
1150
|
+
fch = ch; flen = n; findent = indent
|
|
1151
|
+
flang = lang; fmeta = meta
|
|
1152
|
+
fEolCR = src.charCodeAt(le) === 13 && src.charCodeAt(le + 1) !== 10
|
|
1153
|
+
cVal = ''; cN = 0
|
|
1154
|
+
cLastL = lineNo; cLastLs = ls; cLastLe = le
|
|
1155
|
+
cRealL = lineNo; cRealLs = ls; cRealLe = le
|
|
1156
|
+
lineTok = true; lineCont = true
|
|
1157
|
+
return true
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
function fcodeLine() {
|
|
1161
|
+
// allMatched, in fenced code
|
|
1162
|
+
if (indent <= 3 && !blank) {
|
|
1163
|
+
if (src.charCodeAt(ns) === fch) {
|
|
1164
|
+
let n = 1
|
|
1165
|
+
while (src.charCodeAt(ns + n) === fch) n++
|
|
1166
|
+
if (n >= flen) {
|
|
1167
|
+
let i = ns + n
|
|
1168
|
+
while (i < le && isWs(src.charCodeAt(i))) i++
|
|
1169
|
+
if (i >= le) {
|
|
1170
|
+
cLastL = lineNo; cLastLs = ls; cLastLe = le
|
|
1171
|
+
leaf = NONE
|
|
1172
|
+
finishFCode(true, false)
|
|
1173
|
+
lineTok = true; lineCont = true
|
|
1174
|
+
return
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
const n = indent < findent ? indent : findent
|
|
1180
|
+
advance(n)
|
|
1181
|
+
const piece = restOfLine()
|
|
1182
|
+
if (cN === 0) cVal = piece
|
|
1183
|
+
else cVal += cEol + piece
|
|
1184
|
+
cN++
|
|
1185
|
+
cEol = eolStr(le, eolLen)
|
|
1186
|
+
cLastL = lineNo; cLastLs = ls; cLastLe = le
|
|
1187
|
+
// a last line (no EOL) that the open containers consume entirely is not part of the code
|
|
1188
|
+
cRealL = lineNo; cRealLs = ls; cRealLe = le
|
|
1189
|
+
if (blank && eolLen === 0 && piece === '' && n === 0 && sp > 1) cLastLe = ls
|
|
1190
|
+
if (!blank) lineTok = true; lineCont = true
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// ---- html ----
|
|
1194
|
+
const RAW = ['script', 'pre', 'style', 'textarea']
|
|
1195
|
+
function lc(c) { return c | 32 }
|
|
1196
|
+
function isAlpha(c) { c |= 32; return c >= 97 && c <= 122 }
|
|
1197
|
+
function isAlnum(c) { return isAlpha(c) || (c >= 48 && c <= 57) }
|
|
1198
|
+
|
|
1199
|
+
function htmlStart(paraTip) {
|
|
1200
|
+
const c1 = src.charCodeAt(ns + 1)
|
|
1201
|
+
if (c1 === 33) {
|
|
1202
|
+
const c2 = src.charCodeAt(ns + 2)
|
|
1203
|
+
if (c2 === 45) return src.charCodeAt(ns + 3) === 45 ? 2 : 0
|
|
1204
|
+
if (c2 === 91) return src.startsWith('CDATA[', ns + 3) ? 5 : 0
|
|
1205
|
+
if (isAlpha(c2)) return 4
|
|
1206
|
+
return 0
|
|
1207
|
+
}
|
|
1208
|
+
if (c1 === 63) return 3
|
|
1209
|
+
let i = ns + 1
|
|
1210
|
+
let closing = false
|
|
1211
|
+
if (c1 === 47) { closing = true; i++ }
|
|
1212
|
+
const c = src.charCodeAt(i)
|
|
1213
|
+
if (!isAlpha(c)) return 0
|
|
1214
|
+
const s = i
|
|
1215
|
+
while (i < le && (isAlnum(src.charCodeAt(i)) || src.charCodeAt(i) === 45)) i++
|
|
1216
|
+
const name = src.slice(s, i).toLowerCase()
|
|
1217
|
+
const after = i < le ? src.charCodeAt(i) : 10
|
|
1218
|
+
const terminal = i >= le || after === 62 || after === 47 || after === 32 || after === 9
|
|
1219
|
+
if (terminal) {
|
|
1220
|
+
if (after !== 47 && !closing && RAW.indexOf(name) !== -1) return 1
|
|
1221
|
+
if (BLOCK_NAMES.has(name)) {
|
|
1222
|
+
if (after === 47) return src.charCodeAt(i + 1) === 62 ? 6 : 0
|
|
1223
|
+
return 6
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
if (paraTip) return 0
|
|
1227
|
+
if (!terminal) return 0
|
|
1228
|
+
// kind 7: complete tag
|
|
1229
|
+
const e = completeTag(i, closing)
|
|
1230
|
+
if (e < 0) return 0
|
|
1231
|
+
let j = e
|
|
1232
|
+
while (j < le && isWs(src.charCodeAt(j))) j++
|
|
1233
|
+
return j >= le ? 7 : 0
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// i: index after tag name; returns index after '>' or -1
|
|
1237
|
+
function completeTag(i, closing) {
|
|
1238
|
+
if (closing) {
|
|
1239
|
+
while (i < le && isWs(src.charCodeAt(i))) i++
|
|
1240
|
+
return src.charCodeAt(i) === 62 && i < le ? i + 1 : -1
|
|
1241
|
+
}
|
|
1242
|
+
// port of micromark html-flow complete* states (kind 7); the end of the line behaves like an EOL code
|
|
1243
|
+
let st = 0, marker = 0
|
|
1244
|
+
for (;;) {
|
|
1245
|
+
const c = i < le ? src.charCodeAt(i) : -1
|
|
1246
|
+
switch (st) {
|
|
1247
|
+
case 0: // attribute name before
|
|
1248
|
+
if (c === 47) { i++; st = 7; continue }
|
|
1249
|
+
if (c === 58 || c === 95 || isAlpha(c)) { i++; st = 1; continue }
|
|
1250
|
+
if (c === 32 || c === 9) { i++; continue }
|
|
1251
|
+
st = 7
|
|
1252
|
+
continue
|
|
1253
|
+
case 1: // attribute name
|
|
1254
|
+
if (c === 45 || c === 46 || c === 58 || c === 95 || isAlnum(c)) { i++; continue }
|
|
1255
|
+
st = 2
|
|
1256
|
+
continue
|
|
1257
|
+
case 2: // attribute name after
|
|
1258
|
+
if (c === 61) { i++; st = 3; continue }
|
|
1259
|
+
if (c === 32 || c === 9) { i++; continue }
|
|
1260
|
+
st = 0
|
|
1261
|
+
continue
|
|
1262
|
+
case 3: // attribute value before
|
|
1263
|
+
if (c === -1 || c === 60 || c === 61 || c === 62 || c === 96) return -1
|
|
1264
|
+
if (c === 34 || c === 39) { i++; marker = c; st = 4; continue }
|
|
1265
|
+
if (c === 32 || c === 9) { i++; continue }
|
|
1266
|
+
st = 5
|
|
1267
|
+
continue
|
|
1268
|
+
case 4: // quoted
|
|
1269
|
+
if (c === marker) { i++; st = 6; continue }
|
|
1270
|
+
if (c === -1) return -1
|
|
1271
|
+
i++
|
|
1272
|
+
continue
|
|
1273
|
+
case 5: // unquoted
|
|
1274
|
+
if (c === -1 || c === 34 || c === 39 || c === 47 || c === 60 || c === 61 || c === 62 || c === 96 || c === 32 || c === 9) { st = 2; continue }
|
|
1275
|
+
i++
|
|
1276
|
+
continue
|
|
1277
|
+
case 6: // quoted after
|
|
1278
|
+
if (c === 47 || c === 62 || c === 32 || c === 9) { st = 0; continue }
|
|
1279
|
+
return -1
|
|
1280
|
+
case 7: // end
|
|
1281
|
+
return c === 62 ? i + 1 : -1
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
// index of str within the current line [from, le), else -1 (never scans past the line)
|
|
1287
|
+
function idxLine(str, from) {
|
|
1288
|
+
const c0 = str.charCodeAt(0), n = str.length
|
|
1289
|
+
const end = le - n
|
|
1290
|
+
for (let i = from; i <= end; i++) {
|
|
1291
|
+
if (src.charCodeAt(i) === c0 && (n === 1 || src.startsWith(str, i))) return i
|
|
1292
|
+
}
|
|
1293
|
+
return -1
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
function htmlEnds(k, from) {
|
|
1297
|
+
// does the line (from index) contain the end condition for kind k
|
|
1298
|
+
switch (k) {
|
|
1299
|
+
case 1: {
|
|
1300
|
+
let i = from
|
|
1301
|
+
for (;;) {
|
|
1302
|
+
i = idxLine('</', i)
|
|
1303
|
+
if (i === -1) return false
|
|
1304
|
+
const s = src.slice(i + 2, i + 12).toLowerCase()
|
|
1305
|
+
if (s.startsWith('script>') || s.startsWith('style>') || s.startsWith('pre>') || s.startsWith('textarea>')) return true
|
|
1306
|
+
i += 2
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
case 2: return idxLine('-->', from) !== -1
|
|
1310
|
+
case 3: return idxLine('?>', from) !== -1
|
|
1311
|
+
case 4: return idxLine('>', from) !== -1
|
|
1312
|
+
case 5: {
|
|
1313
|
+
// micromark's state machine: `]]]>` does not end the block, `]]]]>` does
|
|
1314
|
+
let st = 0
|
|
1315
|
+
for (let i = from; i < le; i++) {
|
|
1316
|
+
const c = src.charCodeAt(i)
|
|
1317
|
+
if (st === 0) st = c === 93 ? 1 : 0
|
|
1318
|
+
else if (st === 1) st = c === 93 ? 2 : 0
|
|
1319
|
+
else { if (c === 62) return true; st = c === 93 ? 1 : 0 }
|
|
1320
|
+
}
|
|
1321
|
+
return false
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return false
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
function startHtml(k) {
|
|
1328
|
+
cRealL = lineNo; cRealLs = ls; cRealLe = le
|
|
1329
|
+
leaf = HTML
|
|
1330
|
+
hkind = k
|
|
1331
|
+
cL = lineNo; cLs = ls; cOff = pos
|
|
1332
|
+
cVal = restOfLine()
|
|
1333
|
+
cLastL = lineNo; cLastLs = ls; cLastLe = le
|
|
1334
|
+
lineTok = true; lineCont = true
|
|
1335
|
+
if (k <= 5) {
|
|
1336
|
+
const from = k === 2 ? ns + 2 : k === 3 ? ns + 1 : k === 5 ? ns + 9 : ns + 1
|
|
1337
|
+
if (htmlEnds(k, from)) { leaf = NONE; finishHtml(false) }
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// returns true if consumed
|
|
1342
|
+
function htmlLine() {
|
|
1343
|
+
if (hkind >= 6) {
|
|
1344
|
+
if (blank) { return false } // caller falls through; need to close
|
|
1345
|
+
advance(indent < 0 ? 0 : 0)
|
|
1346
|
+
cVal += eolStr(cLastLe, 0) + ''
|
|
1347
|
+
}
|
|
1348
|
+
// append line
|
|
1349
|
+
const prevEol = cEolHtml()
|
|
1350
|
+
const rest = restOfLine()
|
|
1351
|
+
cVal += prevEol + rest
|
|
1352
|
+
cLastL = lineNo; cLastLs = ls; cLastLe = le
|
|
1353
|
+
cRealL = lineNo; cRealLs = ls; cRealLe = le
|
|
1354
|
+
if (hkind <= 5 && blank && eolLen === 0 && rest === '' && sp > 1) cLastLe = ls
|
|
1355
|
+
lineTok = true; lineCont = true
|
|
1356
|
+
if (hkind <= 5 && htmlEnds(hkind, pos)) { leaf = NONE; finishHtml(false) }
|
|
1357
|
+
return true
|
|
1358
|
+
}
|
|
1359
|
+
function cEolHtml() {
|
|
1360
|
+
// eol of previous line: src at cLastLe
|
|
1361
|
+
const c = src.charCodeAt(cLastLe)
|
|
1362
|
+
if (c === 13) return src.charCodeAt(cLastLe + 1) === 10 ? '\r\n' : '\r'
|
|
1363
|
+
return '\n'
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// ---- thematic / setext ----
|
|
1367
|
+
function isThematic(ch) {
|
|
1368
|
+
let n = 0
|
|
1369
|
+
for (let i = ns; i < le; i++) {
|
|
1370
|
+
const c = src.charCodeAt(i)
|
|
1371
|
+
if (c === ch) n++
|
|
1372
|
+
else if (c !== 32 && c !== 9) return false
|
|
1373
|
+
}
|
|
1374
|
+
return n >= 3
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
function isSetext(ch) {
|
|
1378
|
+
let i = ns
|
|
1379
|
+
while (i < le && src.charCodeAt(i) === ch) i++
|
|
1380
|
+
if (i === ns) return false
|
|
1381
|
+
while (i < le && isWs(src.charCodeAt(i))) i++
|
|
1382
|
+
return i >= le
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
function doSetext(depth, ci) {
|
|
1386
|
+
// finalize paragraph into heading
|
|
1387
|
+
leaf = NONE
|
|
1388
|
+
const end = P(lineNo, ls, le)
|
|
1389
|
+
finishParagraph(-1, {depth, end})
|
|
1390
|
+
lineTok = true; lineCont = true
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
// ---- lists ----
|
|
1394
|
+
let quirkItem = null
|
|
1395
|
+
function tryItem(ci, ch, interrupt) {
|
|
1396
|
+
if (noContainers) return false
|
|
1397
|
+
blank2 = false
|
|
1398
|
+
let mlen = 1
|
|
1399
|
+
let ordered = false
|
|
1400
|
+
let delim = ch
|
|
1401
|
+
let value = 0
|
|
1402
|
+
if (ch >= 48 && ch <= 57) {
|
|
1403
|
+
ordered = true
|
|
1404
|
+
let n = 0
|
|
1405
|
+
let i = ns
|
|
1406
|
+
while (i < le) {
|
|
1407
|
+
const c = src.charCodeAt(i)
|
|
1408
|
+
if (c >= 48 && c <= 57) { n++; i++ } else break
|
|
1409
|
+
}
|
|
1410
|
+
if (n > 9) return false
|
|
1411
|
+
delim = src.charCodeAt(i)
|
|
1412
|
+
if (delim !== 46 && delim !== 41) return false
|
|
1413
|
+
if (interrupt && !(n === 1 && ch === 49)) return false
|
|
1414
|
+
mlen = n + 1
|
|
1415
|
+
value = parseInt(src.slice(ns, ns + n), 10)
|
|
1416
|
+
}
|
|
1417
|
+
const m = ns + mlen
|
|
1418
|
+
// after marker
|
|
1419
|
+
let restBlank = true
|
|
1420
|
+
let wsCols = 0
|
|
1421
|
+
let j = m
|
|
1422
|
+
let cc = col + indent + mlen // virtual column after marker, relative to line start
|
|
1423
|
+
// compute whitespace columns following marker
|
|
1424
|
+
{
|
|
1425
|
+
let cur = cc
|
|
1426
|
+
while (j < le) {
|
|
1427
|
+
const c = src.charCodeAt(j)
|
|
1428
|
+
if (c === 32) { cur++; j++ } else if (c === 9) { cur += 4 - (cur & 3); j++ } else break
|
|
1429
|
+
}
|
|
1430
|
+
wsCols = cur - cc
|
|
1431
|
+
restBlank = j >= le
|
|
1432
|
+
}
|
|
1433
|
+
if (restBlank) {
|
|
1434
|
+
if (interrupt) return false
|
|
1435
|
+
} else if (wsCols === 0) return false
|
|
1436
|
+
// sibling / new list
|
|
1437
|
+
const top = stk[ci]
|
|
1438
|
+
let compatible = false
|
|
1439
|
+
if (top.kind === LIST) {
|
|
1440
|
+
compatible = top.ord === ordered && top.mk === delim
|
|
1441
|
+
}
|
|
1442
|
+
// commit
|
|
1443
|
+
const quirk = leaf === FCODE || (leaf === HTML && hkind <= 5)
|
|
1444
|
+
closeLeaf(failKind === ITEM || true)
|
|
1445
|
+
if (top.kind === LIST && compatible) {
|
|
1446
|
+
// sibling
|
|
1447
|
+
const itemNode = stk[ci + 1].node
|
|
1448
|
+
const pe = pickE2(ci + 1)
|
|
1449
|
+
if (e12Line === lineNo) closeForNew(ci + 1); else closeContainers(ci + 1)
|
|
1450
|
+
itemNode.position.end = pe
|
|
1451
|
+
const fnQuirk = closeFn && closeAtPos >= 0 // footnote indent: the item ends at the end of the next item's prefix
|
|
1452
|
+
quirkItem = quirk || fnQuirk ? itemNode : null
|
|
1453
|
+
lateBlank = false
|
|
1454
|
+
if (!quirk && !fnQuirk && pe.line + 1 < lineNo) top.node.spread = true
|
|
1455
|
+
else if (fnQuirk && !quirk && pe.line + 1 < lineNo) itemNode.spread = true
|
|
1456
|
+
} else {
|
|
1457
|
+
closeForNew(ci + 1)
|
|
1458
|
+
if (top.kind === LIST) { closeForNew(ci); ci--; if (lateBlank) { lateBlank = false; attribBlank(ci) } }
|
|
1459
|
+
const lnode = {type: 'list', ordered, start: ordered ? value : null, spread: false, children: [], position: {start: P(lineNo, ls, ns), end: undefined}}
|
|
1460
|
+
stk[ci].node.children.push(lnode)
|
|
1461
|
+
const lc = cont(LIST, lnode)
|
|
1462
|
+
lc.ord = ordered; lc.mk = delim
|
|
1463
|
+
stk[sp++] = lc
|
|
1464
|
+
ci = sp - 1
|
|
1465
|
+
}
|
|
1466
|
+
const lcont = stk[ci]
|
|
1467
|
+
const item = {type: 'listItem', spread: false, checked: null, children: [], position: {start: P(lineNo, ls, ns), end: undefined}}
|
|
1468
|
+
lcont.node.children.push(item)
|
|
1469
|
+
const ic = cont(ITEM, item)
|
|
1470
|
+
const initialSize = indent
|
|
1471
|
+
stk[sp++] = ic
|
|
1472
|
+
advance(indent + mlen)
|
|
1473
|
+
lineTok = true; lineCont = true
|
|
1474
|
+
if (restBlank) {
|
|
1475
|
+
if (quirkItem) { quirkItem.position.end = P(lineNo, ls, pos); quirkItem = null }
|
|
1476
|
+
if (j === m && eolLen > 0) emptyItemLine = lineNo
|
|
1477
|
+
ic.sb = true
|
|
1478
|
+
ic.cc = initialSize + mlen + 1
|
|
1479
|
+
blank2 = true
|
|
1480
|
+
return true
|
|
1481
|
+
}
|
|
1482
|
+
if (wsCols <= 4 && true) {
|
|
1483
|
+
advance(wsCols)
|
|
1484
|
+
ic.cc = initialSize + mlen + wsCols
|
|
1485
|
+
} else {
|
|
1486
|
+
advance(1)
|
|
1487
|
+
ic.cc = initialSize + mlen + 1
|
|
1488
|
+
}
|
|
1489
|
+
if (quirkItem) { quirkItem.position.end = P(lineNo, ls, pos); quirkItem = null }
|
|
1490
|
+
return true
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// ---- footnote definitions ----
|
|
1494
|
+
function tryFootnote(ci) {
|
|
1495
|
+
if (noContainers) return false
|
|
1496
|
+
let i = ns + 2
|
|
1497
|
+
let size = 0
|
|
1498
|
+
let data = false
|
|
1499
|
+
for (;;) {
|
|
1500
|
+
if (i >= le) return false
|
|
1501
|
+
const c = src.charCodeAt(i)
|
|
1502
|
+
if (size > 999 || (c === 93 && !data) || c === 91 || c === 32 || c === 9) return false
|
|
1503
|
+
if (c === 93) break
|
|
1504
|
+
data = true
|
|
1505
|
+
size++
|
|
1506
|
+
i++
|
|
1507
|
+
if (c === 92) {
|
|
1508
|
+
const d = src.charCodeAt(i)
|
|
1509
|
+
if (d === 91 || d === 92 || d === 93) { i++; size++ }
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
if (src.charCodeAt(i + 1) !== 58) return false
|
|
1513
|
+
const raw = src.slice(ns + 2, i)
|
|
1514
|
+
fnIds.add(normalizeIdentifier(raw))
|
|
1515
|
+
ci = prepCI(ci, true, true, true)
|
|
1516
|
+
const label = raw.indexOf('\\') === -1 && raw.indexOf('&') === -1 ? raw : decodeString(raw)
|
|
1517
|
+
const node = {
|
|
1518
|
+
type: 'footnoteDefinition', identifier: normalizeIdentifier(raw).toLowerCase(), label, children: [],
|
|
1519
|
+
position: {start: P(lineNo, ls, ns), end: undefined}
|
|
1520
|
+
}
|
|
1521
|
+
stk[ci].node.children.push(node)
|
|
1522
|
+
if (streamOn) fnLog.push(normalizeIdentifier(raw), topNow())
|
|
1523
|
+
stk[sp++] = cont(FN, node)
|
|
1524
|
+
advance(indent + (i + 2 - ns))
|
|
1525
|
+
// whitespace
|
|
1526
|
+
let w = 0
|
|
1527
|
+
{
|
|
1528
|
+
let cur = col + vleft
|
|
1529
|
+
let j = pos
|
|
1530
|
+
while (j < le) {
|
|
1531
|
+
const c = src.charCodeAt(j)
|
|
1532
|
+
if (c === 32) { cur++; j++ } else if (c === 9) { cur += 4 - (cur & 3); j++ } else break
|
|
1533
|
+
}
|
|
1534
|
+
w = cur - (col + vleft)
|
|
1535
|
+
}
|
|
1536
|
+
advance(w)
|
|
1537
|
+
lineTok = true; lineCont = true
|
|
1538
|
+
return true
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// ---- tables ----
|
|
1542
|
+
function tryTableHead(ci) {
|
|
1543
|
+
// head row = current line from ns. next line must be delimiter row
|
|
1544
|
+
if (indent >= 4) return false // a paragraph continuation, not a table head
|
|
1545
|
+
if (le + eolLen >= len || eolLen === 0) return false
|
|
1546
|
+
// quick pre-check of next raw line
|
|
1547
|
+
const nls = le + eolLen
|
|
1548
|
+
{
|
|
1549
|
+
let i = nls
|
|
1550
|
+
let dash = false
|
|
1551
|
+
for (;;) {
|
|
1552
|
+
if (i >= len) break
|
|
1553
|
+
const c = src.charCodeAt(i)
|
|
1554
|
+
if (c === 10 || c === 13) break
|
|
1555
|
+
if (c === 45) dash = true
|
|
1556
|
+
else if (c !== 124 && c !== 58 && c !== 32 && c !== 9 && c !== 62) return false
|
|
1557
|
+
i++
|
|
1558
|
+
}
|
|
1559
|
+
if (!dash) return false
|
|
1560
|
+
}
|
|
1561
|
+
const size = headRowSize(ns, le)
|
|
1562
|
+
if (size === 0) return false
|
|
1563
|
+
// peek delimiter row
|
|
1564
|
+
const al = peekDelimiter(size, ci)
|
|
1565
|
+
if (!al) return false
|
|
1566
|
+
// commit table
|
|
1567
|
+
ci = prepCI(ci, true, false)
|
|
1568
|
+
const node = {type: 'table', align: al, children: [], position: {start: P(lineNo, ls, ns), end: undefined}}
|
|
1569
|
+
stk[ci].node.children.push(node)
|
|
1570
|
+
tNode = node
|
|
1571
|
+
leaf = TABLE
|
|
1572
|
+
tState = 1
|
|
1573
|
+
addRow(ns, le)
|
|
1574
|
+
lineTok = true; lineCont = true
|
|
1575
|
+
return true
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
function headRowSize(from, to) {
|
|
1579
|
+
let size = 0, sizeB = 0, seen = false
|
|
1580
|
+
let i = from
|
|
1581
|
+
if (src.charCodeAt(i) !== 124) { seen = true; sizeB += 1 }
|
|
1582
|
+
for (;;) {
|
|
1583
|
+
if (i >= to) return sizeB > 1 ? size : 0
|
|
1584
|
+
const c = src.charCodeAt(i)
|
|
1585
|
+
if (c === 32 || c === 9) { i++; continue }
|
|
1586
|
+
sizeB += 1
|
|
1587
|
+
if (seen) { seen = false; size += 1 }
|
|
1588
|
+
if (c === 124) { seen = true; i++; continue }
|
|
1589
|
+
// data
|
|
1590
|
+
for (;;) {
|
|
1591
|
+
if (i >= to) break
|
|
1592
|
+
const d = src.charCodeAt(i)
|
|
1593
|
+
if (d === 124 || d === 32 || d === 9) break
|
|
1594
|
+
i++
|
|
1595
|
+
if (d === 92) {
|
|
1596
|
+
const e = src.charCodeAt(i)
|
|
1597
|
+
if ((e === 92 || e === 124) && i < to) i++
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
function peekDelimiter(size, ci) {
|
|
1604
|
+
// save state
|
|
1605
|
+
const s_ls = ls, s_le = le, s_eol = eolLen, s_line = lineNo, s_pos = pos, s_col = col, s_vleft = vleft
|
|
1606
|
+
const s_ns = ns, s_indent = indent, s_blank = blank
|
|
1607
|
+
let result = null
|
|
1608
|
+
ls = le + eolLen
|
|
1609
|
+
if (!hasCR) {
|
|
1610
|
+
le = src.indexOf('\n', ls)
|
|
1611
|
+
if (le === -1) { le = len; eolLen = 0 } else eolLen = 1
|
|
1612
|
+
} else {
|
|
1613
|
+
let i = ls
|
|
1614
|
+
for (;;) {
|
|
1615
|
+
if (i >= len) { le = len; eolLen = 0; break }
|
|
1616
|
+
const c = src.charCodeAt(i)
|
|
1617
|
+
if (c === 10) { le = i; eolLen = 1; break }
|
|
1618
|
+
if (c === 13) { le = i; eolLen = src.charCodeAt(i + 1) === 10 ? 2 : 1; break }
|
|
1619
|
+
i++
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
lineNo++
|
|
1623
|
+
pos = ls; col = 0; vleft = 0
|
|
1624
|
+
let ok = true
|
|
1625
|
+
const cnt = stk[ci].kind === LIST ? ci : ci + 1
|
|
1626
|
+
for (let d = 1; d < cnt; d++) {
|
|
1627
|
+
const c = stk[d]
|
|
1628
|
+
const k = c.kind
|
|
1629
|
+
if (k === BQ) {
|
|
1630
|
+
findNS()
|
|
1631
|
+
if (indent <= 3 && ns < le && src.charCodeAt(ns) === 62) {
|
|
1632
|
+
advance(indent + 1)
|
|
1633
|
+
if (pos < le) { const ch = src.charCodeAt(pos); if (ch === 32 || ch === 9) advance(1) }
|
|
1634
|
+
continue
|
|
1635
|
+
}
|
|
1636
|
+
ok = false; break
|
|
1637
|
+
}
|
|
1638
|
+
if (k === LIST) continue
|
|
1639
|
+
findNS()
|
|
1640
|
+
if (k === ITEM) {
|
|
1641
|
+
if (blank || c.fb || c.sb || indent === 0 || indent < c.cc) { ok = false; break }
|
|
1642
|
+
advance(c.cc)
|
|
1643
|
+
continue
|
|
1644
|
+
}
|
|
1645
|
+
if (blank || indent < 4) { ok = false; break }
|
|
1646
|
+
advance(4)
|
|
1647
|
+
}
|
|
1648
|
+
if (ok) {
|
|
1649
|
+
findNS()
|
|
1650
|
+
if (!blank) {
|
|
1651
|
+
// not a new container
|
|
1652
|
+
const ch = src.charCodeAt(ns)
|
|
1653
|
+
let container = false
|
|
1654
|
+
if (indent <= 3 && !noContainers) {
|
|
1655
|
+
if (ch === 62) container = true
|
|
1656
|
+
else if ((ch === 45 || ch === 43 || ch === 42) && (ns + 1 >= le || isWs(src.charCodeAt(ns + 1)))) container = true
|
|
1657
|
+
else if (ch >= 48 && ch <= 57) container = true // approximate
|
|
1658
|
+
else if (ch === 91 && src.charCodeAt(ns + 1) === 94) container = true
|
|
1659
|
+
}
|
|
1660
|
+
if (!container || (ch >= 48 && ch <= 57 && !digitItem())) {
|
|
1661
|
+
result = delimRow(ns, le, size, indent)
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
ls = s_ls; le = s_le; eolLen = s_eol; lineNo = s_line; pos = s_pos; col = s_col; vleft = s_vleft
|
|
1666
|
+
ns = s_ns; indent = s_indent; blank = s_blank
|
|
1667
|
+
return result
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
function digitItem() {
|
|
1671
|
+
let i = ns, n = 0
|
|
1672
|
+
while (i < le) { const c = src.charCodeAt(i); if (c >= 48 && c <= 57) { n++; i++ } else break }
|
|
1673
|
+
if (n === 0 || n > 9) return false
|
|
1674
|
+
const d = src.charCodeAt(i)
|
|
1675
|
+
return d === 46 || d === 41
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
// returns align array or null
|
|
1679
|
+
function delimRow(from, to, size, ind) {
|
|
1680
|
+
let i = from
|
|
1681
|
+
let sizeB = 0
|
|
1682
|
+
let seen = false
|
|
1683
|
+
const aligns = []
|
|
1684
|
+
if (ind > 3) return null
|
|
1685
|
+
const c0 = src.charCodeAt(i)
|
|
1686
|
+
if (c0 === 124) { seen = true; i++ }
|
|
1687
|
+
else if (c0 !== 45 && c0 !== 58) return null
|
|
1688
|
+
for (;;) {
|
|
1689
|
+
// cell: ws, :?-+:? ws
|
|
1690
|
+
while (i < to && isWs(src.charCodeAt(i))) i++
|
|
1691
|
+
let c = src.charCodeAt(i)
|
|
1692
|
+
if (i >= to) break
|
|
1693
|
+
let left = false, right = false
|
|
1694
|
+
if (c === 58) { sizeB++; seen = true; left = true; i++; c = src.charCodeAt(i); if (c !== 45) return null }
|
|
1695
|
+
else if (c === 45) { sizeB++ }
|
|
1696
|
+
else return null
|
|
1697
|
+
while (src.charCodeAt(i) === 45 && i < to) i++
|
|
1698
|
+
if (src.charCodeAt(i) === 58 && i < to) { seen = true; right = true; i++ }
|
|
1699
|
+
while (i < to && isWs(src.charCodeAt(i))) i++
|
|
1700
|
+
aligns.push(left && right ? 'center' : left ? 'left' : right ? 'right' : null)
|
|
1701
|
+
if (i >= to) break
|
|
1702
|
+
if (src.charCodeAt(i) === 124) {
|
|
1703
|
+
seen = true; i++
|
|
1704
|
+
continue
|
|
1705
|
+
}
|
|
1706
|
+
return null
|
|
1707
|
+
}
|
|
1708
|
+
if (!seen || size !== sizeB) return null
|
|
1709
|
+
return aligns
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
function addTableRow() {
|
|
1713
|
+
// body row: line from ns
|
|
1714
|
+
addRow(ns, le)
|
|
1715
|
+
lineTok = true; lineCont = true
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
function addRow(from, to) {
|
|
1719
|
+
// build row with cells per micromark resolveTable semantics
|
|
1720
|
+
const row = {type: 'tableRow', children: [], position: {start: P(lineNo, ls, from), end: P(lineNo, ls, to)}}
|
|
1721
|
+
// tokenise: dividers and data
|
|
1722
|
+
// events list: [idx] -> {kind: 'd' divider | 'data', s, e}
|
|
1723
|
+
const ev = [null]
|
|
1724
|
+
let i = from
|
|
1725
|
+
while (i < to) {
|
|
1726
|
+
const c = src.charCodeAt(i)
|
|
1727
|
+
if (c === 124) { ev.push({k: 1, s: i, e: i + 1}); i++; continue }
|
|
1728
|
+
if (c === 32 || c === 9) { i++; continue }
|
|
1729
|
+
const s = i
|
|
1730
|
+
for (;;) {
|
|
1731
|
+
if (i >= to) break
|
|
1732
|
+
const d = src.charCodeAt(i)
|
|
1733
|
+
if (d === 124 || d === 32 || d === 9) break
|
|
1734
|
+
i++
|
|
1735
|
+
if (d === 92) { const e = src.charCodeAt(i); if ((e === 92 || e === 124) && i < to) i++ }
|
|
1736
|
+
}
|
|
1737
|
+
ev.push({k: 2, s, e: i})
|
|
1738
|
+
}
|
|
1739
|
+
// cells
|
|
1740
|
+
let inFirst = true
|
|
1741
|
+
let lastCell = [0, 0, 0, 0]
|
|
1742
|
+
let cell = [0, 1, 0, 0] // index+1 of row enter => first event index 1 (or row-exit)
|
|
1743
|
+
let cur = undefined
|
|
1744
|
+
const cells = []
|
|
1745
|
+
const pointOf = (idx, rowEnd) => idx === -1 ? to : ev[idx].s // starts
|
|
1746
|
+
// flush
|
|
1747
|
+
function flush(range, rowEnd) {
|
|
1748
|
+
// range[0]: index whose start point closes previous cell
|
|
1749
|
+
if (range[0] !== 0 && cur) cur.e = ev[range[0]] ? ev[range[0]].s : to
|
|
1750
|
+
const startP = range[1] <= ev.length - 1 ? ev[range[1]].s : from
|
|
1751
|
+
const c = {s: range[1] === 1 ? from : startP, e: startP}
|
|
1752
|
+
// first event after row enter starts at row start
|
|
1753
|
+
if (range[1] === 1) c.s = from
|
|
1754
|
+
if (range[2] !== 0) { c.ds = ev[range[2]].s; c.de = ev[range[3]].e }
|
|
1755
|
+
cells.push(c)
|
|
1756
|
+
cur = c
|
|
1757
|
+
if (rowEnd) { c.e = to; cur = undefined }
|
|
1758
|
+
}
|
|
1759
|
+
const n = ev.length - 1
|
|
1760
|
+
for (let idx = 1; idx <= n + 0; idx++) {
|
|
1761
|
+
const e = ev[idx]
|
|
1762
|
+
if (e.k === 2) {
|
|
1763
|
+
inFirst = false
|
|
1764
|
+
if (cell[2] === 0) {
|
|
1765
|
+
if (lastCell[1] !== 0) {
|
|
1766
|
+
cell[0] = cell[1]
|
|
1767
|
+
flush(lastCell, false)
|
|
1768
|
+
lastCell = [0, 0, 0, 0]
|
|
1769
|
+
}
|
|
1770
|
+
cell[2] = idx
|
|
1771
|
+
}
|
|
1772
|
+
cell[3] = idx
|
|
1773
|
+
} else {
|
|
1774
|
+
if (inFirst) inFirst = false
|
|
1775
|
+
else {
|
|
1776
|
+
if (lastCell[1] !== 0) {
|
|
1777
|
+
cell[0] = cell[1]
|
|
1778
|
+
flush(lastCell, false)
|
|
1779
|
+
}
|
|
1780
|
+
lastCell = cell
|
|
1781
|
+
cell = [lastCell[1], idx, 0, 0]
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
// row end
|
|
1786
|
+
if (lastCell[1] !== 0) {
|
|
1787
|
+
cell[0] = cell[1]
|
|
1788
|
+
flush(lastCell, true)
|
|
1789
|
+
} else if (cell[1] !== 0 && n >= 1) {
|
|
1790
|
+
flush(cell, true)
|
|
1791
|
+
}
|
|
1792
|
+
for (const c of cells) {
|
|
1793
|
+
const cn = {type: 'tableCell', children: [], position: {start: P(lineNo, ls, c.s), end: P(lineNo, ls, c.e)}}
|
|
1794
|
+
row.children.push(cn)
|
|
1795
|
+
if (doInline && c.ds !== undefined) pend.push(cn, 'table', lineNo, ls, c.ds, c.de, null, topNow())
|
|
1796
|
+
}
|
|
1797
|
+
tNode.children.push(row)
|
|
1798
|
+
tNode.position.end = P(lineNo, ls, to)
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
// streaming entry point: parse a tail of the document (starting at a top-level safe point) and report what the
|
|
1802
|
+
// stream parser needs: the last safe point, definition / footnote ids by top-level index, root.checked log and the
|
|
1803
|
+
// phrasing nodes whose inline content depends on definition ids
|
|
1804
|
+
export function parseStream(source, opts) {
|
|
1805
|
+
const root = parse(source, {...opts, stream: true, tail: true})
|
|
1806
|
+
return {root, safe: streamSafe, defLog: defLog.slice(), fnLog: fnLog.slice(), rcLog: rcLog.slice(), entries: streamEntries, defIds, fnIds}
|
|
1807
|
+
}
|