From 31d5fe58fe78dd33e45905ca620961574902f33b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Simon?= Date: Fri, 16 Mar 2012 07:27:13 +0100 Subject: [PATCH 1/9] [Console] Fixed output formatter docblock. --- src/Symfony/Component/Console/Formatter/OutputFormatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/src/Symfony/Component/Console/Formatter/OutputFormatter.php index 8d60c74f8d..f8967080e4 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php @@ -166,7 +166,7 @@ class OutputFormatter implements OutputFormatterInterface * * @param string $string * - * @return Symfony\Component\Console\Format\FormatterStyle|Boolean false if string is not format string + * @return \Symfony\Component\Console\Formatter\OutputFormatterStyle|Boolean false if string is not format string */ private function createStyleFromString($string) { From ad334b68a3cf6b3a2f635433a2a6cf02aa155c1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Simon?= Date: Fri, 16 Mar 2012 07:29:46 +0100 Subject: [PATCH 2/9] [Console] Fixed empty style appliance. --- .../Component/Console/Formatter/OutputFormatterStyle.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php index dc88f2a8c5..e48cca4c67 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php @@ -212,6 +212,9 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface if (count($this->options)) { $codes = array_merge($codes, $this->options); } + if (0 === count($codes)) { + $codes = array(0); + } return sprintf("\033[%sm%s\033[0m", implode(';', $codes), $text); } From 48e6b492015f144177c882a4955487f186d1c298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Simon?= Date: Fri, 16 Mar 2012 09:01:11 +0100 Subject: [PATCH 3/9] [Console] Updated formatter test to match styles bug fix. --- .../Tests/Component/Console/Formatter/OutputFormatterTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Symfony/Tests/Component/Console/Formatter/OutputFormatterTest.php b/tests/Symfony/Tests/Component/Console/Formatter/OutputFormatterTest.php index 03dabf9f17..20d045d97d 100644 --- a/tests/Symfony/Tests/Component/Console/Formatter/OutputFormatterTest.php +++ b/tests/Symfony/Tests/Component/Console/Formatter/OutputFormatterTest.php @@ -43,7 +43,7 @@ class FormatterStyleTest extends \PHPUnit_Framework_TestCase $formatter = new OutputFormatter(true); $this->assertEquals( - "\033[37;41msome \033[32msome info\033[0m error\033[0m", $formatter->format('some some info error') + "\033[37;41msome \033[32;41msome info\033[37;41m error\033[0m", $formatter->format('some some info error') ); } From 93ffe5488695837d5b38c6827bd37777e540046b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Simon?= Date: Fri, 16 Mar 2012 09:09:42 +0100 Subject: [PATCH 4/9] [Console] Added getters to output formatter style (and its interface). --- .../Formatter/OutputFormatterStyle.php | 61 ++++++++++++++++++- .../OutputFormatterStyleInterface.php | 28 +++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php index e48cca4c67..46e6af6edc 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php @@ -102,6 +102,22 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface $this->foreground = static::$availableForegroundColors[$color]; } + /** + * Gets style foreground color. + * + * @return string|null + */ + public function getForeground() + { + if (null === $this->foreground) { + return null; + } + + $names = array_flip(static::$availableForegroundColors); + + return $names[$this->foreground]; + } + /** * Sets style background color. * @@ -130,6 +146,22 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface $this->background = static::$availableBackgroundColors[$color]; } + /** + * Gets style background color. + * + * @return string|null + */ + public function getBackground() + { + if (null === $this->background) { + return null; + } + + $names = array_flip(static::$availableBackgroundColors); + + return $names[$this->background]; + } + /** * Sets some specific style option. * @@ -192,6 +224,23 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface } } + /** + * Gets specific style options. + * + * @return array + */ + public function getOptions() + { + $names = array_flip(static::$availableOptions); + $options = array(); + + foreach ($this->options as $code) { + $options[] = $names[$code]; + } + + return $options; + } + /** * Applies the style to a given text. * @@ -200,6 +249,16 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface * @return string */ public function apply($text) + { + return sprintf("%s%s\033[0m", $this->getTerminalSequence(), $text); + } + + /** + * Gets terminal colorization sequence. + * + * @return string + */ + public function getTerminalSequence() { $codes = array(); @@ -216,6 +275,6 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface $codes = array(0); } - return sprintf("\033[%sm%s\033[0m", implode(';', $codes), $text); + return sprintf("\033[%sm", implode(';', $codes)); } } diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php b/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php index 212cb86ff0..5eb186d449 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php @@ -29,6 +29,13 @@ interface OutputFormatterStyleInterface */ function setForeground($color = null); + /** + * Gets style foreground color. + * + * @return string|null + */ + function getForeground(); + /** * Sets style background color. * @@ -38,6 +45,13 @@ interface OutputFormatterStyleInterface */ function setBackground($color = null); + /** + * Gets style background color. + * + * @return string|null + */ + function getBackground(); + /** * Sets some specific style option. * @@ -61,6 +75,20 @@ interface OutputFormatterStyleInterface */ function setOptions(array $options); + /** + * Gets specific style options. + * + * @return array + */ + function getOptions(); + + /** + * Gets terminal colorization sequence. + * + * @return string + */ + function getTerminalSequence(); + /** * Applies the style to a given text. * From 4f298dd7c7162b5af105207c8716601d18a94ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Simon?= Date: Fri, 16 Mar 2012 09:39:23 +0100 Subject: [PATCH 5/9] [Console] Added formatter style stack. --- .../Formatter/OutputFormatterStyleStack.php | 136 ++++++++++++++++++ .../OutputFormatterStyleStackTest.php | 74 ++++++++++ 2 files changed, 210 insertions(+) create mode 100644 src/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php create mode 100644 tests/Symfony/Tests/Component/Console/Formatter/OutputFormatterStyleStackTest.php diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php b/src/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php new file mode 100644 index 0000000000..9dd7fcc6bb --- /dev/null +++ b/src/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * @author: Jean-François Simon + */ +class OutputFormatterStyleStack +{ + /** + * @var array + */ + private $foregrounds; + + /** + * @var array + */ + private $backgrounds; + + /** + * @var array + */ + private $options; + + /** + * Constructor. + */ + public function __construct() + { + $this->reset(); + } + + /** + * Resets stack (ie. empty internal arrays). + */ + public function reset() + { + $this->foregrounds = array(); + $this->backgrounds = array(); + $this->options = array(); + } + + /** + * Pushes a style in the stack. + * + * @param OutputFormatterStyleInterface $style + */ + public function pushStyle(OutputFormatterStyleInterface $style) + { + $foreground = $style->getForeground(); + if (null !== $foreground) { + $this->foregrounds[] = $foreground; + } + + $background = $style->getBackground(); + if (null !== $background) { + $this->backgrounds[] = $background; + } + + foreach ($style->getOptions() as $option) { + if (!isset($this->options[$option])) { + $this->options[$option] = 0; + } + $this->options[$option] += 1; + } + } + + /** + * Pops a style from the stack. + * + * @param OutputFormatterStyleInterface $style + * + * @throws \InvalidArgumentException When style tags incorrectly nested + */ + public function popStyle(OutputFormatterStyleInterface $style) + { + $this->popArrayCode($this->foregrounds, $style->getForeground()); + $this->popArrayCode($this->backgrounds, $style->getBackground()); + + foreach ($style->getOptions() as $option) { + if (!isset($this->options[$option])) { + throw new \InvalidArgumentException('Unexpected style "'.$option.'" option end.'); + } + + $this->options[$option] -= 1; + if (0 === $this->options[$option]) { + unset($this->options[$option]); + } + } + } + + /** + * Computes current style with stacks top codes. + * + * @return OutputFormatterStyle + */ + public function getCurrentStyle() + { + return new OutputFormatterStyle( + end($this->foregrounds) ?: null, + end($this->backgrounds) ?: null, + array_keys($this->options) + ); + } + + /** + * Pops a color from a stack. + * + * @param array $stack An array of color names + * @param int $color A color name + * + * @throws \InvalidArgumentException When poped color is not the expected one + */ + private function popArrayCode(&$stack, $color) + { + if (null === $color) { + return; + } + + $current = end($stack); + if ($current !== $color) { + throw new \InvalidArgumentException('Expected style "'.$current.'" color end but "'.$color.'" end found.'); + } + + array_pop($stack); + } +} diff --git a/tests/Symfony/Tests/Component/Console/Formatter/OutputFormatterStyleStackTest.php b/tests/Symfony/Tests/Component/Console/Formatter/OutputFormatterStyleStackTest.php new file mode 100644 index 0000000000..9f4c9895bc --- /dev/null +++ b/tests/Symfony/Tests/Component/Console/Formatter/OutputFormatterStyleStackTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\Console\Formatter; + +use Symfony\Component\Console\Formatter\OutputFormatterStyleStack; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; + +class OutputFormatterStyleStackTest extends \PHPUnit_Framework_TestCase +{ + public function testPush() + { + $stack = new OutputFormatterStyleStack(); + $stack->pushStyle(new OutputFormatterStyle('white')); + $stack->pushStyle(new OutputFormatterStyle(null, 'black')); + $stack->pushStyle(new OutputFormatterStyle(null, null, array('bold'))); + + $style = $stack->getCurrentStyle(); + $this->assertEquals('white', $style->getForeground()); + $this->assertEquals('black', $style->getBackground()); + $this->assertEquals(array('bold'), $style->getOptions()); + + $stack->pushStyle(new OutputFormatterStyle('yellow', null, array('blink'))); + + $style = $stack->getCurrentStyle(); + $this->assertEquals('yellow', $style->getForeground()); + $this->assertEquals('black', $style->getBackground()); + $this->assertEquals(array('bold', 'blink'), $style->getOptions()); + } + + public function testPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->pushStyle(new OutputFormatterStyle('white', 'black', array('blink', 'bold'))); + $stack->pushStyle(new OutputFormatterStyle('yellow', 'blue')); + + $style = $stack->getCurrentStyle(); + $this->assertEquals('yellow', $style->getForeground()); + $this->assertEquals('blue', $style->getBackground()); + $this->assertEquals(array('blink', 'bold'), $style->getOptions()); + + $stack->popStyle(new OutputFormatterStyle(null, 'blue', array('blink'))); + + $style = $stack->getCurrentStyle(); + $this->assertEquals('yellow', $style->getForeground()); + $this->assertEquals('black', $style->getBackground()); + $this->assertEquals(array('bold'), $style->getOptions()); + + $stack->popStyle(new OutputFormatterStyle('yellow', 'black', array('bold'))); + + $style = $stack->getCurrentStyle(); + $this->assertEquals('white', $style->getForeground()); + $this->assertEquals(null, $style->getBackground()); + $this->assertEquals(array(), $style->getOptions()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidPop() + { + $stack = new OutputFormatterStyleStack(); + $stack->pushStyle(new OutputFormatterStyle('white', 'black', array('blink', 'bold'))); + $stack->popStyle(new OutputFormatterStyle('yellow')); + } +} From a1add4b8d50473cfa34f5fa5fd427ba68b3beccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Simon?= Date: Fri, 16 Mar 2012 09:44:39 +0100 Subject: [PATCH 6/9] [Console] Updated output formatter to use style stack. --- .../Console/Formatter/OutputFormatter.php | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/src/Symfony/Component/Console/Formatter/OutputFormatter.php index f8967080e4..2cdb8c780d 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php @@ -23,10 +23,11 @@ class OutputFormatter implements OutputFormatterInterface /** * The pattern to phrase the format. */ - const FORMAT_PATTERN = '#<([a-z][a-z0-9_=;-]+)>(.*?)#is'; + const FORMAT_PATTERN = '#<(/?)([a-z][a-z0-9_=;-]+)?>#is'; private $decorated; private $styles = array(); + private $styleStack; /** * Initializes console output formatter. @@ -48,6 +49,8 @@ class OutputFormatter implements OutputFormatterInterface foreach ($styles as $name => $style) { $this->setStyle($name, $style); } + + $this->styleStack = new OutputFormatterStyleStack(); } /** @@ -145,20 +148,33 @@ class OutputFormatter implements OutputFormatterInterface private function replaceStyle($match) { if (!$this->isDecorated()) { - return $match[2]; + return ''; } - if (isset($this->styles[strtolower($match[1])])) { - $style = $this->styles[strtolower($match[1])]; + // Special "" tag resets all styles. + if (!isset($match[2])) { + $this->styleStack->reset(); + + return "\033[0m"; + } + + if (isset($this->styles[strtolower($match[2])])) { + $style = $this->styles[strtolower($match[2])]; } else { - $style = $this->createStyleFromString($match[1]); + $style = $this->createStyleFromString($match[2]); if (false === $style) { - return $match[0]; + return ''; } } - return $style->apply($this->format($match[2])); + if ('/' === $match[1]) { + $this->styleStack->popStyle($style); + } else { + $this->styleStack->pushStyle($style); + } + + return $this->styleStack->getCurrentStyle()->getTerminalSequence(); } /** From bd7e01a858b14c9fde4428acf5a219cce98182c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Simon?= Date: Fri, 16 Mar 2012 10:08:17 +0100 Subject: [PATCH 7/9] [Console] Fixed output formatter test broken by new implementation. --- .../Console/Formatter/OutputFormatterTest.php | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/tests/Symfony/Tests/Component/Console/Formatter/OutputFormatterTest.php b/tests/Symfony/Tests/Component/Console/Formatter/OutputFormatterTest.php index 20d045d97d..32e7555c0a 100644 --- a/tests/Symfony/Tests/Component/Console/Formatter/OutputFormatterTest.php +++ b/tests/Symfony/Tests/Component/Console/Formatter/OutputFormatterTest.php @@ -12,6 +12,7 @@ namespace Symfony\Tests\Component\Console\Formatter; use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; class FormatterStyleTest extends \PHPUnit_Framework_TestCase { @@ -51,36 +52,23 @@ class FormatterStyleTest extends \PHPUnit_Framework_TestCase { $formatter = new OutputFormatter(true); - $style = $this->getMockBuilder('Symfony\Component\Console\Formatter\OutputFormatterStyleInterface')->getMock(); + $style = new OutputFormatterStyle('red', 'blue'); $formatter->setStyle('test', $style); $this->assertEquals($style, $formatter->getStyle('test')); $this->assertNotEquals($style, $formatter->getStyle('info')); - $style - ->expects($this->once()) - ->method('apply') - ->will($this->returnValue('[STYLE_BEG]some custom msg[STYLE_END]')); - - $this->assertEquals("[STYLE_BEG]some custom msg[STYLE_END]", $formatter->format('some custom msg')); + $this->assertEquals("\033[31;44msome custom msg\033[0m", $formatter->format('some custom msg')); } public function testRedefineStyle() { $formatter = new OutputFormatter(true); - $style = $this->getMockBuilder('Symfony\Component\Console\Formatter\OutputFormatterStyleInterface') - ->getMock(); + $style = new OutputFormatterStyle('red', 'blue'); $formatter->setStyle('info', $style); - $style - ->expects($this->once()) - ->method('apply') - ->will($this->returnValue('[STYLE_BEG]some custom msg[STYLE_END]')); - - $this->assertEquals( - "[STYLE_BEG]some custom msg[STYLE_END]", $formatter->format('some custom msg') - ); + $this->assertEquals("\033[31;44msome custom msg\033[0m", $formatter->format('some custom msg')); } public function testInlineStyle() From 90a2a6e556baf674e5a06f7864993e1f0ddd5308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Simon?= Date: Fri, 16 Mar 2012 10:22:20 +0100 Subject: [PATCH 8/9] [Console] Undecorated formatter must update style stack too. --- .../Component/Console/Formatter/OutputFormatter.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/src/Symfony/Component/Console/Formatter/OutputFormatter.php index 2cdb8c780d..cc5cfd6e9f 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php @@ -147,15 +147,11 @@ class OutputFormatter implements OutputFormatterInterface */ private function replaceStyle($match) { - if (!$this->isDecorated()) { - return ''; - } - // Special "" tag resets all styles. if (!isset($match[2])) { $this->styleStack->reset(); - return "\033[0m"; + return $this->isDecorated() ? "\033[0m" : ''; } if (isset($this->styles[strtolower($match[2])])) { @@ -174,7 +170,7 @@ class OutputFormatter implements OutputFormatterInterface $this->styleStack->pushStyle($style); } - return $this->styleStack->getCurrentStyle()->getTerminalSequence(); + return $this->isDecorated() ? $this->styleStack->getCurrentStyle()->getTerminalSequence() : ''; } /** From 2a908711cd4cd012df2057ebbf8fbb97795418bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Simon?= Date: Fri, 16 Mar 2012 14:00:53 +0100 Subject: [PATCH 9/9] [Console] Removed previously introduced BC break. --- .../OutputFormatterStyleInterface.php | 28 ------------------- .../Formatter/OutputFormatterStyleStack.php | 12 ++++---- 2 files changed, 6 insertions(+), 34 deletions(-) diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php b/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php index 5eb186d449..212cb86ff0 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php @@ -29,13 +29,6 @@ interface OutputFormatterStyleInterface */ function setForeground($color = null); - /** - * Gets style foreground color. - * - * @return string|null - */ - function getForeground(); - /** * Sets style background color. * @@ -45,13 +38,6 @@ interface OutputFormatterStyleInterface */ function setBackground($color = null); - /** - * Gets style background color. - * - * @return string|null - */ - function getBackground(); - /** * Sets some specific style option. * @@ -75,20 +61,6 @@ interface OutputFormatterStyleInterface */ function setOptions(array $options); - /** - * Gets specific style options. - * - * @return array - */ - function getOptions(); - - /** - * Gets terminal colorization sequence. - * - * @return string - */ - function getTerminalSequence(); - /** * Applies the style to a given text. * diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php b/src/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php index 9dd7fcc6bb..947d6f2ca1 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatterStyleStack.php @@ -52,9 +52,9 @@ class OutputFormatterStyleStack /** * Pushes a style in the stack. * - * @param OutputFormatterStyleInterface $style + * @param OutputFormatterStyle $style */ - public function pushStyle(OutputFormatterStyleInterface $style) + public function pushStyle(OutputFormatterStyle $style) { $foreground = $style->getForeground(); if (null !== $foreground) { @@ -77,11 +77,11 @@ class OutputFormatterStyleStack /** * Pops a style from the stack. * - * @param OutputFormatterStyleInterface $style + * @param OutputFormatterStyle $style * * @throws \InvalidArgumentException When style tags incorrectly nested */ - public function popStyle(OutputFormatterStyleInterface $style) + public function popStyle(OutputFormatterStyle $style) { $this->popArrayCode($this->foregrounds, $style->getForeground()); $this->popArrayCode($this->backgrounds, $style->getBackground()); @@ -115,8 +115,8 @@ class OutputFormatterStyleStack /** * Pops a color from a stack. * - * @param array $stack An array of color names - * @param int $color A color name + * @param array $stack An array of color names + * @param string $color A color name * * @throws \InvalidArgumentException When poped color is not the expected one */