Skip to content

Commit f21d51c

Browse files
authored
editor: Fix sticky headers not accounting for deleted diff hunks (#49296)
Release Notes: - Fixed sticky headers sometimes disappearing when dealing with expanded deleted diff hunks
1 parent e9a8ef1 commit f21d51c

2 files changed

Lines changed: 98 additions & 8 deletions

File tree

crates/editor/src/editor.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ use std::{
200200
time::{Duration, Instant},
201201
};
202202
use task::{ResolvedTask, RunnableTag, TaskTemplate, TaskVariables};
203-
use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _};
203+
use text::{BufferId, FromAnchor, OffsetUtf16, Rope, ToOffset as _, ToPoint as _};
204204
use theme::{
205205
AccentColors, ActiveTheme, GlobalTheme, PlayerColor, StatusColors, SyntaxTheme, Theme,
206206
ThemeSettings, observe_buffer_font_size_adjustment,
@@ -1992,20 +1992,21 @@ impl Editor {
19921992
return;
19931993
}
19941994
let multi_buffer = display_snapshot.buffer_snapshot();
1995-
let multi_buffer_visible_start = self
1995+
let scroll_anchor = self
19961996
.scroll_manager
19971997
.native_anchor(display_snapshot, cx)
1998-
.anchor
1999-
.to_point(&multi_buffer);
2000-
let max_row = multi_buffer.max_point().row;
2001-
2002-
let start_row = (multi_buffer_visible_start.row).min(max_row);
2003-
let end_row = (multi_buffer_visible_start.row + 10).min(max_row);
1998+
.anchor;
20041999
let Some((excerpt_id, _, buffer)) = multi_buffer.as_singleton() else {
20052000
return;
20062001
};
20072002
let buffer = buffer.clone();
20082003
let &excerpt_id = excerpt_id;
2004+
2005+
let buffer_visible_start = scroll_anchor.text_anchor.to_point(&buffer);
2006+
let max_row = buffer.max_point().row;
2007+
let start_row = buffer_visible_start.row.min(max_row);
2008+
let end_row = (buffer_visible_start.row + 10).min(max_row);
2009+
20092010
let syntax = self.style(cx).syntax.clone();
20102011
let background_task = cx.background_spawn(async move {
20112012
buffer

crates/editor/src/editor_tests.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29058,6 +29058,95 @@ async fn test_sticky_scroll(cx: &mut TestAppContext) {
2905829058
assert_eq!(sticky_headers(10.0), vec![]);
2905929059
}
2906029060

29061+
#[gpui::test]
29062+
async fn test_sticky_scroll_with_expanded_deleted_diff_hunks(
29063+
executor: BackgroundExecutor,
29064+
cx: &mut TestAppContext,
29065+
) {
29066+
init_test(cx, |_| {});
29067+
let mut cx = EditorTestContext::new(cx).await;
29068+
29069+
let diff_base = indoc! {"
29070+
fn foo() {
29071+
let a = 1;
29072+
let b = 2;
29073+
let c = 3;
29074+
let d = 4;
29075+
let e = 5;
29076+
}
29077+
"};
29078+
29079+
let buffer = indoc! {"
29080+
ˇfn foo() {
29081+
}
29082+
"};
29083+
29084+
cx.set_state(&buffer);
29085+
29086+
cx.update_editor(|e, _, cx| {
29087+
e.buffer()
29088+
.read(cx)
29089+
.as_singleton()
29090+
.unwrap()
29091+
.update(cx, |buffer, cx| {
29092+
buffer.set_language(Some(rust_lang()), cx);
29093+
})
29094+
});
29095+
29096+
cx.set_head_text(diff_base);
29097+
executor.run_until_parked();
29098+
29099+
cx.update_editor(|editor, window, cx| {
29100+
editor.expand_all_diff_hunks(&ExpandAllDiffHunks, window, cx);
29101+
});
29102+
executor.run_until_parked();
29103+
29104+
// After expanding, the display should look like:
29105+
// row 0: fn foo() {
29106+
// row 1: - let a = 1; (deleted)
29107+
// row 2: - let b = 2; (deleted)
29108+
// row 3: - let c = 3; (deleted)
29109+
// row 4: - let d = 4; (deleted)
29110+
// row 5: - let e = 5; (deleted)
29111+
// row 6: }
29112+
//
29113+
// fn foo() spans display rows 0-6. Scrolling into the deleted region
29114+
// (rows 1-5) should still show fn foo() as a sticky header.
29115+
29116+
let fn_foo = Point { row: 0, column: 0 };
29117+
29118+
let mut sticky_headers = |offset: ScrollOffset| {
29119+
cx.update_editor(|e, window, cx| {
29120+
e.scroll(gpui::Point { x: 0., y: offset }, None, window, cx);
29121+
});
29122+
cx.run_until_parked();
29123+
cx.update_editor(|e, window, cx| {
29124+
EditorElement::sticky_headers(&e, &e.snapshot(window, cx))
29125+
.into_iter()
29126+
.map(
29127+
|StickyHeader {
29128+
start_point,
29129+
offset,
29130+
..
29131+
}| { (start_point, offset) },
29132+
)
29133+
.collect::<Vec<_>>()
29134+
})
29135+
};
29136+
29137+
assert_eq!(sticky_headers(0.0), vec![]);
29138+
assert_eq!(sticky_headers(0.5), vec![(fn_foo, 0.0)]);
29139+
assert_eq!(sticky_headers(1.0), vec![(fn_foo, 0.0)]);
29140+
// Scrolling into deleted lines: fn foo() should still be a sticky header.
29141+
assert_eq!(sticky_headers(2.0), vec![(fn_foo, 0.0)]);
29142+
assert_eq!(sticky_headers(3.0), vec![(fn_foo, 0.0)]);
29143+
assert_eq!(sticky_headers(4.0), vec![(fn_foo, 0.0)]);
29144+
assert_eq!(sticky_headers(5.0), vec![(fn_foo, 0.0)]);
29145+
assert_eq!(sticky_headers(5.5), vec![(fn_foo, -0.5)]);
29146+
// Past the closing brace: no more sticky header.
29147+
assert_eq!(sticky_headers(6.0), vec![]);
29148+
}
29149+
2906129150
#[gpui::test]
2906229151
fn test_relative_line_numbers(cx: &mut TestAppContext) {
2906329152
init_test(cx, |_| {});

0 commit comments

Comments
 (0)