1use std::collections::HashMap;
60use std::collections::hash_map::DefaultHasher;
61use std::hash::{Hash, Hasher};
62use std::sync::{Arc, Mutex};
63
64use arc_swap::ArcSwap;
65use tracing::{debug, info};
66
67use lattice_cells::{
68 ProviderId, VirtualRow, VirtualRowMatrix, VirtualRowProvider, VirtualRowVersion,
69};
70use lattice_core::BufferId;
71
72use crate::editor::VirtualRowsWake;
73use crate::render_state::RenderState;
74
75#[derive(Debug, Default)]
94pub struct VirtualRowProviderRegistry {
95 by_buffer: Mutex<HashMap<BufferId, HashMap<ProviderId, Arc<dyn VirtualRowProvider>>>>,
96}
97
98impl VirtualRowProviderRegistry {
99 pub fn new() -> Self {
100 Self::default()
101 }
102
103 pub fn register(&self, buffer_id: BufferId, provider: Arc<dyn VirtualRowProvider>) -> bool {
109 let id = provider.id();
110 let mut by_buffer = self
111 .by_buffer
112 .lock()
113 .expect("VirtualRowProviderRegistry mutex poisoned");
114 let scope = by_buffer.entry(buffer_id).or_default();
115 if scope.contains_key(&id) {
116 return false;
117 }
118 scope.insert(id, provider);
119 true
120 }
121
122 pub fn unregister(&self, buffer_id: BufferId, id: ProviderId) -> bool {
126 let mut by_buffer = self
127 .by_buffer
128 .lock()
129 .expect("VirtualRowProviderRegistry mutex poisoned");
130 let Some(scope) = by_buffer.get_mut(&buffer_id) else {
131 return false;
132 };
133 let removed = scope.remove(&id).is_some();
134 if scope.is_empty() {
135 by_buffer.remove(&buffer_id);
136 }
137 removed
138 }
139
140 pub fn snapshot(&self, buffer_id: BufferId) -> Vec<Arc<dyn VirtualRowProvider>> {
145 self.by_buffer
146 .lock()
147 .expect("VirtualRowProviderRegistry mutex poisoned")
148 .get(&buffer_id)
149 .map(|scope| scope.values().cloned().collect())
150 .unwrap_or_default()
151 }
152
153 pub fn is_empty(&self) -> bool {
155 self.by_buffer
156 .lock()
157 .expect("VirtualRowProviderRegistry mutex poisoned")
158 .is_empty()
159 }
160
161 pub fn len(&self) -> usize {
163 self.by_buffer
164 .lock()
165 .expect("VirtualRowProviderRegistry mutex poisoned")
166 .values()
167 .map(|scope| scope.len())
168 .sum()
169 }
170}
171
172impl lattice_mode::VirtualRowRegistrar for VirtualRowProviderRegistry {
175 fn register(&self, buffer: BufferId, provider: Arc<dyn VirtualRowProvider>) -> bool {
176 self.register(buffer, provider)
177 }
178
179 fn unregister(&self, buffer: BufferId, id: ProviderId) -> bool {
180 self.unregister(buffer, id)
181 }
182}
183
184#[derive(Debug, PartialEq, Eq)]
192pub enum WorkerDecision {
193 Clear,
198 CacheHit,
203 Recomputed,
208}
209
210#[derive(Debug)]
221pub struct VirtualRowsWorkerState {
222 last_fingerprints: HashMap<BufferId, u64>,
223 next_publish_version: u64,
224}
225
226impl Default for VirtualRowsWorkerState {
227 fn default() -> Self {
228 Self {
229 last_fingerprints: HashMap::new(),
230 next_publish_version: 1,
235 }
236 }
237}
238
239impl VirtualRowsWorkerState {
240 pub fn new() -> Self {
241 Self::default()
242 }
243}
244
245fn compute_fingerprint(providers: &[Arc<dyn VirtualRowProvider>], source_line_count: u32) -> u64 {
251 let mut pairs: Vec<(ProviderId, u64)> =
252 providers.iter().map(|p| (p.id(), p.version())).collect();
253 pairs.sort_unstable();
254 let mut hasher = DefaultHasher::new();
255 pairs.hash(&mut hasher);
256 source_line_count.hash(&mut hasher);
257 hasher.finish()
258}
259
260pub fn recompute(
284 state: &mut VirtualRowsWorkerState,
285 render_state: &ArcSwap<RenderState>,
286 providers: &VirtualRowProviderRegistry,
287) -> WorkerDecision {
288 let rs = render_state.load_full();
289 let cells = rs.cells.load();
291 if cells.panes.is_empty() {
292 return WorkerDecision::CacheHit;
293 }
294 let mut any_recomputed = false;
295 let mut any_cleared = false;
296 for pane in cells.panes.iter() {
297 match recompute_pane(pane, state, providers) {
298 WorkerDecision::CacheHit => {}
299 WorkerDecision::Clear => any_cleared = true,
300 WorkerDecision::Recomputed => any_recomputed = true,
301 }
302 }
303 if any_recomputed {
304 WorkerDecision::Recomputed
305 } else if any_cleared {
306 WorkerDecision::Clear
307 } else {
308 WorkerDecision::CacheHit
309 }
310}
311
312pub fn recompute_pane(
324 pane: &crate::render_state::PaneCellsInputs,
325 state: &mut VirtualRowsWorkerState,
326 providers: &VirtualRowProviderRegistry,
327) -> WorkerDecision {
328 let Some(snapshot) = pane.snapshot.as_ref() else {
329 state.last_fingerprints.remove(&pane.buffer_id);
336 let existing = pane.virtual_rows_matrix.load();
337 if existing.is_empty() && existing.source_line_count == 0 {
338 return WorkerDecision::Clear;
339 }
340 pane.virtual_rows_matrix
341 .store(Arc::new(VirtualRowMatrix::empty()));
342 return WorkerDecision::Clear;
343 };
344
345 let source_line_count = snapshot.buffer.content_line_count();
347 let provider_snap = providers.snapshot(pane.buffer_id);
348 let fingerprint = compute_fingerprint(&provider_snap, source_line_count);
349
350 if state.last_fingerprints.get(&pane.buffer_id) == Some(&fingerprint) {
351 return WorkerDecision::CacheHit;
352 }
353 state.last_fingerprints.insert(pane.buffer_id, fingerprint);
354
355 let mut rows: Vec<VirtualRow> = Vec::new();
356 for p in &provider_snap {
357 rows.extend(p.collect());
358 }
359
360 let publish_version = state.next_publish_version;
361 state.next_publish_version = publish_version.wrapping_add(1);
362 let new_matrix =
363 VirtualRowMatrix::build(rows, source_line_count, VirtualRowVersion(publish_version));
364 pane.virtual_rows_matrix.store(Arc::new(new_matrix));
365 WorkerDecision::Recomputed
366}
367
368pub async fn run(
382 render_state: Arc<ArcSwap<RenderState>>,
383 wake: VirtualRowsWake,
384 providers: Arc<VirtualRowProviderRegistry>,
385 paint_request: Arc<tokio::sync::Notify>,
386) {
387 info!(
388 target: "lattice_host::virtual_rows_worker",
389 "virtual-rows worker spawned"
390 );
391 let mut state = VirtualRowsWorkerState::new();
392 let mut tick: u64 = 0;
393 let mut window_start = std::time::Instant::now();
399 let mut window_ticks: u64 = 0;
400 let mut window_changed: u64 = 0;
401 loop {
402 wake.0.notified().await;
403 let t0 = std::time::Instant::now();
404 let decision = recompute(&mut state, &render_state, &providers);
405 let elapsed_us = t0.elapsed().as_micros();
406 tick += 1;
407 if matches!(decision, WorkerDecision::Recomputed | WorkerDecision::Clear) {
408 paint_request.notify_one();
409 }
410 window_ticks += 1;
411 if !matches!(decision, WorkerDecision::CacheHit) {
412 window_changed += 1;
413 }
414 if window_start.elapsed() >= std::time::Duration::from_secs(1) {
415 debug!(
416 target: "lattice_host::virtual_rows_worker",
417 tick,
418 ticks_per_sec = window_ticks,
419 rebuilds = window_changed,
420 last_us = elapsed_us,
421 last = ?decision,
422 "virtual-rows worker (1s summary)"
423 );
424 window_start = std::time::Instant::now();
425 window_ticks = 0;
426 window_changed = 0;
427 }
428 }
429}
430
431#[cfg(test)]
432mod tests {
433 use super::*;
434
435 use lattice_cells::AnchorPosition;
436 use lattice_core::{Buffer, BufferId};
437 use lattice_runtime::DocumentSnapshot;
438 use std::sync::atomic::{AtomicU64, Ordering};
439
440 const ACTIVE: BufferId = BufferId(0);
446
447 #[derive(Debug)]
450 struct MockProvider {
451 id: ProviderId,
452 version: AtomicU64,
453 rows: Mutex<Vec<VirtualRow>>,
454 }
455
456 impl MockProvider {
457 fn new(id: ProviderId, rows: Vec<VirtualRow>) -> Self {
458 Self {
459 id,
460 version: AtomicU64::new(1),
461 rows: Mutex::new(rows),
462 }
463 }
464
465 fn bump_version(&self) {
466 self.version.fetch_add(1, Ordering::Relaxed);
467 }
468
469 fn replace_rows(&self, rows: Vec<VirtualRow>) {
470 *self.rows.lock().unwrap() = rows;
471 self.bump_version();
472 }
473 }
474
475 impl VirtualRowProvider for MockProvider {
476 fn id(&self) -> ProviderId {
477 self.id
478 }
479
480 fn version(&self) -> u64 {
481 self.version.load(Ordering::Relaxed)
482 }
483
484 fn collect(&self) -> Vec<VirtualRow> {
485 self.rows.lock().unwrap().clone()
486 }
487 }
488
489 fn row(anchor: u32, pos: AnchorPosition) -> VirtualRow {
490 VirtualRow {
491 media: None,
492 anchor_line: anchor,
493 position: pos,
494 cells: Arc::from([] as [lattice_cells::Cell; 0]),
495 height: 1,
496 kind: lattice_cells::VirtualRowKind::Generic,
497 bg: None,
498 scales: None,
499 gutter_line: None,
500 gutter_fg: None,
501 }
502 }
503
504 fn pane_inputs(
509 buffer_id: BufferId,
510 snapshot: Option<Arc<DocumentSnapshot>>,
511 vr_matrix: Arc<ArcSwap<VirtualRowMatrix>>,
512 ) -> crate::render_state::PaneCellsInputs {
513 use lattice_core::ui::pane::PaneId;
514 crate::render_state::PaneCellsInputs {
515 conceal_reveal_line: None,
516 indent_guides: Default::default(),
520 indent_unit: lattice_core::IndentUnit::default(),
521 indent_guides_enabled: true,
522 sticky_context_lines: std::sync::Arc::from([] as [u32; 0]),
523 sticky_context_line_numbers: true,
524 sticky_context_separator: None,
525 sticky_context: Default::default(),
526 pane_id: PaneId::next(),
527 buffer_id,
528 matrix: Arc::new(ArcSwap::from_pointee(lattice_cells::CellMatrix::empty())),
529 display_matrix: Arc::new(ArcSwap::from_pointee(
530 crate::display_matrix::DisplayMatrix::empty(),
531 )),
532 virtual_rows_matrix: vr_matrix,
533 version: lattice_cells::MatrixVersion::ZERO,
534 snapshot,
535 syntax_handle: None,
536 inlay_hints: Arc::from(
537 Vec::<crate::render_state::InlayHintRow>::new().into_boxed_slice(),
538 ),
539 folds: Arc::from(Vec::<lattice_core::Fold>::new().into_boxed_slice()),
540 viewport_height: 10,
541 scroll: 0,
542 viewport_width: 0,
543 wrap: false,
544 wrap_reserved_cols: 0,
545 foldenable: false,
546 last_edit: None,
547 excerpt_syntax: Arc::from([]),
548 extra_spans: Arc::from([]),
549 extra_refine: Arc::from(Vec::new().into_boxed_slice()),
550 }
551 }
552
553 fn rs_with_panes(
559 panes: Vec<crate::render_state::PaneCellsInputs>,
560 ) -> Arc<ArcSwap<RenderState>> {
561 let cells = crate::render_state::CellsRenderState {
562 panes: Arc::from(panes.into_boxed_slice()),
563 ..crate::render_state::CellsRenderState::default()
564 };
565 let rs = RenderState {
566 cells: Arc::new(ArcSwap::from_pointee(cells)),
567 ..RenderState::default()
568 };
569 Arc::new(ArcSwap::from_pointee(rs))
570 }
571
572 fn snapshot_with_text(text: &str) -> Arc<DocumentSnapshot> {
575 let snap = DocumentSnapshot {
576 buffer: Buffer::from_text(text),
577 ..Default::default()
578 };
579 Arc::new(snap)
580 }
581
582 fn rs_with_single_pane(
586 text: &str,
587 cell: Arc<ArcSwap<VirtualRowMatrix>>,
588 ) -> Arc<ArcSwap<RenderState>> {
589 rs_with_panes(vec![pane_inputs(
590 ACTIVE,
591 Some(snapshot_with_text(text)),
592 cell,
593 )])
594 }
595
596 #[test]
599 fn registry_register_lookup_unregister() {
600 let reg = VirtualRowProviderRegistry::new();
601 assert!(reg.is_empty());
602 let p = Arc::new(MockProvider::new(1, vec![]));
603 assert!(reg.register(ACTIVE, p as Arc<dyn VirtualRowProvider>));
604 assert_eq!(reg.len(), 1);
605 assert_eq!(reg.snapshot(ACTIVE).len(), 1);
606 assert!(reg.unregister(ACTIVE, 1));
607 assert!(reg.is_empty());
608 }
609
610 #[test]
611 fn registry_duplicate_register_in_same_scope_returns_false() {
612 let reg = VirtualRowProviderRegistry::new();
613 let p1 = Arc::new(MockProvider::new(1, vec![]));
614 let p2 = Arc::new(MockProvider::new(1, vec![row(0, AnchorPosition::Above)]));
615 assert!(reg.register(ACTIVE, p1 as Arc<dyn VirtualRowProvider>));
616 assert!(!reg.register(ACTIVE, p2 as Arc<dyn VirtualRowProvider>));
617 assert_eq!(reg.len(), 1);
618 }
619
620 #[test]
629 fn registry_isolates_providers_by_buffer() {
630 let reg = VirtualRowProviderRegistry::new();
631 let baseline = BufferId(7);
632 let current = BufferId(8);
633 let p1 = Arc::new(MockProvider::new(42, vec![]));
634 let p2 = Arc::new(MockProvider::new(42, vec![row(0, AnchorPosition::Above)]));
635 assert!(reg.register(baseline, p1 as Arc<dyn VirtualRowProvider>));
636 assert!(reg.register(current, p2 as Arc<dyn VirtualRowProvider>));
637 assert_eq!(reg.len(), 2);
638 assert_eq!(reg.snapshot(baseline).len(), 1);
639 assert_eq!(reg.snapshot(current).len(), 1);
640 }
641
642 #[test]
643 fn registry_snapshot_scoped_to_buffer() {
644 let reg = VirtualRowProviderRegistry::new();
645 let bid_a = BufferId(1);
646 let bid_b = BufferId(2);
647 reg.register(
648 bid_a,
649 Arc::new(MockProvider::new(10, vec![])) as Arc<dyn VirtualRowProvider>,
650 );
651 reg.register(
652 bid_b,
653 Arc::new(MockProvider::new(20, vec![])) as Arc<dyn VirtualRowProvider>,
654 );
655 let snap_a = reg.snapshot(bid_a);
656 let snap_b = reg.snapshot(bid_b);
657 assert_eq!(snap_a.len(), 1);
658 assert_eq!(snap_a[0].id(), 10);
659 assert_eq!(snap_b.len(), 1);
660 assert_eq!(snap_b[0].id(), 20);
661 assert!(reg.snapshot(BufferId(99)).is_empty());
663 }
664
665 #[test]
666 fn registry_unregister_only_affects_its_buffer() {
667 let reg = VirtualRowProviderRegistry::new();
668 let bid_a = BufferId(1);
669 let bid_b = BufferId(2);
670 reg.register(
671 bid_a,
672 Arc::new(MockProvider::new(5, vec![])) as Arc<dyn VirtualRowProvider>,
673 );
674 reg.register(
675 bid_b,
676 Arc::new(MockProvider::new(5, vec![])) as Arc<dyn VirtualRowProvider>,
677 );
678 assert!(reg.unregister(bid_a, 5));
679 assert!(reg.snapshot(bid_a).is_empty());
680 assert_eq!(reg.snapshot(bid_b).len(), 1);
681 assert_eq!(reg.len(), 1);
683 assert!(!reg.unregister(bid_a, 5));
685 assert!(!reg.unregister(BufferId(99), 5));
687 }
688
689 #[test]
692 fn fingerprint_independent_of_provider_order() {
693 let p1: Arc<dyn VirtualRowProvider> = Arc::new(MockProvider::new(1, vec![]));
694 let p2: Arc<dyn VirtualRowProvider> = Arc::new(MockProvider::new(2, vec![]));
695 let fp_a = compute_fingerprint(&[p1.clone(), p2.clone()], 10);
696 let fp_b = compute_fingerprint(&[p2, p1], 10);
697 assert_eq!(fp_a, fp_b);
698 }
699
700 #[test]
701 fn fingerprint_changes_when_source_line_count_changes() {
702 let p: Arc<dyn VirtualRowProvider> = Arc::new(MockProvider::new(1, vec![]));
703 let fp_a = compute_fingerprint(std::slice::from_ref(&p), 10);
704 let fp_b = compute_fingerprint(&[p], 11);
705 assert_ne!(fp_a, fp_b);
706 }
707
708 #[test]
709 fn fingerprint_changes_when_provider_version_changes() {
710 let mock = Arc::new(MockProvider::new(1, vec![]));
711 let dyn_provider: Arc<dyn VirtualRowProvider> = mock.clone();
712 let fp_a = compute_fingerprint(std::slice::from_ref(&dyn_provider), 10);
713 mock.bump_version();
714 let fp_b = compute_fingerprint(&[dyn_provider], 10);
715 assert_ne!(fp_a, fp_b);
716 }
717
718 #[test]
727 fn recompute_empty_panes_is_cache_hit() {
728 let mut state = VirtualRowsWorkerState::new();
729 let rs = rs_with_panes(vec![]);
730 let reg = VirtualRowProviderRegistry::new();
731 let decision = recompute(&mut state, &rs, ®);
732 assert_eq!(decision, WorkerDecision::CacheHit);
733 }
734
735 #[test]
747 fn repeated_ticks_with_no_providers_converge_to_cache_hit() {
748 let mut state = VirtualRowsWorkerState::new();
749 let cell = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
750 let rs = rs_with_single_pane("a\nb\nc\n", cell);
751 let reg = VirtualRowProviderRegistry::new();
752 let _ = recompute(&mut state, &rs, ®);
755 for tick in 2..=5 {
756 let d = recompute(&mut state, &rs, ®);
757 assert_eq!(
758 d,
759 WorkerDecision::CacheHit,
760 "tick {tick} with no providers must be quiet, got {d:?}"
761 );
762 }
763 }
764
765 #[test]
768 fn repeated_ticks_with_a_stable_provider_converge_to_cache_hit() {
769 let mut state = VirtualRowsWorkerState::new();
770 let cell = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
771 let rs = rs_with_single_pane("a\nb\nc\n", cell);
772 let reg = VirtualRowProviderRegistry::new();
773 reg.register(
774 ACTIVE,
775 Arc::new(MockProvider::new(1, vec![row(0, AnchorPosition::Above)]))
776 as Arc<dyn VirtualRowProvider>,
777 );
778 let _ = recompute(&mut state, &rs, ®);
779 for tick in 2..=5 {
780 let d = recompute(&mut state, &rs, ®);
781 assert_eq!(
782 d,
783 WorkerDecision::CacheHit,
784 "tick {tick} with a stable provider must be quiet, got {d:?}"
785 );
786 }
787 }
788
789 #[test]
794 fn recompute_pane_without_snapshot_clears_its_matrix() {
795 let mut state = VirtualRowsWorkerState::new();
796 let cell = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
797 cell.store(Arc::new(VirtualRowMatrix::build(
801 vec![row(0, AnchorPosition::Above)],
802 3,
803 VirtualRowVersion(7),
804 )));
805 assert_eq!(cell.load_full().len(), 1);
806 let rs = rs_with_panes(vec![pane_inputs(ACTIVE, None, cell.clone())]);
807 let reg = VirtualRowProviderRegistry::new();
808 let d = recompute(&mut state, &rs, ®);
809 assert_eq!(d, WorkerDecision::Clear);
810 assert_eq!(cell.load_full().len(), 0, "matrix cleared");
811 }
812
813 #[test]
814 fn recompute_with_no_providers_is_cache_hit_after_first() {
815 let mut state = VirtualRowsWorkerState::new();
816 let cell = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
817 let rs = rs_with_single_pane("a\nb\nc\n", cell.clone());
818 let reg = VirtualRowProviderRegistry::new();
819 let d1 = recompute(&mut state, &rs, ®);
822 assert_eq!(d1, WorkerDecision::Recomputed);
823 let d2 = recompute(&mut state, &rs, ®);
825 assert_eq!(d2, WorkerDecision::CacheHit);
826 }
827
828 #[test]
829 fn recompute_publishes_rows_from_provider() {
830 let mut state = VirtualRowsWorkerState::new();
831 let cell = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
832 let rs = rs_with_single_pane("a\nb\nc\nd\n", cell.clone());
833 let reg = VirtualRowProviderRegistry::new();
834 let provider = Arc::new(MockProvider::new(
835 1,
836 vec![row(1, AnchorPosition::Above), row(2, AnchorPosition::Below)],
837 ));
838 reg.register(ACTIVE, provider.clone() as Arc<dyn VirtualRowProvider>);
839
840 let d = recompute(&mut state, &rs, ®);
841 assert_eq!(d, WorkerDecision::Recomputed);
842 let published = cell.load_full();
843 assert_eq!(published.len(), 2);
844 assert!(!published.is_empty());
845 }
846
847 #[test]
848 fn recompute_cache_hit_after_provider_static() {
849 let mut state = VirtualRowsWorkerState::new();
850 let cell = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
851 let rs = rs_with_single_pane("a\nb\n", cell.clone());
852 let reg = VirtualRowProviderRegistry::new();
853 let provider = Arc::new(MockProvider::new(1, vec![row(0, AnchorPosition::Above)]));
854 reg.register(ACTIVE, provider as Arc<dyn VirtualRowProvider>);
855
856 let d1 = recompute(&mut state, &rs, ®);
857 assert_eq!(d1, WorkerDecision::Recomputed);
858 let d2 = recompute(&mut state, &rs, ®);
859 assert_eq!(d2, WorkerDecision::CacheHit);
860 }
861
862 #[test]
863 fn recompute_re_publishes_when_provider_version_bumps() {
864 let mut state = VirtualRowsWorkerState::new();
865 let cell = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
866 let rs = rs_with_single_pane("a\nb\n", cell.clone());
867 let reg = VirtualRowProviderRegistry::new();
868 let provider = Arc::new(MockProvider::new(1, vec![row(0, AnchorPosition::Above)]));
869 reg.register(ACTIVE, provider.clone() as Arc<dyn VirtualRowProvider>);
870
871 recompute(&mut state, &rs, ®);
872 let v1 = cell.load_full().version;
873
874 provider.replace_rows(vec![
875 row(0, AnchorPosition::Above),
876 row(1, AnchorPosition::Below),
877 ]);
878 let d = recompute(&mut state, &rs, ®);
879 assert_eq!(d, WorkerDecision::Recomputed);
880 let v2 = cell.load_full().version;
881 assert!(v2.0 > v1.0, "publish version monotonically bumps");
882 assert_eq!(cell.load_full().len(), 2);
883 }
884
885 #[test]
886 fn recompute_re_publishes_when_document_line_count_changes() {
887 let mut state = VirtualRowsWorkerState::new();
888 let cell = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
889 let rs1 = rs_with_single_pane("a\nb\n", cell.clone());
890 let reg = VirtualRowProviderRegistry::new();
891
892 let d1 = recompute(&mut state, &rs1, ®);
893 assert_eq!(d1, WorkerDecision::Recomputed);
894 let v1 = cell.load_full().version;
895
896 let rs2 = rs_with_single_pane("a\nb\nc\nd\n", cell.clone());
898 let d2 = recompute(&mut state, &rs2, ®);
899 assert_eq!(d2, WorkerDecision::Recomputed);
900 let v2 = cell.load_full().version;
901 assert!(v2.0 > v1.0);
902 assert_eq!(cell.load_full().source_line_count, 4);
906 }
907
908 #[test]
909 fn recompute_merges_rows_from_multiple_providers() {
910 let mut state = VirtualRowsWorkerState::new();
911 let cell = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
912 let rs = rs_with_single_pane("a\nb\nc\nd\n", cell.clone());
913 let reg = VirtualRowProviderRegistry::new();
914 let p1 = Arc::new(MockProvider::new(1, vec![row(0, AnchorPosition::Above)]));
915 let p2 = Arc::new(MockProvider::new(2, vec![row(2, AnchorPosition::Below)]));
916 reg.register(ACTIVE, p1 as Arc<dyn VirtualRowProvider>);
917 reg.register(ACTIVE, p2 as Arc<dyn VirtualRowProvider>);
918
919 recompute(&mut state, &rs, ®);
920 let published = cell.load_full();
921 assert_eq!(published.len(), 2);
922 }
923
924 #[test]
931 fn recompute_only_polls_active_doc_providers() {
932 let mut state = VirtualRowsWorkerState::new();
933 let cell = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
934 let rs = rs_with_single_pane("a\nb\nc\n", cell.clone());
935 let reg = VirtualRowProviderRegistry::new();
936 let foreign = BufferId(42);
938 assert_ne!(foreign, ACTIVE);
939 let provider = Arc::new(MockProvider::new(
940 1,
941 vec![row(0, AnchorPosition::Above), row(1, AnchorPosition::Below)],
942 ));
943 reg.register(foreign, provider as Arc<dyn VirtualRowProvider>);
944
945 let d = recompute(&mut state, &rs, ®);
946 assert_eq!(d, WorkerDecision::Recomputed);
949 assert_eq!(cell.load_full().len(), 0);
950 }
951
952 #[test]
960 fn two_panes_distinct_buffers_both_rebuild() {
961 let mut state = VirtualRowsWorkerState::new();
962 let bid_a = BufferId(11);
963 let bid_b = BufferId(22);
964 let cell_a = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
965 let cell_b = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
966 let rs = rs_with_panes(vec![
967 pane_inputs(bid_a, Some(snapshot_with_text("a\n")), cell_a.clone()),
968 pane_inputs(bid_b, Some(snapshot_with_text("b\nb\n")), cell_b.clone()),
969 ]);
970 let reg = VirtualRowProviderRegistry::new();
971 let pa = Arc::new(MockProvider::new(1, vec![row(0, AnchorPosition::Above)]));
972 let pb = Arc::new(MockProvider::new(
973 2,
974 vec![row(0, AnchorPosition::Above), row(1, AnchorPosition::Below)],
975 ));
976 reg.register(bid_a, pa as Arc<dyn VirtualRowProvider>);
977 reg.register(bid_b, pb as Arc<dyn VirtualRowProvider>);
978
979 let d = recompute(&mut state, &rs, ®);
980 assert_eq!(d, WorkerDecision::Recomputed);
981 assert_eq!(
982 cell_a.load_full().len(),
983 1,
984 "buffer A: one row from its provider"
985 );
986 assert_eq!(
987 cell_b.load_full().len(),
988 2,
989 "buffer B: two rows from its provider"
990 );
991 }
992
993 #[test]
1000 fn per_pane_cache_hit_skips_unchanged_pane() {
1001 let mut state = VirtualRowsWorkerState::new();
1002 let bid_a = BufferId(11);
1003 let bid_b = BufferId(22);
1004 let cell_a = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
1005 let cell_b = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
1006 let rs = rs_with_panes(vec![
1007 pane_inputs(bid_a, Some(snapshot_with_text("a\n")), cell_a.clone()),
1008 pane_inputs(bid_b, Some(snapshot_with_text("b\n")), cell_b.clone()),
1009 ]);
1010 let reg = VirtualRowProviderRegistry::new();
1011 let pa = Arc::new(MockProvider::new(1, vec![row(0, AnchorPosition::Above)]));
1012 let pb = Arc::new(MockProvider::new(2, vec![row(0, AnchorPosition::Above)]));
1013 reg.register(bid_a, pa.clone() as Arc<dyn VirtualRowProvider>);
1014 reg.register(bid_b, pb as Arc<dyn VirtualRowProvider>);
1015
1016 assert_eq!(recompute(&mut state, &rs, ®), WorkerDecision::Recomputed);
1018 let matrix_b_v1 = cell_b.load_full();
1019
1020 pa.replace_rows(vec![
1022 row(0, AnchorPosition::Above),
1023 row(0, AnchorPosition::Below),
1024 ]);
1025 assert_eq!(recompute(&mut state, &rs, ®), WorkerDecision::Recomputed);
1026
1027 assert_eq!(cell_a.load_full().len(), 2);
1030 let matrix_b_v2 = cell_b.load_full();
1031 assert!(
1032 Arc::ptr_eq(&matrix_b_v1, &matrix_b_v2),
1033 "unchanged pane's matrix Arc must survive untouched"
1034 );
1035 }
1036
1037 #[test]
1044 fn two_panes_sharing_buffer_share_one_matrix_write() {
1045 let mut state = VirtualRowsWorkerState::new();
1046 let bid = BufferId(33);
1047 let shared_cell = Arc::new(ArcSwap::from_pointee(VirtualRowMatrix::empty()));
1048 let snap = snapshot_with_text("x\ny\n");
1049 let rs = rs_with_panes(vec![
1050 pane_inputs(bid, Some(snap.clone()), shared_cell.clone()),
1051 pane_inputs(bid, Some(snap.clone()), shared_cell.clone()),
1052 ]);
1053 let reg = VirtualRowProviderRegistry::new();
1054 let provider = Arc::new(MockProvider::new(
1055 7,
1056 vec![row(0, AnchorPosition::Above), row(1, AnchorPosition::Below)],
1057 ));
1058 reg.register(bid, provider as Arc<dyn VirtualRowProvider>);
1059
1060 let d = recompute(&mut state, &rs, ®);
1061 assert_eq!(d, WorkerDecision::Recomputed);
1066 assert_eq!(shared_cell.load_full().len(), 2);
1067
1068 let d2 = recompute(&mut state, &rs, ®);
1071 assert_eq!(d2, WorkerDecision::CacheHit);
1072 }
1073}