| 467 | } |
| 468 | |
| 469 | async fn did_change(&self, params: DidChangeTextDocumentParams) { |
| 470 | let uri = params.text_document.uri.to_string(); |
| 471 | |
| 472 | if params.content_changes.is_empty() { |
| 473 | return; |
| 474 | } |
| 475 | |
| 476 | // Apply incremental edits to the current content. |
| 477 | // Each change event either has a range (incremental) or replaces |
| 478 | // the entire document (range is None). |
| 479 | let text = { |
| 480 | let open_files = self.open_files.read(); |
| 481 | let mut current = open_files |
| 482 | .get(&uri) |
| 483 | .map(|s| s.to_string()) |
| 484 | .unwrap_or_default(); |
| 485 | drop(open_files); |
| 486 | |
| 487 | for change in ¶ms.content_changes { |
| 488 | if let Some(range) = change.range { |
| 489 | let start = crate::util::position_to_byte_offset(¤t, range.start); |
| 490 | let end = crate::util::position_to_byte_offset(¤t, range.end); |
| 491 | current.replace_range(start..end, &change.text); |
| 492 | } else { |
| 493 | // Full content replacement (fallback) |
| 494 | current = change.text.clone(); |
| 495 | } |
| 496 | } |
| 497 | Arc::new(current) |
| 498 | }; |
| 499 | |
| 500 | // Update stored content |
| 501 | self.open_files |
| 502 | .write() |
| 503 | .insert(uri.clone(), Arc::clone(&text)); |
| 504 | |
| 505 | // Re-parse and update AST map, use map, and namespace map |
| 506 | let signature_changed = self.update_ast(&uri, &text); |
| 507 | |
| 508 | // Schedule diagnostics in a background task with debouncing. |
| 509 | // This returns immediately so that completion, hover, and |
| 510 | // signature help are never blocked by diagnostic computation. |
| 511 | self.schedule_diagnostics(uri.clone()); |
| 512 | |
| 513 | // When a class signature changed (method/property added, |
| 514 | // removed, or modified; class renamed; parent changed; etc.) |
| 515 | // other open files may have stale diagnostics that reference |
| 516 | // the affected classes. Queue them all for a re-check. |
| 517 | if signature_changed { |
| 518 | self.schedule_diagnostics_for_open_files(&uri); |
| 519 | } |
| 520 | } |
| 521 | |
| 522 | async fn did_close(&self, params: DidCloseTextDocumentParams) { |
| 523 | let uri = params.text_document.uri.to_string(); |