Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Build a Smaller Helix

Rebuild the ownership and event machinery, not a toy clone of modal syntax.

Build a tiny editor whose foreground loop owns all mutable state, applies invertible transactions, renders dynamic layers, and accepts asynchronous results only through version-aware callbacks.

1. Start with one concrete editor

Use String, a cursor index, and insert(char). Read scripted input events and print the buffer after each one. Write down the invariant that the cursor is on a UTF-8 boundary.

2. Introduce transactions

Represent retain/delete/insert operations, apply them only to a matching input length, map a cursor through them, compose adjacent edits, and construct an inverse from the pre-edit snapshot. Add undo before async code.

3. Separate document, view, and application

Let a document own text, version, and history; a view own viewport and selection; and the application own documents, views, and current focus. Open two views on one document and prove both selections map through an edit.

4. Add a dynamic compositor

Define Component { handle_event, render }, store boxed layers, and bubble events from the top layer down. Add a prompt overlay that consumes keys without teaching the base editor about prompts.

5. Replace the blocking loop with Tokio

Select over an input channel, redraw notification, timer, and job callback channel. Keep Editor as a plain owned field—do not introduce a mutex.

6. Add the job/callback bridge

Spawn a fake “language service” that receives an owned text snapshot and version, waits, and returns a boxed callback. Apply its diagnostics only if the document still exists and its version matches. Test an out-of-order result.

7. Coalesce rendering

Make ten background notifications request redraw. Render at most once per frame interval, but process input immediately. Count renders in a deterministic test.

8. Add shutdown obligations

Classify jobs as detachable or must-finish. Stop admission, await required jobs under a deadline, execute their returned callbacks, and abort or drop the rest. Simulate a child service that never answers.

9. Compare with production

Map your types to Application, Editor, Document, Transaction, Compositor, Component, Jobs, Callback, redraw events, and LSP transport. Helix adds a mature rope, syntax trees, multi-selection editing, protocols, terminal compatibility, plugins, debouncing, cancellation, and thousands of commands. Your smaller version should still preserve its architectural center: many things wait concurrently, one owner commits editor state.