diff --git a/crates/editor2/src/element.rs b/crates/editor2/src/element.rs index 4f7156a74720db1ab53b98a62942ac8044139b64..4f3bda3752adddf53c1869a9846b08941a9e2211 100644 --- a/crates/editor2/src/element.rs +++ b/crates/editor2/src/element.rs @@ -1449,7 +1449,6 @@ impl EditorElement { let snapshot = editor.snapshot(cx); let style = self.style.clone(); - dbg!(&style.text.font()); let font_id = cx.text_system().font_id(&style.text.font()).unwrap(); let font_size = style.text.font_size.to_pixels(cx.rem_size()); let line_height = style.text.line_height_in_pixels(cx.rem_size()); diff --git a/crates/file_finder2/src/file_finder.rs b/crates/file_finder2/src/file_finder.rs index 2b78a24dea6bc7dca05ccf85643478d9526f10e1..72638f603f8005626ce7baa219669b8de1b88dc6 100644 --- a/crates/file_finder2/src/file_finder.rs +++ b/crates/file_finder2/src/file_finder.rs @@ -593,6 +593,7 @@ impl PickerDelegate for FileFinderDelegate { } fn confirm(&mut self, secondary: bool, cx: &mut ViewContext>) { + dbg!("CONFIRMING???"); if let Some(m) = self.matches.get(self.selected_index()) { if let Some(workspace) = self.workspace.upgrade() { let open_task = workspace.update(cx, move |workspace, cx| { @@ -690,6 +691,7 @@ impl PickerDelegate for FileFinderDelegate { .log_err(); } } + dbg!("DISMISSING"); finder .update(&mut cx, |_, cx| cx.emit(ModalEvent::Dismissed)) .ok()?; @@ -739,1236 +741,1236 @@ impl PickerDelegate for FileFinderDelegate { } } -#[cfg(test)] -mod tests { - use std::{assert_eq, collections::HashMap, path::Path, time::Duration}; - - use super::*; - use editor::Editor; - use gpui::{Entity, TestAppContext, VisualTestContext}; - use menu::{Confirm, SelectNext}; - use serde_json::json; - use workspace::{AppState, Workspace}; - - #[ctor::ctor] - fn init_logger() { - if std::env::var("RUST_LOG").is_ok() { - env_logger::init(); - } - } - - #[gpui::test] - async fn test_matching_paths(cx: &mut TestAppContext) { - let app_state = init_test(cx); - app_state - .fs - .as_fake() - .insert_tree( - "/root", - json!({ - "a": { - "banana": "", - "bandana": "", - } - }), - ) - .await; - - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - - let (picker, workspace, mut cx) = build_find_picker(project, cx); - let cx = &mut cx; - - picker - .update(cx, |picker, cx| { - picker.delegate.update_matches("bna".to_string(), cx) - }) - .await; - - picker.update(cx, |picker, _| { - assert_eq!(picker.delegate.matches.len(), 2); - }); - - let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(SelectNext); - cx.dispatch_action(Confirm); - active_pane - .condition(cx, |pane, _| pane.active_item().is_some()) - .await; - cx.read(|cx| { - let active_item = active_pane.read(cx).active_item().unwrap(); - assert_eq!( - active_item - .to_any() - .downcast::() - .unwrap() - .read(cx) - .title(cx), - "bandana" - ); - }); - } - - #[gpui::test] - async fn test_row_column_numbers_query_inside_file(cx: &mut TestAppContext) { - let app_state = init_test(cx); - - let first_file_name = "first.rs"; - let first_file_contents = "// First Rust file"; - app_state - .fs - .as_fake() - .insert_tree( - "/src", - json!({ - "test": { - first_file_name: first_file_contents, - "second.rs": "// Second Rust file", - } - }), - ) - .await; - - let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; - - let (picker, workspace, mut cx) = build_find_picker(project, cx); - let cx = &mut cx; - - let file_query = &first_file_name[..3]; - let file_row = 1; - let file_column = 3; - assert!(file_column <= first_file_contents.len()); - let query_inside_file = format!("{file_query}:{file_row}:{file_column}"); - picker - .update(cx, |finder, cx| { - finder - .delegate - .update_matches(query_inside_file.to_string(), cx) - }) - .await; - picker.update(cx, |finder, _| { - let finder = &finder.delegate; - assert_eq!(finder.matches.len(), 1); - let latest_search_query = finder - .latest_search_query - .as_ref() - .expect("Finder should have a query after the update_matches call"); - assert_eq!(latest_search_query.path_like.raw_query, query_inside_file); - assert_eq!( - latest_search_query.path_like.file_query_end, - Some(file_query.len()) - ); - assert_eq!(latest_search_query.row, Some(file_row)); - assert_eq!(latest_search_query.column, Some(file_column as u32)); - }); - - let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(SelectNext); - cx.dispatch_action(Confirm); - active_pane - .condition(cx, |pane, _| pane.active_item().is_some()) - .await; - let editor = cx.update(|cx| { - let active_item = active_pane.read(cx).active_item().unwrap(); - active_item.downcast::().unwrap() - }); - cx.executor().advance_clock(Duration::from_secs(2)); - - editor.update(cx, |editor, cx| { - let all_selections = editor.selections.all_adjusted(cx); - assert_eq!( - all_selections.len(), - 1, - "Expected to have 1 selection (caret) after file finder confirm, but got: {all_selections:?}" - ); - let caret_selection = all_selections.into_iter().next().unwrap(); - assert_eq!(caret_selection.start, caret_selection.end, - "Caret selection should have its start and end at the same position"); - assert_eq!(file_row, caret_selection.start.row + 1, - "Query inside file should get caret with the same focus row"); - assert_eq!(file_column, caret_selection.start.column as usize + 1, - "Query inside file should get caret with the same focus column"); - }); - } - - #[gpui::test] - async fn test_row_column_numbers_query_outside_file(cx: &mut TestAppContext) { - let app_state = init_test(cx); - - let first_file_name = "first.rs"; - let first_file_contents = "// First Rust file"; - app_state - .fs - .as_fake() - .insert_tree( - "/src", - json!({ - "test": { - first_file_name: first_file_contents, - "second.rs": "// Second Rust file", - } - }), - ) - .await; - - let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; - - let (picker, workspace, mut cx) = build_find_picker(project, cx); - let cx = &mut cx; - - let file_query = &first_file_name[..3]; - let file_row = 200; - let file_column = 300; - assert!(file_column > first_file_contents.len()); - let query_outside_file = format!("{file_query}:{file_row}:{file_column}"); - picker - .update(cx, |picker, cx| { - picker - .delegate - .update_matches(query_outside_file.to_string(), cx) - }) - .await; - picker.update(cx, |finder, _| { - let delegate = &finder.delegate; - assert_eq!(delegate.matches.len(), 1); - let latest_search_query = delegate - .latest_search_query - .as_ref() - .expect("Finder should have a query after the update_matches call"); - assert_eq!(latest_search_query.path_like.raw_query, query_outside_file); - assert_eq!( - latest_search_query.path_like.file_query_end, - Some(file_query.len()) - ); - assert_eq!(latest_search_query.row, Some(file_row)); - assert_eq!(latest_search_query.column, Some(file_column as u32)); - }); - - let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(SelectNext); - cx.dispatch_action(Confirm); - active_pane - .condition(cx, |pane, _| pane.active_item().is_some()) - .await; - let editor = cx.update(|cx| { - let active_item = active_pane.read(cx).active_item().unwrap(); - active_item.downcast::().unwrap() - }); - cx.executor().advance_clock(Duration::from_secs(2)); - - editor.update(cx, |editor, cx| { - let all_selections = editor.selections.all_adjusted(cx); - assert_eq!( - all_selections.len(), - 1, - "Expected to have 1 selection (caret) after file finder confirm, but got: {all_selections:?}" - ); - let caret_selection = all_selections.into_iter().next().unwrap(); - assert_eq!(caret_selection.start, caret_selection.end, - "Caret selection should have its start and end at the same position"); - assert_eq!(0, caret_selection.start.row, - "Excessive rows (as in query outside file borders) should get trimmed to last file row"); - assert_eq!(first_file_contents.len(), caret_selection.start.column as usize, - "Excessive columns (as in query outside file borders) should get trimmed to selected row's last column"); - }); - } - - #[gpui::test] - async fn test_matching_cancellation(cx: &mut TestAppContext) { - let app_state = init_test(cx); - app_state - .fs - .as_fake() - .insert_tree( - "/dir", - json!({ - "hello": "", - "goodbye": "", - "halogen-light": "", - "happiness": "", - "height": "", - "hi": "", - "hiccup": "", - }), - ) - .await; - - let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await; - - let (picker, _, mut cx) = build_find_picker(project, cx); - let cx = &mut cx; - - let query = test_path_like("hi"); - picker - .update(cx, |picker, cx| { - picker.delegate.spawn_search(query.clone(), cx) - }) - .await; - - picker.update(cx, |picker, _cx| { - assert_eq!(picker.delegate.matches.len(), 5) - }); - - picker.update(cx, |picker, cx| { - let delegate = &mut picker.delegate; - assert!( - delegate.matches.history.is_empty(), - "Search matches expected" - ); - let matches = delegate.matches.search.clone(); - - // Simulate a search being cancelled after the time limit, - // returning only a subset of the matches that would have been found. - drop(delegate.spawn_search(query.clone(), cx)); - delegate.set_search_matches( - delegate.latest_search_id, - true, // did-cancel - query.clone(), - vec![matches[1].clone(), matches[3].clone()], - cx, - ); - - // Simulate another cancellation. - drop(delegate.spawn_search(query.clone(), cx)); - delegate.set_search_matches( - delegate.latest_search_id, - true, // did-cancel - query.clone(), - vec![matches[0].clone(), matches[2].clone(), matches[3].clone()], - cx, - ); - - assert!( - delegate.matches.history.is_empty(), - "Search matches expected" - ); - assert_eq!(delegate.matches.search.as_slice(), &matches[0..4]); - }); - } - - #[gpui::test] - async fn test_ignored_files(cx: &mut TestAppContext) { - let app_state = init_test(cx); - app_state - .fs - .as_fake() - .insert_tree( - "/ancestor", - json!({ - ".gitignore": "ignored-root", - "ignored-root": { - "happiness": "", - "height": "", - "hi": "", - "hiccup": "", - }, - "tracked-root": { - ".gitignore": "height", - "happiness": "", - "height": "", - "hi": "", - "hiccup": "", - }, - }), - ) - .await; - - let project = Project::test( - app_state.fs.clone(), - [ - "/ancestor/tracked-root".as_ref(), - "/ancestor/ignored-root".as_ref(), - ], - cx, - ) - .await; - - let (picker, _, mut cx) = build_find_picker(project, cx); - let cx = &mut cx; - - picker - .update(cx, |picker, cx| { - picker.delegate.spawn_search(test_path_like("hi"), cx) - }) - .await; - picker.update(cx, |picker, _| assert_eq!(picker.delegate.matches.len(), 7)); - } - - // #[gpui::test] - // async fn test_single_file_worktrees(cx: &mut TestAppContext) { - // let app_state = init_test(cx); - // app_state - // .fs - // .as_fake() - // .insert_tree("/root", json!({ "the-parent-dir": { "the-file": "" } })) - // .await; - - // let project = Project::test( - // app_state.fs.clone(), - // ["/root/the-parent-dir/the-file".as_ref()], - // cx, - // ) - // .await; - - // let (picker, _, mut cx) = build_find_picker(project, cx); - // let cx = &mut cx; - - // // Even though there is only one worktree, that worktree's filename - // // is included in the matching, because the worktree is a single file. - // picker - // .update(cx, |picker, cx| { - // picker.delegate.spawn_search(test_path_like("thf"), cx) - // }) - // .await; - // cx.read(|cx| { - // let picker = picker.read(cx); - // let delegate = &picker.delegate; - // assert!( - // delegate.matches.history.is_empty(), - // "Search matches expected" - // ); - // let matches = delegate.matches.search.clone(); - // assert_eq!(matches.len(), 1); - - // let (file_name, file_name_positions, full_path, full_path_positions) = - // delegate.labels_for_path_match(&matches[0]); - // assert_eq!(file_name, "the-file"); - // assert_eq!(file_name_positions, &[0, 1, 4]); - // assert_eq!(full_path, "the-file"); - // assert_eq!(full_path_positions, &[0, 1, 4]); - // }); - - // // Since the worktree root is a file, searching for its name followed by a slash does - // // not match anything. - // picker - // .update(cx, |f, cx| { - // f.delegate.spawn_search(test_path_like("thf/"), cx) - // }) - // .await; - // picker.update(cx, |f, _| assert_eq!(f.delegate.matches.len(), 0)); - // } - - // #[gpui::test] - // async fn test_path_distance_ordering(cx: &mut TestAppContext) { - // let app_state = init_test(cx); - // app_state - // .fs - // .as_fake() - // .insert_tree( - // "/root", - // json!({ - // "dir1": { "a.txt": "" }, - // "dir2": { - // "a.txt": "", - // "b.txt": "" - // } - // }), - // ) - // .await; - - // let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - // let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); - // let cx = &mut cx; - - // let worktree_id = cx.read(|cx| { - // let worktrees = workspace.read(cx).worktrees(cx).collect::>(); - // assert_eq!(worktrees.len(), 1); - // WorktreeId::from_usize(worktrees[0].id()) - // }); - - // // When workspace has an active item, sort items which are closer to that item - // // first when they have the same name. In this case, b.txt is closer to dir2's a.txt - // // so that one should be sorted earlier - // let b_path = Some(dummy_found_path(ProjectPath { - // worktree_id, - // path: Arc::from(Path::new("/root/dir2/b.txt")), - // })); - // cx.dispatch_action(Toggle); - - // let finder = cx - // .add_window(|cx| { - // Picker::new( - // FileFinderDelegate::new( - // workspace.downgrade(), - // workspace.read(cx).project().clone(), - // b_path, - // Vec::new(), - // cx, - // ), - // cx, - // ) - // }) - // .root(cx); - - // finder - // .update(cx, |f, cx| { - // f.delegate.spawn_search(test_path_like("a.txt"), cx) - // }) - // .await; - - // finder.read_with(cx, |f, _| { - // let delegate = &f.delegate; - // assert!( - // delegate.matches.history.is_empty(), - // "Search matches expected" - // ); - // let matches = delegate.matches.search.clone(); - // assert_eq!(matches[0].path.as_ref(), Path::new("dir2/a.txt")); - // assert_eq!(matches[1].path.as_ref(), Path::new("dir1/a.txt")); - // }); - // } - - // #[gpui::test] - // async fn test_search_worktree_without_files(cx: &mut TestAppContext) { - // let app_state = init_test(cx); - // app_state - // .fs - // .as_fake() - // .insert_tree( - // "/root", - // json!({ - // "dir1": {}, - // "dir2": { - // "dir3": {} - // } - // }), - // ) - // .await; - - // let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - // let workspace = cx - // .add_window(|cx| Workspace::test_new(project, cx)) - // .root(cx); - // let finder = cx - // .add_window(|cx| { - // Picker::new( - // FileFinderDelegate::new( - // workspace.downgrade(), - // workspace.read(cx).project().clone(), - // None, - // Vec::new(), - // cx, - // ), - // cx, - // ) - // }) - // .root(cx); - // finder - // .update(cx, |f, cx| { - // f.delegate.spawn_search(test_path_like("dir"), cx) - // }) - // .await; - // cx.read(|cx| { - // let finder = finder.read(cx); - // assert_eq!(finder.delegate.matches.len(), 0); - // }); - // } - - // #[gpui::test] - // async fn test_query_history(cx: &mut gpui::TestAppContext) { - // let app_state = init_test(cx); - - // app_state - // .fs - // .as_fake() - // .insert_tree( - // "/src", - // json!({ - // "test": { - // "first.rs": "// First Rust file", - // "second.rs": "// Second Rust file", - // "third.rs": "// Third Rust file", - // } - // }), - // ) - // .await; - - // let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; - // let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); - // let cx = &mut cx; - // let worktree_id = cx.read(|cx| { - // let worktrees = workspace.read(cx).worktrees(cx).collect::>(); - // assert_eq!(worktrees.len(), 1); - // WorktreeId::from_usize(worktrees[0].id()) - // }); - - // // Open and close panels, getting their history items afterwards. - // // Ensure history items get populated with opened items, and items are kept in a certain order. - // // The history lags one opened buffer behind, since it's updated in the search panel only on its reopen. - // // - // // TODO: without closing, the opened items do not propagate their history changes for some reason - // // it does work in real app though, only tests do not propagate. - - // let initial_history = open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; - // assert!( - // initial_history.is_empty(), - // "Should have no history before opening any files" - // ); - - // let history_after_first = - // open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; - // assert_eq!( - // history_after_first, - // vec![FoundPath::new( - // ProjectPath { - // worktree_id, - // path: Arc::from(Path::new("test/first.rs")), - // }, - // Some(PathBuf::from("/src/test/first.rs")) - // )], - // "Should show 1st opened item in the history when opening the 2nd item" - // ); - - // let history_after_second = - // open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await; - // assert_eq!( - // history_after_second, - // vec![ - // FoundPath::new( - // ProjectPath { - // worktree_id, - // path: Arc::from(Path::new("test/second.rs")), - // }, - // Some(PathBuf::from("/src/test/second.rs")) - // ), - // FoundPath::new( - // ProjectPath { - // worktree_id, - // path: Arc::from(Path::new("test/first.rs")), - // }, - // Some(PathBuf::from("/src/test/first.rs")) - // ), - // ], - // "Should show 1st and 2nd opened items in the history when opening the 3rd item. \ - // 2nd item should be the first in the history, as the last opened." - // ); - - // let history_after_third = - // open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; - // assert_eq!( - // history_after_third, - // vec![ - // FoundPath::new( - // ProjectPath { - // worktree_id, - // path: Arc::from(Path::new("test/third.rs")), - // }, - // Some(PathBuf::from("/src/test/third.rs")) - // ), - // FoundPath::new( - // ProjectPath { - // worktree_id, - // path: Arc::from(Path::new("test/second.rs")), - // }, - // Some(PathBuf::from("/src/test/second.rs")) - // ), - // FoundPath::new( - // ProjectPath { - // worktree_id, - // path: Arc::from(Path::new("test/first.rs")), - // }, - // Some(PathBuf::from("/src/test/first.rs")) - // ), - // ], - // "Should show 1st, 2nd and 3rd opened items in the history when opening the 2nd item again. \ - // 3rd item should be the first in the history, as the last opened." - // ); - - // let history_after_second_again = - // open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await; - // assert_eq!( - // history_after_second_again, - // vec![ - // FoundPath::new( - // ProjectPath { - // worktree_id, - // path: Arc::from(Path::new("test/second.rs")), - // }, - // Some(PathBuf::from("/src/test/second.rs")) - // ), - // FoundPath::new( - // ProjectPath { - // worktree_id, - // path: Arc::from(Path::new("test/third.rs")), - // }, - // Some(PathBuf::from("/src/test/third.rs")) - // ), - // FoundPath::new( - // ProjectPath { - // worktree_id, - // path: Arc::from(Path::new("test/first.rs")), - // }, - // Some(PathBuf::from("/src/test/first.rs")) - // ), - // ], - // "Should show 1st, 2nd and 3rd opened items in the history when opening the 3rd item again. \ - // 2nd item, as the last opened, 3rd item should go next as it was opened right before." - // ); - // } - - // #[gpui::test] - // async fn test_external_files_history(cx: &mut gpui::TestAppContext) { - // let app_state = init_test(cx); - - // app_state - // .fs - // .as_fake() - // .insert_tree( - // "/src", - // json!({ - // "test": { - // "first.rs": "// First Rust file", - // "second.rs": "// Second Rust file", - // } - // }), - // ) - // .await; - - // app_state - // .fs - // .as_fake() - // .insert_tree( - // "/external-src", - // json!({ - // "test": { - // "third.rs": "// Third Rust file", - // "fourth.rs": "// Fourth Rust file", - // } - // }), - // ) - // .await; - - // let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; - // cx.update(|cx| { - // project.update(cx, |project, cx| { - // project.find_or_create_local_worktree("/external-src", false, cx) - // }) - // }) - // .detach(); - // cx.background_executor.run_until_parked(); - - // let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); - // let cx = &mut cx; - // let worktree_id = cx.read(|cx| { - // let worktrees = workspace.read(cx).worktrees(cx).collect::>(); - // assert_eq!(worktrees.len(), 1,); - - // WorktreeId::from_usize(worktrees[0].id()) - // }); - // workspace - // .update(cx, |workspace, cx| { - // workspace.open_abs_path(PathBuf::from("/external-src/test/third.rs"), false, cx) - // }) - // .detach(); - // cx.background_executor.run_until_parked(); - // let external_worktree_id = cx.read(|cx| { - // let worktrees = workspace.read(cx).worktrees(cx).collect::>(); - // assert_eq!( - // worktrees.len(), - // 2, - // "External file should get opened in a new worktree" - // ); - - // WorktreeId::from_usize( - // worktrees - // .into_iter() - // .find(|worktree| worktree.entity_id() != worktree_id.to_usize()) - // .expect("New worktree should have a different id") - // .id(), - // ) - // }); - // close_active_item(&workspace, cx).await; - - // let initial_history_items = - // open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; - // assert_eq!( - // initial_history_items, - // vec![FoundPath::new( - // ProjectPath { - // worktree_id: external_worktree_id, - // path: Arc::from(Path::new("")), - // }, - // Some(PathBuf::from("/external-src/test/third.rs")) - // )], - // "Should show external file with its full path in the history after it was open" - // ); - - // let updated_history_items = - // open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; - // assert_eq!( - // updated_history_items, - // vec![ - // FoundPath::new( - // ProjectPath { - // worktree_id, - // path: Arc::from(Path::new("test/second.rs")), - // }, - // Some(PathBuf::from("/src/test/second.rs")) - // ), - // FoundPath::new( - // ProjectPath { - // worktree_id: external_worktree_id, - // path: Arc::from(Path::new("")), - // }, - // Some(PathBuf::from("/external-src/test/third.rs")) - // ), - // ], - // "Should keep external file with history updates", - // ); - // } - - #[gpui::test] - async fn test_toggle_panel_new_selections(cx: &mut gpui::TestAppContext) { - let app_state = init_test(cx); - - app_state - .fs - .as_fake() - .insert_tree( - "/src", - json!({ - "test": { - "first.rs": "// First Rust file", - "second.rs": "// Second Rust file", - "third.rs": "// Third Rust file", - } - }), - ) - .await; - - let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; - let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); - let cx = &mut cx; - - // generate some history to select from - open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; - open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; - open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await; - let current_history = - open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; - - for expected_selected_index in 0..current_history.len() { - cx.dispatch_action(Toggle); - let selected_index = workspace.update(cx, |workspace, cx| { - workspace - .current_modal::(cx) - .unwrap() - .read(cx) - .picker - .read(cx) - .delegate - .selected_index() - }); - assert_eq!( - selected_index, expected_selected_index, - "Should select the next item in the history" - ); - } - - cx.dispatch_action(Toggle); - let selected_index = workspace.update(cx, |workspace, cx| { - workspace - .current_modal::(cx) - .unwrap() - .read(cx) - .picker - .read(cx) - .delegate - .selected_index() - }); - assert_eq!( - selected_index, 0, - "Should wrap around the history and start all over" - ); - } - - // #[gpui::test] - // async fn test_search_preserves_history_items(cx: &mut gpui::TestAppContext) { - // let app_state = init_test(cx); - - // app_state - // .fs - // .as_fake() - // .insert_tree( - // "/src", - // json!({ - // "test": { - // "first.rs": "// First Rust file", - // "second.rs": "// Second Rust file", - // "third.rs": "// Third Rust file", - // "fourth.rs": "// Fourth Rust file", - // } - // }), - // ) - // .await; - - // let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; - // let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); - // let cx = &mut cx; - // let worktree_id = cx.read(|cx| { - // let worktrees = workspace.read(cx).worktrees(cx).collect::>(); - // assert_eq!(worktrees.len(), 1,); - - // WorktreeId::from_usize(worktrees[0].entity_id()) - // }); - - // // generate some history to select from - // open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; - // open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; - // open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await; - // open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; - - // cx.dispatch_action(Toggle); - // let first_query = "f"; - // let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); - // finder - // .update(cx, |finder, cx| { - // finder.delegate.update_matches(first_query.to_string(), cx) - // }) - // .await; - // finder.read_with(cx, |finder, _| { - // let delegate = &finder.delegate; - // assert_eq!(delegate.matches.history.len(), 1, "Only one history item contains {first_query}, it should be present and others should be filtered out"); - // let history_match = delegate.matches.history.first().unwrap(); - // assert!(history_match.1.is_some(), "Should have path matches for history items after querying"); - // assert_eq!(history_match.0, FoundPath::new( - // ProjectPath { - // worktree_id, - // path: Arc::from(Path::new("test/first.rs")), - // }, - // Some(PathBuf::from("/src/test/first.rs")) - // )); - // assert_eq!(delegate.matches.search.len(), 1, "Only one non-history item contains {first_query}, it should be present"); - // assert_eq!(delegate.matches.search.first().unwrap().path.as_ref(), Path::new("test/fourth.rs")); - // }); - - // let second_query = "fsdasdsa"; - // let finder = workspace.update(cx, |workspace, cx| { - // workspace - // .current_modal::(cx) - // .unwrap() - // .read(cx) - // .picker - // }); - // finder - // .update(cx, |finder, cx| { - // finder.delegate.update_matches(second_query.to_string(), cx) - // }) - // .await; - // finder.update(cx, |finder, _| { - // let delegate = &finder.delegate; - // assert!( - // delegate.matches.history.is_empty(), - // "No history entries should match {second_query}" - // ); - // assert!( - // delegate.matches.search.is_empty(), - // "No search entries should match {second_query}" - // ); - // }); - - // let first_query_again = first_query; - - // let finder = workspace.update(cx, |workspace, cx| { - // workspace - // .current_modal::(cx) - // .unwrap() - // .read(cx) - // .picker - // }); - // finder - // .update(cx, |finder, cx| { - // finder - // .delegate - // .update_matches(first_query_again.to_string(), cx) - // }) - // .await; - // finder.read_with(cx, |finder, _| { - // let delegate = &finder.delegate; - // assert_eq!(delegate.matches.history.len(), 1, "Only one history item contains {first_query_again}, it should be present and others should be filtered out, even after non-matching query"); - // let history_match = delegate.matches.history.first().unwrap(); - // assert!(history_match.1.is_some(), "Should have path matches for history items after querying"); - // assert_eq!(history_match.0, FoundPath::new( - // ProjectPath { - // worktree_id, - // path: Arc::from(Path::new("test/first.rs")), - // }, - // Some(PathBuf::from("/src/test/first.rs")) - // )); - // assert_eq!(delegate.matches.search.len(), 1, "Only one non-history item contains {first_query_again}, it should be present, even after non-matching query"); - // assert_eq!(delegate.matches.search.first().unwrap().path.as_ref(), Path::new("test/fourth.rs")); - // }); - // } - - // #[gpui::test] - // async fn test_history_items_vs_very_good_external_match(cx: &mut gpui::TestAppContext) { - // let app_state = init_test(cx); - - // app_state - // .fs - // .as_fake() - // .insert_tree( - // "/src", - // json!({ - // "collab_ui": { - // "first.rs": "// First Rust file", - // "second.rs": "// Second Rust file", - // "third.rs": "// Third Rust file", - // "collab_ui.rs": "// Fourth Rust file", - // } - // }), - // ) - // .await; - - // let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; - // let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); - // let cx = &mut cx; - // // generate some history to select from - // open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; - // open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; - // open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await; - // open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; - - // cx.dispatch_action(Toggle); - // let query = "collab_ui"; - // let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); - // finder - // .update(cx, |finder, cx| { - // finder.delegate.update_matches(query.to_string(), cx) - // }) - // .await; - // finder.read_with(cx, |finder, _| { - // let delegate = &finder.delegate; - // assert!( - // delegate.matches.history.is_empty(), - // "History items should not math query {query}, they should be matched by name only" - // ); - - // let search_entries = delegate - // .matches - // .search - // .iter() - // .map(|path_match| path_match.path.to_path_buf()) - // .collect::>(); - // assert_eq!( - // search_entries, - // vec![ - // PathBuf::from("collab_ui/collab_ui.rs"), - // PathBuf::from("collab_ui/third.rs"), - // PathBuf::from("collab_ui/first.rs"), - // PathBuf::from("collab_ui/second.rs"), - // ], - // "Despite all search results having the same directory name, the most matching one should be on top" - // ); - // }); - // } - - // #[gpui::test] - // async fn test_nonexistent_history_items_not_shown(cx: &mut gpui::TestAppContext) { - // let app_state = init_test(cx); - - // app_state - // .fs - // .as_fake() - // .insert_tree( - // "/src", - // json!({ - // "test": { - // "first.rs": "// First Rust file", - // "nonexistent.rs": "// Second Rust file", - // "third.rs": "// Third Rust file", - // } - // }), - // ) - // .await; - - // let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; - // let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); - // let cx = &mut cx; - // // generate some history to select from - // open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; - // open_close_queried_buffer("non", 1, "nonexistent.rs", &workspace, cx).await; - // open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await; - // open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; - - // cx.dispatch_action(Toggle); - // let query = "rs"; - // let finder = cx.read(|cx| workspace.read(cx).current_modal::().unwrap()); - // finder - // .update(cx, |finder, cx| { - // finder.picker.update(cx, |picker, cx| { - // picker.delegate.update_matches(query.to_string(), cx) - // }) - // }) - // .await; - // finder.update(cx, |finder, _| { - // let history_entries = finder.delegate - // .matches - // .history - // .iter() - // .map(|(_, path_match)| path_match.as_ref().expect("should have a path match").path.to_path_buf()) - // .collect::>(); - // assert_eq!( - // history_entries, - // vec![ - // PathBuf::from("test/first.rs"), - // PathBuf::from("test/third.rs"), - // ], - // "Should have all opened files in the history, except the ones that do not exist on disk" - // ); - // }); - // } - - async fn open_close_queried_buffer( - input: &str, - expected_matches: usize, - expected_editor_title: &str, - workspace: &View, - cx: &mut gpui::VisualTestContext<'_>, - ) -> Vec { - cx.dispatch_action(Toggle); - let picker = workspace.update(cx, |workspace, cx| { - workspace - .current_modal::(cx) - .unwrap() - .read(cx) - .picker - .clone() - }); - picker - .update(cx, |finder, cx| { - finder.delegate.update_matches(input.to_string(), cx) - }) - .await; - let history_items = picker.update(cx, |finder, _| { - assert_eq!( - finder.delegate.matches.len(), - expected_matches, - "Unexpected number of matches found for query {input}" - ); - finder.delegate.history_items.clone() - }); - - let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - cx.dispatch_action(SelectNext); - cx.dispatch_action(Confirm); - cx.background_executor.run_until_parked(); - active_pane - .condition(cx, |pane, _| pane.active_item().is_some()) - .await; - cx.read(|cx| { - let active_item = active_pane.read(cx).active_item().unwrap(); - let active_editor_title = active_item - .to_any() - .downcast::() - .unwrap() - .read(cx) - .title(cx); - assert_eq!( - expected_editor_title, active_editor_title, - "Unexpected editor title for query {input}" - ); - }); - - close_active_item(workspace, cx).await; - - history_items - } - - async fn close_active_item(workspace: &View, cx: &mut VisualTestContext<'_>) { - let mut original_items = HashMap::new(); - cx.read(|cx| { - for pane in workspace.read(cx).panes() { - let pane_id = pane.entity_id(); - let pane = pane.read(cx); - let insertion_result = original_items.insert(pane_id, pane.items().count()); - assert!(insertion_result.is_none(), "Pane id {pane_id} collision"); - } - }); - - let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); - active_pane - .update(cx, |pane, cx| { - pane.close_active_item(&workspace::CloseActiveItem { save_intent: None }, cx) - .unwrap() - }) - .await - .unwrap(); - cx.background_executor.run_until_parked(); - cx.read(|cx| { - for pane in workspace.read(cx).panes() { - let pane_id = pane.entity_id(); - let pane = pane.read(cx); - match original_items.remove(&pane_id) { - Some(original_items) => { - assert_eq!( - pane.items().count(), - original_items.saturating_sub(1), - "Pane id {pane_id} should have item closed" - ); - } - None => panic!("Pane id {pane_id} not found in original items"), - } - } - }); - assert!( - original_items.len() <= 1, - "At most one panel should got closed" - ); - } - - fn init_test(cx: &mut TestAppContext) -> Arc { - cx.update(|cx| { - let state = AppState::test(cx); - theme::init(cx); - language::init(cx); - super::init(cx); - editor::init(cx); - workspace::init_settings(cx); - Project::init_settings(cx); - state - }) - } - - fn test_path_like(test_str: &str) -> PathLikeWithPosition { - PathLikeWithPosition::parse_str(test_str, |path_like_str| { - Ok::<_, std::convert::Infallible>(FileSearchQuery { - raw_query: test_str.to_owned(), - file_query_end: if path_like_str == test_str { - None - } else { - Some(path_like_str.len()) - }, - }) - }) - .unwrap() - } - - fn dummy_found_path(project_path: ProjectPath) -> FoundPath { - FoundPath { - project: project_path, - absolute: None, - } - } - - fn build_find_picker( - project: Model, - cx: &mut TestAppContext, - ) -> ( - View>, - View, - VisualTestContext, - ) { - let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); - cx.dispatch_action(Toggle); - let picker = workspace.update(&mut cx, |workspace, cx| { - workspace - .current_modal::(cx) - .unwrap() - .read(cx) - .picker - .clone() - }); - (picker, workspace, cx) - } -} +// #[cfg(test)] +// mod tests { +// use std::{assert_eq, collections::HashMap, path::Path, time::Duration}; + +// use super::*; +// use editor::Editor; +// use gpui::{Entity, TestAppContext, VisualTestContext}; +// use menu::{Confirm, SelectNext}; +// use serde_json::json; +// use workspace::{AppState, Workspace}; + +// #[ctor::ctor] +// fn init_logger() { +// if std::env::var("RUST_LOG").is_ok() { +// env_logger::init(); +// } +// } + +// #[gpui::test] +// async fn test_matching_paths(cx: &mut TestAppContext) { +// let app_state = init_test(cx); +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/root", +// json!({ +// "a": { +// "banana": "", +// "bandana": "", +// } +// }), +// ) +// .await; + +// let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + +// let (picker, workspace, mut cx) = build_find_picker(project, cx); +// let cx = &mut cx; + +// picker +// .update(cx, |picker, cx| { +// picker.delegate.update_matches("bna".to_string(), cx) +// }) +// .await; + +// picker.update(cx, |picker, _| { +// assert_eq!(picker.delegate.matches.len(), 2); +// }); + +// let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); +// cx.dispatch_action(SelectNext); +// cx.dispatch_action(Confirm); +// active_pane +// .condition(cx, |pane, _| pane.active_item().is_some()) +// .await; +// cx.read(|cx| { +// let active_item = active_pane.read(cx).active_item().unwrap(); +// assert_eq!( +// active_item +// .to_any() +// .downcast::() +// .unwrap() +// .read(cx) +// .title(cx), +// "bandana" +// ); +// }); +// } + +// #[gpui::test] +// async fn test_row_column_numbers_query_inside_file(cx: &mut TestAppContext) { +// let app_state = init_test(cx); + +// let first_file_name = "first.rs"; +// let first_file_contents = "// First Rust file"; +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/src", +// json!({ +// "test": { +// first_file_name: first_file_contents, +// "second.rs": "// Second Rust file", +// } +// }), +// ) +// .await; + +// let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; + +// let (picker, workspace, mut cx) = build_find_picker(project, cx); +// let cx = &mut cx; + +// let file_query = &first_file_name[..3]; +// let file_row = 1; +// let file_column = 3; +// assert!(file_column <= first_file_contents.len()); +// let query_inside_file = format!("{file_query}:{file_row}:{file_column}"); +// picker +// .update(cx, |finder, cx| { +// finder +// .delegate +// .update_matches(query_inside_file.to_string(), cx) +// }) +// .await; +// picker.update(cx, |finder, _| { +// let finder = &finder.delegate; +// assert_eq!(finder.matches.len(), 1); +// let latest_search_query = finder +// .latest_search_query +// .as_ref() +// .expect("Finder should have a query after the update_matches call"); +// assert_eq!(latest_search_query.path_like.raw_query, query_inside_file); +// assert_eq!( +// latest_search_query.path_like.file_query_end, +// Some(file_query.len()) +// ); +// assert_eq!(latest_search_query.row, Some(file_row)); +// assert_eq!(latest_search_query.column, Some(file_column as u32)); +// }); + +// let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); +// cx.dispatch_action(SelectNext); +// cx.dispatch_action(Confirm); +// active_pane +// .condition(cx, |pane, _| pane.active_item().is_some()) +// .await; +// let editor = cx.update(|cx| { +// let active_item = active_pane.read(cx).active_item().unwrap(); +// active_item.downcast::().unwrap() +// }); +// cx.executor().advance_clock(Duration::from_secs(2)); + +// editor.update(cx, |editor, cx| { +// let all_selections = editor.selections.all_adjusted(cx); +// assert_eq!( +// all_selections.len(), +// 1, +// "Expected to have 1 selection (caret) after file finder confirm, but got: {all_selections:?}" +// ); +// let caret_selection = all_selections.into_iter().next().unwrap(); +// assert_eq!(caret_selection.start, caret_selection.end, +// "Caret selection should have its start and end at the same position"); +// assert_eq!(file_row, caret_selection.start.row + 1, +// "Query inside file should get caret with the same focus row"); +// assert_eq!(file_column, caret_selection.start.column as usize + 1, +// "Query inside file should get caret with the same focus column"); +// }); +// } + +// #[gpui::test] +// async fn test_row_column_numbers_query_outside_file(cx: &mut TestAppContext) { +// let app_state = init_test(cx); + +// let first_file_name = "first.rs"; +// let first_file_contents = "// First Rust file"; +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/src", +// json!({ +// "test": { +// first_file_name: first_file_contents, +// "second.rs": "// Second Rust file", +// } +// }), +// ) +// .await; + +// let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; + +// let (picker, workspace, mut cx) = build_find_picker(project, cx); +// let cx = &mut cx; + +// let file_query = &first_file_name[..3]; +// let file_row = 200; +// let file_column = 300; +// assert!(file_column > first_file_contents.len()); +// let query_outside_file = format!("{file_query}:{file_row}:{file_column}"); +// picker +// .update(cx, |picker, cx| { +// picker +// .delegate +// .update_matches(query_outside_file.to_string(), cx) +// }) +// .await; +// picker.update(cx, |finder, _| { +// let delegate = &finder.delegate; +// assert_eq!(delegate.matches.len(), 1); +// let latest_search_query = delegate +// .latest_search_query +// .as_ref() +// .expect("Finder should have a query after the update_matches call"); +// assert_eq!(latest_search_query.path_like.raw_query, query_outside_file); +// assert_eq!( +// latest_search_query.path_like.file_query_end, +// Some(file_query.len()) +// ); +// assert_eq!(latest_search_query.row, Some(file_row)); +// assert_eq!(latest_search_query.column, Some(file_column as u32)); +// }); + +// let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); +// cx.dispatch_action(SelectNext); +// cx.dispatch_action(Confirm); +// active_pane +// .condition(cx, |pane, _| pane.active_item().is_some()) +// .await; +// let editor = cx.update(|cx| { +// let active_item = active_pane.read(cx).active_item().unwrap(); +// active_item.downcast::().unwrap() +// }); +// cx.executor().advance_clock(Duration::from_secs(2)); + +// editor.update(cx, |editor, cx| { +// let all_selections = editor.selections.all_adjusted(cx); +// assert_eq!( +// all_selections.len(), +// 1, +// "Expected to have 1 selection (caret) after file finder confirm, but got: {all_selections:?}" +// ); +// let caret_selection = all_selections.into_iter().next().unwrap(); +// assert_eq!(caret_selection.start, caret_selection.end, +// "Caret selection should have its start and end at the same position"); +// assert_eq!(0, caret_selection.start.row, +// "Excessive rows (as in query outside file borders) should get trimmed to last file row"); +// assert_eq!(first_file_contents.len(), caret_selection.start.column as usize, +// "Excessive columns (as in query outside file borders) should get trimmed to selected row's last column"); +// }); +// } + +// #[gpui::test] +// async fn test_matching_cancellation(cx: &mut TestAppContext) { +// let app_state = init_test(cx); +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/dir", +// json!({ +// "hello": "", +// "goodbye": "", +// "halogen-light": "", +// "happiness": "", +// "height": "", +// "hi": "", +// "hiccup": "", +// }), +// ) +// .await; + +// let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await; + +// let (picker, _, mut cx) = build_find_picker(project, cx); +// let cx = &mut cx; + +// let query = test_path_like("hi"); +// picker +// .update(cx, |picker, cx| { +// picker.delegate.spawn_search(query.clone(), cx) +// }) +// .await; + +// picker.update(cx, |picker, _cx| { +// assert_eq!(picker.delegate.matches.len(), 5) +// }); + +// picker.update(cx, |picker, cx| { +// let delegate = &mut picker.delegate; +// assert!( +// delegate.matches.history.is_empty(), +// "Search matches expected" +// ); +// let matches = delegate.matches.search.clone(); + +// // Simulate a search being cancelled after the time limit, +// // returning only a subset of the matches that would have been found. +// drop(delegate.spawn_search(query.clone(), cx)); +// delegate.set_search_matches( +// delegate.latest_search_id, +// true, // did-cancel +// query.clone(), +// vec![matches[1].clone(), matches[3].clone()], +// cx, +// ); + +// // Simulate another cancellation. +// drop(delegate.spawn_search(query.clone(), cx)); +// delegate.set_search_matches( +// delegate.latest_search_id, +// true, // did-cancel +// query.clone(), +// vec![matches[0].clone(), matches[2].clone(), matches[3].clone()], +// cx, +// ); + +// assert!( +// delegate.matches.history.is_empty(), +// "Search matches expected" +// ); +// assert_eq!(delegate.matches.search.as_slice(), &matches[0..4]); +// }); +// } + +// #[gpui::test] +// async fn test_ignored_files(cx: &mut TestAppContext) { +// let app_state = init_test(cx); +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/ancestor", +// json!({ +// ".gitignore": "ignored-root", +// "ignored-root": { +// "happiness": "", +// "height": "", +// "hi": "", +// "hiccup": "", +// }, +// "tracked-root": { +// ".gitignore": "height", +// "happiness": "", +// "height": "", +// "hi": "", +// "hiccup": "", +// }, +// }), +// ) +// .await; + +// let project = Project::test( +// app_state.fs.clone(), +// [ +// "/ancestor/tracked-root".as_ref(), +// "/ancestor/ignored-root".as_ref(), +// ], +// cx, +// ) +// .await; + +// let (picker, _, mut cx) = build_find_picker(project, cx); +// let cx = &mut cx; + +// picker +// .update(cx, |picker, cx| { +// picker.delegate.spawn_search(test_path_like("hi"), cx) +// }) +// .await; +// picker.update(cx, |picker, _| assert_eq!(picker.delegate.matches.len(), 7)); +// } + +// #[gpui::test] +// async fn test_single_file_worktrees(cx: &mut TestAppContext) { +// let app_state = init_test(cx); +// app_state +// .fs +// .as_fake() +// .insert_tree("/root", json!({ "the-parent-dir": { "the-file": "" } })) +// .await; + +// let project = Project::test( +// app_state.fs.clone(), +// ["/root/the-parent-dir/the-file".as_ref()], +// cx, +// ) +// .await; + +// let (picker, _, mut cx) = build_find_picker(project, cx); +// let cx = &mut cx; + +// // Even though there is only one worktree, that worktree's filename +// // is included in the matching, because the worktree is a single file. +// picker +// .update(cx, |picker, cx| { +// picker.delegate.spawn_search(test_path_like("thf"), cx) +// }) +// .await; +// cx.read(|cx| { +// let picker = picker.read(cx); +// let delegate = &picker.delegate; +// assert!( +// delegate.matches.history.is_empty(), +// "Search matches expected" +// ); +// let matches = delegate.matches.search.clone(); +// assert_eq!(matches.len(), 1); + +// let (file_name, file_name_positions, full_path, full_path_positions) = +// delegate.labels_for_path_match(&matches[0]); +// assert_eq!(file_name, "the-file"); +// assert_eq!(file_name_positions, &[0, 1, 4]); +// assert_eq!(full_path, "the-file"); +// assert_eq!(full_path_positions, &[0, 1, 4]); +// }); + +// // Since the worktree root is a file, searching for its name followed by a slash does +// // not match anything. +// picker +// .update(cx, |f, cx| { +// f.delegate.spawn_search(test_path_like("thf/"), cx) +// }) +// .await; +// picker.update(cx, |f, _| assert_eq!(f.delegate.matches.len(), 0)); +// } + +// #[gpui::test] +// async fn test_path_distance_ordering(cx: &mut TestAppContext) { +// let app_state = init_test(cx); +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/root", +// json!({ +// "dir1": { "a.txt": "" }, +// "dir2": { +// "a.txt": "", +// "b.txt": "" +// } +// }), +// ) +// .await; + +// let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; +// let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); +// let cx = &mut cx; + +// let worktree_id = cx.read(|cx| { +// let worktrees = workspace.read(cx).worktrees(cx).collect::>(); +// assert_eq!(worktrees.len(), 1); +// WorktreeId::from_usize(worktrees[0].id()) +// }); + +// // When workspace has an active item, sort items which are closer to that item +// // first when they have the same name. In this case, b.txt is closer to dir2's a.txt +// // so that one should be sorted earlier +// let b_path = Some(dummy_found_path(ProjectPath { +// worktree_id, +// path: Arc::from(Path::new("/root/dir2/b.txt")), +// })); +// cx.dispatch_action(Toggle); + +// let finder = cx +// .add_window(|cx| { +// Picker::new( +// FileFinderDelegate::new( +// workspace.downgrade(), +// workspace.read(cx).project().clone(), +// b_path, +// Vec::new(), +// cx, +// ), +// cx, +// ) +// }) +// .root(cx); + +// finder +// .update(cx, |f, cx| { +// f.delegate.spawn_search(test_path_like("a.txt"), cx) +// }) +// .await; + +// finder.read_with(cx, |f, _| { +// let delegate = &f.delegate; +// assert!( +// delegate.matches.history.is_empty(), +// "Search matches expected" +// ); +// let matches = delegate.matches.search.clone(); +// assert_eq!(matches[0].path.as_ref(), Path::new("dir2/a.txt")); +// assert_eq!(matches[1].path.as_ref(), Path::new("dir1/a.txt")); +// }); +// } + +// #[gpui::test] +// async fn test_search_worktree_without_files(cx: &mut TestAppContext) { +// let app_state = init_test(cx); +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/root", +// json!({ +// "dir1": {}, +// "dir2": { +// "dir3": {} +// } +// }), +// ) +// .await; + +// let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; +// let workspace = cx +// .add_window(|cx| Workspace::test_new(project, cx)) +// .root(cx); +// let finder = cx +// .add_window(|cx| { +// Picker::new( +// FileFinderDelegate::new( +// workspace.downgrade(), +// workspace.read(cx).project().clone(), +// None, +// Vec::new(), +// cx, +// ), +// cx, +// ) +// }) +// .root(cx); +// finder +// .update(cx, |f, cx| { +// f.delegate.spawn_search(test_path_like("dir"), cx) +// }) +// .await; +// cx.read(|cx| { +// let finder = finder.read(cx); +// assert_eq!(finder.delegate.matches.len(), 0); +// }); +// } + +// #[gpui::test] +// async fn test_query_history(cx: &mut gpui::TestAppContext) { +// let app_state = init_test(cx); + +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/src", +// json!({ +// "test": { +// "first.rs": "// First Rust file", +// "second.rs": "// Second Rust file", +// "third.rs": "// Third Rust file", +// } +// }), +// ) +// .await; + +// let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; +// let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); +// let cx = &mut cx; +// let worktree_id = cx.read(|cx| { +// let worktrees = workspace.read(cx).worktrees(cx).collect::>(); +// assert_eq!(worktrees.len(), 1); +// WorktreeId::from_usize(worktrees[0].id()) +// }); + +// // Open and close panels, getting their history items afterwards. +// // Ensure history items get populated with opened items, and items are kept in a certain order. +// // The history lags one opened buffer behind, since it's updated in the search panel only on its reopen. +// // +// // TODO: without closing, the opened items do not propagate their history changes for some reason +// // it does work in real app though, only tests do not propagate. + +// let initial_history = open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; +// assert!( +// initial_history.is_empty(), +// "Should have no history before opening any files" +// ); + +// let history_after_first = +// open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; +// assert_eq!( +// history_after_first, +// vec![FoundPath::new( +// ProjectPath { +// worktree_id, +// path: Arc::from(Path::new("test/first.rs")), +// }, +// Some(PathBuf::from("/src/test/first.rs")) +// )], +// "Should show 1st opened item in the history when opening the 2nd item" +// ); + +// let history_after_second = +// open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await; +// assert_eq!( +// history_after_second, +// vec![ +// FoundPath::new( +// ProjectPath { +// worktree_id, +// path: Arc::from(Path::new("test/second.rs")), +// }, +// Some(PathBuf::from("/src/test/second.rs")) +// ), +// FoundPath::new( +// ProjectPath { +// worktree_id, +// path: Arc::from(Path::new("test/first.rs")), +// }, +// Some(PathBuf::from("/src/test/first.rs")) +// ), +// ], +// "Should show 1st and 2nd opened items in the history when opening the 3rd item. \ +// 2nd item should be the first in the history, as the last opened." +// ); + +// let history_after_third = +// open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; +// assert_eq!( +// history_after_third, +// vec![ +// FoundPath::new( +// ProjectPath { +// worktree_id, +// path: Arc::from(Path::new("test/third.rs")), +// }, +// Some(PathBuf::from("/src/test/third.rs")) +// ), +// FoundPath::new( +// ProjectPath { +// worktree_id, +// path: Arc::from(Path::new("test/second.rs")), +// }, +// Some(PathBuf::from("/src/test/second.rs")) +// ), +// FoundPath::new( +// ProjectPath { +// worktree_id, +// path: Arc::from(Path::new("test/first.rs")), +// }, +// Some(PathBuf::from("/src/test/first.rs")) +// ), +// ], +// "Should show 1st, 2nd and 3rd opened items in the history when opening the 2nd item again. \ +// 3rd item should be the first in the history, as the last opened." +// ); + +// let history_after_second_again = +// open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await; +// assert_eq!( +// history_after_second_again, +// vec![ +// FoundPath::new( +// ProjectPath { +// worktree_id, +// path: Arc::from(Path::new("test/second.rs")), +// }, +// Some(PathBuf::from("/src/test/second.rs")) +// ), +// FoundPath::new( +// ProjectPath { +// worktree_id, +// path: Arc::from(Path::new("test/third.rs")), +// }, +// Some(PathBuf::from("/src/test/third.rs")) +// ), +// FoundPath::new( +// ProjectPath { +// worktree_id, +// path: Arc::from(Path::new("test/first.rs")), +// }, +// Some(PathBuf::from("/src/test/first.rs")) +// ), +// ], +// "Should show 1st, 2nd and 3rd opened items in the history when opening the 3rd item again. \ +// 2nd item, as the last opened, 3rd item should go next as it was opened right before." +// ); +// } + +// #[gpui::test] +// async fn test_external_files_history(cx: &mut gpui::TestAppContext) { +// let app_state = init_test(cx); + +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/src", +// json!({ +// "test": { +// "first.rs": "// First Rust file", +// "second.rs": "// Second Rust file", +// } +// }), +// ) +// .await; + +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/external-src", +// json!({ +// "test": { +// "third.rs": "// Third Rust file", +// "fourth.rs": "// Fourth Rust file", +// } +// }), +// ) +// .await; + +// let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; +// cx.update(|cx| { +// project.update(cx, |project, cx| { +// project.find_or_create_local_worktree("/external-src", false, cx) +// }) +// }) +// .detach(); +// cx.background_executor.run_until_parked(); + +// let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); +// let cx = &mut cx; +// let worktree_id = cx.read(|cx| { +// let worktrees = workspace.read(cx).worktrees(cx).collect::>(); +// assert_eq!(worktrees.len(), 1,); + +// WorktreeId::from_usize(worktrees[0].id()) +// }); +// workspace +// .update(cx, |workspace, cx| { +// workspace.open_abs_path(PathBuf::from("/external-src/test/third.rs"), false, cx) +// }) +// .detach(); +// cx.background_executor.run_until_parked(); +// let external_worktree_id = cx.read(|cx| { +// let worktrees = workspace.read(cx).worktrees(cx).collect::>(); +// assert_eq!( +// worktrees.len(), +// 2, +// "External file should get opened in a new worktree" +// ); + +// WorktreeId::from_usize( +// worktrees +// .into_iter() +// .find(|worktree| worktree.entity_id() != worktree_id.to_usize()) +// .expect("New worktree should have a different id") +// .id(), +// ) +// }); +// close_active_item(&workspace, cx).await; + +// let initial_history_items = +// open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; +// assert_eq!( +// initial_history_items, +// vec![FoundPath::new( +// ProjectPath { +// worktree_id: external_worktree_id, +// path: Arc::from(Path::new("")), +// }, +// Some(PathBuf::from("/external-src/test/third.rs")) +// )], +// "Should show external file with its full path in the history after it was open" +// ); + +// let updated_history_items = +// open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; +// assert_eq!( +// updated_history_items, +// vec![ +// FoundPath::new( +// ProjectPath { +// worktree_id, +// path: Arc::from(Path::new("test/second.rs")), +// }, +// Some(PathBuf::from("/src/test/second.rs")) +// ), +// FoundPath::new( +// ProjectPath { +// worktree_id: external_worktree_id, +// path: Arc::from(Path::new("")), +// }, +// Some(PathBuf::from("/external-src/test/third.rs")) +// ), +// ], +// "Should keep external file with history updates", +// ); +// } + +// #[gpui::test] +// async fn test_toggle_panel_new_selections(cx: &mut gpui::TestAppContext) { +// let app_state = init_test(cx); + +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/src", +// json!({ +// "test": { +// "first.rs": "// First Rust file", +// "second.rs": "// Second Rust file", +// "third.rs": "// Third Rust file", +// } +// }), +// ) +// .await; + +// let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; +// let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); +// let cx = &mut cx; + +// // generate some history to select from +// open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; +// cx.executor().run_until_parked(); +// open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; +// open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await; +// let current_history = open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; + +// for expected_selected_index in 0..current_history.len() { +// cx.dispatch_action(Toggle); +// let selected_index = workspace.update(cx, |workspace, cx| { +// workspace +// .current_modal::(cx) +// .unwrap() +// .read(cx) +// .picker +// .read(cx) +// .delegate +// .selected_index() +// }); +// assert_eq!( +// selected_index, expected_selected_index, +// "Should select the next item in the history" +// ); +// } + +// cx.dispatch_action(Toggle); +// let selected_index = workspace.update(cx, |workspace, cx| { +// workspace +// .current_modal::(cx) +// .unwrap() +// .read(cx) +// .picker +// .read(cx) +// .delegate +// .selected_index() +// }); +// assert_eq!( +// selected_index, 0, +// "Should wrap around the history and start all over" +// ); +// } + +// #[gpui::test] +// async fn test_search_preserves_history_items(cx: &mut gpui::TestAppContext) { +// let app_state = init_test(cx); + +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/src", +// json!({ +// "test": { +// "first.rs": "// First Rust file", +// "second.rs": "// Second Rust file", +// "third.rs": "// Third Rust file", +// "fourth.rs": "// Fourth Rust file", +// } +// }), +// ) +// .await; + +// let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; +// let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); +// let cx = &mut cx; +// let worktree_id = cx.read(|cx| { +// let worktrees = workspace.read(cx).worktrees(cx).collect::>(); +// assert_eq!(worktrees.len(), 1,); + +// WorktreeId::from_usize(worktrees[0].entity_id()) +// }); + +// // generate some history to select from +// open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; +// open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; +// open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await; +// open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; + +// cx.dispatch_action(Toggle); +// let first_query = "f"; +// let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); +// finder +// .update(cx, |finder, cx| { +// finder.delegate.update_matches(first_query.to_string(), cx) +// }) +// .await; +// finder.read_with(cx, |finder, _| { +// let delegate = &finder.delegate; +// assert_eq!(delegate.matches.history.len(), 1, "Only one history item contains {first_query}, it should be present and others should be filtered out"); +// let history_match = delegate.matches.history.first().unwrap(); +// assert!(history_match.1.is_some(), "Should have path matches for history items after querying"); +// assert_eq!(history_match.0, FoundPath::new( +// ProjectPath { +// worktree_id, +// path: Arc::from(Path::new("test/first.rs")), +// }, +// Some(PathBuf::from("/src/test/first.rs")) +// )); +// assert_eq!(delegate.matches.search.len(), 1, "Only one non-history item contains {first_query}, it should be present"); +// assert_eq!(delegate.matches.search.first().unwrap().path.as_ref(), Path::new("test/fourth.rs")); +// }); + +// let second_query = "fsdasdsa"; +// let finder = workspace.update(cx, |workspace, cx| { +// workspace +// .current_modal::(cx) +// .unwrap() +// .read(cx) +// .picker +// }); +// finder +// .update(cx, |finder, cx| { +// finder.delegate.update_matches(second_query.to_string(), cx) +// }) +// .await; +// finder.update(cx, |finder, _| { +// let delegate = &finder.delegate; +// assert!( +// delegate.matches.history.is_empty(), +// "No history entries should match {second_query}" +// ); +// assert!( +// delegate.matches.search.is_empty(), +// "No search entries should match {second_query}" +// ); +// }); + +// let first_query_again = first_query; + +// let finder = workspace.update(cx, |workspace, cx| { +// workspace +// .current_modal::(cx) +// .unwrap() +// .read(cx) +// .picker +// }); +// finder +// .update(cx, |finder, cx| { +// finder +// .delegate +// .update_matches(first_query_again.to_string(), cx) +// }) +// .await; +// finder.read_with(cx, |finder, _| { +// let delegate = &finder.delegate; +// assert_eq!(delegate.matches.history.len(), 1, "Only one history item contains {first_query_again}, it should be present and others should be filtered out, even after non-matching query"); +// let history_match = delegate.matches.history.first().unwrap(); +// assert!(history_match.1.is_some(), "Should have path matches for history items after querying"); +// assert_eq!(history_match.0, FoundPath::new( +// ProjectPath { +// worktree_id, +// path: Arc::from(Path::new("test/first.rs")), +// }, +// Some(PathBuf::from("/src/test/first.rs")) +// )); +// assert_eq!(delegate.matches.search.len(), 1, "Only one non-history item contains {first_query_again}, it should be present, even after non-matching query"); +// assert_eq!(delegate.matches.search.first().unwrap().path.as_ref(), Path::new("test/fourth.rs")); +// }); +// } + +// #[gpui::test] +// async fn test_history_items_vs_very_good_external_match(cx: &mut gpui::TestAppContext) { +// let app_state = init_test(cx); + +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/src", +// json!({ +// "collab_ui": { +// "first.rs": "// First Rust file", +// "second.rs": "// Second Rust file", +// "third.rs": "// Third Rust file", +// "collab_ui.rs": "// Fourth Rust file", +// } +// }), +// ) +// .await; + +// let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; +// let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); +// let cx = &mut cx; +// // generate some history to select from +// open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; +// open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; +// open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await; +// open_close_queried_buffer("sec", 1, "second.rs", &workspace, cx).await; + +// cx.dispatch_action(Toggle); +// let query = "collab_ui"; +// let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); +// finder +// .update(cx, |finder, cx| { +// finder.delegate.update_matches(query.to_string(), cx) +// }) +// .await; +// finder.read_with(cx, |finder, _| { +// let delegate = &finder.delegate; +// assert!( +// delegate.matches.history.is_empty(), +// "History items should not math query {query}, they should be matched by name only" +// ); + +// let search_entries = delegate +// .matches +// .search +// .iter() +// .map(|path_match| path_match.path.to_path_buf()) +// .collect::>(); +// assert_eq!( +// search_entries, +// vec![ +// PathBuf::from("collab_ui/collab_ui.rs"), +// PathBuf::from("collab_ui/third.rs"), +// PathBuf::from("collab_ui/first.rs"), +// PathBuf::from("collab_ui/second.rs"), +// ], +// "Despite all search results having the same directory name, the most matching one should be on top" +// ); +// }); +// } + +// #[gpui::test] +// async fn test_nonexistent_history_items_not_shown(cx: &mut gpui::TestAppContext) { +// let app_state = init_test(cx); + +// app_state +// .fs +// .as_fake() +// .insert_tree( +// "/src", +// json!({ +// "test": { +// "first.rs": "// First Rust file", +// "nonexistent.rs": "// Second Rust file", +// "third.rs": "// Third Rust file", +// } +// }), +// ) +// .await; + +// let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; +// let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); +// let cx = &mut cx; +// // generate some history to select from +// open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; +// open_close_queried_buffer("non", 1, "nonexistent.rs", &workspace, cx).await; +// open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await; +// open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; + +// cx.dispatch_action(Toggle); +// let query = "rs"; +// let finder = cx.read(|cx| workspace.read(cx).current_modal::().unwrap()); +// finder +// .update(cx, |finder, cx| { +// finder.picker.update(cx, |picker, cx| { +// picker.delegate.update_matches(query.to_string(), cx) +// }) +// }) +// .await; +// finder.update(cx, |finder, _| { +// let history_entries = finder.delegate +// .matches +// .history +// .iter() +// .map(|(_, path_match)| path_match.as_ref().expect("should have a path match").path.to_path_buf()) +// .collect::>(); +// assert_eq!( +// history_entries, +// vec![ +// PathBuf::from("test/first.rs"), +// PathBuf::from("test/third.rs"), +// ], +// "Should have all opened files in the history, except the ones that do not exist on disk" +// ); +// }); +// } + +// async fn open_close_queried_buffer( +// input: &str, +// expected_matches: usize, +// expected_editor_title: &str, +// workspace: &View, +// cx: &mut gpui::VisualTestContext<'_>, +// ) -> Vec { +// cx.dispatch_action(Toggle); +// let picker = workspace.update(cx, |workspace, cx| { +// workspace +// .current_modal::(cx) +// .unwrap() +// .read(cx) +// .picker +// .clone() +// }); +// picker +// .update(cx, |finder, cx| { +// finder.delegate.update_matches(input.to_string(), cx) +// }) +// .await; +// let history_items = picker.update(cx, |finder, _| { +// assert_eq!( +// finder.delegate.matches.len(), +// expected_matches, +// "Unexpected number of matches found for query {input}" +// ); +// finder.delegate.history_items.clone() +// }); + +// let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); +// cx.dispatch_action(SelectNext); +// cx.dispatch_action(Confirm); +// cx.background_executor.run_until_parked(); +// active_pane +// .condition(cx, |pane, _| pane.active_item().is_some()) +// .await; +// cx.read(|cx| { +// let active_item = active_pane.read(cx).active_item().unwrap(); +// let active_editor_title = active_item +// .to_any() +// .downcast::() +// .unwrap() +// .read(cx) +// .title(cx); +// assert_eq!( +// expected_editor_title, active_editor_title, +// "Unexpected editor title for query {input}" +// ); +// }); + +// close_active_item(workspace, cx).await; + +// history_items +// } + +// async fn close_active_item(workspace: &View, cx: &mut VisualTestContext<'_>) { +// let mut original_items = HashMap::new(); +// cx.read(|cx| { +// for pane in workspace.read(cx).panes() { +// let pane_id = pane.entity_id(); +// let pane = pane.read(cx); +// let insertion_result = original_items.insert(pane_id, pane.items().count()); +// assert!(insertion_result.is_none(), "Pane id {pane_id} collision"); +// } +// }); + +// let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); +// active_pane +// .update(cx, |pane, cx| { +// pane.close_active_item(&workspace::CloseActiveItem { save_intent: None }, cx) +// .unwrap() +// }) +// .await +// .unwrap(); +// cx.background_executor.run_until_parked(); +// cx.read(|cx| { +// for pane in workspace.read(cx).panes() { +// let pane_id = pane.entity_id(); +// let pane = pane.read(cx); +// match original_items.remove(&pane_id) { +// Some(original_items) => { +// assert_eq!( +// pane.items().count(), +// original_items.saturating_sub(1), +// "Pane id {pane_id} should have item closed" +// ); +// } +// None => panic!("Pane id {pane_id} not found in original items"), +// } +// } +// }); +// assert!( +// original_items.len() <= 1, +// "At most one panel should got closed" +// ); +// } + +// fn init_test(cx: &mut TestAppContext) -> Arc { +// cx.update(|cx| { +// let state = AppState::test(cx); +// theme::init(cx); +// language::init(cx); +// super::init(cx); +// editor::init(cx); +// workspace::init_settings(cx); +// Project::init_settings(cx); +// state +// }) +// } + +// fn test_path_like(test_str: &str) -> PathLikeWithPosition { +// PathLikeWithPosition::parse_str(test_str, |path_like_str| { +// Ok::<_, std::convert::Infallible>(FileSearchQuery { +// raw_query: test_str.to_owned(), +// file_query_end: if path_like_str == test_str { +// None +// } else { +// Some(path_like_str.len()) +// }, +// }) +// }) +// .unwrap() +// } + +// fn dummy_found_path(project_path: ProjectPath) -> FoundPath { +// FoundPath { +// project: project_path, +// absolute: None, +// } +// } + +// fn build_find_picker( +// project: Model, +// cx: &mut TestAppContext, +// ) -> ( +// View>, +// View, +// VisualTestContext, +// ) { +// let (workspace, mut cx) = cx.add_window_view(|cx| Workspace::test_new(project, cx)); +// cx.dispatch_action(Toggle); +// let picker = workspace.update(&mut cx, |workspace, cx| { +// workspace +// .current_modal::(cx) +// .unwrap() +// .read(cx) +// .picker +// .clone() +// }); +// (picker, workspace, cx) +// } +// } diff --git a/crates/gpui2/src/elements/uniform_list.rs b/crates/gpui2/src/elements/uniform_list.rs index 6687559d1c811349a3f18a5585f2d2d4110eb16e..c81ff5f26a55111af5bef6db02135eb251719dc9 100644 --- a/crates/gpui2/src/elements/uniform_list.rs +++ b/crates/gpui2/src/elements/uniform_list.rs @@ -15,7 +15,7 @@ use taffy::style::Overflow; pub fn uniform_list( id: Id, item_count: usize, - f: impl 'static + Fn(&mut V, Range, &mut ViewContext) -> SmallVec<[C; 64]>, + f: impl 'static + Fn(&mut V, Range, &mut ViewContext) -> Vec, ) -> UniformList where Id: Into, diff --git a/crates/project_panel2/src/project_panel.rs b/crates/project_panel2/src/project_panel.rs index 1feead1a19e1b08d9ae5b0cca15095f802e997f5..b39d62c9a1527629306316c26e926fb30c99e111 100644 --- a/crates/project_panel2/src/project_panel.rs +++ b/crates/project_panel2/src/project_panel.rs @@ -21,7 +21,6 @@ use project::{ }; use project_panel_settings::{ProjectPanelDockPosition, ProjectPanelSettings}; use serde::{Deserialize, Serialize}; -use smallvec::SmallVec; use std::{ cmp::Ordering, collections::{hash_map, HashMap}, @@ -1468,7 +1467,7 @@ impl Render for ProjectPanel { .map(|(_, worktree_entries)| worktree_entries.len()) .sum(), |this: &mut Self, range, cx| { - let mut items = SmallVec::new(); + let mut items = Vec::new(); this.for_each_visible_entry(range, cx, |id, details, cx| { items.push(this.render_entry(id, details, cx)); }); diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 4786e7e35de96f2f7e7af2c96fface1c7278faa7..0101b60f8805e3ffeadf7404fad764568b07d1b3 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -38,10 +38,10 @@ use futures::{ use gpui::{ actions, div, point, rems, size, Action, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Component, Div, Entity, EntityId, EventEmitter, - FocusHandle, FocusableKeyDispatch, GlobalPixels, KeyContext, Model, ModelContext, - ParentElement, Point, Render, Size, StatefulInteractive, StatefulInteractivity, - StatelessInteractive, StatelessInteractivity, Styled, Subscription, Task, View, ViewContext, - VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions, + FocusHandle, GlobalPixels, KeyContext, Model, ModelContext, ParentElement, Point, Render, Size, + StatefulInteractive, StatelessInteractive, StatelessInteractivity, Styled, Subscription, Task, + View, ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, + WindowOptions, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem}; use itertools::Itertools; @@ -3037,10 +3037,10 @@ impl Workspace { fn force_remove_pane(&mut self, pane: &View, cx: &mut ViewContext) { self.panes.retain(|p| p != pane); - if true { - todo!() - // cx.focus(self.panes.last().unwrap()); - } + self.panes + .last() + .unwrap() + .update(cx, |pane, cx| pane.focus(cx)); if self.last_active_center_pane == Some(pane.downgrade()) { self.last_active_center_pane = None; } @@ -3429,8 +3429,7 @@ impl Workspace { node_runtime: FakeNodeRuntime::new(), }); let workspace = Self::new(0, project, app_state, cx); - dbg!(&workspace.focus_handle); - workspace.focus_handle.focus(cx); + workspace.active_pane.update(cx, |pane, cx| pane.focus(cx)); workspace } @@ -3709,7 +3708,7 @@ fn notify_if_database_failed(workspace: WindowHandle, cx: &mut AsyncA impl EventEmitter for Workspace {} impl Render for Workspace { - type Element = Div, FocusableKeyDispatch>; + type Element = Div; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { let mut context = KeyContext::default(); @@ -3717,7 +3716,6 @@ impl Render for Workspace { let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone(); self.add_workspace_actions_listeners(div()) - .track_focus(&self.focus_handle) .context(context) .relative() .size_full()