Why Sponsor Oils? | source | all docs for version 0.24.0 | all versions | oilshell.org
ul-table
is an HTML processor that lets you write tables as bulleted
lists, in Markdown:
<table>
- thead
- Shell
- Version
- tr
- bash
- 5.2
- tr
- OSH
- 0.25.0
</table>
Shell | Version |
---|---|
bash | 5.2 |
OSH | 0.25.0 |
I designed this format because it's tedious to read, write, and edit <tr>
and
<td>
and </td>
and </tr>
. Aligning columns is also tedious in HTML.
ul-table
does not involve new Markdown syntax, only a new interpretation.
This means your docs are still readable without it, e.g. on sourcehut or Github. It degrades gracefully.
Other design goals:
Let's add hyperlinks to our example, to make it more realistic.
Shell | Version |
---|---|
bash | 5.2 |
OSH | 0.25.0 |
ul-table
SyntaxYou can make this table with a two-level Markdown list, with Markdown hyperlink syntax:
<table> <!-- don't forget this tag -->
- thead
- Shell
- Version
- tr
- [bash](https://www.gnu.org/software/bash/)
- 5.2
- tr
- [OSH](https://www.oilshell.org/)
- 0.25.0
</table>
(This format looks similar to tables in reStructuredText).
It takes two steps to convert:
<table> <ul> <li> ... </li> </ul> </table>
structure.ul-table
plugin transforms that into a
<table> <tr> <td> </td> </tr> </table>
structure, which is a normal HTML
table.Here's the equivalent in CommonMark:
<table>
<thead>
<tr>
<td>Shell</td>
<td>Version</td>
</tr>
</thead>
<tr>
<td>
<!-- be careful not to indent this 4 spaces! -->
[bash](https://www.gnu.org/software/bash/)
</td>
<td>5.2</td>
</tr>
<tr>
<td>
[OSH](https://www.oilshell.org/)
</td>
<td>0.25.0</td>
</tr>
</table>
It uses the rule where you can embed Markdown inside HTML inside Markdown.
With ul-table
, you don't need this mutual nesting.
The text you have to write is also a lot shorter!
Trivia: with CommonMark, you also get an extra <p>
element:
<td>
<p>OSH</p>
</td>
ul-table
can produce simpler HTML:
<td>
OSH
</td>
To make the table look nice, I add a <style>
tag, inside Markdown:
<style>
table {
margin: 0 auto;
}
td {
padding: 0.2em;
}
</style>
If you omit the <table>
tags, then the rendered HTML looks like this:
This is how your tables will appear on sourcehut or Github, which don't (yet)
have ul-table
support. Remember, ul-table
is not an extension to
Markdown syntax.
HTML attributes like <tr class=foo>
and <td id=bar>
let you format and
style your table.
You can add attributes to cells, columns, and rows.
Add cell attributes with a cell-attrs
tag before the cell contents:
- thead
- Name
- Age
- tr
- Alice
- <cell-attrs class=num /> 42
It's important that cell-attrs
is a self-closing tag:
<cell-attrs /> # Yes
<cell-attrs> # No: this is an opening tag
How does this work? ul-table
takes the attributes from <cell-attrs />
, and
puts it on the generated <td>
.
Add attributes to every cell in a column by the same way, except in the
thead
section:
- thead
- Name
- <cell-attrs class=num /> Age
- tr
- Alice
- 42 # this cell gets class=num
- tr
- Bob
- 25 # this cells gets class=num
This is particularly useful for aligning numbers to the right:
<style>
.num {
text-align: right;
}
</style>
If the same attribute appears in the thead
and and a tr
section, the values
are concatenated, with a space. Example:
<td class="from-thead from-tr">
Add row attributes like this:
- thead
- Name
- Age
- tr
- Alice
- 42
- tr <row-attrs class="special-row />
- Bob
- 25
Here's an example that uses more features. Source code of this table: doc/ul-table.md.
Shell | Version | Example Code | ||||
---|---|---|---|---|---|---|
bash | 5.2 |
|
||||
dash | 1.5 | Inline HTML | ||||
mksh | 4.0 |
|
||||
zsh | 3.6 | Unordered List
|
||||
yash | 1.0 | Ordered List
|
||||
This is paragraph one. This is paragraph two |
Another cell with ... ... multiple paragraphs. |
Here are some quirks I ran into when creating ul-tables.
(1) CommonMark doesn't allow empty list items:
- thead
-
- above is not rendered as a list item
You can work around this by using a comment, or invisible character:
- tr
- <!-- empty -->
- above is OK
- tr
-
- also OK
As similar issue is that line breaks affect backtick expansion to <code>
:
- tr
- <cell-attrs /> <!-- we need something on this line -->
... More `proc` features ...
I think this is also because <cell-attrs />
doesn't "count" as text, so the
list item is considered empty.
(2) Likewise, a cell with a literal hyphen may need a comment or space in front of it:
- tr
- <!-- hyphen --> -
- -
Related discussions:
Github-flavored Markdown has an non-standard extension for tables:
This style is hard to read and write, especially with large tables:
| Command | Description |
| --- | --- |
| git status | List all new or modified files |
| git diff | Show file differences that haven't been staged |
Our style is less noisy, and more easily editable:
- thead
- Command
- Description
- tr
- git status
- List all new or modified files
- tr
- git diff
- Show file differences that haven't been staged
ul-table
is a nice way of writing and maintaining HTML tables. The appendix
has links and details.
TODO:
doctools/ul-table-test.ysh
markdown.pl
lazylex/html.py
can got in doctools/
data_lang/htm8.py
, if we can fix the lexerTODO:
ul-table
.
I converted the tables in these September posts to ul-table
:
The markup was much shorter and simpler after conversion!
TODO:
Tables to Make
Wiki pages could use conversion
<th>
is like <td>
, but it belongs in <thead><tr>
. Browsers make it
bold and centered.class=
on <colgroup>
and <col>
and align columns left and
right.
class=
on every <td>
cell instead.ul-table
solves this with "inherited" <cell-attrs />
in the thead
section.We could help users edit well-formed tables with enforced column names:
- thead
- <cell-attrs ult-name=name /> Name
- <cell-attrs ult-name=age /> Age
- tr
- <cell-attrs ult-name=name /> Hi
- <cell-attrs ult-name=age /> 5
This is a bit verbose, but may be worth it for large tables.
Less verbose syntax idea:
- thead
- <ult col=NAME /> <cell-attrs class=foo /> Name
- <ult col=AGE /> Age
- tr
- <ult col=NAME /> Hi
- <ult col=AGE /> 5
Even less verbose:
- thead
- {NAME} Name
- {AGE} Age
- tr
- {NAME} Hi
- {AGE} 5
The obvious problem is that we might want the literal text {NAME}
in the
header. It's unlikely, but possible.