diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php index 594a291602..f95023be97 100644 --- a/src/Symfony/Component/Console/Helper/QuestionHelper.php +++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php @@ -22,6 +22,7 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Question\Question; use Symfony\Component\Console\Terminal; +use function Symfony\Component\String\s; /** * The QuestionHelper class provides helpers to interact with the user. @@ -242,9 +243,10 @@ class QuestionHelper extends Helper } elseif ("\177" === $c) { // Backspace Character if (0 === $numMatches && 0 !== $i) { --$i; - $fullChoice = self::substr($fullChoice, 0, $i); // Move cursor backwards - $output->write("\033[1D"); + $output->write(sprintf("\033[%dD", s($fullChoice)->slice(-1)->width(false))); + + $fullChoice = self::substr($fullChoice, 0, $i); } if (0 === $i) { diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index fcba3b3b2f..19d0c9d5d7 100644 --- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -797,6 +797,25 @@ class QuestionHelperTest extends AbstractQuestionHelperTest $this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); } + public function testAutocompleteMoveCursorBackwards() + { + // F + $inputStream = $this->getInputStream("F\t\177\177\177"); + + $dialog = new QuestionHelper(); + $helperSet = new HelperSet([new FormatterHelper()]); + $dialog->setHelperSet($helperSet); + + $question = new Question('Question?', 'F⭐Y'); + $question->setAutocompleterValues(['F⭐Y']); + + $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question); + + $stream = $output->getStream(); + rewind($stream); + $this->assertStringEndsWith("\033[1D\033[K\033[2D\033[K\033[1D\033[K", stream_get_contents($stream)); + } + protected function getInputStream($input) { $stream = fopen('php://memory', 'r+', false); diff --git a/src/Symfony/Component/Console/composer.json b/src/Symfony/Component/Console/composer.json index ee1d76f6c2..b92d62554d 100644 --- a/src/Symfony/Component/Console/composer.json +++ b/src/Symfony/Component/Console/composer.json @@ -19,7 +19,8 @@ "php": "^7.2.5", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php73": "^1.8", - "symfony/service-contracts": "^1.1|^2" + "symfony/service-contracts": "^1.1|^2", + "symfony/string": "^5.1" }, "require-dev": { "symfony/config": "^4.4|^5.0",