Skip to content

Commit d0a09a9

Browse files
fix: Cannot quit app when there is a dirty editor (#13164) (#13173)
Contributed by STMicroelectronics Signed-off-by: Emil HAMMARSTEDT <[email protected]>
1 parent d2bc79b commit d0a09a9

File tree

2 files changed

+55
-43
lines changed

2 files changed

+55
-43
lines changed

packages/core/src/browser/common-frontend-contribution.ts

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ import { QuickInputService, QuickPickItem, QuickPickItemOrSeparator, QuickPickSe
5757
import { AsyncLocalizationProvider } from '../common/i18n/localization';
5858
import { nls } from '../common/nls';
5959
import { CurrentWidgetCommandAdapter } from './shell/current-widget-command-adapter';
60-
import { ConfirmDialog, confirmExitWithOrWithoutSaving, Dialog } from './dialogs';
60+
import { ConfirmDialog, confirmExit, ConfirmSaveDialog, Dialog } from './dialogs';
6161
import { WindowService } from './window/window-service';
6262
import { FrontendApplicationConfigProvider } from './frontend-application-config-provider';
6363
import { DecorationStyle } from './decoration-style';
@@ -1200,22 +1200,60 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
12001200
action: async () => {
12011201
const captionsToSave = this.unsavedTabsCaptions();
12021202
const untitledCaptionsToSave = this.unsavedUntitledTabsCaptions();
1203-
const result = await confirmExitWithOrWithoutSaving(captionsToSave, async () => {
1203+
const shouldExit = await this.confirmExitWithOrWithoutSaving(captionsToSave, async () => {
12041204
await this.saveDirty(untitledCaptionsToSave);
12051205
await this.shell.saveAll();
12061206
});
1207-
if (this.shell.canSaveAll()) {
1208-
this.shouldPreventClose = true;
1209-
return false;
1210-
} else {
1211-
this.shouldPreventClose = false;
1212-
return result;
1213-
}
1207+
const allSavedOrDoNotSave = (
1208+
shouldExit === true && untitledCaptionsToSave.length === 0 // Should save and cancel if any captions failed to save
1209+
) || shouldExit === false; // Do not save
1210+
1211+
this.shouldPreventClose = !allSavedOrDoNotSave;
1212+
return allSavedOrDoNotSave;
12141213

12151214
}
12161215
};
12171216
}
12181217
}
1218+
// Asks the user to confirm whether they want to exit with or without saving the changes
1219+
private async confirmExitWithOrWithoutSaving(captionsToSave: string[], performSave: () => Promise<void>): Promise<boolean | undefined> {
1220+
const div: HTMLElement = document.createElement('div');
1221+
div.innerText = nls.localizeByDefault("Your changes will be lost if you don't save them.");
1222+
1223+
let result;
1224+
if (captionsToSave.length > 0) {
1225+
const span = document.createElement('span');
1226+
span.appendChild(document.createElement('br'));
1227+
captionsToSave.forEach(cap => {
1228+
const b = document.createElement('b');
1229+
b.innerText = cap;
1230+
span.appendChild(b);
1231+
span.appendChild(document.createElement('br'));
1232+
});
1233+
span.appendChild(document.createElement('br'));
1234+
div.appendChild(span);
1235+
result = await new ConfirmSaveDialog({
1236+
title: nls.localizeByDefault('Do you want to save the changes to the following {0} files?', captionsToSave.length),
1237+
msg: div,
1238+
dontSave: nls.localizeByDefault("Don't Save"),
1239+
save: nls.localizeByDefault('Save All'),
1240+
cancel: Dialog.CANCEL
1241+
}).open();
1242+
1243+
if (result) {
1244+
await performSave();
1245+
}
1246+
} else {
1247+
// fallback if not passed with an empty caption-list.
1248+
result = confirmExit();
1249+
}
1250+
if (result !== undefined) {
1251+
return result === true;
1252+
} else {
1253+
return undefined;
1254+
};
1255+
1256+
}
12191257
protected unsavedTabsCaptions(): string[] {
12201258
return this.shell.widgets
12211259
.filter(widget => this.saveResourceService.canSave(widget))
@@ -1236,11 +1274,19 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
12361274
this.windowService.reload();
12371275
}
12381276
}
1277+
/**
1278+
* saves any dirty widget in toSave
1279+
* side effect - will pop all widgets from toSave that was saved
1280+
* @param toSave
1281+
*/
12391282
protected async saveDirty(toSave: Widget[]): Promise<void> {
12401283
for (const widget of toSave) {
12411284
const saveable = Saveable.get(widget);
12421285
if (saveable?.dirty) {
12431286
await this.saveResourceService.save(widget);
1287+
if (!this.saveResourceService.canSave(widget)) {
1288+
toSave.pop();
1289+
}
12441290
}
12451291
}
12461292
}

packages/core/src/browser/dialogs.ts

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -458,40 +458,6 @@ export class ConfirmSaveDialog extends AbstractDialog<boolean | undefined> {
458458

459459
}
460460

461-
// Asks the user to confirm whether they want to exit with or without saving the changes
462-
export async function confirmExitWithOrWithoutSaving(captionsToSave: string[], performSave: () => Promise<void>): Promise<boolean> {
463-
const div: HTMLElement = document.createElement('div');
464-
div.innerText = nls.localizeByDefault("Your changes will be lost if you don't save them.");
465-
466-
if (captionsToSave.length > 0) {
467-
const span = document.createElement('span');
468-
span.appendChild(document.createElement('br'));
469-
captionsToSave.forEach(cap => {
470-
const b = document.createElement('b');
471-
b.innerText = cap;
472-
span.appendChild(b);
473-
span.appendChild(document.createElement('br'));
474-
});
475-
span.appendChild(document.createElement('br'));
476-
div.appendChild(span);
477-
const result = await new ConfirmSaveDialog({
478-
title: nls.localizeByDefault('Do you want to save the changes to the following {0} files?', captionsToSave.length),
479-
msg: div,
480-
dontSave: nls.localizeByDefault("Don't Save"),
481-
save: nls.localizeByDefault('Save All'),
482-
cancel: Dialog.CANCEL
483-
}).open();
484-
485-
if (result) {
486-
await performSave();
487-
}
488-
return result !== undefined;
489-
} else {
490-
// fallback if not passed with an empty caption-list.
491-
return confirmExit();
492-
}
493-
494-
}
495461
@injectable()
496462
export class SingleTextInputDialogProps extends DialogProps {
497463
readonly confirmButtonLabel?: string;

0 commit comments

Comments
 (0)