sysl

Module

sysl.term

What a terminal understands: the escape sequences that colour text, emphasise it, and move the cursor about.

Import import sysl.term
Requires
no alloc

What a terminal understands: the escape sequences that colour text, emphasise it, and move the cursor about.

Every one of these is a const string, and that is the whole design. A string literal is immortal – it lives in the program’s own image with no owner and no reference count (04) – so naming forty of them costs nothing at run time and nothing in storage, and a module that has declared @no_alloc can reach every one. That is why this module declares it too: colouring a line is exactly what a program with no allocator most wants to do, and a facility it could not use would be no facility at all.

A sequence is written where the text it affects is written, because that is what it is – a mark in the stream rather than a property of a string:

print(f”{red}{bold}error{reset}: {msg}”)

What is not here, and why

Nothing that asks whether the terminal is a terminal. Whether escapes should be emitted at all – output redirected to a file, NO_COLOR set, a dumb terminal – is a question about the process and its environment, so answering it needs posix. This module asks for no capability at all, and putting the answer here would take that away from every program that only wanted to name a colour.

So the answer lives in sysl.posix.tty, which requires what it needs and leaves this file asking for nothing – and the namespace is the honest place for it, since isatty and termios are what it is made of. A hosted program imports both and asks once:

val paint = color()

print(f”\({if paint then red else ""}error\){if paint then reset else “”}: ${msg}”)

The split is what serves both audiences instead of choosing between them, and it shows up in the import, which is honest about what the second one costs.

Nothing that takes a number. Moving the cursor to a row and column means building ESC [ row ; col H, and building a string is an allocation – which is the one thing the whole module is arranged to avoid. The sequences below are the ones whose text is fixed. A program that wants the others has f"\u{1b}[{row};{col}H" and knows what it is spending.

Reading the numbers

These are the SGR parameters, and the arithmetic between them is a real specification rather than a coincidence: a background is its foreground plus ten, and a bright colour is its ordinary one plus sixty. TermTests asserts both relations across all eight colours, because a table of forty constants transcribed by hand has exactly one interesting failure – a wrong number – and it is invisible in review.

Index

black blink blue bold bright_black bright_blue bright_cyan bright_green bright_magenta bright_red bright_white bright_yellow clear_below clear_line clear_screen clear_to_line_end cyan default_color dim green hidden hide_cursor home italic magenta on_black on_blue on_bright_black on_bright_blue on_bright_cyan on_bright_green on_bright_magenta on_bright_red on_bright_white on_bright_yellow on_cyan on_default on_green on_magenta on_red on_white on_yellow red reset restore_cursor reverse save_cursor show_cursor strike underline white yellow

Constants

black

const black: string = ""
const blink: string = ""

blue

const blue: string = ""

bold

const bold: string = ""

bright_black

const bright_black: string = ""

bright_blue

const bright_blue: string = ""

bright_cyan

const bright_cyan: string = ""

bright_green

const bright_green: string = ""

bright_magenta

const bright_magenta: string = ""

bright_red

const bright_red: string = ""

bright_white

const bright_white: string = ""

bright_yellow

const bright_yellow: string = ""

clear_below

const clear_below: string = ""

From the cursor onwards, which is what a program redrawing the tail of something wants.

clear_line

const clear_line: string = ""

clear_screen

const clear_screen: string = ""

The whole screen, and the whole line the cursor is on. Neither moves the cursor, which is why clear_screen is nearly always written with home after it.

clear_to_line_end

const clear_to_line_end: string = ""

cyan

const cyan: string = ""

default_color

const default_color: string = ""

The colour a terminal was already using, which is not the same as reset: this ends the colour and leaves the emphasis where it was.

dim

const dim: string = ""

green

const green: string = ""

hidden

const hidden: string = ""

hide_cursor

const hide_cursor: string = "[?25l"

Hiding it is what stops a full-screen redraw from leaving the cursor skittering across the picture. A program that hides it owns showing it again, including on the way out.

home

const home: string = ""

The top left corner.

italic

const italic: string = ""

magenta

const magenta: string = ""

on_black

const on_black: string = ""

on_blue

const on_blue: string = ""

on_bright_black

const on_bright_black: string = ""

on_bright_blue

const on_bright_blue: string = ""

on_bright_cyan

const on_bright_cyan: string = ""

on_bright_green

const on_bright_green: string = ""

on_bright_magenta

const on_bright_magenta: string = ""

on_bright_red

const on_bright_red: string = ""

on_bright_white

const on_bright_white: string = ""

on_bright_yellow

const on_bright_yellow: string = ""

on_cyan

const on_cyan: string = ""

on_default

const on_default: string = ""

on_green

const on_green: string = ""

on_magenta

const on_magenta: string = ""

on_red

const on_red: string = ""

on_white

const on_white: string = ""

on_yellow

const on_yellow: string = ""

red

const red: string = ""

reset

const reset: string = ""

Ends everything: colour, background and every attribute at once. ANSI has no way to end one attribute and leave the others, which is why the two below exist for the common case of wanting a colour back without losing an emphasis.

restore_cursor

const restore_cursor: string = ""

reverse

const reverse: string = ""

save_cursor

const save_cursor: string = ""

One remembered position – the terminal’s own, so nesting two of these does not work and the second save is the one that is restored.

show_cursor

const show_cursor: string = "[?25h"

strike

const strike: string = ""

underline

const underline: string = ""

white

const white: string = ""

yellow

const yellow: string = ""

Search

Esc
to navigate to open Esc to close