Skip to content
heff

std.cli.interactive

A program’s commands, as std.cli says them, learned by using them: a list of the commands with what each does, filtered by what is typed; a command picked is a form of its positionals and its flags — each with what it is, its values, whether it is wanted — and the command line the form makes, shown as it is filled in, with what is wrong with it yet. Done, it is the words of that command line, for the program to run as if they had been given it (CLI.readLine). An app of std.terminal.widget’s, pure: std.terminal runs it on the terminal (Terminal.interact (Interactive.app "heff" (Interactive.entries cs))); heff with no words runs heff’s own. A command line that lacks only what its command wants (heff run, no file) opens on the command’s form alone, filled in with what it has (lacking, prompt). Import it by a name, import std.cli.interactive as Interactive.

Keys: on the list, up and down (or j and k, Ctrl-N and Ctrl-P) to move, a page, Home and End; anything typed filters it by the commands’ names (/ first to type a j, k or q); Enter or Tab opens the command; Esc clears the filter, or ends, as q does. On a form, Tab and down to the next field, Shift-Tab and up to the one before; a field of words is typed in as a shell’s line is — in a field of paths, the names its last word completes to shown under it, and Tab completing it as far as they all go (listed lists them) — a choice turned by left and right (or space); Enter ends, with the command line, once nothing is wrong with it; Esc goes back to the list (and q, on a choice), or, on a form alone, ends.

Entry

type Entry = { name : String, doc : String, shape : CLI.Shape }

A command, as the list and its form show it: its name, what it does, and the words it takes.

entries

entries : {r : Type} -> List (CLI.Command r) -> List Entry

The commands the usage shows, as entries.

Part

data Part = Positional CLI.Pos | Flagged CLI.Flag

What a field of a form is for: a positional, or a flag.

Input

data Input = Typed Widget.Field | Picked Widget.Choice

What a field holds: words typed, or a value chosen.

Slot

type Slot = { part : Part, input : Input }

A field of a form.

Form

type Form = { entry : Entry, slots : List Slot, at : Int }

A command’s form: the command, its fields — its positionals, then its flags — and the one being filled in.

formOf

formOf : Entry -> Form

The command’s form, empty.

formWords

formWords : Form -> List String

The words of the command line the form makes, after the command’s name.

formProblem

formProblem : Form -> Maybe CLI.Problem

What is wrong with the command line the form makes, if anything.

lacking

lacking : List Entry -> List String -> Maybe Form

The form of the command the words call, filled in with them, where all that is wrong with them is a positional or a flag wanted (Missing, MissingFlag): the first field still wanted is the one being filled in.

>>> maybe Nil formWords (lacking (entries CLI.commandsEx) ["compile", "a.hf", "-bc"])
["a.hf", "--backend=c"]
>>> maybe (0 - 1) (\(fm : Form) -> fm.at) (lacking (entries CLI.commandsEx) ["compile", "a.hf", "-bc"])
1
>>> maybe (0 - 1) (\(fm : Form) -> fm.at) (lacking (entries CLI.commandsEx) ["compile", "a.hf", "a.mjs"])
3
>>> maybe (0 - 1) (\(fm : Form) -> fm.at) (lacking (entries CLI.commandsEx) ["compile", "a.hf", "a.mjs", "b", "-bc"])
-1
>>> maybe (0 - 1) (\(fm : Form) -> fm.at) (lacking (entries CLI.commandsEx) ["compile", "a.hf", "-b", "wasm"])
-1
>>> maybe (0 - 1) (\(fm : Form) -> fm.at) (lacking (entries CLI.commandsEx) ["compile", "-h"])
-1
>>> maybe (0 - 1) (\(fm : Form) -> fm.at) (lacking (entries CLI.commandsEx) ["run"])
0

quoted

quoted : String -> String

A word as a shell is to be given it: as it is, or in single quotes.

>>> List.map quoted ["a.hf", "two words", "it's", ""]
["a.hf", "'two words'", "'it'\\''s'", "''"]

commandLine

commandLine : String -> String -> List String -> String

The command line: the program, the command, its words, as a shell is to be given them.

Listing

type Listing = { dir : String, names : List String }

A directory, as a path typed in a field is completed in it: the directory as typed ("", the working one), and each name in it, a directory’s with a / after it.

pathsFor

pathsFor : CLI.Complete -> Listing -> String -> List String

What a path being typed completes to, of the kind: the names listed in its directory that start as the rest of it does and that the kind takes — one that starts with . only where the rest does.

>>> pathsFor (Files ".hf") { dir = "src/", names = ["a.hf", "ab/", "b.hf", "c.txt", ".x/"] } "src/a"
["src/a.hf", "src/ab/"]
>>> pathsFor Dirs { dir = "", names = ["a.hf", "ab/", ".x/"] } ""
["ab/"]
>>> pathsFor Dirs { dir = "", names = ["ab/", ".x/"] } "."
[".x/"]
>>> pathsFor (Files ".hf") { dir = "", names = ["a.hf"] } "src/"
[]

Model

type Model =
{ prog : String
, entries : List Entry
, filter : Widget.Field
, typing : Bool
, menu : Widget.Menu
, form : Maybe Form
, alone : Bool
, listing : Maybe Listing
}

Where the app is: the program’s name, its commands, the filter typed and whether it is being typed (j, k and q are then letters of it), which of the commands it lets through is picked, the form open, if one is, whether the form is all there is (no list to go back to: prompt), and the directory listed for the path being typed in it, if one is (listed).

start

start : String -> List Entry -> Model

The list of the commands, nothing typed.

matching

matching : Model -> List Entry

The commands the filter lets through: those whose names have it.

picked

picked : Model -> Maybe Entry

The command picked, if the filter lets any through.

Result

type Result = Maybe (List String)

step

step : Widget.Key -> Model -> Widget.Step Model Result

What a key makes of the app: on the list, or on the form open.

view

view : Widget.Size -> Model -> Widget.Frame

How the app is shown on a terminal of the size: the list, or the form open.

app

app : String -> List Entry -> Widget.App Model Result

The app: the commands of the program prog, to pick one of and fill in; what it comes to is the words of the command line made (the command’s name first), or nothing, where it was left.

prompt

prompt : String -> List Entry -> Form -> Widget.App Model Result

The app opened on the form, for what a command line lacks: as the app, but where it would go back to the list (Esc, q on a choice) it ends, with nothing.

listed

listed : {e : HFunctor} -> {{disk : filesystem <: e}} -> Model -> Co e Model

The app’s state with the directory listed that the path being typed in the form’s field is in (what Tab completes it to, and what is shown under it), where it is not yet: for Terminal.interactWith to give each state the app comes to.