112 lines
3.7 KiB
Rust
112 lines
3.7 KiB
Rust
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, bind_code_view_keys};
|
|
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<f64>,\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<CodeViewState>,
|
|
}
|
|
|
|
impl Render for Host {
|
|
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> 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(|cx| {
|
|
gpui_native_theme::Theme::init(cx);
|
|
bind_code_view_keys(cx);
|
|
});
|
|
|
|
let source = large_source(5000);
|
|
let started = Instant::now();
|
|
let (state, highlight_elapsed) = cx.update(|cx| {
|
|
let state = CodeViewState::new("bench-code", source.clone(), Language::Rust, cx);
|
|
(state, started.elapsed())
|
|
});
|
|
|
|
let state_cell: Rc<RefCell<Option<Entity<CodeViewState>>>> = 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:?}"
|
|
);
|
|
}
|