Document based terminal
- Shell 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
While an application that claimed them runs, ctrl+shift+x and ctrl+shift+z are the interrupt and kill. (title ...) is not applied: the window's title stays the terminal's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
| examples | ||
| lib/terminal | ||
| scripts | ||
| .gitignore | ||
| README | ||
| terminal.talc | ||
| VERSION | ||
terminal: a window with the shell in it. The first application: a program
in a repository of its own, built against the libraries beside it --
../canvas for drawing, ../look for the theme, ../toolkit for the window
and the run loop -- the way any program with a window is meant to be.
lib/terminal/part.talc (terminal part): the shell started in a session on
two pipes, what was said kept as a document of text
with roles and drawn from it, the line being typed
edited here and sent whole -- as ONE PART, which any
program puts in a window of its own, beside its
other parts, and more than once
terminal.talc the program: the arguments, the screen, the font, a
window around one such part, the run loop
examples/drive.talc what the regress plays at it: a value, a command
with output, an error and a status, a line recalled
and selected, the window made narrower, a drag over
the document copied and pasted, the window made
short and the wheel turned, styled text, paths found
as links and one pressed, a command folded, a secret
asked for and answered, an escape left unfinished
examples/drive-classic.talc the first steps again, in the classic look
examples/*.ppm the pictures expected at each step
scripts/build.sh builds terminal.talc into bin/terminal, against lib/
scripts/regress.sh runs it under ../screen with no card and the shell
in it, twice (the built-in look, the classic one),
and compares the pictures
Build:
scripts/build.sh
scripts/regress.sh
which need the installed toolchain (compiler on PATH), ../canvas built
(its fonts), ../look (its example themes), ../toolkit, and for the regress
../screen built and ../shell/shell. ../screen/scripts/regress.sh and ../vm
take the terminal from bin/ here.
Run it from the screen: it is what the screen's -run starts and what
super+return starts, found on PATH or beside the screen.
terminal [-shell <program>] [-columns n] [-rows n] [-font <name>]
The shell is /programs/shell unless -shell says otherwise, started with
-session: the shell and the terminal talk in talc forms, one a line, on
two pipes, and never in bytes with escape sequences in them. The
terminal sends (line "text") when enter is pressed, (interrupt) on
ctrl+x, (kill) on ctrl+z, (end) on ctrl+d and (size columns) when its
width changes; the shell answers (prompt "$ "), (text "...") and
(error "...") for what a command wrote on its two streams, (value
"...") for a value it printed, (ended n) and (bye). ../shell/README says
the shell's side.
A program may ask for the whole area with a form of ../toolkit's
(application "title" form): the terminal builds the parts in its own
font and draws them until the program ends or says (application-end),
its changes arriving as forms on the forms pipe and what is done in the
parts going back as lines on its standard input. Inside one, ctrl+x and
ctrl+z are still the interrupt and kill, unless the program claimed
them in its keys ((claim "ctrl+x"), a text's cut and undo, as the
editor does); then ctrl+shift+x and ctrl+shift+z are, which nothing can
claim. The window's title stays the terminal's.
The window begins at 80 by 24 cells of the font's M and follows the
screen from then on. The font is Dina at 8 points (dina-13) when the
screen's fonts have it, else the canvas's own plain-8x16, unless -font
names another -- any font, a proportional one included, since there is
no grid.
HOW IT IS BUILT. What was said is a DOCUMENT: blocks, each a command (the
prompt and what was typed) or the output that followed, until the shell
said it ended; a block is one text with RUNS of a role over it --
prompt, command, text, error, value, faint, or whatever a program
named -- and, once drawn, the lines it wraps into at the width it was
wrapped at. Drawing is a function of the document, the width, the font
and the theme, so a resize wraps the lines again, a new look (the
(theme ...) event, from ../desktop's chooser) only looks the colours up
again, and nothing is ever asked of the shell twice; a shell that has
gone leaves its document behind. A role's colour is the theme's
terminal-<role>, else its <role>, else ink: the built-in look and the
classic one name error, warning, good and a faint terminal-prompt. The
view is a PART, (terminal part) in lib/ here, made with ../toolkit's one
call, on a ground drawn as the theme draws a list's (flat in the
built-in look, a sunken well in the classic one), and drawn by rows: each row drawn is noted by which block, which
line and which version of it, and only rows whose note changed are
drawn again. The shell's pipe is a second source the loop watches
(toolkit-watch!). The oldest blocks are dropped past 20000, and an
output block is closed at 64 KiB and another opened, so that nothing
grows without bound.
THE PART. Everything the view is -- the document, the line, the pipes,
the rows drawn, the colours -- is one record, the part's state, and
nothing of it is the library's own: (terminal-create app font shell
directory on-link) starts a shell in `directory` and answers a part, and
a program puts it in a window beside its other parts, or makes two and
has two shells, each with its document. It reads the look's colours from
the theme it is drawn with, so a new look needs nothing of the program.
on-link, when given, is called with a link's target instead of the
target going into the line (an editor opens the file; when it answers
#f the target goes into the line after all); (terminal-run! part text)
sends a line as if typed and (terminal-end! part) ends the shell. The
control keys the part does not use (ctrl+s, say) are left to the
program, so a window with other parts keeps its keys while the shell's
line has the focus. This program is the forty lines around one such part.
The line being typed is the terminal's: left, right, home, end, shift to
select, ctrl+left and ctrl+right by word, backspace and delete
(ctrl+backspace a word), ctrl+a all, escape clears it; up and down walk
the lines sent before (what was being typed is kept for the way back);
enter sends it -- to the shell at a prompt, to the command's standard
input while one runs. Page up, page down, the wheel and the scroll bar
at the right (../toolkit's, the document its whole) scroll the
document, never past its top, and new output pulls the view down only
when it was at the bottom. A press in the document and a drag select
text there, across lines and blocks, a second press the word; a press
in the line puts the cursor, a second press selects it all. ctrl+c
copies what is selected, in the document or in the line, to the
screen's clipboard (one text for every program) and ctrl+v pastes it
into the line, newlines kept, when it comes.
A program that says what its text is -- (system console)'s
console-write-styled, the shell's forms pipe -- has it drawn in that
role's colour: ../utilities' `list` shows directories strong. A LINK
(console-write-link: text that opens something, with a target) is
drawn in the link colour and underlined, and a press on it puts its
target into the line, where it can be run or finished: ../utilities'
`find` shows every path it found as one. A SECRET (console-read-secret:
a program wants a line not to be shown as it is typed) puts the
program's prompt before the line and shows what is typed as stars; enter
sends it to the program alone, never into the document or the history,
and escape sends nothing.
FOLDING. A command and the output that followed it are a unit: a mark in
the margin before the command's first line, a triangle pointing down,
folds the unit to one row -- the command's first line, cut with "..."
where it stops fitting -- and pointing right unfolds it; a press
anywhere in the margin of the unit does the same. Hidden lines are not
copied with a selection across them. A command that FAILED says so
where a bash user looks for it: "(n)" in the error colour after its
text, folded or not, and its mark in that colour; one that ended 0
says nothing, and the shell prints nothing for a command's status
(../shell/README), so the output is all there is.
Another machine: `(run "ssh" "host")` (../ssh) runs with no terminal on
the other side, since its input here is a pipe: the lines typed go to
a shell there that reads them one by one, what it writes comes back
told apart from its errors, and its exit status is the command's. No
prompt, but clean, and `sleep` there is interrupted by ctrl+x. For
prompts from a bash host, `ssh -t -noecho host "bash --noediting -i"`;
a shell with a line editor of its own paints in escape sequences,
which are skipped here (ESC [ ... and ESC ] ... both) and leave what
it meant to say; one left unfinished when the command ends is
forgotten with it, and hides nothing of the next.
NOT HERE YET: bold for the strong role (a bold Dina to fetch); ctrl+l.
These arrive here, and
what they need of the toolkit arrives there, which is why this is a
repository of its own and not a sample under it.