Commit Graph

9 Commits

Author SHA1 Message Date
Levi Neuwirth 7c46288ef0 search: render matches in both frontends (Q#SR3/SR4)
The search store now hangs off EditorCore (reachable by the
producer, the Lua commands, and the TUI view). The decorations
producer emits SearchMatch for every visible match and
SearchMatchActive for the active one, byte-range-direct (no
line/col conversion), viewport-clipped, and stale-skipped on the
M11.8 model. pmacs-gpu wires the two decoration_kind_to_bg_color
arms (translucent yellow / stronger amber). The TUI gets a
SearchView overlay mirroring DiagnosticView (black-on-yellow wash,
brighter for the active match), reusing diag.rs's now-pub(crate)
line/col helpers.

Nothing populates the store yet (commit 3 wires the input), so the
paths are dormant until then — verified by populating the store
directly: producer emits the right kinds + stale-suppresses, the
TUI view washes the cells.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 10:48:06 -04:00
Levi Neuwirth c2f6905b08 minimap diagnostic marks: severity color rides FileStyleSummary
The GPU's gutter-sign equivalent (framing Q#D3). The producer folds
diagnostics into the per-line summary: each touched line's dominant
style gets the severity's canonical underline_color (most severe
wins), skipped while the URI's store entry is stale — same
discipline as the decorations producer. The minimap stroke prefers
underline_color over the syntax fg, so error/warning lines read at
a glance.

Diagnostics publish without a CRDT generation bump, so the
summary's generation-keyed cache gains a second key: a new per-URI
epoch on DiagnosticStore (bumped on set/clear, not mark_stale). A
republish re-emits the summary; everything else stays suppressed
(framing bet #3 — the gate widens precisely, not naively).

DiagnosticSeverity::underline_color() becomes the canonical palette
(TUI squiggles, col-0 markers, and minimap marks all share it).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 10:16:45 -04:00
Levi Neuwirth acc91fafae diag: squiggle zero-width ranges at their anchor cell
User report: a removed comma produced the column-0 marker but no
squiggle. rust-analyzer anchors 'expected COMMA' as a zero-width
range one past the line's last character (verified:
`rust-analyzer diagnostics` reports col 12 → col 12 on a 12-byte
line), and the per-line clamp collapsed it into the empty-range
skip. Zero-width ranges now underline the single cell at the
anchor — one past EOL is a blank cell inside the window, and a
squiggled space is how other editors surface exactly this error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 09:29:31 -04:00
Levi Neuwirth e724d725de diag: column-0 line markers — the TUI's gutter signs
M4.6 follow-up piece 3. The TUI reserves no gutter column, so the
sign is a severity-colored background on the line's first cell:
the glyph and its syntax color survive (DiagnosticView's contract
stays style-only), most severe diagnostic per line wins, and
zero-width ranges — which the underline pass cannot paint — now
have a visible artifact, closing a long-stale comment's promise.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:24:49 -04:00
Levi Neuwirth 72799f771a protocol v6: per-severity diagnostic underline colors (SGR 58)
M4.6 follow-up piece 2. `Style` gains `underline_color: Color`
(Default = follow the text color) so a diagnostic squiggle can be
red/yellow/cyan/gray without clobbering the syntax color of the
text it underlines — exactly why error_style() left its 'red'
unwired until now.

The wire consequence: Style rides inside Cell / CellDelta /
Snapshot / StyleSpans, so this is the protocol's first
encoding-breaking change. PROTOCOL_VERSION 5 → 6 and
SUPPORTED_PROTOCOL_VERSIONS narrows to [6]: postcard is not
self-describing, so no per-session send gate can keep a v5 peer
decoding v6 cells — a mismatched pair now fails the handshake with
a clean VersionMismatch instead of garbling mid-session. Version
policy tests rewritten to pin the new contract.

Surface wiring:
- diag.rs: per-severity underline_color (indexed 1/3/6/8).
- frontend.rs: kitty-style CSI 4:N for Double/Curly/Dotted/Dashed
  (previously flattened to plain SGR 4) + SGR 58:5/58:2 emission.
- ansi.rs: parse SGR 58/59 with the 38/48 extended-color grammar.
- overlay.rs merge_styles: non-default-wins, like fg/bg/underline.
- lua_bindings.rs: underline_color on Lua style tables.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 19:16:42 -04:00
Levi Neuwirth 9718958c4c Fix stale TUI styling after edits 2026-05-26 11:15:23 -04:00
Levi Neuwirth c414954820
M4.6 — attach DiagnosticView to TUI windows (closes task #23) (#50)
The TUI's `DiagnosticView` has existed in `src/diag.rs` since v0.1 but
was never instantiated, so the local-grid renderer never painted
diagnostic underlines. This wires the view in the same way
`LspStyleView` and `SyntaxHighlightView` are wired — a Lua binding
that pushes the overlay onto the active window, driven from
`lsp.lua`'s `attach_buffer` flow with the standard per-buffer dedup
table.

* `DiagnosticView::kind()` returns `"diagnostic"` so
  `pmacs.window._overlay_kinds()` can verify attachment.
* `pmacs.diag._attach_view(buf, uri)` mirrors `pmacs.lsp._attach_style`
  exactly: requires active window's buffer matches `buf`, constructs
  `DiagnosticView::new(uri, store)`, pushes as overlay.
* `lsp.lua` calls `pmacs.diag._attach_view` from `attach_buffer` and
  tracks pushed buffers in `diag_viewed_buffers` to prevent
  double-attach on repeated `attach_buffer` calls.

Scope is intentionally narrow: view attachment only. Navigation
bindings, statusline summary, and gutter signs remain follow-ups
under task #23.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 21:10:36 +00:00
Levi Neuwirth 886239480e
T M11.8 — diag-store stale-flag closes LSP-re-analysis-gap surface (#48)
Surfaced during session-5 manual validation as the final iteration
of bet #1 from the framing pass: edits that shift byte positions
left old diagnostic colors painted over post-edit text in both
`pmacs-gpu` and (now-visible) the TUI. Persisted for the full LSP
re-analysis window (100ms–5s).

PR #47 fixed the StyleSpans side via generation-tracked full=true
emission, but Decorations remained vulnerable: the producer's diff
shipped old diagnostics from the diag store, whose entries were
indexed at pre-edit byte positions until clangd republished.

Fix: a per-URI `stale_uris` flag in `DiagnosticStore`. The LSP
layer's `did_change_full` marks the URI stale right after sending
the notification; the next `publishDiagnostics` absorb path's
`set` clears it. The `semantic_render` producer reads `is_stale`
and skips diagnostic emission entirely while stale.

Effect: between an edit and clangd's next publish, the producer
ships zero diagnostic decorations. Frontend's replace/merge clears
old positions cleanly. Brief uncolored window (≤ LSP re-analysis
latency) replaces the previous wrong-position-color persistence.
The correct visual tradeoff: honest emptiness over deceptive
staleness.

Files changed:

- `src/diag.rs` — `DiagnosticStore` gains `stale_uris: HashSet<String>`;
  new `mark_stale` / `is_stale` API; `set` and `clear` reset the
  flag on the assumption that absorption / explicit removal mean
  the LSP has caught up.
- `src/lsp.rs` — `LspManager::did_change_full` calls
  `diag_store.lock().mark_stale(uri)` after `send_notification`.
- `src/semantic_render.rs` — `scoped_decorations` reads `is_stale`
  alongside `for_uri`; when stale, suppresses the diagnostic
  loop (selection and other non-diagnostic kinds still emit).

Tests (all crdt-gated where they reference semantic_render):

- `diag::tests::stale_flag_default_false`
- `diag::tests::mark_stale_sets_flag` (per-URI scoping)
- `diag::tests::set_clears_stale_flag`
- `diag::tests::empty_set_clears_stale_flag_too`
- `diag::tests::clear_drops_stale_flag`
- `semantic_render::tests::diagnostics_suppressed_while_diag_store_stale`
  — assert no diagnostic kinds emit while stale; assert they
  re-emit after a fresh `set` clears the flag.

Gates: cargo fmt + clippy (workspace, with/without `crdt`) clean;
lib 1482 (+6) with crdt; 1319 (+6) without; m4 83; m11_5 2.

This is approach (A) from the session-5 ask: track per-URI freshness
relative to buffer edits, suppress emission until LSP catches up.
Approach (B) — clangd's didChange/publishDiagnostics version
matching — would be more precise but requires plumbing version
tracking through the LspManager's document state, which is a
larger change deferred. The stale-flag captures the same semantic
("any edit since last publish ⇒ stale") at a cheaper cost.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 19:01:10 +00:00
Levi Neuwirth 4da4b09d5d Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00