Skip to content

[Console] progress bar fix #19012

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 21 additions & 83 deletions src/Symfony/Component/Console/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,19 @@ class Application
private $definition;
private $helperSet;
private $dispatcher;
private $terminalDimensions;
private $terminal;
private $defaultCommand;
private $singleCommand;

/**
* Constructor.
*
* @param string $name The name of the application
* @param string $version The version of the application
*/
public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
{
$this->name = $name;
$this->version = $version;
$this->terminal = new Terminal();
$this->defaultCommand = 'list';
$this->helperSet = $this->getDefaultHelperSet();
$this->definition = $this->getDefaultInputDefinition();
Expand Down Expand Up @@ -625,7 +624,7 @@ public function renderException(\Exception $e, OutputInterface $output)

$len = $this->stringWidth($title);

$width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX;
$width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : PHP_INT_MAX;
// HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327
if (defined('HHVM_VERSION') && $width > 1 << 31) {
$width = 1 << 31;
Expand Down Expand Up @@ -689,60 +688,42 @@ public function renderException(\Exception $e, OutputInterface $output)
* Tries to figure out the terminal width in which this application runs.
*
* @return int|null
*
* @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead.
*/
protected function getTerminalWidth()
{
$dimensions = $this->getTerminalDimensions();
@trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__, ArgumentResolverInterface::class), E_USER_DEPRECATED);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add the deprecation warnings too

return $dimensions[0];
return $this->terminal->getWidth();
}

/**
* Tries to figure out the terminal height in which this application runs.
*
* @return int|null
*
* @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead.
*/
protected function getTerminalHeight()
{
$dimensions = $this->getTerminalDimensions();
@trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__, ArgumentResolverInterface::class), E_USER_DEPRECATED);

return $dimensions[1];
return $this->terminal->getHeight();
}

/**
* Tries to figure out the terminal dimensions based on the current environment.
*
* @return array Array containing width and height
*
* @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead.
*/
public function getTerminalDimensions()
{
if ($this->terminalDimensions) {
return $this->terminalDimensions;
}
@trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__, ArgumentResolverInterface::class), E_USER_DEPRECATED);

if ('\\' === DIRECTORY_SEPARATOR) {
// extract [w, H] from "wxh (WxH)"
if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
return array((int) $matches[1], (int) $matches[2]);
}
// extract [w, h] from "wxh"
if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) {
return array((int) $matches[1], (int) $matches[2]);
}
}

if ($sttyString = $this->getSttyColumns()) {
// extract [w, h] from "rows h; columns w;"
if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
return array((int) $matches[2], (int) $matches[1]);
}
// extract [w, h] from "; h rows; w columns"
if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
return array((int) $matches[2], (int) $matches[1]);
}
}

return array(null, null);
return array($this->terminal->getWidth(), $this->terminal->getHeight());
}

/**
Expand All @@ -754,10 +735,15 @@ public function getTerminalDimensions()
* @param int $height The height
*
* @return Application The current application
*
* @deprecated since version 3.2, to be removed in 4.0. Set the COLUMNS and LINES env vars instead.
*/
public function setTerminalDimensions($width, $height)
{
$this->terminalDimensions = array($width, $height);
@trigger_error(sprintf('%s is deprecated as of 3.2 and will be removed in 4.0. Set the COLUMNS and LINES env vars instead.', __METHOD__, ArgumentResolverInterface::class), E_USER_DEPRECATED);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we usually use The %s() method... if I'm not wrong

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are using both, but this one a bit more than yours :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

never ending PRs to come :)


putenv('COLUMNS='.$width);
putenv('LINES='.$height);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does not work in case the dimensions were already retrieved previously, because of the Terminal internal cache

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


return $this;
}
Expand Down Expand Up @@ -927,54 +913,6 @@ protected function getDefaultHelperSet()
));
}

/**
* Runs and parses stty -a if it's available, suppressing any error output.
*
* @return string
*/
private function getSttyColumns()
{
if (!function_exists('proc_open')) {
return;
}

$descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
$process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
if (is_resource($process)) {
$info = stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);

return $info;
}
}

/**
* Runs and parses mode CON if it's available, suppressing any error output.
*
* @return string <width>x<height> or null if it could not be parsed
*/
private function getConsoleMode()
{
if (!function_exists('proc_open')) {
return;
}

$descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
$process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
if (is_resource($process)) {
$info = stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);

if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
return $matches[2].'x'.$matches[1];
}
}
}

/**
* Returns abbreviated suggestions in string format.
*
Expand Down
57 changes: 39 additions & 18 deletions src/Symfony/Component/Console/Helper/ProgressBar.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Terminal;

/**
* The ProgressBar provides helpers to display progress output.
Expand Down Expand Up @@ -44,13 +45,12 @@ class ProgressBar
private $formatLineCount;
private $messages = array();
private $overwrite = true;
private $terminal;

private static $formatters;
private static $formats;

/**
* Constructor.
*
* @param OutputInterface $output An OutputInterface instance
* @param int $max Maximum steps (0 if unknown)
*/
Expand All @@ -62,6 +62,7 @@ public function __construct(OutputInterface $output, $max = 0)

$this->output = $output;
$this->setMaxSteps($max);
$this->terminal = new Terminal();

if (!$this->output->isDecorated()) {
// disable overwrite when output does not support ANSI codes.
Expand Down Expand Up @@ -217,7 +218,7 @@ public function getProgressPercent()
*/
public function setBarWidth($size)
{
$this->barWidth = (int) $size;
$this->barWidth = max(1, (int) $size);
}

/**
Expand Down Expand Up @@ -412,21 +413,7 @@ public function display()
$this->setRealFormat($this->internalFormat ?: $this->determineBestFormat());
}

$this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) {
if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) {
$text = call_user_func($formatter, $this, $this->output);
} elseif (isset($this->messages[$matches[1]])) {
$text = $this->messages[$matches[1]];
} else {
return $matches[0];
}

if (isset($matches[2])) {
$text = sprintf('%'.$matches[2], $text);
}

return $text;
}, $this->format));
$this->overwrite($this->buildLine());
}

/**
Expand Down Expand Up @@ -592,4 +579,38 @@ private static function initFormats()
'debug_nomax' => ' %current% [%bar%] %elapsed:6s% %memory:6s%',
);
}

/**
* @return string
*/
private function buildLine()
{
$regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i";
$callback = function ($matches) {
if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) {
$text = call_user_func($formatter, $this, $this->output);
} elseif (isset($this->messages[$matches[1]])) {
$text = $this->messages[$matches[1]];
} else {
return $matches[0];
}

if (isset($matches[2])) {
$text = sprintf('%'.$matches[2], $text);
}

return $text;
};
$line = preg_replace_callback($regex, $callback, $this->format);

$lineLength = Helper::strlenWithoutDecoration($this->output->getFormatter(), $line);
$terminalWidth = $this->terminal->getWidth();
if ($lineLength <= $terminalWidth) {
return $line;
}

$this->setBarWidth($this->barWidth - $lineLength + $terminalWidth);

return preg_replace_callback($regex, $callback, $this->format);
}
}
13 changes: 3 additions & 10 deletions src/Symfony/Component/Console/Style/SymfonyStyle.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

namespace Symfony\Component\Console\Style;

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\Helper;
Expand All @@ -25,6 +24,7 @@
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Terminal;

/**
* Output decorator helpers for the Symfony Style Guide.
Expand All @@ -50,7 +50,8 @@ public function __construct(InputInterface $input, OutputInterface $output)
$this->input = $input;
$this->bufferedOutput = new BufferedOutput($output->getVerbosity(), false, clone $output->getFormatter());
// Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not.
$this->lineLength = min($this->getTerminalWidth() - (int) (DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH);
$width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH;
$this->lineLength = min($width - (int) (DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH);

parent::__construct($output);
}
Expand Down Expand Up @@ -401,14 +402,6 @@ private function getProgressBar()
return $this->progressBar;
}

private function getTerminalWidth()
{
$application = new Application();
$dimensions = $application->getTerminalDimensions();

return $dimensions[0] ?: self::MAX_LINE_LENGTH;
}

private function autoPrependBlock()
{
$chars = substr(str_replace(PHP_EOL, "\n", $this->bufferedOutput->fetch()), -2);
Expand Down
Loading