go_to_line: Fix scroll position restore on dismiss (#41234)

Closes #35347

Release Notes:

- Fixed Go To Line jumping back to previous position on dismiss
This commit is contained in:
Mayank Verma
2025-10-29 08:47:48 +00:00
committed by GitHub
parent f9c97d29c8
commit 8725a2d166
+178 -2
View File
@@ -16,7 +16,7 @@ use text::{Bias, Point};
use theme::ActiveTheme;
use ui::prelude::*;
use util::paths::FILE_ROW_COLUMN_DELIMITER;
use workspace::ModalView;
use workspace::{DismissDecision, ModalView};
pub fn init(cx: &mut App) {
LineIndicatorFormat::register(cx);
@@ -31,7 +31,16 @@ pub struct GoToLine {
_subscriptions: Vec<Subscription>,
}
impl ModalView for GoToLine {}
impl ModalView for GoToLine {
fn on_before_dismiss(
&mut self,
_window: &mut Window,
_cx: &mut Context<Self>,
) -> DismissDecision {
self.prev_scroll_position.take();
DismissDecision::Dismiss(true)
}
}
impl Focusable for GoToLine {
fn focus_handle(&self, cx: &App) -> FocusHandle {
@@ -769,4 +778,171 @@ mod tests {
state
})
}
#[gpui::test]
async fn test_scroll_position_on_outside_click(cx: &mut TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.executor());
let file_content = (0..100)
.map(|i| format!("struct Line{};", i))
.collect::<Vec<_>>()
.join("\n");
fs.insert_tree(path!("/dir"), json!({"a.rs": file_content}))
.await;
let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
let (workspace, cx) =
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
let worktree_id = workspace.update(cx, |workspace, cx| {
workspace.project().update(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id()
})
});
let _buffer = project
.update(cx, |project, cx| {
project.open_local_buffer(path!("/dir/a.rs"), cx)
})
.await
.unwrap();
let editor = workspace
.update_in(cx, |workspace, window, cx| {
workspace.open_path((worktree_id, rel_path("a.rs")), None, true, window, cx)
})
.await
.unwrap()
.downcast::<Editor>()
.unwrap();
let go_to_line_view = open_go_to_line_view(&workspace, cx);
let scroll_position_before_input =
editor.update(cx, |editor, cx| editor.scroll_position(cx));
cx.simulate_input("47");
let scroll_position_after_input =
editor.update(cx, |editor, cx| editor.scroll_position(cx));
assert_ne!(scroll_position_before_input, scroll_position_after_input);
drop(go_to_line_view);
workspace.update_in(cx, |workspace, window, cx| {
workspace.hide_modal(window, cx);
});
cx.run_until_parked();
let scroll_position_after_auto_dismiss =
editor.update(cx, |editor, cx| editor.scroll_position(cx));
assert_eq!(
scroll_position_after_auto_dismiss, scroll_position_after_input,
"Dismissing via outside click should maintain new scroll position"
);
}
#[gpui::test]
async fn test_scroll_position_on_cancel(cx: &mut TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.executor());
let file_content = (0..100)
.map(|i| format!("struct Line{};", i))
.collect::<Vec<_>>()
.join("\n");
fs.insert_tree(path!("/dir"), json!({"a.rs": file_content}))
.await;
let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
let (workspace, cx) =
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
let worktree_id = workspace.update(cx, |workspace, cx| {
workspace.project().update(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id()
})
});
let _buffer = project
.update(cx, |project, cx| {
project.open_local_buffer(path!("/dir/a.rs"), cx)
})
.await
.unwrap();
let editor = workspace
.update_in(cx, |workspace, window, cx| {
workspace.open_path((worktree_id, rel_path("a.rs")), None, true, window, cx)
})
.await
.unwrap()
.downcast::<Editor>()
.unwrap();
let go_to_line_view = open_go_to_line_view(&workspace, cx);
let scroll_position_before_input =
editor.update(cx, |editor, cx| editor.scroll_position(cx));
cx.simulate_input("47");
let scroll_position_after_input =
editor.update(cx, |editor, cx| editor.scroll_position(cx));
assert_ne!(scroll_position_before_input, scroll_position_after_input);
cx.dispatch_action(menu::Cancel);
drop(go_to_line_view);
cx.run_until_parked();
let scroll_position_after_cancel =
editor.update(cx, |editor, cx| editor.scroll_position(cx));
assert_eq!(
scroll_position_after_cancel, scroll_position_after_input,
"Cancel should maintain new scroll position"
);
}
#[gpui::test]
async fn test_scroll_position_on_confirm(cx: &mut TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.executor());
let file_content = (0..100)
.map(|i| format!("struct Line{};", i))
.collect::<Vec<_>>()
.join("\n");
fs.insert_tree(path!("/dir"), json!({"a.rs": file_content}))
.await;
let project = Project::test(fs, [path!("/dir").as_ref()], cx).await;
let (workspace, cx) =
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
let worktree_id = workspace.update(cx, |workspace, cx| {
workspace.project().update(cx, |project, cx| {
project.worktrees(cx).next().unwrap().read(cx).id()
})
});
let _buffer = project
.update(cx, |project, cx| {
project.open_local_buffer(path!("/dir/a.rs"), cx)
})
.await
.unwrap();
let editor = workspace
.update_in(cx, |workspace, window, cx| {
workspace.open_path((worktree_id, rel_path("a.rs")), None, true, window, cx)
})
.await
.unwrap()
.downcast::<Editor>()
.unwrap();
let go_to_line_view = open_go_to_line_view(&workspace, cx);
let scroll_position_before_input =
editor.update(cx, |editor, cx| editor.scroll_position(cx));
cx.simulate_input("47");
let scroll_position_after_input =
editor.update(cx, |editor, cx| editor.scroll_position(cx));
assert_ne!(scroll_position_before_input, scroll_position_after_input);
cx.dispatch_action(menu::Confirm);
drop(go_to_line_view);
cx.run_until_parked();
let scroll_position_after_confirm =
editor.update(cx, |editor, cx| editor.scroll_position(cx));
assert_eq!(
scroll_position_after_confirm, scroll_position_after_input,
"Confirm should maintain new scroll position"
);
}
}