terminal: Preserve terminal order and fix pinned count on workspace restore (#44464)

Replace `FuturesUnordered` with `FuturesOrdered` to maintain terminal
tab ordering when restoring a workspace.
Also clamp `pinned_count` to `items_len()` to prevent panics when fewer
terminals are restored than expected.

The changes:
1. Order preservation: `FuturesUnordered` → `FuturesOrdered` ensures
terminals restore in their original order
2. Bounds fix: `.min(pane.items_len())` prevents setting pinned count
higher than actual items

Closes #44463

Release Notes:

- Preserved terminal order and fixed pinned count on workspace restore

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
This commit is contained in:
Vladislav 2026-02-12 12:59:59 +03:00 committed by GitHub
parent 213de2ec9b
commit e8cfd1941d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,7 +1,7 @@
use anyhow::Result;
use async_recursion::async_recursion;
use collections::HashSet;
use futures::{StreamExt as _, stream::FuturesUnordered};
use futures::future::join_all;
use gpui::{AppContext as _, AsyncWindowContext, Axis, Entity, Task, WeakEntity};
use project::Project;
use serde::{Deserialize, Serialize};
@ -242,7 +242,7 @@ async fn deserialize_pane_group(
let items = pane.update_in(cx, |pane, window, cx| {
populate_pane_items(pane, new_items, active_item, window, cx);
pane.set_pinned_count(pinned_count);
pane.set_pinned_count(pinned_count.min(pane.items_len()));
pane.items_len()
});
// Avoid blank panes in splits
@ -290,30 +290,25 @@ fn deserialize_terminal_views(
item_ids: &[u64],
cx: &mut AsyncWindowContext,
) -> impl Future<Output = Vec<Entity<TerminalView>>> + use<> {
let mut deserialized_items = item_ids
.iter()
.map(|item_id| {
cx.update(|window, cx| {
TerminalView::deserialize(
project.clone(),
workspace.clone(),
workspace_id,
*item_id,
window,
cx,
)
})
.unwrap_or_else(|e| Task::ready(Err(e.context("no window present"))))
let deserialized_items = join_all(item_ids.iter().filter_map(|item_id| {
cx.update(|window, cx| {
TerminalView::deserialize(
project.clone(),
workspace.clone(),
workspace_id,
*item_id,
window,
cx,
)
})
.collect::<FuturesUnordered<_>>();
.ok()
}));
async move {
let mut items = Vec::with_capacity(deserialized_items.len());
while let Some(item) = deserialized_items.next().await {
if let Some(item) = item.log_err() {
items.push(item);
}
}
items
deserialized_items
.await
.into_iter()
.filter_map(|item| item.log_err())
.collect()
}
}