diff options
Diffstat (limited to 'src/persist')
| -rw-r--r-- | src/persist/io.rs | 1 | ||||
| -rw-r--r-- | src/persist/restore.rs | 31 | ||||
| -rw-r--r-- | src/persist/snapshot.rs | 38 |
3 files changed, 69 insertions, 1 deletions
diff --git a/src/persist/io.rs b/src/persist/io.rs index f8525bc5..1b589b09 100644 --- a/src/persist/io.rs +++ b/src/persist/io.rs @@ -133,6 +133,7 @@ mod tests { agent_panel_scope: AgentPanelScope::CurrentWorkspace, sidebar_width: Some(26), sidebar_section_split: Some(0.5), + collapsed_space_keys: std::collections::HashSet::new(), } } diff --git a/src/persist/restore.rs b/src/persist/restore.rs index f558761d..ebad2f16 100644 --- a/src/persist/restore.rs +++ b/src/persist/restore.rs @@ -99,6 +99,8 @@ fn restore_workspace( return None; } + let worktree_space = restored_worktree_space_membership(snap.worktree_space.clone()); + Some(( Workspace { id: snap @@ -107,8 +109,10 @@ fn restore_workspace( .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_branch: crate::workspace::git_branch(&snap.identity_cwd), cached_git_ahead_behind: None, + cached_git_space: crate::workspace::git_space_metadata(&snap.identity_cwd), + worktree_space, public_pane_numbers, next_public_pane_number, active_tab: snap.active_tab.min(tabs.len().saturating_sub(1)), @@ -121,6 +125,16 @@ fn restore_workspace( )) } +fn restored_worktree_space_membership( + space: Option<crate::workspace::WorktreeSpaceMembership>, +) -> Option<crate::workspace::WorktreeSpaceMembership> { + space.filter(|space| { + space.checkout_path.exists() + && crate::workspace::git_space_metadata(&space.checkout_path) + .is_some_and(|current| current.key == space.key) + }) +} + fn restore_tab( snap: &TabSnapshot, number: usize, @@ -407,4 +421,19 @@ mod tests { Some(first) ); } + + #[test] + fn restored_worktree_space_membership_drops_missing_checkout() { + let missing = + std::env::temp_dir().join(format!("herdr-missing-worktree-{}", std::process::id())); + let membership = crate::workspace::WorktreeSpaceMembership { + key: "repo-key".into(), + label: "herdr".into(), + repo_root: missing.join("repo"), + checkout_path: missing.join("checkout"), + is_linked_worktree: true, + }; + + assert_eq!(restored_worktree_space_membership(Some(membership)), None); + } } diff --git a/src/persist/snapshot.rs b/src/persist/snapshot.rs index a3811c36..79d5c406 100644 --- a/src/persist/snapshot.rs +++ b/src/persist/snapshot.rs @@ -26,6 +26,8 @@ pub struct SessionSnapshot { pub sidebar_width: Option<u16>, #[serde(default)] pub sidebar_section_split: Option<f32>, + #[serde(default)] + pub collapsed_space_keys: std::collections::HashSet<String>, } #[derive(Serialize, Deserialize)] @@ -35,6 +37,8 @@ pub struct WorkspaceSnapshot { #[serde(default)] pub custom_name: Option<String>, pub identity_cwd: PathBuf, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub worktree_space: Option<crate::workspace::WorktreeSpaceMembership>, pub tabs: Vec<TabSnapshot>, #[serde(default)] pub active_tab: usize, @@ -109,6 +113,7 @@ impl From<LegacyWorkspaceSnapshot> for WorkspaceSnapshot { id: None, custom_name: snap.custom_name, identity_cwd, + worktree_space: None, tabs: vec![tab], active_tab: 0, } @@ -131,6 +136,8 @@ struct RawSessionSnapshot { sidebar_width: Option<u16>, #[serde(default)] sidebar_section_split: Option<f32>, + #[serde(default)] + collapsed_space_keys: std::collections::HashSet<String>, } fn migrate_snapshot(raw: RawSessionSnapshot) -> Result<SessionSnapshot, String> { @@ -146,6 +153,7 @@ fn migrate_snapshot(raw: RawSessionSnapshot) -> Result<SessionSnapshot, String> agent_panel_scope: raw.agent_panel_scope, sidebar_width: raw.sidebar_width, sidebar_section_split: raw.sidebar_section_split, + collapsed_space_keys: raw.collapsed_space_keys, }) } @@ -208,6 +216,7 @@ pub fn capture( agent_panel_scope: crate::app::state::AgentPanelScope, sidebar_width: u16, sidebar_section_split: f32, + collapsed_space_keys: std::collections::HashSet<String>, ) -> SessionSnapshot { SessionSnapshot { version: SNAPSHOT_VERSION, @@ -220,6 +229,7 @@ pub fn capture( agent_panel_scope, sidebar_width: Some(sidebar_width), sidebar_section_split: Some(sidebar_section_split), + collapsed_space_keys, } } @@ -237,6 +247,7 @@ fn capture_workspace( identity_cwd: ws .resolved_identity_cwd_from(terminals, terminal_runtimes) .unwrap_or_else(|| ws.identity_cwd.clone()), + worktree_space: ws.worktree_space.clone(), tabs: ws .tabs .iter() @@ -375,6 +386,7 @@ mod tests { state.agent_panel_scope, state.sidebar_width, state.sidebar_section_split, + state.collapsed_space_keys.clone(), ) } @@ -395,6 +407,7 @@ mod tests { agent_panel_scope: AgentPanelScope::CurrentWorkspace, sidebar_width: Some(26), sidebar_section_split: Some(0.5), + collapsed_space_keys: std::collections::HashSet::new(), }; let json = serde_json::to_string(&snap).unwrap(); let restored = parse_snapshot(&json).unwrap(); @@ -451,6 +464,7 @@ mod tests { id: Some("wproj".to_string()), custom_name: Some("pi-mono".to_string()), identity_cwd: PathBuf::from("/home/can/Projects/herdr"), + worktree_space: None, tabs: vec![TabSnapshot { custom_name: Some("api".to_string()), layout: LayoutSnapshot::Split { @@ -471,6 +485,7 @@ mod tests { agent_panel_scope: AgentPanelScope::CurrentWorkspace, sidebar_width: Some(26), sidebar_section_split: Some(0.5), + collapsed_space_keys: std::collections::HashSet::new(), version: SNAPSHOT_VERSION, }; @@ -622,11 +637,32 @@ mod tests { state.sidebar_width = 31; state.sidebar_section_split = 0.4; state.agent_panel_scope = AgentPanelScope::AllWorkspaces; + state.collapsed_space_keys.insert("repo-key".into()); let snapshot = capture_from_state(&state); assert_eq!(snapshot.sidebar_width, Some(31)); assert_eq!(snapshot.sidebar_section_split, Some(0.4)); assert_eq!(snapshot.agent_panel_scope, AgentPanelScope::AllWorkspaces); + assert!(snapshot.collapsed_space_keys.contains("repo-key")); + } + + #[test] + fn capture_contract_tracks_worktree_space_membership() { + let mut state = state_with_workspaces(&["main"]); + state.workspaces[0].worktree_space = Some(crate::workspace::WorktreeSpaceMembership { + key: "repo-key".into(), + label: "herdr".into(), + repo_root: PathBuf::from("/repo/herdr"), + checkout_path: PathBuf::from("/repo/herdr/worktree-a"), + is_linked_worktree: true, + }); + + let snapshot = capture_from_state(&state); + + assert_eq!( + snapshot.workspaces[0].worktree_space, + state.workspaces[0].worktree_space + ); } #[test] @@ -776,6 +812,7 @@ mod tests { id: Some("test-ws".to_string()), custom_name: Some("fallback test".to_string()), identity_cwd: PathBuf::from("/tmp"), + worktree_space: None, tabs: vec