1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This is part of the webuni/commonmark-table-extension package. |
5
|
|
|
* |
6
|
|
|
* (c) Martin Hasoň <[email protected]> |
7
|
|
|
* (c) Webuni s.r.o. <[email protected]> |
8
|
|
|
* |
9
|
|
|
* For the full copyright and license information, please view the LICENSE |
10
|
|
|
* file that was distributed with this source code. |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
namespace Webuni\CommonMark\TableExtension; |
14
|
|
|
|
15
|
|
|
use League\CommonMark\Block\Element\Paragraph; |
16
|
|
|
use League\CommonMark\Block\Parser\AbstractBlockParser; |
17
|
|
|
use League\CommonMark\ContextInterface; |
18
|
|
|
use League\CommonMark\Cursor; |
19
|
|
|
use League\CommonMark\Util\RegexHelper; |
20
|
|
|
|
21
|
|
|
class TableParser extends AbstractBlockParser |
22
|
|
|
{ |
23
|
|
|
const REGEXP_DEFINITION = '/(?: *(:?) *-+ *(:?) *)+(?=\||$)/'; |
24
|
|
|
const REGEXP_CELLS = '/(?:`[^`]*`|\\\\\||\\\\|[^|`\\\\]+)+(?=\||$)/'; |
25
|
|
|
const REGEXP_CAPTION = '/^\[(.+?)\](?:\[(.+)\])?\s*$/'; |
26
|
|
|
|
27
|
16 |
|
public function parse(ContextInterface $context, Cursor $cursor) |
28
|
|
|
{ |
29
|
16 |
|
$container = $context->getContainer(); |
30
|
|
|
|
31
|
16 |
|
if (!$container instanceof Paragraph) { |
32
|
16 |
|
return false; |
33
|
|
|
} |
34
|
|
|
|
35
|
16 |
|
$lines = $container->getStrings(); |
36
|
16 |
|
if (count($lines) < 1) { |
37
|
|
|
return false; |
38
|
|
|
} |
39
|
|
|
|
40
|
16 |
|
if (method_exists($cursor, 'getNextNonSpacePosition')) { |
41
|
16 |
|
$expressionOffset = $cursor->getNextNonSpacePosition(); |
42
|
|
|
} else { |
43
|
|
|
$expressionOffset = $cursor->getFirstNonSpacePosition(); |
|
|
|
|
44
|
|
|
} |
45
|
|
|
|
46
|
16 |
|
$match = RegexHelper::matchAll(self::REGEXP_DEFINITION, $cursor->getLine(), $expressionOffset); |
47
|
16 |
|
if (null === $match) { |
48
|
2 |
|
return false; |
49
|
|
|
} |
50
|
|
|
|
51
|
16 |
|
$columns = $this->parseColumns($match); |
52
|
16 |
|
$head = $this->parseRow(trim(array_pop($lines)), $columns, TableCell::TYPE_HEAD); |
53
|
16 |
|
if (null === $head) { |
54
|
|
|
return false; |
55
|
|
|
} |
56
|
|
|
|
57
|
16 |
|
$table = new Table(function (Cursor $cursor) use (&$table, $columns) { |
58
|
16 |
|
$row = $this->parseRow($cursor->getLine(), $columns); |
59
|
16 |
|
if (null === $row) { |
60
|
6 |
|
if (null !== $table->getCaption()) { |
61
|
2 |
|
return false; |
62
|
|
|
} |
63
|
|
|
|
64
|
6 |
|
if (null !== ($caption = $this->parseCaption($cursor->getLine()))) { |
65
|
2 |
|
$table->setCaption($caption); |
66
|
|
|
|
67
|
2 |
|
return true; |
68
|
|
|
} |
69
|
|
|
|
70
|
4 |
|
return false; |
71
|
|
|
} |
72
|
|
|
|
73
|
16 |
|
$table->getBody()->appendChild($row); |
74
|
|
|
|
75
|
16 |
|
return true; |
76
|
16 |
|
}); |
77
|
|
|
|
78
|
16 |
|
$table->getHead()->appendChild($head); |
79
|
|
|
|
80
|
16 |
|
if (count($lines) >= 1) { |
81
|
2 |
|
$paragraph = new Paragraph(); |
82
|
2 |
|
foreach ($lines as $line) { |
83
|
2 |
|
$paragraph->addLine($line); |
84
|
|
|
} |
85
|
|
|
|
86
|
2 |
|
$context->replaceContainerBlock($paragraph); |
87
|
2 |
|
$context->addBlock($table); |
88
|
|
|
} else { |
89
|
15 |
|
$context->replaceContainerBlock($table); |
90
|
|
|
} |
91
|
|
|
|
92
|
16 |
|
return true; |
93
|
|
|
} |
94
|
|
|
|
95
|
16 |
|
private function parseColumns(array $match) |
96
|
|
|
{ |
97
|
16 |
|
$columns = []; |
98
|
16 |
|
foreach ((array) $match[0] as $i => $column) { |
99
|
16 |
|
if (isset($match[1][$i]) && $match[1][$i] && isset($match[2][$i]) && $match[2][$i]) { |
100
|
3 |
|
$columns[] = TableCell::ALIGN_CENTER; |
101
|
16 |
|
} elseif (isset($match[1][$i]) && $match[1][$i]) { |
102
|
3 |
|
$columns[] = TableCell::ALIGN_LEFT; |
103
|
16 |
|
} elseif (isset($match[2][$i]) && $match[2][$i]) { |
104
|
3 |
|
$columns[] = TableCell::ALIGN_RIGHT; |
105
|
|
|
} else { |
106
|
16 |
|
$columns[] = null; |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
|
110
|
16 |
|
return $columns; |
111
|
|
|
} |
112
|
|
|
|
113
|
16 |
|
private function parseRow($line, array $columns, $type = TableCell::TYPE_BODY) |
114
|
|
|
{ |
115
|
16 |
|
$cells = RegexHelper::matchAll(self::REGEXP_CELLS, $line); |
116
|
|
|
|
117
|
16 |
|
if (null === $cells || $line === $cells[0]) { |
118
|
6 |
|
return; |
119
|
|
|
} |
120
|
|
|
|
121
|
16 |
|
$row = new TableRow(); |
122
|
16 |
|
foreach ((array) $cells[0] as $i => $cell) { |
123
|
16 |
|
$row->appendChild(new TableCell(trim($cell), $type, isset($columns[$i]) ? $columns[$i] : null)); |
124
|
|
|
} |
125
|
|
|
|
126
|
16 |
|
for ($j = count($columns) - 1; $j > $i; --$j) { |
|
|
|
|
127
|
1 |
|
$row->appendChild(new TableCell('', $type, null)); |
128
|
|
|
} |
129
|
|
|
|
130
|
16 |
|
return $row; |
131
|
|
|
} |
132
|
|
|
|
133
|
6 |
|
private function parseCaption($line) |
134
|
|
|
{ |
135
|
6 |
|
$caption = RegexHelper::matchAll(self::REGEXP_CAPTION, $line); |
136
|
|
|
|
137
|
6 |
|
if (null === $caption) { |
138
|
4 |
|
return; |
139
|
|
|
} |
140
|
|
|
|
141
|
2 |
|
return new TableCaption($caption[1], $caption[2]); |
142
|
|
|
} |
143
|
|
|
} |
144
|
|
|
|
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.