1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-12-23 05:50:54 +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:
epriestley 2020-06-15 07:58:08 -07:00
parent 98bf58db4a
commit 33484b43c9
5 changed files with 392 additions and 0 deletions

View file

@ -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',

View 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;
}
}

View 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;
}
}

View 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];
}
}

View 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;
}
}