On this page:
1.1 ECMA-48 CSI
ecma-csi-tokenize
ecma-csi-start?
ecma-csi-remove
ecma-csi-type
1.2 ECMA-48 SGR
sgr-rgb
sgr-state
empty-sgr-state
sgr-list?
sgr-lines?
ecma-csi-sgr-parse
sgr-list-compact
sgr-list->csi-string
ecma-csi-sgr-parse-lines
sgr-list-last-state
1.3 Box Drawing
line-weight-spec/  c
line-weight/  c
line-type-spec/  c
line-type/  c
line-style-spec-token/  c
line-style-spec/  c
line-style
no-line-style
merge-line-weight
merge-line-style
parse-line-style-spec
line-direction/  c
straight-line-char
junction
merge-junction
junction->char
8.12

1 Text Output🔗ℹ

These modules deal with the low-level terminal text output.

Any text, that is sent to the terminal is scanned for any control codes and their sequences which may alter the visual properties of characted printed later, move cursor on the screen, insert or erase characters, lines or whole screen and many other actions given terminal supports. What remains after processing these control codes and sequences is the text to be shown on the screen.

The ECMA-48 standard specifies many control codes which can be divided - with respect to this implementation - into the following categories:

Always remember that the CSI SGR processing does not perform any actual character rendering and only allows for easier formatting of strings with these sequences. In the end they are passed to the actual terminal the program is running in. If in doubt, try your sequences without any processing first!

Only a very limited subset of the whole terminal capabilities as specified in the ECMA-48 standard is supported by these modules. Anything non-CSI gets passed through and any CSI except SGR gets filtered. All SGR codes are processed and most of the standard - or commonly used - features are captured and properly processed. Unknown parts of SGR sequences are discarded though.

The actual cell borders are drawn using Unicode character from the Box Drawing Unicode block #x2500-#x257F. All the possible combinations of light and heavy border junctions are supported - there are 81 of them (including blank space). Because not all combinations of double-line characters are present in Unicode, general doubly-lined borders cannot be properly supported.

1.1 ECMA-48 CSI🔗ℹ

 (require uni-table/private/ecma-csi) package: uni-table

This module implements a subset of ECMA-48 CSI (Control Sequence Introducer) decoding, allowing for capturing plain text and SGR (Select Graphic Rendition) state snapshots anytime SGR control sequence is found in the text. Other control sequences are discarded as they would interfere with table output.

The SGR module uses ecma-csi-tokenize from this module to tokenize input strings into a list of plain strings and string containing only the control sequences starting with CSI. For further processing ecma-csi-type is used to determine which type of CSI sequence it is.

procedure

(ecma-csi-tokenize str)  (listof string?)

  str : string?
Converts the string str into a list of strings such that CSI sequences are separated from other text.

A CSI sequence starts with characters #\u001B #\[.

procedure

(ecma-csi-start? str)  boolean?

  str : string?
Returns true if the string str starts with CSI.

procedure

(ecma-csi-remove str)  string?

  str : string?
Returns a string with all ECMA-48 control sequences stripped.

procedure

(ecma-csi-type str)  char?

  str : 
(and/c non-empty-string?
       ecma-csi-start?)
Returns the type of ECMA-48 control sequence. The type is eq? to the last character of the sequence.

1.2 ECMA-48 SGR🔗ℹ

 (require uni-table/private/ecma-sgr) package: uni-table

This module implements the internal SGR (Select Graphic Rendition) state machine of ECMA-48 terminals. The following features are implemented:

Other features are not (yet) supported.

Use ecma-csi-sgr-parse-lines to conver a string possibly containing CSI SGR sequences into a list of sgr-list?s - list of plain strings and snapshots of sgr-states. Each line will become one sgr-list? in the resulting list.

Conversion from sgr-list? to string with CSI sequences can be done using sgr-list->csi-string. The table cell renderer then uses sgr-line-fill-block to ensure the lists of sgr-list? are rectangular.

struct

(struct sgr-rgb (r g b)
    #:transparent)
  r : (integer-in 0 255)
  g : (integer-in 0 255)
  b : (integer-in 0 255)
Color red/green/blue triplet representation to be used in SGR for setting foreground or background direct color. SGR codes (38 2 r g b) and (48 2 r g b).

struct

(struct sgr-state (intensity
    italic
    underline
    blink
    reverse-video
    crossed-out
    foreground
    background)
    #:transparent)
  intensity : (or/c #f 'bold 'half-bright)
  italic : boolean?
  underline : (or/c #f 1 2)
  blink : boolean?
  reverse-video : boolean?
  crossed-out : boolean?
  foreground : 
(or/c #f
      (integer-in 0 255)
      sgr-rgb?)
  background : 
(or/c #f
      (integer-in 0 255)
      sgr-rgb?)
Represents the internal SGR state of ECMA-48 text terminal.
Default (empty) SGR state of ECMA-48 text terminal.
A flat-contract? matching (listof sgr-list?) which represents multiple lines of sgr-list? texts.

procedure

(ecma-csi-sgr-parse 
  str 
  [#:initial-state initial-state 
  #:reset-state reset-state 
  #:prepend-initial-state prepend-inititial-state]) 
  sgr-list?
  str : string?
  initial-state : sgr-state? = empty-sgr-state
  reset-state : sgr-state? = initial-state
  prepend-inititial-state : boolean? = #f
Parses given string? str as sgr-list? discarding any non-SGR ECMA-48 control sequences. Assumes initial-state as initial terminal state.

procedure

(sgr-list-compact lst)  sgr-list?

  lst : sgr-list?
Compacts given sgr-list? by removing redundant sgr-state? snapshots.

procedure

(sgr-list->csi-string lst    
  [#:initial-state initial-state])  string?
  lst : sgr-list?
  initial-state : sgr-state? = empty-sgr-state
Serializes given sgr-list? lst as string? assuming initial-state as the initial terminal state.

procedure

(ecma-csi-sgr-parse-lines str 
  [#:initial-state initial-state]) 
  sgr-lines?
  str : string?
  initial-state : sgr-state? = empty-sgr-state
Parses given string? str as a list of sgr-list? representing individual parsed lines using ecma-csi-sgr-parse with initial-state assumed as initial terminal state.

The current sgr-state? is emitted at the beginning of each line.

procedure

(sgr-list-last-state lst 
  [#:initial-state initial-state]) 
  sgr-state?
  lst : sgr-list?
  initial-state : sgr-state? = empty-sgr-state
Returns SGR state at the end of given list.

1.3 Box Drawing🔗ℹ

 (require uni-table/private/box-drawing) package: uni-table

This module implements two-dimensional orthogonal grid box drawing using two possible line weights: light and heavy lines.

The Unicode Box Drawing Characters block contains all 80 combinations (81 including generic space character) for independently drawing no, light or heavy line from the center of the character box in each of four major grid directions.

As general support for double lines is not possible with current Unicode specification, double lines box drawing is not implemented.

Straight lines can be solid or dashed. Lines in junctions are too short and therefore can only be solid.

Matches valid weight specifications:

These weight names are taken from the Unicode standard Box Drawing block U+2500-U+257F.
Matches valid weight specification like line-weight-spec/c or #f.
Matches valid line type:

For dashed lines, "double dash" characters are used in both horizontal and vertical directions.
Matches valid line type like line-type-spec/c or #f - meaning if the line is actually drawn, it will become 'solid
Matches single line-style-spec-token/c or a listof thereof.

struct

(struct line-style (weight type)
    #:transparent)
  weight : line-weight/c
  type : line-type/c
A struct representing box drawing line style.
A line-style? representing no style information.

procedure

(merge-line-weight orig over)  line-weight/c

  orig : line-weight/c
  over : line-weight/c
Merges the two line weights effectively returning the stronger one.

procedure

(merge-line-style orig    
  over    
  [#:relaxed relaxed])  line-style?
  orig : line-style?
  over : line-style?
  relaxed : boolean? = #f
Returns a new line-style? overriding weaker weight and style from orig by values in over.

The strength of the values is as follows:

Type is overriden only if over contains non-#f weight. This behaviour can be disabled by specifying #:relaxed #t.

The output of this function is identical to what would happen if the line specified in over was drawn over a line specified by orig.

procedure

(parse-line-style-spec token/s)  line-style?

  token/s : line-style-spec/c
Converts given line style specification into a valid line-style?. The ordering specified in merge-line-style applies if multiple successive values affecting either weight or type are supplied.
A flat-contract? matching valid line directions. Only 'horizontal and 'vertical symbols are valid.

procedure

(straight-line-char ls [dir])  char?

  ls : line-style?
  dir : line-direction/c = 'horizontal
Returns a char? for drawing a straight line of given style and direction.

struct

(struct junction (up left right down)
    #:transparent)
  up : line-weight/c
  left : line-weight/c
  right : line-weight/c
  down : line-weight/c
Used to represent junction - a corner or crossing - between lines of possibly different weight.

procedure

(merge-junction orig over)  junction?

  orig : junction?
  over : junction?
Merges two junctions and returns a new one that looks like the two drawn together.

procedure

(junction->char j)  char?

  j : junction?
Converts a junction? struct to char? usable for drawing corners, crossings and T-junctions of box drawing lines.