Merge Workspace::save_item into Pane::save_item
These methods were slightly different which caused (for example) there to be no "Discard" option in the conflict case at the workspace level. To make this work, a new SaveBehavior (::PromptForNewPath) was added to support SaveAs.
This commit is contained in:
parent
a4f96e6452
commit
88a32ae48d
2 changed files with 58 additions and 64 deletions
|
@ -46,13 +46,15 @@ use theme::{Theme, ThemeSettings};
|
||||||
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
|
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum SaveBehavior {
|
pub enum SaveBehavior {
|
||||||
/// ask before overwriting conflicting files (used by default with %s)
|
/// ask before overwriting conflicting files (used by default with cmd-s)
|
||||||
PromptOnConflict,
|
PromptOnConflict,
|
||||||
/// ask before writing any file that wouldn't be auto-saved (used by default with %w)
|
/// 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,
|
PromptOnWrite,
|
||||||
/// never prompt, write on conflict (used with vim's :w!)
|
/// never prompt, write on conflict (used with vim's :w!)
|
||||||
SilentlyOverwrite,
|
SilentlyOverwrite,
|
||||||
/// skip all save-related behaviour (used with vim's :cq)
|
/// skip all save-related behaviour (used with vim's :q!)
|
||||||
DontSave,
|
DontSave,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1019,7 +1021,7 @@ impl Pane {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (has_conflict, is_dirty, can_save, is_singleton) = cx.read(|cx| {
|
let (mut has_conflict, mut is_dirty, mut can_save, is_singleton) = cx.read(|cx| {
|
||||||
(
|
(
|
||||||
item.has_conflict(cx),
|
item.has_conflict(cx),
|
||||||
item.is_dirty(cx),
|
item.is_dirty(cx),
|
||||||
|
@ -1028,6 +1030,12 @@ impl Pane {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if save_behavior == SaveBehavior::PromptForNewPath {
|
||||||
|
has_conflict = false;
|
||||||
|
is_dirty = true;
|
||||||
|
can_save = false;
|
||||||
|
}
|
||||||
|
|
||||||
if has_conflict && can_save {
|
if has_conflict && can_save {
|
||||||
if save_behavior == SaveBehavior::SilentlyOverwrite {
|
if save_behavior == SaveBehavior::SilentlyOverwrite {
|
||||||
pane.update(cx, |_, cx| item.save(project, cx))?.await?;
|
pane.update(cx, |_, cx| item.save(project, cx))?.await?;
|
||||||
|
@ -2589,10 +2597,17 @@ mod tests {
|
||||||
add_labeled_item(&pane, "C", false, cx);
|
add_labeled_item(&pane, "C", false, cx);
|
||||||
assert_item_labels(&pane, ["A", "B", "C*"], cx);
|
assert_item_labels(&pane, ["A", "B", "C*"], cx);
|
||||||
|
|
||||||
pane.update(cx, |pane, cx| pane.close_all_items(&CloseAllItems, cx))
|
pane.update(cx, |pane, cx| {
|
||||||
.unwrap()
|
pane.close_all_items(
|
||||||
.await
|
&CloseAllItems {
|
||||||
.unwrap();
|
save_behavior: None,
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
assert_item_labels(&pane, [], cx);
|
assert_item_labels(&pane, [], cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,7 @@ actions!(
|
||||||
Open,
|
Open,
|
||||||
NewFile,
|
NewFile,
|
||||||
NewWindow,
|
NewWindow,
|
||||||
|
CloseWindow,
|
||||||
CloseInactiveTabsAndPanes,
|
CloseInactiveTabsAndPanes,
|
||||||
AddFolderToProject,
|
AddFolderToProject,
|
||||||
Unfollow,
|
Unfollow,
|
||||||
|
@ -168,12 +169,6 @@ pub struct Save {
|
||||||
pub save_behavior: Option<SaveBehavior>,
|
pub save_behavior: Option<SaveBehavior>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct CloseWindow {
|
|
||||||
pub save_behavior: Option<SaveBehavior>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
|
#[derive(Clone, PartialEq, Debug, Deserialize, Default)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CloseAllItemsAndPanes {
|
pub struct CloseAllItemsAndPanes {
|
||||||
|
@ -236,10 +231,9 @@ impl_actions!(
|
||||||
ActivatePane,
|
ActivatePane,
|
||||||
ActivatePaneInDirection,
|
ActivatePaneInDirection,
|
||||||
Toast,
|
Toast,
|
||||||
OpenTerminal,
|
OpenTerminal,
|
||||||
SaveAll,
|
SaveAll,
|
||||||
Save,
|
Save,
|
||||||
CloseWindow,
|
|
||||||
CloseAllItemsAndPanes,
|
CloseAllItemsAndPanes,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -294,13 +288,22 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
cx.add_action(
|
cx.add_action(
|
||||||
|workspace: &mut Workspace, _: &Save, cx: &mut ViewContext<Workspace>| {
|
|workspace: &mut Workspace, action: &Save, cx: &mut ViewContext<Workspace>| {
|
||||||
workspace.save_active_item(false, cx).detach_and_log_err(cx);
|
workspace
|
||||||
|
.save_active_item(
|
||||||
|
action
|
||||||
|
.save_behavior
|
||||||
|
.unwrap_or(SaveBehavior::PromptOnConflict),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.detach_and_log_err(cx);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
cx.add_action(
|
cx.add_action(
|
||||||
|workspace: &mut Workspace, _: &SaveAs, cx: &mut ViewContext<Workspace>| {
|
|workspace: &mut Workspace, _: &SaveAs, cx: &mut ViewContext<Workspace>| {
|
||||||
workspace.save_active_item(true, cx).detach_and_log_err(cx);
|
workspace
|
||||||
|
.save_active_item(SaveBehavior::PromptForNewPath, cx)
|
||||||
|
.detach_and_log_err(cx);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| {
|
cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| {
|
||||||
|
@ -1294,15 +1297,11 @@ impl Workspace {
|
||||||
|
|
||||||
pub fn close(
|
pub fn close(
|
||||||
&mut self,
|
&mut self,
|
||||||
action: &CloseWindow,
|
_: &CloseWindow,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Option<Task<Result<()>>> {
|
) -> Option<Task<Result<()>>> {
|
||||||
let window = cx.window();
|
let window = cx.window();
|
||||||
let prepare = self.prepare_to_close(
|
let prepare = self.prepare_to_close(false, cx);
|
||||||
false,
|
|
||||||
action.save_behavior.unwrap_or(SaveBehavior::PromptOnWrite),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
Some(cx.spawn(|_, mut cx| async move {
|
Some(cx.spawn(|_, mut cx| async move {
|
||||||
if prepare.await? {
|
if prepare.await? {
|
||||||
window.remove(&mut cx);
|
window.remove(&mut cx);
|
||||||
|
@ -1685,51 +1684,31 @@ impl Workspace {
|
||||||
|
|
||||||
pub fn save_active_item(
|
pub fn save_active_item(
|
||||||
&mut self,
|
&mut self,
|
||||||
force_name_change: bool,
|
save_behavior: SaveBehavior,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
let project = self.project.clone();
|
let project = self.project.clone();
|
||||||
if let Some(item) = self.active_item(cx) {
|
let pane = self.active_pane();
|
||||||
if !force_name_change && item.can_save(cx) {
|
let item_ix = pane.read(cx).active_item_index();
|
||||||
if item.has_conflict(cx) {
|
let item = pane.read(cx).active_item();
|
||||||
const CONFLICT_MESSAGE: &str = "This file has changed on disk since you started editing it. Do you want to overwrite it?";
|
let pane = pane.downgrade();
|
||||||
|
|
||||||
let mut answer = cx.prompt(
|
cx.spawn(|_, mut cx| async move {
|
||||||
PromptLevel::Warning,
|
if let Some(item) = item {
|
||||||
CONFLICT_MESSAGE,
|
Pane::save_item(
|
||||||
&["Overwrite", "Cancel"],
|
project,
|
||||||
);
|
&pane,
|
||||||
cx.spawn(|this, mut cx| async move {
|
item_ix,
|
||||||
let answer = answer.recv().await;
|
item.as_ref(),
|
||||||
if answer == Some(0) {
|
save_behavior,
|
||||||
this.update(&mut cx, |this, cx| item.save(this.project.clone(), cx))?
|
&mut cx,
|
||||||
.await?;
|
)
|
||||||
}
|
.await
|
||||||
Ok(())
|
.map(|_| ())
|
||||||
})
|
|
||||||
} else {
|
|
||||||
item.save(self.project.clone(), cx)
|
|
||||||
}
|
|
||||||
} else if item.is_singleton(cx) {
|
|
||||||
let worktree = self.worktrees(cx).next();
|
|
||||||
let start_abs_path = worktree
|
|
||||||
.and_then(|w| w.read(cx).as_local())
|
|
||||||
.map_or(Path::new(""), |w| w.abs_path())
|
|
||||||
.to_path_buf();
|
|
||||||
let mut abs_path = cx.prompt_for_new_path(&start_abs_path);
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
|
||||||
if let Some(abs_path) = abs_path.recv().await.flatten() {
|
|
||||||
this.update(&mut cx, |_, cx| item.save_as(project, abs_path, cx))?
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Task::ready(Ok(()))
|
Ok(())
|
||||||
}
|
}
|
||||||
} else {
|
})
|
||||||
Task::ready(Ok(()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close_inactive_items_and_panes(
|
pub fn close_inactive_items_and_panes(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue