diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d72704da0120f979081a72457bbd2690794b9177..3aa85c4a9577540092bee02ebed536fb0479d6fa 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2701,11 +2701,62 @@ fn open_new(app_state: &Arc, cx: &mut MutableAppContext) { #[cfg(test)] mod tests { + use std::cell::Cell; + use super::*; use gpui::{executor::Deterministic, ModelHandle, TestAppContext, ViewContext}; use project::{FakeFs, Project, ProjectEntryId}; use serde_json::json; + #[gpui::test] + async fn test_tab_disambiguation(cx: &mut TestAppContext) { + cx.foreground().forbid_parking(); + Settings::test_async(cx); + + let fs = FakeFs::new(cx.background()); + let project = Project::test(fs, [], cx).await; + let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); + + // Adding an item with no ambiguity renders the tab without detail. + let item1 = cx.add_view(window_id, |_| { + let mut item = TestItem::new(); + item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]); + item + }); + workspace.update(cx, |workspace, cx| { + workspace.add_item(Box::new(item1.clone()), cx); + }); + item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), None)); + + // Adding an item that creates ambiguity increases the level of detail on + // both tabs. + let item2 = cx.add_view(window_id, |_| { + let mut item = TestItem::new(); + item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]); + item + }); + workspace.update(cx, |workspace, cx| { + workspace.add_item(Box::new(item2.clone()), cx); + }); + item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1))); + item2.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1))); + + // Adding an item that creates ambiguity increases the level of detail only + // on the ambiguous tabs. In this case, the ambiguity can't be resolved so + // we stop at the highest detail available. + let item3 = cx.add_view(window_id, |_| { + let mut item = TestItem::new(); + item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]); + item + }); + workspace.update(cx, |workspace, cx| { + workspace.add_item(Box::new(item3.clone()), cx); + }); + item1.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1))); + item2.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3))); + item3.read_with(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3))); + } + #[gpui::test] async fn test_tracking_active_path(cx: &mut TestAppContext) { cx.foreground().forbid_parking(); @@ -3226,6 +3277,8 @@ mod tests { project_entry_ids: Vec, project_path: Option, nav_history: Option, + tab_descriptions: Option>, + tab_detail: Cell>, } enum TestItemEvent { @@ -3245,6 +3298,8 @@ mod tests { project_entry_ids: self.project_entry_ids.clone(), project_path: self.project_path.clone(), nav_history: None, + tab_descriptions: None, + tab_detail: Default::default(), } } } @@ -3262,6 +3317,8 @@ mod tests { project_path: None, is_singleton: true, nav_history: None, + tab_descriptions: None, + tab_detail: Default::default(), } } @@ -3292,7 +3349,15 @@ mod tests { } impl Item for TestItem { - fn tab_content(&self, _: Option, _: &theme::Tab, _: &AppContext) -> ElementBox { + fn tab_description<'a>(&'a self, detail: usize, _: &'a AppContext) -> Option> { + self.tab_descriptions.as_ref().and_then(|descriptions| { + let description = *descriptions.get(detail).or(descriptions.last())?; + Some(description.into()) + }) + } + + fn tab_content(&self, detail: Option, _: &theme::Tab, _: &AppContext) -> ElementBox { + self.tab_detail.set(detail); Empty::new().boxed() }