[Console] Fix traversable autocomplete values

This commit is contained in:
Roland Franssen 2017-11-11 23:09:14 +01:00
parent 6fe8435c6f
commit 965b5b5f8d
3 changed files with 54 additions and 7 deletions

View File

@ -134,7 +134,7 @@ class QuestionHelper extends Helper
$ret = trim($ret);
}
} else {
$ret = trim($this->autocomplete($output, $question, $inputStream));
$ret = trim($this->autocomplete($output, $question, $inputStream, is_array($autocomplete) ? $autocomplete : iterator_to_array($autocomplete, false)));
}
$ret = strlen($ret) > 0 ? $ret : $question->getDefault();
@ -190,12 +190,12 @@ class QuestionHelper extends Helper
* @param OutputInterface $output
* @param Question $question
* @param resource $inputStream
* @param array $autocomplete
*
* @return string
*/
private function autocomplete(OutputInterface $output, Question $question, $inputStream)
private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete)
{
$autocomplete = $question->getAutocompleterValues();
$ret = '';
$i = 0;

View File

@ -137,10 +137,8 @@ class Question
$values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values);
}
if (null !== $values && !is_array($values)) {
if (!$values instanceof \Traversable || !$values instanceof \Countable) {
throw new \InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.');
}
if (null !== $values && !is_array($values) && !$values instanceof \Traversable) {
throw new \InvalidArgumentException('Autocompleter values can be either an array, `null` or a `Traversable` object.');
}
if ($this->hidden) {

View File

@ -514,6 +514,40 @@ class QuestionHelperTest extends TestCase
new ChoiceQuestion('Question', array(), 'irrelevant');
}
public function testTraversableAutocomplete()
{
if (!$this->hasSttyAvailable()) {
$this->markTestSkipped('`stty` is required to test autocomplete functionality');
}
// Acm<NEWLINE>
// Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE>
// <NEWLINE>
// <UP ARROW><UP ARROW><NEWLINE>
// <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE>
// <DOWN ARROW><NEWLINE>
// S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE>
// F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE>
$inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n");
$dialog = new QuestionHelper();
$dialog->setInputStream($inputStream);
$helperSet = new HelperSet(array(new FormatterHelper()));
$dialog->setHelperSet($helperSet);
$question = new Question('Please select a bundle', 'FrameworkBundle');
$question->setAutocompleterValues(new AutocompleteValues(array('irrelevant' => 'AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle')));
$this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('FrameworkBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('SecurityBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('FooBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
}
protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);
@ -545,3 +579,18 @@ class QuestionHelperTest extends TestCase
return 0 === $exitcode;
}
}
class AutocompleteValues implements \IteratorAggregate
{
private $values;
public function __construct(array $values)
{
$this->values = $values;
}
public function getIterator()
{
return new \ArrayIterator($this->values);
}
}