Commit 89ea3fef by Carsten Brandt

Next iteration on console commands

issue #33 - implemted getScreenSize for linux systems - tiny adjustments and better naming for console color methods - color switch in base Controller changed to magic property - colorized Help command
parent aadcb59c
...@@ -36,12 +36,35 @@ class Controller extends \yii\base\Controller ...@@ -36,12 +36,35 @@ class Controller extends \yii\base\Controller
public $interactive = true; public $interactive = true;
/** /**
* @var bool whether to enable ANSI style in output. * @var boolean whether to enable ANSI style in output.
* Defaults to null meaning auto-detect.
*/
private $_colors;
/**
* Whether to enable ANSI style in output.
*
* Setting this will affect [[ansiFormat()]], [[stdout()]] and [[stderr()]]. * Setting this will affect [[ansiFormat()]], [[stdout()]] and [[stderr()]].
* If not set it will be auto detected using [[yii\helpers\Console::streamSupportsAnsiColors()]] with STDOUT * If not set it will be auto detected using [[yii\helpers\Console::streamSupportsAnsiColors()]] with STDOUT
* for [[ansiFormat()]] and [[stdout()]] and STDERR for [[stderr()]]. * for [[ansiFormat()]] and [[stdout()]] and STDERR for [[stderr()]].
* @param resource $stream
* @return boolean Whether to enable ANSI style in output.
*/
public function getColors($stream = STDOUT)
{
if ($this->_colors === null) {
return Console::streamSupportsAnsiColors($stream);
}
return $this->_colors;
}
/**
* Whether to enable ANSI style in output.
*/ */
public $colors; public function setColors($value)
{
$this->_colors = (bool) $value;
}
/** /**
* Runs an action with the specified action ID and parameters. * Runs an action with the specified action ID and parameters.
...@@ -138,7 +161,7 @@ class Controller extends \yii\base\Controller ...@@ -138,7 +161,7 @@ class Controller extends \yii\base\Controller
*/ */
public function ansiFormat($string) public function ansiFormat($string)
{ {
if ($this->ansi === true || $this->ansi === null && Console::streamSupportsAnsiColors(STDOUT)) { if ($this->getColors()) {
$args = func_get_args(); $args = func_get_args();
array_shift($args); array_shift($args);
$string = Console::ansiFormat($string, $args); $string = Console::ansiFormat($string, $args);
...@@ -162,7 +185,7 @@ class Controller extends \yii\base\Controller ...@@ -162,7 +185,7 @@ class Controller extends \yii\base\Controller
*/ */
public function stdout($string) public function stdout($string)
{ {
if ($this->ansi === true || $this->ansi === null && Console::streamSupportsAnsiColors(STDOUT)) { if ($this->getColors()) {
$args = func_get_args(); $args = func_get_args();
array_shift($args); array_shift($args);
$string = Console::ansiFormat($string, $args); $string = Console::ansiFormat($string, $args);
...@@ -186,7 +209,7 @@ class Controller extends \yii\base\Controller ...@@ -186,7 +209,7 @@ class Controller extends \yii\base\Controller
*/ */
public function stderr($string) public function stderr($string)
{ {
if ($this->ansi === true || $this->ansi === null && Console::streamSupportsAnsiColors(STDERR)) { if ($this->getColors(STDERR)) {
$args = func_get_args(); $args = func_get_args();
array_shift($args); array_shift($args);
$string = Console::ansiFormat($string, $args); $string = Console::ansiFormat($string, $args);
...@@ -259,6 +282,6 @@ class Controller extends \yii\base\Controller ...@@ -259,6 +282,6 @@ class Controller extends \yii\base\Controller
*/ */
public function globalOptions() public function globalOptions()
{ {
return array(); return array('colors', 'interactive');
} }
} }
...@@ -13,6 +13,7 @@ use yii\base\InlineAction; ...@@ -13,6 +13,7 @@ use yii\base\InlineAction;
use yii\console\Controller; use yii\console\Controller;
use yii\console\Exception; use yii\console\Exception;
use yii\console\Request; use yii\console\Request;
use yii\helpers\Console;
use yii\helpers\Inflector; use yii\helpers\Inflector;
/** /**
...@@ -56,7 +57,7 @@ class HelpController extends Controller ...@@ -56,7 +57,7 @@ class HelpController extends Controller
$result = Yii::$app->createController($command); $result = Yii::$app->createController($command);
if ($result === false) { if ($result === false) {
throw new Exception(Yii::t('yii', 'No help for unknown command "{command}".', array( throw new Exception(Yii::t('yii', 'No help for unknown command "{command}".', array(
'{command}' => $command, '{command}' => $this->ansiFormat($command, Console::FG_YELLOW),
))); )));
} }
...@@ -143,14 +144,15 @@ class HelpController extends Controller ...@@ -143,14 +144,15 @@ class HelpController extends Controller
{ {
$commands = $this->getCommands(); $commands = $this->getCommands();
if (!empty($commands)) { if (!empty($commands)) {
echo "The following commands are available:\n\n"; $this->stdout("\nThe following commands are available:\n\n", Console::BOLD);
foreach ($commands as $command) { foreach ($commands as $command) {
echo "* $command\n"; echo "- " . $this->ansiFormat($command, Console::FG_YELLOW) . "\n";
} }
echo "\nTo see the help of each command, enter:\n"; $this->stdout("\nTo see the help of each command, enter:\n", Console::BOLD);
echo "\n yii help <command-name>\n\n"; echo "\n yii " . $this->ansiFormat('help', Console::FG_YELLOW) . ' '
. $this->ansiFormat('<command-name>', Console::FG_CYAN) . "\n\n";
} else { } else {
echo "\nNo commands are found.\n"; $this->stdout("\nNo commands are found.\n\n", Console::BOLD);
} }
} }
...@@ -167,19 +169,18 @@ class HelpController extends Controller ...@@ -167,19 +169,18 @@ class HelpController extends Controller
} }
if ($comment !== '') { if ($comment !== '') {
echo "\nDESCRIPTION\n"; $this->stdout("\nDESCRIPTION\n", Console::BOLD);
echo "\n" . $comment . "\n\n"; echo "\n" . Console::renderColoredString($comment) . "\n\n";
} }
$actions = $this->getActions($controller); $actions = $this->getActions($controller);
if (!empty($actions)) { if (!empty($actions)) {
echo "\nSUB-COMMANDS\n\n"; $this->stdout("\nSUB-COMMANDS\n\n", Console::BOLD);
$prefix = $controller->getUniqueId(); $prefix = $controller->getUniqueId();
foreach ($actions as $action) { foreach ($actions as $action) {
echo '- ' . $this->ansiFormat($prefix.'/'.$action, Console::FG_YELLOW);
if ($action === $controller->defaultAction) { if ($action === $controller->defaultAction) {
echo "* $prefix/$action (default)"; $this->stdout(' (default)', Console::FG_GREEN);
} else {
echo "* $prefix/$action";
} }
$summary = $this->getActionSummary($controller, $action); $summary = $this->getActionSummary($controller, $action);
if ($summary !== '') { if ($summary !== '') {
...@@ -187,8 +188,9 @@ class HelpController extends Controller ...@@ -187,8 +188,9 @@ class HelpController extends Controller
} }
echo "\n"; echo "\n";
} }
echo "\n\nTo see the detailed information about individual sub-commands, enter:\n"; echo "\nTo see the detailed information about individual sub-commands, enter:\n";
echo "\n yii help <sub-command>\n\n"; echo "\n yii " . $this->ansiFormat('help', Console::FG_YELLOW) . ' '
. $this->ansiFormat('<sub-command>', Console::FG_CYAN) . "\n\n";
} }
} }
...@@ -253,25 +255,25 @@ class HelpController extends Controller ...@@ -253,25 +255,25 @@ class HelpController extends Controller
$options = $this->getOptionHelps($controller); $options = $this->getOptionHelps($controller);
if ($tags['description'] !== '') { if ($tags['description'] !== '') {
echo "\nDESCRIPTION"; $this->stdout("\nDESCRIPTION\n", Console::BOLD);
echo "\n\n" . $tags['description'] . "\n\n"; echo "\n" . Console::renderColoredString($tags['description']) . "\n\n";
} }
echo "\nUSAGE\n\n"; $this->stdout("\nUSAGE\n\n", Console::BOLD);
if ($action->id === $controller->defaultAction) { if ($action->id === $controller->defaultAction) {
echo 'yii ' . $controller->getUniqueId(); echo 'yii ' . $this->ansiFormat($controller->getUniqueId(), Console::FG_YELLOW);
} else { } else {
echo "yii " . $action->getUniqueId(); echo 'yii ' . $this->ansiFormat($action->getUniqueId(), Console::FG_YELLOW);
} }
list ($required, $optional) = $this->getArgHelps($method, isset($tags['param']) ? $tags['param'] : array()); list ($required, $optional) = $this->getArgHelps($method, isset($tags['param']) ? $tags['param'] : array());
if (!empty($required)) { foreach ($required as $arg => $description) {
echo ' <' . implode('> <', array_keys($required)) . '>'; $this->stdout(' <' . $arg . '>', Console::FG_CYAN);
} }
if (!empty($optional)) { foreach ($optional as $arg => $description) {
echo ' [' . implode('] [', array_keys($optional)) . ']'; $this->stdout(' [' . $arg . ']', Console::FG_CYAN);
} }
if (!empty($options)) { if (!empty($options)) {
echo ' [...options...]'; $this->stdout(' [...options...]', Console::FG_RED);
} }
echo "\n\n"; echo "\n\n";
...@@ -281,7 +283,7 @@ class HelpController extends Controller ...@@ -281,7 +283,7 @@ class HelpController extends Controller
$options = $this->getOptionHelps($controller); $options = $this->getOptionHelps($controller);
if (!empty($options)) { if (!empty($options)) {
echo "\nOPTIONS\n\n"; $this->stdout("\nOPTIONS\n\n", Console::BOLD);
echo implode("\n\n", $options) . "\n\n"; echo implode("\n\n", $options) . "\n\n";
} }
} }
...@@ -310,9 +312,9 @@ class HelpController extends Controller ...@@ -310,9 +312,9 @@ class HelpController extends Controller
$comment = $tag; $comment = $tag;
} }
if ($param->isDefaultValueAvailable()) { if ($param->isDefaultValueAvailable()) {
$optional[$name] = $this->formatOptionHelp('* ' . $name, false, $type, $param->getDefaultValue(), $comment); $optional[$name] = $this->formatOptionHelp('- ' . $this->ansiFormat($name, Console::FG_CYAN), false, $type, $param->getDefaultValue(), $comment);
} else { } else {
$required[$name] = $this->formatOptionHelp('* ' . $name, true, $type, null, $comment); $required[$name] = $this->formatOptionHelp('- ' . $this->ansiFormat($name, Console::FG_CYAN), true, $type, null, $comment);
} }
} }
...@@ -352,9 +354,9 @@ class HelpController extends Controller ...@@ -352,9 +354,9 @@ class HelpController extends Controller
$type = null; $type = null;
$comment = $doc; $comment = $doc;
} }
$options[$name] = $this->formatOptionHelp('--' . $name, false, $type, $defaultValue, $comment); $options[$name] = $this->formatOptionHelp($this->ansiFormat('--' . $name, Console::FG_RED), false, $type, $defaultValue, $comment);
} else { } else {
$options[$name] = $this->formatOptionHelp('--' . $name, false, null, $defaultValue, ''); $options[$name] = $this->formatOptionHelp($this->ansiFormat('--' . $name, Console::FG_RED), false, null, $defaultValue, '');
} }
} }
ksort($options); ksort($options);
......
...@@ -45,6 +45,7 @@ class Console ...@@ -45,6 +45,7 @@ class Console
const BG_CYAN = 46; const BG_CYAN = 46;
const BG_GREY = 47; const BG_GREY = 47;
const RESET = 0;
const NORMAL = 0; const NORMAL = 0;
const BOLD = 1; const BOLD = 1;
const ITALIC = 3; const ITALIC = 3;
...@@ -240,34 +241,41 @@ class Console ...@@ -240,34 +241,41 @@ class Console
} }
/** /**
* Sets the ANSI format for any text that is printed afterwards. * Returns the ANSI format code.
* *
* You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xterm256ColorFg]] and [[xterm256ColorBg]]. * @param array $format You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xtermFgColor]] and [[xtermBgColor]].
* TODO: documentation * TODO: documentation
* @return string
*/ */
public static function ansiFormatBegin() public static function ansiFormatCode($format)
{ {
echo "\033[" . implode(';', func_get_args()) . 'm'; return "\033[" . implode(';', $format) . 'm';
} }
/** /**
* Resets any ANSI format set by previous method [[ansiFormatBegin()]] * Sets the ANSI format for any text that is printed afterwards.
* Any output after this is will have default text style. *
* @param array $format You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xtermFgColor]] and [[xtermBgColor]].
* TODO: documentation
* @see ansiFormatEnd()
*/ */
public static function ansiFormatReset() public static function beginAnsiFormat($format)
{ {
echo "\033[0m"; echo "\033[" . implode(';', $format) . 'm';
} }
/** /**
* Returns the ANSI format code. * Resets any ANSI format set by previous method [[ansiFormatBegin()]]
* Any output after this is will have default text style.
* This is equal to
* *
* You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xterm256ColorFg]] and [[xterm256ColorBg]]. * ```php
* TODO: documentation * echo Console::ansiFormatCode(array(Console::RESET))
* ```
*/ */
public static function ansiFormatCode($format) public static function endAnsiFormat()
{ {
return "\033[" . implode(';', $format) . 'm'; echo "\033[0m";
} }
/** /**
...@@ -275,7 +283,7 @@ class Console ...@@ -275,7 +283,7 @@ class Console
* *
* @param string $string the string to be formatted * @param string $string the string to be formatted
* @param array $format array containing formatting values. * @param array $format array containing formatting values.
* You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xterm256ColorFg]] and [[xterm256ColorBg]]. * You can pass any of the FG_*, BG_* and TEXT_* constants and also [[xtermFgColor]] and [[xtermBgColor]].
* @return string * @return string
*/ */
public static function ansiFormat($string, $format=array()) public static function ansiFormat($string, $format=array())
...@@ -284,15 +292,32 @@ class Console ...@@ -284,15 +292,32 @@ class Console
return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string . "\033[0m"; return "\033[0m" . ($code !== '' ? "\033[" . $code . "m" : '') . $string . "\033[0m";
} }
//const COLOR_XTERM256 = 38;// http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors /**
public static function xterm256ColorFg($i) // TODO naming! * Returns the ansi format code for xterm foreground color.
* You can pass the returnvalue of this to one of the formatting methods:
* [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]]
*
* @param integer $colorCode xterm color code
* @return string
* @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors
*/
public static function xtermFgColor($colorCode)
{ {
return '38;5;' . $i; return '38;5;' . $colorCode;
} }
public static function xterm256ColorBg($i) // TODO naming! /**
* Returns the ansi format code for xterm foreground color.
* You can pass the returnvalue of this to one of the formatting methods:
* [[ansiFormat]], [[ansiFormatCode]], [[beginAnsiFormat]]
*
* @param integer $colorCode xterm color code
* @return string
* @see http://en.wikipedia.org/wiki/Talk:ANSI_escape_code#xterm-256colors
*/
public static function xtermBgColor($colorCode)
{ {
return '48;5;' . $i; return '48;5;' . $colorCode;
} }
/** /**
...@@ -303,7 +328,7 @@ class Console ...@@ -303,7 +328,7 @@ class Console
*/ */
public static function stripAnsiFormat($string) public static function stripAnsiFormat($string)
{ {
return preg_replace('/\033\[[\d;]+m/', '', $string); // TODO currently only strips color return preg_replace('/\033\[[\d;?]*\w/', '', $string);
} }
// TODO refactor and review // TODO refactor and review
...@@ -418,10 +443,11 @@ class Console ...@@ -418,10 +443,11 @@ class Console
} }
/** /**
* TODO syntax copied from https://github.com/pear/Console_Color2/blob/master/Console/Color2.php * Converts a string to ansi formatted by replacing patterns like %y (for yellow) with ansi control codes
* *
* Converts colorcodes in the format %y (for yellow) into ansi-control * // TODO documentation
* codes. The conversion table is: ('bold' meaning 'light' on some * Uses almost the same syntax as https://github.com/pear/Console_Color2/blob/master/Console/Color2.php
* The conversion table is: ('bold' meaning 'light' on some
* terminals). It's almost the same conversion table irssi uses. * terminals). It's almost the same conversion table irssi uses.
* <pre> * <pre>
* text text background * text text background
...@@ -450,7 +476,6 @@ class Console ...@@ -450,7 +476,6 @@ class Console
* *
* @param string $string String to convert * @param string $string String to convert
* @param bool $colored Should the string be colored? * @param bool $colored Should the string be colored?
*
* @return string * @return string
*/ */
public static function renderColoredString($string, $colored = true) public static function renderColoredString($string, $colored = true)
...@@ -508,7 +533,8 @@ class Console ...@@ -508,7 +533,8 @@ class Console
} }
/** /**
* Escapes % so they don't get interpreted as color codes * Escapes % so they don't get interpreted as color codes when
* the string is parsed by [[renderColoredString]]
* *
* @param string $string String to escape * @param string $string String to escape
* *
...@@ -548,12 +574,38 @@ class Console ...@@ -548,12 +574,38 @@ class Console
/** /**
* Usage: list($w, $h) = ConsoleHelper::getScreenSize(); * Usage: list($w, $h) = ConsoleHelper::getScreenSize();
* *
* @return array * @return array|boolean An array of ($width, $height) or false when it was not able to determine size.
*/ */
public static function getScreenSize() public static function getScreenSize($refresh = false)
{ {
// TODO implement static $size;
return array(150, 50); if ($size !== null && !$refresh) {
return $size;
}
if (static::isRunningOnWindows()) {
// TODO implement for windows
return $size = false;
} else {
// try stty if available
$stty = array();
if (exec('stty -a 2>&1', $stty) && preg_match('/rows\s+(\d+);\s*columns\s+(\d+);/mi', implode(' ', $stty), $matches)) {
return $size = array($matches[2], $matches[1]);
}
// fallback to tput, which may not be updated on terminal resize
if (($width = (int) exec('tput cols 2>&1')) > 0 && ($height = (int) exec('tput lines 2>&1')) > 0) {
return $size = array($width, $height);
}
// fallback to ENV variables, which may not be updated on terminal resize
if (($width = (int) getenv('COLUMNS')) > 0 && ($height = (int) getenv('LINES')) > 0) {
return $size = array($width, $height);
}
}
return $size = false;
} }
/** /**
...@@ -607,27 +659,23 @@ class Console ...@@ -607,27 +659,23 @@ class Console
/** /**
* Prints text to STDOUT appended with a carriage return (PHP_EOL). * Prints text to STDOUT appended with a carriage return (PHP_EOL).
* *
* @param string $text * @param string $string
* @param bool $raw
*
* @return mixed Number of bytes printed or bool false on error * @return mixed Number of bytes printed or bool false on error
*/ */
public static function output($text = null) public static function output($string = null)
{ {
return static::stdout($text . PHP_EOL); return static::stdout($string . PHP_EOL);
} }
/** /**
* Prints text to STDERR appended with a carriage return (PHP_EOL). * Prints text to STDERR appended with a carriage return (PHP_EOL).
* *
* @param string $text * @param string $string
* @param bool $raw
*
* @return mixed Number of bytes printed or false on error * @return mixed Number of bytes printed or false on error
*/ */
public static function error($text = null) public static function error($string = null)
{ {
return static::stderr($text . PHP_EOL); return static::stderr($string . PHP_EOL);
} }
/** /**
......
<?php
namespace yiiunit\framework\helpers;
use Yii;
use yii\helpers\Console;
use yiiunit\TestCase;
class ConsoleTest extends TestCase
{
public function testStripAnsiFormat()
{
ob_start();
ob_implicit_flush(false);
echo 'a';
Console::moveCursorForward(1);
echo 'a';
Console::moveCursorDown(1);
echo 'a';
Console::moveCursorUp(1);
echo 'a';
Console::moveCursorBackward(1);
echo 'a';
Console::moveCursorNextLine(1);
echo 'a';
Console::moveCursorPrevLine(1);
echo 'a';
Console::moveCursorTo(1);
echo 'a';
Console::moveCursorTo(1, 2);
echo 'a';
Console::clearLine();
echo 'a';
Console::clearLineAfterCursor();
echo 'a';
Console::clearLineBeforeCursor();
echo 'a';
Console::clearScreen();
echo 'a';
Console::clearScreenAfterCursor();
echo 'a';
Console::clearScreenBeforeCursor();
echo 'a';
Console::scrollDown();
echo 'a';
Console::scrollUp();
echo 'a';
Console::hideCursor();
echo 'a';
Console::showCursor();
echo 'a';
Console::saveCursorPosition();
echo 'a';
Console::restoreCursorPosition();
echo 'a';
Console::beginAnsiFormat(array(Console::FG_GREEN, Console::BG_BLUE, Console::UNDERLINE));
echo 'a';
Console::endAnsiFormat();
echo 'a';
Console::beginAnsiFormat(array(Console::xtermBgColor(128), Console::xtermFgColor(55)));
echo 'a';
Console::endAnsiFormat();
echo 'a';
$ouput = Console::stripAnsiFormat(ob_get_clean());
ob_implicit_flush(true);
// $output = str_replace("\033", 'X003', $ouput );// uncomment for debugging
$this->assertEquals(str_repeat('a', 25), $ouput);
}
/* public function testScreenSize()
{
for($i = 1; $i < 20; $i++) {
echo implode(', ', Console::getScreenSize(true)) . "\n";
ob_flush();
sleep(1);
}
}*/
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment