Map: repl / frontend
ral/src/repl/frontend.rs declares the Frontend trait — the seam between the
session loop and the line editor. One method, read, returns
a typed Read event (Line / Edit / Interrupt / Eof); the session never
sees keybindings, escape sequences, or the buffer stack. The frontend owns
plugin sync, keybinding dispatch, continuation reads, and flushing deferred
diagnostics before returning. EditBuffer carries a re-feed buffer whose
cursor is a character offset (matching the plugin surface — see
plugins); the rustyline boundary converts to bytes.
Both backends drive the multi-line read through frontend::join_continuation,
which folds backend-supplied lines into the buffer until it parses complete
(or is abandoned by a Continuation::Discard). The loop, the newline joining,
and the abandon semantics live there once, so Ctrl-C / Ctrl-D / a read error
behave identically — abandoning the partial buffer keeps the session alive
rather than ending it (the prior per-frontend copies had drifted, letting
Ctrl-D kill the shell from the rustyline path). History saves append the
session’s own entries rather than rewriting the file, so concurrent sessions do
not clobber each other.
Three implementations live in frontend/, selected by Surface
(--surface flag or rc surface:; the capability gate forces minimal on
dumb terminals whatever was asked):
minimal.rs::MinimalFrontend— canonical-stdin fallback for dumb terminals andRAL_INTERACTIVE_MODE=minimal. No raw mode, no DECSET, no plugin features; justread_linewith a>continuation prompt.rustyline.rs::RustylineFrontend— the default editor: completion, plugin keybindings, ghost text, highlights, and rustyline history, on TTYs that support raw mode and ANSI. Its constructor wires the shell’s stdout onto rustyline’sExternalPrinter, so backgroundwatchoutput lands above the live prompt.structural.rs::StructuralFrontend— the ratatui inline-viewport projection surface (structuralfeature,--surface structural): the typed spine, worksheet, and handles matrix around the prompt, plus Tab completion (below). See repl-as-structural-surface. It drives the same in-editor plugin surface the rustyline backend does, off the shared `PluginRuntime` rather than a parallel copy: each iteration runsrun_buffer_change_hooksand reads back the fish-style ghost suggestion and highlight spans (overlaid as ratatui cells viahighlight_style::style_ratatui), accepts the ghost on right-arrow at buffer end, and matcheskeybinding_chordsagainst the keypress. A matched chord breaks the loop to aComposed::Keybindingoutcome — no newReadvariant — tears down the viewport and raw mode, then runsdispatch_keybinding, so an_ed-tuihandler (fzf, zoxide) gets the terminal exactly as the rustyline path dispatches only after leavingreadline; an_ed-pushbuffer is popped when nothing is pending.
The structural surface and exarch TUI share their prompt editor through
the prompt-editor crate (backed by edtui 0.11): the PromptEditor facade
owns all cursor positioning, key dispatch (Emacs and Vim via edtui
EditorEventHandler), height hinting, and highlight rendering. Both
frontends show the terminal native cursor in every mode, the same shape
throughout (no painted vi modal-mode block). One implementation, not a copy each.
The structural surface handles shell-line chords before the editor:
Ctrl-U kills to line-start (readline unix-line-discard, not edtui
undo), in emacs and in vi-Insert mode. vi Normal/Visual keep the edtui keymap.
Ctrl-D on a non-empty buffer deletes the char under the cursor;
only an empty buffer reads as Eof.
Completion
The completion engine is frontend-neutral: completion.rs classifies the
token under the cursor ($-variable / command-position name / path), gathers
candidates from a Sources snapshot of the live shell (PATH commands +
builtins + handlers + bindings; cwd-anchored path entries), and ranks them.
completion::complete(line, pos, &Sources) -> (replace_from, Vec<Candidate>)
is the single entry point both surfaces call. Ranking is nucleo fuzzy
matching for every surface — path-tuned for path entries, ties broken
alphabetically.
complete.rs::RalHelperis the rustyline adapter: it holds theSourcessnapshot (rebuilt each prompt viarefresh), delegatesCompleter::completeto the engine, and maps eachCandidateto a rustylinePair. It also implementsHinter/Highlighter— ghost text and syntax highlights are not computed here; they come from pluginbuffer-changehooks recorded in the `PluginRuntime`, andRalHelperonly paints what the runtime last produced.highlight_style.rs::STYLESis the one data-driven table of legal highlight styles — each row gives the name, its ANSI escape (style_ansi, which_ed-highlightvalidates against), and its ratatui cell style (style_ratatui) — so the two surfaces cannot drift.structural.rsdrives the engine as a drop-down menu band (structural/menu.rs): Tab completes the token under the cursor — a unique match is spliced in place, several open a bordered popup rendered over the top of the projection band, anchored under the token. Tab/↓ and ⇧Tab/↑ cycle the selection, Enter accepts, Esc (or any editing key) dismisses. The lower band reserves room for the menu so a fresh session still has space to drop it down.