diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index bdf7ab1796..e80cd8a87f 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -16,6 +16,7 @@ "cmd-w": "pane::CloseActiveItem", "alt-cmd-w": "pane::CloseInactiveItems", "cmd-s": "workspace::Save", + "cmd-shift-S": "workspace::SaveAs", "cmd-=": "zed::IncreaseBufferFontSize", "cmd--": "zed::DecreaseBufferFontSize", "cmd-,": "zed::OpenSettings", diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d5ec34ad69..3a4ee0f211 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -80,6 +80,7 @@ actions!( AddFolderToProject, Unfollow, Save, + SaveAs, ActivatePreviousPane, ActivateNextPane, FollowNextCollaborator, @@ -150,7 +151,12 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { ); cx.add_action( |workspace: &mut Workspace, _: &Save, cx: &mut ViewContext| { - workspace.save_active_item(cx).detach_and_log_err(cx); + workspace.save_active_item(false, cx).detach_and_log_err(cx); + }, + ); + cx.add_action( + |workspace: &mut Workspace, _: &SaveAs, cx: &mut ViewContext| { + workspace.save_active_item(true, cx).detach_and_log_err(cx); }, ); cx.add_action(Workspace::toggle_sidebar_item); @@ -1064,10 +1070,14 @@ impl Workspace { self.active_item(cx).and_then(|item| item.project_path(cx)) } - pub fn save_active_item(&mut self, cx: &mut ViewContext) -> Task> { + pub fn save_active_item( + &mut self, + force_name_change: bool, + cx: &mut ViewContext, + ) -> Task> { let project = self.project.clone(); if let Some(item) = self.active_item(cx) { - if item.can_save(cx) { + if !force_name_change && item.can_save(cx) { if item.has_conflict(cx.as_ref()) { const CONFLICT_MESSAGE: &'static str = "This file has changed on disk since you started editing it. Do you want to overwrite it?"; diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index 6267ac77c9..b7aabffd18 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -50,6 +50,10 @@ pub fn menus() -> Vec> { name: "Save", action: Box::new(workspace::Save), }, + MenuItem::Action { + name: "Save As…", + action: Box::new(workspace::SaveAs), + }, MenuItem::Action { name: "Close Editor", action: Box::new(workspace::CloseActiveItem), diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index b3d831c9cc..c2f6c60ea6 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -391,7 +391,7 @@ mod tests { assert!(editor.text(cx).is_empty()); }); - let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(cx)); + let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(false, cx)); app_state.fs.as_fake().insert_dir("/root").await; cx.simulate_new_path_selection(|_| Some(PathBuf::from("/root/the-new-name"))); save_task.await.unwrap(); @@ -666,7 +666,7 @@ mod tests { .await; cx.read(|cx| assert!(editor.is_dirty(cx))); - let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(cx)); + let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(false, cx)); cx.simulate_prompt_answer(window_id, 0); save_task.await.unwrap(); editor.read_with(cx, |editor, cx| { @@ -707,7 +707,7 @@ mod tests { }); // Save the buffer. This prompts for a filename. - let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(cx)); + let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(false, cx)); cx.simulate_new_path_selection(|parent_dir| { assert_eq!(parent_dir, Path::new("/root")); Some(parent_dir.join("the-new-name.rs")) @@ -731,7 +731,7 @@ mod tests { editor.handle_input(&editor::Input(" there".into()), cx); assert_eq!(editor.is_dirty(cx.as_ref()), true); }); - let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(cx)); + let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(false, cx)); save_task.await.unwrap(); assert!(!cx.did_prompt_for_new_path()); editor.read_with(cx, |editor, cx| { @@ -793,7 +793,7 @@ mod tests { }); // Save the buffer. This prompts for a filename. - let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(cx)); + let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(false, cx)); 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.