Skip to content

Commit 4174c75

Browse files
authored
Merge pull request #919 from phpDocumentor/backport/1.x/pr-907
[1.x] Merge pull request #907 from phpDocumentor/feature/list-table
2 parents 6f07223 + 51777dc commit 4174c75

File tree

8 files changed

+307
-4
lines changed

8 files changed

+307
-4
lines changed

packages/guides-restructured-text/resources/config/guides-restructured-text.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
use phpDocumentor\Guides\RestructuredText\Directives\IncludeDirective;
3333
use phpDocumentor\Guides\RestructuredText\Directives\IndexDirective;
3434
use phpDocumentor\Guides\RestructuredText\Directives\LaTeXMain;
35+
use phpDocumentor\Guides\RestructuredText\Directives\ListTableDirective;
3536
use phpDocumentor\Guides\RestructuredText\Directives\LiteralincludeDirective;
3637
use phpDocumentor\Guides\RestructuredText\Directives\MenuDirective;
3738
use phpDocumentor\Guides\RestructuredText\Directives\MetaDirective;
@@ -205,6 +206,7 @@
205206
->arg('$startingRule', service(DocumentRule::class))
206207
->set(IndexDirective::class)
207208
->set(LaTeXMain::class)
209+
->set(ListTableDirective::class)
208210
->set(LiteralincludeDirective::class)
209211
->args([
210212
'$codeNodeOptionMapper' => service(
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link https://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Guides\RestructuredText\Directives;
15+
16+
use phpDocumentor\Guides\Nodes\CollectionNode;
17+
use phpDocumentor\Guides\Nodes\ListItemNode;
18+
use phpDocumentor\Guides\Nodes\ListNode;
19+
use phpDocumentor\Guides\Nodes\Node;
20+
use phpDocumentor\Guides\Nodes\Table\TableColumn;
21+
use phpDocumentor\Guides\Nodes\Table\TableRow;
22+
use phpDocumentor\Guides\Nodes\TableNode;
23+
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
24+
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
25+
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
26+
use Psr\Log\LoggerInterface;
27+
28+
use function array_map;
29+
use function array_shift;
30+
use function assert;
31+
use function count;
32+
use function explode;
33+
use function sprintf;
34+
use function strval;
35+
36+
class ListTableDirective extends SubDirective
37+
{
38+
public function __construct(
39+
protected Rule $startingRule,
40+
private readonly LoggerInterface $logger,
41+
) {
42+
parent::__construct($startingRule);
43+
}
44+
45+
public function getName(): string
46+
{
47+
return 'list-table';
48+
}
49+
50+
/** {@inheritDoc} */
51+
protected function processSub(
52+
BlockContext $blockContext,
53+
CollectionNode $collectionNode,
54+
Directive $directive,
55+
): Node|null {
56+
$options = $this->optionsToArray($directive->getOptions());
57+
58+
if (count($collectionNode->getChildren()) === 0) {
59+
$this->logger->warning('The list-table directive is missing its content. It has to contain exactly one list with sub-lists of equal count. ', $blockContext->getLoggerInformation());
60+
61+
return null;
62+
}
63+
64+
if (count($collectionNode->getChildren()) > 1) {
65+
$this->logger->warning(
66+
sprintf('The list-table must have exactly one list as sub-content. %s nodes found.', count($collectionNode->getChildren())),
67+
$blockContext->getLoggerInformation(),
68+
);
69+
}
70+
71+
$subNode = $collectionNode->getChildren()[0];
72+
if (!$subNode instanceof ListNode) {
73+
$this->logger->warning(
74+
sprintf('The list-table must have exactly one list as sub-content. A node of type %s found.', $subNode::class),
75+
$blockContext->getLoggerInformation(),
76+
);
77+
78+
return null;
79+
}
80+
81+
$tableData = [];
82+
foreach ($subNode->getChildren() as $listItemNode) {
83+
assert($listItemNode instanceof ListItemNode);
84+
$tableRow = new TableRow();
85+
foreach ($listItemNode->getChildren() as $subListNode) {
86+
if (!$subListNode instanceof ListNode) {
87+
$this->logger->warning(
88+
sprintf('The list-table must have a nested list of 2 levels. A node of type %s was found on level 2.', $subListNode::class),
89+
$blockContext->getLoggerInformation(),
90+
);
91+
continue;
92+
}
93+
94+
foreach ($subListNode->getChildren() as $subListItemNode) {
95+
assert($subListItemNode instanceof ListItemNode);
96+
$tableRow->addColumn(new TableColumn('', 1, $subListItemNode->getChildren()));
97+
}
98+
}
99+
100+
$tableData[] = $tableRow;
101+
}
102+
103+
$headerRows = [];
104+
if ($directive->getOption('header-rows')->getValue() !== null) {
105+
for ($i = $directive->getOption('header-rows')->getValue(); $i > 0; $i--) {
106+
if (empty($tableData)) {
107+
break;
108+
}
109+
110+
$headerRows[] = array_shift($tableData);
111+
}
112+
}
113+
114+
$tableNode = new TableNode($tableData, $headerRows);
115+
if (isset($options['widths']) && $options['widths'] !== 'auto' && $options['widths'] !== 'grid') {
116+
$colWidths = array_map('intval', explode(',', strval($options['widths'])));
117+
// A list of integers is used instead of the input column widths. Implies "grid".
118+
$options['widths'] = 'grid';
119+
$tableNode = $tableNode->withColumnWidth($colWidths);
120+
}
121+
122+
$tableNode = $tableNode->withOptions($options);
123+
124+
return $tableNode;
125+
}
126+
}

packages/guides-restructured-text/src/RestructuredText/Parser/Productions/ListRule.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,6 @@ private function parseListItem(array $listConfig, Buffer $buffer, BlockContext $
173173
}
174174

175175
if (!isset($nodes[0])) {
176-
$this->logger->warning('List item without content', $blockContext->getLoggerInformation());
177-
178176
return $listItem;
179177
}
180178

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link https://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Guides\Compiler\NodeTransformers;
15+
16+
use phpDocumentor\Guides\Compiler\CompilerContext;
17+
use phpDocumentor\Guides\Compiler\NodeTransformer;
18+
use phpDocumentor\Guides\Nodes\ListItemNode;
19+
use phpDocumentor\Guides\Nodes\ListNode;
20+
use phpDocumentor\Guides\Nodes\Node;
21+
use Psr\Log\LoggerInterface;
22+
23+
use function assert;
24+
25+
/** @implements NodeTransformer<ListNode> */
26+
final class ListNodeTransformer implements NodeTransformer
27+
{
28+
public function __construct(
29+
private readonly LoggerInterface $logger,
30+
) {
31+
}
32+
33+
public function enterNode(Node $node, CompilerContext $compilerContext): Node
34+
{
35+
return $node;
36+
}
37+
38+
public function leaveNode(Node $node, CompilerContext $compilerContext): Node|null
39+
{
40+
assert($node instanceof ListNode);
41+
foreach ($node->getChildren() as $listItemNode) {
42+
assert($listItemNode instanceof ListItemNode);
43+
if (!empty($listItemNode->getChildren())) {
44+
continue;
45+
}
46+
47+
$this->logger->warning('List item without content', $compilerContext->getLoggerInformation());
48+
}
49+
50+
return $node;
51+
}
52+
53+
public function supports(Node $node): bool
54+
{
55+
return $node instanceof ListNode;
56+
}
57+
58+
public function getPriority(): int
59+
{
60+
return 1000;
61+
}
62+
}

phpstan-baseline.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ parameters:
115115
count: 1
116116
path: packages/guides-restructured-text/src/RestructuredText/Parser/Productions/InlineRules/NamedPhraseRule.php
117117

118+
-
119+
message: "#^Property phpDocumentor\\\\Guides\\\\RestructuredText\\\\Parser\\\\Productions\\\\ListRule\\:\\:\\$logger is never read, only written\\.$#"
120+
count: 1
121+
path: packages/guides-restructured-text/src/RestructuredText/Parser/Productions/ListRule.php
122+
118123
-
119124
message: "#^Function Symfony\\\\Component\\\\DependencyInjection\\\\Loader\\\\Configurator\\\\tagged_iterator not found\\.$#"
120125
count: 2
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
app.WARNING: List item without content {"rst-file":"index.rst","currentLineNumber":7,"currentLine":""} []
2-
app.WARNING: List item without content {"rst-file":"index.rst","currentLineNumber":11,"currentLine":"*"} []
1+
app.WARNING: List item without content {"rst-file":"index.rst"} []
2+
app.WARNING: List item without content {"rst-file":"index.rst"} []
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>table</title>
5+
6+
</head>
7+
<body>
8+
<!-- content start -->
9+
<div class="section" id="table">
10+
<h1>table</h1>
11+
12+
<table class="colwidths-grid">
13+
<colgroup>
14+
<col style="width: 15%">
15+
<col style="width: 10%">
16+
<col style="width: 30%">
17+
</colgroup>
18+
<thead>
19+
<tr>
20+
<th>Treat</th>
21+
<th>Quantity</th>
22+
<th>Description</th>
23+
</tr>
24+
</thead>
25+
<tbody>
26+
<tr>
27+
<td>Albatross</td>
28+
<td>2.99</td>
29+
<td>On a stick!</td>
30+
</tr>
31+
<tr>
32+
<td>Crunchy Frog</td>
33+
<td>1.49</td>
34+
<td>If we took the bones out, it wouldn&#039;t be
35+
crunchy, now would it?</td>
36+
</tr>
37+
<tr>
38+
<td>Gannet Ripple</td>
39+
<td>1.99</td>
40+
<td>On a stick!</td>
41+
</tr>
42+
</tbody>
43+
</table>
44+
45+
<table class="colwidths-grid">
46+
<caption>And another table</caption>
47+
<colgroup>
48+
<col style="width: 25%">
49+
</colgroup>
50+
<thead>
51+
<tr>
52+
<th>Heading row 1, column 1</th>
53+
<th>Heading row 1, column 2</th>
54+
<th>Heading row 1, column 3</th>
55+
</tr>
56+
</thead>
57+
<tbody>
58+
<tr>
59+
<td>Row 1, column 1</td>
60+
<td>&nbsp;</td>
61+
<td>Row 1, column 3</td>
62+
</tr>
63+
<tr>
64+
<td>Row 2, column 1</td>
65+
<td>Row 2, column 2</td>
66+
<td>Row 2, column 3</td>
67+
</tr>
68+
</tbody>
69+
</table>
70+
71+
</div>
72+
73+
<!-- content end -->
74+
</body>
75+
</html>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
table
2+
=====
3+
4+
.. list-table:: Frozen Delights!
5+
:widths: 15, 10, 30
6+
:header-rows: 1
7+
8+
* - Treat
9+
- Quantity
10+
- Description
11+
* - Albatross
12+
- 2.99
13+
- On a stick!
14+
* - Crunchy Frog
15+
- 1.49
16+
- If we took the bones out, it wouldn't be
17+
crunchy, now would it?
18+
* - Gannet Ripple
19+
- 1.99
20+
- On a stick!
21+
22+
.. list-table:: Title
23+
:widths: 25 25 50
24+
:header-rows: 1
25+
:caption: And another table
26+
27+
* - Heading row 1, column 1
28+
- Heading row 1, column 2
29+
- Heading row 1, column 3
30+
* - Row 1, column 1
31+
-
32+
- Row 1, column 3
33+
* - Row 2, column 1
34+
- Row 2, column 2
35+
- Row 2, column 3

0 commit comments

Comments
 (0)