diff --git a/assets/settings/default.json b/assets/settings/default.json index 628b51df2c..3634f088f4 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -561,9 +561,11 @@ // What to do after closing the current tab. // // 1. Activate the tab that was open previously (default) - // "History" - // 2. Activate the neighbour tab (prefers the right one, if present) - // "Neighbour" + // "history" + // 2. Activate the right neighbour tab if present + // "neighbour" + // 3. Activate the left neighbour tab if present + // "left_neighbour" "activate_on_close": "history", /// Which files containing diagnostic errors/warnings to mark in the tabs. /// Diagnostics are only shown when file icons are also active. diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 7b9478a9a7..4519b67192 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -71,11 +71,12 @@ pub enum ShowDiagnostics { } #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "lowercase")] +#[serde(rename_all = "snake_case")] pub enum ActivateOnClose { #[default] History, Neighbour, + LeftNeighbour, } #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index a4ca58c11c..3d844f7872 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1506,6 +1506,7 @@ impl Pane { self.pinned_tab_count -= 1; } if item_index == self.active_item_index { + let left_neighbour_index = || item_index.min(self.items.len()).saturating_sub(1); let index_to_activate = match activate_on_close { ActivateOnClose::History => self .activation_history @@ -1517,7 +1518,7 @@ impl Pane { }) // We didn't have a valid activation history entry, so fallback // to activating the item to the left - .unwrap_or_else(|| item_index.min(self.items.len()).saturating_sub(1)), + .unwrap_or_else(left_neighbour_index), ActivateOnClose::Neighbour => { self.activation_history.pop(); if item_index + 1 < self.items.len() { @@ -1526,6 +1527,10 @@ impl Pane { item_index.saturating_sub(1) } } + ActivateOnClose::LeftNeighbour => { + self.activation_history.pop(); + left_neighbour_index() + } }; let should_activate = activate_pane || self.has_focus(cx); @@ -3666,6 +3671,69 @@ mod tests { assert_item_labels(&pane, ["A*"], cx); } + #[gpui::test] + async fn test_remove_item_ordering_left_neighbour(cx: &mut TestAppContext) { + init_test(cx); + cx.update_global::(|s, cx| { + s.update_user_settings::(cx, |s| { + s.activate_on_close = Some(ActivateOnClose::LeftNeighbour); + }); + }); + 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.activate_item(0, false, false, cx)); + 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, ["B*", "C"], cx); + + pane.update(cx, |pane, cx| { + pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) + }) + .unwrap() + .await + .unwrap(); + assert_item_labels(&pane, ["C*"], cx); + } + #[gpui::test] async fn test_close_inactive_items(cx: &mut TestAppContext) { init_test(cx); diff --git a/docs/src/configuring-zed.md b/docs/src/configuring-zed.md index 57df291338..e4caeaf431 100644 --- a/docs/src/configuring-zed.md +++ b/docs/src/configuring-zed.md @@ -691,7 +691,7 @@ List of `string` values } ``` -2. Activate the neighbour tab (prefers the right one, if present): +2. Activate the right neighbour tab if present: ```json { @@ -699,6 +699,14 @@ List of `string` values } ``` +3. Activate the left neighbour tab if present: + +```json +{ + "activate_on_close": "left_neighbour" +} +``` + ### Always show the close button - Description: Whether to always show the close button on tabs.