diff --git a/src/ui/app.rs b/src/ui/app.rs index 3d1f07a4..a64ddf08 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -1816,10 +1816,26 @@ impl Tty7App { fn quit_stop_sessions(&mut self, window: &mut Window, cx: &mut Context) { cx.activate(true); window.activate_window(); + // The body promises the tabs and layout come back. A buffer nobody has + // written down does not, so when there is one it is named here rather + // than raised as a second dialog — the reader is already being asked + // one question about what they are about to lose, and the answer to it + // should account for all of it. + let body = match Self::quit_loses_unwritten_work(cx) { + None => t(crate::ui::i18n::L10nKey::QuitStopServerBody).to_string(), + Some((_, _, name)) => format!( + "{} {}", + t(crate::ui::i18n::L10nKey::QuitStopServerBody), + t_fmt( + crate::ui::i18n::L10nKey::QuitStopServerUnsaved, + &[("name", &name)] + ) + ), + }; let answer = window.prompt( PromptLevel::Warning, t(crate::ui::i18n::L10nKey::QuitStopServerTitle), - Some(t(crate::ui::i18n::L10nKey::QuitStopServerBody)), + Some(&body), &crate::ui::confirm_answers( t(crate::ui::i18n::L10nKey::QuitAndStop), t(crate::ui::i18n::L10nKey::Cancel), diff --git a/src/ui/code_editor.rs b/src/ui/code_editor.rs index bed9e5f7..a97d374e 100644 --- a/src/ui/code_editor.rs +++ b/src/ui/code_editor.rs @@ -2031,6 +2031,50 @@ mod unsaved_close_gpui_tests { let _ = (first, vcx); } + /// Quit-and-stop names the buffer it is about to take. + /// + /// That prompt already asks a question about loss — it says every shell + /// ends — and reassures that "your tabs and layout reopen with fresh + /// shells next launch". Unwritten text does not reopen, so a reader who + /// accepts on the strength of that sentence loses something the sentence + /// implied was safe. Named in the body rather than raised as a second + /// dialog: they are already being asked once, and that answer should + /// account for all of it. + #[gpui::test] + fn quit_and_stop_names_an_unwritten_buffer_in_its_body(cx: &mut TestAppContext) { + use crate::ui::i18n::{L10nKey, t, t_fmt}; + + cx.update(crate::ui::windows::WindowRegistry::init); + let (app, mut vcx, _streams) = harness_with_tabs(cx, 2); + app.update_in(&mut vcx, |app, window, cx| { + let (ws, weak) = (app.workspace, cx.weak_entity()); + crate::ui::windows::WindowRegistry::register(cx, ws, window.window_handle(), weak); + }); + + // Nothing unwritten: the body is the plain one, unchanged. + cx.update(|cx| { + assert!(crate::ui::app::Tty7App::quit_loses_unwritten_work(cx).is_none()); + }); + + app.update_in(&mut vcx, |app, window, cx| { + app.editor_seed_dirty_file_for_test("/w/repo/notes.md", window, cx); + }); + + let (_, _, name) = cx + .update(crate::ui::app::Tty7App::quit_loses_unwritten_work) + .expect("quit-and-stop would take it"); + assert_eq!(name, "notes.md"); + + // The sentence the body gains says the changes do not come back — + // which is the half the plain body does not cover. + let added = t_fmt(L10nKey::QuitStopServerUnsaved, &[("name", &name)]); + assert!(added.contains("notes.md"), "it names the file: {added}"); + assert!( + !t(L10nKey::QuitStopServerBody).contains("notes.md"), + "and the plain body never did" + ); + } + /// Building the buffer the way `editor_install_file` does. fn new_buffer( text: &str, diff --git a/src/ui/i18n/en.rs b/src/ui/i18n/en.rs index 766f22ba..cbcd7813 100644 --- a/src/ui/i18n/en.rs +++ b/src/ui/i18n/en.rs @@ -21,6 +21,7 @@ pub fn translate_en(key: L10nKey) -> &'static str { L10nKey::CloseUnsavedEditsTitle => "Close this tab?", L10nKey::CloseWindowUnsavedEditsTitle => "Close this window?", L10nKey::QuitUnsavedEditsTitle => "Quit tty7?", + L10nKey::QuitStopServerUnsaved => "{name} has unsaved changes, and those do not come back.", L10nKey::RelaunchUnsavedEditsTitle => "Relaunch to update?", L10nKey::HomeReopenClosedTab => "Reopen Closed Tab", L10nKey::HomeSwitchWorkspace => "Switch Workspace…", diff --git a/src/ui/i18n/ja.rs b/src/ui/i18n/ja.rs index d3e9d6cd..a9dd877f 100644 --- a/src/ui/i18n/ja.rs +++ b/src/ui/i18n/ja.rs @@ -21,6 +21,7 @@ pub fn translate_ja(key: L10nKey) -> Option<&'static str> { L10nKey::CloseUnsavedEditsTitle => "このタブを閉じますか?", L10nKey::CloseWindowUnsavedEditsTitle => "このウィンドウを閉じますか?", L10nKey::QuitUnsavedEditsTitle => "tty7 を終了しますか?", + L10nKey::QuitStopServerUnsaved => "{name} に未保存の変更があります。これは復元されません。", L10nKey::RelaunchUnsavedEditsTitle => "更新のため再起動しますか?", L10nKey::HomeReopenClosedTab => "閉じたタブをもう一度開く", L10nKey::HomeSwitchWorkspace => "ワークスペースを切り替える…", diff --git a/src/ui/i18n/mod.rs b/src/ui/i18n/mod.rs index 117ed4bd..f07dad3f 100644 --- a/src/ui/i18n/mod.rs +++ b/src/ui/i18n/mod.rs @@ -99,6 +99,7 @@ l10n_keys! { CloseUnsavedEditsTitle, CloseWindowUnsavedEditsTitle, QuitUnsavedEditsTitle, + QuitStopServerUnsaved, RelaunchUnsavedEditsTitle, HomeReopenClosedTab, HomeSwitchWorkspace, diff --git a/src/ui/i18n/zh.rs b/src/ui/i18n/zh.rs index 3704bc11..4ab7ab35 100644 --- a/src/ui/i18n/zh.rs +++ b/src/ui/i18n/zh.rs @@ -21,6 +21,7 @@ pub fn translate_zh(key: L10nKey) -> Option<&'static str> { L10nKey::CloseUnsavedEditsTitle => "关闭这个标签页?", L10nKey::CloseWindowUnsavedEditsTitle => "要关闭此窗口吗?", L10nKey::QuitUnsavedEditsTitle => "要退出 tty7 吗?", + L10nKey::QuitStopServerUnsaved => "{name} 有未保存的更改,这些不会恢复。", L10nKey::RelaunchUnsavedEditsTitle => "要重新启动以更新吗?", L10nKey::HomeReopenClosedTab => "重新打开已关闭的标签页", L10nKey::HomeSwitchWorkspace => "切换工作区…",