[Console] Get dimensions from stty on windows if possible
This commit is contained in:
parent
ac7df04fd6
commit
52650803bf
@ -21,6 +21,7 @@ use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
|||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||||
use Symfony\Component\Console\Question\Question;
|
use Symfony\Component\Console\Question\Question;
|
||||||
|
use Symfony\Component\Console\Terminal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The QuestionHelper class provides helpers to interact with the user.
|
* The QuestionHelper class provides helpers to interact with the user.
|
||||||
@ -157,7 +158,7 @@ class QuestionHelper extends Helper
|
|||||||
$inputStream = $this->inputStream ?: STDIN;
|
$inputStream = $this->inputStream ?: STDIN;
|
||||||
$autocomplete = $question->getAutocompleterValues();
|
$autocomplete = $question->getAutocompleterValues();
|
||||||
|
|
||||||
if (null === $autocomplete || !$this->hasSttyAvailable()) {
|
if (null === $autocomplete || !Terminal::hasSttyAvailable()) {
|
||||||
$ret = false;
|
$ret = false;
|
||||||
if ($question->isHidden()) {
|
if ($question->isHidden()) {
|
||||||
try {
|
try {
|
||||||
@ -409,7 +410,7 @@ class QuestionHelper extends Helper
|
|||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->hasSttyAvailable()) {
|
if (Terminal::hasSttyAvailable()) {
|
||||||
$sttyMode = shell_exec('stty -g');
|
$sttyMode = shell_exec('stty -g');
|
||||||
|
|
||||||
shell_exec('stty -echo');
|
shell_exec('stty -echo');
|
||||||
@ -495,20 +496,4 @@ class QuestionHelper extends Helper
|
|||||||
|
|
||||||
return self::$shell;
|
return self::$shell;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether Stty is available or not.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function hasSttyAvailable()
|
|
||||||
{
|
|
||||||
if (null !== self::$stty) {
|
|
||||||
return self::$stty;
|
|
||||||
}
|
|
||||||
|
|
||||||
exec('stty 2>&1', $output, $exitcode);
|
|
||||||
|
|
||||||
return self::$stty = 0 === $exitcode;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ class Terminal
|
|||||||
{
|
{
|
||||||
private static $width;
|
private static $width;
|
||||||
private static $height;
|
private static $height;
|
||||||
|
private static $stty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the terminal width.
|
* Gets the terminal width.
|
||||||
@ -54,6 +55,22 @@ class Terminal
|
|||||||
return self::$height ?: 50;
|
return self::$height ?: 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function hasSttyAvailable()
|
||||||
|
{
|
||||||
|
if (null !== self::$stty) {
|
||||||
|
return self::$stty;
|
||||||
|
}
|
||||||
|
|
||||||
|
exec('stty 2>&1', $output, $exitcode);
|
||||||
|
|
||||||
|
return self::$stty = 0 === $exitcode;
|
||||||
|
}
|
||||||
|
|
||||||
private static function initDimensions()
|
private static function initDimensions()
|
||||||
{
|
{
|
||||||
if ('\\' === \DIRECTORY_SEPARATOR) {
|
if ('\\' === \DIRECTORY_SEPARATOR) {
|
||||||
@ -62,12 +79,21 @@ class Terminal
|
|||||||
// or [w, h] from "wxh"
|
// or [w, h] from "wxh"
|
||||||
self::$width = (int) $matches[1];
|
self::$width = (int) $matches[1];
|
||||||
self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2];
|
self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2];
|
||||||
|
} elseif (self::hasSttyAvailable()) {
|
||||||
|
self::initDimensionsUsingStty();
|
||||||
} elseif (null !== $dimensions = self::getConsoleMode()) {
|
} elseif (null !== $dimensions = self::getConsoleMode()) {
|
||||||
// extract [w, h] from "wxh"
|
// extract [w, h] from "wxh"
|
||||||
self::$width = (int) $dimensions[0];
|
self::$width = (int) $dimensions[0];
|
||||||
self::$height = (int) $dimensions[1];
|
self::$height = (int) $dimensions[1];
|
||||||
}
|
}
|
||||||
} elseif ($sttyString = self::getSttyColumns()) {
|
} else {
|
||||||
|
self::initDimensionsUsingStty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function initDimensionsUsingStty()
|
||||||
|
{
|
||||||
|
if ($sttyString = self::getSttyColumns()) {
|
||||||
if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
|
if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
|
||||||
// extract [w, h] from "rows h; columns w;"
|
// extract [w, h] from "rows h; columns w;"
|
||||||
self::$width = (int) $matches[2];
|
self::$width = (int) $matches[2];
|
||||||
|
@ -19,6 +19,7 @@ use Symfony\Component\Console\Output\StreamOutput;
|
|||||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
use Symfony\Component\Console\Question\Question;
|
use Symfony\Component\Console\Question\Question;
|
||||||
|
use Symfony\Component\Console\Terminal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group tty
|
* @group tty
|
||||||
@ -167,7 +168,7 @@ class QuestionHelperTest extends AbstractQuestionHelperTest
|
|||||||
|
|
||||||
public function testAskWithAutocomplete()
|
public function testAskWithAutocomplete()
|
||||||
{
|
{
|
||||||
if (!$this->hasSttyAvailable()) {
|
if (!Terminal::hasSttyAvailable()) {
|
||||||
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,7 +201,7 @@ class QuestionHelperTest extends AbstractQuestionHelperTest
|
|||||||
|
|
||||||
public function testAskWithAutocompleteWithNonSequentialKeys()
|
public function testAskWithAutocompleteWithNonSequentialKeys()
|
||||||
{
|
{
|
||||||
if (!$this->hasSttyAvailable()) {
|
if (!Terminal::hasSttyAvailable()) {
|
||||||
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,7 +220,7 @@ class QuestionHelperTest extends AbstractQuestionHelperTest
|
|||||||
|
|
||||||
public function testAskWithAutocompleteWithExactMatch()
|
public function testAskWithAutocompleteWithExactMatch()
|
||||||
{
|
{
|
||||||
if (!$this->hasSttyAvailable()) {
|
if (!Terminal::hasSttyAvailable()) {
|
||||||
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,7 +256,7 @@ class QuestionHelperTest extends AbstractQuestionHelperTest
|
|||||||
*/
|
*/
|
||||||
public function testAskWithAutocompleteWithMultiByteCharacter($character)
|
public function testAskWithAutocompleteWithMultiByteCharacter($character)
|
||||||
{
|
{
|
||||||
if (!$this->hasSttyAvailable()) {
|
if (!Terminal::hasSttyAvailable()) {
|
||||||
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +280,7 @@ class QuestionHelperTest extends AbstractQuestionHelperTest
|
|||||||
|
|
||||||
public function testAutocompleteWithTrailingBackslash()
|
public function testAutocompleteWithTrailingBackslash()
|
||||||
{
|
{
|
||||||
if (!$this->hasSttyAvailable()) {
|
if (!Terminal::hasSttyAvailable()) {
|
||||||
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -672,7 +673,7 @@ class QuestionHelperTest extends AbstractQuestionHelperTest
|
|||||||
*/
|
*/
|
||||||
public function testLegacyAskWithAutocomplete()
|
public function testLegacyAskWithAutocomplete()
|
||||||
{
|
{
|
||||||
if (!$this->hasSttyAvailable()) {
|
if (!Terminal::hasSttyAvailable()) {
|
||||||
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -709,7 +710,7 @@ class QuestionHelperTest extends AbstractQuestionHelperTest
|
|||||||
*/
|
*/
|
||||||
public function testLegacyAskWithAutocompleteWithNonSequentialKeys()
|
public function testLegacyAskWithAutocompleteWithNonSequentialKeys()
|
||||||
{
|
{
|
||||||
if (!$this->hasSttyAvailable()) {
|
if (!Terminal::hasSttyAvailable()) {
|
||||||
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -977,7 +978,7 @@ class QuestionHelperTest extends AbstractQuestionHelperTest
|
|||||||
|
|
||||||
public function testTraversableAutocomplete()
|
public function testTraversableAutocomplete()
|
||||||
{
|
{
|
||||||
if (!$this->hasSttyAvailable()) {
|
if (!Terminal::hasSttyAvailable()) {
|
||||||
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1062,13 +1063,6 @@ class QuestionHelperTest extends AbstractQuestionHelperTest
|
|||||||
|
|
||||||
return $mock;
|
return $mock;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function hasSttyAvailable()
|
|
||||||
{
|
|
||||||
exec('stty 2>&1', $output, $exitcode);
|
|
||||||
|
|
||||||
return 0 === $exitcode;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AutocompleteValues implements \IteratorAggregate
|
class AutocompleteValues implements \IteratorAggregate
|
||||||
|
@ -18,17 +18,31 @@ class TerminalTest extends TestCase
|
|||||||
{
|
{
|
||||||
private $colSize;
|
private $colSize;
|
||||||
private $lineSize;
|
private $lineSize;
|
||||||
|
private $ansiCon;
|
||||||
|
|
||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
$this->colSize = getenv('COLUMNS');
|
$this->colSize = getenv('COLUMNS');
|
||||||
$this->lineSize = getenv('LINES');
|
$this->lineSize = getenv('LINES');
|
||||||
|
$this->ansiCon = getenv('ANSICON');
|
||||||
|
$this->resetStatics();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function tearDown()
|
protected function tearDown()
|
||||||
{
|
{
|
||||||
putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS');
|
putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS');
|
||||||
putenv($this->lineSize ? 'LINES' : 'LINES='.$this->lineSize);
|
putenv($this->lineSize ? 'LINES' : 'LINES='.$this->lineSize);
|
||||||
|
putenv($this->ansiCon ? 'ANSICON='.$this->ansiCon : 'ANSICON');
|
||||||
|
$this->resetStatics();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resetStatics()
|
||||||
|
{
|
||||||
|
foreach (['height', 'width', 'stty'] as $name) {
|
||||||
|
$property = new \ReflectionProperty(Terminal::class, $name);
|
||||||
|
$property->setAccessible(true);
|
||||||
|
$property->setValue(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test()
|
public function test()
|
||||||
@ -56,4 +70,28 @@ class TerminalTest extends TestCase
|
|||||||
$this->assertSame(0, $terminal->getWidth());
|
$this->assertSame(0, $terminal->getWidth());
|
||||||
$this->assertSame(0, $terminal->getHeight());
|
$this->assertSame(0, $terminal->getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSttyOnWindows()
|
||||||
|
{
|
||||||
|
if ('\\' !== \DIRECTORY_SEPARATOR) {
|
||||||
|
$this->markTestSkipped('Must be on windows');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sttyString = exec('(stty -a | grep columns) 2>&1', $output, $exitcode);
|
||||||
|
if (0 !== $exitcode) {
|
||||||
|
$this->markTestSkipped('Must have stty support');
|
||||||
|
}
|
||||||
|
|
||||||
|
$matches = [];
|
||||||
|
if (0 === preg_match('/columns.(\d+)/i', $sttyString, $matches)) {
|
||||||
|
$this->fail('Could not determine existing stty columns');
|
||||||
|
}
|
||||||
|
|
||||||
|
putenv('COLUMNS');
|
||||||
|
putenv('LINES');
|
||||||
|
putenv('ANSICON');
|
||||||
|
|
||||||
|
$terminal = new Terminal();
|
||||||
|
$this->assertSame((int) $matches[1], $terminal->getWidth());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user