Skip to content

Commit c381423

Browse files
committed
Merge branch '3.4' into 4.2
* 3.4: [Console] Fix auto-complete for ChoiceQuestion (multi-select answers) Translated form, security, validators resources into Belarusian (be) [WebProfilerBundle] Don't filter submitted IP values bumped Symfony version to 3.4.28 updated VERSION for 3.4.27 update CONTRIBUTORS for 3.4.27 updated CHANGELOG for 3.4.27
2 parents e2840bb + 606e4d9 commit c381423

File tree

2 files changed

+66
-6
lines changed

2 files changed

+66
-6
lines changed

Helper/QuestionHelper.php

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ protected function writeError(OutputInterface $output, \Exception $error)
200200
*/
201201
private function autocomplete(OutputInterface $output, Question $question, $inputStream, array $autocomplete): string
202202
{
203+
$fullChoice = '';
203204
$ret = '';
204205

205206
$i = 0;
@@ -226,6 +227,7 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
226227
} elseif ("\177" === $c) { // Backspace Character
227228
if (0 === $numMatches && 0 !== $i) {
228229
--$i;
230+
$fullChoice = substr($fullChoice, 0, -1);
229231
// Move cursor backwards
230232
$output->write("\033[1D");
231233
}
@@ -262,8 +264,10 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
262264
if ($numMatches > 0 && -1 !== $ofs) {
263265
$ret = $matches[$ofs];
264266
// Echo out remaining chars for current match
265-
$output->write(substr($ret, $i));
266-
$i = \strlen($ret);
267+
$remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
268+
$output->write($remainingCharacters);
269+
$fullChoice .= $remainingCharacters;
270+
$i = \strlen($fullChoice);
267271
}
268272

269273
if ("\n" === $c) {
@@ -282,14 +286,21 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
282286

283287
$output->write($c);
284288
$ret .= $c;
289+
$fullChoice .= $c;
285290
++$i;
286291

292+
$tempRet = $ret;
293+
294+
if ($question instanceof ChoiceQuestion && $question->isMultiselect()) {
295+
$tempRet = $this->mostRecentlyEnteredValue($fullChoice);
296+
}
297+
287298
$numMatches = 0;
288299
$ofs = 0;
289300

290301
foreach ($autocomplete as $value) {
291302
// If typed characters match the beginning chunk of value (e.g. [AcmeDe]moBundle)
292-
if (0 === strpos($value, $ret)) {
303+
if (0 === strpos($value, $tempRet)) {
293304
$matches[$numMatches++] = $value;
294305
}
295306
}
@@ -301,8 +312,9 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
301312
if ($numMatches > 0 && -1 !== $ofs) {
302313
// Save cursor position
303314
$output->write("\0337");
304-
// Write highlighted text
305-
$output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $i)).'</hl>');
315+
// Write highlighted text, complete the partially entered response
316+
$charactersEntered = \strlen(trim($this->mostRecentlyEnteredValue($fullChoice)));
317+
$output->write('<hl>'.OutputFormatter::escapeTrailingBackslash(substr($matches[$ofs], $charactersEntered)).'</hl>');
306318
// Restore cursor position
307319
$output->write("\0338");
308320
}
@@ -311,7 +323,24 @@ private function autocomplete(OutputInterface $output, Question $question, $inpu
311323
// Reset stty so it behaves normally again
312324
shell_exec(sprintf('stty %s', $sttyMode));
313325

314-
return $ret;
326+
return $fullChoice;
327+
}
328+
329+
private function mostRecentlyEnteredValue($entered)
330+
{
331+
$tempEntered = $entered;
332+
333+
// Determine the most recent value that the user entered
334+
if (false !== strpos($entered, ',')) {
335+
$choices = explode(',', $entered);
336+
$lastChoice = trim($choices[\count($choices) - 1]);
337+
338+
if (\strlen($lastChoice) > 0) {
339+
$tempEntered = $lastChoice;
340+
}
341+
}
342+
343+
return $tempEntered;
315344
}
316345

317346
/**

Tests/Helper/QuestionHelperTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,37 @@ public function testTraversableAutocomplete()
667667
$this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
668668
}
669669

670+
public function testTraversableMultiselectAutocomplete()
671+
{
672+
// <NEWLINE>
673+
// F<TAB><NEWLINE>
674+
// A<3x UP ARROW><TAB>,F<TAB><NEWLINE>
675+
// F00<BACKSPACE><BACKSPACE>o<TAB>,A<DOWN ARROW>,<SPACE>SecurityBundle<NEWLINE>
676+
// Acme<TAB>,<SPACE>As<TAB><29x BACKSPACE>S<TAB><NEWLINE>
677+
// Ac<TAB>,As<TAB><3x BACKSPACE>d<TAB><NEWLINE>
678+
$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");
679+
680+
$dialog = new QuestionHelper();
681+
$helperSet = new HelperSet([new FormatterHelper()]);
682+
$dialog->setHelperSet($helperSet);
683+
684+
$question = new ChoiceQuestion(
685+
'Please select a bundle (defaults to AcmeDemoBundle and AsseticBundle)',
686+
['AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'],
687+
'0,1'
688+
);
689+
690+
// This tests that autocomplete works for all multiselect choices entered by the user
691+
$question->setMultiselect(true);
692+
693+
$this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
694+
$this->assertEquals(['FooBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
695+
$this->assertEquals(['AsseticBundle', 'FooBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
696+
$this->assertEquals(['FooBundle', 'AsseticBundle', 'SecurityBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
697+
$this->assertEquals(['SecurityBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
698+
$this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
699+
}
700+
670701
protected function getInputStream($input)
671702
{
672703
$stream = fopen('php://memory', 'r+', false);

0 commit comments

Comments
 (0)