| 1256 | } |
| 1257 | |
| 1258 | async fn workspace_diagnostic( |
| 1259 | &self, |
| 1260 | params: WorkspaceDiagnosticParams, |
| 1261 | ) -> Result<WorkspaceDiagnosticReportResult> { |
| 1262 | // Build a set of previous result IDs sent by the client so we |
| 1263 | // can return Unchanged for files that haven't changed. |
| 1264 | let previous: HashMap<&str, &str> = params |
| 1265 | .previous_result_ids |
| 1266 | .iter() |
| 1267 | .map(|p| (p.uri.as_str(), p.value.as_str())) |
| 1268 | .collect(); |
| 1269 | |
| 1270 | let open_uris: Vec<String> = { |
| 1271 | let files = self.open_files.read(); |
| 1272 | files.keys().cloned().collect() |
| 1273 | }; |
| 1274 | |
| 1275 | let mut items = Vec::new(); |
| 1276 | |
| 1277 | for uri_str in &open_uris { |
| 1278 | // Read the current resultId for this file. |
| 1279 | let current_id = { |
| 1280 | let ids = self.diag_result_ids.lock(); |
| 1281 | ids.get(uri_str.as_str()).copied().unwrap_or(0) |
| 1282 | }; |
| 1283 | |
| 1284 | // Check if the client already has up-to-date diagnostics. |
| 1285 | // The resultId must match AND the full cache must be present. |
| 1286 | // When `schedule_diagnostics` invalidates the cache (removing |
| 1287 | // diag_last_full), the resultId is intentionally kept so it |
| 1288 | // doesn't reset to 0. But we must not return "unchanged" |
| 1289 | // when the cache is missing — that means fresh computation |
| 1290 | // is needed. |
| 1291 | let cache_present = self.diag_last_full.lock().contains_key(uri_str.as_str()); |
| 1292 | if cache_present |
| 1293 | && let Some(prev_id) = previous.get(uri_str.as_str()) |
| 1294 | && *prev_id == current_id.to_string() |
| 1295 | { |
| 1296 | let uri = match uri_str.parse::<Url>() { |
| 1297 | Ok(u) => u, |
| 1298 | Err(_) => continue, |
| 1299 | }; |
| 1300 | items.push(WorkspaceDocumentDiagnosticReport::Unchanged( |
| 1301 | WorkspaceUnchangedDocumentDiagnosticReport { |
| 1302 | uri, |
| 1303 | version: None, |
| 1304 | unchanged_document_diagnostic_report: UnchangedDocumentDiagnosticReport { |
| 1305 | result_id: current_id.to_string(), |
| 1306 | }, |
| 1307 | }, |
| 1308 | )); |
| 1309 | continue; |
| 1310 | } |
| 1311 | |
| 1312 | // If the cache is missing, trigger computation (same as |
| 1313 | // textDocument/diagnostic). |
| 1314 | let needs_compute = { |
| 1315 | let cache = self.diag_last_full.lock(); |