Skip to content

Commit

Permalink
Merge pull request #100 from weaverryan/failing-test-case-table-struc…
Browse files Browse the repository at this point in the history
…ture

Adding support for colspan to tables
  • Loading branch information
jwage authored Apr 30, 2019
2 parents 089dc6d + 457e0fb commit 59e913f
Show file tree
Hide file tree
Showing 37 changed files with 997 additions and 214 deletions.
11 changes: 11 additions & 0 deletions lib/Exception/InvalidTableStructure.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Doctrine\RST\Exception;

use Exception;

final class InvalidTableStructure extends Exception
{
}
42 changes: 12 additions & 30 deletions lib/HTML/Renderers/TableNodeRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

namespace Doctrine\RST\HTML\Renderers;

use Doctrine\RST\Nodes\SpanNode;
use Doctrine\RST\Nodes\TableNode;
use Doctrine\RST\Renderers\NodeRenderer;
use Doctrine\RST\Templates\TemplateRenderer;
use function count;
use LogicException;
use function sprintf;

class TableNodeRenderer implements NodeRenderer
{
Expand All @@ -27,45 +27,27 @@ public function __construct(TableNode $tableNode, TemplateRenderer $templateRend
public function render() : string
{
$headers = $this->tableNode->getHeaders();
$data = $this->tableNode->getData();
$rows = $this->tableNode->getData();

$tableHeader = [];
$tableRows = [];
$tableHeaderRows = [];

if (count($headers) !== 0) {
foreach ($headers as $k => $isHeader) {
if (! isset($data[$k])) {
continue;
}

/** @var SpanNode $col */
foreach ($data[$k] as $col) {
$tableHeader[] = $col->render();
}

unset($data[$k]);
}
}

foreach ($data as $k => $row) {
if ($row === []) {
foreach ($headers as $k => $isHeader) {
if ($isHeader === false) {
continue;
}

$tableRow = [];

/** @var SpanNode $col */
foreach ($row as $col) {
$tableRow[] = $col->render();
if (! isset($rows[$k])) {
throw new LogicException(sprintf('Row "%d" should be a header, but that row does not exist.', $k));
}

$tableRows[] = $tableRow;
$tableHeaderRows[] = $rows[$k];
unset($rows[$k]);
}

return $this->templateRenderer->render('table.html.twig', [
'tableNode' => $this->tableNode,
'tableHeader' => $tableHeader,
'tableRows' => $tableRows,
'tableHeaderRows' => $tableHeaderRows,
'tableRows' => $rows,
]);
}
}
12 changes: 4 additions & 8 deletions lib/LaTeX/Renderers/TableNodeRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,15 @@ public function render() : string
$cols = 0;

$rows = [];
foreach ($this->tableNode->getData() as &$row) {
if ($row === []) {
continue;
}

foreach ($this->tableNode->getData() as $row) {
$rowTex = '';
$cols = max($cols, count($row));
$cols = max($cols, count($row->getColumns()));

/** @var SpanNode $col */
foreach ($row as $n => &$col) {
foreach ($row->getColumns() as $n => $col) {
$rowTex .= $col->render();

if ((int) $n + 1 >= count($row)) {
if ((int) $n + 1 >= count($row->getColumns())) {
continue;
}

Expand Down
7 changes: 2 additions & 5 deletions lib/NodeFactory/DefaultNodeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,10 @@ public function createListNode() : ListNode
return $listNode;
}

/**
* @param string[] $parts
*/
public function createTableNode(array $parts, string $type, LineChecker $lineChecker) : TableNode
public function createTableNode(Parser\TableSeparatorLineConfig $separatorLineConfig, string $type, LineChecker $lineChecker) : TableNode
{
/** @var TableNode $tableNode */
$tableNode = $this->create(NodeTypes::TABLE, [$parts, $type, $lineChecker]);
$tableNode = $this->create(NodeTypes::TABLE, [$separatorLineConfig, $type, $lineChecker]);

return $tableNode;
}
Expand Down
6 changes: 2 additions & 4 deletions lib/NodeFactory/NodeFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
use Doctrine\RST\Parser;
use Doctrine\RST\Parser\DefinitionList;
use Doctrine\RST\Parser\LineChecker;
use Doctrine\RST\Parser\TableSeparatorLineConfig;

interface NodeFactory
{
Expand Down Expand Up @@ -65,10 +66,7 @@ public function createAnchorNode(?string $value = null) : AnchorNode;

public function createListNode() : ListNode;

/**
* @param string[] $parts
*/
public function createTableNode(array $parts, string $type, LineChecker $lineChecker) : TableNode;
public function createTableNode(TableSeparatorLineConfig $separatorLineConfig, string $type, LineChecker $lineChecker) : TableNode;

/**
* @param string|string[]|SpanNode $span
Expand Down
74 changes: 74 additions & 0 deletions lib/Nodes/Table/TableColumn.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

declare(strict_types=1);

namespace Doctrine\RST\Nodes\Table;

use Doctrine\RST\Nodes\Node;
use LogicException;
use function strlen;
use function trim;
use function utf8_encode;

final class TableColumn
{
/** @var string */
private $content;

/** @var int */
private $colSpan;

/** @var Node|null */
private $node;

public function __construct(string $content, int $colSpan)
{
$this->content = utf8_encode(trim($content));
$this->colSpan = $colSpan;
}

public function getContent() : string
{
// "\" is a special way to make a column "empty", but
// still indicate that you *want* that column
if ($this->content === '\\') {
return '';
}

return $this->content;
}

public function getColSpan() : int
{
return $this->colSpan;
}

public function addContent(string $content) : void
{
$this->content = trim($this->content . utf8_encode($content));
}

public function getNode() : Node
{
if ($this->node === null) {
throw new LogicException('The node is not yet set.');
}

return $this->node;
}

public function setNode(Node $node) : void
{
$this->node = $node;
}

public function render() : string
{
return $this->getNode()->render();
}

public function isEmpty() : bool
{
return strlen($this->content) === 0;
}
}
75 changes: 75 additions & 0 deletions lib/Nodes/Table/TableRow.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);

namespace Doctrine\RST\Nodes\Table;

use Doctrine\RST\Exception\InvalidTableStructure;
use LogicException;
use function array_map;
use function implode;
use function sprintf;

final class TableRow
{
/** @var TableColumn[] */
private $columns = [];

public function addColumn(string $content, int $colSpan) : void
{
$this->columns[] = new TableColumn($content, $colSpan);
}

/**
* @return TableColumn[]
*/
public function getColumns() : array
{
return $this->columns;
}

public function getColumn(int $index) : ?TableColumn
{
return $this->columns[$index] ?? null;
}

public function getFirstColumn() : TableColumn
{
$column = $this->getColumn(0);

if ($column === null) {
throw new LogicException('Row has no columns');
}

return $column;
}

/**
* Push the content from the columns of a row onto this row.
*
* Useful when we discover that a row is actually just a continuation
* of this row, and so we want to copy the content to this row's
* columns before removing the row.
*
* @throws InvalidTableStructure
*/
public function absorbRowContent(TableRow $targetRow) : void
{
// iterate over each column and combine the content
foreach ($this->getColumns() as $columnIndex => $column) {
$targetColumn = $targetRow->getColumn($columnIndex);
if ($targetColumn === null) {
throw new InvalidTableStructure(sprintf('Malformed table: lines "%s" and "%s" appear to be in the same row, but don\'t share the same number of columns.', $this->toString(), $targetRow->toString()));
}

$column->addContent("\n" . $targetColumn->getContent());
}
}

public function toString() : string
{
return implode(' | ', array_map(static function (TableColumn $column) {
return $column->getContent();
}, $this->columns));
}
}
Loading

0 comments on commit 59e913f

Please sign in to comment.