1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
/** internal
* class ParserBlock
*
* Block-level tokenizer.
**/
import Ruler from './ruler.mjs'
import StateBlock from './rules_block/state_block.mjs'
import r_table from './rules_block/table.mjs'
import r_code from './rules_block/code.mjs'
import r_fence from './rules_block/fence.mjs'
import r_blockquote from './rules_block/blockquote.mjs'
import r_hr from './rules_block/hr.mjs'
import r_list from './rules_block/list.mjs'
import r_reference from './rules_block/reference.mjs'
import r_html_block from './rules_block/html_block.mjs'
import r_heading from './rules_block/heading.mjs'
import r_lheading from './rules_block/lheading.mjs'
import r_paragraph from './rules_block/paragraph.mjs'
const _rules = [
// First 2 params - rule name & source. Secondary array - list of rules,
// which can be terminated by this one.
['table', r_table, ['paragraph', 'reference']],
['code', r_code],
['fence', r_fence, ['paragraph', 'reference', 'blockquote', 'list']],
['blockquote', r_blockquote, ['paragraph', 'reference', 'blockquote', 'list']],
['hr', r_hr, ['paragraph', 'reference', 'blockquote', 'list']],
['list', r_list, ['paragraph', 'reference', 'blockquote']],
['reference', r_reference],
['html_block', r_html_block, ['paragraph', 'reference', 'blockquote']],
['heading', r_heading, ['paragraph', 'reference', 'blockquote']],
['lheading', r_lheading],
['paragraph', r_paragraph]
]
/**
* new ParserBlock()
**/
function ParserBlock () {
/**
* ParserBlock#ruler -> Ruler
*
* [[Ruler]] instance. Keep configuration of block rules.
**/
this.ruler = new Ruler()
for (let i = 0; i < _rules.length; i++) {
this.ruler.push(_rules[i][0], _rules[i][1], { alt: (_rules[i][2] || []).slice() })
}
}
// Generate tokens for input range
//
ParserBlock.prototype.tokenize = function (state, startLine, endLine) {
const rules = this.ruler.getRules('')
const len = rules.length
const maxNesting = state.md.options.maxNesting
let line = startLine
let hasEmptyLines = false
while (line < endLine) {
state.line = line = state.skipEmptyLines(line)
if (line >= endLine) { break }
// Termination condition for nested calls.
// Nested calls currently used for blockquotes & lists
if (state.sCount[line] < state.blkIndent) { break }
// If nesting level exceeded - skip tail to the end. That's not ordinary
// situation and we should not care about content.
if (state.level >= maxNesting) {
state.line = endLine
break
}
// Try all possible rules.
// On success, rule should:
//
// - update `state.line`
// - update `state.tokens`
// - return true
const prevLine = state.line
let ok = false
for (let i = 0; i < len; i++) {
ok = rules[i](state, line, endLine, false)
if (ok) {
if (prevLine >= state.line) {
throw new Error("block rule didn't increment state.line")
}
break
}
}
// this can only happen if user disables paragraph rule
if (!ok) throw new Error('none of the block rules matched')
// set state.tight if we had an empty line before current tag
// i.e. latest empty line should not count
state.tight = !hasEmptyLines
// paragraph might "eat" one newline after it in nested lists
if (state.isEmpty(state.line - 1)) {
hasEmptyLines = true
}
line = state.line
if (line < endLine && state.isEmpty(line)) {
hasEmptyLines = true
line++
state.line = line
}
}
}
/**
* ParserBlock.parse(str, md, env, outTokens)
*
* Process input string and push block tokens into `outTokens`
**/
ParserBlock.prototype.parse = function (src, md, env, outTokens) {
if (!src) { return }
const state = new this.State(src, md, env, outTokens)
this.tokenize(state, state.line, state.lineMax)
}
ParserBlock.prototype.State = StateBlock
export default ParserBlock
|