diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index 11588212ef76e3891c57fc5a14778fd8e48a9e37..e5b4125447668fb1246821f2b4d5d78c8a6d438f 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -177,7 +177,7 @@ pub struct Pane { toolbar: View, new_item_menu: Option>, split_item_menu: Option>, - // tab_context_menu: ViewHandle, + // tab_context_menu: View, workspace: WeakView, project: Model, drag_split_direction: Option, @@ -256,11 +256,11 @@ struct DraggedTab { // struct TabBarContextMenu { // kind: TabBarContextMenuKind, -// handle: ViewHandle, +// handle: View, // } // impl TabBarContextMenu { -// fn handle_if_kind(&self, kind: TabBarContextMenuKind) -> Option> { +// fn handle_if_kind(&self, kind: TabBarContextMenuKind) -> Option> { // if self.kind == kind { // return Some(self.handle.clone()); // } @@ -329,7 +329,7 @@ impl Pane { ) -> Self { // todo!("context menu") // let pane_view_id = cx.view_id(); - // let context_menu = cx.add_view(|cx| ContextMenu::new(pane_view_id, cx)); + // let context_menu = cx.build_view(|cx| ContextMenu::new(pane_view_id, cx)); // context_menu.update(cx, |menu, _| { // menu.set_position_mode(OverlayPositionMode::Local) // }); @@ -368,7 +368,7 @@ impl Pane { // kind: TabBarContextMenuKind::New, // handle: context_menu, // }, - // tab_context_menu: cx.add_view(|cx| ContextMenu::new(pane_view_id, cx)), + // tab_context_menu: cx.build_view(|_| ContextMenu::new(pane_view_id, cx)), workspace, project, // can_drop: Rc::new(|_, _| true), @@ -1968,52 +1968,6 @@ impl Render for Pane { }), ) } - - // fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext) { - // if !self.has_focus { - // self.has_focus = true; - // cx.emit(Event::Focus); - // cx.notify(); - // } - - // self.toolbar.update(cx, |toolbar, cx| { - // toolbar.focus_changed(true, cx); - // }); - - // if let Some(active_item) = self.active_item() { - // if cx.is_self_focused() { - // // Pane was focused directly. We need to either focus a view inside the active item, - // // or focus the active item itself - // if let Some(weak_last_focused_view) = - // self.last_focused_view_by_item.get(&active_item.id()) - // { - // if let Some(last_focused_view) = weak_last_focused_view.upgrade(cx) { - // cx.focus(&last_focused_view); - // return; - // } else { - // self.last_focused_view_by_item.remove(&active_item.id()); - // } - // } - - // cx.focus(active_item.as_any()); - // } else if focused != self.tab_bar_context_menu.handle { - // self.last_focused_view_by_item - // .insert(active_item.id(), focused.downgrade()); - // } - // } - // } - - // fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - // self.has_focus = false; - // self.toolbar.update(cx, |toolbar, cx| { - // toolbar.focus_changed(false, cx); - // }); - // cx.notify(); - // } - - // fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) { - // Self::reset_to_default_keymap_context(keymap); - // } } impl ItemNavHistory { @@ -2171,98 +2125,6 @@ impl NavHistoryState { } } -// pub struct PaneBackdrop { -// child_view: usize, -// child: AnyElement, -// } - -// impl PaneBackdrop { -// pub fn new(pane_item_view: usize, child: AnyElement) -> Self { -// PaneBackdrop { -// child, -// child_view: pane_item_view, -// } -// } -// } - -// impl Element for PaneBackdrop { -// type LayoutState = (); - -// type PaintState = (); - -// fn layout( -// &mut self, -// constraint: gpui::SizeConstraint, -// view: &mut V, -// cx: &mut ViewContext, -// ) -> (Vector2F, Self::LayoutState) { -// let size = self.child.layout(constraint, view, cx); -// (size, ()) -// } - -// fn paint( -// &mut self, -// bounds: RectF, -// visible_bounds: RectF, -// _: &mut Self::LayoutState, -// view: &mut V, -// cx: &mut ViewContext, -// ) -> Self::PaintState { -// let background = theme::current(cx).editor.background; - -// let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); - -// cx.scene().push_quad(gpui::Quad { -// bounds: RectF::new(bounds.origin(), bounds.size()), -// background: Some(background), -// ..Default::default() -// }); - -// let child_view_id = self.child_view; -// cx.scene().push_mouse_region( -// MouseRegion::new::(child_view_id, 0, visible_bounds).on_down( -// gpui::platform::MouseButton::Left, -// move |_, _: &mut V, cx| { -// let window = cx.window(); -// cx.app_context().focus(window, Some(child_view_id)) -// }, -// ), -// ); - -// cx.scene().push_layer(Some(bounds)); -// self.child.paint(bounds.origin(), visible_bounds, view, cx); -// cx.scene().pop_layer(); -// } - -// fn rect_for_text_range( -// &self, -// range_utf16: std::ops::Range, -// _bounds: RectF, -// _visible_bounds: RectF, -// _layout: &Self::LayoutState, -// _paint: &Self::PaintState, -// view: &V, -// cx: &gpui::ViewContext, -// ) -> Option { -// self.child.rect_for_text_range(range_utf16, view, cx) -// } - -// fn debug( -// &self, -// _bounds: RectF, -// _layout: &Self::LayoutState, -// _paint: &Self::PaintState, -// view: &V, -// cx: &gpui::ViewContext, -// ) -> serde_json::Value { -// gpui::json::json!({ -// "type": "Pane Back Drop", -// "view": self.child_view, -// "child": self.child.debug(view, cx), -// }) -// } -// } - fn dirty_message_for(buffer_path: Option) -> String { let path = buffer_path .as_ref() @@ -2272,528 +2134,549 @@ fn dirty_message_for(buffer_path: Option) -> String { format!("{path} contains unsaved edits. Do you want to save it?") } -// todo!("uncomment tests") -// #[cfg(test)] -// mod tests { -// use super::*; -// use crate::item::test::{TestItem, TestProjectItem}; -// use gpui::TestAppContext; -// use project::FakeFs; -// use settings::SettingsStore; - -// #[gpui::test] -// async fn test_remove_active_empty(cx: &mut TestAppContext) { -// init_test(cx); -// let fs = FakeFs::new(cx.background()); - -// let project = Project::test(fs, None, cx).await; -// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); -// let workspace = window.root(cx); -// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); - -// pane.update(cx, |pane, cx| { -// assert!(pane -// .close_active_item(&CloseActiveItem { save_intent: None }, cx) -// .is_none()) -// }); -// } +#[cfg(test)] +mod tests { + use super::*; + use crate::item::test::{TestItem, TestProjectItem}; + use gpui::{TestAppContext, VisualTestContext}; + use project::FakeFs; + use settings::SettingsStore; + use theme::LoadThemes; + + #[gpui::test] + async fn test_remove_active_empty(cx: &mut TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.executor()); + + let project = Project::test(fs, None, cx).await; + let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx)); + let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone()); + + pane.update(cx, |pane, cx| { + assert!(pane + .close_active_item(&CloseActiveItem { save_intent: None }, cx) + .is_none()) + }); + } -// #[gpui::test] -// async fn test_add_item_with_new_item(cx: &mut TestAppContext) { -// cx.foreground().forbid_parking(); -// init_test(cx); -// let fs = FakeFs::new(cx.background()); - -// let project = Project::test(fs, None, cx).await; -// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); -// let workspace = window.root(cx); -// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); - -// // 1. Add with a destination index -// // a. Add before the active item -// set_labeled_items(&pane, ["A", "B*", "C"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item( -// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))), -// false, -// false, -// Some(0), -// cx, -// ); -// }); -// assert_item_labels(&pane, ["D*", "A", "B", "C"], cx); - -// // b. Add after the active item -// set_labeled_items(&pane, ["A", "B*", "C"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item( -// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))), -// false, -// false, -// Some(2), -// cx, -// ); -// }); -// assert_item_labels(&pane, ["A", "B", "D*", "C"], cx); - -// // c. Add at the end of the item list (including off the length) -// set_labeled_items(&pane, ["A", "B*", "C"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item( -// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))), -// false, -// false, -// Some(5), -// cx, -// ); -// }); -// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); - -// // 2. Add without a destination index -// // a. Add with active item at the start of the item list -// set_labeled_items(&pane, ["A*", "B", "C"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item( -// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))), -// false, -// false, -// None, -// cx, -// ); -// }); -// set_labeled_items(&pane, ["A", "D*", "B", "C"], cx); - -// // b. Add with active item at the end of the item list -// set_labeled_items(&pane, ["A", "B", "C*"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item( -// Box::new(cx.add_view(|_| TestItem::new().with_label("D"))), -// false, -// false, -// None, -// cx, -// ); -// }); -// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); -// } + #[gpui::test] + async fn test_add_item_with_new_item(cx: &mut TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.executor()); + + let project = Project::test(fs, None, cx).await; + let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx)); + let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone()); + + // 1. Add with a destination index + // a. Add before the active item + set_labeled_items(&pane, ["A", "B*", "C"], cx); + pane.update(cx, |pane, cx| { + pane.add_item( + Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))), + false, + false, + Some(0), + cx, + ); + }); + assert_item_labels(&pane, ["D*", "A", "B", "C"], cx); + + // b. Add after the active item + set_labeled_items(&pane, ["A", "B*", "C"], cx); + pane.update(cx, |pane, cx| { + pane.add_item( + Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))), + false, + false, + Some(2), + cx, + ); + }); + assert_item_labels(&pane, ["A", "B", "D*", "C"], cx); + + // c. Add at the end of the item list (including off the length) + set_labeled_items(&pane, ["A", "B*", "C"], cx); + pane.update(cx, |pane, cx| { + pane.add_item( + Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))), + false, + false, + Some(5), + cx, + ); + }); + assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); + + // 2. Add without a destination index + // a. Add with active item at the start of the item list + set_labeled_items(&pane, ["A*", "B", "C"], cx); + pane.update(cx, |pane, cx| { + pane.add_item( + Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))), + false, + false, + None, + cx, + ); + }); + set_labeled_items(&pane, ["A", "D*", "B", "C"], cx); + + // b. Add with active item at the end of the item list + set_labeled_items(&pane, ["A", "B", "C*"], cx); + pane.update(cx, |pane, cx| { + pane.add_item( + Box::new(cx.build_view(|cx| TestItem::new(cx).with_label("D"))), + false, + false, + None, + cx, + ); + }); + assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); + } -// #[gpui::test] -// async fn test_add_item_with_existing_item(cx: &mut TestAppContext) { -// cx.foreground().forbid_parking(); -// init_test(cx); -// let fs = FakeFs::new(cx.background()); - -// let project = Project::test(fs, None, cx).await; -// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); -// let workspace = window.root(cx); -// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); - -// // 1. Add with a destination index -// // 1a. Add before the active item -// let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item(d, false, false, Some(0), cx); -// }); -// assert_item_labels(&pane, ["D*", "A", "B", "C"], cx); - -// // 1b. Add after the active item -// let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item(d, false, false, Some(2), cx); -// }); -// assert_item_labels(&pane, ["A", "B", "D*", "C"], cx); - -// // 1c. Add at the end of the item list (including off the length) -// let [a, _, _, _] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item(a, false, false, Some(5), cx); -// }); -// assert_item_labels(&pane, ["B", "C", "D", "A*"], cx); - -// // 1d. Add same item to active index -// let [_, b, _] = set_labeled_items(&pane, ["A", "B*", "C"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item(b, false, false, Some(1), cx); -// }); -// assert_item_labels(&pane, ["A", "B*", "C"], cx); - -// // 1e. Add item to index after same item in last position -// let [_, _, c] = set_labeled_items(&pane, ["A", "B*", "C"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item(c, false, false, Some(2), cx); -// }); -// assert_item_labels(&pane, ["A", "B", "C*"], cx); - -// // 2. Add without a destination index -// // 2a. Add with active item at the start of the item list -// let [_, _, _, d] = set_labeled_items(&pane, ["A*", "B", "C", "D"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item(d, false, false, None, cx); -// }); -// assert_item_labels(&pane, ["A", "D*", "B", "C"], cx); - -// // 2b. Add with active item at the end of the item list -// let [a, _, _, _] = set_labeled_items(&pane, ["A", "B", "C", "D*"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item(a, false, false, None, cx); -// }); -// assert_item_labels(&pane, ["B", "C", "D", "A*"], cx); - -// // 2c. Add active item to active item at end of list -// let [_, _, c] = set_labeled_items(&pane, ["A", "B", "C*"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item(c, false, false, None, cx); -// }); -// assert_item_labels(&pane, ["A", "B", "C*"], cx); - -// // 2d. Add active item to active item at start of list -// let [a, _, _] = set_labeled_items(&pane, ["A*", "B", "C"], cx); -// pane.update(cx, |pane, cx| { -// pane.add_item(a, false, false, None, cx); -// }); -// assert_item_labels(&pane, ["A*", "B", "C"], cx); -// } + #[gpui::test] + async fn test_add_item_with_existing_item(cx: &mut TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.executor()); -// #[gpui::test] -// async fn test_add_item_with_same_project_entries(cx: &mut TestAppContext) { -// cx.foreground().forbid_parking(); -// init_test(cx); -// let fs = FakeFs::new(cx.background()); - -// let project = Project::test(fs, None, cx).await; -// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); -// let workspace = window.root(cx); -// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); - -// // singleton view -// pane.update(cx, |pane, cx| { -// let item = TestItem::new() -// .with_singleton(true) -// .with_label("buffer 1") -// .with_project_items(&[TestProjectItem::new(1, "one.txt", cx)]); - -// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx); -// }); -// assert_item_labels(&pane, ["buffer 1*"], cx); - -// // new singleton view with the same project entry -// pane.update(cx, |pane, cx| { -// let item = TestItem::new() -// .with_singleton(true) -// .with_label("buffer 1") -// .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]); - -// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx); -// }); -// assert_item_labels(&pane, ["buffer 1*"], cx); - -// // new singleton view with different project entry -// pane.update(cx, |pane, cx| { -// let item = TestItem::new() -// .with_singleton(true) -// .with_label("buffer 2") -// .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)]); -// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx); -// }); -// assert_item_labels(&pane, ["buffer 1", "buffer 2*"], cx); - -// // new multibuffer view with the same project entry -// pane.update(cx, |pane, cx| { -// let item = TestItem::new() -// .with_singleton(false) -// .with_label("multibuffer 1") -// .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]); - -// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx); -// }); -// assert_item_labels(&pane, ["buffer 1", "buffer 2", "multibuffer 1*"], cx); - -// // another multibuffer view with the same project entry -// pane.update(cx, |pane, cx| { -// let item = TestItem::new() -// .with_singleton(false) -// .with_label("multibuffer 1b") -// .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]); - -// pane.add_item(Box::new(cx.add_view(|_| item)), false, false, None, cx); -// }); -// assert_item_labels( -// &pane, -// ["buffer 1", "buffer 2", "multibuffer 1", "multibuffer 1b*"], -// cx, -// ); -// } + let project = Project::test(fs, None, cx).await; + let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx)); + let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone()); -// #[gpui::test] -// async fn test_remove_item_ordering(cx: &mut TestAppContext) { -// init_test(cx); -// let fs = FakeFs::new(cx.background()); - -// let project = Project::test(fs, None, cx).await; -// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); -// let workspace = window.root(cx); -// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); - -// add_labeled_item(&pane, "A", false, cx); -// add_labeled_item(&pane, "B", false, cx); -// add_labeled_item(&pane, "C", false, cx); -// add_labeled_item(&pane, "D", false, cx); -// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); - -// pane.update(cx, |pane, cx| pane.activate_item(1, false, false, cx)); -// add_labeled_item(&pane, "1", false, cx); -// assert_item_labels(&pane, ["A", "B", "1*", "C", "D"], cx); - -// pane.update(cx, |pane, cx| { -// pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) -// }) -// .unwrap() -// .await -// .unwrap(); -// assert_item_labels(&pane, ["A", "B*", "C", "D"], cx); - -// pane.update(cx, |pane, cx| pane.activate_item(3, false, false, cx)); -// assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); - -// pane.update(cx, |pane, cx| { -// pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) -// }) -// .unwrap() -// .await -// .unwrap(); -// assert_item_labels(&pane, ["A", "B*", "C"], cx); - -// pane.update(cx, |pane, cx| { -// pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) -// }) -// .unwrap() -// .await -// .unwrap(); -// assert_item_labels(&pane, ["A", "C*"], cx); - -// pane.update(cx, |pane, cx| { -// pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) -// }) -// .unwrap() -// .await -// .unwrap(); -// assert_item_labels(&pane, ["A*"], cx); -// } + // 1. Add with a destination index + // 1a. Add before the active item + let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx); + pane.update(cx, |pane, cx| { + pane.add_item(d, false, false, Some(0), cx); + }); + assert_item_labels(&pane, ["D*", "A", "B", "C"], cx); -// #[gpui::test] -// async fn test_close_inactive_items(cx: &mut TestAppContext) { -// init_test(cx); -// let fs = FakeFs::new(cx.background()); - -// let project = Project::test(fs, None, cx).await; -// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); -// let workspace = window.root(cx); -// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); - -// set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); - -// pane.update(cx, |pane, cx| { -// pane.close_inactive_items(&CloseInactiveItems, cx) -// }) -// .unwrap() -// .await -// .unwrap(); -// assert_item_labels(&pane, ["C*"], cx); -// } + // 1b. Add after the active item + let [_, _, _, d] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx); + pane.update(cx, |pane, cx| { + pane.add_item(d, false, false, Some(2), cx); + }); + assert_item_labels(&pane, ["A", "B", "D*", "C"], cx); -// #[gpui::test] -// async fn test_close_clean_items(cx: &mut TestAppContext) { -// init_test(cx); -// let fs = FakeFs::new(cx.background()); - -// let project = Project::test(fs, None, cx).await; -// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); -// let workspace = window.root(cx); -// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); - -// add_labeled_item(&pane, "A", true, cx); -// add_labeled_item(&pane, "B", false, cx); -// add_labeled_item(&pane, "C", true, cx); -// add_labeled_item(&pane, "D", false, cx); -// add_labeled_item(&pane, "E", false, cx); -// assert_item_labels(&pane, ["A^", "B", "C^", "D", "E*"], cx); - -// pane.update(cx, |pane, cx| pane.close_clean_items(&CloseCleanItems, cx)) -// .unwrap() -// .await -// .unwrap(); -// assert_item_labels(&pane, ["A^", "C*^"], cx); -// } + // 1c. Add at the end of the item list (including off the length) + let [a, _, _, _] = set_labeled_items(&pane, ["A", "B*", "C", "D"], cx); + pane.update(cx, |pane, cx| { + pane.add_item(a, false, false, Some(5), cx); + }); + assert_item_labels(&pane, ["B", "C", "D", "A*"], cx); -// #[gpui::test] -// async fn test_close_items_to_the_left(cx: &mut TestAppContext) { -// init_test(cx); -// let fs = FakeFs::new(cx.background()); - -// let project = Project::test(fs, None, cx).await; -// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); -// let workspace = window.root(cx); -// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); - -// set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); - -// pane.update(cx, |pane, cx| { -// pane.close_items_to_the_left(&CloseItemsToTheLeft, cx) -// }) -// .unwrap() -// .await -// .unwrap(); -// assert_item_labels(&pane, ["C*", "D", "E"], cx); -// } + // 1d. Add same item to active index + let [_, b, _] = set_labeled_items(&pane, ["A", "B*", "C"], cx); + pane.update(cx, |pane, cx| { + pane.add_item(b, false, false, Some(1), cx); + }); + assert_item_labels(&pane, ["A", "B*", "C"], cx); -// #[gpui::test] -// async fn test_close_items_to_the_right(cx: &mut TestAppContext) { -// init_test(cx); -// let fs = FakeFs::new(cx.background()); - -// let project = Project::test(fs, None, cx).await; -// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); -// let workspace = window.root(cx); -// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); - -// set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); - -// pane.update(cx, |pane, cx| { -// pane.close_items_to_the_right(&CloseItemsToTheRight, cx) -// }) -// .unwrap() -// .await -// .unwrap(); -// assert_item_labels(&pane, ["A", "B", "C*"], cx); -// } + // 1e. Add item to index after same item in last position + let [_, _, c] = set_labeled_items(&pane, ["A", "B*", "C"], cx); + pane.update(cx, |pane, cx| { + pane.add_item(c, false, false, Some(2), cx); + }); + assert_item_labels(&pane, ["A", "B", "C*"], cx); -// #[gpui::test] -// async fn test_close_all_items(cx: &mut TestAppContext) { -// init_test(cx); -// let fs = FakeFs::new(cx.background()); - -// let project = Project::test(fs, None, cx).await; -// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); -// let workspace = window.root(cx); -// let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); - -// add_labeled_item(&pane, "A", false, cx); -// add_labeled_item(&pane, "B", false, cx); -// add_labeled_item(&pane, "C", false, cx); -// assert_item_labels(&pane, ["A", "B", "C*"], cx); - -// pane.update(cx, |pane, cx| { -// pane.close_all_items(&CloseAllItems { save_intent: None }, cx) -// }) -// .unwrap() -// .await -// .unwrap(); -// assert_item_labels(&pane, [], cx); - -// add_labeled_item(&pane, "A", true, cx); -// add_labeled_item(&pane, "B", true, cx); -// add_labeled_item(&pane, "C", true, cx); -// assert_item_labels(&pane, ["A^", "B^", "C*^"], cx); - -// let save = pane -// .update(cx, |pane, cx| { -// pane.close_all_items(&CloseAllItems { save_intent: None }, cx) -// }) -// .unwrap(); - -// cx.foreground().run_until_parked(); -// window.simulate_prompt_answer(2, cx); -// save.await.unwrap(); -// assert_item_labels(&pane, [], cx); -// } + // 2. Add without a destination index + // 2a. Add with active item at the start of the item list + let [_, _, _, d] = set_labeled_items(&pane, ["A*", "B", "C", "D"], cx); + pane.update(cx, |pane, cx| { + pane.add_item(d, false, false, None, cx); + }); + assert_item_labels(&pane, ["A", "D*", "B", "C"], cx); -// fn init_test(cx: &mut TestAppContext) { -// cx.update(|cx| { -// cx.set_global(SettingsStore::test(cx)); -// theme::init((), cx); -// crate::init_settings(cx); -// Project::init_settings(cx); -// }); -// } + // 2b. Add with active item at the end of the item list + let [a, _, _, _] = set_labeled_items(&pane, ["A", "B", "C", "D*"], cx); + pane.update(cx, |pane, cx| { + pane.add_item(a, false, false, None, cx); + }); + assert_item_labels(&pane, ["B", "C", "D", "A*"], cx); -// fn add_labeled_item( -// pane: &ViewHandle, -// label: &str, -// is_dirty: bool, -// cx: &mut TestAppContext, -// ) -> Box> { -// pane.update(cx, |pane, cx| { -// let labeled_item = -// Box::new(cx.add_view(|_| TestItem::new().with_label(label).with_dirty(is_dirty))); -// pane.add_item(labeled_item.clone(), false, false, None, cx); -// labeled_item -// }) -// } + // 2c. Add active item to active item at end of list + let [_, _, c] = set_labeled_items(&pane, ["A", "B", "C*"], cx); + pane.update(cx, |pane, cx| { + pane.add_item(c, false, false, None, cx); + }); + assert_item_labels(&pane, ["A", "B", "C*"], cx); -// fn set_labeled_items( -// pane: &ViewHandle, -// labels: [&str; COUNT], -// cx: &mut TestAppContext, -// ) -> [Box>; COUNT] { -// pane.update(cx, |pane, cx| { -// pane.items.clear(); -// let mut active_item_index = 0; - -// let mut index = 0; -// let items = labels.map(|mut label| { -// if label.ends_with("*") { -// label = label.trim_end_matches("*"); -// active_item_index = index; -// } - -// let labeled_item = Box::new(cx.add_view(|_| TestItem::new().with_label(label))); -// pane.add_item(labeled_item.clone(), false, false, None, cx); -// index += 1; -// labeled_item -// }); - -// pane.activate_item(active_item_index, false, false, cx); - -// items -// }) -// } + // 2d. Add active item to active item at start of list + let [a, _, _] = set_labeled_items(&pane, ["A*", "B", "C"], cx); + pane.update(cx, |pane, cx| { + pane.add_item(a, false, false, None, cx); + }); + assert_item_labels(&pane, ["A*", "B", "C"], cx); + } + + #[gpui::test] + async fn test_add_item_with_same_project_entries(cx: &mut TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.executor()); + + let project = Project::test(fs, None, cx).await; + let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx)); + let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone()); + + // singleton view + pane.update(cx, |pane, cx| { + pane.add_item( + Box::new(cx.build_view(|cx| { + TestItem::new(cx) + .with_singleton(true) + .with_label("buffer 1") + .with_project_items(&[TestProjectItem::new(1, "one.txt", cx)]) + })), + false, + false, + None, + cx, + ); + }); + assert_item_labels(&pane, ["buffer 1*"], cx); + + // new singleton view with the same project entry + pane.update(cx, |pane, cx| { + pane.add_item( + Box::new(cx.build_view(|cx| { + TestItem::new(cx) + .with_singleton(true) + .with_label("buffer 1") + .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) + })), + false, + false, + None, + cx, + ); + }); + assert_item_labels(&pane, ["buffer 1*"], cx); + + // new singleton view with different project entry + pane.update(cx, |pane, cx| { + pane.add_item( + Box::new(cx.build_view(|cx| { + TestItem::new(cx) + .with_singleton(true) + .with_label("buffer 2") + .with_project_items(&[TestProjectItem::new(2, "2.txt", cx)]) + })), + false, + false, + None, + cx, + ); + }); + assert_item_labels(&pane, ["buffer 1", "buffer 2*"], cx); + + // new multibuffer view with the same project entry + pane.update(cx, |pane, cx| { + pane.add_item( + Box::new(cx.build_view(|cx| { + TestItem::new(cx) + .with_singleton(false) + .with_label("multibuffer 1") + .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) + })), + false, + false, + None, + cx, + ); + }); + assert_item_labels(&pane, ["buffer 1", "buffer 2", "multibuffer 1*"], cx); + + // another multibuffer view with the same project entry + pane.update(cx, |pane, cx| { + pane.add_item( + Box::new(cx.build_view(|cx| { + TestItem::new(cx) + .with_singleton(false) + .with_label("multibuffer 1b") + .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)]) + })), + false, + false, + None, + cx, + ); + }); + assert_item_labels( + &pane, + ["buffer 1", "buffer 2", "multibuffer 1", "multibuffer 1b*"], + cx, + ); + } -// // Assert the item label, with the active item label suffixed with a '*' -// fn assert_item_labels( -// pane: &ViewHandle, -// expected_states: [&str; COUNT], -// cx: &mut TestAppContext, -// ) { -// pane.read_with(cx, |pane, cx| { -// let actual_states = pane -// .items -// .iter() -// .enumerate() -// .map(|(ix, item)| { -// let mut state = item -// .as_any() -// .downcast_ref::() -// .unwrap() -// .read(cx) -// .label -// .clone(); -// if ix == pane.active_item_index { -// state.push('*'); -// } -// if item.is_dirty(cx) { -// state.push('^'); -// } -// state -// }) -// .collect::>(); - -// assert_eq!( -// actual_states, expected_states, -// "pane items do not match expectation" -// ); -// }) -// } -// } + #[gpui::test] + async fn test_remove_item_ordering(cx: &mut TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.executor()); + + let project = Project::test(fs, None, cx).await; + let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx)); + let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone()); + + add_labeled_item(&pane, "A", false, cx); + add_labeled_item(&pane, "B", false, cx); + add_labeled_item(&pane, "C", false, cx); + add_labeled_item(&pane, "D", false, cx); + assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); + + pane.update(cx, |pane, cx| pane.activate_item(1, false, false, cx)); + add_labeled_item(&pane, "1", false, cx); + assert_item_labels(&pane, ["A", "B", "1*", "C", "D"], cx); + + pane.update(cx, |pane, cx| { + pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) + }) + .unwrap() + .await + .unwrap(); + assert_item_labels(&pane, ["A", "B*", "C", "D"], cx); + + pane.update(cx, |pane, cx| pane.activate_item(3, false, false, cx)); + assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); + + pane.update(cx, |pane, cx| { + pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) + }) + .unwrap() + .await + .unwrap(); + assert_item_labels(&pane, ["A", "B*", "C"], cx); + + pane.update(cx, |pane, cx| { + pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) + }) + .unwrap() + .await + .unwrap(); + assert_item_labels(&pane, ["A", "C*"], cx); + + pane.update(cx, |pane, cx| { + pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) + }) + .unwrap() + .await + .unwrap(); + assert_item_labels(&pane, ["A*"], cx); + } + + #[gpui::test] + async fn test_close_inactive_items(cx: &mut TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.executor()); + + let project = Project::test(fs, None, cx).await; + let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx)); + let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone()); + + set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); + + pane.update(cx, |pane, cx| { + pane.close_inactive_items(&CloseInactiveItems, cx) + }) + .unwrap() + .await + .unwrap(); + assert_item_labels(&pane, ["C*"], cx); + } + + #[gpui::test] + async fn test_close_clean_items(cx: &mut TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.executor()); + + let project = Project::test(fs, None, cx).await; + let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx)); + let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone()); + + add_labeled_item(&pane, "A", true, cx); + add_labeled_item(&pane, "B", false, cx); + add_labeled_item(&pane, "C", true, cx); + add_labeled_item(&pane, "D", false, cx); + add_labeled_item(&pane, "E", false, cx); + assert_item_labels(&pane, ["A^", "B", "C^", "D", "E*"], cx); + + pane.update(cx, |pane, cx| pane.close_clean_items(&CloseCleanItems, cx)) + .unwrap() + .await + .unwrap(); + assert_item_labels(&pane, ["A^", "C*^"], cx); + } + + #[gpui::test] + async fn test_close_items_to_the_left(cx: &mut TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.executor()); + + let project = Project::test(fs, None, cx).await; + let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx)); + let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone()); + + set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); + + pane.update(cx, |pane, cx| { + pane.close_items_to_the_left(&CloseItemsToTheLeft, cx) + }) + .unwrap() + .await + .unwrap(); + assert_item_labels(&pane, ["C*", "D", "E"], cx); + } + + #[gpui::test] + async fn test_close_items_to_the_right(cx: &mut TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.executor()); + + let project = Project::test(fs, None, cx).await; + let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx)); + let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone()); + + set_labeled_items(&pane, ["A", "B", "C*", "D", "E"], cx); + + pane.update(cx, |pane, cx| { + pane.close_items_to_the_right(&CloseItemsToTheRight, cx) + }) + .unwrap() + .await + .unwrap(); + assert_item_labels(&pane, ["A", "B", "C*"], cx); + } + + #[gpui::test] + async fn test_close_all_items(cx: &mut TestAppContext) { + init_test(cx); + let fs = FakeFs::new(cx.executor()); + + let project = Project::test(fs, None, cx).await; + let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx)); + let pane = workspace.update(cx, |workspace, _| workspace.active_pane().clone()); + + add_labeled_item(&pane, "A", false, cx); + add_labeled_item(&pane, "B", false, cx); + add_labeled_item(&pane, "C", false, cx); + assert_item_labels(&pane, ["A", "B", "C*"], cx); + + pane.update(cx, |pane, cx| { + pane.close_all_items(&CloseAllItems { save_intent: None }, cx) + }) + .unwrap() + .await + .unwrap(); + assert_item_labels(&pane, [], cx); + + add_labeled_item(&pane, "A", true, cx); + add_labeled_item(&pane, "B", true, cx); + add_labeled_item(&pane, "C", true, cx); + assert_item_labels(&pane, ["A^", "B^", "C*^"], cx); + + let save = pane + .update(cx, |pane, cx| { + pane.close_all_items(&CloseAllItems { save_intent: None }, cx) + }) + .unwrap(); + + cx.executor().run_until_parked(); + cx.simulate_prompt_answer(2); + save.await.unwrap(); + assert_item_labels(&pane, [], cx); + } + + fn init_test(cx: &mut TestAppContext) { + cx.update(|cx| { + let settings_store = SettingsStore::test(cx); + cx.set_global(settings_store); + theme::init(LoadThemes::JustBase, cx); + crate::init_settings(cx); + Project::init_settings(cx); + }); + } + + fn add_labeled_item( + pane: &View, + label: &str, + is_dirty: bool, + cx: &mut VisualTestContext, + ) -> Box> { + pane.update(cx, |pane, cx| { + let labeled_item = Box::new( + cx.build_view(|cx| TestItem::new(cx).with_label(label).with_dirty(is_dirty)), + ); + pane.add_item(labeled_item.clone(), false, false, None, cx); + labeled_item + }) + } + + fn set_labeled_items( + pane: &View, + labels: [&str; COUNT], + cx: &mut VisualTestContext, + ) -> [Box>; COUNT] { + pane.update(cx, |pane, cx| { + pane.items.clear(); + let mut active_item_index = 0; + + let mut index = 0; + let items = labels.map(|mut label| { + if label.ends_with("*") { + label = label.trim_end_matches("*"); + active_item_index = index; + } + + let labeled_item = + Box::new(cx.build_view(|cx| TestItem::new(cx).with_label(label))); + pane.add_item(labeled_item.clone(), false, false, None, cx); + index += 1; + labeled_item + }); + + pane.activate_item(active_item_index, false, false, cx); + + items + }) + } + + // Assert the item label, with the active item label suffixed with a '*' + fn assert_item_labels( + pane: &View, + expected_states: [&str; COUNT], + cx: &mut VisualTestContext, + ) { + pane.update(cx, |pane, cx| { + let actual_states = pane + .items + .iter() + .enumerate() + .map(|(ix, item)| { + let mut state = item + .to_any() + .downcast::() + .unwrap() + .read(cx) + .label + .clone(); + if ix == pane.active_item_index { + state.push('*'); + } + if item.is_dirty(cx) { + state.push('^'); + } + state + }) + .collect::>(); + + assert_eq!( + actual_states, expected_states, + "pane items do not match expectation" + ); + }) + } +} impl Render for DraggedTab { type Element = ::Rendered;