Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 60b505e

Browse filesBrowse files
committed
bug #31377 [Console] Fix auto-complete for ChoiceQuestion (multi-select 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)
2 parents 5453f3e + 59321fe commit 60b505e
Copy full SHA for 60b505e

File tree

Expand file treeCollapse file tree

2 files changed

+66
-6
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+66
-6
lines changed

‎src/Symfony/Component/Console/Helper/QuestionHelper.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Helper/QuestionHelper.php
+35-6Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ protected function writeError(OutputInterface $output, \Exception $error)
239239
*/
240240
private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete)
241241
{
242+
$fullChoice = '';
242243
$ret = '';
243244

244245
$i = 0;
@@ -265,6 +266,7 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
265266
} elseif ("\177" === $c) { // Backspace Character
266267
if (0 === $numMatches && 0 !== $i) {
267268
--$i;
269+
$fullChoice = substr($fullChoice, 0, -1);
268270
// Move cursor backwards
269271
$output->write("\033[1D");
270272
}
@@ -301,8 +303,10 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
301303
if ($numMatches > 0 && -1 !== $ofs) {
302304
$ret = $matches[$ofs];
303305
// Echo out remaining chars for current match
304-
$output->write(substr($ret, $i));
305-
$i = \strlen($ret);
306+
$remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
307+
$output->write($remainingCharacters);
308+
$fullChoice .= $remainingCharacters;
309+
$i = \strlen($fullChoice);
306310
}
307311

308312
if ("\n" === $c) {
@@ -321,14 +325,21 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
321325

322326
$output->write($c);
323327
$ret .= $c;
328+
$fullChoice .= $c;
324329
++$i;
325330

331+
$tempRet = $ret;
332+
333+
if ($question instanceof ChoiceQuestion && $question->isMultiselect()) {
334+
$tempRet = $this->mostRecentlyEnteredValue($fullChoice);
335+
}
336+
326337
$numMatches = 0;
327338
$ofs = 0;
328339

329340
foreach ($autocomplete as $value) {
330341
// If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
331-
if (0 === strpos($value, $ret)) {
342+
if (0 === strpos($value, $tempRet)) {
332343
$matches[$numMatches++] = $value;
333344
}
334345
}
@@ -340,8 +351,9 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
340351
if ($numMatches > 0 && -1 !== $ofs) {
341352
// Save cursor position
342353
$output->write("\0337");
343-
// Write highlighted text
344-
$output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $i)).'</hl>');
354+
// Write highlighted text, complete the partially entered response
355+
$charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
356+
$output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>');
345357
// Restore cursor position
346358
$output->write("\0338");
347359
}
@@ -350,7 +362,24 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
350362
// Reset stty so it behaves normally again
351363
shell_exec(sprintf('stty %s', $sttyMode));
352364

353-
return $ret;
365+
return $fullChoice;
366+
}
367+
368+
private function mostRecentlyEnteredValue($entered)
369+
{
370+
$tempEntered = $entered;
371+
372+
// Determine the most recent value that the user entered
373+
if (false !== strpos($entered, ',')) {
374+
$choices = explode(',', $entered);
375+
$lastChoice = trim($choices[\count($choices) - 1]);
376+
377+
if (\strlen($lastChoice) > 0) {
378+
$tempEntered = $lastChoice;
379+
}
380+
}
381+
382+
return $tempEntered;
354383
}
355384

356385
/**

‎src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php
+31Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,37 @@ public function testTraversableAutocomplete()
10181018
$this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
10191019
}
10201020

1021+
public function testTraversableMultiselectAutocomplete()
1022+
{
1023+
// <NEWLINE>
1024+
// F<TAB><NEWLINE>
1025+
// A<3x UP ARROW><TAB>,F<TAB><NEWLINE>
1026+
// F00<BACKSPACE><BACKSPACE>o<TAB>,A<DOWN ARROW>,<SPACE>SecurityBundle<NEWLINE>
1027+
// Acme<TAB>,<SPACE>As<TAB><29x BACKSPACE>S<TAB><NEWLINE>
1028+
// Ac<TAB>,As<TAB><3x BACKSPACE>d<TAB><NEWLINE>
1029+
$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");
1030+
1031+
$dialog = new QuestionHelper();
1032+
$helperSet = new HelperSet([new FormatterHelper()]);
1033+
$dialog->setHelperSet($helperSet);
1034+
1035+
$question = new ChoiceQuestion(
1036+
'Please select a bundle (defaults to AcmeDemoBundle and AsseticBundle)',
1037+
['AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'],
1038+
'0,1'
1039+
);
1040+
1041+
// This tests that autocomplete works for all multiselect choices entered by the user
1042+
$question->setMultiselect(true);
1043+
1044+
$this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
1045+
$this->assertEquals(['FooBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
1046+
$this->assertEquals(['AsseticBundle', 'FooBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
1047+
$this->assertEquals(['FooBundle', 'AsseticBundle', 'SecurityBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
1048+
$this->assertEquals(['SecurityBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
1049+
$this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
1050+
}
1051+
10211052
protected function getInputStream($input)
10221053
{
10231054
$stream = fopen('php://memory', 'r+', false);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.