[Console]Improve formatter for double-width character
This commit is contained in:
parent
c2e134fbb9
commit
a52f41d414
@ -99,7 +99,7 @@ class Application
|
|||||||
* @param InputInterface $input An Input instance
|
* @param InputInterface $input An Input instance
|
||||||
* @param OutputInterface $output An Output instance
|
* @param OutputInterface $output An Output instance
|
||||||
*
|
*
|
||||||
* @return integer 0 if everything went fine, or an error code
|
* @return int 0 if everything went fine, or an error code
|
||||||
*
|
*
|
||||||
* @throws \Exception When doRun returns Exception
|
* @throws \Exception When doRun returns Exception
|
||||||
*
|
*
|
||||||
@ -159,7 +159,7 @@ class Application
|
|||||||
* @param InputInterface $input An Input instance
|
* @param InputInterface $input An Input instance
|
||||||
* @param OutputInterface $output An Output instance
|
* @param OutputInterface $output An Output instance
|
||||||
*
|
*
|
||||||
* @return integer 0 if everything went fine, or an error code
|
* @return int 0 if everything went fine, or an error code
|
||||||
*/
|
*/
|
||||||
public function doRun(InputInterface $input, OutputInterface $output)
|
public function doRun(InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
@ -449,7 +449,7 @@ class Application
|
|||||||
*
|
*
|
||||||
* @param string $name The command name or alias
|
* @param string $name The command name or alias
|
||||||
*
|
*
|
||||||
* @return Boolean true if the command exists, false otherwise
|
* @return bool true if the command exists, false otherwise
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
@ -713,29 +713,17 @@ class Application
|
|||||||
*/
|
*/
|
||||||
public function renderException($e, $output)
|
public function renderException($e, $output)
|
||||||
{
|
{
|
||||||
$strlen = function ($string) {
|
|
||||||
if (!function_exists('mb_strlen')) {
|
|
||||||
return strlen($string);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (false === $encoding = mb_detect_encoding($string)) {
|
|
||||||
return strlen($string);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mb_strlen($string, $encoding);
|
|
||||||
};
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
$title = sprintf(' [%s] ', get_class($e));
|
$title = sprintf(' [%s] ', get_class($e));
|
||||||
$len = $strlen($title);
|
$len = $this->stringWidth($title);
|
||||||
// HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327
|
// HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327
|
||||||
$width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : (defined('HHVM_VERSION') ? 1 << 31 : PHP_INT_MAX);
|
$width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : (defined('HHVM_VERSION') ? 1 << 31 : PHP_INT_MAX);
|
||||||
$formatter = $output->getFormatter();
|
$formatter = $output->getFormatter();
|
||||||
$lines = array();
|
$lines = array();
|
||||||
foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
|
foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
|
||||||
foreach (str_split($line, $width - 4) as $line) {
|
foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
|
||||||
// pre-format lines to get the right string length
|
// pre-format lines to get the right string length
|
||||||
$lineLength = $strlen(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4;
|
$lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4;
|
||||||
$lines[] = array($line, $lineLength);
|
$lines[] = array($line, $lineLength);
|
||||||
|
|
||||||
$len = max($lineLength, $len);
|
$len = max($lineLength, $len);
|
||||||
@ -744,7 +732,7 @@ class Application
|
|||||||
|
|
||||||
$messages = array('', '');
|
$messages = array('', '');
|
||||||
$messages[] = $emptyLine = $formatter->format(sprintf('<error>%s</error>', str_repeat(' ', $len)));
|
$messages[] = $emptyLine = $formatter->format(sprintf('<error>%s</error>', str_repeat(' ', $len)));
|
||||||
$messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $strlen($title)))));
|
$messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))));
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
$messages[] = $formatter->format(sprintf('<error> %s %s</error>', $line[0], str_repeat(' ', $len - $line[1])));
|
$messages[] = $formatter->format(sprintf('<error> %s %s</error>', $line[0], str_repeat(' ', $len - $line[1])));
|
||||||
}
|
}
|
||||||
@ -890,7 +878,7 @@ class Application
|
|||||||
* @param InputInterface $input An Input instance
|
* @param InputInterface $input An Input instance
|
||||||
* @param OutputInterface $output An Output instance
|
* @param OutputInterface $output An Output instance
|
||||||
*
|
*
|
||||||
* @return integer 0 if everything went fine, or an error code
|
* @return int 0 if everything went fine, or an error code
|
||||||
*/
|
*/
|
||||||
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
|
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
@ -1125,4 +1113,53 @@ class Application
|
|||||||
|
|
||||||
return array_keys($alternatives);
|
return array_keys($alternatives);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function stringWidth($string)
|
||||||
|
{
|
||||||
|
if (!function_exists('mb_strwidth')) {
|
||||||
|
return strlen($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $encoding = mb_detect_encoding($string)) {
|
||||||
|
return strlen($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mb_strwidth($string, $encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function splitStringByWidth($string, $width)
|
||||||
|
{
|
||||||
|
// str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly.
|
||||||
|
// additionally, array_slice() is not enough as some character has doubled width.
|
||||||
|
// we need a function to split string not by character count but by string width
|
||||||
|
|
||||||
|
if (!function_exists('mb_strwidth')) {
|
||||||
|
return str_split($string, $width);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $encoding = mb_detect_encoding($string)) {
|
||||||
|
return str_split($string, $width);
|
||||||
|
}
|
||||||
|
|
||||||
|
$utf8String = mb_convert_encoding($string, 'utf8', $encoding);
|
||||||
|
$lines = array();
|
||||||
|
$line = '';
|
||||||
|
foreach (preg_split('//u', $utf8String) as $char) {
|
||||||
|
// test if $char could be appended to current line
|
||||||
|
if (mb_strwidth($line.$char) <= $width) {
|
||||||
|
$line .= $char;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// if not, push current line to array and make new line
|
||||||
|
$lines[] = str_pad($line, $width);
|
||||||
|
$line = $char;
|
||||||
|
}
|
||||||
|
if (strlen($line)) {
|
||||||
|
$lines[] = count($lines) ? str_pad($line, $width) : $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
mb_convert_variables($encoding, 'utf8', $lines);
|
||||||
|
|
||||||
|
return $lines;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,11 +45,11 @@ abstract class Helper implements HelperInterface
|
|||||||
*
|
*
|
||||||
* @param string $string The string to check its length
|
* @param string $string The string to check its length
|
||||||
*
|
*
|
||||||
* @return integer The length of the string
|
* @return int The length of the string
|
||||||
*/
|
*/
|
||||||
protected function strlen($string)
|
protected function strlen($string)
|
||||||
{
|
{
|
||||||
if (!function_exists('mb_strlen')) {
|
if (!function_exists('mb_strwidth')) {
|
||||||
return strlen($string);
|
return strlen($string);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +57,6 @@ abstract class Helper implements HelperInterface
|
|||||||
return strlen($string);
|
return strlen($string);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mb_strlen($string, $encoding);
|
return mb_strwidth($string, $encoding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -469,6 +469,33 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal');
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception4.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRenderExceptionWithDoubleWidthCharacters()
|
||||||
|
{
|
||||||
|
$application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
|
||||||
|
$application->setAutoExit(false);
|
||||||
|
$application->expects($this->any())
|
||||||
|
->method('getTerminalWidth')
|
||||||
|
->will($this->returnValue(120));
|
||||||
|
$application->register('foo')->setCode(function () {throw new \Exception('エラーメッセージ');});
|
||||||
|
$tester = new ApplicationTester($application);
|
||||||
|
|
||||||
|
$tester->run(array('command' => 'foo'), array('decorated' => false));
|
||||||
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1.txt', $tester->getDisplay(true), '->renderException() renderes a pretty exceptions with previous exceptions');
|
||||||
|
|
||||||
|
$tester->run(array('command' => 'foo'), array('decorated' => true));
|
||||||
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth1decorated.txt', $tester->getDisplay(true), '->renderException() renderes a pretty exceptions with previous exceptions');
|
||||||
|
|
||||||
|
$application = $this->getMock('Symfony\Component\Console\Application', array('getTerminalWidth'));
|
||||||
|
$application->setAutoExit(false);
|
||||||
|
$application->expects($this->any())
|
||||||
|
->method('getTerminalWidth')
|
||||||
|
->will($this->returnValue(32));
|
||||||
|
$application->register('foo')->setCode(function () {throw new \Exception('コマンドの実行中にエラーが発生しました。');});
|
||||||
|
$tester = new ApplicationTester($application);
|
||||||
|
$tester->run(array('command' => 'foo'), array('decorated' => false));
|
||||||
|
$this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception_doublewidth2.txt', $tester->getDisplay(true), '->renderException() wraps messages when they are bigger than the terminal');
|
||||||
|
}
|
||||||
|
|
||||||
public function testRun()
|
public function testRun()
|
||||||
{
|
{
|
||||||
$application = new Application();
|
$application = new Application();
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Exception]
|
||||||
|
エラーメッセージ
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
foo
|
||||||
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
[37;41m [0m
|
||||||
|
[37;41m [Exception] [0m
|
||||||
|
[37;41m エラーメッセージ [0m
|
||||||
|
[37;41m [0m
|
||||||
|
|
||||||
|
|
||||||
|
[32mfoo[0m
|
||||||
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Exception]
|
||||||
|
コマンドの実行中にエラーが
|
||||||
|
発生しました。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
foo
|
||||||
|
|
||||||
|
|
@ -69,6 +69,21 @@ class FormatterHelperTest extends \PHPUnit_Framework_TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testFormatBlockWithDoubleWidthDiacriticLetters()
|
||||||
|
{
|
||||||
|
if (!extension_loaded('mbstring')) {
|
||||||
|
$this->markTestSkipped('This test requires mbstring to work.');
|
||||||
|
}
|
||||||
|
$formatter = new FormatterHelper();
|
||||||
|
$this->assertEquals(
|
||||||
|
'<error> </error>'."\n" .
|
||||||
|
'<error> 表示するテキスト </error>'."\n" .
|
||||||
|
'<error> </error>',
|
||||||
|
$formatter->formatBlock('表示するテキスト', 'error', true),
|
||||||
|
'::formatBlock() formats a message in a block'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function testFormatBlockLGEscaping()
|
public function testFormatBlockLGEscaping()
|
||||||
{
|
{
|
||||||
$formatter = new FormatterHelper();
|
$formatter = new FormatterHelper();
|
||||||
|
Reference in New Issue
Block a user