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>
This commit is contained in:
parent
8db0485839
commit
c414954820
|
|
@ -327,6 +327,12 @@ end
|
|||
-- is keyed by the same `tostring(buf)` the attachments table uses.
|
||||
local styled_buffers = {}
|
||||
|
||||
-- M4.6 (task #23): buffers that already had a `DiagnosticView`
|
||||
-- overlay pushed. Same dedup discipline as `styled_buffers` —
|
||||
-- `pmacs.diag._attach_view` stacks a fresh overlay on every call,
|
||||
-- so the after-load path must gate itself.
|
||||
local diag_viewed_buffers = {}
|
||||
|
||||
local function attach_buffer(buf)
|
||||
if not buf then return nil end
|
||||
local key = tostring(buf)
|
||||
|
|
@ -361,6 +367,15 @@ local function attach_buffer(buf)
|
|||
local ok, attached = pcall(pmacs.lsp._attach_style, buf)
|
||||
if ok and attached then styled_buffers[key] = true end
|
||||
end
|
||||
-- M4.6 (task #23): attach the DiagnosticView so the TUI grid
|
||||
-- renderer underlines bytes covered by published diagnostics.
|
||||
-- Keyed by `uri` to match the diag store; the view re-reads the
|
||||
-- store on every render, so no further wiring is needed when
|
||||
-- diagnostics update.
|
||||
if not diag_viewed_buffers[key] then
|
||||
local ok, attached = pcall(pmacs.diag._attach_view, buf, uri)
|
||||
if ok and attached then diag_viewed_buffers[key] = true end
|
||||
end
|
||||
return rec
|
||||
end
|
||||
|
||||
|
|
|
|||
13
src/diag.rs
13
src/diag.rs
|
|
@ -409,6 +409,10 @@ impl DiagnosticView {
|
|||
}
|
||||
|
||||
impl View for DiagnosticView {
|
||||
fn kind(&self) -> &'static str {
|
||||
"diagnostic"
|
||||
}
|
||||
|
||||
fn render(&mut self, buf: &Buffer, viewport: Viewport, cells: &mut CellGrid<'_>) {
|
||||
// Snapshot the diagnostics under the lock and drop it
|
||||
// immediately so we don't hold the lock through rendering
|
||||
|
|
@ -783,4 +787,13 @@ mod tests {
|
|||
assert_eq!(s.gutter_glyph(), gl);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_advertises_diagnostic_kind() {
|
||||
// `pmacs.window._overlay_kinds()` introspection (task #23 wire-up,
|
||||
// mirroring "syntax-highlight" / LspStyleView) relies on this.
|
||||
let store = make_shared_store();
|
||||
let view = DiagnosticView::new("file:///a", store);
|
||||
assert_eq!(view.kind(), "diagnostic");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8742,6 +8742,36 @@ pub fn install_diag(lua: &Lua, manager: &SharedLspManager) -> mlua::Result<()> {
|
|||
})?;
|
||||
}
|
||||
|
||||
// Sibling of `pmacs.lsp._attach_style` and
|
||||
// `pmacs.parse._attach_highlight`: pushes a `DiagnosticView`
|
||||
// overlay on the active window keyed under `uri`, so the TUI
|
||||
// grid renderer paints diagnostic underlines for buffers that
|
||||
// have an LSP server publishing diagnostics. Lua callers dedup
|
||||
// per buffer; double-attach stacks duplicate overlays.
|
||||
{
|
||||
let m = manager.clone();
|
||||
diag_mod.set(
|
||||
"_attach_view",
|
||||
lua.create_function(move |lua, (id, uri): (BufferIdLua, String)| {
|
||||
let store_handle = m.borrow().diag_store();
|
||||
let core = lua
|
||||
.app_data_ref::<SharedCore>()
|
||||
.ok_or_else(|| mlua::Error::external("editor core not yet installed"))?;
|
||||
let mut core_borrow = core.borrow_mut();
|
||||
let win = core_borrow.active_window_mut();
|
||||
if win.buffer_id != id.0 {
|
||||
return Err(mlua::Error::external(format!(
|
||||
"active window's buffer is not {:?}",
|
||||
id.0
|
||||
)));
|
||||
}
|
||||
let overlay = crate::diag::DiagnosticView::new(uri, store_handle);
|
||||
win.push_overlay(Box::new(overlay));
|
||||
Ok(true)
|
||||
})?,
|
||||
)?;
|
||||
}
|
||||
|
||||
pmacs.set("diag", diag_mod)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1754,6 +1754,80 @@ fn m4_6_lua_surface_reads_diagnostics() {
|
|||
);
|
||||
}
|
||||
|
||||
/// Task #23: `pmacs.diag._attach_view` pushes a `DiagnosticView` onto
|
||||
/// the active window's overlay stack so the TUI grid renderer paints
|
||||
/// diagnostic underlines. Verifies the binding lands and that the
|
||||
/// overlay advertises the stable `"diagnostic"` kind that callers
|
||||
/// (`builtin/runtime/lsp.lua`'s dedup table, future tests) key on.
|
||||
#[test]
|
||||
fn m4_6_diag_attach_view_pushes_diagnostic_overlay() {
|
||||
use pmacs::editor::EditorState;
|
||||
|
||||
let state = EditorState::new();
|
||||
let attached: bool = state
|
||||
.lua_host
|
||||
.lua()
|
||||
.load(
|
||||
r"
|
||||
local buf = pmacs.window.buffer()
|
||||
return pmacs.diag._attach_view(buf, 'file:///fake.rs')
|
||||
",
|
||||
)
|
||||
.eval()
|
||||
.expect("_attach_view");
|
||||
assert!(attached, "_attach_view should return true on success");
|
||||
|
||||
let has_diag_overlay: bool = state
|
||||
.lua_host
|
||||
.lua()
|
||||
.load(
|
||||
r"
|
||||
for _, k in ipairs(pmacs.window._overlay_kinds()) do
|
||||
if k == 'diagnostic' then return true end
|
||||
end
|
||||
return false
|
||||
",
|
||||
)
|
||||
.eval()
|
||||
.expect("overlay_kinds query");
|
||||
assert!(
|
||||
has_diag_overlay,
|
||||
"active window must carry a 'diagnostic' overlay after _attach_view"
|
||||
);
|
||||
|
||||
// Mirroring `_attach_style` / `_attach_highlight`: the binding
|
||||
// itself does not dedup; callers (lsp.lua's `diag_viewed_buffers`)
|
||||
// are responsible. Calling twice stacks two overlays.
|
||||
state
|
||||
.lua_host
|
||||
.lua()
|
||||
.load(
|
||||
r"
|
||||
pmacs.diag._attach_view(pmacs.window.buffer(), 'file:///fake.rs')
|
||||
",
|
||||
)
|
||||
.exec()
|
||||
.expect("second attach");
|
||||
let diag_count: i64 = state
|
||||
.lua_host
|
||||
.lua()
|
||||
.load(
|
||||
r"
|
||||
local n = 0
|
||||
for _, k in ipairs(pmacs.window._overlay_kinds()) do
|
||||
if k == 'diagnostic' then n = n + 1 end
|
||||
end
|
||||
return n
|
||||
",
|
||||
)
|
||||
.eval()
|
||||
.expect("overlay count");
|
||||
assert_eq!(
|
||||
diag_count, 2,
|
||||
"binding does not dedup; two calls = two overlays"
|
||||
);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// T M4.7 --- LSP-backed views: completion, hover, signature
|
||||
// ===========================================================================
|
||||
|
|
|
|||
Loading…
Reference in New Issue