-- welcome.lua --- journey step 4: say something when the editor opens. -- Framing: docs/journey-stage1b3-welcome-framing.md. -- -- `COHERENCE.md` §18 graded onboarding "missing entirely": no welcome, -- no tutorial, no cheat sheet reachable from inside the editor. The sole -- discovery affordance was knowing to press `M-x`. -- -- Split of responsibility with Rust: this file owns WHAT is said (the -- entries and their rendering) and the `help` command; the Rust seam -- `EditorState::finalize_local_launch` owns WHEN and WHERE — it alone -- decides that this is a local, no-target launch whose `*scratch*` is -- still untouched, and it clears the modified flag afterwards (there is -- no Lua API for that, deliberately). pmacs.welcome = pmacs.welcome or {} --- The keys the welcome advertises, in display order. --- --- `keys` is EXACTLY what `pmacs.keymap.lookup` accepts, which is the --- whole point of the shape: the acceptance suite checks every entry --- resolves, so the welcome can never advertise a binding a later stage --- removed. Scraping the rendered prose instead would be ambiguous — --- `C-c c` is two chords and nothing in the text marks the boundary. --- --- Public so a user who rebinds can rebuild it from `init.lua`. pmacs.welcome.entries = { { keys = "C-x C-f", label = "open a file" }, { keys = "C-c t", label = "terminal" }, { keys = "C-c c", label = "build" }, { keys = "C-x b", label = "switch buffer" }, } -- Two entries per line, padded so the labels align. Kept deliberately -- small: three lines total, because the greeting a user must delete -- before typing should not be chrome. local function entry_columns() local width = 0 for _, e in ipairs(pmacs.welcome.entries) do if #e.keys > width then width = #e.keys end end local lines, pending = {}, nil for _, e in ipairs(pmacs.welcome.entries) do local cell = string.format("%-" .. width .. "s %s", e.keys, e.label) if pending then lines[#lines + 1] = " " .. string.format("%-24s", pending) .. cell pending = nil else pending = cell end end if pending then lines[#lines + 1] = " " .. pending end return lines end --- The welcome text, as written into an untouched `*scratch*`. --- --- `M-x` and `M-x help` are prose rather than entries: `M-x` is the --- palette itself and `help` is a command name, so neither is a keymap --- lookup. The acceptance checks the command exists instead. function pmacs.welcome.text() local lines = { "Welcome to pmacs. M-x runs any command; M-x help lists the keys." } for _, line in ipairs(entry_columns()) do lines[#lines + 1] = line end return table.concat(lines, "\n") .. "\n" end -- --------------------------------------------------------------------- -- M-x help -- --------------------------------------------------------------------- -- -- The smallest version of §18's second item, included because the -- welcome would otherwise point at nothing. It is the ROOT of the -- eventual family: when the discovery arc adds `help.keys` and friends, -- `help` stays the index they are reached from, so no rename is owed. -- -- Renders through `editor.describe-command`'s existing `*help*` -- mechanism rather than growing a second help surface. local function help_text() local lines = { "pmacs help", "", " M-x run a command by name", } for _, e in ipairs(pmacs.welcome.entries) do lines[#lines + 1] = string.format(" %-18s %s", e.keys, e.label) end lines[#lines + 1] = "" lines[#lines + 1] = " M-x editor.describe-command what a command does" lines[#lines + 1] = " M-x editor.list-buffers every open buffer" lines[#lines + 1] = "" lines[#lines + 1] = "The full keymap reference is docs/keybindings.md." return table.concat(lines, "\n") .. "\n" end pmacs.command.define { name = "help", description = "Show the pmacs key and command cheat sheet.", fn = function() pmacs.editor._show_help(help_text()) end, }