Scroll project search results to the top (#8329)

Scroll project search results to the top after every new search.

Release Notes:

- Fixed autoscrolling of the project search results ([8237](https://github.com/zed-industries/zed/issues/8237))
This commit is contained in:
Andrew Lygin 2024-03-01 12:40:57 +03:00 committed by GitHub
parent d6492d091d
commit aeed9d0d8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -7,16 +7,18 @@ use crate::{
use anyhow::{Context as _, Result}; use anyhow::{Context as _, Result};
use collections::HashMap; use collections::HashMap;
use editor::{ use editor::{
actions::SelectAll, items::active_match_index, scroll::Autoscroll, Anchor, Editor, EditorEvent, actions::SelectAll,
MultiBuffer, MAX_TAB_TITLE_LEN, items::active_match_index,
scroll::{Autoscroll, Axis},
Anchor, Editor, EditorEvent, MultiBuffer, MAX_TAB_TITLE_LEN,
}; };
use editor::{EditorElement, EditorStyle}; use editor::{EditorElement, EditorStyle};
use gpui::{ use gpui::{
actions, div, Action, AnyElement, AnyView, AppContext, Context as _, Element, EntityId, actions, div, Action, AnyElement, AnyView, AppContext, Context as _, Element, EntityId,
EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight, Global, Hsla, EventEmitter, FocusHandle, FocusableView, FontStyle, FontWeight, Global, Hsla,
InteractiveElement, IntoElement, KeyContext, Model, ModelContext, ParentElement, PromptLevel, InteractiveElement, IntoElement, KeyContext, Model, ModelContext, ParentElement, Point,
Render, SharedString, Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext, PromptLevel, Render, SharedString, Styled, Subscription, Task, TextStyle, View, ViewContext,
WeakModel, WeakView, WhiteSpace, WindowContext, VisualContext, WeakModel, WeakView, WhiteSpace, WindowContext,
}; };
use menu::Confirm; use menu::Confirm;
use project::{ use project::{
@ -1302,6 +1304,7 @@ impl ProjectSearchView {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| { editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_ranges(range_to_select) s.select_ranges(range_to_select)
}); });
editor.scroll(Point::default(), Some(Axis::Vertical), cx);
} }
editor.highlight_background::<Self>( editor.highlight_background::<Self>(
match_ranges, match_ranges,
@ -2094,11 +2097,12 @@ fn register_workspace_action_for_present_search<A: Action>(
pub mod tests { pub mod tests {
use super::*; use super::*;
use editor::DisplayPoint; use editor::DisplayPoint;
use gpui::{Action, TestAppContext}; use gpui::{Action, TestAppContext, WindowHandle};
use project::FakeFs; use project::FakeFs;
use semantic_index::semantic_index_settings::SemanticIndexSettings; use semantic_index::semantic_index_settings::SemanticIndexSettings;
use serde_json::json; use serde_json::json;
use settings::{Settings, SettingsStore}; use settings::{Settings, SettingsStore};
use std::sync::Arc;
use workspace::DeploySearch; use workspace::DeploySearch;
#[gpui::test] #[gpui::test]
@ -2120,15 +2124,7 @@ pub mod tests {
let search = cx.new_model(|cx| ProjectSearch::new(project, cx)); let search = cx.new_model(|cx| ProjectSearch::new(project, cx));
let search_view = cx.add_window(|cx| ProjectSearchView::new(search.clone(), cx, None)); let search_view = cx.add_window(|cx| ProjectSearchView::new(search.clone(), cx, None));
search_view perform_search(search_view, "TWO", cx);
.update(cx, |search_view, cx| {
search_view
.query_editor
.update(cx, |query_editor, cx| query_editor.set_text("TWO", cx));
search_view.search(cx);
})
.unwrap();
cx.background_executor.run_until_parked();
search_view.update(cx, |search_view, cx| { search_view.update(cx, |search_view, cx| {
assert_eq!( assert_eq!(
search_view search_view
@ -3377,7 +3373,78 @@ pub mod tests {
.unwrap(); .unwrap();
} }
pub fn init_test(cx: &mut TestAppContext) { #[gpui::test]
async fn test_scroll_search_results_to_top(cx: &mut TestAppContext) {
init_test(cx);
// We need many lines in the search results to be able to scroll the window
let fs = FakeFs::new(cx.background_executor.clone());
fs.insert_tree(
"/dir",
json!({
"1.txt": "\n\n\n\n\n A \n\n\n\n\n",
"2.txt": "\n\n\n\n\n A \n\n\n\n\n",
"3.rs": "\n\n\n\n\n A \n\n\n\n\n",
"4.rs": "\n\n\n\n\n A \n\n\n\n\n",
"5.rs": "\n\n\n\n\n A \n\n\n\n\n",
"6.rs": "\n\n\n\n\n A \n\n\n\n\n",
"7.rs": "\n\n\n\n\n A \n\n\n\n\n",
"8.rs": "\n\n\n\n\n A \n\n\n\n\n",
"9.rs": "\n\n\n\n\n A \n\n\n\n\n",
"a.rs": "\n\n\n\n\n A \n\n\n\n\n",
"b.rs": "\n\n\n\n\n B \n\n\n\n\n",
"c.rs": "\n\n\n\n\n B \n\n\n\n\n",
"d.rs": "\n\n\n\n\n B \n\n\n\n\n",
"e.rs": "\n\n\n\n\n B \n\n\n\n\n",
"f.rs": "\n\n\n\n\n B \n\n\n\n\n",
"g.rs": "\n\n\n\n\n B \n\n\n\n\n",
"h.rs": "\n\n\n\n\n B \n\n\n\n\n",
"i.rs": "\n\n\n\n\n B \n\n\n\n\n",
"j.rs": "\n\n\n\n\n B \n\n\n\n\n",
"k.rs": "\n\n\n\n\n B \n\n\n\n\n",
}),
)
.await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let search = cx.new_model(|cx| ProjectSearch::new(project, cx));
let search_view = cx.add_window(|cx| ProjectSearchView::new(search.clone(), cx, None));
// First search
perform_search(search_view.clone(), "A", cx);
search_view
.update(cx, |search_view, cx| {
search_view.results_editor.update(cx, |results_editor, cx| {
// Results are correct and scrolled to the top
assert_eq!(
results_editor.display_text(cx).match_indices(" A ").count(),
10
);
assert_eq!(results_editor.scroll_position(cx), Point::default());
// Scroll results all the way down
results_editor.scroll(Point::new(0., f32::MAX), Some(Axis::Vertical), cx);
});
})
.expect("unable to update search view");
// Second search
perform_search(search_view.clone(), "B", cx);
search_view
.update(cx, |search_view, cx| {
search_view.results_editor.update(cx, |results_editor, cx| {
// Results are correct...
assert_eq!(
results_editor.display_text(cx).match_indices(" B ").count(),
10
);
// ...and scrolled back to the top
assert_eq!(results_editor.scroll_position(cx), Point::default());
});
})
.expect("unable to update search view");
}
fn init_test(cx: &mut TestAppContext) {
cx.update(|cx| { cx.update(|cx| {
let settings = SettingsStore::test(cx); let settings = SettingsStore::test(cx);
cx.set_global(settings); cx.set_global(settings);
@ -3394,4 +3461,20 @@ pub mod tests {
super::init(cx); super::init(cx);
}); });
} }
fn perform_search(
search_view: WindowHandle<ProjectSearchView>,
text: impl Into<Arc<str>>,
cx: &mut TestAppContext,
) {
search_view
.update(cx, |search_view, cx| {
search_view
.query_editor
.update(cx, |query_editor, cx| query_editor.set_text(text, cx));
search_view.search(cx);
})
.unwrap();
cx.background_executor.run_until_parked();
}
} }