Skip to content

Commit 5c77533

Browse files
fix(app): restore closed panes by workspace id, not index (closes #48)
ClosedPaneRecord stored workspace_idx. Closing or reordering workspaces shifted indexes, so undo inserted into a different workspace. Store Workspace.id and refuse with a toast if that workspace is gone.
1 parent 6db1a10 commit 5c77533

2 files changed

Lines changed: 36 additions & 15 deletions

File tree

‎src-app/src/app/workspace_ops/mod.rs‎

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ fn closed_pane_scrollback_bytes(records: &[ClosedPaneRecord]) -> usize {
9595

9696
fn capture_closed_pane_record(
9797
pane: &gpui::Entity<crate::pane::Pane>,
98-
workspace_idx: usize,
98+
workspace_id: u64,
9999
cx: &App,
100100
) -> Option<ClosedPaneRecord> {
101101
let pane_ref = pane.read(cx);
@@ -130,10 +130,18 @@ fn capture_closed_pane_record(
130130
Some(ClosedPaneRecord {
131131
tabs,
132132
selected_idx: pane_ref.selected_idx,
133-
workspace_idx,
133+
workspace_id,
134134
})
135135
}
136136

137+
/// Locate the workspace a closed-pane record should restore into.
138+
///
139+
/// Indexes shift when workspaces close or reorder; the record stores a
140+
/// stable `Workspace.id`. `None` means that workspace is gone.
141+
fn workspace_index_for_undo(ids: &[u64], record_id: u64) -> Option<usize> {
142+
ids.iter().position(|&id| id == record_id)
143+
}
144+
137145
/// After `workspaces.remove(removed_idx)`, map the previous `active_idx` onto
138146
/// the remaining `len` slots. Closing a workspace before the active one
139147
/// decrements; closing at or past the new last index clamps; an empty list is 0.
@@ -496,17 +504,17 @@ impl PaneFlowApp {
496504
) {
497505
// Capture state of the pane being closed for undo (US-014).
498506
// Must happen BEFORE the tree mutation that drops the pane entity.
499-
let workspace_idx = self.active_idx;
500507
if let Some(ws) = self.active_workspace()
501508
&& let Some(root) = &ws.root
502509
{
510+
let workspace_id = ws.id;
503511
let closing_pane = if ws.is_zoomed() {
504512
root.first_leaf()
505513
} else {
506514
root.focused_pane(window, cx)
507515
};
508516
if let Some(pane) = closing_pane
509-
&& let Some(record) = capture_closed_pane_record(&pane, workspace_idx, cx)
517+
&& let Some(record) = capture_closed_pane_record(&pane, workspace_id, cx)
510518
{
511519
push_closed_pane_record(&mut self.closed_panes, record);
512520
}
@@ -575,16 +583,15 @@ impl PaneFlowApp {
575583
return; // No closed panes to restore
576584
};
577585

578-
// Switch to the workspace where the pane was closed, if it still exists
579-
if record.workspace_idx < self.workspaces.len() {
580-
self.active_idx = record.workspace_idx;
581-
}
582-
583-
let Some(ws_id) = self.active_workspace().map(|ws| ws.id) else {
584-
self.closed_panes.push(record);
585-
self.show_toast("No active workspace to restore pane", cx);
586+
// Restore into the workspace the pane was closed in. Indexes shift
587+
// on close/reorder; the record stores a stable Workspace.id.
588+
let ids: Vec<u64> = self.workspaces.iter().map(|ws| ws.id).collect();
589+
let Some(idx) = workspace_index_for_undo(&ids, record.workspace_id) else {
590+
self.show_toast("Workspace no longer exists", cx);
586591
return;
587592
};
593+
self.active_idx = idx;
594+
let ws_id = record.workspace_id;
588595
let selected_idx = record.selected_idx;
589596
let tabs = record
590597
.tabs
@@ -1162,10 +1169,24 @@ mod tests {
11621169
font_size: None,
11631170
}],
11641171
selected_idx: 0,
1165-
workspace_idx: 0,
1172+
workspace_id: 0,
11661173
}
11671174
}
11681175

1176+
#[test]
1177+
fn workspace_index_for_undo_matches_after_lower_workspace_closed() {
1178+
// Originally [10, 20, 30]; workspace 10 (index 0) closed → [20, 30].
1179+
// A pane closed in workspace 20 was index 1; it is now index 0.
1180+
assert_eq!(workspace_index_for_undo(&[20, 30], 20), Some(0));
1181+
assert_eq!(workspace_index_for_undo(&[20, 30], 30), Some(1));
1182+
}
1183+
1184+
#[test]
1185+
fn workspace_index_for_undo_missing_id_returns_none() {
1186+
assert_eq!(workspace_index_for_undo(&[20, 30], 10), None);
1187+
assert_eq!(workspace_index_for_undo(&[], 1), None);
1188+
}
1189+
11691190
#[test]
11701191
fn closed_pane_budget_drops_oldest_scrollback_not_record() {
11711192
let one_mib = 1024 * 1024;
@@ -1268,7 +1289,7 @@ mod tests {
12681289
font_size: None,
12691290
}],
12701291
selected_idx: 0,
1271-
workspace_idx: 0,
1292+
workspace_id: 0,
12721293
},
12731294
);
12741295

‎src-app/src/main.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ pub(crate) enum ClosedTabRecord {
241241
pub(crate) struct ClosedPaneRecord {
242242
pub(crate) tabs: Vec<ClosedTabRecord>,
243243
pub(crate) selected_idx: usize,
244-
pub(crate) workspace_idx: usize,
244+
pub(crate) workspace_id: u64,
245245
}
246246

247247
/// US-053: in-app self-update flow state, extracted from the `PaneFlowApp`

0 commit comments

Comments
 (0)