Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 175f68f

Browse filesBrowse files
ro0NLRobin Chalas
authored andcommitted
[Console] Support max column width in Table
1 parent c3fd60d commit 175f68f
Copy full SHA for 175f68f

File tree

Expand file treeCollapse file tree

4 files changed

+99
-15
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+99
-15
lines changed

‎src/Symfony/Component/Console/Formatter/OutputFormatter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Formatter/OutputFormatter.php
+35-12Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ public function formatAndWrap(string $message, int $width)
142142
$offset = 0;
143143
$output = '';
144144
$tagRegex = '[a-z][a-z0-9,_=;-]*+';
145+
$currentLineLength = 0;
145146
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#ix", $message, $matches, PREG_OFFSET_CAPTURE);
146147
foreach ($matches[0] as $i => $match) {
147148
$pos = $match[1];
@@ -152,7 +153,7 @@ public function formatAndWrap(string $message, int $width)
152153
}
153154

154155
// add the text up to the next tag
155-
$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width);
156+
$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength);
156157
$offset = $pos + \strlen($text);
157158

158159
// opening tag?
@@ -166,15 +167,15 @@ public function formatAndWrap(string $message, int $width)
166167
// </>
167168
$this->styleStack->pop();
168169
} elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
169-
$output .= $this->applyCurrentStyle($text, $output, $width);
170+
$output .= $this->applyCurrentStyle($text, $output, $width, $currentLineLength);
170171
} elseif ($open) {
171172
$this->styleStack->push($style);
172173
} else {
173174
$this->styleStack->pop($style);
174175
}
175176
}
176177

177-
$output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width);
178+
$output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width, $currentLineLength);
178179

179180
if (false !== strpos($output, "\0")) {
180181
return strtr($output, array("\0" => '\\', '\\<' => '<'));
@@ -231,24 +232,46 @@ private function createStyleFromString(string $string)
231232
/**
232233
* Applies current style from stack to text, if must be applied.
233234
*/
234-
private function applyCurrentStyle(string $text, string $current, int $width): string
235+
private function applyCurrentStyle(string $text, string $current, int $width, int &$currentLineLength): string
235236
{
236237
if ('' === $text) {
237238
return '';
238239
}
239240

240-
if ($width) {
241-
if ('' !== $current) {
242-
$text = ltrim($text);
243-
}
241+
if (!$width) {
242+
return $this->isDecorated() ? $this->styleStack->getCurrent()->apply($text) : $text;
243+
}
244+
245+
if (!$currentLineLength && '' !== $current) {
246+
$text = ltrim($text);
247+
}
248+
249+
if ($currentLineLength) {
250+
$prefix = substr($text, 0, $i = $width - $currentLineLength)."\n";
251+
$text = substr($text, $i);
252+
} else {
253+
$prefix = '';
254+
}
255+
256+
preg_match('~(\\n)$~', $text, $matches);
257+
$text = $prefix.preg_replace('~([^\\n]{'.$width.'})\\ *~', "\$1\n", $text);
258+
$text = rtrim($text, "\n").($matches[1] ?? '');
244259

245-
$text = wordwrap($text, $width, "\n", true);
260+
if (!$currentLineLength && '' !== $current && "\n" !== substr($current, -1)) {
261+
$text = "\n".$text;
262+
}
263+
264+
$lines = explode("\n", $text);
265+
if ($width === $currentLineLength = \strlen(end($lines))) {
266+
$currentLineLength = 0;
267+
}
246268

247-
if ('' !== $current && "\n" !== substr($current, -1)) {
248-
$text = "\n".$text;
269+
if ($this->isDecorated()) {
270+
foreach ($lines as $i => $line) {
271+
$lines[$i] = $this->styleStack->getCurrent()->apply($line);
249272
}
250273
}
251274

252-
return $this->isDecorated() && \strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
275+
return implode("\n", $lines);
253276
}
254277
}

‎src/Symfony/Component/Console/Helper/Table.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Helper/Table.php
+28-2Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Console\Exception\InvalidArgumentException;
1515
use Symfony\Component\Console\Exception\RuntimeException;
16+
use Symfony\Component\Console\Formatter\WrappableOutputFormatterInterface;
1617
use Symfony\Component\Console\Output\ConsoleSectionOutput;
1718
use Symfony\Component\Console\Output\OutputInterface;
1819

@@ -80,6 +81,7 @@ class Table
8081
* @var array
8182
*/
8283
private $columnWidths = array();
84+
private $columnMaxWidths = array();
8385

8486
private static $styles;
8587

@@ -222,6 +224,25 @@ public function setColumnWidths(array $widths)
222224
return $this;
223225
}
224226

227+
/**
228+
* Sets the maximum width of a column.
229+
*
230+
* Any cell within this column which contents exceeds the specified width will be wrapped into multiple lines, while
231+
* formatted strings are preserved.
232+
*
233+
* @return $this
234+
*/
235+
public function setColumnMaxWidth(int $columnIndex, int $width): self
236+
{
237+
if (!$this->output->getFormatter() instanceof WrappableOutputFormatterInterface) {
238+
throw new \LogicException(sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, \get_class($this->output->getFormatter())));
239+
}
240+
241+
$this->columnMaxWidths[$columnIndex] = $width;
242+
243+
return $this;
244+
}
245+
225246
public function setHeaders(array $headers)
226247
{
227248
$headers = array_values($headers);
@@ -434,7 +455,6 @@ private function renderColumnSeparator($type = self::BORDER_OUTSIDE)
434455
* Example:
435456
*
436457
* | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
437-
*
438458
*/
439459
private function renderRow(array $row, string $cellFormat)
440460
{
@@ -498,12 +518,17 @@ private function calculateNumberOfColumns($rows)
498518

499519
private function buildTableRows($rows)
500520
{
521+
/** @var WrappableOutputFormatterInterface $formatter */
522+
$formatter = $this->output->getFormatter();
501523
$unmergedRows = array();
502524
for ($rowKey = 0; $rowKey < \count($rows); ++$rowKey) {
503525
$rows = $this->fillNextRows($rows, $rowKey);
504526

505527
// Remove any new line breaks and replace it with a new line
506528
foreach ($rows[$rowKey] as $column => $cell) {
529+
if (isset($this->columnMaxWidths[$column]) && Helper::strlenWithoutDecoration($formatter, $cell) > $this->columnMaxWidths[$column]) {
530+
$cell = $formatter->formatAndWrap($cell, $this->columnMaxWidths[$column]);
531+
}
507532
if (!strstr($cell, "\n")) {
508533
continue;
509534
}
@@ -711,8 +736,9 @@ private function getCellWidth(array $row, int $column): int
711736
}
712737

713738
$columnWidth = isset($this->columnWidths[$column]) ? $this->columnWidths[$column] : 0;
739+
$cellWidth = max($cellWidth, $columnWidth);
714740

715-
return max($cellWidth, $columnWidth);
741+
return isset($this->columnMaxWidths[$column]) ? min($this->columnMaxWidths[$column], $cellWidth) : $cellWidth;
716742
}
717743

718744
/**

‎src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Tests/Formatter/OutputFormatterTest.php
+9-1Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,11 +327,19 @@ public function testFormatAndWrap()
327327
{
328328
$formatter = new OutputFormatter(true);
329329

330-
$this->assertSame("pre\n\033[37;41mfoo\nbar\nbaz\033[39;49m\npos\nt", $formatter->formatAndWrap('pre <error>foo bar baz</error> post', 3));
330+
$this->assertSame("fo\no\e[37;41mb\e[39;49m\n\e[37;41mar\e[39;49m\nba\nz", $formatter->formatAndWrap('foo<error>bar</error> baz', 2));
331+
$this->assertSame("pr\ne \e[37;41m\e[39;49m\n\e[37;41mfo\e[39;49m\n\e[37;41mo \e[39;49m\n\e[37;41mba\e[39;49m\n\e[37;41mr \e[39;49m\n\e[37;41mba\e[39;49m\n\e[37;41mz\e[39;49m \npo\nst", $formatter->formatAndWrap('pre <error>foo bar baz</error> post', 2));
332+
$this->assertSame("pre\e[37;41m\e[39;49m\n\e[37;41mfoo\e[39;49m\n\e[37;41mbar\e[39;49m\n\e[37;41mbaz\e[39;49m\npos\nt", $formatter->formatAndWrap('pre <error>foo bar baz</error> post', 3));
333+
$this->assertSame("pre \e[37;41m\e[39;49m\n\e[37;41mfoo \e[39;49m\n\e[37;41mbar \e[39;49m\n\e[37;41mbaz\e[39;49m \npost", $formatter->formatAndWrap('pre <error>foo bar baz</error> post', 4));
334+
$this->assertSame("pre \e[37;41mf\e[39;49m\n\e[37;41moo ba\e[39;49m\n\e[37;41mr baz\e[39;49m\npost", $formatter->formatAndWrap('pre <error>foo bar baz</error> post', 5));
331335

332336
$formatter = new OutputFormatter();
333337

338+
$this->assertSame("fo\nob\nar\nba\nz", $formatter->formatAndWrap('foo<error>bar</error> baz', 2));
339+
$this->assertSame("pr\ne \nfo\no \nba\nr \nba\nz \npo\nst", $formatter->formatAndWrap('pre <error>foo bar baz</error> post', 2));
334340
$this->assertSame("pre\nfoo\nbar\nbaz\npos\nt", $formatter->formatAndWrap('pre <error>foo bar baz</error> post', 3));
341+
$this->assertSame("pre \nfoo \nbar \nbaz \npost", $formatter->formatAndWrap('pre <error>foo bar baz</error> post', 4));
342+
$this->assertSame("pre f\noo ba\nr baz\npost", $formatter->formatAndWrap('pre <error>foo bar baz</error> post', 5));
335343
}
336344
}
337345

‎src/Symfony/Component/Console/Tests/Helper/TableTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Tests/Helper/TableTest.php
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,33 @@ public function renderSetTitle()
10501050
);
10511051
}
10521052

1053+
public function testColumnMaxWidths()
1054+
{
1055+
$table = new Table($output = $this->getOutputStream());
1056+
$table
1057+
->setRows(array(
1058+
array('Divine Comedy', 'A Tale of Two Cities', 'The Lord of the Rings', 'And Then There Were None'),
1059+
))
1060+
->setColumnMaxWidth(1, 5)
1061+
->setColumnMaxWidth(2, 10)
1062+
->setColumnMaxWidth(3, 15);
1063+
1064+
$table->render();
1065+
1066+
$expected =
1067+
<<<TABLE
1068+
+---------------+-------+------------+-----------------+
1069+
| Divine Comedy | A Tal | The Lord o | And Then There |
1070+
| | e of | f the Ring | Were None |
1071+
| | Two C | s | |
1072+
| | ities | | |
1073+
+---------------+-------+------------+-----------------+
1074+
1075+
TABLE;
1076+
1077+
$this->assertEquals($expected, $this->getOutputContent($output));
1078+
}
1079+
10531080
protected function getOutputStream($decorated = false)
10541081
{
10551082
return new StreamOutput($this->stream, StreamOutput::VERBOSITY_NORMAL, $decorated);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.