From 10dc407a3c3f583e267ceb2460f11cbe8b40d988 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 25 Jun 2014 18:05:29 +0200 Subject: [PATCH] implemented markdown parsing for console command description issue #746 --- composer.json | 5 +- .../apidoc/helpers/ApiMarkdownTrait.php | 7 +- .../apidoc/helpers/IndexFileAnalyzer.php | 8 +- extensions/twig/Extension.php | 2 +- framework/composer.json | 5 +- framework/console/Markdown.php | 171 ++++++++++++++++++ .../console/controllers/AssetController.php | 8 +- .../console/controllers/HelpController.php | 4 +- framework/helpers/BaseConsole.php | 11 +- framework/yii | 17 ++ 10 files changed, 219 insertions(+), 19 deletions(-) create mode 100644 framework/console/Markdown.php diff --git a/composer.json b/composer.json index 22a31ddf2d..21f5992a4b 100644 --- a/composer.json +++ b/composer.json @@ -121,5 +121,8 @@ "yii\\sphinx\\": "extensions/sphinx/", "yii\\twig\\": "extensions/twig/" } - } + }, + "bin": [ + "framework/yii" + ] } diff --git a/extensions/apidoc/helpers/ApiMarkdownTrait.php b/extensions/apidoc/helpers/ApiMarkdownTrait.php index d6bbee0a83..c3cc48c0f0 100644 --- a/extensions/apidoc/helpers/ApiMarkdownTrait.php +++ b/extensions/apidoc/helpers/ApiMarkdownTrait.php @@ -1,9 +1,8 @@ + * @since 2.0 + */ +class Markdown extends \cebe\markdown\Parser +{ + /** + * @var array these are "escapeable" characters. When using one of these prefixed with a + * backslash, the character will be outputted without the backslash and is not interpreted + * as markdown. + */ + protected $escapeCharacters = [ + '\\', // backslash + '`', // backtick + '*', // asterisk + '_', // underscore + ]; + + /** + * @inheritDoc + */ + protected function identifyLine($lines, $current) + { + if (isset($lines[$current]) && (strncmp($lines[$current], '```', 3) === 0 || strncmp($lines[$current], '~~~', 3) === 0)) { + return 'fencedCode'; + } + return parent::identifyLine($lines, $current); + } + + /** + * Consume lines for a fenced code block + */ + protected function consumeFencedCode($lines, $current) + { + // consume until ``` + $block = [ + 'type' => 'code', + 'content' => [], + ]; + $line = rtrim($lines[$current]); + $fence = substr($line, 0, $pos = strrpos($line, $line[0]) + 1); + $language = substr($line, $pos); + if (!empty($language)) { + $block['language'] = $language; + } + for ($i = $current + 1, $count = count($lines); $i < $count; $i++) { + if (rtrim($line = $lines[$i]) !== $fence) { + $block['content'][] = $line; + } else { + break; + } + } + return [$block, $i]; + } + + /** + * Renders a code block + */ + protected function renderCode($block) + { + return Console::ansiFormat(implode("\n", $block['content']), [Console::BG_GREY]) . "\n"; + } + + protected function renderParagraph($block) + { + return rtrim($this->parseInline(implode("\n", $block['content']))) . "\n"; + } + + /** + * @inheritDoc + */ + protected function inlineMarkers() + { + return [ + '*' => 'parseEmphStrong', + '_' => 'parseEmphStrong', + '\\' => 'parseEscape', + '`' => 'parseCode', + '~~' => 'parseStrike', + ]; + } + + /** + * Parses an inline code span `` ` ``. + */ + protected function parseCode($text) + { + // skip fenced code + if (strncmp($text, '```', 3) === 0) { + return [$text[0], 1]; + } + if (preg_match('/^(`+) (.+?) \1/', $text, $matches)) { // code with enclosed backtick + return [ + Console::ansiFormat($matches[2], [Console::UNDERLINE]), + strlen($matches[0]) + ]; + } elseif (preg_match('/^`(.+?)`/', $text, $matches)) { + return [ + Console::ansiFormat($matches[1], [Console::UNDERLINE]), + strlen($matches[0]) + ]; + } + return [$text[0], 1]; + } + + /** + * Parses empathized and strong elements. + */ + protected function parseEmphStrong($text) + { + $marker = $text[0]; + + if (!isset($text[1])) { + return [$text[0], 1]; + } + + if ($marker == $text[1]) { // strong + if ($marker == '*' && preg_match('/^[*]{2}((?:[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', $text, $matches) || + $marker == '_' && preg_match('/^__((?:[^_]|_[^_]*_)+?)__(?!_)/us', $text, $matches)) { + + return [Console::ansiFormat($this->parseInline($matches[1]), Console::BOLD), strlen($matches[0])]; + } + } else { // emph + if ($marker == '*' && preg_match('/^[*]((?:[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', $text, $matches) || + $marker == '_' && preg_match('/^_((?:[^_]|__[^_]*__)+?)_(?!_)\b/us', $text, $matches)) { + return [Console::ansiFormat($this->parseInline($matches[1]), Console::ITALIC), strlen($matches[0])]; + } + } + return [$text[0], 1]; + } + + /** + * Parses the strikethrough feature. + */ + protected function parseStrike($markdown) + { + if (preg_match('/^~~(.+?)~~/', $markdown, $matches)) { + return [ + Console::ansiFormat($this->parseInline($matches[1]), [Console::CROSSED_OUT]), + strlen($matches[0]) + ]; + } + return [$markdown[0] . $markdown[1], 2]; + } + + /** + * Parses escaped special characters. + */ + protected function parseEscape($text) + { + if (isset($text[1]) && in_array($text[1], $this->escapeCharacters)) { + return [$text[1], 2]; + } + return [$text[0], 1]; + } +} \ No newline at end of file diff --git a/framework/console/controllers/AssetController.php b/framework/console/controllers/AssetController.php index ee9fcf6b6b..87e10e4b20 100644 --- a/framework/console/controllers/AssetController.php +++ b/framework/console/controllers/AssetController.php @@ -16,14 +16,18 @@ use yii\helpers\VarDumper; * Allows you to combine and compress your JavaScript and CSS files. * * Usage: - * 1. Create a configuration file using 'template' action: + * 1. Create a configuration file using the `template` action: + * * yii asset/template /path/to/myapp/config.php + * * 2. Edit the created config file, adjusting it for your web application needs. * 3. Run the 'compress' action, using created config: + * * yii asset /path/to/myapp/config.php /path/to/myapp/config/assets_compressed.php + * * 4. Adjust your web application config to use compressed assets. * - * Note: in the console environment some path aliases like '@webroot' and '@web' may not exist, + * Note: in the console environment some path aliases like `@webroot` and `@web` may not exist, * so corresponding paths inside the configuration should be specified directly. * * Note: by default this command relies on an external tools to perform actual files compression, diff --git a/framework/console/controllers/HelpController.php b/framework/console/controllers/HelpController.php index 43e8c955ff..7b7d00cd28 100644 --- a/framework/console/controllers/HelpController.php +++ b/framework/console/controllers/HelpController.php @@ -225,7 +225,7 @@ class HelpController extends Controller if ($comment !== '') { $this->stdout("\nDESCRIPTION\n", Console::BOLD); - echo "\n" . Console::renderColoredString($comment) . "\n\n"; + echo "\n" . rtrim(Console::renderColoredString(Console::markdownToAnsi($comment))) . "\n\n"; } $actions = $this->getActions($controller); @@ -313,7 +313,7 @@ class HelpController extends Controller if ($tags['description'] !== '') { $this->stdout("\nDESCRIPTION\n", Console::BOLD); - echo "\n" . Console::renderColoredString($tags['description']) . "\n\n"; + echo "\n" . rtrim(Console::renderColoredString(Console::markdownToAnsi($tags['description']))) . "\n\n"; } $this->stdout("\nUSAGE\n\n", Console::BOLD); diff --git a/framework/helpers/BaseConsole.php b/framework/helpers/BaseConsole.php index c825ba81bb..e10967a188 100644 --- a/framework/helpers/BaseConsole.php +++ b/framework/helpers/BaseConsole.php @@ -7,6 +7,8 @@ namespace yii\helpers; +use yii\console\Markdown; + /** * BaseConsole provides concrete implementation for [[Console]]. * @@ -442,10 +444,13 @@ class BaseConsole return $result; } - // TODO rework/refactor according to https://github.com/yiisoft/yii2/issues/746 - public function markdownToAnsi() + /** + * Converts Markdown to be better readable in console environments by applying some ANSI format + */ + public static function markdownToAnsi($markdown) { - // TODO implement + $parser = new Markdown(); + return $parser->parse($markdown); } /** diff --git a/framework/yii b/framework/yii index d7fa01c792..288c3534d3 100755 --- a/framework/yii +++ b/framework/yii @@ -14,6 +14,20 @@ defined('YII_DEBUG') or define('YII_DEBUG', true); defined('STDIN') or define('STDIN', fopen('php://stdin', 'r')); defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w')); +$composerAutoload = [ + __DIR__ . '/../vendor/autoload.php', // in yii2-dev repo + __DIR__ . '/../../autoload.php', // installed as a composer binary +]; +$vendorPath = null; +foreach ($composerAutoload as $autoload) { + if (file_exists($autoload)) { + require($autoload); + $vendorPath = dirname($autoload); + break; + } +} + + require(__DIR__ . '/Yii.php'); $application = new yii\console\Application([ @@ -21,5 +35,8 @@ $application = new yii\console\Application([ 'basePath' => __DIR__ . '/console', 'controllerNamespace' => 'yii\console\controllers', ]); +if (isset($vendorPath)) { + $application->setVendorPath($vendorPath); +} $exitCode = $application->run(); exit($exitCode);