From bb53ad9862684acce0f80230ec7559878e440a82 Mon Sep 17 00:00:00 2001 From: Dylan Date: Mon, 17 Feb 2025 17:01:56 -0600 Subject: [PATCH] Only allow save_as for pane if can_save_as is true (#25028) When saving an item, some logic is done to determine whether one can save it. In the special case where the intent is to `SaveAs`, it was previously allowed to proceed as long as the buffer was a singleton (presumably since it only makes sense to provide a save path for a single file). However, we need to _also_ check that this item can be "saved as" at all. For this, we resurrect the `ItemHandle`/`Item` trait method `can_save_as`. We have given it the default implementation of returning `false`, and then overridden this in the implementation for `TerminalView`. Closes #25023 Release Notes: - Fixed crash when trying to save terminal buffer --------- Co-authored-by: Conrad Irwin --- crates/editor/src/items.rs | 4 +++ crates/terminal_view/src/terminal_view.rs | 4 +++ crates/workspace/src/item.rs | 12 +++++++++ crates/workspace/src/pane.rs | 31 +++++++++++++---------- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 3077c0793768ca9062ab2ea30ea0f6c7ebf1bc26..8a15bbb10cc385155ad70f992a82a24bf949fb52 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -706,6 +706,10 @@ impl Item for Editor { self.buffer.read(cx).is_singleton() } + fn can_save_as(&self, cx: &App) -> bool { + self.buffer.read(cx).is_singleton() + } + fn clone_on_split( &self, _workspace_id: Option, diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 6d2540940ad595532b4bef32df1b04744967b3f4..ff2e89f4ade78918c14a22e8355918c426cf904c 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -1342,6 +1342,10 @@ impl Item for TerminalView { false } + fn can_save_as(&self, _cx: &App) -> bool { + false + } + fn is_singleton(&self, _cx: &App) -> bool { true } diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index ff2c022a96c6a66296a154d1ad8b6e7b200fdbb7..8a2463d19996f13bdbe4dd663c3475629041005f 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -276,6 +276,9 @@ pub trait Item: Focusable + EventEmitter + Render + Sized { fn can_save(&self, _cx: &App) -> bool { false } + fn can_save_as(&self, _: &App) -> bool { + false + } fn save( &mut self, _format: bool, @@ -477,6 +480,7 @@ pub trait ItemHandle: 'static + Send { fn has_deleted_file(&self, cx: &App) -> bool; fn has_conflict(&self, cx: &App) -> bool; fn can_save(&self, cx: &App) -> bool; + fn can_save_as(&self, cx: &App) -> bool; fn save( &self, format: bool, @@ -890,6 +894,10 @@ impl ItemHandle for Entity { self.read(cx).can_save(cx) } + fn can_save_as(&self, cx: &App) -> bool { + self.read(cx).can_save_as(cx) + } + fn save( &self, format: bool, @@ -1468,6 +1476,10 @@ pub mod test { .all(|item| item.read(cx).entry_id.is_some()) } + fn can_save_as(&self, _cx: &App) -> bool { + self.is_singleton + } + fn save( &mut self, _: bool, diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 71fcf10534847b28f0f31cc16ddb5d0be818a59c..50563169ddc40fa793316fb06661e2e9b4ab40ba 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1794,18 +1794,23 @@ impl Pane { return Ok(true); }; - let (mut has_conflict, mut is_dirty, mut can_save, is_singleton, has_deleted_file) = cx - .update(|_window, cx| { - ( - item.has_conflict(cx), - item.is_dirty(cx), - item.can_save(cx), - item.is_singleton(cx), - item.has_deleted_file(cx), - ) - })?; - - let can_save_as = is_singleton; + let ( + mut has_conflict, + mut is_dirty, + mut can_save, + can_save_as, + is_singleton, + has_deleted_file, + ) = cx.update(|_window, cx| { + ( + item.has_conflict(cx), + item.is_dirty(cx), + item.can_save(cx), + item.can_save_as(cx), + item.is_singleton(cx), + item.has_deleted_file(cx), + ) + })?; // when saving a single buffer, we ignore whether or not it's dirty. if save_intent == SaveIntent::Save || save_intent == SaveIntent::SaveWithoutFormat { @@ -1939,7 +1944,7 @@ impl Pane { item.save(should_format, project, window, cx) })? .await?; - } else if can_save_as { + } else if can_save_as && is_singleton { let abs_path = pane.update_in(cx, |pane, window, cx| { pane.activate_item(item_ix, true, true, window, cx); pane.workspace.update(cx, |workspace, cx| {