diff --git a/Formatter/OutputFormatter.php b/Formatter/OutputFormatter.php index 9cb631048..3e4897c33 100644 --- a/Formatter/OutputFormatter.php +++ b/Formatter/OutputFormatter.php @@ -13,6 +13,8 @@ use Symfony\Component\Console\Exception\InvalidArgumentException; +use function Symfony\Component\String\b; + /** * Formatter class for console output. * @@ -241,7 +243,7 @@ private function applyCurrentStyle(string $text, string $current, int $width, in } preg_match('~(\\n)$~', $text, $matches); - $text = $prefix.preg_replace('~([^\\n]{'.$width.'})\\ *~', "\$1\n", $text); + $text = $prefix.$this->addLineBreaks($text, $width); $text = rtrim($text, "\n").($matches[1] ?? ''); if (!$currentLineLength && '' !== $current && !str_ends_with($current, "\n")) { @@ -265,4 +267,11 @@ private function applyCurrentStyle(string $text, string $current, int $width, in return implode("\n", $lines); } + + private function addLineBreaks(string $text, int $width): string + { + $encoding = mb_detect_encoding($text, null, true) ?: 'UTF-8'; + + return b($text)->toCodePointString($encoding)->wordwrap($width, "\n", true)->toByteString($encoding); + } } diff --git a/Tests/Formatter/OutputFormatterTest.php b/Tests/Formatter/OutputFormatterTest.php index 20669e6d3..610522a7e 100644 --- a/Tests/Formatter/OutputFormatterTest.php +++ b/Tests/Formatter/OutputFormatterTest.php @@ -358,10 +358,10 @@ public function testFormatAndWrap() $formatter = new OutputFormatter(true); $this->assertSame("fo\no\e[37;41mb\e[39;49m\n\e[37;41mar\e[39;49m\nba\nz", $formatter->formatAndWrap('foobar baz', 2)); - $this->assertSame("pr\ne \e[37;41m\e[39;49m\n\e[37;41mfo\e[39;49m\n\e[37;41mo \e[39;49m\n\e[37;41mba\e[39;49m\n\e[37;41mr \e[39;49m\n\e[37;41mba\e[39;49m\n\e[37;41mz\e[39;49m \npo\nst", $formatter->formatAndWrap('pre foo bar baz post', 2)); + $this->assertSame("pr\ne \e[37;41m\e[39;49m\n\e[37;41mfo\e[39;49m\n\e[37;41mo\e[39;49m\n\e[37;41mba\e[39;49m\n\e[37;41mr\e[39;49m\n\e[37;41mba\e[39;49m\n\e[37;41mz\e[39;49m \npo\nst", $formatter->formatAndWrap('pre foo bar baz post', 2)); $this->assertSame("pre\e[37;41m\e[39;49m\n\e[37;41mfoo\e[39;49m\n\e[37;41mbar\e[39;49m\n\e[37;41mbaz\e[39;49m\npos\nt", $formatter->formatAndWrap('pre foo bar baz post', 3)); - $this->assertSame("pre \e[37;41m\e[39;49m\n\e[37;41mfoo \e[39;49m\n\e[37;41mbar \e[39;49m\n\e[37;41mbaz\e[39;49m \npost", $formatter->formatAndWrap('pre foo bar baz post', 4)); - $this->assertSame("pre \e[37;41mf\e[39;49m\n\e[37;41moo ba\e[39;49m\n\e[37;41mr baz\e[39;49m\npost", $formatter->formatAndWrap('pre foo bar baz post', 5)); + $this->assertSame("pre \e[37;41m\e[39;49m\n\e[37;41mfoo\e[39;49m\n\e[37;41mbar\e[39;49m\n\e[37;41mbaz\e[39;49m \npost", $formatter->formatAndWrap('pre foo bar baz post', 4)); + $this->assertSame("pre \e[37;41mf\e[39;49m\n\e[37;41moo\e[39;49m\n\e[37;41mbar\e[39;49m\n\e[37;41mbaz\e[39;49m p\nost", $formatter->formatAndWrap('pre foo bar baz post', 5)); $this->assertSame("Lore\nm \e[37;41mip\e[39;49m\n\e[37;41msum\e[39;49m \ndolo\nr \e[32msi\e[39m\n\e[32mt\e[39m am\net", $formatter->formatAndWrap('Lorem ipsum dolor sit amet', 4)); $this->assertSame("Lorem \e[37;41mip\e[39;49m\n\e[37;41msum\e[39;49m dolo\nr \e[32msit\e[39m am\net", $formatter->formatAndWrap('Lorem ipsum dolor sit amet', 8)); $this->assertSame("Lorem \e[37;41mipsum\e[39;49m dolor \e[32m\e[39m\n\e[32msit\e[39m, \e[37;41mamet\e[39;49m et \e[32mlauda\e[39m\n\e[32mntium\e[39m architecto", $formatter->formatAndWrap('Lorem ipsum dolor sit, amet et laudantium architecto', 18)); @@ -369,10 +369,12 @@ public function testFormatAndWrap() $formatter = new OutputFormatter(); $this->assertSame("fo\nob\nar\nba\nz", $formatter->formatAndWrap('foobar baz', 2)); - $this->assertSame("pr\ne \nfo\no \nba\nr \nba\nz \npo\nst", $formatter->formatAndWrap('pre foo bar baz post', 2)); + $this->assertSame("pr\ne \nfo\no\nba\nr\nba\nz \npo\nst", $formatter->formatAndWrap('pre foo bar baz post', 2)); $this->assertSame("pre\nfoo\nbar\nbaz\npos\nt", $formatter->formatAndWrap('pre foo bar baz post', 3)); - $this->assertSame("pre \nfoo \nbar \nbaz \npost", $formatter->formatAndWrap('pre foo bar baz post', 4)); - $this->assertSame("pre f\noo ba\nr baz\npost", $formatter->formatAndWrap('pre foo bar baz post', 5)); + $this->assertSame("pre \nfoo\nbar\nbaz \npost", $formatter->formatAndWrap('pre foo bar baz post', 4)); + $this->assertSame("pre f\noo\nbar\nbaz p\nost", $formatter->formatAndWrap('pre foo bar baz post', 5)); + $this->assertSame("Â rèälly\nlöng tîtlè\nthät cöüld\nnèêd\nmúltîplê\nlínès", $formatter->formatAndWrap('Â rèälly löng tîtlè thät cöüld nèêd múltîplê línès', 10)); + $this->assertSame("Â rèälly\nlöng tîtlè\nthät cöüld\nnèêd\nmúltîplê\n línès", $formatter->formatAndWrap("Â rèälly löng tîtlè thät cöüld nèêd múltîplê\n línès", 10)); $this->assertSame('', $formatter->formatAndWrap(null, 5)); } } diff --git a/Tests/Helper/TableTest.php b/Tests/Helper/TableTest.php index 5908c5b97..9cd5dcc5f 100644 --- a/Tests/Helper/TableTest.php +++ b/Tests/Helper/TableTest.php @@ -118,30 +118,30 @@ public static function renderProvider() ['ISBN', 'Title', 'Author'], $books, 'compact', -<<<'TABLE' -ISBN Title Author -99921-58-10-7 Divine Comedy Dante Alighieri -9971-5-0210-0 A Tale of Two Cities Charles Dickens -960-425-059-0 The Lord of the Rings J. R. R. Tolkien -80-902734-1-6 And Then There Were None Agatha Christie - -TABLE + implode("\n", [ + 'ISBN Title Author ', + '99921-58-10-7 Divine Comedy Dante Alighieri ', + '9971-5-0210-0 A Tale of Two Cities Charles Dickens ', + '960-425-059-0 The Lord of the Rings J. R. R. Tolkien ', + '80-902734-1-6 And Then There Were None Agatha Christie ', + '', + ]), ], [ ['ISBN', 'Title', 'Author'], $books, 'borderless', -<<<'TABLE' - =============== ========================== ================== - ISBN Title Author - =============== ========================== ================== - 99921-58-10-7 Divine Comedy Dante Alighieri - 9971-5-0210-0 A Tale of Two Cities Charles Dickens - 960-425-059-0 The Lord of the Rings J. R. R. Tolkien - 80-902734-1-6 And Then There Were None Agatha Christie - =============== ========================== ================== - -TABLE + implode("\n", [ + ' =============== ========================== ================== ', + ' ISBN Title Author ', + ' =============== ========================== ================== ', + ' 99921-58-10-7 Divine Comedy Dante Alighieri ', + ' 9971-5-0210-0 A Tale of Two Cities Charles Dickens ', + ' 960-425-059-0 The Lord of the Rings J. R. R. Tolkien ', + ' 80-902734-1-6 And Then There Were None Agatha Christie ', + ' =============== ========================== ================== ', + '', + ]), ], [ ['ISBN', 'Title', 'Author'], @@ -1378,12 +1378,14 @@ public function testColumnMaxWidths() $expected = <<