Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
declare(strict_types=1);
4
 
5
/*
6
 * This is part of the league/commonmark package.
7
 *
8
 * (c) Martin Hasoň <martin.hason@gmail.com>
9
 * (c) Webuni s.r.o. <info@webuni.cz>
10
 * (c) Colin O'Dell <colinodell@gmail.com>
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 */
15
 
16
namespace League\CommonMark\Extension\Table;
17
 
18
use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
19
use League\CommonMark\Parser\Block\BlockContinue;
20
use League\CommonMark\Parser\Block\BlockContinueParserInterface;
21
use League\CommonMark\Parser\Block\BlockContinueParserWithInlinesInterface;
22
use League\CommonMark\Parser\Cursor;
23
use League\CommonMark\Parser\InlineParserEngineInterface;
24
use League\CommonMark\Util\ArrayCollection;
25
 
26
final class TableParser extends AbstractBlockContinueParser implements BlockContinueParserWithInlinesInterface
27
{
28
    /** @psalm-readonly */
29
    private Table $block;
30
 
31
    /**
32
     * @var ArrayCollection<string>
33
     *
34
     * @psalm-readonly-allow-private-mutation
35
     */
36
    private ArrayCollection $bodyLines;
37
 
38
    /**
39
     * @var array<int, string|null>
40
     * @psalm-var array<int, TableCell::ALIGN_*|null>
41
     * @phpstan-var array<int, TableCell::ALIGN_*|null>
42
     *
43
     * @psalm-readonly
44
     */
45
    private array $columns;
46
 
47
    /**
48
     * @var array<int, string>
49
     *
50
     * @psalm-readonly-allow-private-mutation
51
     */
52
    private array $headerCells;
53
 
54
    /** @psalm-readonly-allow-private-mutation */
55
    private bool $nextIsSeparatorLine = true;
56
 
57
    /**
58
     * @param array<int, string|null> $columns
59
     * @param array<int, string>      $headerCells
60
     *
61
     * @psalm-param array<int, TableCell::ALIGN_*|null> $columns
62
     *
63
     * @phpstan-param array<int, TableCell::ALIGN_*|null> $columns
64
     */
65
    public function __construct(array $columns, array $headerCells)
66
    {
67
        $this->block       = new Table();
68
        $this->bodyLines   = new ArrayCollection();
69
        $this->columns     = $columns;
70
        $this->headerCells = $headerCells;
71
    }
72
 
73
    public function canHaveLazyContinuationLines(): bool
74
    {
75
        return true;
76
    }
77
 
78
    public function getBlock(): Table
79
    {
80
        return $this->block;
81
    }
82
 
83
    public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
84
    {
85
        if (\strpos($cursor->getLine(), '|') === false) {
86
            return BlockContinue::none();
87
        }
88
 
89
        return BlockContinue::at($cursor);
90
    }
91
 
92
    public function addLine(string $line): void
93
    {
94
        if ($this->nextIsSeparatorLine) {
95
            $this->nextIsSeparatorLine = false;
96
        } else {
97
            $this->bodyLines[] = $line;
98
        }
99
    }
100
 
101
    public function parseInlines(InlineParserEngineInterface $inlineParser): void
102
    {
103
        $headerColumns = \count($this->headerCells);
104
 
105
        $head = new TableSection(TableSection::TYPE_HEAD);
106
        $this->block->appendChild($head);
107
 
108
        $headerRow = new TableRow();
109
        $head->appendChild($headerRow);
110
        for ($i = 0; $i < $headerColumns; $i++) {
111
            $cell      = $this->headerCells[$i];
112
            $tableCell = $this->parseCell($cell, $i, $inlineParser);
113
            $tableCell->setType(TableCell::TYPE_HEADER);
114
            $headerRow->appendChild($tableCell);
115
        }
116
 
117
        $body = null;
118
        foreach ($this->bodyLines as $rowLine) {
119
            $cells = self::split($rowLine);
120
            $row   = new TableRow();
121
 
122
            // Body can not have more columns than head
123
            for ($i = 0; $i < $headerColumns; $i++) {
124
                $cell      = $cells[$i] ?? '';
125
                $tableCell = $this->parseCell($cell, $i, $inlineParser);
126
                $row->appendChild($tableCell);
127
            }
128
 
129
            if ($body === null) {
130
                // It's valid to have a table without body. In that case, don't add an empty TableBody node.
131
                $body = new TableSection();
132
                $this->block->appendChild($body);
133
            }
134
 
135
            $body->appendChild($row);
136
        }
137
    }
138
 
139
    private function parseCell(string $cell, int $column, InlineParserEngineInterface $inlineParser): TableCell
140
    {
141
        $tableCell = new TableCell();
142
 
143
        if ($column < \count($this->columns)) {
144
            $tableCell->setAlign($this->columns[$column]);
145
        }
146
 
147
        $inlineParser->parse(\trim($cell), $tableCell);
148
 
149
        return $tableCell;
150
    }
151
 
152
    /**
153
     * @internal
154
     *
155
     * @return array<int, string>
156
     */
157
    public static function split(string $line): array
158
    {
159
        $cursor = new Cursor(\trim($line));
160
 
161
        if ($cursor->getCurrentCharacter() === '|') {
162
            $cursor->advanceBy(1);
163
        }
164
 
165
        $cells = [];
166
        $sb    = '';
167
 
168
        while (! $cursor->isAtEnd()) {
169
            switch ($c = $cursor->getCurrentCharacter()) {
170
                case '\\':
171
                    if ($cursor->peek() === '|') {
172
                        // Pipe is special for table parsing. An escaped pipe doesn't result in a new cell, but is
173
                        // passed down to inline parsing as an unescaped pipe. Note that that applies even for the `\|`
174
                        // in an input like `\\|` - in other words, table parsing doesn't support escaping backslashes.
175
                        $sb .= '|';
176
                        $cursor->advanceBy(1);
177
                    } else {
178
                        // Preserve backslash before other characters or at end of line.
179
                        $sb .= '\\';
180
                    }
181
 
182
                    break;
183
                case '|':
184
                    $cells[] = $sb;
185
                    $sb      = '';
186
                    break;
187
                default:
188
                    $sb .= $c;
189
            }
190
 
191
            $cursor->advanceBy(1);
192
        }
193
 
194
        if ($sb !== '') {
195
            $cells[] = $sb;
196
        }
197
 
198
        return $cells;
199
    }
200
}