diff --git a/pmacs-gpu/src/main.rs b/pmacs-gpu/src/main.rs index 7e6fa73..e9f980e 100644 --- a/pmacs-gpu/src/main.rs +++ b/pmacs-gpu/src/main.rs @@ -410,13 +410,13 @@ impl State { /// (avoids the re-shape cost when an unchanged buffer ticks). /// /// Replaces the rope text and routes through `reshape` so the - /// rich-text rendering uses the current `current_spans` and - /// `current_decorations`. The `CrdtOp` arm of - /// [`Self::apply_attach_message`] clears both vectors before - /// calling `set_text`, so post-edit text never renders with - /// pre-edit-position colors. Brief uncolored window between an - /// edit and the daemon's next styling frame is the accepted - /// tradeoff (see that arm's doc comment for the rationale). + /// rich-text rendering uses the current `current_spans`. When + /// called from the `CrdtOp` path (text shifted under existing + /// spans) the spans are momentarily stale relative to the new + /// byte positions — `reshape` clamps via `range.end.min(text_len)` + /// so rendering is safe, but visual styling may be off until the + /// daemon's next `StyleSpans` frame catches up. A real artifact; + /// classified as a session-4 known limitation rather than a bug. fn set_text(&mut self, text: &str) { if self.current_text == text { return; @@ -498,28 +498,22 @@ impl State { eprintln!("pmacs-gpu: CrdtOp import failed: {e:?}"); return None; } - // **Invalidate styling on text-shift.** The current - // spans + decorations index into byte positions of - // the *pre-edit* text. Once `set_text` swaps in the - // post-edit text, those positions no longer - // correspond to the right characters, so painting - // them produces wrong-character-colored fragments - // (the session-5 manual-validation finding: probe - // #3, "edit at a diagnostic boundary leaves stale - // color fragments"). The session-4 PR documented - // this as a "one-frame stale" artifact; in practice - // it stretches to the full LSP re-analysis cycle - // (100ms–5s), making the wrong-color period - // visible for human-perceptible durations. - // - // The fix is to drop both vectors here. Tree-sitter - // re-emits StyleSpans almost immediately (one - // frame after CrdtOp lands daemon-side); LSP - // decorations re-emit when clangd republishes. - // Cost: a brief uncolored window after each edit; - // gain: no wrong-position color persists. - self.current_spans.clear(); - self.current_decorations.clear(); + // NOTE: `current_spans` / `current_decorations` index + // into the *pre-edit* byte positions. The producer's + // next render frame (in pmacs core, post-T M11.7 + // generation-transition fix) ships `full=true` + // styling for buffers whose generation advanced, so + // the next message replaces the stale items + // wholesale via `replace_style_spans` / + // `replace_decorations`. The single-frame gap + // between CrdtOp arrival and that next frame paints + // styling at stale byte positions — the session-4 + // documented "one-frame stale" artifact. A previous + // attempt to fix it by clearing both vectors here + // (`49785c4`) was reverted because the producer's + // *incremental* updates ship dirty-range spans only, + // and an emptied cache loses the non-dirty viewport + // styling entirely. let text = doc.get_text(LORO_TEXT_CONTAINER).to_string(); self.set_text(&text); None