[Console] fixed progress bar when using ANSI colors and Emojis
This commit is contained in:
parent
38f7a6f817
commit
8c0022b769
@ -11,6 +11,8 @@
|
||||
|
||||
namespace Symfony\Component\Console\Helper;
|
||||
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
|
||||
|
||||
/**
|
||||
* Helper is the base class for all helper classes.
|
||||
*
|
||||
@ -103,4 +105,17 @@ abstract class Helper implements HelperInterface
|
||||
|
||||
return sprintf('%d B', $memory);
|
||||
}
|
||||
|
||||
public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string)
|
||||
{
|
||||
$isDecorated = $formatter->isDecorated();
|
||||
$formatter->setDecorated(false);
|
||||
// remove <...> formatting
|
||||
$string = $formatter->format($string);
|
||||
// remove already formatted characters
|
||||
$string = preg_replace("/\033\[[^m]*m/", '', $string);
|
||||
$formatter->setDecorated($isDecorated);
|
||||
|
||||
return self::strlen($string);
|
||||
}
|
||||
}
|
||||
|
@ -387,12 +387,15 @@ class ProgressBar
|
||||
throw new \LogicException('You must start the progress bar before calling display().');
|
||||
}
|
||||
|
||||
// these 3 variables can be removed in favor of using $this in the closure when support for PHP 5.3 will be dropped.
|
||||
$self = $this;
|
||||
$this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self) {
|
||||
$output = $this->output;
|
||||
$messages = $this->messages;
|
||||
$this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) use ($self, $output, $messages) {
|
||||
if ($formatter = $self::getPlaceholderFormatterDefinition($matches[1])) {
|
||||
$text = call_user_func($formatter, $self);
|
||||
} elseif (isset($this->messages[$matches[1]])) {
|
||||
$text = $this->messages[$matches[1]];
|
||||
$text = call_user_func($formatter, $self, $output);
|
||||
} elseif (isset($messages[$matches[1]])) {
|
||||
$text = $messages[$matches[1]];
|
||||
} else {
|
||||
return $matches[0];
|
||||
}
|
||||
@ -424,13 +427,15 @@ class ProgressBar
|
||||
*/
|
||||
private function overwrite($message)
|
||||
{
|
||||
$length = Helper::strlen($message);
|
||||
$lines = explode("\n", $message);
|
||||
|
||||
// append whitespace to match the last line's length
|
||||
// FIXME: on each line!!!!!
|
||||
// FIXME: max of each line for lastMessagesLength or an array?
|
||||
if (null !== $this->lastMessagesLength && $this->lastMessagesLength > $length) {
|
||||
$message = str_pad($message, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT);
|
||||
// append whitespace to match the line's length
|
||||
if (null !== $this->lastMessagesLength) {
|
||||
foreach ($lines as $i => $line) {
|
||||
if ($this->lastMessagesLength > Helper::strlenWithoutDecoration($this->output->getFormatter(), $line)) {
|
||||
$lines[$i] = str_pad($line, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move back to the beginning of the progress bar before redrawing it
|
||||
@ -438,9 +443,15 @@ class ProgressBar
|
||||
if ($this->formatLineCount) {
|
||||
$this->output->write(sprintf("\033[%dA", $this->formatLineCount));
|
||||
}
|
||||
$this->output->write($message);
|
||||
$this->output->write(implode("\n", $lines));
|
||||
|
||||
$this->lastMessagesLength = Helper::strlen($message);
|
||||
$this->lastMessagesLength = 0;
|
||||
foreach ($lines as $line) {
|
||||
$len = Helper::strlenWithoutDecoration($this->output->getFormatter(), $line);
|
||||
if ($len > $this->lastMessagesLength) {
|
||||
$this->lastMessagesLength = $len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function determineBestFormat()
|
||||
@ -460,11 +471,11 @@ class ProgressBar
|
||||
private static function initPlaceholderFormatters()
|
||||
{
|
||||
return array(
|
||||
'bar' => function (ProgressBar $bar) {
|
||||
'bar' => function (ProgressBar $bar, OutputInterface $output) {
|
||||
$completeBars = floor($bar->getMaxSteps() > 0 ? $bar->getProgressPercent() * $bar->getBarWidth() : $bar->getStep() % $bar->getBarWidth());
|
||||
$emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlen($bar->getProgressCharacter());
|
||||
$display = str_repeat($bar->getBarCharacter(), $completeBars);
|
||||
if ($completeBars < $bar->getBarWidth()) {
|
||||
$emptyBars = $bar->getBarWidth() - $completeBars - Helper::strlenWithoutDecoration($output->getFormatter(), $bar->getProgressCharacter());
|
||||
$display .= $bar->getProgressCharacter().str_repeat($bar->getEmptyBarCharacter(), $emptyBars);
|
||||
}
|
||||
|
||||
|
@ -427,7 +427,7 @@ class TableHelper extends Helper
|
||||
$width += strlen($cell) - mb_strlen($cell, $encoding);
|
||||
}
|
||||
|
||||
$width += $this->strlen($cell) - $this->computeLengthWithoutDecoration($cell);
|
||||
$width += $this->strlen($cell) - self::strlenWithoutDecoration($this->output->getFormatter(), $cell);
|
||||
|
||||
$content = sprintf($this->cellRowContentFormat, $cell);
|
||||
|
||||
@ -486,7 +486,7 @@ class TableHelper extends Helper
|
||||
*/
|
||||
private function getCellWidth(array $row, $column)
|
||||
{
|
||||
return isset($row[$column]) ? $this->computeLengthWithoutDecoration($row[$column]) : 0;
|
||||
return isset($row[$column]) ? self::strlenWithoutDecoration($this->output->getFormatter(), $row[$column]) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -498,18 +498,6 @@ class TableHelper extends Helper
|
||||
$this->numberOfColumns = null;
|
||||
}
|
||||
|
||||
private function computeLengthWithoutDecoration($string)
|
||||
{
|
||||
$formatter = $this->output->getFormatter();
|
||||
$isDecorated = $formatter->isDecorated();
|
||||
$formatter->setDecorated(false);
|
||||
|
||||
$string = $formatter->format($string);
|
||||
$formatter->setDecorated($isDecorated);
|
||||
|
||||
return $this->strlen($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Symfony\Component\Console\Tests\Helper;
|
||||
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Helper\Helper;
|
||||
use Symfony\Component\Console\Output\StreamOutput;
|
||||
|
||||
class ProgressBarTest extends \PHPUnit_Framework_TestCase
|
||||
@ -206,7 +207,7 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals(
|
||||
$this->generateOutput(' 0/50 [>---------------------------] 0%').
|
||||
$this->generateOutput(' 25/50 [==============>-------------] 50%').
|
||||
$this->generateOutput(''),
|
||||
$this->generateOutput(' '),
|
||||
stream_get_contents($output->getStream())
|
||||
);
|
||||
}
|
||||
@ -332,9 +333,53 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
|
||||
rewind($output->getStream());
|
||||
$this->assertEquals(
|
||||
$this->generateOutput(">---------------------------\nfoobar").
|
||||
$this->generateOutput("=========>------------------\nfoobar").
|
||||
$this->generateOutput("\n").
|
||||
$this->generateOutput("============================\nfoobar"),
|
||||
$this->generateOutput("=========>------------------\nfoobar ").
|
||||
$this->generateOutput(" \n ").
|
||||
$this->generateOutput("============================\nfoobar "),
|
||||
stream_get_contents($output->getStream())
|
||||
);
|
||||
}
|
||||
|
||||
public function testAnsiColorsAndEmojis()
|
||||
{
|
||||
$bar = new ProgressBar($output = $this->getOutputStream(), 15);
|
||||
ProgressBar::setPlaceholderFormatterDefinition('memory', function (ProgressBar $bar) {
|
||||
static $i = 0;
|
||||
$mem = 100000 * $i;
|
||||
$colors = $i++ ? '41;37' : '44;37';
|
||||
|
||||
return "\033[".$colors."m ".Helper::formatMemory($mem)." \033[0m";
|
||||
});
|
||||
$bar->setFormat(" \033[44;37m %title:-37s% \033[0m\n %current%/%max% %bar% %percent:3s%%\n 🏁 %remaining:-10s% %memory:37s%");
|
||||
$bar->setBarCharacter($done = "\033[32m●\033[0m");
|
||||
$bar->setEmptyBarCharacter($empty = "\033[31m●\033[0m");
|
||||
$bar->setProgressCharacter($progress = "\033[32m➤ \033[0m");
|
||||
|
||||
$bar->setMessage('Starting the demo... fingers crossed', 'title');
|
||||
$bar->start();
|
||||
$bar->setMessage('Looks good to me...', 'title');
|
||||
$bar->advance(4);
|
||||
$bar->setMessage('Thanks, bye', 'title');
|
||||
$bar->finish();
|
||||
|
||||
rewind($output->getStream());
|
||||
|
||||
$this->assertEquals(
|
||||
$this->generateOutput(
|
||||
" \033[44;37m Starting the demo... fingers crossed \033[0m\n".
|
||||
" 0/15 ".$progress.str_repeat($empty, 26)." 0%\n".
|
||||
" 🏁 1 sec \033[44;37m 0 B \033[0m"
|
||||
).
|
||||
$this->generateOutput(
|
||||
" \033[44;37m Looks good to me... \033[0m\n".
|
||||
" 4/15 ".str_repeat($done, 7).$progress.str_repeat($empty, 19)." 26%\n".
|
||||
" 🏁 1 sec \033[41;37m 97 kB \033[0m"
|
||||
).
|
||||
$this->generateOutput(
|
||||
" \033[44;37m Thanks, bye \033[0m\n".
|
||||
" 15/15 ".str_repeat($done, 28)." 100%\n".
|
||||
" 🏁 1 sec \033[41;37m 195 kB \033[0m"
|
||||
),
|
||||
stream_get_contents($output->getStream())
|
||||
);
|
||||
}
|
||||
@ -346,16 +391,8 @@ class ProgressBarTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
protected function generateOutput($expected)
|
||||
{
|
||||
$expectedout = $expected;
|
||||
|
||||
if (null !== $this->lastMessagesLength) {
|
||||
$expectedout = str_pad($expected, $this->lastMessagesLength, "\x20", STR_PAD_RIGHT);
|
||||
}
|
||||
|
||||
$this->lastMessagesLength = strlen($expectedout);
|
||||
|
||||
$count = substr_count($expected, "\n");
|
||||
|
||||
return "\x0D".($count ? sprintf("\033[%dA", $count) : '').$expectedout;
|
||||
return "\x0D".($count ? sprintf("\033[%dA", $count) : '').$expected;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user