Fix save related tests, and refactor saves again

Conrad Irwin created

Change summary

crates/vim/src/command.rs         |  43 ++++---
crates/workspace/src/item.rs      |   6 
crates/workspace/src/pane.rs      | 195 ++++++++++++++++----------------
crates/workspace/src/workspace.rs |  47 ++-----
crates/zed/src/zed.rs             |  28 ++--
5 files changed, 156 insertions(+), 163 deletions(-)

Detailed changes

crates/vim/src/command.rs 🔗

@@ -2,7 +2,7 @@ use command_palette::CommandInterceptResult;
 use editor::{SortLinesCaseInsensitive, SortLinesCaseSensitive};
 use gpui::{impl_actions, Action, AppContext};
 use serde_derive::Deserialize;
-use workspace::{SaveBehavior, Workspace};
+use workspace::{SaveIntent, Workspace};
 
 use crate::{
     motion::{EndOfDocument, Motion},
@@ -50,112 +50,119 @@ pub fn command_interceptor(mut query: &str, _: &AppContext) -> Option<CommandInt
         "w" | "wr" | "wri" | "writ" | "write" => (
             "write",
             workspace::Save {
-                save_behavior: Some(SaveBehavior::PromptOnConflict),
+                save_behavior: Some(SaveIntent::Save),
             }
             .boxed_clone(),
         ),
         "w!" | "wr!" | "wri!" | "writ!" | "write!" => (
             "write!",
             workspace::Save {
-                save_behavior: Some(SaveBehavior::SilentlyOverwrite),
+                save_behavior: Some(SaveIntent::Overwrite),
             }
             .boxed_clone(),
         ),
         "q" | "qu" | "qui" | "quit" => (
             "quit",
             workspace::CloseActiveItem {
-                save_behavior: Some(SaveBehavior::PromptOnWrite),
+                save_behavior: Some(SaveIntent::Close),
             }
             .boxed_clone(),
         ),
         "q!" | "qu!" | "qui!" | "quit!" => (
             "quit!",
             workspace::CloseActiveItem {
-                save_behavior: Some(SaveBehavior::DontSave),
+                save_behavior: Some(SaveIntent::Skip),
             }
             .boxed_clone(),
         ),
         "wq" => (
             "wq",
             workspace::CloseActiveItem {
-                save_behavior: Some(SaveBehavior::PromptOnConflict),
+                save_behavior: Some(SaveIntent::Save),
             }
             .boxed_clone(),
         ),
         "wq!" => (
             "wq!",
             workspace::CloseActiveItem {
-                save_behavior: Some(SaveBehavior::SilentlyOverwrite),
+                save_behavior: Some(SaveIntent::Overwrite),
             }
             .boxed_clone(),
         ),
         "x" | "xi" | "xit" | "exi" | "exit" => (
             "exit",
             workspace::CloseActiveItem {
-                save_behavior: Some(SaveBehavior::PromptOnConflict),
+                save_behavior: Some(SaveIntent::Save),
             }
             .boxed_clone(),
         ),
         "x!" | "xi!" | "xit!" | "exi!" | "exit!" => (
             "exit!",
             workspace::CloseActiveItem {
-                save_behavior: Some(SaveBehavior::SilentlyOverwrite),
+                save_behavior: Some(SaveIntent::Overwrite),
+            }
+            .boxed_clone(),
+        ),
+        "up" | "upd" | "upda" | "updat" | "update" => (
+            "update",
+            workspace::Save {
+                save_behavior: Some(SaveIntent::SaveAll),
             }
             .boxed_clone(),
         ),
         "wa" | "wal" | "wall" => (
             "wall",
             workspace::SaveAll {
-                save_behavior: Some(SaveBehavior::PromptOnConflict),
+                save_behavior: Some(SaveIntent::SaveAll),
             }
             .boxed_clone(),
         ),
         "wa!" | "wal!" | "wall!" => (
             "wall!",
             workspace::SaveAll {
-                save_behavior: Some(SaveBehavior::SilentlyOverwrite),
+                save_behavior: Some(SaveIntent::Overwrite),
             }
             .boxed_clone(),
         ),
         "qa" | "qal" | "qall" | "quita" | "quital" | "quitall" => (
             "quitall",
             workspace::CloseAllItemsAndPanes {
-                save_behavior: Some(SaveBehavior::PromptOnWrite),
+                save_behavior: Some(SaveIntent::Close),
             }
             .boxed_clone(),
         ),
         "qa!" | "qal!" | "qall!" | "quita!" | "quital!" | "quitall!" => (
             "quitall!",
             workspace::CloseAllItemsAndPanes {
-                save_behavior: Some(SaveBehavior::DontSave),
+                save_behavior: Some(SaveIntent::Skip),
             }
             .boxed_clone(),
         ),
         "xa" | "xal" | "xall" => (
             "xall",
             workspace::CloseAllItemsAndPanes {
-                save_behavior: Some(SaveBehavior::PromptOnConflict),
+                save_behavior: Some(SaveIntent::SaveAll),
             }
             .boxed_clone(),
         ),
         "xa!" | "xal!" | "xall!" => (
             "xall!",
             workspace::CloseAllItemsAndPanes {
-                save_behavior: Some(SaveBehavior::SilentlyOverwrite),
+                save_behavior: Some(SaveIntent::Overwrite),
             }
             .boxed_clone(),
         ),
         "wqa" | "wqal" | "wqall" => (
             "wqall",
             workspace::CloseAllItemsAndPanes {
-                save_behavior: Some(SaveBehavior::PromptOnConflict),
+                save_behavior: Some(SaveIntent::SaveAll),
             }
             .boxed_clone(),
         ),
         "wqa!" | "wqal!" | "wqall!" => (
             "wqall!",
             workspace::CloseAllItemsAndPanes {
-                save_behavior: Some(SaveBehavior::SilentlyOverwrite),
+                save_behavior: Some(SaveIntent::Overwrite),
             }
             .boxed_clone(),
         ),
@@ -190,7 +197,7 @@ pub fn command_interceptor(mut query: &str, _: &AppContext) -> Option<CommandInt
         "tabc" | "tabcl" | "tabclo" | "tabclos" | "tabclose" => (
             "tabclose",
             workspace::CloseActiveItem {
-                save_behavior: Some(SaveBehavior::PromptOnWrite),
+                save_behavior: Some(SaveIntent::Close),
             }
             .boxed_clone(),
         ),

crates/workspace/src/item.rs 🔗

@@ -475,11 +475,7 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
                         match item_event {
                             ItemEvent::CloseItem => {
                                 pane.update(cx, |pane, cx| {
-                                    pane.close_item_by_id(
-                                        item.id(),
-                                        crate::SaveBehavior::PromptOnWrite,
-                                        cx,
-                                    )
+                                    pane.close_item_by_id(item.id(), crate::SaveIntent::Close, cx)
                                 })
                                 .detach_and_log_err(cx);
                                 return;

crates/workspace/src/pane.rs 🔗

@@ -45,17 +45,21 @@ use theme::{Theme, ThemeSettings};
 
 #[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
 #[serde(rename_all = "camelCase")]
-pub enum SaveBehavior {
-    /// ask before overwriting conflicting files (used by default with cmd-s)
-    PromptOnConflict,
-    /// ask for a new path before writing (used with cmd-shift-s)
-    PromptForNewPath,
-    /// ask before writing any file that wouldn't be auto-saved (used by default with cmd-w)
-    PromptOnWrite,
-    /// never prompt, write on conflict (used with vim's :w!)
-    SilentlyOverwrite,
-    /// skip all save-related behaviour (used with vim's :q!)
-    DontSave,
+pub enum SaveIntent {
+    /// write all files (even if unchanged)
+    /// prompt before overwriting on-disk changes
+    Save,
+    /// write any files that have local changes
+    /// prompt before overwriting on-disk changes
+    SaveAll,
+    /// always prompt for a new path
+    SaveAs,
+    /// prompt "you have unsaved changes" before writing
+    Close,
+    /// write all dirty files, don't prompt on conflict
+    Overwrite,
+    /// skip all save-related behavior
+    Skip,
 }
 
 #[derive(Clone, Deserialize, PartialEq)]
@@ -82,13 +86,13 @@ pub struct CloseItemsToTheRightById {
 #[derive(Clone, PartialEq, Debug, Deserialize, Default)]
 #[serde(rename_all = "camelCase")]
 pub struct CloseActiveItem {
-    pub save_behavior: Option<SaveBehavior>,
+    pub save_behavior: Option<SaveIntent>,
 }
 
 #[derive(Clone, PartialEq, Debug, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct CloseAllItems {
-    pub save_behavior: Option<SaveBehavior>,
+    pub save_behavior: Option<SaveIntent>,
 }
 
 actions!(
@@ -730,7 +734,7 @@ impl Pane {
         let active_item_id = self.items[self.active_item_index].id();
         Some(self.close_item_by_id(
             active_item_id,
-            action.save_behavior.unwrap_or(SaveBehavior::PromptOnWrite),
+            action.save_behavior.unwrap_or(SaveIntent::Close),
             cx,
         ))
     }
@@ -738,7 +742,7 @@ impl Pane {
     pub fn close_item_by_id(
         &mut self,
         item_id_to_close: usize,
-        save_behavior: SaveBehavior,
+        save_behavior: SaveIntent,
         cx: &mut ViewContext<Self>,
     ) -> Task<Result<()>> {
         self.close_items(cx, save_behavior, move |view_id| {
@@ -756,11 +760,9 @@ impl Pane {
         }
 
         let active_item_id = self.items[self.active_item_index].id();
-        Some(
-            self.close_items(cx, SaveBehavior::PromptOnWrite, move |item_id| {
-                item_id != active_item_id
-            }),
-        )
+        Some(self.close_items(cx, SaveIntent::Close, move |item_id| {
+            item_id != active_item_id
+        }))
     }
 
     pub fn close_clean_items(
@@ -773,11 +775,9 @@ impl Pane {
             .filter(|item| !item.is_dirty(cx))
             .map(|item| item.id())
             .collect();
-        Some(
-            self.close_items(cx, SaveBehavior::PromptOnWrite, move |item_id| {
-                item_ids.contains(&item_id)
-            }),
-        )
+        Some(self.close_items(cx, SaveIntent::Close, move |item_id| {
+            item_ids.contains(&item_id)
+        }))
     }
 
     pub fn close_items_to_the_left(
@@ -802,7 +802,7 @@ impl Pane {
             .take_while(|item| item.id() != item_id)
             .map(|item| item.id())
             .collect();
-        self.close_items(cx, SaveBehavior::PromptOnWrite, move |item_id| {
+        self.close_items(cx, SaveIntent::Close, move |item_id| {
             item_ids.contains(&item_id)
         })
     }
@@ -830,7 +830,7 @@ impl Pane {
             .take_while(|item| item.id() != item_id)
             .map(|item| item.id())
             .collect();
-        self.close_items(cx, SaveBehavior::PromptOnWrite, move |item_id| {
+        self.close_items(cx, SaveIntent::Close, move |item_id| {
             item_ids.contains(&item_id)
         })
     }
@@ -846,7 +846,7 @@ impl Pane {
 
         Some(self.close_items(
             cx,
-            action.save_behavior.unwrap_or(SaveBehavior::PromptOnWrite),
+            action.save_behavior.unwrap_or(SaveIntent::Close),
             |_| true,
         ))
     }
@@ -854,7 +854,7 @@ impl Pane {
     pub fn close_items(
         &mut self,
         cx: &mut ViewContext<Pane>,
-        save_behavior: SaveBehavior,
+        save_behavior: SaveIntent,
         should_close: impl 'static + Fn(usize) -> bool,
     ) -> Task<Result<()>> {
         // Find the items to close.
@@ -1010,18 +1010,18 @@ impl Pane {
         pane: &WeakViewHandle<Pane>,
         item_ix: usize,
         item: &dyn ItemHandle,
-        save_behavior: SaveBehavior,
+        save_behavior: SaveIntent,
         cx: &mut AsyncAppContext,
     ) -> Result<bool> {
         const CONFLICT_MESSAGE: &str =
             "This file has changed on disk since you started editing it. Do you want to overwrite it?";
         const DIRTY_MESSAGE: &str = "This file contains unsaved edits. Do you want to save it?";
 
-        if save_behavior == SaveBehavior::DontSave {
+        if save_behavior == SaveIntent::Skip {
             return Ok(true);
         }
 
-        let (mut has_conflict, mut is_dirty, mut can_save, is_singleton) = cx.read(|cx| {
+        let (mut has_conflict, mut is_dirty, mut can_save, can_save_as) = cx.read(|cx| {
             (
                 item.has_conflict(cx),
                 item.is_dirty(cx),
@@ -1030,73 +1030,76 @@ impl Pane {
             )
         });
 
-        if save_behavior == SaveBehavior::PromptForNewPath {
-            has_conflict = false;
+        // when saving a single buffer, we ignore whether or not it's dirty.
+        if save_behavior == SaveIntent::Save {
             is_dirty = true;
+        }
+
+        if save_behavior == SaveIntent::SaveAs {
+            is_dirty = true;
+            has_conflict = false;
             can_save = false;
         }
 
+        if save_behavior == SaveIntent::Overwrite {
+            has_conflict = false;
+        }
+
         if has_conflict && can_save {
-            if save_behavior == SaveBehavior::SilentlyOverwrite {
-                pane.update(cx, |_, cx| item.save(project, cx))?.await?;
-            } else {
-                let mut answer = pane.update(cx, |pane, cx| {
-                    pane.activate_item(item_ix, true, true, cx);
-                    cx.prompt(
-                        PromptLevel::Warning,
-                        CONFLICT_MESSAGE,
-                        &["Overwrite", "Discard", "Cancel"],
-                    )
-                })?;
-                match answer.next().await {
-                    Some(0) => pane.update(cx, |_, cx| item.save(project, cx))?.await?,
-                    Some(1) => pane.update(cx, |_, cx| item.reload(project, cx))?.await?,
-                    _ => return Ok(false),
-                }
+            let mut answer = pane.update(cx, |pane, cx| {
+                pane.activate_item(item_ix, true, true, cx);
+                cx.prompt(
+                    PromptLevel::Warning,
+                    CONFLICT_MESSAGE,
+                    &["Overwrite", "Discard", "Cancel"],
+                )
+            })?;
+            match answer.next().await {
+                Some(0) => pane.update(cx, |_, cx| item.save(project, cx))?.await?,
+                Some(1) => pane.update(cx, |_, cx| item.reload(project, cx))?.await?,
+                _ => return Ok(false),
             }
-        } else if is_dirty && (can_save || is_singleton) {
-            let will_autosave = cx.read(|cx| {
-                matches!(
-                    settings::get::<WorkspaceSettings>(cx).autosave,
-                    AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange
-                ) && Self::can_autosave_item(&*item, cx)
-            });
-            let should_save = if save_behavior == SaveBehavior::PromptOnWrite && !will_autosave {
-                let mut answer = pane.update(cx, |pane, cx| {
-                    pane.activate_item(item_ix, true, true, cx);
-                    cx.prompt(
-                        PromptLevel::Warning,
-                        DIRTY_MESSAGE,
-                        &["Save", "Don't Save", "Cancel"],
-                    )
-                })?;
-                match answer.next().await {
-                    Some(0) => true,
-                    Some(1) => false,
-                    _ => return Ok(false),
+        } else if is_dirty && (can_save || can_save_as) {
+            if save_behavior == SaveIntent::Close {
+                let will_autosave = cx.read(|cx| {
+                    matches!(
+                        settings::get::<WorkspaceSettings>(cx).autosave,
+                        AutosaveSetting::OnFocusChange | AutosaveSetting::OnWindowChange
+                    ) && Self::can_autosave_item(&*item, cx)
+                });
+                if !will_autosave {
+                    let mut answer = pane.update(cx, |pane, cx| {
+                        pane.activate_item(item_ix, true, true, cx);
+                        cx.prompt(
+                            PromptLevel::Warning,
+                            DIRTY_MESSAGE,
+                            &["Save", "Don't Save", "Cancel"],
+                        )
+                    })?;
+                    match answer.next().await {
+                        Some(0) => {}
+                        Some(1) => return Ok(true), // Don't save this file
+                        _ => return Ok(false),      // Cancel
+                    }
                 }
-            } else {
-                true
-            };
+            }
 
-            if should_save {
-                if can_save {
-                    pane.update(cx, |_, cx| item.save(project, cx))?.await?;
-                } else if is_singleton {
-                    let start_abs_path = project
-                        .read_with(cx, |project, cx| {
-                            let worktree = project.visible_worktrees(cx).next()?;
-                            Some(worktree.read(cx).as_local()?.abs_path().to_path_buf())
-                        })
-                        .unwrap_or_else(|| Path::new("").into());
+            if can_save {
+                pane.update(cx, |_, cx| item.save(project, cx))?.await?;
+            } else if can_save_as {
+                let start_abs_path = project
+                    .read_with(cx, |project, cx| {
+                        let worktree = project.visible_worktrees(cx).next()?;
+                        Some(worktree.read(cx).as_local()?.abs_path().to_path_buf())
+                    })
+                    .unwrap_or_else(|| Path::new("").into());
 
-                    let mut abs_path = cx.update(|cx| cx.prompt_for_new_path(&start_abs_path));
-                    if let Some(abs_path) = abs_path.next().await.flatten() {
-                        pane.update(cx, |_, cx| item.save_as(project, abs_path, cx))?
-                            .await?;
-                    } else {
-                        return Ok(false);
-                    }
+                let mut abs_path = cx.update(|cx| cx.prompt_for_new_path(&start_abs_path));
+                if let Some(abs_path) = abs_path.next().await.flatten() {
+                    pane.update(cx, |_, cx| item.save_as(project, abs_path, cx))?
+                        .await?;
+                } else {
+                    return Ok(false);
                 }
             }
         }
@@ -1210,7 +1213,7 @@ impl Pane {
                                     pane.update(cx, |pane, cx| {
                                         pane.close_item_by_id(
                                             target_item_id,
-                                            SaveBehavior::PromptOnWrite,
+                                            SaveIntent::Close,
                                             cx,
                                         )
                                         .detach_and_log_err(cx);
@@ -1367,12 +1370,8 @@ impl Pane {
                                 .on_click(MouseButton::Middle, {
                                     let item_id = item.id();
                                     move |_, pane, cx| {
-                                        pane.close_item_by_id(
-                                            item_id,
-                                            SaveBehavior::PromptOnWrite,
-                                            cx,
-                                        )
-                                        .detach_and_log_err(cx);
+                                        pane.close_item_by_id(item_id, SaveIntent::Close, cx)
+                                            .detach_and_log_err(cx);
                                     }
                                 })
                                 .on_down(
@@ -1580,7 +1579,7 @@ impl Pane {
                     cx.window_context().defer(move |cx| {
                         if let Some(pane) = pane.upgrade(cx) {
                             pane.update(cx, |pane, cx| {
-                                pane.close_item_by_id(item_id, SaveBehavior::PromptOnWrite, cx)
+                                pane.close_item_by_id(item_id, SaveIntent::Close, cx)
                                     .detach_and_log_err(cx);
                             });
                         }

crates/workspace/src/workspace.rs 🔗

@@ -163,19 +163,19 @@ pub struct NewFileInDirection(pub SplitDirection);
 #[derive(Clone, PartialEq, Debug, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct SaveAll {
-    pub save_behavior: Option<SaveBehavior>,
+    pub save_behavior: Option<SaveIntent>,
 }
 
 #[derive(Clone, PartialEq, Debug, Deserialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Save {
-    pub save_behavior: Option<SaveBehavior>,
+    pub save_behavior: Option<SaveIntent>,
 }
 
 #[derive(Clone, PartialEq, Debug, Deserialize, Default)]
 #[serde(rename_all = "camelCase")]
 pub struct CloseAllItemsAndPanes {
-    pub save_behavior: Option<SaveBehavior>,
+    pub save_behavior: Option<SaveIntent>,
 }
 
 #[derive(Deserialize)]
@@ -294,19 +294,14 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
     cx.add_action(
         |workspace: &mut Workspace, action: &Save, cx: &mut ViewContext<Workspace>| {
             workspace
-                .save_active_item(
-                    action
-                        .save_behavior
-                        .unwrap_or(SaveBehavior::PromptOnConflict),
-                    cx,
-                )
+                .save_active_item(action.save_behavior.unwrap_or(SaveIntent::Save), cx)
                 .detach_and_log_err(cx);
         },
     );
     cx.add_action(
         |workspace: &mut Workspace, _: &SaveAs, cx: &mut ViewContext<Workspace>| {
             workspace
-                .save_active_item(SaveBehavior::PromptForNewPath, cx)
+                .save_active_item(SaveIntent::SaveAs, cx)
                 .detach_and_log_err(cx);
         },
     );
@@ -1356,7 +1351,7 @@ impl Workspace {
 
             Ok(this
                 .update(&mut cx, |this, cx| {
-                    this.save_all_internal(SaveBehavior::PromptOnWrite, cx)
+                    this.save_all_internal(SaveIntent::Close, cx)
                 })?
                 .await?)
         })
@@ -1367,12 +1362,8 @@ impl Workspace {
         action: &SaveAll,
         cx: &mut ViewContext<Self>,
     ) -> Option<Task<Result<()>>> {
-        let save_all = self.save_all_internal(
-            action
-                .save_behavior
-                .unwrap_or(SaveBehavior::PromptOnConflict),
-            cx,
-        );
+        let save_all =
+            self.save_all_internal(action.save_behavior.unwrap_or(SaveIntent::SaveAll), cx);
         Some(cx.foreground().spawn(async move {
             save_all.await?;
             Ok(())
@@ -1381,7 +1372,7 @@ impl Workspace {
 
     fn save_all_internal(
         &mut self,
-        save_behaviour: SaveBehavior,
+        save_behaviour: SaveIntent,
         cx: &mut ViewContext<Self>,
     ) -> Task<Result<bool>> {
         if self.project.read(cx).is_read_only() {
@@ -1688,7 +1679,7 @@ impl Workspace {
 
     pub fn save_active_item(
         &mut self,
-        save_behavior: SaveBehavior,
+        save_behavior: SaveIntent,
         cx: &mut ViewContext<Self>,
     ) -> Task<Result<()>> {
         let project = self.project.clone();
@@ -1720,7 +1711,7 @@ impl Workspace {
         _: &CloseInactiveTabsAndPanes,
         cx: &mut ViewContext<Self>,
     ) -> Option<Task<Result<()>>> {
-        self.close_all_internal(true, SaveBehavior::PromptOnWrite, cx)
+        self.close_all_internal(true, SaveIntent::Close, cx)
     }
 
     pub fn close_all_items_and_panes(
@@ -1728,17 +1719,13 @@ impl Workspace {
         action: &CloseAllItemsAndPanes,
         cx: &mut ViewContext<Self>,
     ) -> Option<Task<Result<()>>> {
-        self.close_all_internal(
-            false,
-            action.save_behavior.unwrap_or(SaveBehavior::PromptOnWrite),
-            cx,
-        )
+        self.close_all_internal(false, action.save_behavior.unwrap_or(SaveIntent::Close), cx)
     }
 
     fn close_all_internal(
         &mut self,
         retain_active_pane: bool,
-        save_behavior: SaveBehavior,
+        save_behavior: SaveIntent,
         cx: &mut ViewContext<Self>,
     ) -> Option<Task<Result<()>>> {
         let current_pane = self.active_pane();
@@ -4433,7 +4420,7 @@ mod tests {
             let item1_id = item1.id();
             let item3_id = item3.id();
             let item4_id = item4.id();
-            pane.close_items(cx, SaveBehavior::PromptOnWrite, move |id| {
+            pane.close_items(cx, SaveIntent::Close, move |id| {
                 [item1_id, item3_id, item4_id].contains(&id)
             })
         });
@@ -4571,7 +4558,7 @@ mod tests {
         // prompts, the task should complete.
 
         let close = left_pane.update(cx, |pane, cx| {
-            pane.close_items(cx, SaveBehavior::PromptOnWrite, move |_| true)
+            pane.close_items(cx, SaveIntent::Close, move |_| true)
         });
         cx.foreground().run_until_parked();
         left_pane.read_with(cx, |pane, cx| {
@@ -4689,7 +4676,7 @@ mod tests {
         });
 
         pane.update(cx, |pane, cx| {
-            pane.close_items(cx, SaveBehavior::PromptOnWrite, move |id| id == item_id)
+            pane.close_items(cx, SaveIntent::Close, move |id| id == item_id)
         })
         .await
         .unwrap();
@@ -4712,7 +4699,7 @@ mod tests {
 
         // Ensure autosave is prevented for deleted files also when closing the buffer.
         let _close_items = pane.update(cx, |pane, cx| {
-            pane.close_items(cx, SaveBehavior::PromptOnWrite, move |id| id == item_id)
+            pane.close_items(cx, SaveIntent::Close, move |id| id == item_id)
         });
         deterministic.run_until_parked();
         assert!(window.has_pending_prompt(cx));

crates/zed/src/zed.rs 🔗

@@ -744,7 +744,7 @@ mod tests {
     use theme::{ThemeRegistry, ThemeSettings};
     use workspace::{
         item::{Item, ItemHandle},
-        open_new, open_paths, pane, NewFile, SaveBehavior, SplitDirection, WorkspaceHandle,
+        open_new, open_paths, pane, NewFile, SaveIntent, SplitDirection, WorkspaceHandle,
     };
 
     #[gpui::test]
@@ -945,12 +945,14 @@ mod tests {
 
         editor.update(cx, |editor, cx| {
             assert!(editor.text(cx).is_empty());
+            assert!(!editor.is_dirty(cx));
         });
 
         let save_task = workspace.update(cx, |workspace, cx| {
-            workspace.save_active_item(SaveBehavior::PromptOnConflict, cx)
+            workspace.save_active_item(SaveIntent::Save, cx)
         });
         app_state.fs.create_dir(Path::new("/root")).await.unwrap();
+        cx.foreground().run_until_parked();
         cx.simulate_new_path_selection(|_| Some(PathBuf::from("/root/the-new-name")));
         save_task.await.unwrap();
         editor.read_with(cx, |editor, cx| {
@@ -1314,7 +1316,7 @@ mod tests {
         cx.read(|cx| assert!(editor.is_dirty(cx)));
 
         let save_task = workspace.update(cx, |workspace, cx| {
-            workspace.save_active_item(SaveBehavior::PromptOnConflict, cx)
+            workspace.save_active_item(SaveIntent::Save, cx)
         });
         window.simulate_prompt_answer(0, cx);
         save_task.await.unwrap();
@@ -1358,8 +1360,9 @@ mod tests {
 
         // Save the buffer. This prompts for a filename.
         let save_task = workspace.update(cx, |workspace, cx| {
-            workspace.save_active_item(SaveBehavior::PromptOnConflict, cx)
+            workspace.save_active_item(SaveIntent::Save, cx)
         });
+        cx.foreground().run_until_parked();
         cx.simulate_new_path_selection(|parent_dir| {
             assert_eq!(parent_dir, Path::new("/root"));
             Some(parent_dir.join("the-new-name.rs"))
@@ -1384,7 +1387,7 @@ mod tests {
             assert!(editor.is_dirty(cx));
         });
         let save_task = workspace.update(cx, |workspace, cx| {
-            workspace.save_active_item(SaveBehavior::PromptOnConflict, cx)
+            workspace.save_active_item(SaveIntent::Save, cx)
         });
         save_task.await.unwrap();
         assert!(!cx.did_prompt_for_new_path());
@@ -1453,8 +1456,9 @@ mod tests {
 
         // Save the buffer. This prompts for a filename.
         let save_task = workspace.update(cx, |workspace, cx| {
-            workspace.save_active_item(SaveBehavior::PromptOnConflict, cx)
+            workspace.save_active_item(SaveIntent::Save, cx)
         });
+        cx.foreground().run_until_parked();
         cx.simulate_new_path_selection(|_| Some(PathBuf::from("/root/the-new-name.rs")));
         save_task.await.unwrap();
         // The buffer is not dirty anymore and the language is assigned based on the path.
@@ -1692,7 +1696,7 @@ mod tests {
         pane.update(cx, |pane, cx| {
             let editor3_id = editor3.id();
             drop(editor3);
-            pane.close_item_by_id(editor3_id, SaveBehavior::PromptOnWrite, cx)
+            pane.close_item_by_id(editor3_id, SaveIntent::Close, cx)
         })
         .await
         .unwrap();
@@ -1727,7 +1731,7 @@ mod tests {
         pane.update(cx, |pane, cx| {
             let editor2_id = editor2.id();
             drop(editor2);
-            pane.close_item_by_id(editor2_id, SaveBehavior::PromptOnWrite, cx)
+            pane.close_item_by_id(editor2_id, SaveIntent::Close, cx)
         })
         .await
         .unwrap();
@@ -1884,28 +1888,28 @@ mod tests {
 
         // Close all the pane items in some arbitrary order.
         pane.update(cx, |pane, cx| {
-            pane.close_item_by_id(file1_item_id, SaveBehavior::PromptOnWrite, cx)
+            pane.close_item_by_id(file1_item_id, SaveIntent::Close, cx)
         })
         .await
         .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file4.clone()));
 
         pane.update(cx, |pane, cx| {
-            pane.close_item_by_id(file4_item_id, SaveBehavior::PromptOnWrite, cx)
+            pane.close_item_by_id(file4_item_id, SaveIntent::Close, cx)
         })
         .await
         .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file3.clone()));
 
         pane.update(cx, |pane, cx| {
-            pane.close_item_by_id(file2_item_id, SaveBehavior::PromptOnWrite, cx)
+            pane.close_item_by_id(file2_item_id, SaveIntent::Close, cx)
         })
         .await
         .unwrap();
         assert_eq!(active_path(&workspace, cx), Some(file3.clone()));
 
         pane.update(cx, |pane, cx| {
-            pane.close_item_by_id(file3_item_id, SaveBehavior::PromptOnWrite, cx)
+            pane.close_item_by_id(file3_item_id, SaveIntent::Close, cx)
         })
         .await
         .unwrap();