Skip to content

Add an in-TUI editor, solution diff, and lesson-first flow - #1

Merged
cheesecakeMafia merged 3 commits into
mainfrom
feat/in-tui-editor
Sep 3, 2026
Merged

cheesecakeMafia merged 3 commits into
mainfrom
feat/in-tui-editor

Conversation

@cheesecakeMafia

Copy link
Copy Markdown
Owner

Why

Learning Rust in this tool meant keeping two terminals open: the game in one,
your editor in the other. That split is where the friction was — every fix
needed a window change, and the compiler output you were fixing lived in the
window you had just left.

It also fixes the problem that started the project. Hitting println without
the ! on exercise one and having nowhere to turn is exactly what the lesson
gating is for: the module's lesson now arrives before its exercises rather than
sitting behind a marker you can ignore.

What

Three commits, ordered so each builds and tests on its own.

d48514d — fix: fire a save that lands inside the debounce window

A pre-existing bug found while building the rest. A second save arriving within
300ms of another was recorded and then dropped: the block meant to fire it later
had an empty body. Its regression test fails against the old logic and passes
against the new one.

9bb2fc4 — feat: track layout and viewed solutions in state v3

Schema bump for the two new pieces of persisted state. Both fields carry
#[serde(default)] so a v2 file parses before migration runs. The migration
test asserts every prior exercise, module and lesson entry survives — a schema
bump that quietly drops progress is indistinguishable from a corrupt file.

ba2f80c — feat: add an in-TUI editor with lesson gating and diff

Watch mode becomes an editable split. Ctrl+S saves and the existing watcher
drives verification. Ctrl+D shows your code beside the reference solution with
differing lines highlighted, recorded but never penalised. Ctrl+J switches
between side-by-side and stacked. An unread lesson opens before its exercises;
Esc skips it for the session.

Things worth knowing when reading the diff

  • edtui's EditorState defaults to Normal mode, where its modeless keymap
    binds nothing. Both construction sites set Insert explicitly — without it
    the editor renders perfectly and ignores every keystroke.
  • Saves are atomic (temp file plus rename). A partial write would fire the
    watcher and, under disk-wins, reload the truncated file over the live buffer.
  • Our own writes are fingerprinted: they still trigger verification but never a
    reload, which would discard anything typed while the compile ran.
  • Ctrl+I/Ctrl+M arrive as Tab and Enter, Ctrl+H is how some terminals send
    Backspace, and Ctrl+Q/T/W/Z are claimed by the terminal emulator. All are
    left unbound — a binding taken upstream never reaches the application.
  • Search moved to Ctrl+F because Ctrl+S is now save. edtui registers
    Ctrl+S in two modes, so both had to move or repeat-search would silently
    stop working.
  • The editor pane refuses to render below a size floor: edtui's line wrapper
    spins forever at zero content width, and a hang defeats the panic hook that
    would otherwise restore the terminal.

Verification

  • cargo clippy --all-targets -- -D warnings — clean
  • cargo test — 182 passing, 0 failing (was 150)
  • cargo test --test solution_verification_test -- --ignored — 64/64 solutions
  • Commits 1 and 2 verified building and testing standalone in a scratch copy

A second change arriving within the 300ms window set `last_event` and
returned false; the block meant to fire it later had an empty body, so
the save was recorded and then dropped. Verification never ran for it.

Replaced with a single trailing-edge path: any event restarts the window
and reports nothing, and a quiet poll fires once the window elapses. The
leading edge is deliberately gone — editors emit truncate and write as
separate events, which could straddle a poll and verify twice.
Adds `solution_viewed` per exercise and a `preferences` block holding the
editor layout, both behind `#[serde(default)]` so a v2 file parses before
the migration runs. Fills the v2 -> v3 slot the v1 -> v2 work left marked.

`clear_solution_viewed` exists because the two resets differ: the CLI one
forfeits XP via `forget_exercise`, the TUI one only restores the file. The
record follows the file, not the XP.

The migration test asserts every prior exercise, module and lesson entry
survives — a schema bump that silently drops progress would look exactly
like a corrupt state file.
Watch mode becomes an editable split: edtui above, compiler output beside
or below it. Ctrl+S saves and the existing watcher drives verification, so
no second terminal is needed. Ctrl+D compares your code against the
reference solution; Ctrl+J switches the split; an unread module lesson
opens before its exercises and Esc skips it for the session.

Notes on choices that are not obvious from the diff:

- edtui's EditorState defaults to Normal mode, where its modeless keymap
  binds nothing at all. Both construction sites set Insert explicitly or
  the editor renders perfectly and ignores every keystroke.
- Saving is atomic (temp file plus rename). A partial write would fire the
  watcher and, under disk-wins, reload the truncated file over the buffer.
- Our own writes are fingerprinted so they still verify but never trigger
  a reload, which would discard anything typed during the compile.
- Ctrl+I and Ctrl+M arrive as Tab and Enter; Ctrl+H is how some terminals
  send Backspace; Ctrl+Q/T/W/Z are claimed by the terminal emulator. All
  are left unbound — a binding taken upstream never reaches us.
- Search moved to Ctrl+F. edtui registers Ctrl+S in two modes, so both had
  to move or repeat-search would silently stop working.
- render_editor refuses to draw below a size floor: edtui's line wrapper
  spins forever at zero content width, and a hang defeats the panic hook.
@cheesecakeMafia
cheesecakeMafia merged commit ba2f80c into main Sep 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant