feat: add diff view component

This commit is contained in:
2026-11-06 08:01:23 +07:00
parent f272f80d09
commit 7214c70ea7
11 changed files with 1067 additions and 35 deletions
+109
View File
@@ -0,0 +1,109 @@
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<DiffViewState>,
}
impl Render for Host {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> 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<RefCell<Option<Entity<DiffViewState>>>> = 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:?}"
);
}