feature #22225 [Console] Support formatted text cutting (ro0NL)

This PR was squashed before being merged into the 4.2-dev branch (closes #22225).

Discussion
----------

[Console] Support formatted text cutting

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes/no
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!--highly recommended for new features-->

allows cutting a formatted text to a certain width. Actually needed if we want to support max. column widths in tables (see #22156)

```php
$text = 'pre <error>foo bar baz</error> post';
dump('BEFORE');
$output->writeln(wordwrap($output->getFormatter()->format($text), 3, "\n", true), OutputInterface::OUTPUT_RAW);
dump('AFTER');
$output->writeln($output->getFormatter()->format($text, 3), OutputInterface::OUTPUT_RAW);
```
![image](https://cloud.githubusercontent.com/assets/1047696/24519346/19c9b0ca-1585-11e7-8437-0bcfb6fab63e.png)

Commits
-------

09f8ad9 [Console] Support formatted text cutting
This commit is contained in:
Robin Chalas 2018-09-04 22:10:18 +02:00
commit c3fd60df2c
4 changed files with 67 additions and 6 deletions

View File

@ -9,6 +9,7 @@ CHANGELOG
* deprecated passing a command as a string to `ProcessHelper::run()`,
pass it the command as an array of its arguments instead
* made the `ProcessHelper` class final
* added `WrappableOutputFormatterInterface::formatAndWrap()` (implemented in `OutputFormatter`)
4.1.0
-----

View File

@ -17,8 +17,9 @@ use Symfony\Component\Console\Exception\InvalidArgumentException;
* Formatter class for console output.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
* @author Roland Franssen <franssen.roland@gmail.com>
*/
class OutputFormatter implements OutputFormatterInterface
class OutputFormatter implements WrappableOutputFormatterInterface
{
private $decorated;
private $styles = array();
@ -130,7 +131,14 @@ class OutputFormatter implements OutputFormatterInterface
*/
public function format($message)
{
$message = (string) $message;
return $this->formatAndWrap((string) $message, 0);
}
/**
* {@inheritdoc}
*/
public function formatAndWrap(string $message, int $width)
{
$offset = 0;
$output = '';
$tagRegex = '[a-z][a-z0-9,_=;-]*+';
@ -144,7 +152,7 @@ class OutputFormatter implements OutputFormatterInterface
}
// add the text up to the next tag
$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset));
$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width);
$offset = $pos + \strlen($text);
// opening tag?
@ -158,7 +166,7 @@ class OutputFormatter implements OutputFormatterInterface
// </>
$this->styleStack->pop();
} elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
$output .= $this->applyCurrentStyle($text);
$output .= $this->applyCurrentStyle($text, $output, $width);
} elseif ($open) {
$this->styleStack->push($style);
} else {
@ -166,7 +174,7 @@ class OutputFormatter implements OutputFormatterInterface
}
}
$output .= $this->applyCurrentStyle(substr($message, $offset));
$output .= $this->applyCurrentStyle(substr($message, $offset), $output, $width);
if (false !== strpos($output, "\0")) {
return strtr($output, array("\0" => '\\', '\\<' => '<'));
@ -223,8 +231,24 @@ class OutputFormatter implements OutputFormatterInterface
/**
* Applies current style from stack to text, if must be applied.
*/
private function applyCurrentStyle(string $text): string
private function applyCurrentStyle(string $text, string $current, int $width): string
{
if ('' === $text) {
return '';
}
if ($width) {
if ('' !== $current) {
$text = ltrim($text);
}
$text = wordwrap($text, $width, "\n", true);
if ('' !== $current && "\n" !== substr($current, -1)) {
$text = "\n".$text;
}
}
return $this->isDecorated() && \strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
}
}

View File

@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Formatter;
/**
* Formatter interface for console output that supports word wrapping.
*
* @author Roland Franssen <franssen.roland@gmail.com>
*/
interface WrappableOutputFormatterInterface extends OutputFormatterInterface
{
/**
* Formats a message according to the given styles, wrapping at `$width` (0 means no wrapping).
*/
public function formatAndWrap(string $message, int $width);
}

View File

@ -322,6 +322,17 @@ more text
EOF
));
}
public function testFormatAndWrap()
{
$formatter = new OutputFormatter(true);
$this->assertSame("pre\n\033[37;41mfoo\nbar\nbaz\033[39;49m\npos\nt", $formatter->formatAndWrap('pre <error>foo bar baz</error> post', 3));
$formatter = new OutputFormatter();
$this->assertSame("pre\nfoo\nbar\nbaz\npos\nt", $formatter->formatAndWrap('pre <error>foo bar baz</error> post', 3));
}
}
class TableCell