mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-21 22:32:41 +01:00
Introduce "GridView", an updated version of "ConsoleTableView"
Summary: Ref T13546. In a future change, I'm providing a fancier version of "arc branches" that requires more sophisticated table rendering. Implement a new view which can do some fancier things, like handle alignment of multi-line table cells. Test Plan: See future changes. Maniphest Tasks: T13546 Differential Revision: https://secure.phabricator.com/D21360
This commit is contained in:
parent
98bf58db4a
commit
33484b43c9
5 changed files with 392 additions and 0 deletions
|
@ -228,6 +228,10 @@ phutil_register_library_map(array(
|
|||
'ArcanistGoLintLinterTestCase' => 'lint/linter/__tests__/ArcanistGoLintLinterTestCase.php',
|
||||
'ArcanistGoTestResultParser' => 'unit/parser/ArcanistGoTestResultParser.php',
|
||||
'ArcanistGoTestResultParserTestCase' => 'unit/parser/__tests__/ArcanistGoTestResultParserTestCase.php',
|
||||
'ArcanistGridCell' => 'console/grid/ArcanistGridCell.php',
|
||||
'ArcanistGridColumn' => 'console/grid/ArcanistGridColumn.php',
|
||||
'ArcanistGridRow' => 'console/grid/ArcanistGridRow.php',
|
||||
'ArcanistGridView' => 'console/grid/ArcanistGridView.php',
|
||||
'ArcanistHLintLinter' => 'lint/linter/ArcanistHLintLinter.php',
|
||||
'ArcanistHLintLinterTestCase' => 'lint/linter/__tests__/ArcanistHLintLinterTestCase.php',
|
||||
'ArcanistHardpoint' => 'hardpoint/ArcanistHardpoint.php',
|
||||
|
@ -1246,6 +1250,10 @@ phutil_register_library_map(array(
|
|||
'ArcanistGoLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistGoTestResultParser' => 'ArcanistTestResultParser',
|
||||
'ArcanistGoTestResultParserTestCase' => 'PhutilTestCase',
|
||||
'ArcanistGridCell' => 'Phobject',
|
||||
'ArcanistGridColumn' => 'Phobject',
|
||||
'ArcanistGridRow' => 'Phobject',
|
||||
'ArcanistGridView' => 'Phobject',
|
||||
'ArcanistHLintLinter' => 'ArcanistExternalLinter',
|
||||
'ArcanistHLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
|
||||
'ArcanistHardpoint' => 'Phobject',
|
||||
|
|
56
src/console/grid/ArcanistGridCell.php
Normal file
56
src/console/grid/ArcanistGridCell.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistGridCell
|
||||
extends Phobject {
|
||||
|
||||
private $key;
|
||||
private $content;
|
||||
private $contentWidth;
|
||||
private $contentHeight;
|
||||
|
||||
public function setKey($key) {
|
||||
$this->key = $key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getKey() {
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function setContent($content) {
|
||||
$this->content = $content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContent() {
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function getContentDisplayWidth() {
|
||||
$lines = $this->getContentDisplayLines();
|
||||
|
||||
$width = 0;
|
||||
foreach ($lines as $line) {
|
||||
$width = max($width, phutil_utf8_console_strlen($line));
|
||||
}
|
||||
|
||||
return $width;
|
||||
}
|
||||
|
||||
public function getContentDisplayLines() {
|
||||
$content = $this->getContent();
|
||||
$content = tsprintf('%B', $content);
|
||||
$content = phutil_string_cast($content);
|
||||
|
||||
$lines = phutil_split_lines($content, false);
|
||||
|
||||
$result = array();
|
||||
foreach ($lines as $line) {
|
||||
$result[] = tsprintf('%R', $line);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
}
|
41
src/console/grid/ArcanistGridColumn.php
Normal file
41
src/console/grid/ArcanistGridColumn.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistGridColumn
|
||||
extends Phobject {
|
||||
|
||||
private $key;
|
||||
private $alignment = self::ALIGNMENT_LEFT;
|
||||
private $displayWidth;
|
||||
|
||||
const ALIGNMENT_LEFT = 'align.left';
|
||||
const ALIGNMENT_CENTER = 'align.center';
|
||||
const ALIGNMENT_RIGHT = 'align.right';
|
||||
|
||||
public function setKey($key) {
|
||||
$this->key = $key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getKey() {
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function setAlignment($alignment) {
|
||||
$this->alignment = $alignment;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAlignment() {
|
||||
return $this->alignment;
|
||||
}
|
||||
|
||||
public function setDisplayWidth($display_width) {
|
||||
$this->displayWidth = $display_width;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDisplayWidth() {
|
||||
return $this->displayWidth;
|
||||
}
|
||||
|
||||
}
|
40
src/console/grid/ArcanistGridRow.php
Normal file
40
src/console/grid/ArcanistGridRow.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistGridRow
|
||||
extends Phobject {
|
||||
|
||||
private $cells;
|
||||
|
||||
public function setCells(array $cells) {
|
||||
$cells = id(new PhutilArrayCheck())
|
||||
->setInstancesOf('ArcanistGridCell')
|
||||
->setUniqueMethod('getKey')
|
||||
->setContext($this, 'setCells')
|
||||
->checkValue($cells);
|
||||
|
||||
$this->cells = $cells;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCells() {
|
||||
return $this->cells;
|
||||
}
|
||||
|
||||
public function hasCell($key) {
|
||||
return isset($this->cells[$key]);
|
||||
}
|
||||
|
||||
public function getCell($key) {
|
||||
if (!isset($this->cells[$key])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Row has no cell "%s".\n',
|
||||
$key));
|
||||
}
|
||||
|
||||
return $this->cells[$key];
|
||||
}
|
||||
|
||||
|
||||
}
|
247
src/console/grid/ArcanistGridView.php
Normal file
247
src/console/grid/ArcanistGridView.php
Normal file
|
@ -0,0 +1,247 @@
|
|||
<?php
|
||||
|
||||
final class ArcanistGridView
|
||||
extends Phobject {
|
||||
|
||||
private $rows = array();
|
||||
private $columns = array();
|
||||
|
||||
private $displayWidths = array();
|
||||
|
||||
public function setColumns(array $columns) {
|
||||
assert_instances_of($columns, 'ArcanistGridColumn');
|
||||
$this->columns = $columns;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getColumns() {
|
||||
return $this->columns;
|
||||
}
|
||||
|
||||
public function newColumn($key) {
|
||||
$column = id(new ArcanistGridColumn())
|
||||
->setKey($key);
|
||||
|
||||
$this->columns[$key] = $column;
|
||||
|
||||
return $column;
|
||||
}
|
||||
|
||||
public function newRow(array $cells) {
|
||||
assert_instances_of($cells, 'ArcanistGridCell');
|
||||
|
||||
$row = id(new ArcanistGridRow())
|
||||
->setCells($cells);
|
||||
|
||||
$this->rows[] = $row;
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
public function drawGrid() {
|
||||
$columns = $this->getColumns();
|
||||
if (!$columns) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Can not draw a grid with no columns!'));
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
foreach ($this->rows as $row) {
|
||||
$rows[] = $this->drawRow($row);
|
||||
}
|
||||
|
||||
$rows = phutil_glue($rows, tsprintf("\n"));
|
||||
|
||||
return tsprintf("%s\n", $rows);
|
||||
}
|
||||
|
||||
private function getDisplayWidth($key) {
|
||||
if (!isset($this->displayWidths[$key])) {
|
||||
$column = $this->getColumn($key);
|
||||
|
||||
$width = $column->getDisplayWidth();
|
||||
if ($width === null) {
|
||||
$width = 1;
|
||||
foreach ($this->getRows() as $row) {
|
||||
if (!$row->hasCell($key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$cell = $row->getCell($key);
|
||||
$width = max($width, $cell->getContentDisplayWidth());
|
||||
}
|
||||
}
|
||||
|
||||
$this->displayWidths[$key] = $width;
|
||||
}
|
||||
|
||||
return $this->displayWidths[$key];
|
||||
}
|
||||
|
||||
public function getColumn($key) {
|
||||
if (!isset($this->columns[$key])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Grid has no column "%s".',
|
||||
$key));
|
||||
}
|
||||
|
||||
return $this->columns[$key];
|
||||
}
|
||||
|
||||
public function getRows() {
|
||||
return $this->rows;
|
||||
}
|
||||
|
||||
private function drawRow(ArcanistGridRow $row) {
|
||||
$columns = $this->getColumns();
|
||||
|
||||
$cells = $row->getCells();
|
||||
|
||||
$out = array();
|
||||
$widths = array();
|
||||
foreach ($columns as $column_key => $column) {
|
||||
$display_width = $this->getDisplayWidth($column_key);
|
||||
|
||||
$cell = idx($cells, $column_key);
|
||||
if ($cell) {
|
||||
$content = $cell->getContentDisplayLines();
|
||||
} else {
|
||||
$content = array('');
|
||||
}
|
||||
|
||||
foreach ($content as $line_key => $line) {
|
||||
$line_width = phutil_utf8_console_strlen($line);
|
||||
|
||||
if ($line_width === $display_width) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($line_width < $display_width) {
|
||||
$line = $this->padContentLineToWidth(
|
||||
$line,
|
||||
$line_width,
|
||||
$display_width,
|
||||
$column->getAlignment());
|
||||
} else if ($line_width > $display_width) {
|
||||
$line = $this->truncateContentLineToWidth(
|
||||
$line,
|
||||
$line_width,
|
||||
$display_width,
|
||||
$column->getAlignment());
|
||||
}
|
||||
|
||||
$content[$line_key] = $line;
|
||||
}
|
||||
|
||||
$out[] = $content;
|
||||
$widths[] = $display_width;
|
||||
}
|
||||
|
||||
return $this->drawRowLayout($out, $widths);
|
||||
}
|
||||
|
||||
private function drawRowLayout(array $raw_cells, array $display_widths) {
|
||||
$line_count = 0;
|
||||
foreach ($raw_cells as $key => $cells) {
|
||||
$raw_cells[$key] = array_values($cells);
|
||||
$line_count = max($line_count, count($cells));
|
||||
}
|
||||
|
||||
$line_head = '';
|
||||
$cell_separator = ' ';
|
||||
$line_tail = '';
|
||||
|
||||
$out = array();
|
||||
$cell_count = count($raw_cells);
|
||||
for ($ii = 0; $ii < $line_count; $ii++) {
|
||||
$line = array();
|
||||
for ($jj = 0; $jj < $cell_count; $jj++) {
|
||||
if (isset($raw_cells[$jj][$ii])) {
|
||||
$raw_line = $raw_cells[$jj][$ii];
|
||||
} else {
|
||||
$display_width = $display_widths[$jj];
|
||||
$raw_line = str_repeat(' ', $display_width);
|
||||
}
|
||||
$line[] = $raw_line;
|
||||
}
|
||||
|
||||
$line = array(
|
||||
$line_head,
|
||||
phutil_glue($line, $cell_separator),
|
||||
$line_tail,
|
||||
);
|
||||
|
||||
$out[] = $line;
|
||||
}
|
||||
|
||||
$out = phutil_glue($out, tsprintf("\n"));
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
private function padContentLineToWidth(
|
||||
$line,
|
||||
$src_width,
|
||||
$dst_width,
|
||||
$alignment) {
|
||||
|
||||
$delta = ($dst_width - $src_width);
|
||||
|
||||
switch ($alignment) {
|
||||
case ArcanistGridColumn::ALIGNMENT_LEFT:
|
||||
$head = null;
|
||||
$tail = str_repeat(' ', $delta);
|
||||
break;
|
||||
case ArcanistGridColumn::ALIGNMENT_CENTER:
|
||||
$head_delta = (int)floor($delta / 2);
|
||||
$tail_delta = (int)ceil($delta / 2);
|
||||
|
||||
if ($head_delta) {
|
||||
$head = str_repeat(' ', $head_delta);
|
||||
} else {
|
||||
$head = null;
|
||||
}
|
||||
|
||||
if ($tail_delta) {
|
||||
$tail = str_repeat(' ', $tail_delta);
|
||||
} else {
|
||||
$tail = null;
|
||||
}
|
||||
break;
|
||||
case ArcanistGridColumn::ALIGNMENT_RIGHT:
|
||||
$head = str_repeat(' ', $delta);
|
||||
$tail = null;
|
||||
break;
|
||||
default:
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unknown column alignment "%s".',
|
||||
$alignment));
|
||||
}
|
||||
|
||||
$result = array();
|
||||
|
||||
if ($head !== null) {
|
||||
$result[] = $head;
|
||||
}
|
||||
|
||||
$result[] = $line;
|
||||
|
||||
if ($tail !== null) {
|
||||
$result[] = $tail;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function truncateContentLineToWidth(
|
||||
$line,
|
||||
$src_width,
|
||||
$dst_width,
|
||||
$alignment) {
|
||||
return $line;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue