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::code_view::CodeViewState; use gpui_native_editor::highlight::Language; /// Deterministic pseudo-Rust with mixed tokens, roughly `lines` lines long. fn large_source(lines: usize) -> String { let mut source = String::with_capacity(lines * 48); let mut index = 0usize; while source.lines().count() < lines { let i = index; source.push_str(&format!( "// Section {i}: generated for the scroll performance test.\n" )); source.push_str(&format!("struct Section{i} {{\n")); source.push_str(" id: u64,\n"); source.push_str(" label: String,\n"); source.push_str(" scores: Vec,\n"); source.push_str("}\n\n"); source.push_str(&format!("impl Section{i} {{\n")); source.push_str(" /// A one-line summary of this section.\n"); source.push_str(" fn describe(&self) -> String {\n"); source.push_str(&format!( " format!(\"section {i}: {{}} at {{:?}}\", self.label, self.scores)\n" )); source.push_str(" }\n"); source.push_str("}\n\n"); index += 1; } source } struct Host { code: Entity, } impl Render for Host { fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { div().size_full().child(self.code.clone()) } } #[test] fn scrolling_a_large_file_is_not_pathologically_slow() { let mut cx = TestAppContext::single(); cx.update(gpui_native_theme::Theme::init); let source = large_source(5000); let started = Instant::now(); let state = CodeViewState::new("bench-code", source.clone(), Language::Rust); let highlight_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 code = cx.new(|_| state); cell.borrow_mut().replace(code.clone()); Host { code } }); 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 line_count = cx.update(|cx| state.read(cx).code().lines.len()); assert!( (5000..=5020).contains(&line_count), "the whole file highlighted, got {line_count} lines" ); 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!( "highlighting {line_count} lines took {highlight_elapsed:?}; 30 wheel frames over {} lines took {scroll_elapsed:?} ({:?} per frame)", line_count, scroll_elapsed / 30 ); assert!( highlight_elapsed < Duration::from_secs(3), "highlighting took {highlight_elapsed:?}" ); assert!( scroll_elapsed < Duration::from_secs(2), "scrolling 30 frames took {scroll_elapsed:?}" ); }