summaryrefslogtreecommitdiff
path: root/packages/markdown-it-14.1.0/lib/rules_block
diff options
context:
space:
mode:
Diffstat (limited to 'packages/markdown-it-14.1.0/lib/rules_block')
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_block/blockquote.mjs209
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_block/code.mjs30
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_block/fence.mjs94
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_block/heading.mjs51
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_block/hr.mjs40
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_block/html_block.mjs69
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_block/lheading.mjs82
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_block/list.mjs331
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_block/paragraph.mjs46
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_block/reference.mjs212
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_block/state_block.mjs220
-rw-r--r--packages/markdown-it-14.1.0/lib/rules_block/table.mjs228
12 files changed, 1612 insertions, 0 deletions
diff --git a/packages/markdown-it-14.1.0/lib/rules_block/blockquote.mjs b/packages/markdown-it-14.1.0/lib/rules_block/blockquote.mjs
new file mode 100644
index 0000000..b61da02
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_block/blockquote.mjs
@@ -0,0 +1,209 @@
+// Block quotes
+
+import { isSpace } from '../common/utils.mjs'
+
+export default function blockquote (state, startLine, endLine, silent) {
+ let pos = state.bMarks[startLine] + state.tShift[startLine]
+ let max = state.eMarks[startLine]
+
+ const oldLineMax = state.lineMax
+
+ // if it's indented more than 3 spaces, it should be a code block
+ if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
+
+ // check the block quote marker
+ if (state.src.charCodeAt(pos) !== 0x3E/* > */) { return false }
+
+ // we know that it's going to be a valid blockquote,
+ // so no point trying to find the end of it in silent mode
+ if (silent) { return true }
+
+ const oldBMarks = []
+ const oldBSCount = []
+ const oldSCount = []
+ const oldTShift = []
+
+ const terminatorRules = state.md.block.ruler.getRules('blockquote')
+
+ const oldParentType = state.parentType
+ state.parentType = 'blockquote'
+ let lastLineEmpty = false
+ let nextLine
+
+ // Search the end of the block
+ //
+ // Block ends with either:
+ // 1. an empty line outside:
+ // ```
+ // > test
+ //
+ // ```
+ // 2. an empty line inside:
+ // ```
+ // >
+ // test
+ // ```
+ // 3. another tag:
+ // ```
+ // > test
+ // - - -
+ // ```
+ for (nextLine = startLine; nextLine < endLine; nextLine++) {
+ // check if it's outdented, i.e. it's inside list item and indented
+ // less than said list item:
+ //
+ // ```
+ // 1. anything
+ // > current blockquote
+ // 2. checking this line
+ // ```
+ const isOutdented = state.sCount[nextLine] < state.blkIndent
+
+ pos = state.bMarks[nextLine] + state.tShift[nextLine]
+ max = state.eMarks[nextLine]
+
+ if (pos >= max) {
+ // Case 1: line is not inside the blockquote, and this line is empty.
+ break
+ }
+
+ if (state.src.charCodeAt(pos++) === 0x3E/* > */ && !isOutdented) {
+ // This line is inside the blockquote.
+
+ // set offset past spaces and ">"
+ let initial = state.sCount[nextLine] + 1
+ let spaceAfterMarker
+ let adjustTab
+
+ // skip one optional space after '>'
+ if (state.src.charCodeAt(pos) === 0x20 /* space */) {
+ // ' > test '
+ // ^ -- position start of line here:
+ pos++
+ initial++
+ adjustTab = false
+ spaceAfterMarker = true
+ } else if (state.src.charCodeAt(pos) === 0x09 /* tab */) {
+ spaceAfterMarker = true
+
+ if ((state.bsCount[nextLine] + initial) % 4 === 3) {
+ // ' >\t test '
+ // ^ -- position start of line here (tab has width===1)
+ pos++
+ initial++
+ adjustTab = false
+ } else {
+ // ' >\t test '
+ // ^ -- position start of line here + shift bsCount slightly
+ // to make extra space appear
+ adjustTab = true
+ }
+ } else {
+ spaceAfterMarker = false
+ }
+
+ let offset = initial
+ oldBMarks.push(state.bMarks[nextLine])
+ state.bMarks[nextLine] = pos
+
+ while (pos < max) {
+ const ch = state.src.charCodeAt(pos)
+
+ if (isSpace(ch)) {
+ if (ch === 0x09) {
+ offset += 4 - (offset + state.bsCount[nextLine] + (adjustTab ? 1 : 0)) % 4
+ } else {
+ offset++
+ }
+ } else {
+ break
+ }
+
+ pos++
+ }
+
+ lastLineEmpty = pos >= max
+
+ oldBSCount.push(state.bsCount[nextLine])
+ state.bsCount[nextLine] = state.sCount[nextLine] + 1 + (spaceAfterMarker ? 1 : 0)
+
+ oldSCount.push(state.sCount[nextLine])
+ state.sCount[nextLine] = offset - initial
+
+ oldTShift.push(state.tShift[nextLine])
+ state.tShift[nextLine] = pos - state.bMarks[nextLine]
+ continue
+ }
+
+ // Case 2: line is not inside the blockquote, and the last line was empty.
+ if (lastLineEmpty) { break }
+
+ // Case 3: another tag found.
+ let terminate = false
+ for (let i = 0, l = terminatorRules.length; i < l; i++) {
+ if (terminatorRules[i](state, nextLine, endLine, true)) {
+ terminate = true
+ break
+ }
+ }
+
+ if (terminate) {
+ // Quirk to enforce "hard termination mode" for paragraphs;
+ // normally if you call `tokenize(state, startLine, nextLine)`,
+ // paragraphs will look below nextLine for paragraph continuation,
+ // but if blockquote is terminated by another tag, they shouldn't
+ state.lineMax = nextLine
+
+ if (state.blkIndent !== 0) {
+ // state.blkIndent was non-zero, we now set it to zero,
+ // so we need to re-calculate all offsets to appear as
+ // if indent wasn't changed
+ oldBMarks.push(state.bMarks[nextLine])
+ oldBSCount.push(state.bsCount[nextLine])
+ oldTShift.push(state.tShift[nextLine])
+ oldSCount.push(state.sCount[nextLine])
+ state.sCount[nextLine] -= state.blkIndent
+ }
+
+ break
+ }
+
+ oldBMarks.push(state.bMarks[nextLine])
+ oldBSCount.push(state.bsCount[nextLine])
+ oldTShift.push(state.tShift[nextLine])
+ oldSCount.push(state.sCount[nextLine])
+
+ // A negative indentation means that this is a paragraph continuation
+ //
+ state.sCount[nextLine] = -1
+ }
+
+ const oldIndent = state.blkIndent
+ state.blkIndent = 0
+
+ const token_o = state.push('blockquote_open', 'blockquote', 1)
+ token_o.markup = '>'
+ const lines = [startLine, 0]
+ token_o.map = lines
+
+ state.md.block.tokenize(state, startLine, nextLine)
+
+ const token_c = state.push('blockquote_close', 'blockquote', -1)
+ token_c.markup = '>'
+
+ state.lineMax = oldLineMax
+ state.parentType = oldParentType
+ lines[1] = state.line
+
+ // Restore original tShift; this might not be necessary since the parser
+ // has already been here, but just to make sure we can do that.
+ for (let i = 0; i < oldTShift.length; i++) {
+ state.bMarks[i + startLine] = oldBMarks[i]
+ state.tShift[i + startLine] = oldTShift[i]
+ state.sCount[i + startLine] = oldSCount[i]
+ state.bsCount[i + startLine] = oldBSCount[i]
+ }
+ state.blkIndent = oldIndent
+
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_block/code.mjs b/packages/markdown-it-14.1.0/lib/rules_block/code.mjs
new file mode 100644
index 0000000..e45e6f9
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_block/code.mjs
@@ -0,0 +1,30 @@
+// Code block (4 spaces padded)
+
+export default function code (state, startLine, endLine/*, silent */) {
+ if (state.sCount[startLine] - state.blkIndent < 4) { return false }
+
+ let nextLine = startLine + 1
+ let last = nextLine
+
+ while (nextLine < endLine) {
+ if (state.isEmpty(nextLine)) {
+ nextLine++
+ continue
+ }
+
+ if (state.sCount[nextLine] - state.blkIndent >= 4) {
+ nextLine++
+ last = nextLine
+ continue
+ }
+ break
+ }
+
+ state.line = last
+
+ const token = state.push('code_block', 'code', 0)
+ token.content = state.getLines(startLine, last, 4 + state.blkIndent, false) + '\n'
+ token.map = [startLine, state.line]
+
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_block/fence.mjs b/packages/markdown-it-14.1.0/lib/rules_block/fence.mjs
new file mode 100644
index 0000000..930f7b3
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_block/fence.mjs
@@ -0,0 +1,94 @@
+// fences (``` lang, ~~~ lang)
+
+export default function fence (state, startLine, endLine, silent) {
+ let pos = state.bMarks[startLine] + state.tShift[startLine]
+ let max = state.eMarks[startLine]
+
+ // if it's indented more than 3 spaces, it should be a code block
+ if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
+
+ if (pos + 3 > max) { return false }
+
+ const marker = state.src.charCodeAt(pos)
+
+ if (marker !== 0x7E/* ~ */ && marker !== 0x60 /* ` */) {
+ return false
+ }
+
+ // scan marker length
+ let mem = pos
+ pos = state.skipChars(pos, marker)
+
+ let len = pos - mem
+
+ if (len < 3) { return false }
+
+ const markup = state.src.slice(mem, pos)
+ const params = state.src.slice(pos, max)
+
+ if (marker === 0x60 /* ` */) {
+ if (params.indexOf(String.fromCharCode(marker)) >= 0) {
+ return false
+ }
+ }
+
+ // Since start is found, we can report success here in validation mode
+ if (silent) { return true }
+
+ // search end of block
+ let nextLine = startLine
+ let haveEndMarker = false
+
+ for (;;) {
+ nextLine++
+ if (nextLine >= endLine) {
+ // unclosed block should be autoclosed by end of document.
+ // also block seems to be autoclosed by end of parent
+ break
+ }
+
+ pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]
+ max = state.eMarks[nextLine]
+
+ if (pos < max && state.sCount[nextLine] < state.blkIndent) {
+ // non-empty line with negative indent should stop the list:
+ // - ```
+ // test
+ break
+ }
+
+ if (state.src.charCodeAt(pos) !== marker) { continue }
+
+ if (state.sCount[nextLine] - state.blkIndent >= 4) {
+ // closing fence should be indented less than 4 spaces
+ continue
+ }
+
+ pos = state.skipChars(pos, marker)
+
+ // closing code fence must be at least as long as the opening one
+ if (pos - mem < len) { continue }
+
+ // make sure tail has spaces only
+ pos = state.skipSpaces(pos)
+
+ if (pos < max) { continue }
+
+ haveEndMarker = true
+ // found!
+ break
+ }
+
+ // If a fence has heading spaces, they should be removed from its inner block
+ len = state.sCount[startLine]
+
+ state.line = nextLine + (haveEndMarker ? 1 : 0)
+
+ const token = state.push('fence', 'code', 0)
+ token.info = params
+ token.content = state.getLines(startLine + 1, nextLine, len, true)
+ token.markup = markup
+ token.map = [startLine, state.line]
+
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_block/heading.mjs b/packages/markdown-it-14.1.0/lib/rules_block/heading.mjs
new file mode 100644
index 0000000..d2f7b79
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_block/heading.mjs
@@ -0,0 +1,51 @@
+// heading (#, ##, ...)
+
+import { isSpace } from '../common/utils.mjs'
+
+export default function heading (state, startLine, endLine, silent) {
+ let pos = state.bMarks[startLine] + state.tShift[startLine]
+ let max = state.eMarks[startLine]
+
+ // if it's indented more than 3 spaces, it should be a code block
+ if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
+
+ let ch = state.src.charCodeAt(pos)
+
+ if (ch !== 0x23/* # */ || pos >= max) { return false }
+
+ // count heading level
+ let level = 1
+ ch = state.src.charCodeAt(++pos)
+ while (ch === 0x23/* # */ && pos < max && level <= 6) {
+ level++
+ ch = state.src.charCodeAt(++pos)
+ }
+
+ if (level > 6 || (pos < max && !isSpace(ch))) { return false }
+
+ if (silent) { return true }
+
+ // Let's cut tails like ' ### ' from the end of string
+
+ max = state.skipSpacesBack(max, pos)
+ const tmp = state.skipCharsBack(max, 0x23, pos) // #
+ if (tmp > pos && isSpace(state.src.charCodeAt(tmp - 1))) {
+ max = tmp
+ }
+
+ state.line = startLine + 1
+
+ const token_o = state.push('heading_open', 'h' + String(level), 1)
+ token_o.markup = '########'.slice(0, level)
+ token_o.map = [startLine, state.line]
+
+ const token_i = state.push('inline', '', 0)
+ token_i.content = state.src.slice(pos, max).trim()
+ token_i.map = [startLine, state.line]
+ token_i.children = []
+
+ const token_c = state.push('heading_close', 'h' + String(level), -1)
+ token_c.markup = '########'.slice(0, level)
+
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_block/hr.mjs b/packages/markdown-it-14.1.0/lib/rules_block/hr.mjs
new file mode 100644
index 0000000..d467b21
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_block/hr.mjs
@@ -0,0 +1,40 @@
+// Horizontal rule
+
+import { isSpace } from '../common/utils.mjs'
+
+export default function hr (state, startLine, endLine, silent) {
+ const max = state.eMarks[startLine]
+ // if it's indented more than 3 spaces, it should be a code block
+ if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
+
+ let pos = state.bMarks[startLine] + state.tShift[startLine]
+ const marker = state.src.charCodeAt(pos++)
+
+ // Check hr marker
+ if (marker !== 0x2A/* * */ &&
+ marker !== 0x2D/* - */ &&
+ marker !== 0x5F/* _ */) {
+ return false
+ }
+
+ // markers can be mixed with spaces, but there should be at least 3 of them
+
+ let cnt = 1
+ while (pos < max) {
+ const ch = state.src.charCodeAt(pos++)
+ if (ch !== marker && !isSpace(ch)) { return false }
+ if (ch === marker) { cnt++ }
+ }
+
+ if (cnt < 3) { return false }
+
+ if (silent) { return true }
+
+ state.line = startLine + 1
+
+ const token = state.push('hr', 'hr', 0)
+ token.map = [startLine, state.line]
+ token.markup = Array(cnt + 1).join(String.fromCharCode(marker))
+
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_block/html_block.mjs b/packages/markdown-it-14.1.0/lib/rules_block/html_block.mjs
new file mode 100644
index 0000000..197520f
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_block/html_block.mjs
@@ -0,0 +1,69 @@
+// HTML block
+
+import block_names from '../common/html_blocks.mjs'
+import { HTML_OPEN_CLOSE_TAG_RE } from '../common/html_re.mjs'
+
+// An array of opening and corresponding closing sequences for html tags,
+// last argument defines whether it can terminate a paragraph or not
+//
+const HTML_SEQUENCES = [
+ [/^<(script|pre|style|textarea)(?=(\s|>|$))/i, /<\/(script|pre|style|textarea)>/i, true],
+ [/^<!--/, /-->/, true],
+ [/^<\?/, /\?>/, true],
+ [/^<![A-Z]/, />/, true],
+ [/^<!\[CDATA\[/, /\]\]>/, true],
+ [new RegExp('^</?(' + block_names.join('|') + ')(?=(\\s|/?>|$))', 'i'), /^$/, true],
+ [new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + '\\s*$'), /^$/, false]
+]
+
+export default function html_block (state, startLine, endLine, silent) {
+ let pos = state.bMarks[startLine] + state.tShift[startLine]
+ let max = state.eMarks[startLine]
+
+ // if it's indented more than 3 spaces, it should be a code block
+ if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
+
+ if (!state.md.options.html) { return false }
+
+ if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false }
+
+ let lineText = state.src.slice(pos, max)
+
+ let i = 0
+ for (; i < HTML_SEQUENCES.length; i++) {
+ if (HTML_SEQUENCES[i][0].test(lineText)) { break }
+ }
+ if (i === HTML_SEQUENCES.length) { return false }
+
+ if (silent) {
+ // true if this sequence can be a terminator, false otherwise
+ return HTML_SEQUENCES[i][2]
+ }
+
+ let nextLine = startLine + 1
+
+ // If we are here - we detected HTML block.
+ // Let's roll down till block end.
+ if (!HTML_SEQUENCES[i][1].test(lineText)) {
+ for (; nextLine < endLine; nextLine++) {
+ if (state.sCount[nextLine] < state.blkIndent) { break }
+
+ pos = state.bMarks[nextLine] + state.tShift[nextLine]
+ max = state.eMarks[nextLine]
+ lineText = state.src.slice(pos, max)
+
+ if (HTML_SEQUENCES[i][1].test(lineText)) {
+ if (lineText.length !== 0) { nextLine++ }
+ break
+ }
+ }
+ }
+
+ state.line = nextLine
+
+ const token = state.push('html_block', '', 0)
+ token.map = [startLine, nextLine]
+ token.content = state.getLines(startLine, nextLine, state.blkIndent, true)
+
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_block/lheading.mjs b/packages/markdown-it-14.1.0/lib/rules_block/lheading.mjs
new file mode 100644
index 0000000..ee3b9a3
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_block/lheading.mjs
@@ -0,0 +1,82 @@
+// lheading (---, ===)
+
+export default function lheading (state, startLine, endLine/*, silent */) {
+ const terminatorRules = state.md.block.ruler.getRules('paragraph')
+
+ // if it's indented more than 3 spaces, it should be a code block
+ if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
+
+ const oldParentType = state.parentType
+ state.parentType = 'paragraph' // use paragraph to match terminatorRules
+
+ // jump line-by-line until empty one or EOF
+ let level = 0
+ let marker
+ let nextLine = startLine + 1
+
+ for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
+ // this would be a code block normally, but after paragraph
+ // it's considered a lazy continuation regardless of what's there
+ if (state.sCount[nextLine] - state.blkIndent > 3) { continue }
+
+ //
+ // Check for underline in setext header
+ //
+ if (state.sCount[nextLine] >= state.blkIndent) {
+ let pos = state.bMarks[nextLine] + state.tShift[nextLine]
+ const max = state.eMarks[nextLine]
+
+ if (pos < max) {
+ marker = state.src.charCodeAt(pos)
+
+ if (marker === 0x2D/* - */ || marker === 0x3D/* = */) {
+ pos = state.skipChars(pos, marker)
+ pos = state.skipSpaces(pos)
+
+ if (pos >= max) {
+ level = (marker === 0x3D/* = */ ? 1 : 2)
+ break
+ }
+ }
+ }
+ }
+
+ // quirk for blockquotes, this line should already be checked by that rule
+ if (state.sCount[nextLine] < 0) { continue }
+
+ // Some tags can terminate paragraph without empty line.
+ let terminate = false
+ for (let i = 0, l = terminatorRules.length; i < l; i++) {
+ if (terminatorRules[i](state, nextLine, endLine, true)) {
+ terminate = true
+ break
+ }
+ }
+ if (terminate) { break }
+ }
+
+ if (!level) {
+ // Didn't find valid underline
+ return false
+ }
+
+ const content = state.getLines(startLine, nextLine, state.blkIndent, false).trim()
+
+ state.line = nextLine + 1
+
+ const token_o = state.push('heading_open', 'h' + String(level), 1)
+ token_o.markup = String.fromCharCode(marker)
+ token_o.map = [startLine, state.line]
+
+ const token_i = state.push('inline', '', 0)
+ token_i.content = content
+ token_i.map = [startLine, state.line - 1]
+ token_i.children = []
+
+ const token_c = state.push('heading_close', 'h' + String(level), -1)
+ token_c.markup = String.fromCharCode(marker)
+
+ state.parentType = oldParentType
+
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_block/list.mjs b/packages/markdown-it-14.1.0/lib/rules_block/list.mjs
new file mode 100644
index 0000000..fb53abd
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_block/list.mjs
@@ -0,0 +1,331 @@
+// Lists
+
+import { isSpace } from '../common/utils.mjs'
+
+// Search `[-+*][\n ]`, returns next pos after marker on success
+// or -1 on fail.
+function skipBulletListMarker (state, startLine) {
+ const max = state.eMarks[startLine]
+ let pos = state.bMarks[startLine] + state.tShift[startLine]
+
+ const marker = state.src.charCodeAt(pos++)
+ // Check bullet
+ if (marker !== 0x2A/* * */ &&
+ marker !== 0x2D/* - */ &&
+ marker !== 0x2B/* + */) {
+ return -1
+ }
+
+ if (pos < max) {
+ const ch = state.src.charCodeAt(pos)
+
+ if (!isSpace(ch)) {
+ // " -test " - is not a list item
+ return -1
+ }
+ }
+
+ return pos
+}
+
+// Search `\d+[.)][\n ]`, returns next pos after marker on success
+// or -1 on fail.
+function skipOrderedListMarker (state, startLine) {
+ const start = state.bMarks[startLine] + state.tShift[startLine]
+ const max = state.eMarks[startLine]
+ let pos = start
+
+ // List marker should have at least 2 chars (digit + dot)
+ if (pos + 1 >= max) { return -1 }
+
+ let ch = state.src.charCodeAt(pos++)
+
+ if (ch < 0x30/* 0 */ || ch > 0x39/* 9 */) { return -1 }
+
+ for (;;) {
+ // EOL -> fail
+ if (pos >= max) { return -1 }
+
+ ch = state.src.charCodeAt(pos++)
+
+ if (ch >= 0x30/* 0 */ && ch <= 0x39/* 9 */) {
+ // List marker should have no more than 9 digits
+ // (prevents integer overflow in browsers)
+ if (pos - start >= 10) { return -1 }
+
+ continue
+ }
+
+ // found valid marker
+ if (ch === 0x29/* ) */ || ch === 0x2e/* . */) {
+ break
+ }
+
+ return -1
+ }
+
+ if (pos < max) {
+ ch = state.src.charCodeAt(pos)
+
+ if (!isSpace(ch)) {
+ // " 1.test " - is not a list item
+ return -1
+ }
+ }
+ return pos
+}
+
+function markTightParagraphs (state, idx) {
+ const level = state.level + 2
+
+ for (let i = idx + 2, l = state.tokens.length - 2; i < l; i++) {
+ if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') {
+ state.tokens[i + 2].hidden = true
+ state.tokens[i].hidden = true
+ i += 2
+ }
+ }
+}
+
+export default function list (state, startLine, endLine, silent) {
+ let max, pos, start, token
+ let nextLine = startLine
+ let tight = true
+
+ // if it's indented more than 3 spaces, it should be a code block
+ if (state.sCount[nextLine] - state.blkIndent >= 4) { return false }
+
+ // Special case:
+ // - item 1
+ // - item 2
+ // - item 3
+ // - item 4
+ // - this one is a paragraph continuation
+ if (state.listIndent >= 0 &&
+ state.sCount[nextLine] - state.listIndent >= 4 &&
+ state.sCount[nextLine] < state.blkIndent) {
+ return false
+ }
+
+ let isTerminatingParagraph = false
+
+ // limit conditions when list can interrupt
+ // a paragraph (validation mode only)
+ if (silent && state.parentType === 'paragraph') {
+ // Next list item should still terminate previous list item;
+ //
+ // This code can fail if plugins use blkIndent as well as lists,
+ // but I hope the spec gets fixed long before that happens.
+ //
+ if (state.sCount[nextLine] >= state.blkIndent) {
+ isTerminatingParagraph = true
+ }
+ }
+
+ // Detect list type and position after marker
+ let isOrdered
+ let markerValue
+ let posAfterMarker
+ if ((posAfterMarker = skipOrderedListMarker(state, nextLine)) >= 0) {
+ isOrdered = true
+ start = state.bMarks[nextLine] + state.tShift[nextLine]
+ markerValue = Number(state.src.slice(start, posAfterMarker - 1))
+
+ // If we're starting a new ordered list right after
+ // a paragraph, it should start with 1.
+ if (isTerminatingParagraph && markerValue !== 1) return false
+ } else if ((posAfterMarker = skipBulletListMarker(state, nextLine)) >= 0) {
+ isOrdered = false
+ } else {
+ return false
+ }
+
+ // If we're starting a new unordered list right after
+ // a paragraph, first line should not be empty.
+ if (isTerminatingParagraph) {
+ if (state.skipSpaces(posAfterMarker) >= state.eMarks[nextLine]) return false
+ }
+
+ // For validation mode we can terminate immediately
+ if (silent) { return true }
+
+ // We should terminate list on style change. Remember first one to compare.
+ const markerCharCode = state.src.charCodeAt(posAfterMarker - 1)
+
+ // Start list
+ const listTokIdx = state.tokens.length
+
+ if (isOrdered) {
+ token = state.push('ordered_list_open', 'ol', 1)
+ if (markerValue !== 1) {
+ token.attrs = [['start', markerValue]]
+ }
+ } else {
+ token = state.push('bullet_list_open', 'ul', 1)
+ }
+
+ const listLines = [nextLine, 0]
+ token.map = listLines
+ token.markup = String.fromCharCode(markerCharCode)
+
+ //
+ // Iterate list items
+ //
+
+ let prevEmptyEnd = false
+ const terminatorRules = state.md.block.ruler.getRules('list')
+
+ const oldParentType = state.parentType
+ state.parentType = 'list'
+
+ while (nextLine < endLine) {
+ pos = posAfterMarker
+ max = state.eMarks[nextLine]
+
+ const initial = state.sCount[nextLine] + posAfterMarker - (state.bMarks[nextLine] + state.tShift[nextLine])
+ let offset = initial
+
+ while (pos < max) {
+ const ch = state.src.charCodeAt(pos)
+
+ if (ch === 0x09) {
+ offset += 4 - (offset + state.bsCount[nextLine]) % 4
+ } else if (ch === 0x20) {
+ offset++
+ } else {
+ break
+ }
+
+ pos++
+ }
+
+ const contentStart = pos
+ let indentAfterMarker
+
+ if (contentStart >= max) {
+ // trimming space in "- \n 3" case, indent is 1 here
+ indentAfterMarker = 1
+ } else {
+ indentAfterMarker = offset - initial
+ }
+
+ // If we have more than 4 spaces, the indent is 1
+ // (the rest is just indented code block)
+ if (indentAfterMarker > 4) { indentAfterMarker = 1 }
+
+ // " - test"
+ // ^^^^^ - calculating total length of this thing
+ const indent = initial + indentAfterMarker
+
+ // Run subparser & write tokens
+ token = state.push('list_item_open', 'li', 1)
+ token.markup = String.fromCharCode(markerCharCode)
+ const itemLines = [nextLine, 0]
+ token.map = itemLines
+ if (isOrdered) {
+ token.info = state.src.slice(start, posAfterMarker - 1)
+ }
+
+ // change current state, then restore it after parser subcall
+ const oldTight = state.tight
+ const oldTShift = state.tShift[nextLine]
+ const oldSCount = state.sCount[nextLine]
+
+ // - example list
+ // ^ listIndent position will be here
+ // ^ blkIndent position will be here
+ //
+ const oldListIndent = state.listIndent
+ state.listIndent = state.blkIndent
+ state.blkIndent = indent
+
+ state.tight = true
+ state.tShift[nextLine] = contentStart - state.bMarks[nextLine]
+ state.sCount[nextLine] = offset
+
+ if (contentStart >= max && state.isEmpty(nextLine + 1)) {
+ // workaround for this case
+ // (list item is empty, list terminates before "foo"):
+ // ~~~~~~~~
+ // -
+ //
+ // foo
+ // ~~~~~~~~
+ state.line = Math.min(state.line + 2, endLine)
+ } else {
+ state.md.block.tokenize(state, nextLine, endLine, true)
+ }
+
+ // If any of list item is tight, mark list as tight
+ if (!state.tight || prevEmptyEnd) {
+ tight = false
+ }
+ // Item become loose if finish with empty line,
+ // but we should filter last element, because it means list finish
+ prevEmptyEnd = (state.line - nextLine) > 1 && state.isEmpty(state.line - 1)
+
+ state.blkIndent = state.listIndent
+ state.listIndent = oldListIndent
+ state.tShift[nextLine] = oldTShift
+ state.sCount[nextLine] = oldSCount
+ state.tight = oldTight
+
+ token = state.push('list_item_close', 'li', -1)
+ token.markup = String.fromCharCode(markerCharCode)
+
+ nextLine = state.line
+ itemLines[1] = nextLine
+
+ if (nextLine >= endLine) { break }
+
+ //
+ // Try to check if list is terminated or continued.
+ //
+ if (state.sCount[nextLine] < state.blkIndent) { break }
+
+ // if it's indented more than 3 spaces, it should be a code block
+ if (state.sCount[nextLine] - state.blkIndent >= 4) { break }
+
+ // fail if terminating block found
+ let terminate = false
+ for (let i = 0, l = terminatorRules.length; i < l; i++) {
+ if (terminatorRules[i](state, nextLine, endLine, true)) {
+ terminate = true
+ break
+ }
+ }
+ if (terminate) { break }
+
+ // fail if list has another type
+ if (isOrdered) {
+ posAfterMarker = skipOrderedListMarker(state, nextLine)
+ if (posAfterMarker < 0) { break }
+ start = state.bMarks[nextLine] + state.tShift[nextLine]
+ } else {
+ posAfterMarker = skipBulletListMarker(state, nextLine)
+ if (posAfterMarker < 0) { break }
+ }
+
+ if (markerCharCode !== state.src.charCodeAt(posAfterMarker - 1)) { break }
+ }
+
+ // Finalize list
+ if (isOrdered) {
+ token = state.push('ordered_list_close', 'ol', -1)
+ } else {
+ token = state.push('bullet_list_close', 'ul', -1)
+ }
+ token.markup = String.fromCharCode(markerCharCode)
+
+ listLines[1] = nextLine
+ state.line = nextLine
+
+ state.parentType = oldParentType
+
+ // mark paragraphs tight if needed
+ if (tight) {
+ markTightParagraphs(state, listTokIdx)
+ }
+
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_block/paragraph.mjs b/packages/markdown-it-14.1.0/lib/rules_block/paragraph.mjs
new file mode 100644
index 0000000..6ecdcef
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_block/paragraph.mjs
@@ -0,0 +1,46 @@
+// Paragraph
+
+export default function paragraph (state, startLine, endLine) {
+ const terminatorRules = state.md.block.ruler.getRules('paragraph')
+ const oldParentType = state.parentType
+ let nextLine = startLine + 1
+ state.parentType = 'paragraph'
+
+ // jump line-by-line until empty one or EOF
+ for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
+ // this would be a code block normally, but after paragraph
+ // it's considered a lazy continuation regardless of what's there
+ if (state.sCount[nextLine] - state.blkIndent > 3) { continue }
+
+ // quirk for blockquotes, this line should already be checked by that rule
+ if (state.sCount[nextLine] < 0) { continue }
+
+ // Some tags can terminate paragraph without empty line.
+ let terminate = false
+ for (let i = 0, l = terminatorRules.length; i < l; i++) {
+ if (terminatorRules[i](state, nextLine, endLine, true)) {
+ terminate = true
+ break
+ }
+ }
+ if (terminate) { break }
+ }
+
+ const content = state.getLines(startLine, nextLine, state.blkIndent, false).trim()
+
+ state.line = nextLine
+
+ const token_o = state.push('paragraph_open', 'p', 1)
+ token_o.map = [startLine, state.line]
+
+ const token_i = state.push('inline', '', 0)
+ token_i.content = content
+ token_i.map = [startLine, state.line]
+ token_i.children = []
+
+ state.push('paragraph_close', 'p', -1)
+
+ state.parentType = oldParentType
+
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_block/reference.mjs b/packages/markdown-it-14.1.0/lib/rules_block/reference.mjs
new file mode 100644
index 0000000..4166286
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_block/reference.mjs
@@ -0,0 +1,212 @@
+import { isSpace, normalizeReference } from '../common/utils.mjs'
+
+export default function reference (state, startLine, _endLine, silent) {
+ let pos = state.bMarks[startLine] + state.tShift[startLine]
+ let max = state.eMarks[startLine]
+ let nextLine = startLine + 1
+
+ // if it's indented more than 3 spaces, it should be a code block
+ if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
+
+ if (state.src.charCodeAt(pos) !== 0x5B/* [ */) { return false }
+
+ function getNextLine (nextLine) {
+ const endLine = state.lineMax
+
+ if (nextLine >= endLine || state.isEmpty(nextLine)) {
+ // empty line or end of input
+ return null
+ }
+
+ let isContinuation = false
+
+ // this would be a code block normally, but after paragraph
+ // it's considered a lazy continuation regardless of what's there
+ if (state.sCount[nextLine] - state.blkIndent > 3) { isContinuation = true }
+
+ // quirk for blockquotes, this line should already be checked by that rule
+ if (state.sCount[nextLine] < 0) { isContinuation = true }
+
+ if (!isContinuation) {
+ const terminatorRules = state.md.block.ruler.getRules('reference')
+ const oldParentType = state.parentType
+ state.parentType = 'reference'
+
+ // Some tags can terminate paragraph without empty line.
+ let terminate = false
+ for (let i = 0, l = terminatorRules.length; i < l; i++) {
+ if (terminatorRules[i](state, nextLine, endLine, true)) {
+ terminate = true
+ break
+ }
+ }
+
+ state.parentType = oldParentType
+ if (terminate) {
+ // terminated by another block
+ return null
+ }
+ }
+
+ const pos = state.bMarks[nextLine] + state.tShift[nextLine]
+ const max = state.eMarks[nextLine]
+
+ // max + 1 explicitly includes the newline
+ return state.src.slice(pos, max + 1)
+ }
+
+ let str = state.src.slice(pos, max + 1)
+
+ max = str.length
+ let labelEnd = -1
+
+ for (pos = 1; pos < max; pos++) {
+ const ch = str.charCodeAt(pos)
+ if (ch === 0x5B /* [ */) {
+ return false
+ } else if (ch === 0x5D /* ] */) {
+ labelEnd = pos
+ break
+ } else if (ch === 0x0A /* \n */) {
+ const lineContent = getNextLine(nextLine)
+ if (lineContent !== null) {
+ str += lineContent
+ max = str.length
+ nextLine++
+ }
+ } else if (ch === 0x5C /* \ */) {
+ pos++
+ if (pos < max && str.charCodeAt(pos) === 0x0A) {
+ const lineContent = getNextLine(nextLine)
+ if (lineContent !== null) {
+ str += lineContent
+ max = str.length
+ nextLine++
+ }
+ }
+ }
+ }
+
+ if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3A/* : */) { return false }
+
+ // [label]: destination 'title'
+ // ^^^ skip optional whitespace here
+ for (pos = labelEnd + 2; pos < max; pos++) {
+ const ch = str.charCodeAt(pos)
+ if (ch === 0x0A) {
+ const lineContent = getNextLine(nextLine)
+ if (lineContent !== null) {
+ str += lineContent
+ max = str.length
+ nextLine++
+ }
+ } else if (isSpace(ch)) {
+ /* eslint no-empty:0 */
+ } else {
+ break
+ }
+ }
+
+ // [label]: destination 'title'
+ // ^^^^^^^^^^^ parse this
+ const destRes = state.md.helpers.parseLinkDestination(str, pos, max)
+ if (!destRes.ok) { return false }
+
+ const href = state.md.normalizeLink(destRes.str)
+ if (!state.md.validateLink(href)) { return false }
+
+ pos = destRes.pos
+
+ // save cursor state, we could require to rollback later
+ const destEndPos = pos
+ const destEndLineNo = nextLine
+
+ // [label]: destination 'title'
+ // ^^^ skipping those spaces
+ const start = pos
+ for (; pos < max; pos++) {
+ const ch = str.charCodeAt(pos)
+ if (ch === 0x0A) {
+ const lineContent = getNextLine(nextLine)
+ if (lineContent !== null) {
+ str += lineContent
+ max = str.length
+ nextLine++
+ }
+ } else if (isSpace(ch)) {
+ /* eslint no-empty:0 */
+ } else {
+ break
+ }
+ }
+
+ // [label]: destination 'title'
+ // ^^^^^^^ parse this
+ let titleRes = state.md.helpers.parseLinkTitle(str, pos, max)
+ while (titleRes.can_continue) {
+ const lineContent = getNextLine(nextLine)
+ if (lineContent === null) break
+ str += lineContent
+ pos = max
+ max = str.length
+ nextLine++
+ titleRes = state.md.helpers.parseLinkTitle(str, pos, max, titleRes)
+ }
+ let title
+
+ if (pos < max && start !== pos && titleRes.ok) {
+ title = titleRes.str
+ pos = titleRes.pos
+ } else {
+ title = ''
+ pos = destEndPos
+ nextLine = destEndLineNo
+ }
+
+ // skip trailing spaces until the rest of the line
+ while (pos < max) {
+ const ch = str.charCodeAt(pos)
+ if (!isSpace(ch)) { break }
+ pos++
+ }
+
+ if (pos < max && str.charCodeAt(pos) !== 0x0A) {
+ if (title) {
+ // garbage at the end of the line after title,
+ // but it could still be a valid reference if we roll back
+ title = ''
+ pos = destEndPos
+ nextLine = destEndLineNo
+ while (pos < max) {
+ const ch = str.charCodeAt(pos)
+ if (!isSpace(ch)) { break }
+ pos++
+ }
+ }
+ }
+
+ if (pos < max && str.charCodeAt(pos) !== 0x0A) {
+ // garbage at the end of the line
+ return false
+ }
+
+ const label = normalizeReference(str.slice(1, labelEnd))
+ if (!label) {
+ // CommonMark 0.20 disallows empty labels
+ return false
+ }
+
+ // Reference can not terminate anything. This check is for safety only.
+ /* istanbul ignore if */
+ if (silent) { return true }
+
+ if (typeof state.env.references === 'undefined') {
+ state.env.references = {}
+ }
+ if (typeof state.env.references[label] === 'undefined') {
+ state.env.references[label] = { title, href }
+ }
+
+ state.line = nextLine
+ return true
+}
diff --git a/packages/markdown-it-14.1.0/lib/rules_block/state_block.mjs b/packages/markdown-it-14.1.0/lib/rules_block/state_block.mjs
new file mode 100644
index 0000000..3c2a876
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_block/state_block.mjs
@@ -0,0 +1,220 @@
+// Parser state class
+
+import Token from '../token.mjs'
+import { isSpace } from '../common/utils.mjs'
+
+function StateBlock (src, md, env, tokens) {
+ this.src = src
+
+ // link to parser instance
+ this.md = md
+
+ this.env = env
+
+ //
+ // Internal state vartiables
+ //
+
+ this.tokens = tokens
+
+ this.bMarks = [] // line begin offsets for fast jumps
+ this.eMarks = [] // line end offsets for fast jumps
+ this.tShift = [] // offsets of the first non-space characters (tabs not expanded)
+ this.sCount = [] // indents for each line (tabs expanded)
+
+ // An amount of virtual spaces (tabs expanded) between beginning
+ // of each line (bMarks) and real beginning of that line.
+ //
+ // It exists only as a hack because blockquotes override bMarks
+ // losing information in the process.
+ //
+ // It's used only when expanding tabs, you can think about it as
+ // an initial tab length, e.g. bsCount=21 applied to string `\t123`
+ // means first tab should be expanded to 4-21%4 === 3 spaces.
+ //
+ this.bsCount = []
+
+ // block parser variables
+
+ // required block content indent (for example, if we are
+ // inside a list, it would be positioned after list marker)
+ this.blkIndent = 0
+ this.line = 0 // line index in src
+ this.lineMax = 0 // lines count
+ this.tight = false // loose/tight mode for lists
+ this.ddIndent = -1 // indent of the current dd block (-1 if there isn't any)
+ this.listIndent = -1 // indent of the current list block (-1 if there isn't any)
+
+ // can be 'blockquote', 'list', 'root', 'paragraph' or 'reference'
+ // used in lists to determine if they interrupt a paragraph
+ this.parentType = 'root'
+
+ this.level = 0
+
+ // Create caches
+ // Generate markers.
+ const s = this.src
+
+ for (let start = 0, pos = 0, indent = 0, offset = 0, len = s.length, indent_found = false; pos < len; pos++) {
+ const ch = s.charCodeAt(pos)
+
+ if (!indent_found) {
+ if (isSpace(ch)) {
+ indent++
+
+ if (ch === 0x09) {
+ offset += 4 - offset % 4
+ } else {
+ offset++
+ }
+ continue
+ } else {
+ indent_found = true
+ }
+ }
+
+ if (ch === 0x0A || pos === len - 1) {
+ if (ch !== 0x0A) { pos++ }
+ this.bMarks.push(start)
+ this.eMarks.push(pos)
+ this.tShift.push(indent)
+ this.sCount.push(offset)
+ this.bsCount.push(0)
+
+ indent_found = false
+ indent = 0
+ offset = 0
+ start = pos + 1
+ }
+ }
+
+ // Push fake entry to simplify cache bounds checks
+ this.bMarks.push(s.length)
+ this.eMarks.push(s.length)
+ this.tShift.push(0)
+ this.sCount.push(0)
+ this.bsCount.push(0)
+
+ this.lineMax = this.bMarks.length - 1 // don't count last fake line
+}
+
+// Push new token to "stream".
+//
+StateBlock.prototype.push = function (type, tag, nesting) {
+ const token = new Token(type, tag, nesting)
+ token.block = true
+
+ if (nesting < 0) this.level-- // closing tag
+ token.level = this.level
+ if (nesting > 0) this.level++ // opening tag
+
+ this.tokens.push(token)
+ return token
+}
+
+StateBlock.prototype.isEmpty = function isEmpty (line) {
+ return this.bMarks[line] + this.tShift[line] >= this.eMarks[line]
+}
+
+StateBlock.prototype.skipEmptyLines = function skipEmptyLines (from) {
+ for (let max = this.lineMax; from < max; from++) {
+ if (this.bMarks[from] + this.tShift[from] < this.eMarks[from]) {
+ break
+ }
+ }
+ return from
+}
+
+// Skip spaces from given position.
+StateBlock.prototype.skipSpaces = function skipSpaces (pos) {
+ for (let max = this.src.length; pos < max; pos++) {
+ const ch = this.src.charCodeAt(pos)
+ if (!isSpace(ch)) { break }
+ }
+ return pos
+}
+
+// Skip spaces from given position in reverse.
+StateBlock.prototype.skipSpacesBack = function skipSpacesBack (pos, min) {
+ if (pos <= min) { return pos }
+
+ while (pos > min) {
+ if (!isSpace(this.src.charCodeAt(--pos))) { return pos + 1 }
+ }
+ return pos
+}
+
+// Skip char codes from given position
+StateBlock.prototype.skipChars = function skipChars (pos, code) {
+ for (let max = this.src.length; pos < max; pos++) {
+ if (this.src.charCodeAt(pos) !== code) { break }
+ }
+ return pos
+}
+
+// Skip char codes reverse from given position - 1
+StateBlock.prototype.skipCharsBack = function skipCharsBack (pos, code, min) {
+ if (pos <= min) { return pos }
+
+ while (pos > min) {
+ if (code !== this.src.charCodeAt(--pos)) { return pos + 1 }
+ }
+ return pos
+}
+
+// cut lines range from source.
+StateBlock.prototype.getLines = function getLines (begin, end, indent, keepLastLF) {
+ if (begin >= end) {
+ return ''
+ }
+
+ const queue = new Array(end - begin)
+
+ for (let i = 0, line = begin; line < end; line++, i++) {
+ let lineIndent = 0
+ const lineStart = this.bMarks[line]
+ let first = lineStart
+ let last
+
+ if (line + 1 < end || keepLastLF) {
+ // No need for bounds check because we have fake entry on tail.
+ last = this.eMarks[line] + 1
+ } else {
+ last = this.eMarks[line]
+ }
+
+ while (first < last && lineIndent < indent) {
+ const ch = this.src.charCodeAt(first)
+
+ if (isSpace(ch)) {
+ if (ch === 0x09) {
+ lineIndent += 4 - (lineIndent + this.bsCount[line]) % 4
+ } else {
+ lineIndent++
+ }
+ } else if (first - lineStart < this.tShift[line]) {
+ // patched tShift masked characters to look like spaces (blockquotes, list markers)
+ lineIndent++
+ } else {
+ break
+ }
+
+ first++
+ }
+
+ if (lineIndent > indent) {
+ // partially expanding tabs in code blocks, e.g '\t\tfoobar'
+ // with indent=2 becomes ' \tfoobar'
+ queue[i] = new Array(lineIndent - indent + 1).join(' ') + this.src.slice(first, last)
+ } else {
+ queue[i] = this.src.slice(first, last)
+ }
+ }
+
+ return queue.join('')
+}
+
+// re-export Token class to use in block rules
+StateBlock.prototype.Token = Token
+
+export default StateBlock
diff --git a/packages/markdown-it-14.1.0/lib/rules_block/table.mjs b/packages/markdown-it-14.1.0/lib/rules_block/table.mjs
new file mode 100644
index 0000000..be0ba0a
--- /dev/null
+++ b/packages/markdown-it-14.1.0/lib/rules_block/table.mjs
@@ -0,0 +1,228 @@
+// GFM table, https://github.github.com/gfm/#tables-extension-
+
+import { isSpace } from '../common/utils.mjs'
+
+// Limit the amount of empty autocompleted cells in a table,
+// see https://github.com/markdown-it/markdown-it/issues/1000,
+//
+// Both pulldown-cmark and commonmark-hs limit the number of cells this way to ~200k.
+// We set it to 65k, which can expand user input by a factor of x370
+// (256x256 square is 1.8kB expanded into 650kB).
+const MAX_AUTOCOMPLETED_CELLS = 0x10000
+
+function getLine (state, line) {
+ const pos = state.bMarks[line] + state.tShift[line]
+ const max = state.eMarks[line]
+
+ return state.src.slice(pos, max)
+}
+
+function escapedSplit (str) {
+ const result = []
+ const max = str.length
+
+ let pos = 0
+ let ch = str.charCodeAt(pos)
+ let isEscaped = false
+ let lastPos = 0
+ let current = ''
+
+ while (pos < max) {
+ if (ch === 0x7c/* | */) {
+ if (!isEscaped) {
+ // pipe separating cells, '|'
+ result.push(current + str.substring(lastPos, pos))
+ current = ''
+ lastPos = pos + 1
+ } else {
+ // escaped pipe, '\|'
+ current += str.substring(lastPos, pos - 1)
+ lastPos = pos
+ }
+ }
+
+ isEscaped = (ch === 0x5c/* \ */)
+ pos++
+
+ ch = str.charCodeAt(pos)
+ }
+
+ result.push(current + str.substring(lastPos))
+
+ return result
+}
+
+export default function table (state, startLine, endLine, silent) {
+ // should have at least two lines
+ if (startLine + 2 > endLine) { return false }
+
+ let nextLine = startLine + 1
+
+ if (state.sCount[nextLine] < state.blkIndent) { return false }
+
+ // if it's indented more than 3 spaces, it should be a code block
+ if (state.sCount[nextLine] - state.blkIndent >= 4) { return false }
+
+ // first character of the second line should be '|', '-', ':',
+ // and no other characters are allowed but spaces;
+ // basically, this is the equivalent of /^[-:|][-:|\s]*$/ regexp
+
+ let pos = state.bMarks[nextLine] + state.tShift[nextLine]
+ if (pos >= state.eMarks[nextLine]) { return false }
+
+ const firstCh = state.src.charCodeAt(pos++)
+ if (firstCh !== 0x7C/* | */ && firstCh !== 0x2D/* - */ && firstCh !== 0x3A/* : */) { return false }
+
+ if (pos >= state.eMarks[nextLine]) { return false }
+
+ const secondCh = state.src.charCodeAt(pos++)
+ if (secondCh !== 0x7C/* | */ && secondCh !== 0x2D/* - */ && secondCh !== 0x3A/* : */ && !isSpace(secondCh)) {
+ return false
+ }
+
+ // if first character is '-', then second character must not be a space
+ // (due to parsing ambiguity with list)
+ if (firstCh === 0x2D/* - */ && isSpace(secondCh)) { return false }
+
+ while (pos < state.eMarks[nextLine]) {
+ const ch = state.src.charCodeAt(pos)
+
+ if (ch !== 0x7C/* | */ && ch !== 0x2D/* - */ && ch !== 0x3A/* : */ && !isSpace(ch)) { return false }
+
+ pos++
+ }
+
+ let lineText = getLine(state, startLine + 1)
+ let columns = lineText.split('|')
+ const aligns = []
+ for (let i = 0; i < columns.length; i++) {
+ const t = columns[i].trim()
+ if (!t) {
+ // allow empty columns before and after table, but not in between columns;
+ // e.g. allow ` |---| `, disallow ` ---||--- `
+ if (i === 0 || i === columns.length - 1) {
+ continue
+ } else {
+ return false
+ }
+ }
+
+ if (!/^:?-+:?$/.test(t)) { return false }
+ if (t.charCodeAt(t.length - 1) === 0x3A/* : */) {
+ aligns.push(t.charCodeAt(0) === 0x3A/* : */ ? 'center' : 'right')
+ } else if (t.charCodeAt(0) === 0x3A/* : */) {
+ aligns.push('left')
+ } else {
+ aligns.push('')
+ }
+ }
+
+ lineText = getLine(state, startLine).trim()
+ if (lineText.indexOf('|') === -1) { return false }
+ if (state.sCount[startLine] - state.blkIndent >= 4) { return false }
+ columns = escapedSplit(lineText)
+ if (columns.length && columns[0] === '') columns.shift()
+ if (columns.length && columns[columns.length - 1] === '') columns.pop()
+
+ // header row will define an amount of columns in the entire table,
+ // and align row should be exactly the same (the rest of the rows can differ)
+ const columnCount = columns.length
+ if (columnCount === 0 || columnCount !== aligns.length) { return false }
+
+ if (silent) { return true }
+
+ const oldParentType = state.parentType
+ state.parentType = 'table'
+
+ // use 'blockquote' lists for termination because it's
+ // the most similar to tables
+ const terminatorRules = state.md.block.ruler.getRules('blockquote')
+
+ const token_to = state.push('table_open', 'table', 1)
+ const tableLines = [startLine, 0]
+ token_to.map = tableLines
+
+ const token_tho = state.push('thead_open', 'thead', 1)
+ token_tho.map = [startLine, startLine + 1]
+
+ const token_htro = state.push('tr_open', 'tr', 1)
+ token_htro.map = [startLine, startLine + 1]
+
+ for (let i = 0; i < columns.length; i++) {
+ const token_ho = state.push('th_open', 'th', 1)
+ if (aligns[i]) {
+ token_ho.attrs = [['style', 'text-align:' + aligns[i]]]
+ }
+
+ const token_il = state.push('inline', '', 0)
+ token_il.content = columns[i].trim()
+ token_il.children = []
+
+ state.push('th_close', 'th', -1)
+ }
+
+ state.push('tr_close', 'tr', -1)
+ state.push('thead_close', 'thead', -1)
+
+ let tbodyLines
+ let autocompletedCells = 0
+
+ for (nextLine = startLine + 2; nextLine < endLine; nextLine++) {
+ if (state.sCount[nextLine] < state.blkIndent) { break }
+
+ let terminate = false
+ for (let i = 0, l = terminatorRules.length; i < l; i++) {
+ if (terminatorRules[i](state, nextLine, endLine, true)) {
+ terminate = true
+ break
+ }
+ }
+
+ if (terminate) { break }
+ lineText = getLine(state, nextLine).trim()
+ if (!lineText) { break }
+ if (state.sCount[nextLine] - state.blkIndent >= 4) { break }
+ columns = escapedSplit(lineText)
+ if (columns.length && columns[0] === '') columns.shift()
+ if (columns.length && columns[columns.length - 1] === '') columns.pop()
+
+ // note: autocomplete count can be negative if user specifies more columns than header,
+ // but that does not affect intended use (which is limiting expansion)
+ autocompletedCells += columnCount - columns.length
+ if (autocompletedCells > MAX_AUTOCOMPLETED_CELLS) { break }
+
+ if (nextLine === startLine + 2) {
+ const token_tbo = state.push('tbody_open', 'tbody', 1)
+ token_tbo.map = tbodyLines = [startLine + 2, 0]
+ }
+
+ const token_tro = state.push('tr_open', 'tr', 1)
+ token_tro.map = [nextLine, nextLine + 1]
+
+ for (let i = 0; i < columnCount; i++) {
+ const token_tdo = state.push('td_open', 'td', 1)
+ if (aligns[i]) {
+ token_tdo.attrs = [['style', 'text-align:' + aligns[i]]]
+ }
+
+ const token_il = state.push('inline', '', 0)
+ token_il.content = columns[i] ? columns[i].trim() : ''
+ token_il.children = []
+
+ state.push('td_close', 'td', -1)
+ }
+ state.push('tr_close', 'tr', -1)
+ }
+
+ if (tbodyLines) {
+ state.push('tbody_close', 'tbody', -1)
+ tbodyLines[1] = nextLine
+ }
+
+ state.push('table_close', 'table', -1)
+ tableLines[1] = nextLine
+
+ state.parentType = oldParentType
+ state.line = nextLine
+ return true
+}