Documentation
¶
Overview ¶
Package ask draws a question of the shared type (internal/core/question) in a plain Terminal (spc-2610030911534855, itd-2610030810370060): the layout from a question to lines at a width and a colour rung, and the sanitising every part passes before it is measured or drawn.
The layout is pure: it takes the window's width and the colour rung and returns the lines, so every layout is a golden test with an injected width. It is a front door, so it holds no limit of its own: how much each part may hold is the question package's check, and a question taller than the window is drawn whole for the Terminal to scroll.
Index ¶
- Constants
- Variables
- func Draw(a question.Ask, f Frame) []string
- func Layout(a question.Ask, width int, mode term.ColorMode) []string
- func Prepare(a question.Ask) (question.Ask, error)
- func Safe(a question.Ask) question.Ask
- type Answer
- type Frame
- type Key
- type KeyKind
- type Mode
- type Outcome
- type Picker
- func (p *Picker) Above() int
- func (p *Picker) Answers() []Answer
- func (p *Picker) Apply(k Key) Outcome
- func (p *Picker) Below() int
- func (p *Picker) Collapsed(f Frame) []string
- func (p *Picker) Current() int
- func (p *Picker) Lines(f Frame) (head, live []string)
- func (p *Picker) SetWindow(n int)
- func (p *Picker) Shown() []int
- func (p *Picker) Tab() int
- func (p *Picker) Typed() string
- type Refusal
- type Terminal
Constants ¶
const ExitInterrupted = 130
ExitInterrupted is the exit code a front door ends with on ErrInterrupted: 128 plus SIGINT, as a shell reports an interrupted command.
const NumberedHint = "Type a number, or part of a name to narrow the list, then Enter."
NumberedHint is the numbered reader's one instruction, written beneath every list it draws.
const TypedHint = "Type a number, or type your answer, then Enter."
TypedHint is the numbered reader's instruction beneath a question with a typed part: a line that is not a number is the answer itself.
const TypedPrefix = "or type:"
TypedPrefix opens the line the typed part is drawn on.
Variables ¶
var ErrInterrupted = errors.New("interrupted")
ErrInterrupted is Ctrl-C at a question: the terminal is restored and a newline written, nothing is recorded for the question, and the front door keeps the answers already given and exits 130.
Functions ¶
func Draw ¶
Draw draws a in the frame f, top to bottom: the chip line (the chip, then the tab strip when a has more than one part), the material (each paragraph wrapped on its own, a paragraph of lines a line for each, each list item with a hanging indent), the ask, the options numbered from 1 with Later as the last number, and the Now: and Change later: lines the question carries. One blank line separates each.
Every part is sanitised first (Safe), so widths are of what is drawn. Prose is wrapped at min(width, 80) less its indent. An option's label sits on its numbered line with its meaning beneath it, unless the window is wider than 80 and holds the label column and a meaning column of at least 60, when the meaning sits beside the label, wrapped at a measure of at most 80.
func Layout ¶
Layout draws a for a window width columns wide at the colour rung mode: the first part, its first option current, the marks of a UTF-8 locale.
func Prepare ¶
Prepare holds a to the structural check (question.Check) and returns it sanitised (Safe): the one door a question passes before it is drawn. Until the companion bundle's limits are run on this path, a question drawn in a plain Terminal is held to the structural check alone (spc-2610030911534855, Scope "Out"). A refused question is returned as a *Refusal naming every finding.
Types ¶
type Answer ¶
type Answer struct {
ID string
Value string
Label string
// Later is true when the way to decide later was chosen.
Later bool
// Typed is true when the answer is text typed for the question's typed
// part: Value is then the text as typed, trimmed.
Typed bool
}
Answer is one part's answer: the question's id and the value chosen, as the question carried them, with the label drawn for it.
type Frame ¶
type Frame struct {
Width int
Mode term.ColorMode
// ASCII draws the marks without a UTF-8 locale (term.UTF8Locale false):
// '>' for the glyph and '|' between tabs.
ASCII bool
// Tab is the part drawn, counted from zero, of an Ask with more than one.
Tab int
// Current is the current option, counted from zero; the number of options
// is Later. A value outside that range marks no option.
Current int
}
Frame is how a question is drawn: the window's width, the colour rung, the locale, and which part and which option are current.
type Key ¶
Key is one key press.
func DecodeKeys ¶
DecodeKeys decodes what one read from a raw terminal holds. A sequence is taken whole or not at all: an unknown CSI or SS3 sequence is dropped, a lone ESC at the end of the read is Escape (a terminal sends a sequence in one write), and a character split across reads is returned in rest for the next. Other control bytes are dropped.
type KeyKind ¶
type KeyKind int
KeyKind is what one key press is to the answer loop.
const ( KeyRune KeyKind = iota // a printable character: Key.Rune KeyUp KeyDown KeyPageUp KeyPageDown KeyHome KeyEnd KeyEnter KeyBackspace KeyEscape KeyLeft KeyRight KeyTab KeyInterrupt // Ctrl-C, the byte 0x03 under raw mode KeySuspend // Ctrl-Z, the byte 0x1a under raw mode )
The keys the arrow-key list acts on.
type Mode ¶
type Mode int
Mode is how the answer loop takes a choice.
func SelectMode ¶
SelectMode picks the mode (spc-2610030911534855, "The numbered fallback"; open question 2, decided to honour both variables): numbered under TERM=dumb, which cannot move its cursor; numbered for ABCD_ACCESSIBLE non-empty, else ACCESSIBLE non-empty, so a screen-reader user who cannot set a file first gets it for one session, and abcd's own variable is the one named when both are set; numbered for interview.list set to numbered; arrows otherwise. why says which, or is empty for the default.
type Picker ¶
type Picker struct {
// contains filtered or unexported fields
}
Picker is the arrow-key list's state (spc-2610030911534855, "The answer loop: Arrow keys first, typing to narrow"): a pure function of key events, with no terminal, so every behaviour is a test of keys in and state out.
Up and Down move the current entry; Page Up and Page Down move a window; Home and End go to the ends, Later being the last. A printable character narrows: the typed text is matched by question.Matches against each answer's label and value, and Backspace widens, Escape clears. Text that is all digits is a number instead: it narrows nothing, and Enter chooses the entry with that number, which is the entry's place in the full list, so a number read off a narrowed list chooses what it names. Enter otherwise chooses the current entry, except that on a question with a typed part, Enter on text no answer matches answers with the text. Left, Right and Tab move between the parts of a tabbed Ask, and the Ask is done when every part is answered. Later is never narrowed away: when nothing matches, it is the current entry.
func NewPicker ¶
NewPicker is the list for a, its first part shown, each part's default entry current (its first, where the question names no default), nothing typed.
func (*Picker) Collapsed ¶
Collapsed is what stays on screen once the Ask is answered: one plain line per part, "› <chip>: <label>".
func (*Picker) Current ¶
Current is the current entry, as an index into the part's options; the number of options is Later.
func (*Picker) Lines ¶
Lines draws the list in the frame f: head is the part above the answers (the chip line, the material and the ask, then a blank line), drawn once per part; live is the rest, redrawn as keys arrive: the filter line, the window of kept answers with how many lie above and below, Later, and the Now: and Change later: lines. f's Tab and Current are the picker's.
type Terminal ¶
type Terminal struct {
// In is the terminal the person answers at.
In *os.File
// Out is where the question is drawn.
Out io.Writer
// Getenv reads the environment (TERM, ABCD_ACCESSIBLE, ACCESSIBLE, and
// COLUMNS and LINES for a size the terminal does not answer).
Getenv func(string) string
// Mode is the colour rung (term.ResolveColorMode).
Mode term.ColorMode
// ASCII draws the marks without a UTF-8 locale.
ASCII bool
// Roots are where Put reads the interview.list setting from
// (layered.InterviewList): the machine's ~/.abcd.noindex/config.json under
// Roots.Home, a repository's under Roots.Repo refused.
Roots layered.Roots
}
Terminal is where the answer loop puts a question: the keyboard it reads, the stream it draws on, and how it draws. The question is drawn on Out, which a front door makes stderr, so a --json result on stdout stays clean.
A caller under signal.NotifyContext (or any handler of its own for SIGINT, SIGTERM or SIGHUP) must not draw: while an arrow-key list is up, such a signal from outside restores the terminal and is raised again with its default action (term.RawSession), so the process ends as on an unhandled signal, and neither the caller's handler nor any deferred cleanup runs.
func (Terminal) Put ¶
Put asks a at the terminal and returns its answers, one per part. It holds a to the structural check first (as Prepare does), reads interview.list through layered.InterviewList (a refusal is said in one line on Out; a repository's fault is passed over for the machine's setting, a fault in the machine's own file gives the numbered reader), then takes the answer in the mode SelectMode picks; an arrow-key list the terminal cannot take (TERM=dumb, or raw mode refused) falls to the numbered reader with one line on Out saying so, never a failed interview. Ctrl-C returns ErrInterrupted.