Replace the physical ekp-region renderer with ekp-buffer text-property projection backed by shared semantic layout plans and stable live-edit transactions. Optimize the C-backed resize hot path, and add deterministic performance, ERT, GUI, fuzz, release, and documentation coverage for task017 through task032. BREAKING CHANGE: require ekp-buffer instead of ekp-region and rename module-owned ekp-region-* settings to ekp-buffer-*.
5.4 KiB
Let Native Redisplay Own Unfinished Text
Context
The first text-property live engine correctly kept synthesized layout out of the source character stream, but it still treated an unfinished hard paragraph as a partial KP document. Stable anchors, lookahead, push/pull, and convergence limited the amount of recomputation; they did not change the user-visible ownership error.
Near the right edge, that engine could choose several visual breaks and a discretionary hyphen while the user was still typing. Calling one row “natural” only suppressed its glue adjustment. The surrounding partial KP plan still controlled wrapping, so editing did not behave like ordinary Emacs.
The product requirement is stricter: before the user naturally leaves a visual row, EKP must have no opinion about its layout.
Decision
Use three states with distinct owners:
- The point-containing, underfilled source tail belongs entirely to native Emacs redisplay. EKP performs no KP planning and publishes no layout property there.
- Once native redisplay has moved point to a later visual row, EKP may
align internal gaps on rows already left behind. This projection uses
only
space-widthandmin-width; it cannot addline-prefix, a replacing break, or a discretionary hyphen. - Once a hard paragraph ends or point leaves it, EKP runs one complete KP pass. Only this state may publish planned visual breaks, indentation, and discretionary hyphens.
Before a source edit, the active hard paragraph is restored to native
display. If editing crosses into a previously committed row, that prefix is
therefore invalidated before Emacs changes the source. After the edit and
redisplay, completed native row boundaries are derived with
vertical-motion; gap targets are then computed for those fixed source
ranges. The row boundary itself remains Emacs-owned.
IME composition defers projection. Resize work is generation-checked. Narrowing may release an inaccessible previous live paragraph but must not modify it outside the accessible restriction. Foreign replacing display ownership causes the affected row to remain natural rather than inviting a second owner.
Why This Is Simpler
The live engine no longer predicts how an incomplete paragraph will flow. It has no lookahead limit, stable-line signature, anchor-creep rule, pullback algorithm, or convergence state. Each transition follows a visible editor event:
- still on the same native row: do nothing;
- native wrap completed a row: align that row's gaps;
- hard paragraph completed or was left: run full KP.
The completed-paragraph planner and the live editor now share only semantic width/glue computations. They do not pretend to share the same control flow.
Alternatives Rejected
Keep partial KP and tune the active-line exception
This retains the wrong owner. Suppressing glue on one chosen row cannot prevent adjacent planned breaks or hyphens from appearing while the paragraph is unfinished.
Run full KP synchronously after every edit
Removing an idle timer does not make completed-paragraph optimization an editing algorithm. It would still move unfinished text and would make typing latency proportional to paragraph planning.
Insert soft newlines or hyphen characters
That would restore the original source-pollution defect. Direct Elisp character APIs, save, search, and syntax must continue to observe only logical text.
Use overlays for transient rows
The project has one text-property projection ownership model, and the user explicitly excluded overlays. A second transient representation would complicate invalidation without correcting the row-ownership rule.
Consequences
- Typing before native wrap looks and behaves like auto layout is disabled.
- A committed live row can improve spacing without changing its native wrap boundary.
- Live editing deliberately has no discretionary hyphenation. Hyphens appear only in the completed paragraph's display projection and never in source text.
- Deleting back across a native wrap removes the committed row projection before the source edit, so Emacs immediately restores its natural flow.
- One authoritative graphical window determines native row boundaries. Text properties cannot represent simultaneous plans for different window widths.
- Automatic work remains bounded by the existing hard-paragraph limit; explicit refill is the opt-in path for an oversized paragraph.
Verification
Behavioral ERT proves the three state transitions through the installed before/after-change path, including underfilled tails, native wrap, backward invalidation, hard newline, paragraph exit, IME, narrowing, resize, foreign ownership, and teardown.
Static GUI probes prove exact space-width/min-width projection from
1–64 pixels, display-only completed-paragraph breaks/hyphens/indentation,
unchanged source, and zero overlays across scale, remap, fringe, and width
variants.
The retained dynamic artifact
/private/tmp/ekp-native-live-2dYu2U records native mixed Latin/CJK wrap,
deletion back across the boundary, and hard-paragraph completion. Every
immediate and post-redisplay checkpoint passes, and temporal review finds
no flicker, black segment, or non-Emacs frame.
Rollback
Revert the native progressive live transition as one unit. Do not restore the partial-KP state machine piecemeal or add a compatibility shim. The semantic completed-paragraph planner and text-property projection layer remain independently valid.