Page MenuHomePhabricator

D14136.id34170.diff
No OneTemporary

D14136.id34170.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -125,14 +125,18 @@
'PhutilCodeSnippetContextFreeGrammar' => 'grammar/code/PhutilCodeSnippetContextFreeGrammar.php',
'PhutilCommandString' => 'xsprintf/PhutilCommandString.php',
'PhutilConsole' => 'console/PhutilConsole.php',
+ 'PhutilConsoleBlock' => 'console/view/PhutilConsoleBlock.php',
+ 'PhutilConsoleConcatenatedView' => 'console/view/PhutilConsoleConcatenatedView.php',
'PhutilConsoleFormatter' => 'console/PhutilConsoleFormatter.php',
+ 'PhutilConsoleList' => 'console/view/PhutilConsoleList.php',
'PhutilConsoleMessage' => 'console/PhutilConsoleMessage.php',
'PhutilConsoleProgressBar' => 'console/PhutilConsoleProgressBar.php',
'PhutilConsoleServer' => 'console/PhutilConsoleServer.php',
'PhutilConsoleServerChannel' => 'console/PhutilConsoleServerChannel.php',
'PhutilConsoleStdinNotInteractiveException' => 'console/PhutilConsoleStdinNotInteractiveException.php',
'PhutilConsoleSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilConsoleSyntaxHighlighter.php',
- 'PhutilConsoleTable' => 'console/PhutilConsoleTable.php',
+ 'PhutilConsoleTable' => 'console/view/PhutilConsoleTable.php',
+ 'PhutilConsoleView' => 'console/view/PhutilConsoleView.php',
'PhutilConsoleWrapTestCase' => 'console/__tests__/PhutilConsoleWrapTestCase.php',
'PhutilContextFreeGrammar' => 'grammar/PhutilContextFreeGrammar.php',
'PhutilCowsay' => 'utils/PhutilCowsay.php',
@@ -635,14 +639,18 @@
'PhutilCodeSnippetContextFreeGrammar' => 'PhutilContextFreeGrammar',
'PhutilCommandString' => 'Phobject',
'PhutilConsole' => 'Phobject',
+ 'PhutilConsoleBlock' => 'PhutilConsoleView',
+ 'PhutilConsoleConcatenatedView' => 'PhutilConsoleView',
'PhutilConsoleFormatter' => 'Phobject',
+ 'PhutilConsoleList' => 'PhutilConsoleView',
'PhutilConsoleMessage' => 'Phobject',
'PhutilConsoleProgressBar' => 'Phobject',
'PhutilConsoleServer' => 'Phobject',
'PhutilConsoleServerChannel' => 'PhutilChannelChannel',
'PhutilConsoleStdinNotInteractiveException' => 'Exception',
'PhutilConsoleSyntaxHighlighter' => 'Phobject',
- 'PhutilConsoleTable' => 'Phobject',
+ 'PhutilConsoleTable' => 'PhutilConsoleView',
+ 'PhutilConsoleView' => 'Phobject',
'PhutilConsoleWrapTestCase' => 'PhutilTestCase',
'PhutilContextFreeGrammar' => 'Phobject',
'PhutilCowsay' => 'Phobject',
diff --git a/src/console/PhutilConsoleFormatter.php b/src/console/PhutilConsoleFormatter.php
--- a/src/console/PhutilConsoleFormatter.php
+++ b/src/console/PhutilConsoleFormatter.php
@@ -42,6 +42,24 @@
}
public static function formatString($format /* ... */) {
+ $args = func_get_args();
+ $args[0] = self::interpretFormat($args[0]);
+ return call_user_func_array('sprintf', $args);
+ }
+
+ public static function replaceColorCode($matches) {
+ $codes = self::$colorCodes;
+ $offset = 30 + $codes[$matches[2]];
+ $default = 39;
+ if ($matches[1] == 'bg') {
+ $offset += 10;
+ $default += 10;
+ }
+
+ return chr(27).'['.$offset.'m'.$matches[3].chr(27).'['.$default.'m';
+ }
+
+ public static function interpretFormat($format) {
$colors = implode('|', array_keys(self::$colorCodes));
// Sequence should be preceded by start-of-string or non-backslash
@@ -74,24 +92,7 @@
}
// Remove backslash escaping
- $format = preg_replace('/\\\\(\*\*.*\*\*|__.*__|##.*##)/sU', '\1', $format);
-
- $args = func_get_args();
- $args[0] = $format;
-
- return call_user_func_array('sprintf', $args);
- }
-
- public static function replaceColorCode($matches) {
- $codes = self::$colorCodes;
- $offset = 30 + $codes[$matches[2]];
- $default = 39;
- if ($matches[1] == 'bg') {
- $offset += 10;
- $default += 10;
- }
-
- return chr(27).'['.$offset.'m'.$matches[3].chr(27).'['.$default.'m';
+ return preg_replace('/\\\\(\*\*.*\*\*|__.*__|##.*##)/sU', '\1', $format);
}
}
diff --git a/src/console/view/PhutilConsoleBlock.php b/src/console/view/PhutilConsoleBlock.php
new file mode 100644
--- /dev/null
+++ b/src/console/view/PhutilConsoleBlock.php
@@ -0,0 +1,45 @@
+<?php
+
+final class PhutilConsoleBlock extends PhutilConsoleView {
+
+ private $items = array();
+
+ public function addParagraph($item) {
+ $this->items[] = array(
+ 'type' => 'paragraph',
+ 'item' => $item,
+ );
+ return $this;
+ }
+
+ public function addList(PhutilConsoleList $list) {
+ $this->items[] = array(
+ 'type' => 'list',
+ 'item' => $list,
+ );
+ return $this;
+ }
+
+ protected function drawView() {
+ $output = array();
+
+ foreach ($this->items as $spec) {
+ $type = $spec['type'];
+ $item = $spec['item'];
+
+ switch ($type) {
+ case 'paragraph':
+ $item = phutil_console_wrap($item)."\n";
+ break;
+ case 'list':
+ $item = $item;
+ break;
+ }
+
+ $output[] = $item;
+ }
+
+ return $this->drawLines($output);
+ }
+
+}
diff --git a/src/console/view/PhutilConsoleConcatenatedView.php b/src/console/view/PhutilConsoleConcatenatedView.php
new file mode 100644
--- /dev/null
+++ b/src/console/view/PhutilConsoleConcatenatedView.php
@@ -0,0 +1,22 @@
+<?php
+
+final class PhutilConsoleConcatenatedView extends PhutilConsoleView {
+
+ private $items = array();
+
+ public function addItem($item) {
+ $this->items[] = $item;
+ return $this;
+ }
+
+ protected function drawView() {
+ $output = array();
+
+ foreach ($this->items as $item) {
+ $output[] = $this->flattenView($item);
+ }
+
+ return implode('', $output);
+ }
+
+}
diff --git a/src/console/view/PhutilConsoleList.php b/src/console/view/PhutilConsoleList.php
new file mode 100644
--- /dev/null
+++ b/src/console/view/PhutilConsoleList.php
@@ -0,0 +1,42 @@
+<?php
+
+final class PhutilConsoleList extends PhutilConsoleView {
+
+ private $items = array();
+ private $wrap = true;
+
+ public function addItem($item) {
+ $this->items[] = $item;
+ return $this;
+ }
+
+ public function addItems(array $items) {
+ foreach ($items as $item) {
+ $this->addItem($item);
+ }
+ return $this;
+ }
+
+ public function getItems() {
+ return $this->items;
+ }
+
+ public function setWrap($wrap) {
+ $this->wrap = $wrap;
+ return $this;
+ }
+
+ protected function drawView() {
+ $output = array();
+ foreach ($this->getItems() as $item) {
+ if ($this->wrap) {
+ $item = phutil_console_wrap($item, 8);
+ }
+ $item = ' - '.$item;
+ $output[] = $item;
+ }
+
+ return $this->drawLines($output);
+ }
+
+}
diff --git a/src/console/PhutilConsoleTable.php b/src/console/view/PhutilConsoleTable.php
rename from src/console/PhutilConsoleTable.php
rename to src/console/view/PhutilConsoleTable.php
--- a/src/console/PhutilConsoleTable.php
+++ b/src/console/view/PhutilConsoleTable.php
@@ -22,7 +22,7 @@
* ->setBorders(true)
* ->draw();
*/
-final class PhutilConsoleTable extends Phobject {
+final class PhutilConsoleTable extends PhutilConsoleView {
private $columns = array();
private $data = array();
@@ -30,30 +30,15 @@
private $borders = false;
private $padding = 1;
private $showHeader = true;
- private $console;
const ALIGN_LEFT = 'left';
const ALIGN_CENTER = 'center';
const ALIGN_RIGHT = 'right';
-/* -( Console )------------------------------------------------------------ */
-
- protected function getConsole() {
- if ($this->console) {
- return $this->console;
- }
- return PhutilConsole::getConsole();
- }
-
- public function setConsole(PhutilConsole $console) {
- $this->console = $console;
- return $this;
- }
-
-
/* -( Configuration )------------------------------------------------------ */
+
public function setBorders($borders) {
$this->borders = $borders;
return $this;
@@ -103,19 +88,19 @@
/* -( Drawing )------------------------------------------------------------ */
- public function draw() {
- $console = $this->getConsole();
-
- $console->writeOut('%s', $this->getHeader());
- $console->writeOut('%s', $this->getBody());
- $console->writeOut('%s', $this->getFooter());
+ protected function drawView() {
+ return $this->drawLines(
+ array_merge(
+ $this->getHeader(),
+ $this->getBody(),
+ $this->getFooter()));
}
private function getHeader() {
- $output = '';
+ $output = array();
if ($this->borders) {
- $output .= $this->formatSeparator('=');
+ $output[] = $this->formatSeparator('=');
}
if (!$this->showHeader) {
@@ -133,45 +118,45 @@
idx($column, 'align', self::ALIGN_LEFT));
}
- $columns[] = PhutilConsoleFormatter::formatString(
+ $columns[] = tsprintf(
'**%s**',
$column_str);
}
- $output .= $this->formatRow($columns);
+ $output[] = $this->formatRow($columns);
if ($this->borders) {
- $output .= $this->formatSeparator('=');
+ $output[] = $this->formatSeparator('=');
}
return $output;
}
private function getBody() {
- $output = '';
+ $output = array();
foreach ($this->data as $data) {
$columns = array();
foreach ($this->columns as $key => $column) {
if (!$this->shouldAddSpacing($key, $column)) {
- $columns[] = (string)idx($data, $key, '');
+ $columns[] = idx($data, $key, '');
} else {
$columns[] = $this->alignString(
- (string)idx($data, $key, ''),
+ idx($data, $key, ''),
$this->getWidth($key),
idx($column, 'align', self::ALIGN_LEFT));
}
}
- $output .= $this->formatRow($columns);
+ $output[] = $this->formatRow($columns);
}
return $output;
}
private function getFooter() {
- $output = '';
+ $output = array();
if ($this->borders) {
$columns = array();
@@ -180,7 +165,11 @@
$columns[] = str_repeat('=', $this->getWidth($column));
}
- $output .= '+'.implode('+', $columns)."+\n";
+ $output[] = array(
+ '+',
+ $this->implode('+', $columns),
+ '+',
+ );
}
return $output;
@@ -258,7 +247,11 @@
$left_padding = str_repeat(' ', $num_left_padding);
$right_padding = str_repeat(' ', $num_right_padding);
- return $left_padding.$string.$right_padding;
+ return array(
+ $left_padding,
+ $string,
+ $right_padding,
+ );
}
/**
@@ -272,9 +265,13 @@
if ($this->borders) {
$separator = $padding.'|'.$padding;
- return '|'.$padding.implode($separator, $columns).$padding."|\n";
+ return array(
+ '|'.$padding,
+ $this->implode($separator, $columns),
+ $padding.'|',
+ );
} else {
- return implode($padding, $columns)."\n";
+ return $this->implode($padding, $columns);
}
}
@@ -291,7 +288,11 @@
$columns[] = str_repeat($string, $this->getWidth($column));
}
- return $separator.implode($separator, $columns).$separator."\n";
+ return array(
+ $separator,
+ $this->implode($separator, $columns),
+ $separator,
+ );
}
}
diff --git a/src/console/view/PhutilConsoleView.php b/src/console/view/PhutilConsoleView.php
new file mode 100644
--- /dev/null
+++ b/src/console/view/PhutilConsoleView.php
@@ -0,0 +1,112 @@
+<?php
+
+abstract class PhutilConsoleView extends Phobject {
+
+ private $console;
+
+ abstract protected function drawView();
+
+ final public function setConsole(PhutilConsole $console) {
+ $this->console = $console;
+ return $this;
+ }
+
+ final public function getConsole() {
+ if ($this->console) {
+ return $this->console;
+ }
+ return PhutilConsole::getConsole();
+ }
+
+
+ /**
+ * Draw a view to the console.
+ *
+ * @return this
+ * @task draw
+ */
+ final public function draw() {
+ $string = $this->drawConsoleString();
+
+ $console = $this->getConsole();
+ $console->writeOut('%s', $string);
+
+ return $this;
+ }
+
+
+ /**
+ * Draw a view to a string and return it.
+ *
+ * @return string Console-printable string.
+ * @task draw
+ */
+ final public function drawConsoleString() {
+ $view = $this->drawView();
+ $parts = $this->reduceView($view);
+
+ $out = array();
+ foreach ($parts as $part) {
+ $out[] = PhutilTerminalString::escapeStringValue($part, true);
+ }
+
+ return implode('', $out);
+ }
+
+
+ /**
+ * Reduce a view to a list of simple, unnested parts.
+ *
+ * @param wild Any drawable view.
+ * @return list<wild> List of unnested drawables.
+ * @task draw
+ */
+ private function reduceView($view) {
+ if ($view instanceof PhutilConsoleView) {
+ $view = $view->drawView();
+ return $this->reduceView($view);
+ }
+
+ if (is_array($view)) {
+ $parts = array();
+ foreach ($view as $item) {
+ foreach ($this->reduceView($item) as $part) {
+ $parts[] = $part;
+ }
+ }
+ return $parts;
+ }
+
+ return array($view);
+ }
+
+/* -( Drawing Utilities )-------------------------------------------------- */
+
+
+ /**
+ * @param list<wild> List of views, one per line.
+ * @return wild Each view rendered on a separate line.
+ */
+ final protected function drawLines(array $parts) {
+ $result = array();
+ foreach ($parts as $part) {
+ if ($part !== null) {
+ $result[] = $part;
+ $result[] = "\n";
+ }
+ }
+
+ return $result;
+ }
+
+ final protected function implode($separator, array $items) {
+ $result = array();
+ foreach ($items as $item) {
+ $result[] = $item;
+ $result[] = $separator;
+ }
+ array_pop($result);
+ return $result;
+ }
+
+}
diff --git a/src/xsprintf/tsprintf.php b/src/xsprintf/tsprintf.php
--- a/src/xsprintf/tsprintf.php
+++ b/src/xsprintf/tsprintf.php
@@ -15,6 +15,7 @@
*/
function tsprintf($pattern /* , ... */) {
$args = func_get_args();
+ $args[0] = PhutilConsoleFormatter::interpretFormat($args[0]);
$string = xsprintf('xsprintf_terminal', null, $args);
return new PhutilTerminalString($string);
}

File Metadata

Mime Type
text/plain
Expires
Sat, May 11, 12:54 AM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6266477
Default Alt Text
D14136.id34170.diff (14 KB)

Event Timeline