diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-17 01:41:16 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-17 01:41:16 +0300 |
| commit | a36da363f14639085bec91d7a378d6c2eeaa6c58 (patch) | |
| tree | 2c86155b115f8c79db2b8826fd8b2199d153f7bf /src/persist | |
| parent | e4de58b37f58d59d146158239ed218a8accd5c3c (diff) | |
refactor: extract terminal state and runtime from panes
Diffstat (limited to 'src/persist')
| -rw-r--r-- | src/persist/restore.rs | 149 | ||||
| -rw-r--r-- | src/persist/snapshot.rs | 70 |
2 files changed, 149 insertions, 70 deletions
diff --git a/src/persist/restore.rs b/src/persist/restore.rs index 1e10df6d..e53a61d9 100644 --- a/src/persist/restore.rs +++ b/src/persist/restore.rs @@ -9,7 +9,8 @@ use tracing::{error, warn}; use crate::events::AppEvent; use crate::layout::{Node, PaneId, TileLayout}; -use crate::pane::{PaneRuntime, PaneState}; +use crate::pane::PaneState; +use crate::terminal::{TerminalId, TerminalRuntime, TerminalState}; use crate::workspace::Workspace; use super::{DirectionSnapshot, LayoutSnapshot, SessionSnapshot, TabSnapshot, WorkspaceSnapshot}; @@ -23,22 +24,32 @@ pub fn restore( events: mpsc::Sender<AppEvent>, render_notify: Arc<Notify>, render_dirty: Arc<AtomicBool>, -) -> Vec<Workspace> { - snapshot - .workspaces - .iter() - .filter_map(|ws_snap| { - restore_workspace( - ws_snap, - rows, - cols, - scrollback_limit_bytes, - events.clone(), - render_notify.clone(), - render_dirty.clone(), - ) - }) - .collect() +) -> ( + Vec<Workspace>, + HashMap<TerminalId, TerminalState>, + HashMap<TerminalId, TerminalRuntime>, +) { + let mut workspaces = Vec::new(); + let mut terminals = HashMap::new(); + let mut terminal_runtimes = HashMap::new(); + for ws_snap in &snapshot.workspaces { + if let Some((workspace, restored_terminals, restored_runtimes)) = restore_workspace( + ws_snap, + rows, + cols, + scrollback_limit_bytes, + events.clone(), + render_notify.clone(), + render_dirty.clone(), + ) { + for terminal in restored_terminals { + terminals.insert(terminal.id.clone(), terminal); + } + terminal_runtimes.extend(restored_runtimes); + workspaces.push(workspace); + } + } + (workspaces, terminals, terminal_runtimes) } fn restore_workspace( @@ -49,13 +60,19 @@ fn restore_workspace( events: mpsc::Sender<AppEvent>, render_notify: Arc<Notify>, render_dirty: Arc<AtomicBool>, -) -> Option<Workspace> { +) -> Option<( + Workspace, + Vec<TerminalState>, + HashMap<TerminalId, TerminalRuntime>, +)> { let mut tabs = Vec::new(); + let mut terminals = Vec::new(); + let mut terminal_runtimes = HashMap::new(); let mut public_pane_numbers = HashMap::new(); let mut next_public_pane_number = 1; for (idx, tab_snap) in snap.tabs.iter().enumerate() { - let tab = restore_tab( + let (tab, restored_terminals, restored_runtimes) = restore_tab( tab_snap, idx + 1, rows, @@ -69,6 +86,8 @@ fn restore_workspace( public_pane_numbers.insert(pane_id, next_public_pane_number); next_public_pane_number += 1; } + terminals.extend(restored_terminals); + terminal_runtimes.extend(restored_runtimes); tabs.push(tab); } @@ -76,20 +95,26 @@ fn restore_workspace( return None; } - Some(Workspace { - id: snap - .id - .clone() - .unwrap_or_else(crate::workspace::generate_workspace_id), - custom_name: snap.custom_name.clone(), - identity_cwd: snap.identity_cwd.clone(), - cached_git_branch: None, - cached_git_ahead_behind: None, - public_pane_numbers, - next_public_pane_number, - active_tab: snap.active_tab.min(tabs.len().saturating_sub(1)), - tabs, - }) + Some(( + Workspace { + id: snap + .id + .clone() + .unwrap_or_else(crate::workspace::generate_workspace_id), + custom_name: snap.custom_name.clone(), + identity_cwd: snap.identity_cwd.clone(), + cached_git_branch: None, + cached_git_ahead_behind: None, + public_pane_numbers, + next_public_pane_number, + active_tab: snap.active_tab.min(tabs.len().saturating_sub(1)), + tabs, + #[cfg(test)] + test_runtimes: HashMap::new(), + }, + terminals, + terminal_runtimes, + )) } fn restore_tab( @@ -101,7 +126,11 @@ fn restore_tab( events: mpsc::Sender<AppEvent>, render_notify: Arc<Notify>, render_dirty: Arc<AtomicBool>, -) -> Option<crate::workspace::Tab> { +) -> Option<( + crate::workspace::Tab, + Vec<TerminalState>, + HashMap<TerminalId, TerminalRuntime>, +)> { let (node, id_map) = restore_node_remapped(&snap.layout); let reverse_id_map: HashMap<PaneId, u32> = id_map .iter() @@ -110,9 +139,8 @@ fn restore_tab( let pane_ids = collect_pane_ids(&node); let mut panes = HashMap::new(); - let mut pane_cwds = HashMap::new(); - let mut terminal_ids = HashMap::new(); - let mut runtimes = HashMap::new(); + let mut terminals = Vec::new(); + let mut terminal_runtimes = HashMap::new(); for id in &pane_ids { let saved_cwd = reverse_id_map .get(id) @@ -142,7 +170,7 @@ fn restore_tab( .and_then(|old_id| snap.panes.get(old_id)) .and_then(|p| p.label.clone()); - match PaneRuntime::spawn( + match TerminalRuntime::spawn( *id, rows, cols, @@ -154,12 +182,14 @@ fn restore_tab( render_dirty.clone(), ) { Ok(runtime) => { - let mut pane_state = PaneState::new(); - pane_state.manual_label = saved_label; - panes.insert(*id, pane_state); - pane_cwds.insert(*id, cwd.clone()); - terminal_ids.insert(*id, crate::terminal::TerminalId::alloc()); - runtimes.insert(*id, runtime); + let terminal_id = TerminalId::alloc(); + let mut terminal = TerminalState::new(terminal_id.clone(), cwd.clone()); + if let Some(label) = saved_label { + terminal.set_manual_label(label); + } + panes.insert(*id, PaneState::new(terminal_id.clone())); + terminal_runtimes.insert(terminal_id, runtime); + terminals.push(terminal); } Err(e) => { error!( @@ -193,20 +223,23 @@ fn restore_tab( let root_pane = resolve_restored_pane(snap.root_pane, &id_map, &surviving, &pane_ids)?; let layout = TileLayout::from_saved(node, focus); - Some(crate::workspace::Tab { - custom_name: snap.custom_name.clone(), - number, - root_pane, - layout, - panes, - pane_cwds, - terminal_ids, - runtimes, - zoomed: snap.zoomed, - events, - render_notify, - render_dirty, - }) + Some(( + crate::workspace::Tab { + custom_name: snap.custom_name.clone(), + number, + root_pane, + layout, + panes, + #[cfg(test)] + runtimes: HashMap::new(), + zoomed: snap.zoomed, + events, + render_notify, + render_dirty, + }, + terminals, + terminal_runtimes, + )) } pub(super) fn prune_restored_node(node: Node, surviving: &HashSet<PaneId>) -> Option<Node> { diff --git a/src/persist/snapshot.rs b/src/persist/snapshot.rs index 76ed96f5..245fbb76 100644 --- a/src/persist/snapshot.rs +++ b/src/persist/snapshot.rs @@ -195,6 +195,14 @@ fn first_pane_id_in_layout(layout: &LayoutSnapshot) -> Option<u32> { /// Capture the current app state into a serializable snapshot. pub fn capture( workspaces: &[Workspace], + terminals: &std::collections::HashMap< + crate::terminal::TerminalId, + crate::terminal::TerminalState, + >, + terminal_runtimes: &std::collections::HashMap< + crate::terminal::TerminalId, + crate::terminal::TerminalRuntime, + >, active: Option<usize>, selected: usize, agent_panel_scope: crate::app::state::AgentPanelScope, @@ -203,7 +211,10 @@ pub fn capture( ) -> SessionSnapshot { SessionSnapshot { version: SNAPSHOT_VERSION, - workspaces: workspaces.iter().map(capture_workspace).collect(), + workspaces: workspaces + .iter() + .map(|workspace| capture_workspace(workspace, terminals, terminal_runtimes)) + .collect(), active, selected, agent_panel_scope, @@ -212,25 +223,53 @@ pub fn capture( } } -fn capture_workspace(ws: &Workspace) -> WorkspaceSnapshot { +fn capture_workspace( + ws: &Workspace, + terminals: &std::collections::HashMap< + crate::terminal::TerminalId, + crate::terminal::TerminalState, + >, + terminal_runtimes: &std::collections::HashMap< + crate::terminal::TerminalId, + crate::terminal::TerminalRuntime, + >, +) -> WorkspaceSnapshot { WorkspaceSnapshot { id: Some(ws.id.clone()), custom_name: ws.custom_name.clone(), identity_cwd: ws .resolved_identity_cwd() .unwrap_or_else(|| ws.identity_cwd.clone()), - tabs: ws.tabs.iter().map(capture_tab).collect(), + tabs: ws + .tabs + .iter() + .map(|tab| capture_tab(tab, terminals, terminal_runtimes)) + .collect(), active_tab: ws.active_tab, } } -fn capture_tab(tab: &crate::workspace::Tab) -> TabSnapshot { +fn capture_tab( + tab: &crate::workspace::Tab, + terminals: &std::collections::HashMap< + crate::terminal::TerminalId, + crate::terminal::TerminalState, + >, + terminal_runtimes: &std::collections::HashMap< + crate::terminal::TerminalId, + crate::terminal::TerminalRuntime, + >, +) -> TabSnapshot { let mut panes = HashMap::new(); for id in tab.panes.keys() { let cwd = tab - .cwd_for_pane(*id) + .cwd_for_pane(*id, terminals, terminal_runtimes) .unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| "/".into())); - let label = tab.panes.get(id).and_then(|pane| pane.manual_label.clone()); + let label = tab + .panes + .get(id) + .and_then(|pane| terminals.get(&pane.attached_terminal_id)) + .and_then(|terminal| terminal.manual_label.clone()); panes.insert(id.raw(), PaneSnapshot { cwd, label }); } TabSnapshot { @@ -310,6 +349,7 @@ mod tests { fn state_with_workspaces(names: &[&str]) -> AppState { let mut state = AppState::test_new(); state.workspaces = names.iter().map(|name| Workspace::test_new(name)).collect(); + state.ensure_test_terminals(); if !state.workspaces.is_empty() { state.active = Some(0); state.selected = 0; @@ -321,6 +361,8 @@ mod tests { fn capture_from_state(state: &AppState) -> SessionSnapshot { capture( &state.workspaces, + &state.terminals, + &state.terminal_runtimes, state.active, state.selected, state.agent_panel_scope, @@ -657,13 +699,17 @@ mod tests { fn capture_contract_tracks_workspace_identity_and_pane_cwds() { let mut state = state_with_workspaces(&["one"]); let root = state.workspaces[0].tabs[0].root_pane; - state.workspaces[0].tabs[0] - .pane_cwds - .insert(root, PathBuf::from("/tmp/pion")); + state.workspaces[0].identity_cwd = PathBuf::from("/tmp/pion"); let second = state.workspaces[0].test_split(Direction::Horizontal); - state.workspaces[0].tabs[0] - .pane_cwds - .insert(second, PathBuf::from("/tmp/herdr")); + state.ensure_test_terminals(); + let root_terminal_id = state.workspaces[0].tabs[0].panes[&root] + .attached_terminal_id + .clone(); + state.terminals.get_mut(&root_terminal_id).unwrap().cwd = PathBuf::from("/tmp/pion"); + let second_terminal_id = state.workspaces[0].tabs[0].panes[&second] + .attached_terminal_id + .clone(); + state.terminals.get_mut(&second_terminal_id).unwrap().cwd = PathBuf::from("/tmp/herdr"); let snapshot = capture_from_state(&state); let workspace = &snapshot.workspaces[0]; |
