//! Get diagnostics from LSP. //! //! Sends a `textDocument/diagnostic` request to the connected language //! server for a given file and returns errors, warnings, and other //! diagnostics. use crate::tools::{Tool, ToolCtx}; use anyhow::Result; use serde_json::{json, Value}; use tracing::{error, info, instrument}; /// Tool that retrieves diagnostics (errors, warnings) from the LSP for a file. /// /// Flow: parse language/path → lock LSP manager → find client /// → send `textDocument/diagnostic` → return pretty-printed JSON response. pub struct LspDiagnostics; impl Tool for LspDiagnostics { fn name(&self) -> &'static str { "lsp_diagnostics" } fn description(&self) -> &'static str { "Get diagnostics (errors, warnings) from the LSP for a file" } fn parameters(&self) -> Value { json!({ "type": "object", "properties": { "language": { "type": "string", "description": "Language identifier" }, "path": { "type": "string", "description": "File path to get diagnostics for" } }, "required": ["language", "path"] }) } #[instrument(skip(self, ctx, args))] fn run(&self, ctx: &ToolCtx, args: &Value) -> Result { let language = crate::tools::arg_str(args, "language")?; let path = crate::tools::arg_str(args, "path")?; info!(language, path, "LSP diagnostics requested"); let manager = match ctx.lsp_manager.lock() { Ok(g) => g, Err(poisoned) => { error!("LSP manager mutex poisoned, recovering"); poisoned.into_inner() } }; if let Some(client) = manager.get_client(&language) { let result = client.send_request( "textDocument/diagnostic", &json!({ "textDocument": { "uri": format!("file://{}", path) } }), )?; Ok(serde_json::to_string_pretty(&result)?) } else { anyhow::bail!("no LSP client connected for '{language}'") } } }