Skip to content

Commit

Permalink
fix: Cannot quit app when there is a dirty editor (eclipse-theia#13164)
Browse files Browse the repository at this point in the history
Contributed by STMicroelectronics

Signed-off-by: Emil HAMMARSTEDT <[email protected]>
  • Loading branch information
emilhammarstedtst committed Dec 18, 2023
1 parent 469bd74 commit e900e2f
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 43 deletions.
64 changes: 55 additions & 9 deletions packages/core/src/browser/common-frontend-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<void>): Promise<boolean | undefined> {
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))
Expand All @@ -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<void> {
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();
}
}
}
}
Expand Down
34 changes: 0 additions & 34 deletions packages/core/src/browser/dialogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,40 +458,6 @@ export class ConfirmSaveDialog extends AbstractDialog<boolean | undefined> {

}

// Asks the user to confirm whether they want to exit with or without saving the changes
export async function confirmExitWithOrWithoutSaving(captionsToSave: string[], performSave: () => Promise<void>): Promise<boolean> {
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;
Expand Down

0 comments on commit e900e2f

Please sign in to comment.