Suggest unsaved buffer content text as the default filename (#35707)

Closes #24672

This PR complements a feature added earlier by @JosephTLyons (in
https://github.com/zed-industries/zed/pull/32353) where the text is
considered as the tab title in a new buffer. It piggybacks off that
change and sets the title as the suggested filename in the save dialog
(completely mirroring the same functionality in VSCode):

![2025-08-05 11 50
28](https://github.com/user-attachments/assets/49ad9e4a-5559-44b0-a4b0-ae19890e478e)

Release Notes:

- Text entered in a new untitled buffer is considered as the default
filename when saving
This commit is contained in:
Igal Tabachnik 2025-08-15 18:26:38 +03:00 committed by GitHub
parent 485802b9e5
commit 7993ee9c07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 75 additions and 18 deletions

View file

@ -270,6 +270,12 @@ pub trait Item: Focusable + EventEmitter<Self::Event> + Render + Sized {
/// Returns the textual contents of the tab.
fn tab_content_text(&self, _detail: usize, _cx: &App) -> SharedString;
/// Returns the suggested filename for saving this item.
/// By default, returns the tab content text.
fn suggested_filename(&self, cx: &App) -> SharedString {
self.tab_content_text(0, cx)
}
fn tab_icon(&self, _window: &Window, _cx: &App) -> Option<Icon> {
None
}
@ -497,6 +503,7 @@ pub trait ItemHandle: 'static + Send {
) -> gpui::Subscription;
fn tab_content(&self, params: TabContentParams, window: &Window, cx: &App) -> AnyElement;
fn tab_content_text(&self, detail: usize, cx: &App) -> SharedString;
fn suggested_filename(&self, cx: &App) -> SharedString;
fn tab_icon(&self, window: &Window, cx: &App) -> Option<Icon>;
fn tab_tooltip_text(&self, cx: &App) -> Option<SharedString>;
fn tab_tooltip_content(&self, cx: &App) -> Option<TabTooltipContent>;
@ -631,6 +638,10 @@ impl<T: Item> ItemHandle for Entity<T> {
self.read(cx).tab_content_text(detail, cx)
}
fn suggested_filename(&self, cx: &App) -> SharedString {
self.read(cx).suggested_filename(cx)
}
fn tab_icon(&self, window: &Window, cx: &App) -> Option<Icon> {
self.read(cx).tab_icon(window, cx)
}

View file

@ -2062,6 +2062,8 @@ impl Pane {
})?
.await?;
} else if can_save_as && is_singleton {
let suggested_name =
cx.update(|_window, cx| item.suggested_filename(cx).to_string())?;
let new_path = pane.update_in(cx, |pane, window, cx| {
pane.activate_item(item_ix, true, true, window, cx);
pane.workspace.update(cx, |workspace, cx| {
@ -2073,7 +2075,7 @@ impl Pane {
} else {
DirectoryLister::Project(workspace.project().clone())
};
workspace.prompt_for_new_path(lister, window, cx)
workspace.prompt_for_new_path(lister, Some(suggested_name), window, cx)
})
})??;
let Some(new_path) = new_path.await.ok().flatten().into_iter().flatten().next()

View file

@ -2067,6 +2067,7 @@ impl Workspace {
pub fn prompt_for_new_path(
&mut self,
lister: DirectoryLister,
suggested_name: Option<String>,
window: &mut Window,
cx: &mut Context<Self>,
) -> oneshot::Receiver<Option<Vec<PathBuf>>> {
@ -2094,7 +2095,7 @@ impl Workspace {
})
.or_else(std::env::home_dir)
.unwrap_or_else(|| PathBuf::from(""));
cx.prompt_for_new_path(&relative_to)
cx.prompt_for_new_path(&relative_to, suggested_name.as_deref())
})?;
let abs_path = match abs_path.await? {
Ok(path) => path,