Page MenuHomePhabricator

Add a `PhutilConsoleTable` class for drawing tables.
ClosedPublic

Authored by joshuaspence on Jun 14 2014, 9:11 PM.
Tags
None
Referenced Files
F13311335: D9533.id.diff
Tue, Jun 11, 3:40 AM
F13302700: D9533.diff
Sat, Jun 8, 5:35 AM
F13289505: D9533.diff
Tue, Jun 4, 12:57 PM
F13274233: D9533.diff
Fri, May 31, 3:28 AM
F13270604: D9533.diff
Wed, May 29, 10:00 AM
F13260011: D9533.diff
Sun, May 26, 11:23 PM
F13256797: D9533.id22893.diff
Sat, May 25, 5:41 PM
F13251305: D9533.id22878.diff
Fri, May 24, 10:06 PM
Subscribers
Tokens
"Haypence" token, awarded by epriestley.

Details

Summary

Currently there is no easy way to draw a table. PhabricatorDaemonManagementStatusWorkflow draws a table in an ad-hoc way, but it would be useful to provide a more flexible mechanism to do so.

Test Plan
$table = id(new PhutilConsoleTable())
  ->addColumn('foo', array('title' => 'Foo', 'align' => 'right'))
  ->addColumn('bar', array('title' => 'Foobar', 'align' => 'center'))
  ->addColumn('baz', array('title' => '日本語'))

  ->addRow(array('foo' => 12345, 'bar' => 'foo', 'baz' => 'baz'))
  ->addRow(array('foo' => '汉语漢語', 'bar' => '한국어조선말', 'baz' => 'ру́сский язы́к'))
  ->addRow(array('foo' => 'aaa', 'bar' => '✓✗', 'baz' => 'dddddddddddd'))
  ->addRow(array('foo' => '', 'bar' => '', 'baz' => ''));

$table->setBorders(false)->draw();
echo "\n";
$table->setBorders(true)->draw();

Output

table.png (257×497 px, 25 KB)

Diff Detail

Repository
rPHU libphutil
Lint
Lint Skipped
Unit
Tests Skipped

Event Timeline

joshuaspence retitled this revision from to Add a `PhutilConsoleTable` class for drawing tables..
joshuaspence updated this object.
joshuaspence edited the test plan for this revision. (Show Details)
joshuaspence added a reviewer: epriestley.
joshuaspence edited edge metadata.

Allow tables to have borders. For example:

=============
| Foo | Bar |
-------------
| bar | foo |
=============
epriestley edited edge metadata.

When borders are enabled, it might be nice to draw the + style tables like MySQL? I think they look pretty good and they're probably familiar to a lot of users.

+---+
| N |
+---+
| 1 |
| 2 |
+---+

Major general issue is UTF8:

  • UTF8 characters may contain multiple bytes. You can use phutil_utf8_strlen() to get the number of characters in a string.
  • Some UTF8 characters take up two characters when displayed in a monospaced font. For example:

Screen_Shot_2014-06-14_at_2.48.57_PM.png (111×240 px, 11 KB)

  • ...you can use phutil_utf8_console_strlen() to get the number of monospaced characters a string requires to display.
  • Since sprintf() can't handle any of this, you'll have to write your own padding stuff.
  • Stuff like strrev() does not work in the general case.

Broadly:

  • It might be nice to specify a maximum table width and a column to truncate if the width is exceeded (or truncation widths per columns?). This could be in v2 or something. We can usually figure out how many columns are available with phutil_console_get_terminal_width(). In particular, this would make tables like "your open tasks", "your open revisions", etc., behave better when you have a task or revision that someone typed a million pages into the title of.
  • We could use this in some arc commands today -- notably, arc tasks does a (bad, nongeneral) job of this.
  • This is definitely a good thing overall and will make numerous commands cleaner and/or more useful.
src/console/PhutilConsoleTable.php
29

By convention, prefer to return $this; from set...() methods so they can be chained.

35

Use phutil_utf8_console_strlen() to get the display width of a UTF8 string in a monospaced font.

96–112

This won't do the right thing with multibyte and double-width characters. sprintf() probably can't be used anywhere in this.

139

Won't work with UTF8, and not sure what the use case is?

This revision now requires changes to proceed.Jun 14 2014, 9:54 PM
joshuaspence edited edge metadata.
  • Return $this.
  • MySQL-style tables.
  • Don't use strrev
  • Minor fixes
  • Minor tidying
  • Refactoring. Improved unicode support.

Here's a revised usage example:

$table = id(new PhutilConsoleTable())
  ->addColumn('foo', array('title' => 'Foo'))
  ->addColumn('bar', array('title' => 'Foobar'))
  ->addColumn('baz', array('title' => '日本語'))

  ->addData(array('foo' => 12345, 'bar' => 'foo', 'baz' => 'baz'))
  ->addData(array('foo' => '日本語', 'bar' => '日本語', 'baz' => '日本語'))
  ->addData(array('foo' => 'aaa', 'bar' => 'bbb', 'baz' => 'dddddddddddd'))


  ->setDrawBorders(true)
  ->draw();

Output

table.png (136×498 px, 11 KB)

A more complicated example:

$table = id(new PhutilConsoleTable())
  ->addColumn('foo', array('title' => 'Foo'))
  ->addColumn('bar', array('title' => 'Foobar'))
  ->addColumn('baz', array('title' => '日本語'))

  ->addData(array('foo' => 12345, 'bar' => 'foo', 'baz' => 'baz'))
  ->addData(array('foo' => '汉语漢語', 'bar' => '한국어조선말', 'baz' => 'ру́сский язы́к'))
  ->addData(array('foo' => 'aaa', 'bar' => '✓✗', 'baz' => 'dddddddddddd'))
  ->addData(array('foo' => '', 'bar' => '', 'baz' => ''));

$table->setDrawBorders(false)->draw();
echo "\n";
$table->setDrawBorders(true)->draw();

Output

table.png (276×513 px, 30 KB)

(Interestingly, it doesn't work properly with Russian characters. I think this is a bug with phutil_utf8_console_strlen?)

joshuaspence edited edge metadata.
  • Various minor improvements.
  • Allow custom alignment.
  • Fix line lengths.
  • Fix encoding.
  • Minor refactoring.
  • Header row in bold.
  • Add example usage.
  • Minor formatting changes.
  • Changed some methods from private to protected
  • Rename method.
epriestley edited edge metadata.

Three tiny stylistic nits -- any of these feel worth improving?

I'll see if I can fix phutil_utf8_console_strlen(), that does look like a bug.

src/console/PhutilConsoleTable.php
78

Maybe addRow()?

94–96

Maybe build the whole table, then write it out in one go, instead of writing it out bit-by-bit?

103

Maybe just - (like MySQL), instead of =?

This revision is now accepted and ready to land.Jun 15 2014, 12:39 PM

D9546 should fix the combining characters in the russian strings, if you still have that test case handy.

src/console/PhutilConsoleTable.php
103

I'm open to either, but I sort of prefer =. One reason is that I had considered having an addSeparator function which would insert a row of -. For example:

+=====+=====+
+ Foo | Bar |
+=====+=====+
+ foo | bar +
+ 1   | 2   +
+-----|-----+
+ foo | bar +
+=====|=====+
joshuaspence edited edge metadata.
  • Rename addData to addRow.
  • Write out table all at once.

(Let me just fix up the test plan before we land this)

epriestley updated this revision to Diff 22893.

Closed by commit rPHU1fa58627e797 (authored by @joshuaspence, committed by @epriestley).