Skip to content

Commit

Permalink
[FEATURE] Render tables to rst
Browse files Browse the repository at this point in the history
  • Loading branch information
linawolf authored and jaapio committed Nov 22, 2024
1 parent 3ceb2cf commit 2331e23
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use phpDocumentor\Guides\RstTheme\Twig\RstExtension;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

use function Symfony\Component\DependencyInjection\Loader\Configurator\service;

return static function (ContainerConfigurator $container): void {
$container->services()
->defaults()
Expand All @@ -29,6 +31,7 @@
)

->set(RstExtension::class)
->arg('$nodeRenderer', service('phpdoc.guides.output_node_renderer'))
->tag('twig.extension')
->autowire();
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

{{ renderRstTable(node) | raw }}
2 changes: 2 additions & 0 deletions packages/guides-theme-rst/resources/template/rst/template.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
use phpDocumentor\Guides\Nodes\QuoteNode;
use phpDocumentor\Guides\Nodes\SectionNode;
use phpDocumentor\Guides\Nodes\SeparatorNode;
use phpDocumentor\Guides\Nodes\TableNode;
use phpDocumentor\Guides\Nodes\TitleNode;

return [
Expand All @@ -74,6 +75,7 @@
CitationNode::class => 'body/citation.rst.twig',
FootnoteNode::class => 'body/footnote.rst.twig',
AnnotationListNode::class => 'body/annotation-list.rst.twig',
TableNode::class => 'body/table.rst.twig',
// Inline
ImageInlineNode::class => 'inline/image.rst.twig',
AbbreviationInlineNode::class => 'inline/textroles/abbreviation.rst.twig',
Expand Down
83 changes: 83 additions & 0 deletions packages/guides-theme-rst/src/RstTheme/Twig/RstExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@

namespace phpDocumentor\Guides\RstTheme\Twig;

use phpDocumentor\Guides\NodeRenderers\NodeRenderer;
use phpDocumentor\Guides\Nodes\Table\TableColumn;
use phpDocumentor\Guides\Nodes\Table\TableRow;
use phpDocumentor\Guides\Nodes\TableNode;
use phpDocumentor\Guides\Nodes\TitleNode;
use phpDocumentor\Guides\RenderContext;
use phpDocumentor\Guides\RstTheme\Configuration\HeaderSyntax;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
Expand All @@ -22,6 +27,9 @@
use function array_map;
use function explode;
use function implode;
use function max;
use function mb_str_pad;
use function mb_strlen;
use function min;
use function preg_replace;
use function rtrim;
Expand All @@ -30,11 +38,17 @@

final class RstExtension extends AbstractExtension
{
public function __construct(
private NodeRenderer $nodeRenderer,
) {
}

/** @return TwigFunction[] */
public function getFunctions(): array
{
return [
new TwigFunction('renderRstTitle', $this->renderRstTitle(...), ['is_safe' => ['rst'], 'needs_context' => false]),
new TwigFunction('renderRstTable', $this->renderRstTable(...), ['is_safe' => ['rst'], 'needs_context' => true]),
new TwigFunction('renderRstIndent', $this->renderRstIndent(...), ['is_safe' => ['rst'], 'needs_context' => false]),
];
}
Expand Down Expand Up @@ -75,6 +89,75 @@ public function renderRstTitle(TitleNode $node, string $content): string

$ret .= $content . "\n" . str_repeat($headerSyntax->delimiter(), strlen($content));

return $ret . "\n";
}

/** @param array{env: RenderContext} $context */
public function renderRstTable(array $context, TableNode $node): string
{
$columnWidths = [];

$this->determineMaxLenght($node->getHeaders(), $context['env'], $columnWidths);
$this->determineMaxLenght($node->getData(), $context['env'], $columnWidths);

$ret = $this->renderTableRowEnd($columnWidths);
$ret .= $this->renderRows($node->getHeaders(), $context['env'], $columnWidths, '=');
$ret .= $this->renderRows($node->getData(), $context['env'], $columnWidths);

return $ret . "\n";
}

private function renderCellContent(RenderContext $env, TableColumn $column): string
{
return implode('', array_map(fn ($node) => $this->nodeRenderer->render($node, $env), $column->getValue()));
}

/**
* @param TableRow[] $rows
* @param int[] &$columnWidths
*/
private function determineMaxLenght(array $rows, RenderContext $env, array &$columnWidths): void
{
foreach ($rows as $row) {
foreach ($row->getColumns() as $index => $column) {
$content = $this->renderCellContent($env, $column);

$columnWidths[$index] = max(mb_strlen($content) + 2, $columnWidths[$index] ?? 0);
}
}
}

/**
* @param TableRow[] $rows
* @param int[] $columnWidths
*/
private function renderRows(array $rows, RenderContext $env, array $columnWidths, string $separator = '-'): string
{
$ret = '';
foreach ($rows as $row) {
$ret .= '|';
foreach ($row->getColumns() as $index => $column) {
$content = $this->renderCellContent($env, $column);

$ret .= ' ' . mb_str_pad($content, $columnWidths[$index] - 2) . ' |';
}

$ret .= "\n" . $this->renderTableRowEnd($columnWidths, $separator);
}

return $ret;
}

/** @param int[] $columnWidths */
private function renderTableRowEnd(array $columnWidths, string $char = '-'): string
{
$ret = '';
foreach ($columnWidths as $width) {
$ret .= '+' . str_repeat($char, $width);
}

$ret .= '+' . "\n";

return $ret;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
===============
Markdown Tables
===============

Simple Table
============

+------------+-----+---------------+
| Name | Age | City |
+============+=====+===============+
| John Doe | 29 | New York |
+------------+-----+---------------+
| Jane Smith | 34 | San Francisco |
+------------+-----+---------------+
| Sam Green | 22 | Boston |
+------------+-----+---------------+

Table 1
=======

+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+--------------------------------------------------------------------------------------------+
| Method name | Description | Parameters | Default |
+==============================================+===================================================================================================================================================================+=======================================================+============================================================================================+
| `setIcon` | icon file, or existing icon identifier | `string $icon` | `'EXT:container/Resources/Public/Icons/Extension.svg'` |
+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+--------------------------------------------------------------------------------------------+
| `setBackendTemplate` | Template for backend view | `string $backendTemplate` | `null'` |
+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+--------------------------------------------------------------------------------------------+
| `setGridTemplate` | Template for grid | `string $gridTemplate` | `'EXT:container/Resources/Private/Templates/Container.html'` |
+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+--------------------------------------------------------------------------------------------+
| `setGridPartialPaths` / `addGridPartialPath` | Partial root paths for grid | `array $gridPartialPaths` / `string $gridPartialPath` | `['EXT:backend/Resources/Private/Partials/', 'EXT:container/Resources/Private/Partials/']` |
+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+--------------------------------------------------------------------------------------------+
| `setGridLayoutPaths` | Layout root paths for grid | `array $gridLayoutPaths` | `[]` |
+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+--------------------------------------------------------------------------------------------+
| `setSaveAndCloseInNewContentElementWizard` | saveAndClose for new content element wizard | `bool $saveAndCloseInNewContentElementWizard` | `true` |
+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+--------------------------------------------------------------------------------------------+
| `setRegisterInNewContentElementWizard` | register in new content element wizard | `bool $registerInNewContentElementWizard` | `true` |
+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+--------------------------------------------------------------------------------------------+
| `setGroup` | Custom Group (used as optgroup for CType select, and as tab in New Content Element Wizard). If empty "container" is used as tab and no optgroup in CType is used. | `string $group` | `'container'` |
+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+--------------------------------------------------------------------------------------------+
| `setDefaultValues` | Default values for the newContentElement.wizardItems | `array $defaultValues` | `[]` |
+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+--------------------------------------------------------------------------------------------+

Table 2
=======

+-----------------------------+---------------------------------------------------------------------------------------------------------+------------------------------------------------------------+-----------+
| Option | Description | Default | Parameter |
+=============================+=========================================================================================================+============================================================+===========+
| `contentId` | id of container to to process | current uid of content element `$cObj->data['uid']` | `?int` |
+-----------------------------+---------------------------------------------------------------------------------------------------------+------------------------------------------------------------+-----------+
| `colPos` | colPos of children to to process | empty, all children are processed (as `children_<colPos>`) | `?int` |
+-----------------------------+---------------------------------------------------------------------------------------------------------+------------------------------------------------------------+-----------+
| `as` | variable to use for proceesedData (only if `colPos` is set) | `children` | `?string` |
+-----------------------------+---------------------------------------------------------------------------------------------------------+------------------------------------------------------------+-----------+
| `skipRenderingChildContent` | do not call `ContentObjectRenderer->render()` for children, (`renderedContent` in child will not exist) | empty | `?int` |
+-----------------------------+---------------------------------------------------------------------------------------------------------+------------------------------------------------------------+-----------+
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<guides xmlns="https://www.phpdoc.org/guides"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.phpdoc.org/guides packages/guides-cli/resources/schema/guides.xsd"
input-format="md"
theme="rst"
>
<project title="Project Title" version="6.4"/>
<extension class="phpDocumentor\Guides\RstTheme"/>
<output-format>rst</output-format>
</guides>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Markdown Tables

## Simple Table

| Name | Age | City |
|------------|-----|--------------|
| John Doe | 29 | New York |
| Jane Smith | 34 | San Francisco|
| Sam Green | 22 | Boston |

## Table 1

| Method name | Description | Parameters | Default |
| ----------- | ----------- | ---------- | ---------- |
| `setIcon` | icon file, or existing icon identifier | `string $icon` | `'EXT:container/Resources/Public/Icons/Extension.svg'` |
| `setBackendTemplate` | Template for backend view| `string $backendTemplate` | `null'` |
| `setGridTemplate` | Template for grid | `string $gridTemplate` | `'EXT:container/Resources/Private/Templates/Container.html'` |
| `setGridPartialPaths` / `addGridPartialPath` | Partial root paths for grid | `array $gridPartialPaths` / `string $gridPartialPath` | `['EXT:backend/Resources/Private/Partials/', 'EXT:container/Resources/Private/Partials/']` |
| `setGridLayoutPaths` | Layout root paths for grid | `array $gridLayoutPaths` | `[]` |
| `setSaveAndCloseInNewContentElementWizard` | saveAndClose for new content element wizard | `bool $saveAndCloseInNewContentElementWizard` | `true` |
| `setRegisterInNewContentElementWizard` |register in new content element wizard | `bool $registerInNewContentElementWizard` | `true` |
| `setGroup` | Custom Group (used as optgroup for CType select, and as tab in New Content Element Wizard). If empty "container" is used as tab and no optgroup in CType is used. | `string $group` | `'container'` |
| `setDefaultValues` | Default values for the newContentElement.wizardItems | `array $defaultValues` | `[]` |

## Table 2

| Option | Description | Default | Parameter |
|-----------------------------|------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------|-------------|
| `contentId` | id of container to to process | current uid of content element ``$cObj->data['uid']`` | ``?int`` |
| `colPos` | colPos of children to to process | empty, all children are processed (as ``children_<colPos>``) | ``?int`` |
| `as` | variable to use for proceesedData (only if ``colPos`` is set) | ``children`` | ``?string`` |
| `skipRenderingChildContent` | do not call ``ContentObjectRenderer->render()`` for children, (``renderedContent`` in child will not exist) | empty | ``?int`` |

0 comments on commit 2331e23

Please sign in to comment.