Doc: mark Phase 1 (IME) done; record chunked-buffer fixed-slot drift risk

This commit is contained in:
Greg Pomerantz 2026-08-16 02:45:09 -04:00
parent 72b3c3f9c1
commit 92d8a6b7f0

View File

@ -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 14, 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.