use std::cell::RefCell; use std::rc::Rc; use std::time::{Duration, Instant}; use gpui::{ AppContext as _, Context, Entity, IntoElement, Modifiers, ParentElement as _, Render, ScrollDelta, ScrollEvent, Styled as _, TestAppContext, VisualTestContext, Window, div, point, px, }; use gpui_native_editor::diff_view::DiffViewState; /// Deterministic old/new pairs of roughly `lines` lines each, with changes /// sprinkled through. fn large_diff_pair(lines: usize) -> (String, String) { let mut old = String::with_capacity(lines * 48); let mut new = String::with_capacity(lines * 48); let mut index = 0usize; while old.lines().count() < lines { let i = index; let comment = format!("// Handler {i} in the generated dispatch table.\n"); old.push_str(&comment); new.push_str(&comment); old.push_str(&format!("fn handle_{i}(payload: &str) -> bool {{\n")); new.push_str(&format!("fn handle_{i}(payload: &Payload) -> bool {{\n")); old.push_str(&format!(" payload.len() < {i} + 16\n")); new.push_str(&format!( " payload.size() < {i} + 16 && !payload.is_empty()\n" )); if i.is_multiple_of(5) { old.push_str(&format!(" log::debug(\"handler {i} ran\");\n")); } if i.is_multiple_of(7) { new.push_str(" metrics::count(\"handle\");\n"); } old.push_str("}\n\n"); new.push_str("}\n\n"); index += 1; } (old, new) } struct Host { diff: Entity, } impl Render for Host { fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { div().size_full().child(self.diff.clone()) } } #[test] fn scrolling_a_large_diff_is_not_pathologically_slow() { let mut cx = TestAppContext::single(); cx.update(gpui_native_theme::Theme::init); let (old, new) = large_diff_pair(5000); let started = Instant::now(); let state = DiffViewState::new( "bench-diff", &old, &new, gpui_native_editor::highlight::Language::Rust, ); let diff_elapsed = started.elapsed(); let state_cell: Rc>>> = Rc::new(RefCell::new(None)); let cell = state_cell.clone(); let window = cx.add_window(move |_, cx| { let diff = cx.new(|_| state); cell.borrow_mut().replace(diff.clone()); Host { diff } }); let state = state_cell.borrow().clone().expect("state captured"); let mut visual = VisualTestContext::from_window(window.into(), &cx); visual.update(|window, cx| { window.draw(cx).clear(cx); }); let row_count = cx.update(|cx| state.read(cx).rows().len()); assert!(row_count > 5000, "changes add rows on top of the context"); let started = Instant::now(); for _ in 0..30 { visual.simulate_event(ScrollEvent { position: point(px(300.), px(300.)), delta: ScrollDelta::Pixels(point(px(0.), px(-240.))), modifiers: Modifiers::default(), ..Default::default() }); visual.update(|window, cx| { window.draw(cx).clear(cx); }); } let scroll_elapsed = started.elapsed(); println!( "diffing and highlighting into {row_count} rows took {diff_elapsed:?}; 30 wheel frames took {scroll_elapsed:?} ({:?} per frame)", scroll_elapsed / 30 ); assert!( diff_elapsed < Duration::from_secs(3), "diffing took {diff_elapsed:?}" ); assert!( scroll_elapsed < Duration::from_secs(2), "scrolling 30 frames took {scroll_elapsed:?}" ); }