-
Notifications
You must be signed in to change notification settings - Fork 10
/
ParserBlock.php
146 lines (125 loc) · 4.92 KB
/
ParserBlock.php
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
135
136
137
138
139
140
141
142
143
144
145
146
<?php
/** internal
* class ParserBlock
*
* Block-level tokenizer.
**/
namespace Kaoken\MarkdownIt;
use Kaoken\MarkdownIt\RulesBlock\StateBlock;
use \Exception;
class ParserBlock
{
protected array $_rules = [
// First 2 params - rule name & source. Secondary array - list of rules,
// which can be terminated by this one.
[ 'table', \Kaoken\MarkdownIt\RulesBlock\Table::class, [ 'paragraph', 'reference' ] ],
[ 'code', \Kaoken\MarkdownIt\RulesBlock\Code::class ],
[ 'fence', \Kaoken\MarkdownIt\RulesBlock\Fence::class, [ 'paragraph', 'reference', 'blockquote', 'list' ] ],
[ 'blockquote', \Kaoken\MarkdownIt\RulesBlock\BlockQuote::class, [ 'paragraph', 'reference', 'blockquote', 'list' ] ],
[ 'hr', \Kaoken\MarkdownIt\RulesBlock\Hr::class, [ 'paragraph', 'reference', 'blockquote', 'list' ] ],
[ 'list', \Kaoken\MarkdownIt\RulesBlock\CList::class, [ 'paragraph', 'reference', 'blockquote' ] ],
[ 'reference', \Kaoken\MarkdownIt\RulesBlock\Reference::class ],
[ 'html_block', \Kaoken\MarkdownIt\RulesBlock\HtmlBlock::class, [ 'paragraph', 'reference', 'blockquote' ] ],
[ 'heading', \Kaoken\MarkdownIt\RulesBlock\Heading::class, [ 'paragraph', 'reference', 'blockquote' ] ],
[ 'lheading', \Kaoken\MarkdownIt\RulesBlock\LHeading::class],
[ 'paragraph', \Kaoken\MarkdownIt\RulesBlock\Paragraph::class ]
];
/**
* @var Ruler
*/
public Ruler $ruler;
public function __construct()
{
/**
* ParserBlock#ruler -> Ruler
*
* [[Ruler]] instance. Keep configuration of block rules.
**/
$this->ruler = new Ruler();
foreach ($this->_rules as &$rule) {
$obj = new \stdClass();
$obj->alt = $rule[2] ?? [];
$class = $rule[1];
$this->ruler->push($rule[0], [new $class(), 'set'], $obj);
}
}
//
//
/**
* Generate tokens for input range
* @param StateBlock $state
* @param integer $startLine
* @param integer $endLine
* @throws Exception
*/
public function tokenize(StateBlock $state, int $startLine, int $endLine)
{
$rules = $this->ruler->getRules('');
$len = count($rules);
$line = $startLine;
$hasEmptyLines = false;
$maxNesting = $state->md->options->maxNesting;
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
$prevLine = $state->line;
$ok = false;
foreach ($rules as &$rule) {
if( is_array($rule) )
$ok = $rule[0]->{$rule[1]}($state, $line, $endLine, false);
else
$ok = $rule($state, $line, $endLine, false);
if ($ok) {
if ($prevLine >= $state->line) {
throw new Exception("block rule didn't increment state.line");
}
break;
}
}
// this can only happen if user disables paragraph rule
if (!$ok) throw new Exception('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;
}
}
}
/**
* Process input string and push block tokens into `outTokens`
*
* @param string $src
* @param MarkdownIt $md
* @param null|object $env
* @param Token[] $outTokens
*/
public function parse(string $src, MarkdownIt $md, ?object $env, array &$outTokens)
{
if (!$src) { return; }
$state = new StateBlock($src, $md, $env, $outTokens);
$this->tokenize($state, $state->line, $state->lineMax);
}
}