knEmacs
knEmacs is the text editor that ships inside the kec binary. It is a small,
Emacs-flavored terminal editor: open a file (or an empty scratch buffer), type
into it, move the cursor with the usual control keys, and save. It runs on a
normal computer — no KN-86 hardware required — and is the same editor the
handheld exposes on-device.
Open it
kec nemacs # an empty *scratch* buffer — just start typingkec nemacs FILE # open FILE (created on first save if it doesn't exist)kec edit FILE # `edit` is an alias for `nemacs`It takes over the terminal (raw mode, alternate full-screen draw) and restores it
on exit. To leave, press C-x C-c (hold Ctrl, tap x, tap c).
Run it in a real terminal. knEmacs is interactive; piping keystrokes into it works for scripting and tests, but to actually use it you need a TTY.
Keys
Notation is Emacs-standard: C-f means Ctrl+f; C-x C-s is a
two-key sequence (C-x then C-s).
| Key | Command |
|---|---|
| any printable character | insert it at point (self-insert) |
Enter | newline |
Backspace | delete the character before point |
C-d | delete the character at point (forward) |
Tab | indent — insert spaces to the next tab stop (width 2) |
C-f / C-b | forward / backward one character (or → / ←) |
C-n / C-p | next / previous line (or ↓ / ↑) |
C-a / C-e | beginning / end of line |
C-Space | set the mark (start of a region) |
C-w / M-w | kill / copy the region (mark…point) |
C-k | kill to end of line (the newline if at end of line) |
C-y | yank (paste the most recent kill) |
C-s | incremental search forward (C-s again: next match) |
C-/ / C-x u | undo (redo: C-M-_, alias C-? — not yet reachable from the kec TTY) |
C-x C-s | save the buffer to its file |
C-x C-c | quit |
C-h k key | describe what key does (help) |
C-g | quit the current action (keyboard-quit) |
Forward/backward motion wraps across line boundaries: C-f at end-of-line moves
to the start of the next line, and C-b at column 0 moves to the end of the
previous line. Vertical motion (C-n/C-p) keeps a goal column: it lands at
that column where the line is long enough and clamps to the end of shorter lines,
without forgetting it — so passing through a short line and reaching a longer
one returns you to the original column. A horizontal move (or an edit) sets a new
goal. Lines wider than the window scroll horizontally so point stays visible;
Tab inserts soft spaces (never a literal tab) so the cursor stays aligned to the
grid.
The modeline
The top row is an inverse-video status bar:
path/to/file.lsp * L2 C1- the buffer name (the file path, or
*scratch*) - a
*when there are unsaved edits L<row> C<col>— the cursor’s line and column (1-based)
The bottom row is the echo line: it shows messages like Wrote path/to/file.lsp
after a save, a help line after C-h k, or … is undefined for an unbound key.
Saving and quitting
C-x C-swrites the buffer back to its file (byte-exact, at any size) and reportsWrote …. A successful save clears the modeline*.- A bare
kec nemacs(no file) has nothing to save to —C-x C-swill say “No file”. Pass a path if you want to keep what you type. C-x C-cprompts before discarding unsaved edits. With a clean buffer it exits immediately; with unsaved changes it asksSave modified buffer before quit? (y/n, C-g cancel)—ysaves and exits,nexits and drops the edits,C-gcancels and returns you to the buffer. (A modified*scratch*with no file warns that the changes will be lost.)
How it works
knEmacs follows the lesson of rxi/lite and Emacs: the buffer is text — lines of characters with a point (cursor) — and structure (paren-matching, s-expression motion) is a lens computed on top, never the representation. So opening Lisp source shows it as source, and you edit it left-to-right like any text editor.
The split between the C host and the Lisp tier is deliberately thin:
- The C host (
cli/main.c) owns only the terminal: raw mode, reading keystrokes, normalizing them to Emacs notation (C-f,<up>,C-x C-s), and file I/O. It knows no keybindings of its own. - The editor tier (
editor/*.lsp, an on-demand library — see ADR-0002) owns everything else. The text buffer iseditor/32-text.lsp: a line zipper (the lines above point in reverse, the current line, the lines below, and a column), so every keystroke is an O(1) splice of one line or a shuffle of one line between the two stacks. The renderer (text-screen) lays out the modeline, a vertically scrolled text window, and the echo line, then parks the cursor at point. - The keymap is data (
editor/55-bindings.lsp): a table mapping a key string to a command. The host normalizes a keystroke, asks the table what it resolves to, and dispatches. Rebinding is a data edit, not a code change.
This page covers using knEmacs. For the underlying Lisp API — the text-buffer functions, the keymap/dispatch primitives, the REPL engine, and the SEAM contract a new host implements to get its own knEmacs — see the Extended Library Reference.
Idle timers
The main loop isn’t purely blocking: each iteration it asks the Lisp timer
registry (editor/72-timer.lsp) how long until the next armed timer and
poll()s the keyboard for that long. If a timer comes due before a key arrives,
its thunk fires and the screen repaints — so periodic work (animation, a clock, a
background repaint) runs between keystrokes, like Emacs’s run-with-timer. With
no timer armed the editor blocks for input exactly as before. Arm one with
(run-with-timer secs repeat fn (now)); the KN86_NEMACS_INIT environment
variable is a Lisp expression evaluated once at startup, a convenient place to do
so. (Timers pause inside a modal prompt — C-x C-c’s save question, C-s
search.) See ADR-0006.
Scope and roadmap
knEmacs today is the minimal functional editor: open, type, move, save. It is intentionally small, and some familiar Emacs features are not built yet:
- Structural (“paredit”) editing —
kill-sexp, slurp/barf, wrap. The plan is to reintroduce the s-expression zipper as a command lens over the text (parse the current form → operate → reprint), i.e. a “lisp-mode”, rather than as the buffer itself. - Undo/redo is built — command-based (it stores the inverse of each edit, not
whole-buffer snapshots), so it stays cheap on large files.
C-/(orC-x u) undoes;C-M-_(orC-?) redoes, though neither notation is producible from thekecCLI TTY yet; consecutive typing coalesces into one step.M-/stays reserved for dabbrev-expand. - Mark, region, kill & yank are built —
C-Spacesets the mark;C-w/M-wkill/copy the region;C-kkills to end of line;C-yyanks. There is a bounded kill ring;M-y(yank-pop) is not built yet, and the mark is a plain position (not adjusted by edits made before a kill). - Incremental search (
C-s) is built — type to extend the pattern,C-sagain jumps to the next match,DELshrinks it,RETaccepts (the match becomes the region),C-gcancels and returns to where you started. Single-line patterns; reverse search (C-r) and search-wraparound are not built yet. - A minibuffer /
M-xcommand-by-name, completion,M-yyank-pop,C-rreverse search, and multiple buffers are all deferred.
The separate kec repl surface (a structural Lisp prompt) is unrelated to the
text editor and continues to use the s-expression zipper directly.