Commit Graph

2 Commits

Author SHA1 Message Date
d8b5bc704b Handle drags: 1:1 finger tracking with cross-flip; fix caret/taps on empty lines
Selection handles now track the finger 1:1 (anchor grab point + displacement)
instead of snapping by whole lines, and crossing the opposite handle flips
the selection (native behaviour) instead of clearing it.

Caret and tap/handle line resolution use VisualLineStarts instead of the
min-Y baseline: the window's first visual line may be an empty line with no
recorded glyphs, which used to draw boundary carets one line too low per
leading empty line and land taps/dragged handles one line below the finger.
New exported ui.CaretPoint centralises byte->insertion-point mapping.

The off-screen caret no longer clamps to the window edge: EditorLayout ships
the true (possibly negative / past-end) window-relative cursor and the
renderer skips the caret when the cursor is outside the shaped window, so
scrolling past the caret no longer makes it jump onto the top/bottom line.

IME/router replay fixes: key.FocusCmd is issued only on a focus transition
(a per-frame no-op still takes the immediate-command path and re-queues all
pointer events), and the key.SelectionCmd IME sync is deferred while a
handle drag is in progress (each push re-injected the drag into every
gesture). Handle drags forward only Grabbed events; a tap inside a handle
grab box is a no-op.

Also: key.FocusEvent no longer logs as unexpected in main; dead code removed
(worker taskWrapper, browser applyXxxResult stubs, scrollIndex, mock_setup
sortModeKey/lineSpan helpers); mock FileSystem.ListPaths prefix match uses
strings.HasPrefix; build scripts run the new scripts/check.sh static gate
(go vet + staticcheck). Tests: caret_point_test, touch_selection updates
(flip/empty-line cases), off-window caret e2e, selection drag e2e grab step.
2026-08-19 22:34:12 -04:00
5205906de4 Data-corruption test suite: differential fuzz + atomicity contract
Adds the corruption-proofing layer for the edit/persist path:

- chunked_buffer_fuzz_test.go: differential fuzz of ChunkedBuffer
  Insert/Delete against a plain []byte shadow model (arbitrary byte
  positions/text, chunk sizes 1..64KB, 500-2000 ops each) verifying
  FileLen, FullContent, Content probes and the chunk-size invariant
  after every op; rune-aligned variant adds UTF-8 validity and an
  independent RuneIndexToByte oracle.
- line_index_fuzz_test.go: differential fuzz of the incremental
  LineIndex updates against a full-recomputation oracle (mixed,
  no-newline, single-line, CRLF, trailing-newline shapes), plus a
  trailing-empty-line structural invariant test.
- state_api_fuzz_test.go: differential fuzz of the production edit
  entry points (HandleInsert/Backspace/Delete/ReplaceRange, incl.
  selection variants) checking content, UTF-8 validity, chunk
  invariant, line index and exact cursor after every op.
- real_file_fuzz_test.go (e2e): random edit sequences (incl.
  window-relative IME replaces) against the REAL filesystem with
  per-batch IME-window-consistency checks, forced-flush disk
  byte-comparison, then a second logic instance (restart simulation)
  must reload byte-identical content with a fresh line index;
  asserts no stray temp files.
- filesystem_test.go (real): atomicity contract - exact round trip
  at edge sizes, concurrent reader never sees a torn file across 150
  alternating 2MB writes, failed write (read-only dir) leaves the
  original byte-identical, stale temp file is consumed.

Fixes a real invariant violation the fuzzing exposed: Insert halved
an oversized spliced result once, so a large paste into a non-empty
buffer left chunks up to ~P/2 (8x target at 1MB/64KB), breaking the
documented 'no chunk > 2x target' invariant. Insert now re-chunks the
oversized result into pieces of at most chunkSize, making the
invariant hold after every edit. Mutation-tested: dropping one byte
in Insert and one entry in UpdateLineIndexAfterInsert are both
caught by the fuzz suite.
2026-08-17 12:43:17 -04:00