bug #40524 [Console] fix emojis messing up the line width (MarionLeHerisson)

This PR was submitted for the 5.x branch but it was merged into the 5.2 branch instead.

Discussion
----------

[Console] fix emojis messing up the line width

| Q             | A
| ------------- | ---
| Branch?       | 5.2
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | Fix https://github.com/symfony/symfony/issues/37904
| License       | MIT

Description
========

The emojis, because they take as much space as two characters, would cause the console to display too many spaces to complete a line, which made it uneven, as described in the issue.

The fix uses the `width` function instead of `strlen`. To answer @ogizanagi's comment, yes it does work with "composed" emojis.

Before :

![image](https://user-images.githubusercontent.com/11477247/111832081-9d72b100-88f0-11eb-8eda-65ee480c898d.png)

After :

![image](https://user-images.githubusercontent.com/11477247/111832103-a6638280-88f0-11eb-802e-838d97f61c81.png)

Other changes
==========

Removed two unused lines of code, the value of `$messageLineLength` was never used.

Note
====
I'd like to add some tests, but I don't know how since I think this depends on console client width ?

Thanks for your reviews 🙏

Commits
-------

36b36dcecc [Command] fix emojis messing up the line width
This commit is contained in:
Nicolas Grekas 2021-03-23 13:19:25 +01:00
commit 1044c0bd4b
5 changed files with 39 additions and 10 deletions

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
use Symfony\Component\String\UnicodeString;
/**
* Helper is the base class for all helper classes.
@ -45,7 +46,11 @@ abstract class Helper implements HelperInterface
*/
public static function strlen(?string $string)
{
$string = (string) $string;
$string ?? $string = '';
if (preg_match('//u', $string)) {
return (new UnicodeString($string))->width(false);
}
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return \strlen($string);
@ -59,9 +64,9 @@ abstract class Helper implements HelperInterface
*
* @return string The string subset
*/
public static function substr(string $string, int $from, int $length = null)
public static function substr(?string $string, int $from, int $length = null)
{
$string = (string) $string;
$string ?? $string = '';
if (false === $encoding = mb_detect_encoding($string, null, true)) {
return substr($string, $from, $length);
@ -116,17 +121,23 @@ abstract class Helper implements HelperInterface
return sprintf('%d B', $memory);
}
public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, $string)
public static function strlenWithoutDecoration(OutputFormatterInterface $formatter, ?string $string)
{
return self::strlen(self::removeDecoration($formatter, $string));
$string = self::removeDecoration($formatter, $string);
if (preg_match('//u', $string)) {
return (new UnicodeString($string))->width(true);
}
return self::strlen($string);
}
public static function removeDecoration(OutputFormatterInterface $formatter, $string)
public static function removeDecoration(OutputFormatterInterface $formatter, ?string $string)
{
$isDecorated = $formatter->isDecorated();
$formatter->setDecorated(false);
// remove <...> formatting
$string = $formatter->format($string);
$string = $formatter->format($string ?? '');
// remove already formatted characters
$string = preg_replace("/\033\[[^m]*m/", '', $string);
$formatter->setDecorated($isDecorated);

View File

@ -311,7 +311,7 @@ class QuestionHelper extends Helper
$remainingCharacters = substr($ret, \strlen(trim($this->mostRecentlyEnteredValue($fullChoice))));
$output->write($remainingCharacters);
$fullChoice .= $remainingCharacters;
$i = self::strlen($fullChoice);
$i = (false === $encoding = mb_detect_encoding($fullChoice, null, true)) ? \strlen($fullChoice) : mb_strlen($fullChoice, $encoding);
$matches = array_filter(
$autocomplete($ret),

View File

@ -501,8 +501,6 @@ class SymfonyStyle extends OutputStyle
}
$line = $prefix.$line;
$decorationLength = Helper::strlen($line) - Helper::strlenWithoutDecoration($this->getFormatter(), $line);
$messageLineLength = min($this->lineLength - $prefixLength - $indentLength + $decorationLength, $this->lineLength);
$line .= str_repeat(' ', max($this->lineLength - Helper::strlenWithoutDecoration($this->getFormatter(), $line), 0));
if ($style) {

View File

@ -0,0 +1,13 @@
<?php
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
//Ensure texts with emojis don't make longer lines than expected
return function (InputInterface $input, OutputInterface $output) {
$output = new SymfonyStyle($input, $output);
$output->success('Lorem ipsum dolor sit amet');
$output->success('Lorem ipsum dolor sit amet with one emoji 🎉');
$output->success('Lorem ipsum dolor sit amet with so many of them 👩‍🌾👩‍🌾👩‍🌾👩‍🌾👩‍🌾');
};

View File

@ -0,0 +1,7 @@
[OK] Lorem ipsum dolor sit amet
[OK] Lorem ipsum dolor sit amet with one emoji 🎉
[OK] Lorem ipsum dolor sit amet with so many of them 👩‍🌾👩‍🌾👩‍🌾👩‍🌾👩‍🌾