Skip to content

Commit f700eb4

Browse files
authored
Merge pull request #576 from phpDocumentor/feature/table
[FEATURE] Introduce table directive
2 parents 481cc03 + 860aa62 commit f700eb4

File tree

22 files changed

+286
-50
lines changed

22 files changed

+286
-50
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
@@ -42,6 +42,7 @@
4242
use phpDocumentor\Guides\RestructuredText\Directives\SeeAlsoDirective;
4343
use phpDocumentor\Guides\RestructuredText\Directives\SidebarDirective;
4444
use phpDocumentor\Guides\RestructuredText\Directives\SubDirective;
45+
use phpDocumentor\Guides\RestructuredText\Directives\TableDirective;
4546
use phpDocumentor\Guides\RestructuredText\Directives\TipDirective;
4647
use phpDocumentor\Guides\RestructuredText\Directives\TitleDirective;
4748
use phpDocumentor\Guides\RestructuredText\Directives\ToctreeDirective;
@@ -197,6 +198,7 @@
197198
->set(RoleDirective::class)
198199
->set(SeeAlsoDirective::class)
199200
->set(SidebarDirective::class)
201+
->set(TableDirective::class)
200202
->set(TipDirective::class)
201203
->set(TitleDirective::class)
202204
->set(ToctreeDirective::class)

packages/guides-restructured-text/src/RestructuredText/Directives/BaseDirective.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public function process(
6060
): Node|null {
6161
return $this->processNode($blockContext, $directive)
6262
// Ensure options are always available
63-
->withOptions($this->optionsToArray($directive->getOptions()));
63+
->withKeepExistingOptions($this->optionsToArray($directive->getOptions()));
6464
}
6565

6666
/**

packages/guides-restructured-text/src/RestructuredText/Directives/CsvTableDirective.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
use Psr\Log\LoggerInterface;
1818

1919
use function array_filter;
20+
use function array_map;
2021
use function count;
22+
use function explode;
2123
use function implode;
24+
use function strval;
2225
use function trim;
2326

2427
/**
@@ -47,6 +50,7 @@ public function processNode(
4750
BlockContext $blockContext,
4851
Directive $directive,
4952
): Node {
53+
$options = $this->optionsToArray($directive->getOptions());
5054
if ($directive->hasOption('file')) {
5155
$csvStream = $blockContext->getDocumentParserContext()
5256
->getContext()
@@ -96,7 +100,17 @@ public function processNode(
96100
$rows[] = $tableRow;
97101
}
98102

99-
return new TableNode($rows, array_filter([$header]));
103+
$tableNode = new TableNode($rows, array_filter([$header]));
104+
if (isset($options['widths']) && $options['widths'] !== 'auto' && $options['widths'] !== 'grid') {
105+
$colWidths = array_map('intval', explode(',', strval($options['widths'])));
106+
// A list of integers is used instead of the input column widths. Implies "grid".
107+
$options['widths'] = 'grid';
108+
$tableNode = $tableNode->withColumnWidth($colWidths);
109+
}
110+
111+
$tableNode = $tableNode->withOptions($options);
112+
113+
return $tableNode;
100114
}
101115

102116
private function buildColumn(

packages/guides-restructured-text/src/RestructuredText/Directives/SubDirective.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ final public function process(
4444
return null;
4545
}
4646

47-
return $node->withOptions($this->optionsToArray($directive->getOptions()));
47+
return $node->withKeepExistingOptions($this->optionsToArray($directive->getOptions()));
4848
}
4949

5050
/** @return Rule<CollectionNode> */
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Guides\RestructuredText\Directives;
6+
7+
use phpDocumentor\Guides\Nodes\Node;
8+
use phpDocumentor\Guides\Nodes\TableNode;
9+
use phpDocumentor\Guides\RestructuredText\Nodes\CollectionNode;
10+
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
11+
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
12+
use Psr\Log\LoggerInterface;
13+
14+
use function array_map;
15+
use function count;
16+
use function explode;
17+
use function sprintf;
18+
use function strval;
19+
20+
/**
21+
* Applies more options to a table
22+
*
23+
* .. table:: Table Title
24+
* :widths: 30,70
25+
* :align: center
26+
* :class: custom-table
27+
*
28+
* +-----------------+-----------------+
29+
* | Header 1 | Header 2 |
30+
* +=================+=================+
31+
* | Row 1, Column 1 | Row 1, Column 2 |
32+
* +-----------------+-----------------+
33+
* | Row 2, Column 1 | Row 2, Column 2 |
34+
* +-----------------+-----------------+
35+
*/
36+
final class TableDirective extends SubDirective
37+
{
38+
public function __construct(
39+
protected Rule $startingRule,
40+
private LoggerInterface $logger,
41+
) {
42+
parent::__construct($startingRule);
43+
}
44+
45+
public function getName(): string
46+
{
47+
return 'table';
48+
}
49+
50+
/** {@inheritDoc} */
51+
protected function processSub(
52+
CollectionNode $collectionNode,
53+
Directive $directive,
54+
): Node|null {
55+
if (count($collectionNode->getChildren()) !== 1) {
56+
$this->logger->warning(sprintf('The table directive may contain exactly one table. %s children found', count($collectionNode->getChildren())));
57+
58+
return $collectionNode;
59+
}
60+
61+
$tableNode = $collectionNode->getChildren()[0];
62+
if (!$tableNode instanceof TableNode) {
63+
$this->logger->warning(sprintf('The table directive may contain exactly one table. A node of type %s was found. ', $tableNode::class));
64+
65+
return $collectionNode;
66+
}
67+
68+
$options = $this->optionsToArray($directive->getOptions());
69+
if (isset($options['widths']) && $options['widths'] !== 'auto' && $options['widths'] !== 'grid') {
70+
$colWidths = array_map('intval', explode(',', strval($options['widths'])));
71+
// A list of integers is used instead of the input column widths. Implies "grid".
72+
$options['widths'] = 'grid';
73+
$tableNode = $tableNode->withColumnWidth($colWidths);
74+
}
75+
76+
return $tableNode->withOptions($options);
77+
}
78+
}

packages/guides-restructured-text/tests/unit/Parser/DummyNode.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ public function withOptions(array $options): Node
3636
return $this;
3737
}
3838

39+
/** {@inheritDoc} */
40+
public function withKeepExistingOptions(array $options): Node
41+
{
42+
return $this;
43+
}
44+
3945
public function hasOption(string $name): bool
4046
{
4147
return false;
Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,7 @@
1-
<table{% if tableNode.classes %} class="{{ tableNode.classesString }}"{% endif %}>
2-
{% if tableHeaderRows is not empty %}
3-
<thead>
4-
{% for tableHeaderRow in tableHeaderRows %}
5-
<tr>
6-
{% for column in tableHeaderRow.columns %}
7-
<th{% if column.colspan > 1 %} colspan="{{ column.colspan }}"{% endif %}>
8-
{%- for child in column.children -%}
9-
{{- renderNode(child) -}}
10-
{% endfor %}</th>
11-
{% endfor %}
12-
</tr>
13-
{% endfor %}
14-
</thead>
15-
{% endif %}
16-
17-
<tbody>
18-
{% for tableRow in tableRows %}
19-
<tr>
20-
{% for column in tableRow.columns %}
21-
<td{% if column.colSpan > 1 %} colspan="{{ column.colSpan }}"{% endif %}{% if column.rowSpan > 1 %} rowspan="{{ column.rowSpan }}"{% endif %}>
22-
{%- for child in column.children -%}
23-
{{- renderNode(child) -}}
24-
{%- else %}&nbsp;{% endfor %}</td>
25-
{% endfor %}
26-
</tr>
27-
{% endfor %}
28-
</tbody>
1+
<table {%- include "body/table/table-classes.html.twig" -%}
2+
{%- include "body/table/table-inline-style.html.twig" -%}>
3+
{% include "body/table/table-caption.html.twig" %}
4+
{% include "body/table/table-colgroups.html.twig" %}
5+
{% include "body/table/table-header.html.twig" %}
6+
{% include "body/table/table-body.html.twig" %}
297
</table>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<tbody>
2+
{% for tableRow in tableRows %}
3+
{% include "body/table/table-row.html.twig" %}
4+
{% endfor %}
5+
</tbody>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{% if tableNode.hasOption('caption') %}
2+
<caption>{{ tableNode.option('caption') }}</caption>
3+
{% endif %}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<td{% if column.colSpan > 1 %} colspan="{{ column.colSpan }}"{% endif %}{% if column.rowSpan > 1 %} rowspan="{{ column.rowSpan }}"{% endif %}>
2+
{%- for child in column.children -%}
3+
{{- renderNode(child) -}}
4+
{%- else %}&nbsp;{% endfor %}</td>

0 commit comments

Comments
 (0)