tessella 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +900 -0
- data/doc/README.md +74 -0
- data/doc/bitfield.md +123 -0
- data/doc/charts.md +1370 -0
- data/doc/check.rb +75 -0
- data/doc/cli.md +483 -0
- data/doc/composition.md +599 -0
- data/doc/getting-started.md +121 -0
- data/doc/glyphs.md +134 -0
- data/doc/images.md +325 -0
- data/doc/library.md +239 -0
- data/doc/lint.md +270 -0
- data/doc/meter.md +210 -0
- data/doc/rule.md +51 -0
- data/doc/seq.md +169 -0
- data/doc/sparkline.md +63 -0
- data/doc/table.md +347 -0
- data/doc/targets.md +222 -0
- data/doc/text.md +84 -0
- data/doc/tree.md +108 -0
- data/doc/wave.md +122 -0
- data/examples/README.md +103 -0
- data/examples/ages.txt +9 -0
- data/examples/bad.csv +3 -0
- data/examples/boxes.txt +3 -0
- data/examples/broken.txt +4 -0
- data/examples/calibration.rb +12 -0
- data/examples/cities.csv +4 -0
- data/examples/cjk.rb +13 -0
- data/examples/comment.rb +9 -0
- data/examples/comment.sh +13 -0
- data/examples/cpu.txt +60 -0
- data/examples/dashboard.sh +25 -0
- data/examples/datasheet.rb +16 -0
- data/examples/disc.pgm +0 -0
- data/examples/disks.sh +18 -0
- data/examples/disks.txt +5 -0
- data/examples/dose.txt +7 -0
- data/examples/drift.txt +4 -0
- data/examples/ethernet.txt +3 -0
- data/examples/figure.c +5 -0
- data/examples/fit.rb +16 -0
- data/examples/floors.rb +21 -0
- data/examples/gallery.sh +20 -0
- data/examples/gauges.sh +31 -0
- data/examples/gradient.rb +16 -0
- data/examples/growth.txt +6 -0
- data/examples/hosts.csv +4 -0
- data/examples/icmp.txt +3 -0
- data/examples/ipv4.txt +12 -0
- data/examples/kinds.csv +4 -0
- data/examples/langs.csv +4 -0
- data/examples/lint.rb +16 -0
- data/examples/load.rb +14 -0
- data/examples/login.txt +10 -0
- data/examples/neg.csv +4 -0
- data/examples/normal.txt +200 -0
- data/examples/note.txt +2 -0
- data/examples/notes-fixed.md +17 -0
- data/examples/notes.md +17 -0
- data/examples/ohlc.txt +7 -0
- data/examples/os.csv +10 -0
- data/examples/outline.txt +8 -0
- data/examples/plot.txt +5 -0
- data/examples/refusals.rb +17 -0
- data/examples/report.sh +21 -0
- data/examples/rfc.sh +22 -0
- data/examples/samples.txt +7 -0
- data/examples/shapes.pgm +0 -0
- data/examples/sizes.tsv +3 -0
- data/examples/spi.txt +6 -0
- data/examples/status.sh +23 -0
- data/examples/stem.txt +12 -0
- data/examples/table-drift.txt +4 -0
- data/examples/temps.txt +31 -0
- data/examples/trig.sh +13 -0
- data/examples/trig.txt +102 -0
- data/examples/week.txt +8 -0
- data/examples/wide.txt +2 -0
- data/exe/tessella +6 -0
- data/lib/tessella/axis.rb +71 -0
- data/lib/tessella/bitfield.rb +194 -0
- data/lib/tessella/block.rb +56 -0
- data/lib/tessella/borders.rb +85 -0
- data/lib/tessella/canvas/area.rb +155 -0
- data/lib/tessella/canvas/ascii.rb +120 -0
- data/lib/tessella/canvas/block.rb +40 -0
- data/lib/tessella/canvas/braille.rb +30 -0
- data/lib/tessella/canvas/clip.rb +49 -0
- data/lib/tessella/canvas/density.rb +51 -0
- data/lib/tessella/canvas/dot.rb +27 -0
- data/lib/tessella/canvas/eighths.rb +43 -0
- data/lib/tessella/canvas/half.rb +51 -0
- data/lib/tessella/canvas/sextant.rb +36 -0
- data/lib/tessella/canvas.rb +168 -0
- data/lib/tessella/cell.rb +10 -0
- data/lib/tessella/chart.rb +211 -0
- data/lib/tessella/check.rb +52 -0
- data/lib/tessella/cli/bitfield.rb +121 -0
- data/lib/tessella/cli/chart.rb +615 -0
- data/lib/tessella/cli/chart_candle.rb +36 -0
- data/lib/tessella/cli/chart_heatmap.rb +21 -0
- data/lib/tessella/cli/chart_help.rb +85 -0
- data/lib/tessella/cli/chart_pie.rb +38 -0
- data/lib/tessella/cli/compose.rb +156 -0
- data/lib/tessella/cli/compose_options.rb +259 -0
- data/lib/tessella/cli/glyphs.rb +97 -0
- data/lib/tessella/cli/image.rb +250 -0
- data/lib/tessella/cli/input.rb +44 -0
- data/lib/tessella/cli/iso_time.rb +28 -0
- data/lib/tessella/cli/lint.rb +102 -0
- data/lib/tessella/cli/lint_options.rb +99 -0
- data/lib/tessella/cli/main.rb +121 -0
- data/lib/tessella/cli/meter.rb +80 -0
- data/lib/tessella/cli/rule.rb +57 -0
- data/lib/tessella/cli/seq.rb +117 -0
- data/lib/tessella/cli/sparkline.rb +72 -0
- data/lib/tessella/cli/table.rb +167 -0
- data/lib/tessella/cli/text.rb +78 -0
- data/lib/tessella/cli/tree.rb +100 -0
- data/lib/tessella/cli/wave.rb +148 -0
- data/lib/tessella/cli.rb +36 -0
- data/lib/tessella/color.rb +106 -0
- data/lib/tessella/compose.rb +243 -0
- data/lib/tessella/compose_fill.rb +176 -0
- data/lib/tessella/compose_grid.rb +36 -0
- data/lib/tessella/compose_shadow.rb +41 -0
- data/lib/tessella/error.rb +13 -0
- data/lib/tessella/frame.rb +170 -0
- data/lib/tessella/glyphs.rb +106 -0
- data/lib/tessella/image.rb +454 -0
- data/lib/tessella/image_octant.rb +46 -0
- data/lib/tessella/image_petscii.rb +116 -0
- data/lib/tessella/kinds/bar.rb +344 -0
- data/lib/tessella/kinds/box.rb +213 -0
- data/lib/tessella/kinds/box_upright.rb +110 -0
- data/lib/tessella/kinds/candlestick.rb +164 -0
- data/lib/tessella/kinds/columns.rb +259 -0
- data/lib/tessella/kinds/date_axis.rb +102 -0
- data/lib/tessella/kinds/error_bars.rb +71 -0
- data/lib/tessella/kinds/heatmap.rb +130 -0
- data/lib/tessella/kinds/line.rb +173 -0
- data/lib/tessella/kinds/multibar.rb +98 -0
- data/lib/tessella/kinds/multibar_columns.rb +130 -0
- data/lib/tessella/kinds/multibar_fine.rb +45 -0
- data/lib/tessella/kinds/multibar_rows.rb +93 -0
- data/lib/tessella/kinds/pie.rb +123 -0
- data/lib/tessella/kinds/pie_rim.rb +78 -0
- data/lib/tessella/kinds/sideways.rb +62 -0
- data/lib/tessella/kinds/stem.rb +134 -0
- data/lib/tessella/kinds/threshold.rb +73 -0
- data/lib/tessella/legend.rb +31 -0
- data/lib/tessella/lint/alphabet.rb +56 -0
- data/lib/tessella/lint/drift.rb +82 -0
- data/lib/tessella/lint/fences.rb +51 -0
- data/lib/tessella/lint/geometry.rb +120 -0
- data/lib/tessella/lint/geometry_checks.rb +224 -0
- data/lib/tessella/lint/lines.rb +115 -0
- data/lib/tessella/lint/mixing.rb +61 -0
- data/lib/tessella/lint/runner.rb +73 -0
- data/lib/tessella/lint/shadows.rb +120 -0
- data/lib/tessella/lint/target_rows.rb +66 -0
- data/lib/tessella/lint.rb +158 -0
- data/lib/tessella/meter.rb +133 -0
- data/lib/tessella/meter_upright.rb +43 -0
- data/lib/tessella/png.rb +456 -0
- data/lib/tessella/raster.rb +92 -0
- data/lib/tessella/rmagick_reader.rb +52 -0
- data/lib/tessella/rule.rb +59 -0
- data/lib/tessella/seq.rb +522 -0
- data/lib/tessella/sparkline.rb +57 -0
- data/lib/tessella/stats.rb +199 -0
- data/lib/tessella/table.rb +547 -0
- data/lib/tessella/table_cells.rb +225 -0
- data/lib/tessella/target.rb +105 -0
- data/lib/tessella/text.rb +76 -0
- data/lib/tessella/text_forms.rb +150 -0
- data/lib/tessella/tree.rb +221 -0
- data/lib/tessella/version.rb +5 -0
- data/lib/tessella/wave.rb +353 -0
- data/lib/tessella/width.rb +122 -0
- data/lib/tessella/width_class.rb +77 -0
- data/lib/tessella/width_invisible.rb +66 -0
- data/lib/tessella/width_invisible_high.rb +60 -0
- data/lib/tessella.rb +94 -0
- metadata +226 -0
data/doc/composition.md
ADDED
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
# Composition
|
|
2
|
+
|
|
3
|
+
Every figure tessella draws can become a **block**: lines of text,
|
|
4
|
+
measured in display columns. Composition takes blocks (or anything
|
|
5
|
+
that can become one) and returns a new block, so figures nest: a
|
|
6
|
+
chart beside a table, framed, captioned, indented into a comment.
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
# frozen_string_literal: true
|
|
10
|
+
|
|
11
|
+
# examples/load.rb: a bar chart and a table side by side, framed and
|
|
12
|
+
# captioned -- composition of any two figures.
|
|
13
|
+
require "tessella"
|
|
14
|
+
|
|
15
|
+
rows = Tessella::Table.parse(File.read("langs.csv"))
|
|
16
|
+
labels = rows.drop(1).map(&:first)
|
|
17
|
+
values = rows.drop(1).map { Integer(it[1], exception: false) || it[1].to_f }
|
|
18
|
+
plot = Tessella.bar(labels, values, width: 20)
|
|
19
|
+
table = Tessella.table(rows, style: :rounded)
|
|
20
|
+
fig = Tessella.beside(plot, table, gap: 2, align: :middle)
|
|
21
|
+
puts Tessella.caption(Tessella.frame(fig, title: "Load"),
|
|
22
|
+
text: "Figure 1: lines of code, in thousands")
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
$ ruby load.rb
|
|
27
|
+
┌───────────────────── Load ─────────────────────┐
|
|
28
|
+
│ ╭──────┬─────╮ │
|
|
29
|
+
│ ┌ ┐ │ lang │ loc │ │
|
|
30
|
+
│ ruby ┤██████▍········· 12 ├──────┼─────┤ │
|
|
31
|
+
│ c ┤████████████████ 30 │ ruby │ 12 │ │
|
|
32
|
+
│ rust ┤████············ 7.5 │ c │ 30 │ │
|
|
33
|
+
│ └ ┘ │ rust │ 7.5 │ │
|
|
34
|
+
│ ╰──────┴─────╯ │
|
|
35
|
+
└────────────────────────────────────────────────┘
|
|
36
|
+
|
|
37
|
+
Figure 1: lines of code, in thousands
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Blocks
|
|
41
|
+
|
|
42
|
+
`Tessella::Block` is a finished figure:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
block.lines # => Array of String, no trailing blank on any
|
|
46
|
+
block.width # => display columns of the widest line
|
|
47
|
+
block.height # => number of lines
|
|
48
|
+
block.to_s # => the lines joined, "\n"-terminated
|
|
49
|
+
block.check_width!(78) # ArgumentError naming the line, if wider
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Every composition function accepts a figure in any form
|
|
53
|
+
`Block.from` takes: a `Block`, a `String` of lines, or a chart (any
|
|
54
|
+
object whose `to_s` is the figure). The diagram factories
|
|
55
|
+
(`Tessella.tree`, `.table`, `.bitfield`, `.wave`, `.seq`, `.image`)
|
|
56
|
+
already return blocks.
|
|
57
|
+
|
|
58
|
+
Three rules hold for every block:
|
|
59
|
+
|
|
60
|
+
- **Width is display width.** A CJK character counts two columns, a
|
|
61
|
+
combining mark none, an ANSI colour code none, so a coloured or
|
|
62
|
+
CJK-labelled figure lines up by what it shows.
|
|
63
|
+
- **A block is never rewritten, only padded** on the right, so its
|
|
64
|
+
own alignment survives every composition. `stack`'s `center:` is
|
|
65
|
+
the one shift, and it moves a whole block at once.
|
|
66
|
+
- **What has no width of its own is refused.** A tab (its width
|
|
67
|
+
belongs to the viewer) and an empty block raise `ArgumentError`;
|
|
68
|
+
trailing blank lines are dropped.
|
|
69
|
+
|
|
70
|
+
## frame
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
Tessella.frame(fig, title: nil, style: nil, pad: 1, fill: nil,
|
|
74
|
+
ascii: false)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Draws a border around one figure, with an optional title centred in
|
|
78
|
+
its top edge. `pad:` is the blank columns on each side inside the
|
|
79
|
+
border; the frame hugs its content vertically.
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
$ tessella compose frame --title Notes --style rounded note.txt
|
|
83
|
+
╭───── Notes ─────╮
|
|
84
|
+
│ ruby is 12 kLOC │
|
|
85
|
+
│ c is the bulk │
|
|
86
|
+
╰─────────────────╯
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
$ tessella compose frame --pad 0 note.txt
|
|
91
|
+
┌───────────────┐
|
|
92
|
+
│ruby is 12 kLOC│
|
|
93
|
+
│c is the bulk │
|
|
94
|
+
└───────────────┘
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The styles are `:light` (the default), `:heavy`, `:double`,
|
|
98
|
+
`:rounded`, the four dashed ones `:dashed` (`┄┆`), `:dashed_fine`
|
|
99
|
+
(`┈┊`), `:heavy_dashed` (`┅┇`) and `:heavy_dashed_fine` (`┉┋`), the
|
|
100
|
+
two mixed doubles `:double_h` (double horizontals, `╒═╕`) and
|
|
101
|
+
`:double_v` (double verticals, `╓─╖`), and `:ascii`. A name may say
|
|
102
|
+
`-` for `_`, and `tessella glyphs border` lists them all with their
|
|
103
|
+
glyphs ([glyph sets](glyphs.md)). Four of them:
|
|
104
|
+
|
|
105
|
+
```text
|
|
106
|
+
$ tessella compose frame --style heavy note.txt && \
|
|
107
|
+
tessella compose frame --style double note.txt
|
|
108
|
+
┏━━━━━━━━━━━━━━━━━┓
|
|
109
|
+
┃ ruby is 12 kLOC ┃
|
|
110
|
+
┃ c is the bulk ┃
|
|
111
|
+
┗━━━━━━━━━━━━━━━━━┛
|
|
112
|
+
╔═════════════════╗
|
|
113
|
+
║ ruby is 12 kLOC ║
|
|
114
|
+
║ c is the bulk ║
|
|
115
|
+
╚═════════════════╝
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
$ tessella compose frame --style dashed note.txt && \
|
|
120
|
+
tessella compose frame --style double-h note.txt
|
|
121
|
+
┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
|
|
122
|
+
┆ ruby is 12 kLOC ┆
|
|
123
|
+
┆ c is the bulk ┆
|
|
124
|
+
└┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
|
|
125
|
+
╒═════════════════╕
|
|
126
|
+
│ ruby is 12 kLOC │
|
|
127
|
+
│ c is the bulk │
|
|
128
|
+
╘═════════════════╛
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Custom borders
|
|
132
|
+
|
|
133
|
+
`style:` also takes a set of your own: a String of six glyphs in the
|
|
134
|
+
order `tl tr bl br h v` (top-left, top-right, bottom-left,
|
|
135
|
+
bottom-right, horizontal, vertical), or a
|
|
136
|
+
`Tessella::Borders::Set.new(tl, tr, bl, br, h, v)`. A String that
|
|
137
|
+
names a style is that style. Each glyph must take one column.
|
|
138
|
+
|
|
139
|
+
```text
|
|
140
|
+
$ tessella compose frame --style '╓╖╙╜─║' --title Notes note.txt
|
|
141
|
+
╓───── Notes ─────╖
|
|
142
|
+
║ ruby is 12 kLOC ║
|
|
143
|
+
║ c is the bulk ║
|
|
144
|
+
╙─────────────────╜
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Under `ascii: true` a custom set is drawn if every glyph is ASCII, and
|
|
148
|
+
refused otherwise: it is never quietly replaced by `ascii`.
|
|
149
|
+
|
|
150
|
+
```text
|
|
151
|
+
$ tessella compose frame --ascii --style '****=!' note.txt
|
|
152
|
+
*=================*
|
|
153
|
+
! ruby is 12 kLOC !
|
|
154
|
+
! c is the bulk !
|
|
155
|
+
*=================*
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
<!-- tessella: allow-mixed -->
|
|
159
|
+
```text
|
|
160
|
+
$ tessella compose frame --ascii --style '╓╖╙╜─║' note.txt
|
|
161
|
+
tessella compose: border glyph ╓ conflicts with ascii: true
|
|
162
|
+
Usage: tessella compose frame [--title T] [--style S] [--pad N]
|
|
163
|
+
[--fill P] [--halo H] BLOCK
|
|
164
|
+
tessella compose shadow [--glyph G] BLOCK
|
|
165
|
+
tessella compose fill [--pattern P] [--halo H] BLOCK
|
|
166
|
+
tessella compose grid [--columns N] [--gap N] [--row-gap N]
|
|
167
|
+
[--align A] BLOCK...
|
|
168
|
+
tessella compose beside [--gap N] [--align A] BLOCK...
|
|
169
|
+
tessella compose stack [--gap N] [--rule C] [--center] BLOCK...
|
|
170
|
+
tessella compose indent --prefix STR [--suffix STR] BLOCK
|
|
171
|
+
tessella compose caption --text T [--above] BLOCK
|
|
172
|
+
Every subcommand also takes --[no-]ascii and --max-width N.
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
A figure framed in a custom set can still be checked by
|
|
176
|
+
[`tessella lint --geometry`](lint.md#geometry), given the same
|
|
177
|
+
glyphs with `--border`.
|
|
178
|
+
|
|
179
|
+
## beside
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
Tessella.beside(fig, fig, ..., gap: 1, align: :top, ascii: false)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Sets two or more figures side by side, `gap:` columns apart. A
|
|
186
|
+
shorter figure is placed by `align:`: `:top`, `:middle` or `:bottom`.
|
|
187
|
+
`plot.txt` is the output of `tessella chart --kind bar --width 20
|
|
188
|
+
langs.csv`.
|
|
189
|
+
|
|
190
|
+
```text
|
|
191
|
+
$ tessella compose beside --gap 3 --align middle plot.txt note.txt
|
|
192
|
+
┌ ┐
|
|
193
|
+
ruby ┤██████▍········· 12 ruby is 12 kLOC
|
|
194
|
+
c ┤████████████████ 30 c is the bulk
|
|
195
|
+
rust ┤████············ 7.5
|
|
196
|
+
└ ┘
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## grid
|
|
200
|
+
|
|
201
|
+
```ruby
|
|
202
|
+
Tessella.grid(fig, fig, ..., columns: 2, gap: 2, row_gap: 1,
|
|
203
|
+
align: :top, ascii: false)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Lays figures out in rows of `columns:`, each row side by side and
|
|
207
|
+
the rows stacked, every column as wide as its widest figure: a
|
|
208
|
+
dashboard of charts, tables, meters and sparklines. A short last row
|
|
209
|
+
stays short. `gap:` is the blank columns between figures in a row,
|
|
210
|
+
`row_gap:` the blank rows between rows, and `align:` places a shorter
|
|
211
|
+
figure in its row, as `beside` does.
|
|
212
|
+
|
|
213
|
+
```sh
|
|
214
|
+
#!/bin/sh
|
|
215
|
+
# examples/dashboard.sh -- a dashboard: a chart, a table, meters and a
|
|
216
|
+
# sparkline, each drawn to a file and laid out two to a row by
|
|
217
|
+
# `compose grid`, then framed.
|
|
218
|
+
# usage: sh dashboard.sh
|
|
219
|
+
set -eu
|
|
220
|
+
|
|
221
|
+
cd -- "$(dirname -- "$0")" || exit 1
|
|
222
|
+
|
|
223
|
+
tmpdir=
|
|
224
|
+
cleanup() { [ -z "$tmpdir" ] || rm -rf -- "$tmpdir"; }
|
|
225
|
+
trap cleanup EXIT INT HUP TERM
|
|
226
|
+
tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/dashboard.XXXXXX") || exit 1
|
|
227
|
+
|
|
228
|
+
tessella chart --kind bar --width 16 langs.csv > "$tmpdir/1"
|
|
229
|
+
tessella table --style rounded langs.csv > "$tmpdir/2"
|
|
230
|
+
{
|
|
231
|
+
tessella meter --width 12 --title cpu --title-width 4 0.62
|
|
232
|
+
tessella meter --width 12 --title disk --title-width 4 0.87
|
|
233
|
+
} > "$tmpdir/3"
|
|
234
|
+
tessella sparkline --width 20 < cpu.txt |
|
|
235
|
+
tessella compose caption --text 'cpu, last hour' - > "$tmpdir/4"
|
|
236
|
+
tessella compose grid --columns 2 --gap 3 --align middle \
|
|
237
|
+
"$tmpdir/1" "$tmpdir/2" "$tmpdir/3" "$tmpdir/4" |
|
|
238
|
+
tessella compose frame --title status -
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
```text
|
|
242
|
+
$ sh dashboard.sh
|
|
243
|
+
┌──────────────────── status ─────────────────────┐
|
|
244
|
+
│ ╭──────┬─────╮ │
|
|
245
|
+
│ ┌ ┐ │ lang │ loc │ │
|
|
246
|
+
│ ruby ┤████▊······· 12 ├──────┼─────┤ │
|
|
247
|
+
│ c ┤████████████ 30 │ ruby │ 12 │ │
|
|
248
|
+
│ rust ┤███········· 7.5 │ c │ 30 │ │
|
|
249
|
+
│ └ ┘ │ rust │ 7.5 │ │
|
|
250
|
+
│ ╰──────┴─────╯ │
|
|
251
|
+
│ │
|
|
252
|
+
│ cpu [███████▌····] 62 % ▅▇██▇▆▄▃▂▁▁▃▄▆▇██▇▅▄ │
|
|
253
|
+
│ disk [██████████▌·] 87 % │
|
|
254
|
+
│ cpu, last hour │
|
|
255
|
+
└─────────────────────────────────────────────────┘
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## stack
|
|
259
|
+
|
|
260
|
+
```ruby
|
|
261
|
+
Tessella.stack(fig, fig, ..., gap: 0, rule: nil, center: false,
|
|
262
|
+
ascii: false)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Sets two or more figures one under another. `gap:` is the number of
|
|
266
|
+
blank lines between two figures. `rule:` is one character, drawn as a
|
|
267
|
+
line as wide as the widest figure, with `gap:` blank lines on each
|
|
268
|
+
side of it. `center:` centres each narrower figure as a whole.
|
|
269
|
+
|
|
270
|
+
```text
|
|
271
|
+
$ tessella compose stack --rule '─' --center plot.txt note.txt
|
|
272
|
+
┌ ┐
|
|
273
|
+
ruby ┤██████▍········· 12
|
|
274
|
+
c ┤████████████████ 30
|
|
275
|
+
rust ┤████············ 7.5
|
|
276
|
+
└ ┘
|
|
277
|
+
──────────────────────────────
|
|
278
|
+
ruby is 12 kLOC
|
|
279
|
+
c is the bulk
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
```text
|
|
283
|
+
$ tessella compose stack --gap 1 note.txt note.txt
|
|
284
|
+
ruby is 12 kLOC
|
|
285
|
+
c is the bulk
|
|
286
|
+
|
|
287
|
+
ruby is 12 kLOC
|
|
288
|
+
c is the bulk
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## indent
|
|
292
|
+
|
|
293
|
+
```ruby
|
|
294
|
+
Tessella.indent(fig, prefix:, suffix: nil, ascii: false)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Puts `prefix:` before every line. A line that is empty gets the
|
|
298
|
+
prefix without its trailing blank, so `"# "` becomes `"#"` there and
|
|
299
|
+
the result keeps the no-trailing-blank rule. This is how a figure
|
|
300
|
+
goes into a source comment:
|
|
301
|
+
|
|
302
|
+
```ruby
|
|
303
|
+
# frozen_string_literal: true
|
|
304
|
+
|
|
305
|
+
# examples/comment.rb: a tree, in ASCII, as a comment block for a
|
|
306
|
+
# source file kept to ASCII.
|
|
307
|
+
require "tessella"
|
|
308
|
+
|
|
309
|
+
outline = File.read("outline.txt")
|
|
310
|
+
puts Tessella.indent(Tessella.tree(outline, ascii: true),
|
|
311
|
+
prefix: "# ", ascii: true)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
```text
|
|
315
|
+
$ ruby comment.rb
|
|
316
|
+
# tessella
|
|
317
|
+
# |-- lib # the library
|
|
318
|
+
# | |-- tessella.rb # the entry
|
|
319
|
+
# | `-- tessella
|
|
320
|
+
# | `-- cli
|
|
321
|
+
# |-- exe
|
|
322
|
+
# | `-- tessella # the command
|
|
323
|
+
# `-- test
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
With `suffix:` (`--suffix STR`), every row is padded to the block's
|
|
327
|
+
width and closed by the suffix, a blank row too, so a comment wrapped
|
|
328
|
+
around each row closes at one column; a suffix ending in a blank is
|
|
329
|
+
refused. `tessella lint --prefix STR --suffix STR` charges both to
|
|
330
|
+
the budget.
|
|
331
|
+
|
|
332
|
+
```text
|
|
333
|
+
$ printf 'a -> b\n\nc\n' | \
|
|
334
|
+
tessella compose indent --prefix '<!-- ' --suffix ' -->' -
|
|
335
|
+
<!-- a -> b -->
|
|
336
|
+
<!-- -->
|
|
337
|
+
<!-- c -->
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## caption
|
|
341
|
+
|
|
342
|
+
```ruby
|
|
343
|
+
Tessella.caption(fig, text:, above: false, ascii: false)
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Adds a line of text centred on the figure, below it (or above it
|
|
347
|
+
with `above: true`), one blank line away.
|
|
348
|
+
|
|
349
|
+
```text
|
|
350
|
+
$ tessella compose caption --text 'Figure 2: lines of code' plot.txt
|
|
351
|
+
┌ ┐
|
|
352
|
+
ruby ┤██████▍········· 12
|
|
353
|
+
c ┤████████████████ 30
|
|
354
|
+
rust ┤████············ 7.5
|
|
355
|
+
└ ┘
|
|
356
|
+
|
|
357
|
+
Figure 2: lines of code
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## shadow
|
|
361
|
+
|
|
362
|
+
```ruby
|
|
363
|
+
Tessella.shadow(fig, glyph: nil, ascii: false)
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Adds a drop shadow: a glyph one column right of every row but the
|
|
367
|
+
first, and a bar under the figure from its second column to the
|
|
368
|
+
shadow's. The glyph is `░`, `#` under `ascii: true`; `glyph:`
|
|
369
|
+
(`--glyph`) is any glyph one column wide.
|
|
370
|
+
|
|
371
|
+
```text
|
|
372
|
+
$ tessella compose frame note.txt | tessella compose shadow -
|
|
373
|
+
┌─────────────────┐
|
|
374
|
+
│ ruby is 12 kLOC │░
|
|
375
|
+
│ c is the bulk │░
|
|
376
|
+
└─────────────────┘░
|
|
377
|
+
░░░░░░░░░░░░░░░░░░░
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
```text
|
|
381
|
+
$ tessella compose frame --ascii note.txt | tessella compose shadow --ascii -
|
|
382
|
+
+-----------------+
|
|
383
|
+
| ruby is 12 kLOC |#
|
|
384
|
+
| c is the bulk |#
|
|
385
|
+
+-----------------+#
|
|
386
|
+
###################
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
A shadow drawn this way is the one [the lint](lint.md) expects, so
|
|
390
|
+
the figure stays lint-clean.
|
|
391
|
+
|
|
392
|
+
## fill
|
|
393
|
+
|
|
394
|
+
```ruby
|
|
395
|
+
Tessella.fill(fig, pattern: nil, halo: true, ascii: false)
|
|
396
|
+
Tessella.frame(fig, ..., fill: nil, halo: true)
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Fills every enclosed blank cell with a pattern: inside a box, a
|
|
400
|
+
frame, any closed region. What is enclosed is what a flood from the
|
|
401
|
+
figure's edge, through blank cells, cannot reach; text, lines and
|
|
402
|
+
everything outside are left as they are. Here two hand-drawn boxes in
|
|
403
|
+
`boxes.txt`: the space between them is not enclosed, so it stays
|
|
404
|
+
blank.
|
|
405
|
+
|
|
406
|
+
```text
|
|
407
|
+
$ tessella compose fill --pattern stipple boxes.txt
|
|
408
|
+
┌────┐ ┌────┐
|
|
409
|
+
│∴∵∴∵│──│ ok │
|
|
410
|
+
└────┘ └────┘
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
`frame`'s `fill:` (`--fill P`) fills only the background the frame
|
|
414
|
+
adds, flooding from just inside its border; boxes the content draws
|
|
415
|
+
keep their own inside:
|
|
416
|
+
|
|
417
|
+
```text
|
|
418
|
+
$ tessella compose frame --fill light --pad 2 boxes.txt
|
|
419
|
+
┌──────────────────┐
|
|
420
|
+
│░░┌────┐░░┌────┐░░│
|
|
421
|
+
│░░│ │──│ ok │░░│
|
|
422
|
+
│░░└────┘░░└────┘░░│
|
|
423
|
+
└──────────────────┘
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### The halo
|
|
427
|
+
|
|
428
|
+
A pattern packed against letters makes them hard to read, so the fill
|
|
429
|
+
keeps a halo of blanks around text: by default (`halo: true`,
|
|
430
|
+
`--halo sides`) the cell on each side of a text glyph stays blank, so
|
|
431
|
+
a word keeps its shape and the gap between words survives:
|
|
432
|
+
|
|
433
|
+
```text
|
|
434
|
+
$ tessella compose frame --fill light --pad 3 note.txt
|
|
435
|
+
┌─────────────────────┐
|
|
436
|
+
│░░ ruby is 12 kLOC ░░│
|
|
437
|
+
│░░ c is the bulk ░░░░│
|
|
438
|
+
└─────────────────────┘
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
`--halo around` (`halo: :around`) also keeps the cells above and
|
|
442
|
+
below text blank; `--halo none` or `--no-halo` (`halo: false`) fills
|
|
443
|
+
right up to the letters, and between words:
|
|
444
|
+
|
|
445
|
+
```text
|
|
446
|
+
$ tessella compose frame --fill light --pad 3 --halo none note.txt
|
|
447
|
+
┌─────────────────────┐
|
|
448
|
+
│░░░ruby░is░12░kLOC░░░│
|
|
449
|
+
│░░░c░is░the░bulk░░░░░│
|
|
450
|
+
└─────────────────────┘
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Text is any glyph but box drawing and `+ - |`, so lines and borders
|
|
454
|
+
take the pattern right up to them.
|
|
455
|
+
|
|
456
|
+
A pattern is a name from the registry (`light`, the default, `medium`,
|
|
457
|
+
`dark`, `solid`, `hatch`, `backhatch`, `cross`, `grid`, `stipple`,
|
|
458
|
+
`sparse`, `dotted`, `dots`, `colons`, `hash`, `at`; `tessella glyphs
|
|
459
|
+
pattern` lists them) or glyphs of one's own. Several glyphs tile
|
|
460
|
+
diagonally. Each glyph must be one column wide, and all must share
|
|
461
|
+
one [width class](glyphs.md#width-classes), else the fill would not
|
|
462
|
+
line up in every terminal:
|
|
463
|
+
|
|
464
|
+
```text
|
|
465
|
+
$ tessella compose fill --pattern '░▒' boxes.txt
|
|
466
|
+
tessella compose: pattern "░▒" mixes width classes: N, A
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Under `ascii: true` the pattern must be ASCII, and the default is
|
|
470
|
+
`colons`:
|
|
471
|
+
|
|
472
|
+
```text
|
|
473
|
+
$ tessella compose frame --ascii --fill hash --pad 3 note.txt
|
|
474
|
+
+---------------------+
|
|
475
|
+
|## ruby is 12 kLOC ##|
|
|
476
|
+
|## c is the bulk ####|
|
|
477
|
+
+---------------------+
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
## ASCII
|
|
481
|
+
|
|
482
|
+
Every function takes `ascii: true`. Its borders are then drawn in
|
|
483
|
+
`+ - |`, any other `style:` is refused, and so is any figure or text
|
|
484
|
+
holding a non-ASCII character: it is refused rather than rewritten,
|
|
485
|
+
so an ASCII figure is ASCII because every part of it was drawn so.
|
|
486
|
+
|
|
487
|
+
```text
|
|
488
|
+
$ tessella chart --kind bar --ascii --width 20 langs.csv | \
|
|
489
|
+
tessella compose frame --ascii --title Load -
|
|
490
|
+
+------------- Load -------------+
|
|
491
|
+
| + + |
|
|
492
|
+
| ruby |######.......... 12 |
|
|
493
|
+
| c |################ 30 |
|
|
494
|
+
| rust |####............ 7.5 |
|
|
495
|
+
| + + |
|
|
496
|
+
+--------------------------------+
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## A larger figure
|
|
500
|
+
|
|
501
|
+
Diagrams compose like charts. Here a register layout is captioned and
|
|
502
|
+
stacked over its timing, under one rule:
|
|
503
|
+
|
|
504
|
+
```ruby
|
|
505
|
+
# frozen_string_literal: true
|
|
506
|
+
|
|
507
|
+
# examples/datasheet.rb: a register layout over its timing, as a
|
|
508
|
+
# datasheet draws them, stacked under one rule.
|
|
509
|
+
require "tessella"
|
|
510
|
+
|
|
511
|
+
register = Tessella.bitfield([["MODE", 4], ["DIV", 8], ["EN", 4]],
|
|
512
|
+
word: 16)
|
|
513
|
+
timing = Tessella.wave(<<~WAVE, period: 4)
|
|
514
|
+
sclk: p.......
|
|
515
|
+
cs: 10.....1
|
|
516
|
+
mosi: x=.=.=.x | CMD ADDR DATA
|
|
517
|
+
WAVE
|
|
518
|
+
puts Tessella.stack(Tessella.caption(register, text: "CTRL (0x04)",
|
|
519
|
+
above: true),
|
|
520
|
+
timing, gap: 1, rule: "─")
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
```text
|
|
524
|
+
$ ruby datasheet.rb
|
|
525
|
+
CTRL (0x04)
|
|
526
|
+
|
|
527
|
+
0 1
|
|
528
|
+
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
|
529
|
+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
530
|
+
| MODE | DIV | EN |
|
|
531
|
+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
532
|
+
|
|
533
|
+
─────────────────────────────────────
|
|
534
|
+
|
|
535
|
+
──┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐
|
|
536
|
+
sclk └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─
|
|
537
|
+
|
|
538
|
+
────┐ ┌───
|
|
539
|
+
cs └───────────────────────┘
|
|
540
|
+
|
|
541
|
+
▒▒▒▒╳ ╳ ╳ ▒▒▒▒
|
|
542
|
+
mosi ▒▒▒▒╳──CMD──╳─ADDR──╳─DATA──▒▒▒▒
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
## `tessella compose`
|
|
546
|
+
|
|
547
|
+
```text
|
|
548
|
+
tessella compose frame [--title T] [--style S] [--pad N] [--fill P]
|
|
549
|
+
[--halo H] BLOCK
|
|
550
|
+
tessella compose beside [--gap N] [--align A] BLOCK...
|
|
551
|
+
tessella compose stack [--gap N] [--rule C] [--center] BLOCK...
|
|
552
|
+
tessella compose indent --prefix STR BLOCK
|
|
553
|
+
tessella compose caption --text T [--above] BLOCK
|
|
554
|
+
tessella compose shadow [--glyph G] BLOCK
|
|
555
|
+
tessella compose fill [--pattern P] [--halo H] BLOCK
|
|
556
|
+
tessella compose grid [--columns N] [--gap N] [--row-gap N] [--align A]
|
|
557
|
+
BLOCK...
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
`--halo` is `sides`, `around` or `none`; `--no-halo` is `none`.
|
|
561
|
+
Each BLOCK is a file, or `-` for stdin (once). Every subcommand also
|
|
562
|
+
takes `--ascii`, and `--max-width N` to refuse a result wider than N
|
|
563
|
+
columns.
|
|
564
|
+
|
|
565
|
+
Bad input exits 1, naming the block and the line: a missing file, an
|
|
566
|
+
empty block, a tab, a non-ASCII byte under `--ascii`, a result wider
|
|
567
|
+
than `--max-width`.
|
|
568
|
+
|
|
569
|
+
```text
|
|
570
|
+
$ tessella compose frame --ascii plot.txt
|
|
571
|
+
tessella compose: plot.txt:1: non-ASCII byte
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
```text
|
|
575
|
+
$ tessella compose frame --max-width 20 plot.txt
|
|
576
|
+
tessella compose: output is 34 columns on line 1 (max-width 20)
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
Composing several files from a script, with a temp directory for the
|
|
580
|
+
blocks, is shown in [the command's scripts](cli.md#scripts).
|
|
581
|
+
|
|
582
|
+
A usage mistake exits 2: an unknown style or alignment, a style
|
|
583
|
+
other than `ascii` with `--ascii`, the wrong number of blocks.
|
|
584
|
+
|
|
585
|
+
```text
|
|
586
|
+
$ tessella compose beside plot.txt
|
|
587
|
+
tessella compose: beside takes at least 2 BLOCKs
|
|
588
|
+
Usage: tessella compose frame [--title T] [--style S] [--pad N]
|
|
589
|
+
[--fill P] [--halo H] BLOCK
|
|
590
|
+
tessella compose shadow [--glyph G] BLOCK
|
|
591
|
+
tessella compose fill [--pattern P] [--halo H] BLOCK
|
|
592
|
+
tessella compose grid [--columns N] [--gap N] [--row-gap N]
|
|
593
|
+
[--align A] BLOCK...
|
|
594
|
+
tessella compose beside [--gap N] [--align A] BLOCK...
|
|
595
|
+
tessella compose stack [--gap N] [--rule C] [--center] BLOCK...
|
|
596
|
+
tessella compose indent --prefix STR [--suffix STR] BLOCK
|
|
597
|
+
tessella compose caption --text T [--above] BLOCK
|
|
598
|
+
Every subcommand also takes --[no-]ascii and --max-width N.
|
|
599
|
+
```
|