Skip to content
Permalink
Browse files

bug #31377 [Console] Fix auto-complete for ChoiceQuestion (multi-sele…

…ct answers) (battye)

This PR was squashed before being merged into the 3.4 branch (closes #31377).

Discussion
----------

[Console] Fix auto-complete for ChoiceQuestion (multi-select answers)

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #24072
| License       | MIT
| Doc PR        | -

Previously, a bug existed whereby for multi-select questions, the auto-complete would only work for the first answer supplied by the user. On all subsequent answers, the auto-complete would not appear.

Now it works as expected:

![screenshot](https://user-images.githubusercontent.com/2110222/57158657-8c147e80-6e16-11e9-94f7-a9bc95506545.png)

Commits
-------

59321fe [Console] Fix auto-complete for ChoiceQuestion (multi-select answers)
  • Loading branch information...
fabpot committed May 6, 2019
2 parents 5453f3e + 59321fe commit 60b505e88cd036685a8fc6674ea4c3f3b7186cd0
@@ -239,6 +239,7 @@ protected function writeError(OutputInterface $output, \Exception $error)
*/
private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete)
{
$fullChoice = '';
$ret = '';
$i = 0;
@@ -265,6 +266,7 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
} elseif ("\177" === $c) { // Backspace Character
if (0 === $numMatches && 0 !== $i) {
--$i;
$fullChoice = substr($fullChoice, 0, -1);
// Move cursor backwards
$output->write("\033[1D");
}
@@ -301,8 +303,10 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
if ($numMatches > 0 && -1 !== $ofs) {
$ret = $matches[$ofs];
// Echo out remaining chars for current match
$output->write(substr($ret, $i));
$i = \strlen($ret);
$remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
$output->write($remainingCharacters);
$fullChoice .= $remainingCharacters;
$i = \strlen($fullChoice);
}
if ("\n" === $c) {
@@ -321,14 +325,21 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
$output->write($c);
$ret .= $c;
$fullChoice .= $c;
++$i;
$tempRet = $ret;
if ($question instanceof ChoiceQuestion && $question->isMultiselect()) {
$tempRet = $this->mostRecentlyEnteredValue($fullChoice);
}
$numMatches = 0;
$ofs = 0;
foreach ($autocomplete as $value) {
// If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
if (0 === strpos($value, $ret)) {
if (0 === strpos($value, $tempRet)) {
$matches[$numMatches++] = $value;
}
}
@@ -340,8 +351,9 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
if ($numMatches > 0 && -1 !== $ofs) {
// Save cursor position
$output->write("\0337");
// Write highlighted text
$output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $i)).'</hl>');
// Write highlighted text, complete the partially entered response
$charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
$output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>');
// Restore cursor position
$output->write("\0338");
}
@@ -350,7 +362,24 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
// Reset stty so it behaves normally again
shell_exec(sprintf('stty %s', $sttyMode));
return $ret;
return $fullChoice;
}
private function mostRecentlyEnteredValue($entered)
{
$tempEntered = $entered;
// Determine the most recent value that the user entered
if (false !== strpos($entered, ',')) {
$choices = explode(',', $entered);
$lastChoice = trim($choices[\count($choices) - 1]);
if (\strlen($lastChoice) > 0) {
$tempEntered = $lastChoice;
}
}
return $tempEntered;
}
/**
@@ -1018,6 +1018,37 @@ public function testTraversableAutocomplete()
$this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
}
public function testTraversableMultiselectAutocomplete()
{
// <NEWLINE>
// F<TAB><NEWLINE>
// A<3x UP ARROW><TAB>,F<TAB><NEWLINE>
// F00<BACKSPACE><BACKSPACE>o<TAB>,A<DOWN ARROW>,<SPACE>SecurityBundle<NEWLINE>
// Acme<TAB>,<SPACE>As<TAB><29x BACKSPACE>S<TAB><NEWLINE>
// Ac<TAB>,As<TAB><3x BACKSPACE>d<TAB><NEWLINE>
$inputStream = $this->getInputStream("\nF\t\nA\033[A\033[A\033[A\t,F\t\nF00\177\177o\t,A\033[B\t, SecurityBundle\nAcme\t, As\t\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177S\t\nAc\t,As\t\177\177\177d\t\n");
$dialog = new QuestionHelper();
$helperSet = new HelperSet([new FormatterHelper()]);
$dialog->setHelperSet($helperSet);
$question = new ChoiceQuestion(
'Please select a bundle (defaults to AcmeDemoBundle and AsseticBundle)',
['AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'],
'0,1'
);
// This tests that autocomplete works for all multiselect choices entered by the user
$question->setMultiselect(true);
$this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals(['FooBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals(['AsseticBundle', 'FooBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals(['FooBundle', 'AsseticBundle', 'SecurityBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals(['SecurityBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
$this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
}
protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);

0 comments on commit 60b505e

Please sign in to comment.
You can’t perform that action at this time.