From e900e2f3c282af29e2cb284820bbbb48fc090e55 Mon Sep 17 00:00:00 2001 From: Emil HAMMARSTEDT Date: Wed, 13 Dec 2023 08:51:14 +0100 Subject: [PATCH] fix: Cannot quit app when there is a dirty editor (#13164) Contributed by STMicroelectronics Signed-off-by: Emil HAMMARSTEDT --- .../browser/common-frontend-contribution.ts | 64 ++++++++++++++++--- packages/core/src/browser/dialogs.ts | 34 ---------- 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/packages/core/src/browser/common-frontend-contribution.ts b/packages/core/src/browser/common-frontend-contribution.ts index f436fcf2349b4..dc1532e3315a4 100644 --- a/packages/core/src/browser/common-frontend-contribution.ts +++ b/packages/core/src/browser/common-frontend-contribution.ts @@ -57,7 +57,7 @@ import { QuickInputService, QuickPickItem, QuickPickItemOrSeparator, QuickPickSe import { AsyncLocalizationProvider } from '../common/i18n/localization'; import { nls } from '../common/nls'; import { CurrentWidgetCommandAdapter } from './shell/current-widget-command-adapter'; -import { ConfirmDialog, confirmExitWithOrWithoutSaving, Dialog } from './dialogs'; +import { ConfirmDialog, confirmExit, ConfirmSaveDialog, Dialog } from './dialogs'; import { WindowService } from './window/window-service'; import { FrontendApplicationConfigProvider } from './frontend-application-config-provider'; import { DecorationStyle } from './decoration-style'; @@ -1200,22 +1200,60 @@ export class CommonFrontendContribution implements FrontendApplicationContributi action: async () => { const captionsToSave = this.unsavedTabsCaptions(); const untitledCaptionsToSave = this.unsavedUntitledTabsCaptions(); - const result = await confirmExitWithOrWithoutSaving(captionsToSave, async () => { + const shouldExit = await this.confirmExitWithOrWithoutSaving(captionsToSave, async () => { await this.saveDirty(untitledCaptionsToSave); await this.shell.saveAll(); }); - if (this.shell.canSaveAll()) { - this.shouldPreventClose = true; - return false; - } else { - this.shouldPreventClose = false; - return result; - } + const allSavedOrDoNotSave = ( + shouldExit === true && untitledCaptionsToSave.length === 0 // Should save and cancel if any captions failed to save + ) || shouldExit === false; // Do not save + + this.shouldPreventClose = !allSavedOrDoNotSave; + return allSavedOrDoNotSave; } }; } } + // Asks the user to confirm whether they want to exit with or without saving the changes + private async confirmExitWithOrWithoutSaving(captionsToSave: string[], performSave: () => Promise): Promise { + const div: HTMLElement = document.createElement('div'); + div.innerText = nls.localizeByDefault("Your changes will be lost if you don't save them."); + + let result; + if (captionsToSave.length > 0) { + const span = document.createElement('span'); + span.appendChild(document.createElement('br')); + captionsToSave.forEach(cap => { + const b = document.createElement('b'); + b.innerText = cap; + span.appendChild(b); + span.appendChild(document.createElement('br')); + }); + span.appendChild(document.createElement('br')); + div.appendChild(span); + result = await new ConfirmSaveDialog({ + title: nls.localizeByDefault('Do you want to save the changes to the following {0} files?', captionsToSave.length), + msg: div, + dontSave: nls.localizeByDefault("Don't Save"), + save: nls.localizeByDefault('Save All'), + cancel: Dialog.CANCEL + }).open(); + + if (result) { + await performSave(); + } + } else { + // fallback if not passed with an empty caption-list. + result = confirmExit(); + } + if (result !== undefined) { + return result === true; + } else { + return undefined; + }; + + } protected unsavedTabsCaptions(): string[] { return this.shell.widgets .filter(widget => this.saveResourceService.canSave(widget)) @@ -1236,11 +1274,19 @@ export class CommonFrontendContribution implements FrontendApplicationContributi this.windowService.reload(); } } + /** + * saves any dirty widget in toSave + * side effect - will pop all widgets from toSave that was saved + * @param toSave + */ protected async saveDirty(toSave: Widget[]): Promise { for (const widget of toSave) { const saveable = Saveable.get(widget); if (saveable?.dirty) { await this.saveResourceService.save(widget); + if (!this.saveResourceService.canSave(widget)) { + toSave.pop(); + } } } } diff --git a/packages/core/src/browser/dialogs.ts b/packages/core/src/browser/dialogs.ts index 2ab0a1203e976..c72fe33792ea6 100644 --- a/packages/core/src/browser/dialogs.ts +++ b/packages/core/src/browser/dialogs.ts @@ -458,40 +458,6 @@ export class ConfirmSaveDialog extends AbstractDialog { } -// Asks the user to confirm whether they want to exit with or without saving the changes -export async function confirmExitWithOrWithoutSaving(captionsToSave: string[], performSave: () => Promise): Promise { - const div: HTMLElement = document.createElement('div'); - div.innerText = nls.localizeByDefault("Your changes will be lost if you don't save them."); - - if (captionsToSave.length > 0) { - const span = document.createElement('span'); - span.appendChild(document.createElement('br')); - captionsToSave.forEach(cap => { - const b = document.createElement('b'); - b.innerText = cap; - span.appendChild(b); - span.appendChild(document.createElement('br')); - }); - span.appendChild(document.createElement('br')); - div.appendChild(span); - const result = await new ConfirmSaveDialog({ - title: nls.localizeByDefault('Do you want to save the changes to the following {0} files?', captionsToSave.length), - msg: div, - dontSave: nls.localizeByDefault("Don't Save"), - save: nls.localizeByDefault('Save All'), - cancel: Dialog.CANCEL - }).open(); - - if (result) { - await performSave(); - } - return result !== undefined; - } else { - // fallback if not passed with an empty caption-list. - return confirmExit(); - } - -} @injectable() export class SingleTextInputDialogProps extends DialogProps { readonly confirmButtonLabel?: string;