session 5 fixup: clear styling on CrdtOp to kill stale-position artifacts
Surfaced during session-5 manual validation (probe #3, the bet-#1 shape from the framing pass): editing at a diagnostic boundary left stale color fragments visible against now-different text. The session-4 PR documented this as a "one-frame stale" artifact, but in practice the LSP re-analysis window stretches the wrong-color period to 100ms–5s — human-perceptible and confusing. Mechanism: CrdtOp updates `current_text` but `current_spans` / `current_decorations` still index into pre-edit byte positions. `reshape()` paints them at those stale positions against the new text, producing colored fragments over wrong characters until the producer ships an updated frame. Between CrdtOp arrival and clangd's next publishDiagnostics (LSP debounce + re-analysis), no Decorations frame ships at all (the producer's `changed_intervals(prev, curr)` sees identical sets because clangd hasn't republished yet). Fix: drop both vectors in the CrdtOp arm before `set_text`. Tree- sitter re-emits StyleSpans within ~one frame; LSP decorations re-emit when clangd republishes. Cost = a brief uncolored window per edit. Gain = no wrong-position color persists. Manual revalidation (post-rebase on #44 + #45 + #46): edit-at-boundary in the TUI now drops the old diagnostic color cleanly. New diagnostic colors paint once clangd republishes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
2076a3682d
commit
abd6f46eee
|
|
@ -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`. 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.
|
||||
/// 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).
|
||||
fn set_text(&mut self, text: &str) {
|
||||
if self.current_text == text {
|
||||
return;
|
||||
|
|
@ -498,6 +498,28 @@ 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();
|
||||
let text = doc.get_text(LORO_TEXT_CONTAINER).to_string();
|
||||
self.set_text(&text);
|
||||
None
|
||||
|
|
|
|||
Loading…
Reference in New Issue