diff --git a/src/app/actions.rs b/src/app/actions.rs index 5c4c2f75..0709ed4c 100644 --- a/src/app/actions.rs +++ b/src/app/actions.rs @@ -3113,7 +3113,8 @@ impl AppState { let sound = sound_for_toast_kind(kind, suppress_active_tab_notifications) .filter(|_| self.sound.allows(known_agent)); let build_toast = || { - let workspace_label = self.workspaces[ws_idx].display_name(); + let workspace_label = + self.workspaces[ws_idx].display_name_from_terminals(&self.terminals); let context = notification_context(&self.workspaces[ws_idx], &workspace_label, ws_idx, pane_id); ToastNotification { diff --git a/src/app/mod.rs b/src/app/mod.rs index 5e007091..c2d479b3 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -2412,6 +2412,21 @@ mod tests { assert!(app.state.toast.is_none()); } + #[test] + fn unchanged_git_status_drain_has_no_render_impact() { + let mut app = test_app(); + app.git_refresh_in_flight = true; + app.event_tx + .try_send(AppEvent::GitStatusRefreshed { + results: Vec::new(), + cache_updates: Vec::new(), + }) + .unwrap(); + + assert!(!app.drain_internal_events()); + assert!(!app.git_refresh_in_flight); + } + #[test] fn internal_event_drain_limits_work_per_tick() { let mut app = test_app(); diff --git a/src/app/runtime.rs b/src/app/runtime.rs index 02f3e947..180c3cef 100644 --- a/src/app/runtime.rs +++ b/src/app/runtime.rs @@ -586,26 +586,33 @@ impl App { pub(crate) fn drain_internal_events(&mut self) -> bool { self.drain_internal_events_up_to(super::APP_EVENT_DRAIN_LIMIT) + .1 } pub(crate) fn drain_all_internal_events(&mut self) -> bool { - let mut had_event = false; - while self.drain_internal_events_up_to(super::APP_EVENT_DRAIN_LIMIT) { - had_event = true; + let mut changed = false; + loop { + let (had_event, batch_changed) = + self.drain_internal_events_up_to(super::APP_EVENT_DRAIN_LIMIT); + changed |= batch_changed; + if !had_event { + break; + } } - had_event + changed } - fn drain_internal_events_up_to(&mut self, limit: usize) -> bool { + fn drain_internal_events_up_to(&mut self, limit: usize) -> (bool, bool) { let mut had_event = false; + let mut changed = false; for _ in 0..limit { let Ok(ev) = self.event_rx.try_recv() else { break; }; had_event = true; - self.handle_internal_event_with_prefix_sync(ev); + changed |= self.handle_internal_event_with_prefix_sync(ev); } - had_event + (had_event, changed) } } diff --git a/src/ui/sidebar.rs b/src/ui/sidebar.rs index 0ecfb652..c7da990a 100644 --- a/src/ui/sidebar.rs +++ b/src/ui/sidebar.rs @@ -197,12 +197,12 @@ fn workspace_row_height(app: &AppState, ws: &crate::workspace::Workspace, indent let (state, seen) = ws.aggregate_state(&app.terminals); let label = if indented { grouped_child_display_label( - &ws.display_name(), + &ws.display_name_from_terminals(&app.terminals), ws.branch().as_deref(), ws.custom_name.is_some(), ) } else { - ws.display_name() + ws.display_name_from_terminals(&app.terminals) }; let token_values = ws.metadata_tokens.values(); tokens::space_rows( diff --git a/src/workspace.rs b/src/workspace.rs index 62864536..546a294e 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -1093,16 +1093,31 @@ impl Workspace { .or_else(|| Some(self.identity_cwd.clone())) } + #[cfg(test)] pub fn display_name(&self) -> String { if let Some(name) = &self.custom_name { return name.clone(); } - if self.cached_identity_cwd == self.identity_cwd { - self.cached_auto_label.clone() - } else { - fallback_label_from_cwd(&self.identity_cwd) + self.automatic_display_name_for_cwd(&self.identity_cwd) + } + + pub(crate) fn display_name_from_terminals( + &self, + terminals: &HashMap, + ) -> String { + if let Some(name) = &self.custom_name { + return name.clone(); } + + let cwd = self + .tabs + .first() + .and_then(|tab| tab.terminal_id(tab.root_pane)) + .and_then(|terminal_id| terminals.get(terminal_id)) + .map(|terminal| &terminal.cwd) + .unwrap_or(&self.identity_cwd); + self.automatic_display_name_for_cwd(cwd) } pub fn display_name_from( @@ -1115,16 +1130,18 @@ impl Workspace { } self.resolved_identity_cwd_from(terminals, terminal_runtimes) - .map(|cwd| { - if cwd == self.cached_identity_cwd { - self.cached_auto_label.clone() - } else { - fallback_label_from_cwd(&cwd) - } - }) + .map(|cwd| self.automatic_display_name_for_cwd(&cwd)) .unwrap_or_else(|| "workspace".into()) } + fn automatic_display_name_for_cwd(&self, cwd: &std::path::Path) -> String { + if cwd == self.cached_identity_cwd { + self.cached_auto_label.clone() + } else { + fallback_label_from_cwd(cwd) + } + } + pub fn branch(&self) -> Option { self.cached_git_branch.clone() } @@ -1642,6 +1659,24 @@ mod tests { assert_eq!(ws.display_name(), "cached-repo"); } + #[test] + fn terminal_aware_display_name_uses_latest_admitted_identity_cache() { + let mut ws = Workspace::test_new("ignored"); + let root_pane = ws.tabs[0].root_pane; + let terminal_id = ws.tabs[0].terminal_id(root_pane).unwrap().clone(); + ws.custom_name = None; + ws.identity_cwd = PathBuf::from("/old/workspace"); + ws.cached_identity_cwd = PathBuf::from("/new/repo/deep"); + ws.cached_auto_label = "repo".into(); + let terminals = HashMap::from([( + terminal_id.clone(), + TerminalState::new(terminal_id, PathBuf::from("/new/repo/deep")), + )]); + + assert_eq!(ws.display_name_from_terminals(&terminals), "repo"); + assert_eq!(ws.display_name(), "workspace"); + } + #[test] fn workspace_identity_follows_first_tab_root_pane_cwd() { let mut ws = Workspace::test_new("ignored");