diff --git a/doc/development_plan.md b/doc/development_plan.md index b9cce04..49a3c1c 100644 --- a/doc/development_plan.md +++ b/doc/development_plan.md @@ -100,11 +100,28 @@ autocorrect produce clean, non-duplicated text. 2. `go test -race ./...` green (add `-race`; the channel model may surface races — fix any found). — `58725a5`. Details in §13. -### Phase 1 — complete the IME (§4) -Implement items 1–4, with **Go unit tests** for item 3 (range-replace on the -chunked buffer is pure and directly testable) and a state-assertion test for -items 1/2 (after N frames, `app.State` holds the expected caret/selection and the -next emitted op set is correct). This is the make-or-break feature. +### Phase 1 — complete the IME (§4) — DONE (`9b78219`, `72b3c3f`) +All four items implemented and tested: +- **Item 3** (`9b78219`): `HandleKeyDown` honors `key.EditEvent.Range` via + `HandleReplaceRange` (rune→byte via UTF-8 leading-byte scan, string + chunked + paths). Also fixed a multi-chunk `Delete` corruption and a `FullContent` + truncation bug found while testing it. +- **Items 1/2/4** (`72b3c3f`): `TextField.Draw` emits, when focused, + `key.InputHintOp{HintText}`, `key.SnippetCmd` (the **visible window** as the + snippet, `Range {0,len}` so the IME reports `EditEvent.Range` window-relative), + and `key.SelectionCmd` (caret, window-relative rune index). `HandleReplaceRange` + offsets the window-relative range by `IMEWindowStartByte` (new `EditorState` + fields set during layout) to address the buffer. + +Tests: `ime_range_test.go` covers string + chunked + unicode + swapped-bounds + +windowed (scrolled) paths for item 3. Items 1/2/4 are renderer-side op emission +(not observable headlessly); they are verified by build + on-device (Phase 2). + +Note: pushing the visible window (not the whole file) as the snippet keeps IME +traffic small for large files. An `EditEvent.Range` that falls outside the +window (the IME discarding and re-anchoring the snippet — a rare case) is +clamped to the window end by the byte-conversion, so it degrades rather than +corrupting; the common within-window path is exact. ### Phase 2 — emulator verification (the observation loop, §9.3) 1. Install Android SDK + NDK + platform-tools; create an x86_64 AVD (16 GB VM, @@ -222,6 +239,20 @@ within ~1 s. Repo: `go test -race ./...` green. `-race` is green and is now a standing regression gate. - **Chunked buffer at 10 MB** (Phase 3): unproven on-device; the measurement is the gate for the size claim. +- **Chunked buffer fixed-slot drift** (Phase 3, discovered during IME work): the + buffer models chunk *i* as bytes `[i*chunkSize, (i+1)*chunkSize)`. That holds + for a freshly loaded file and for read-only display, but a **length-changing + edit** (insert/delete) shifts the bytes after the edit point, so the + fixed-slot offsets no longer line up for any *not-yet-loaded* tail chunk. In + practice: (a) files fully in memory are fine (all chunks loaded, `FullContent` + concatenates by index); (b) the caret region is always loaded, so **IME commits + at the caret are correct**; (c) a *large* file with an *unloaded* tail that is + then edited and scrolled into is where it breaks (lazy `loadChunk` reads stale + disk, since the file is only rewritten whole on save). A proper fix is a rope + or an offset-index structure. Phase 3 must either avoid this case (cap edits + to loaded region / flush before loading a shifted tail) or replace the slot + model. Until then the honest size limit should assume the file is small enough + to stay fully in memory (the ~50 MB target is borderline; see §6). - **AOSP keyboard** is a proxy for real IMEs (Gboard, etc.); final swipe/autocorrect sign-off needs a real device with a real IME.