diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index de2f2382e1..83eb5d27c5 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -802,6 +802,7 @@ impl ItemHandle for Entity { } ItemEvent::UpdateTab => { + workspace.update_item_dirty_state(item, window, cx); pane.update(cx, |_, cx| { cx.emit(pane::Event::ChangeItemTitle); cx.notify(); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 4614807156..d867b1f215 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -826,6 +826,7 @@ pub struct Workspace { follower_states: HashMap, last_leaders_by_pane: HashMap, PeerId>, window_edited: bool, + dirty_items: HashMap, active_call: Option<(Entity, Vec)>, leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>, database_id: Option, @@ -1128,6 +1129,7 @@ impl Workspace { last_leaders_by_pane: Default::default(), dispatching_keystrokes: Default::default(), window_edited: false, + dirty_items: Default::default(), active_call, database_id: workspace_id, app_state, @@ -3395,13 +3397,11 @@ impl Workspace { if *pane == self.active_pane { self.active_item_path_changed(window, cx); } - self.update_window_edited(window, cx); serialize_workspace = false; } pane::Event::RemoveItem { .. } => {} pane::Event::RemovedItem { item_id } => { cx.emit(Event::ActiveItemChanged); - self.update_window_edited(window, cx); if let hash_map::Entry::Occupied(entry) = self.panes_by_item.entry(*item_id) { if entry.get().entity_id() == pane.entity_id() { entry.remove(); @@ -3858,16 +3858,47 @@ impl Workspace { } fn update_window_edited(&mut self, window: &mut Window, cx: &mut App) { - let is_edited = !self.project.read(cx).is_disconnected(cx) - && self - .items(cx) - .any(|item| item.has_conflict(cx) || item.is_dirty(cx)); + let is_edited = !self.project.read(cx).is_disconnected(cx) && !self.dirty_items.is_empty(); if is_edited != self.window_edited { self.window_edited = is_edited; window.set_window_edited(self.window_edited) } } + fn update_item_dirty_state( + &mut self, + item: &dyn ItemHandle, + window: &mut Window, + cx: &mut App, + ) { + let is_dirty = item.is_dirty(cx); + let item_id = item.item_id(); + let was_dirty = self.dirty_items.contains_key(&item_id); + if is_dirty == was_dirty { + return; + } + if was_dirty { + self.dirty_items.remove(&item_id); + self.update_window_edited(window, cx); + return; + } + if let Some(window_handle) = window.window_handle().downcast::() { + let s = item.on_release( + cx, + Box::new(move |cx| { + window_handle + .update(cx, |this, window, cx| { + this.dirty_items.remove(&item_id); + this.update_window_edited(window, cx) + }) + .ok(); + }), + ); + self.dirty_items.insert(item_id, s); + self.update_window_edited(window, cx); + } + } + fn render_notifications(&self, _window: &mut Window, _cx: &mut Context) -> Option
{ if self.notifications.is_empty() { None diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 0344c2e82d..6aa23ad53e 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -2112,6 +2112,7 @@ mod tests { }) .unwrap(); assert!(window_is_edited(window, cx)); + let weak = editor.downgrade(); // Closing the item restores the window's edited state. let close = window @@ -2127,11 +2128,12 @@ mod tests { cx.simulate_prompt_answer("Don't Save"); close.await.unwrap(); - assert!(!window_is_edited(window, cx)); // Advance the clock to ensure that the item has been serialized and dropped from the queue cx.executor().advance_clock(Duration::from_secs(1)); + weak.assert_released(); + assert!(!window_is_edited(window, cx)); // Opening the buffer again doesn't impact the window's edited state. cx.update(|cx| { open_paths( @@ -2266,6 +2268,8 @@ mod tests { assert_eq!(cx.update(|cx| cx.windows().len()), 1); assert!(cx.update(|cx| cx.active_window().is_some())); + cx.run_until_parked(); + // When opening the workspace, the window is not in a edited state. let window = cx.update(|cx| cx.active_window().unwrap().downcast::().unwrap()); assert!(window_is_edited(window, cx));