Overview
Formatting in two layers: interpolation of scalars, and a composable layout of blocks.
(import (std prelude))
(import (std fmt))
;; Layer 1: interpolation
(println* ["Hello, " 42 ", ok? " #t])
;; Layer 2: layout
(show "Hello\n")
(show ["Padding: [" (padded/left 10 "foo") "]" nl])
(show ["Number: " (numeric/comma 1234567.89) nl])
(show ["Columns:" nl
(columnar [["first long text string" "second text string"]
["col 2" "more text for col 2"]])])
;; A string, an int or a char is a block, as a variable or a call as
;; well as written out. A vec of blocks is a block too.
(def name "Ada")
(def n 3)
(show (padded/left 8 name)) ;; Ada
(show ["Hello, " name "! You have " n " new messages" #\. nl])
;; Hello, Ada! You have 3 new messages.
(show [(padded/left 8 ["x = " n]) "|" nl]) ;; x = 3|
;; A block made from a record, by a function with a signature.
;; A double goes through numeric; an if picks a block or nothing.
(type (: Item (Record (: name string) (: price double) (: stock int))))
(: item-line (-> Item Block))
(defun (item-line i)
[(padded/right 10 (record-ref i name))
(padded/left 9 (numeric (record-ref i price) #:precision 2 #:comma 3))
(if (= (record-ref i stock) 0) " sold out" nothing)
nl])
;; vec-map gives a vec of blocks, which is a block.
(show (vec-map item-line stock))
;; Green tea 3.50
;; Gaiwan 1,240.00
;; Kettle 89.90 sold out
;; A row per record. A row is a (Vec Block), a block per cell.
(: item-row (-> Item (Vec Block)))
(defun (item-row i)
[(record-ref i name)
(numeric (record-ref i price) #:precision 2)
(record-ref i stock)])
(show (tabular/rows (vec-map item-row stock)
#:titles ["Item" "Price" "Stock"]
#:align [AlignLeft (AlignOn #\.) AlignRight]
#:style StyleAscii))
;; +-----------+---------+-------+
;; | Item | Price | Stock |
;; +-----------+---------+-------+
;; | Green tea | 3.50 | 12 |
;; | Gaiwan | 1240.00 | 2 |
;; | Kettle | 89.90 | 0 |
;; +-----------+---------+-------+
;; What an if, a cond, a match or a let gives is a block if it is
;; one of the things a block can be.
(show ["Title: " (match title ((Some t) t) (None "-")) nl])
(: greeting (-> string (Option string) Block))
(defun (greeting name title)
["Hello, "
(match title
((Some t) [t " "])
(None nothing))
name "!"])
(show [(greeting "Ada" (Some "Dr")) nl (greeting "Bob" None) nl])
;; Hello, Dr Ada!
;; Hello, Bob!
Interpolation combines scalars of mixed types into text. It
works on a (Vec Disp): a vec literal whose elements are strings,
ints, doubles, chars and bools. The grammar Disp reads each element
into the case that holds it. println* prints one.
Layout is a block algebra, for padding, aligning, wrapping
paragraphs and laying out tables. Everything is a Block: a piece
of output. Every function here that lays something out takes its
content as a block and gives back a block, so they nest. Nothing is
written until a block is run by show, render or
render->string.
What is a block. Where a Block is wanted, as an
argument or in a vec literal of blocks, each of these is one:
| What you write | What it is |
|---|---|
| A string, an int or a char: written out, a variable or a call | BStr, BInt, BChar |
A vec of any of these, written out or a (Vec Block) | BMany: its blocks in order |
A Block: nl, nothing, a call such as (padded/left 5 "x") | itself |
An if, cond, match or let whose results are any of these | the result it gives |
So a single thing needs no brackets, and several things are a vec:
(show "hej") ; hej
(show (padded/left 5 name)) ; " Ada"
(show (padded/left 8 ["x = " n])) ; " x = 3"
(show [(padded/left 5 "x") (padded/left 5 "y") nl])
A vec in a vec is a block in a block, so extra brackets change nothing.
A table is the place where the brackets matter: tabular/rows
takes a vec of rows, and a row is a (Vec Block) with a block for
each cell. A cell of several pieces is a vec in the row:
["tea" [3 " cups"]] is two cells.
Anything else is converted first:
| You have | Write |
|---|---|
A double x | (numeric x), or (numeric x #:precision 2) |
A bool b | (if b "yes" "no") |
A long n | (long->string n), or (bytes n) for a size |
A (Vec string) xs | (vec-map BStr xs) |
A (List %a) xs | (joined f xs ", "), where f makes the block of one element |
Where the type is known. A string or a vec is read as a
block where a block is wanted: as an argument, as what a function
declared to return a Block returns, and as the value of a
binding whose type is written. A lambda given to a function that does
not say what the lambda returns, such as vec-map, has no type
to read a vec literal at, so a vec of mixed pieces that it returns is
refused: give it to each, as in (vec-map (fun (x) (each
[x nl])) xs), or write a function with a signature. A function of
your own that takes blocks needs its signature for the same reason.
A number bound by def. (def n 3) does not say
what kind of number n is. Where a block is wanted, it is read as
the number written there would be: ["n = " n] is what
["n = " 3] is, and n is an int from then on. A number used
as a double later, as in (+ n 0.5), needs (: n double)
above its def.
When it does not compile. The messages for the common mistakes, and what to write instead:
| The compiler says | Write |
|---|---|
| Block: … found a double | (numeric x) |
| Block: … found the number 1.5 | (numeric 1.5) |
| Block: … found a boolean, or found a bool | (if b "yes" "no") |
| Block: … found a (Vec string) | (vec-map BStr xs), or (: xs (Vec Block)) above (def xs ...) |
| Block: … found a (Vec (Vec …)), at a lambda that returns a vec | (each [...]) around the vec the lambda returns |
A combinator of your own is a BRun and the two functions that run
blocks, run-block and run-blocks. It is the same kind of
thing as the ones in this module:
(: boxed (-> Block Block))
(defun (boxed b)
(BRun (fun (out col)
(run-blocks out col ["[" (render->string b) "]"]))))
Settings are parameters, so they are dynamically scoped:
with-fmt binds one around a block while it runs, and it is
restored when the block ends; with-fmt* binds several, as
parameterize does. A block reads a setting when it runs, so a
parameterize works around show, but not around the code
that builds the block. The settings here are:
| Parameter | Default | What it sets |
|---|---|---|
current-pad | #\space | The character padding is made of. |
current-width | 78 | The right margin of wrapped, justified, columnar, and a table that cuts or wraps. |
current-ellipsis | "" | What replaces the part a trim cuts off. |
current-string-width | the character count | How wide a string is, in columns. terminal-width counts as a terminal does. |
current-precision | -1 | Digits after the decimal separator; negative is as many as needed. |
current-comma | 0 | Digits per group; zero is no grouping. |
current-comma-sep | #\, | The character between groups. |
current-decimal-sep | #\. | The decimal separator. |
current-signed | #f | Whether a positive number gets a leading +. |
A setting of your own works the same way: with-fmt takes the
parameter as an argument. Read it with parameter-ref inside the
BRun’s function, when the block runs. Read where the block is
built, it is read before any enclosing with-fmt has bound it.
Tables are tabular, tabular/rows and
columnar as functions, table-row a row at a time, and
Table as a quoted literal that the compiler checks. They draw
borders in a TableStyle, a header and rules, align a column on a
character, and fit current-width by cutting or wrapping cells.
bytes, percent, bar and sparkline are blocks
for what goes in a cell, or anywhere else. Set
current-string-width to terminal-width for text with wide
characters or colours in it.
See also: println*, show, render->string, with-fmt, Block
Reference
AlignBlockBorderCharsColumnWidthDispOverflowSinkTableTableCellTableLayoutTableRulesTableStyle
barbytescapturedcolumnareachfitted/bothfitted/leftfitted/rightjoinedjoined/lastjoined/prefixjoined/suffixjustifiednumericnumeric/commapadded/bothpadded/leftpadded/rightpercentprintln*renderrender->stringrun-blockrun-blocksshowshow-tablespace-tosparklinetab-totable-blocktable-bottomtable-layouttable-rowtable-ruletable-toptable-widthstabulartabular/rowsterminal-widthtrimmed/bothtrimmed/lefttrimmed/rightwidth-ofwith-fmtwith-padwrapped
Types
Disp
union ↑ Types
A scalar to interpolate: a string, int, double, char or bool.
DStr- A string, as it is.
DInt- An int, by int->string.
DDouble- A double, by double->string.
DChar- A char, as one character.
DBool- A bool, as #t or #f.
Disp is a grammar (see def/grammar). In a vec literal of
mixed scalars, such as ["n = " 42 ", ok? " #t], the grammar reads
each element into the case that can hold it. Thus you rarely write the
cases.
See also: println*
Block
union ↑ Types
A piece of layout: a scalar, or a function that writes output and returns the final column position.
BStr- A string, written as it is.
BInt- An int, written in decimal.
BChar- A char.
BMany- Blocks in order: what a vec of blocks is.
BRun- A function taking (sink, col) that writes output and returns the resulting column.
(render->string ["x = " 7 #\!]) ; "x = 7!": a vec of a string, an int and a char
;; The cases are rarely written: where a block is wanted, a value is
;; read as the case that holds it.
(def s "tea")
(: b Block)
(def b s) ; (BStr "tea")
(: bs (Vec Block))
(def bs [s " and " b])
(show [bs nl]) ; tea and tea: a vec in a vec is a block in a block
(show (vec-map BStr ["a" "b"])) ; ab: a vec of strings made a vec of blocks
;; A function that returns a block.
(: bullets (-> (Vec string) Block))
(defun (bullets xs)
(vec-map (fun (x) (each [" * " x nl])) xs))
(: labelled (-> string int Block))
(defun (labelled label n)
[label ": " n nl])
(show [(bullets ["tea" "milk"]) (labelled "cups" 3)])
;; * tea
;; * milk
;; cups: 3
The column is counted in characters since the last newline written. It
is the one thing threaded through every block, and what lets fl,
space-to and wrapped know where the cursor is.
Block is a grammar (see def/grammar). Where a block is
wanted, a string, an int, a char or a vec of blocks is read as the case
that holds it, whether it is written out or the value of a variable or
a call. The module doc lists what else is a block, and how to convert
what is not.
Align
union ↑ Types
Where a cell sits in its table column.
AlignLeft- Against the left edge, padded on the right. The default.
AlignRight- Against the right edge, padded on the left: what numbers want.
AlignCentre- In the middle, the odd column of padding on the right.
AlignOn- Every line of the column has the character in the same place, as a decimal point is in a column of numbers. A line without the character ends where the character would be. The part as a whole is aligned to the right, and so is the title over it.
(show (tabular/rows [["tea" "3.5"] ["gaiwan" "1240"] ["kettle" "89.90"]]
#:align [AlignLeft (AlignOn #\.)]))
;; tea 3.5
;; gaiwan 1240
;; kettle 89.90
AlignOn needs the widest part of a line before the character and
the widest part from it on, so it is measured by the tables that see all
their rows: tabular and tabular/rows. A table laid out a
row at a time, and columnar, align such a column to the right.
See also: tabular, tabular/rows, table-layout, columnar
TableStyle
union ↑ Types
How a table’s borders and rules are drawn.
StyleNone- No borders: the columns with a separator between them. The default.
StyleSimple- No borders, two spaces between the columns, and a line of ─ under the header.
StyleAscii- A box of +, - and |.
StyleLight- A box of light lines: ┌─┬─┐.
StyleHeavy- A box of heavy lines: ┏━┳━┓.
StyleDouble- A box of double lines: ╔═╦═╗.
StyleRounded- A box of light lines with round corners: ╭─┬─╮.
StyleMarkdown- A Markdown table, whose rule under the header says how each column is aligned.
StyleCustom- The pieces of a BorderChars.
(show (tabular/rows [["tea" "3.50"] ["gaiwan" "1240.00"]]
#:titles ["Item" "Price"]
#:style StyleAscii
#:align [AlignLeft AlignRight]))
;; +--------+---------+
;; | Item | Price |
;; +--------+---------+
;; | tea | 3.50 |
;; | gaiwan | 1240.00 |
;; +--------+---------+
;;
;; The other boxes are the same table drawn with other characters.
A style with a box pads each cell with a space on each side, and pads every cell to its column, the last one too, so that the right edge is straight. A Markdown column is at least three wide, as its rule needs.
See also: tabular/rows, tabular, table-layout, BorderChars, TableRules
TableRules
union ↑ Types
Which horizontal rules a table has inside its borders.
RulesHeader- A rule under the header. The default.
RulesEvery- A rule under the header and between every two rows. Markdown has none between rows.
RulesNone- No rules inside the table.
See also: TableStyle, tabular/rows
ColumnWidth
union ↑ Types
How wide a table column is.
WidthAuto- As wide as its widest line. The default.
WidthFixed- This many columns, at least one.
WidthAtLeast- As wide as its content, and at least this many columns.
WidthAtMost- As wide as its content, and at most this many columns.
WidthShare- A share of what the other columns and the borders leave of current-width: two columns of shares 1 and 2 take a third and two thirds of it.
(with-fmt current-width 30
(tabular/rows [["a" "b" "c"]] #:style StyleAscii
#:widths [(WidthFixed 5) (WidthShare 1) (WidthShare 2)]))
;; +-------+-------+------------+
;; | a | b | c |
;; +-------+-------+------------+
A column narrower than its content shows the content as its
Overflow says: pushed out of line, cut or wrapped. A width of no
more than zero is one.
See also: tabular/rows, Overflow, current-width
Overflow
union ↑ Types
What a table cell wider than its column does.
OverflowPush- It pushes the rest of its row out of line. The default, and what a table without width rules does.
OverflowCut- It is cut to the column, on the side away from its alignment, with current-ellipsis in place of what is cut.
OverflowWrap- It is broken at its spaces onto more lines, and a word wider than the column is broken where it reaches the edge.
(with-fmt current-width 40
(tabular/rows [["tea" "A green tea from the hills, picked in spring and rolled by hand."]]
#:titles ["Item" "Note"] #:style StyleAscii #:overflow OverflowWrap))
;; +------+-------------------------------+
;; | Item | Note |
;; +------+-------------------------------+
;; | tea | A green tea from the hills, |
;; | | picked in spring and rolled |
;; | | by hand. |
;; +------+-------------------------------+
A table that cuts or wraps is made to fit current-width: while it
is too wide, the widest column that may shrink loses a column. A column
of a fixed width does not shrink, nor one at its WidthAtLeast,
nor one below one column. A column aligned on a character is neither
cut nor wrapped, since the character would no longer line up.
See also: tabular/rows, ColumnWidth, current-width, current-ellipsis
BorderChars
record ↑ Types
The pieces a table’s borders are drawn with, for a style of your own.
top-left- The start of the line above the table.
top- Repeated across each column in the line above the table. Empty means there is no such line.
top-join- Where two columns meet in the line above the table.
top-right- The end of the line above the table.
left- Before the first cell of a row.
column- Between two cells of a row.
right- After the last cell of a row.
rule-left- The start of a rule.
rule- Repeated across each column in a rule. Empty means there are no rules.
rule-join- Where two columns meet in a rule.
rule-right- The end of a rule.
bottom-left- The start of the line under the table.
bottom- Repeated across each column in the line under the table. Empty means there is no such line.
bottom-join- Where two columns meet in the line under the table.
bottom-right- The end of the line under the table.
marks- Whether the rule under the header marks each column's alignment with colons, as Markdown's does.
A piece of a horizontal line is as wide as the piece of a row it is
under: top-left as left, top-join as
column, and so on. The built-in boxes put a space on each side of
a cell, so their left is "│ " and their top-left
is "┌─". A table without left and right leaves out
the empty cells at the end of a line and does not pad its last cell, as
a table without borders does.
See also: TableStyle
TableLayout
record ↑ Types
The widths, alignments, separator and style of a table laid out a row at a time.
widths- The width of each column.
aligns- The alignment of each column, from the first; a column not given one is AlignLeft.
sep- Written between the columns, without borders.
style- How the borders are drawn.
See also: table-layout, table-row
Table
record ↑ Types
A table written as a quoted literal: its options, its columns, and its rows.
options- The table's style, header, rules, width and overflow, each there or not.
columns- The columns, each with its title, alignment, width and kind.
rows- The rows, each written or spliced.
(: (Vec (Vec TableCell)))
(def products
[[(CellText "Green tea") (CellNum 3.5) (CellInt 12) (CellNum 0.4)]
[(CellText "Gaiwan") (CellNum 1240.0) (CellInt 2) (CellNum 1.0)]])
(show-table
'(table (style ascii) header
(column "Item")
(column "Price" (align (on #\.)) (number (precision 2) (comma 3)))
(column "Stock" (align right))
(column "Share" (percent (precision 0)))
(rows ,products)))
;; +-----------+----------+-------+-------+
;; | Item | Price | Stock | Share |
;; +-----------+----------+-------+-------+
;; | Green tea | 3.50 | 12 | 40% |
;; | Gaiwan | 1,240.00 | 2 | 100% |
;; +-----------+----------+-------+-------+
;;
;; A column of (bar (max 100.0)) shows each number as a bar of its
;; share of the max, as the block bar does.
(show-table '(table (style markdown) header (column "Key") (column "Value")
(rows (row "width" 78) (row "style" "markdown"))))
The literal is (table option ... column ... (rows row ...)). The
options come first, each at most once and in any order:
(style s) with a TableStyle as its name, rounded or
markdown; header, which makes the column titles a header
row; (rules r) with a TableRules; (width n), the
width the table is fitted to in place of current-width; and
(overflow o) with an Overflow.
A column is (column "Title" option ...), its options again each
at most once: (align a) with an Align, as right or
(on #\.); (width w) with a ColumnWidth, as
12 or (share 2); and a kind, which shows the column’s
numbers as one of this module’s blocks: (number (precision 2)
(comma 3) signed) as numeric, bytes, (percent
(precision 1)), or (bar (max 100.0) (width 10)), a bar of the
number’s share of the max. A column of a kind other than a bar is
aligned to the right unless it says otherwise. A cell that is not a
number is shown as it is in any column.
A row is written, (row cell ...), a cell being a string or a
number or a spliced value of one; or spliced, as a (Vec
TableCell) for one row or a (Vec (Vec TableCell)) for many. A
spliced number bound by a def of a bare literal is read as the
number written there would be, as it is in a block.
The literal is read where it is written, so a misspelled option, an option given twice, a style that is not one, a table with no columns and a splice of the wrong type are compile errors that say where.
See also: table-block, show-table, TableCell, tabular/rows
TableCell
union ↑ Types
A cell of a Table: what a written cell is, and what a spliced row holds.
CellText- Text, shown as it is.
CellInt- A whole number, shown as its column's kind says.
CellNum- A number, shown as its column's kind says.
CellBlocks- Blocks, shown as they are, as each shows them.
See also: Table
Functions
println*
function ↑ Functions
(: println* (-> (Vec Disp) void))
(println* ds)
Prints scalars of mixed types, then a newline.
ds- The scalars, written as a vec literal.
(println* ["n = " 42 ", ok? " #t ", " #\x ", " 1.5])
render
function ↑ Functions
(: render (-> OutputPort Block void))
(render port b)
Writes a block to a port.
port- The port to write to.
b- The block, run from column 0.
It is #:sync, and cannot be otherwise: the sink is stored in a
BRun, and a stored function’s colour is its type, so no
suspending sink can go there. To render from a fiber, use
render->string and write the string yourself.
See also: show, render->string
show
function ↑ Functions
(: show (-> Block void))
(show b)
Writes a block to the current output port.
b- The block, run from column 0.
(show "Hello\n")
(show ["Padding: [" (padded/left 10 "foo") "]" nl])
render to current-output-port, and #:sync for the
same reason.
See also: render, render->string
render->string
function ↑ Functions
(: render->string (-> Block string))
(render->string b)
The text a block writes, as a string.
b- The block, run from column 0.
- returns
- Everything the block wrote.
(render->string (padded/left 5 "7")) ; " 7"
run-block
function ↑ Functions
(: run-block (-> Sink int Block int))
(run-block out col b)
Runs one block: the engine’s step, for writing combinators.
out- The sink to write to.
col- The column the block starts at.
b- The block.
- returns
- The column it ends at.
See also: run-blocks, Block
run-blocks
function ↑ Functions
(: run-blocks (-> Sink int (Vec Block) int))
(run-blocks out col bs)
Runs blocks one after another, threading the column.
out- The sink to write to.
col- The column the first block starts at.
bs- The blocks.
- returns
- The column the last one ends at.
See also: run-block
with-fmt
function ↑ Functions
(: with-fmt (-> (Param %a) %a Block Block))
(with-fmt p v b)
Runs a block with a parameter bound to a value.
%a- The parameter's type.
p- The parameter: one of this module's settings, or one of your own.
v- Its value while the block runs.
b- The block.
- returns
- A block. The parameter is bound when it runs, not when it is built, and restored when it ends.
(show (with-fmt current-precision 2 (numeric 3.14159))) ; 3.14
;; Settings nest; each applies to the blocks inside it.
(show [(with-fmt current-width 24
[(with-fmt current-ellipsis "…"
[(trimmed/right 8 "A green tea from the hills") nl])
(wrapped "A green tea from the hills, picked in spring and rolled by hand.")])
nl])
;; A green…
;; A green tea from the
;; hills, picked in spring
;; and rolled by hand.
A block reads a setting when it runs, not when it is built. So a
parameterize around the code that builds a block does nothing:
the block runs later, in show, when the parameterize has
ended.
(parameterize ((current-width 20)) (show (wrapped text))) ; 20 wide
(show (with-fmt current-width 20 (wrapped text))) ; 20 wide
(show (parameterize ((current-width 20)) (wrapped text))) ; 78 wide: the default
A parameterize around show is the simplest way to set
something for a whole output. with-fmt and with-fmt* set
it for part of one: a cell, a paragraph, the numbers of a column.
See also: with-fmt*, with-pad, current-width
with-pad
function ↑ Functions
(: with-pad (-> char Block Block))
(with-pad c b)
Runs a block with another padding character.
c- The padding character.
b- The block.
- returns
- A block:
(with-fmt current-pad c b).
(show (with-pad #\. ["ab" (space-to 6) "c"])) ; ab....c, a dot leader
See also: current-pad, space-to
terminal-width
function ↑ Functions
(: terminal-width (-> string int))
(terminal-width s)
How many columns a string takes in a terminal, for current-string-width.
s- The string.
- returns
- Its width in columns.
(def esc (char->string (integer->char 27)))
(terminal-width "tea") ;; 3
(terminal-width "茶") ;; 2
(terminal-width (str "e" (char->string (integer->char 769)))) ;; 1: e and an accent
(terminal-width (str esc "[1mtea" esc "[0m")) ;; 3: the escapes take none
(with-fmt current-string-width terminal-width
(tabular/rows [["茶" "1"] ["tea" "2"]] #:style StyleAscii))
East Asian Wide and Fullwidth characters, emoji among them, take two
columns. Combining marks, format and control characters take none. An
escape sequence takes none: a CSI, as in a colour, ends at a character
from @ to ~, and an OSC, as in a link, at BEL or at ESC and a
backslash. What follows a zero-width joiner takes no columns of its own,
so a family emoji is two wide. Variation selector 16 makes a one-column
character before it two wide, a skin tone modifier joins the emoji
before it, and two regional indicators are one flag, two wide.
It is not the default measure, which counts code points and is cheaper. Set it where the text is for a terminal and holds such characters: a wide character measured as one column puts every column after it out of line.
The table is Unicode 16.0. A terminal whose own idea of a character’s width differs from it, as some do for emoji, draws that character wider or narrower than measured.
See also: current-string-width, width-of
each
function ↑ Functions
(: each (-> (Vec Block) Block))
(each bs)
Many blocks as one.
bs- The blocks.
- returns
- A block that runs them in order:
(BMany bs).
;; vec-map does not say what its function returns, so a vec literal
;; of mixed pieces that the lambda returns is not read as blocks.
;; Under each, it is.
(show (vec-map (fun (i) (each [(record-ref i name) ": " (record-ref i stock) nl]))
stock))
;; Green tea: 12
;; Gaiwan: 2
;; Kettle: 0
A vec of blocks is a block where a block is wanted, so each is
needed only where nothing says that one is: as the result of a lambda
given to a function that does not say what its function returns, such
as vec-map. A lambda given to joined or captured
returns a block, as their signatures say, and needs no each.
captured
function ↑ Functions
(: captured (-> (-> string Block) Block Block))
(captured f b)
Renders a block to a string and passes it to a function returning a replacement block.
f- Given the text the block wrote; its block is run in its place.
b- The block to render.
- returns
- A block.
(captured (fun (s) (BInt (string-count s))) "abcd") ; writes 4
;; Brackets around whatever the content is.
(: boxed (-> Block Block))
(defun (boxed b)
(captured (fun (s) ["[" s "]"]) b))
(show [(boxed ["x = " 7]) nl]) ; [x = 7]
How a combinator that needs the whole text, rather than writing as it
goes, is made: padding, trimming and wrapping are all written with it.
The block is rendered from column 0, and f’s block runs at the
current column.
See also: render->string
width-of
function ↑ Functions
(: width-of (-> string int))
(width-of s)
How many columns a string takes, by current-string-width.
s- The string.
- returns
- Its width.
See also: current-string-width
space-to
function ↑ Functions
(: space-to (-> int Block))
(space-to goal)
Pads to a column.
goal- The column to move to.
- returns
- A block that writes padding up to goal, and nothing if the cursor is already at or past it.
(render->string ["ab" (space-to 5) "c"]) ; "ab c"
;; A dot leader: every price starts at column 12.
(show [(with-pad #\. ["Tea" (space-to 12) "3.50"]) nl
(with-pad #\. ["Gaiwan" (space-to 12) "1240.00"]) nl])
;; Tea.........3.50
;; Gaiwan......1240.00
It fills with current-pad, so under with-pad it is a
leader. It never moves backwards.
tab-to
function ↑ Functions
(: tab-to (-> (#:width int) Block))
(tab-to #:width 8)
Pads to the next tab stop.
#:width- The distance between tab stops. 8 unless given.
- returns
- A block that pads to the next multiple of width.
(render->string ["ab" (tab-to #:width 4) "c"]) ; "ab c"
(render->string ["abcd" (tab-to #:width 4) "c"]) ; "abcd c"
It always moves: at a tab stop already, it pads to the next one.
See also: space-to
padded/left
function ↑ Functions
(: padded/left (-> int Block Block))
(padded/left w b)
Pads content on the left to a width: aligned right.
w- The width, in columns.
b- The content, a block.
- returns
- A block. Content wider than w is left as it is: padding never cuts.
(render->string (padded/left 6 ["x=" 7])) ; " x=7"
;; The content is a vec even when it is one thing.
(show [(padded/left 9 (numeric 1240.0 #:precision 2 #:comma 3)) nl]) ; 1,240.00
(show [(with-pad #\0 (padded/left 4 42)) nl]) ; 0042
See also: padded/right, padded/both, fitted/left, current-pad
padded/right
function ↑ Functions
(: padded/right (-> int Block Block))
(padded/right w b)
Pads content on the right to a width: aligned left.
w- The width, in columns.
b- The content, a block.
- returns
- A block. Content wider than w is left as it is.
(show [(padded/right 10 "Green tea") "3.50" nl
(padded/right 10 "Gaiwan") "1240.00" nl])
;; Green tea 3.50
;; Gaiwan 1240.00
See also: padded/left, padded/both, fitted/right
padded/both
function ↑ Functions
(: padded/both (-> int Block Block))
(padded/both w b)
Pads content on both sides to a width: centred.
w- The width, in columns.
b- The content, a block.
- returns
- A block, with the odd column, if there is one, on the right. Content wider than w is left as it is.
(show [(with-pad #\= (padded/both 20 " Receipt ")) nl])
;; ===== Receipt ======
See also: padded/left, padded/right, fitted/both
trimmed/left
function ↑ Functions
(: trimmed/left (-> int Block Block))
(trimmed/left w b)
Cuts content to a width, keeping its end.
w- The width, in columns.
b- The content, a block.
- returns
- A block. Content that fits is left as it is; otherwise current-ellipsis stands at the start, where the cut was.
(render->string (trimmed/left 3 "abcdef")) ; "def"
See also: trimmed/right, trimmed/both, fitted/left, current-ellipsis
trimmed/right
function ↑ Functions
(: trimmed/right (-> int Block Block))
(trimmed/right w b)
Cuts content to a width, keeping its start.
w- The width, in columns.
b- The content, a block.
- returns
- A block. Content that fits is left as it is; otherwise current-ellipsis stands at the end.
(render->string (trimmed/right 3 "abcdef")) ; "abc"
See also: trimmed/left, trimmed/both, fitted/right, current-ellipsis
trimmed/both
function ↑ Functions
(: trimmed/both (-> int Block Block))
(trimmed/both w b)
Cuts content to a width, keeping its middle.
w- The width, in columns.
b- The content, a block.
- returns
- A block. Content that fits is left as it is; otherwise current-ellipsis stands at both ends.
(render->string (trimmed/both 4 "abcdefgh")) ; "cdef"
See also: trimmed/left, trimmed/right, fitted/both
fitted/left
function ↑ Functions
(: fitted/left (-> int Block Block))
(fitted/left w b)
Content at exactly a width, aligned right: padded if short, cut if long.
w- The width, in columns.
b- The content, a block.
- returns
- A block: trimmed/left, then padded/left.
See also: padded/left, trimmed/left
fitted/right
function ↑ Functions
(: fitted/right (-> int Block Block))
(fitted/right w b)
Content at exactly a width, aligned left: padded if short, cut if long.
w- The width, in columns.
b- The content, a block.
- returns
- A block: trimmed/right, then padded/right.
(render->string [(fitted/right 5 "ab") "|"]) ; "ab |"
(render->string [(fitted/right 3 "abcdef") "|"]) ; "abc|"
See also: padded/right, trimmed/right
fitted/both
function ↑ Functions
(: fitted/both (-> int Block Block))
(fitted/both w b)
Content at exactly a width, centred: padded if short, cut in the middle if long.
w- The width, in columns.
b- The content, a block.
- returns
- A block: trimmed/both, then padded/both.
(show ["[" (fitted/both 6 "ab") "]" nl]) ; [ ab ]
See also: padded/both, trimmed/both
joined
function ↑ Functions
(: joined (-> (-> %a Block) (List %a) Block Block))
(joined f xs sep)
Every element of a list, formatted, with a separator between them.
%a- The elements' type.
f- Formats one element.
xs- The elements.
sep- The separator, itself blocks.
- returns
- A block. One element has no separator, and none is nothing.
(show (joined (fun (n) (BInt n)) (list 1 2 3) ", ")) ; 1, 2, 3
;; A constructor of Block is a function, so a list of strings is
;; joined with BStr.
(show [(joined BStr (list "tea" "milk" "honey") ", ") nl]) ; tea, milk, honey
;; f returns a block, so content of several pieces is under each.
(show [(joined (fun (s) ["* " s]) (list "tea" "milk") nl) nl])
;; * tea
;; * milk
;; For a vec, a separator is vec->list away.
(show [(joined BStr (vec->list ["a" "b"]) " | ") nl]) ; a | b
See also: joined/prefix, joined/suffix, joined/last
joined/prefix
function ↑ Functions
(: joined/prefix (-> (-> %a Block) (List %a) Block Block))
(joined/prefix f xs sep)
Every element of a list, formatted, with a separator before each, the first included.
%a- The elements' type.
f- Formats one element.
xs- The elements.
sep- The separator, itself blocks.
- returns
- A block.
(show (joined/prefix (fun (s) (BStr s)) (list "a" "b") ",")) ; ,a,b
See also: joined, joined/suffix
joined/suffix
function ↑ Functions
(: joined/suffix (-> (-> %a Block) (List %a) Block Block))
(joined/suffix f xs sep)
Every element of a list, formatted, with a separator after each, the last included.
%a- The elements' type.
f- Formats one element.
xs- The elements.
sep- The separator, itself blocks.
- returns
- A block.
(show (joined/suffix (fun (s) (BStr s)) (list "a" "b") ",")) ; a,b,
See also: joined, joined/prefix
joined/last
function ↑ Functions
(: joined/last (-> (-> %a Block) (-> %a Block) (List %a) Block Block))
(joined/last f last-f xs sep)
Like joined, with the last element formatted by a function of its own.
%a- The elements' type.
f- Formats every element but the last.
last-f- Formats the last element.
xs- The elements.
sep- The separator, itself blocks.
- returns
- A block. A list of one is formatted by last-f alone.
(show (joined/last (fun (s) (BStr s))
(fun (s) ["and " s])
(list "a" "b" "c")
", "))
;; a, b, and c
The separator is written before the last element too, so what
last-f adds is a word, not a comma.
See also: joined
numeric
function ↑ Functions
(: numeric (-> double (#:precision int) (#:comma int) (#:comma-sep char) (#:decimal-sep char) (#:signed bool) Block))
(numeric x #:precision -1 #:comma -1 #:comma-sep #\nul
#:decimal-sep #\nul #:signed #f)
A number, with a precision, grouped digits and separators of your choosing.
x- The number. An int is formatted as (cast double n).
#:precision- Digits after the decimal separator. Negative, the default, is current-precision.
#:comma- Digits per group; 0 is no grouping. Negative, the default, is current-comma.
#:comma-sep- The character between groups. #\nul, the default, is current-comma-sep.
#:decimal-sep- The decimal separator. #\nul, the default, is current-decimal-sep.
#:signed- #t gives a positive number a leading +. #f, the default, is current-signed.
- returns
- A block.
(numeric 2.0 #:precision 2) ; 2.00
(numeric -3.14159 #:precision 3) ; -3.142
(numeric 1234567.891 #:precision 2 #:comma 3) ; 1,234,567.89
(numeric 42.0 #:signed #t) ; +42
(numeric 1234.5 #:comma 3 #:comma-sep #\space #:decimal-sep #\,) ; 1 234,5
;; A double is not a block. numeric makes it one.
(: total double)
(def total 1333.4)
(show ["Total: " (numeric total #:precision 2 #:comma 3) nl]) ; Total: 1,333.40
;; The settings apply to every numeric inside a with-fmt.
(show (with-fmt current-precision 1 ["pi is about " (numeric 3.14159) nl]))
;; pi is about 3.1
The digits are the invariant culture’s and the separators are settings,
so the machine’s locale never shows. A keyword not given defaults to its
parameter when the block runs. Since a keyword’s default is what stands
for the parameter, a keyword cannot ask for that default against a
parameter bound otherwise: #:signed #f under a
current-signed of #t still signs, and a negative
#:precision still reads current-precision.
See also: numeric/comma, current-precision, current-comma
numeric/comma
function ↑ Functions
(: numeric/comma (-> double Block))
(numeric/comma x)
A number with its digits grouped in thousands: the common case.
x- The number.
- returns
- A block:
(numeric x #:comma 3).
(numeric/comma 1234567.0) ; 1,234,567
(numeric/comma 42.0) ; 42
See also: numeric
bytes
function ↑ Functions
(: bytes (-> long (#:si bool) (#:precision int) Block))
(bytes n #:si #f #:precision 1)
A number of bytes, in the largest unit there is at least one of.
n- The number of bytes.
#:si- Units of 1000, kB and MB, rather than of 1024, KiB and MiB. False unless given.
#:precision- Digits after the decimal separator, in any unit but B. One unless given.
- returns
- A block.
(show [(bytes 512L) " " (bytes 1258291L) " " (bytes 1500L #:si #t)])
;; 512 B 1.2 MiB 1.5 kB
Bytes are whole, so a size under one kibibyte, or kilobyte, has no
decimals. The units go to EiB, or EB. The decimal separator is
current-decimal-sep.
See also: percent, numeric, current-decimal-sep
percent
function ↑ Functions
(: percent (-> double (#:precision int) Block))
(percent x #:precision 1)
A share, written as a percentage: 0.125 is 12.5%.
x- The share, 1.0 being all of it.
#:precision- Digits after the decimal separator. One unless given.
- returns
- A block.
(show [(percent 0.125) " " (percent 1.0 #:precision 0)])
;; 12.5% 100%
bar
function ↑ Functions
(: bar (-> double (#:width int) (#:empty string) Block))
(bar fraction #:width 10 #:empty " ")
A horizontal bar, filled to a fraction of its width to an eighth of a column.
fraction- How much of the bar is filled, from 0 to 1; a fraction outside that is the nearer end.
#:width- The width of the whole bar in columns. Ten unless given.
#:empty- What the part not filled is made of, a column at a time. A space unless given.
- returns
- A block, exactly width columns wide.
(show ["[" (bar 0.62 #:width 20) "]"])
;; [████████████▍ ]
(show ["[" (bar 0.5 #:width 5 #:empty "░") "]"])
;; [██▌░░]
The filled part is whole blocks, █, and one of the seven eighths of a block, ▏ to ▉, for what is left over, rounded to the nearest eighth.
sparkline
function ↑ Functions
(: sparkline (-> (Vec double) Block))
(sparkline xs)
Numbers as a line of bars of eight heights, a column each.
xs- The numbers.
- returns
- A block, a column for each number.
(show (sparkline [1.0 2.0 4.0 8.0 4.0 2.0 1.0]))
;; ▁▂▄█▄▂▁
The smallest number is the lowest bar, ▁, and the largest the highest, █; the rest are divided evenly between them. Numbers that are all the same are all in the middle, ▄.
See also: bar
wrapped
function ↑ Functions
(: wrapped (-> Block Block))
(wrapped b)
Content filled to the right margin, ragged on the right.
b- The content. Every run of whitespace in it, newlines too, is a break between words.
- returns
- A block.
(with-fmt current-width 12 (wrapped "the quick brown fox jumps"))
;; the quick
;; brown fox
;; jumps
;; The first line starts after the label, so it has less room; the
;; lines after it start at column 0. The content may mix strings and
;; numbers like any other.
(show [(with-fmt current-width 30
["Note: " (wrapped ["picked on day " 3 " of spring, rolled by hand and dried in the sun."])])
nl])
;; Note: picked on day 3 of
;; spring, rolled by hand and
;; dried in the sun.
The first line starts wherever the cursor is, and has only the room left
before current-width. Words are separated by one
current-pad. A word wider than a line gets a line of its own
rather than being cut.
See also: justified, current-width
justified
function ↑ Functions
(: justified (-> Block Block))
(justified b)
Content filled to the right margin and flush against both, the last line excepted.
b- The content. Every run of whitespace in it is a break between words.
- returns
- A block.
(with-fmt current-width 12 (justified "the quick brown fox jumps"))
;; the quick
;; brown fox
;; jumps
Lines are broken as wrapped breaks them, then the gaps are
widened to fill the line, the leftmost ones taking any extra column. The
last line, and a line of one word, are not stretched.
See also: wrapped, current-width
columnar
function ↑ Functions
(: columnar (-> (Vec (Vec Block)) (#:sep string) (#:align (Vec Align)) Block))
(columnar cols #:sep " " #:align [])
Columns side by side, dividing current-width evenly and cutting what does not fit.
cols- The columns, each a vec of blocks; a newline in one starts its next row.
#:sep- Written between the columns. A space unless given.
#:align- The alignment of each column, from the first; a column not given one is AlignLeft.
- returns
- A block.
(show (with-fmt current-width 11
(columnar [["abc" nl "def"]
["123" nl "456"]])))
;; abc 123
;; def 456
Each column is rendered on its own and the lines are laid out together. The width left once the separators are taken out is shared equally, and every cell is cut and padded to its share, the last column cut but not padded on the right. A cut keeps the side the cell is aligned to: the start of a left-aligned cell, the end of a right-aligned one, the middle of a centred one. A column with fewer lines than the others contributes blanks, and blanks at the end of a line are left out, separators and all. The table starts on a fresh line, and every row ends with a newline.
See also: tabular, Align, current-width
tabular
function ↑ Functions
(: tabular (-> (Vec (Vec Block)) (#:sep string) (#:align (Vec Align)) (#:style TableStyle) (#:titles (Vec Block)) (#:rules TableRules) (#:widths (Vec ColumnWidth)) (#:overflow Overflow) Block))
(tabular cols #:sep " " #:align [] #:style StyleNone #:titles [] #:rules RulesHeader
#:widths [] #:overflow OverflowPush)
Columns side by side, each as wide as its widest line, cutting nothing.
cols- The columns, each a vec of blocks; a newline in one starts its next row.
#:sep- Written between the columns, without borders. A space unless given.
#:align- The alignment of each column, from the first; a column not given one is AlignLeft.
#:style- How the borders are drawn. StyleNone unless given.
#:titles- A header row, a block per column. None unless given.
#:rules- Which rules there are inside the borders. RulesHeader unless given.
#:widths- How wide each column is, from the first; a column not given a rule is as wide as its content.
#:overflow- What a cell wider than its column does. OverflowPush unless given.
- returns
- A block.
(show (tabular [["a" nl "bbb"]
["1" nl "2"]]))
;; a 1
;; bbb 2
(show (tabular [["a" nl "bbb"]
["1" nl "2"]]
#:sep " | "))
;; a | 1
;; bbb | 2
;; A vec of columns, each a vec of blocks, with nl between its cells.
;; Here the brackets in brackets are right: a table is a vec of vecs.
(show (tabular [["tea" nl "gaiwan" nl "kettle"]
[3 nl 1240 nl 90]]
#:sep " " #:align [AlignLeft AlignRight]))
;; tea 3
;; gaiwan 1240
;; kettle 90
Laid out as columnar lays them out, but with each column padded
to the width its own content asks for, and nothing cut. A left-aligned
last column is not padded at all. A column with fewer lines than the
others contributes blanks, and blanks at the end of a line are left out,
separators and all. The table starts on a fresh line, and every row ends
with a newline.
See also: tabular/rows, columnar, Align
tabular/rows
function ↑ Functions
(: tabular/rows (-> (Vec (Vec Block)) (#:sep string) (#:align (Vec Align)) (#:style TableStyle) (#:titles (Vec Block)) (#:rules TableRules) (#:widths (Vec ColumnWidth)) (#:overflow Overflow) Block))
(tabular/rows rows #:sep " " #:align [] #:style StyleNone #:titles [] #:rules RulesHeader
#:widths [] #:overflow OverflowPush)
A table given as rows, each column as wide as its widest cell.
rows- The rows, each a vec of cells, a block per cell; a newline in a cell gives its row another line.
#:sep- Written between the columns, without borders. A space unless given.
#:align- The alignment of each column, from the first; a column not given one is AlignLeft.
#:style- How the borders are drawn. StyleNone unless given.
#:titles- A header row, a block per column. None unless given.
#:rules- Which rules there are inside the borders. RulesHeader unless given.
#:widths- How wide each column is, from the first; a column not given a rule is as wide as its content.
#:overflow- What a cell wider than its column does. OverflowPush unless given.
- returns
- A block.
(show (tabular/rows [["name" "n" "time"]
["sort" 10 "1.2 µs"]
["sort" 1000 "210 µs"]]
#:sep " "
#:align [AlignLeft AlignRight AlignRight]))
;; name n time
;; sort 10 1.2 µs
;; sort 1000 210 µs
;; A cell is one block. A cell of several pieces is a vec in the row,
;; and a newline in it gives its row another line.
(show (tabular/rows [["tea" [3 " cups"]]
["kettle" ["boils in" nl "4 minutes"]]]
#:style StyleAscii))
;; +--------+-----------+
;; | tea | 3 cups |
;; | kettle | boils in |
;; | | 4 minutes |
;; +--------+-----------+
;; Rows made from data. A function with a signature makes each row, so
;; that the literal in it is read as blocks.
(: item-row (-> Item (Vec Block)))
(defun (item-row i)
[(record-ref i name) (numeric (record-ref i price) #:precision 2) (record-ref i stock)])
(show (tabular/rows (vec-map item-row stock)
#:titles ["Item" "Price" "Stock"]
#:align [AlignLeft (AlignOn #\.) AlignRight]
#:style StyleAscii))
;; +-----------+---------+-------+
;; | Item | Price | Stock |
;; +-----------+---------+-------+
;; | Green tea | 3.50 | 12 |
;; | Gaiwan | 1240.00 | 2 |
;; | Kettle | 89.90 | 0 |
;; +-----------+---------+-------+
The same table as tabular makes of the columns these rows are
made of. It is table-row on each row, with the layout that
table-widths gives them; rows of different lengths are fine, a
short one ending early.
See also: tabular, table-row, TableStyle
table-layout
function ↑ Functions
(: table-layout (-> (Vec int) (#:sep string) (#:align (Vec Align)) (#:style TableStyle) TableLayout))
(table-layout widths #:sep " " #:align [] #:style StyleNone)
A layout for table-row: the width of each column, settled before any row is written.
widths- The width of each column, in columns as current-string-width counts them.
#:sep- Written between the columns, without borders. A space unless given.
#:align- The alignment of each column, from the first; a column not given one is AlignLeft.
#:style- How the borders are drawn. StyleNone unless given.
- returns
- A TableLayout.
See also: table-row, table-widths, table-top, table-rule, table-bottom
table-top
function ↑ Functions
(: table-top (-> TableLayout Block))
(table-top layout)
The line above a table laid out a row at a time.
layout- The table's layout.
- returns
- A block: the line, or nothing for a style without one.
(def layout (table-layout [6 5] #:style StyleAscii #:align [AlignLeft AlignRight]))
(show [(table-top layout) (table-row layout ["Item" "Price"]) (table-rule layout)
(table-row layout ["tea" "3.50"]) (table-bottom layout)])
;; +--------+-------+
;; | Item | Price |
;; +--------+-------+
;; | tea | 3.50 |
;; +--------+-------+
See also: table-row, table-rule, table-bottom
table-rule
function ↑ Functions
(: table-rule (-> TableLayout Block))
(table-rule layout)
A rule across a table laid out a row at a time: under its header, or between two rows.
layout- The table's layout.
- returns
- A block: the rule, or nothing for a style without rules.
See also: table-top, table-row, table-bottom
table-bottom
function ↑ Functions
(: table-bottom (-> TableLayout Block))
(table-bottom layout)
The line under a table laid out a row at a time.
layout- The table's layout.
- returns
- A block: the line, or nothing for a style without one.
See also: table-top, table-row, table-rule
table-widths
function ↑ Functions
(: table-widths (-> (Vec (Vec Block)) (Vec int)))
(table-widths rows)
The width each column of some rows asks for: its widest cell.
rows- The rows, each a vec of cells, a block per cell.
- returns
- A vec with a width for every column any of the rows has.
(table-widths [["name" "median"] ["fib 20" "14.9 µs" "note"]])
;; [6 7 4]
See also: table-layout, tabular/rows
table-row
function ↑ Functions
(: table-row (-> TableLayout (Vec Block) Block))
(table-row layout row)
One row of a table, laid out to a layout fixed beforehand, so that it can be written as soon as it is known.
layout- The widths, alignments and separator, from table-layout.
row- The cells, a block each; a newline in a cell gives the row another line.
- returns
- A block.
;; Each result printed when it is measured, under a heading printed first.
(def layout (table-layout [8 8] #:sep " " #:align [AlignLeft AlignRight]))
(show (table-row layout ["name" "time"]))
(loop (:for (Tuple name t) results)
(:do (show (table-row layout [name t]))))
;; name time
;; sort 1.2 µs
Every cell is padded to its column’s width as its alignment says, and a left-aligned last cell is not padded at all. A cell wider than its column is not cut: it pushes the rest of its row to the right, and the rows after it are not affected. Cells past the columns of the layout are written as they are, with the separator before each. Empty cells at the end of a line are left out, separators and all. The row starts on a fresh line and ends with a newline.
See also: table-layout, table-widths, tabular/rows
table-block
function ↑ Functions
(: table-block (-> Table Block))
(table-block t)
A table literal as a block.
t- The table.
- returns
- A block, laid out as tabular/rows lays out a table.
See also: Table, show-table, tabular/rows
show-table
function ↑ Functions
(: show-table (-> Table void))
(show-table t)
Writes a table literal to the current output port.
t- The table.
See also: Table, table-block, show
Values
current-pad
value ↑ Values
(: current-pad (Param char))
The character padding is made of. A space unless set.
Padding that is not a whole number of pad characters wide is as many as
fit. Words in wrapped and justified are separated by it
too.
See also: with-pad, padded/left, space-to
current-width
value ↑ Values
(: current-width (Param int))
The right margin: what wrapped, justified and columnar fill up to. 78 unless set.
current-ellipsis
value ↑ Values
(: current-ellipsis (Param string))
What replaces the part a trim cuts off. Empty, which cuts silently, unless set.
(with-fmt current-ellipsis "..." (trimmed/right 6 "abcdefgh")) ; abc...
It takes its room out of the content, so a trimmed text is never wider than asked for. An ellipsis wider than that is cut itself.
See also: trimmed/right
current-string-width
value ↑ Values
(: current-string-width (Param (-> string int)))
How many columns a string takes. Its character count unless set.
;; An astral character takes two columns on a terminal.
(defun (astral-width s)
(string-fold (fun (ch acc) (+ acc (if (> (char->integer ch) 65535) 2 1))) 0 s))
(with-fmt current-string-width astral-width (padded/left 5 "😀"))
Everything that measures or cuts goes through it: padding, trimming,
wrapping and tables. The column a BRun is handed is not: it
counts characters.
See also: width-of
current-precision
value ↑ Values
(: current-precision (Param int))
Digits after the decimal separator. Negative, the default, is as many as the number needs.
As many as it needs is what double->string would give. A
precision rounds.
See also: numeric
current-comma
value ↑ Values
(: current-comma (Param int))
Digits per group in the integer part. Zero, the default, is no grouping.
See also: numeric, numeric/comma, current-comma-sep
current-comma-sep
value ↑ Values
(: current-comma-sep (Param char))
The character between groups of digits. A comma unless set.
See also: current-comma, numeric
current-decimal-sep
value ↑ Values
(: current-decimal-sep (Param char))
The decimal separator. A full stop unless set.
See also: numeric, current-comma-sep
current-signed
value ↑ Values
(: current-signed (Param bool))
Whether a number with no sign of its own gets a leading +. #f unless set.
See also: numeric
nothing
value ↑ Values
(: nothing Block)
An empty block that writes nothing, suitable as a no-op branch in conditionals.
(render->string ["a" (if verbose? nl nothing) "b"])
;; The arm of a match that writes nothing.
(show ["Hello, "
(match title
((Some t) [t " "])
(None nothing))
name nl])
See also: each
fl
value ↑ Values
(: fl Block)
A fresh line: a newline, unless the cursor is already at the start of one.
(render->string ["a" nl fl "b"]) ; "a\nb"
(render->string [fl "a"]) ; "a"
See also: nl
Macros
with-fmt*
macro ↑ Macros
(with-fmt* ((p v) ...) block ...)
Runs blocks with settings bound, written as parameterize is.
p- A parameter: one of this module's settings, or one of your own.
v- Its value while the blocks run.
block ...- The blocks, run in order.
(show (with-fmt* ((current-width 24)
(current-ellipsis "…"))
(trimmed/right 8 "A green tea from the hills") nl
(wrapped "A green tea from the hills, picked in spring and rolled by hand.")))
;; A green…
;; A green tea from the
;; hills, picked in spring
;; and rolled by hand.
with-fmt for each setting, the first outermost, around the
blocks as a vec. The values are computed where the form is, when the
block is built, as the arguments of with-fmt are. With no
settings it is the blocks alone.