diff --git a/src/vs/code/electron-main/lifecycle.ts b/src/vs/code/electron-main/lifecycle.ts index 60b0170039c..b2ce7ca14e3 100644 --- a/src/vs/code/electron-main/lifecycle.ts +++ b/src/vs/code/electron-main/lifecycle.ts @@ -185,7 +185,7 @@ export class LifecycleService implements ILifecycleService { c(true); // veto }); - vscodeWindow.send('vscode:beforeUnload', { okChannel: oneTimeOkEvent, cancelChannel: oneTimeCancelEvent }); + vscodeWindow.send('vscode:beforeUnload', { okChannelReply: oneTimeOkEvent, cancelChannelReply: oneTimeCancelEvent, quitRequested: this.quitRequested }); }); } diff --git a/src/vs/platform/lifecycle/common/lifecycle.ts b/src/vs/platform/lifecycle/common/lifecycle.ts index 3804d593348..783cd97712b 100644 --- a/src/vs/platform/lifecycle/common/lifecycle.ts +++ b/src/vs/platform/lifecycle/common/lifecycle.ts @@ -22,6 +22,7 @@ export const ILifecycleService = createDecorator('lifecycleSe export interface ShutdownEvent { veto(value: boolean | TPromise): void; + quitRequested: boolean; } /** @@ -38,6 +39,12 @@ export interface ILifecycleService { */ willShutdown: boolean; + /** + * A flag indications if the application is in the process of quitting all windows. This will be + * set before the onWillShutdown event is fired and reverted to false afterwards. + */ + quitRequested: boolean; + /** * Fired before shutdown happens. Allows listeners to veto against the * shutdown. diff --git a/src/vs/workbench/services/backup/common/backup.ts b/src/vs/workbench/services/backup/common/backup.ts index 2cfde98db27..d7bef9c12c7 100644 --- a/src/vs/workbench/services/backup/common/backup.ts +++ b/src/vs/workbench/services/backup/common/backup.ts @@ -22,7 +22,7 @@ export interface IBackupService { _serviceBrand: any; isHotExitEnabled: boolean; - backupBeforeShutdown(dirtyToBackup: Uri[], textFileEditorModelManager: ITextFileEditorModelManager, confirmCallback: () => boolean | TPromise): boolean | TPromise; + backupBeforeShutdown(dirtyToBackup: Uri[], textFileEditorModelManager: ITextFileEditorModelManager, quitRequested: boolean, confirmCallback: () => boolean | TPromise): boolean | TPromise; cleanupBackupsBeforeShutdown(): boolean | TPromise; doBackup(resource: Uri, content: string, immediate?: boolean): TPromise; diff --git a/src/vs/workbench/services/backup/node/backupService.ts b/src/vs/workbench/services/backup/node/backupService.ts index c9489cd4d92..fca7f1a2da0 100644 --- a/src/vs/workbench/services/backup/node/backupService.ts +++ b/src/vs/workbench/services/backup/node/backupService.ts @@ -129,15 +129,16 @@ export class BackupService implements IBackupService { return this.configuredHotExit && this.contextService.getWorkspace() && !platform.isMacintosh; } - public backupBeforeShutdown(dirtyToBackup: Uri[], textFileEditorModelManager: ITextFileEditorModelManager, confirmCallback: () => boolean | TPromise): boolean | TPromise { + public backupBeforeShutdown(dirtyToBackup: Uri[], textFileEditorModelManager: ITextFileEditorModelManager, quitRequested: boolean, confirmCallback: () => boolean | TPromise): boolean | TPromise { // If there are no dirty files, clean up and exit if (dirtyToBackup.length === 0) { return this.cleanupBackupsBeforeShutdown(); } return this.backupFileService.getWorkspaceBackupPaths().then(workspaceBackupPaths => { - // Only remove the workspace from the backup service if it's not the last one or it's not dirty - if (workspaceBackupPaths.length > 1) { + // Only remove the workspace's backups if it's not the last one or it's not dirty. The one exception to this + // is if quit (all windows) was requested then all windows should be backed up and restored on next launch.. + if (!quitRequested && workspaceBackupPaths.length > 1) { return confirmCallback(); // confirm save } diff --git a/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts b/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts index fe3b0d3fcaf..1e613f2cc73 100644 --- a/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/electron-browser/lifecycleService.ts @@ -21,6 +21,7 @@ export class LifecycleService implements ILifecycleService { private _onShutdown = new Emitter(); private _willShutdown: boolean; + private _quitRequested: boolean; constructor( @IMessageService private messageService: IMessageService, @@ -33,6 +34,10 @@ export class LifecycleService implements ILifecycleService { return this._willShutdown; } + public get quitRequested(): boolean { + return this._quitRequested; + } + public get onWillShutdown(): Event { return this._onWillShutdown.event; } @@ -45,29 +50,32 @@ export class LifecycleService implements ILifecycleService { const windowId = this.windowService.getWindowId(); // Main side indicates that window is about to unload, check for vetos - ipc.on('vscode:beforeUnload', (event, reply: { okChannel: string, cancelChannel: string }) => { + ipc.on('vscode:beforeUnload', (event, args: { okChannelReply: string, cancelChannelReply: string, quitRequested: boolean }) => { this._willShutdown = true; + this._quitRequested = args.quitRequested; // trigger onWillShutdown events and veto collecting - this.onBeforeUnload().done(veto => { + this.onBeforeUnload(args.quitRequested).done(veto => { + this._quitRequested = false; if (veto) { this._willShutdown = false; // reset this flag since the shutdown has been vetoed! - ipc.send(reply.cancelChannel, windowId); + ipc.send(args.cancelChannelReply, windowId); } else { this._onShutdown.fire(); - ipc.send(reply.okChannel, windowId); + ipc.send(args.okChannelReply, windowId); } }); }); } - private onBeforeUnload(): TPromise { + private onBeforeUnload(quitRequested: boolean): TPromise { const vetos: (boolean | TPromise)[] = []; this._onWillShutdown.fire({ veto(value) { vetos.push(value); - } + }, + quitRequested }); if (vetos.length === 0) { diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index d36d1a20467..74002331893 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -101,7 +101,7 @@ export abstract class TextFileService implements ITextFileService { private registerListeners(): void { // Lifecycle - this.lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown())); + this.lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown(event.quitRequested))); this.lifecycleService.onShutdown(this.dispose, this); // Configuration changes @@ -113,9 +113,9 @@ export abstract class TextFileService implements ITextFileService { this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorFocusChanged())); } - private beforeShutdown(): boolean | TPromise { + private beforeShutdown(quitRequested: boolean): boolean | TPromise { if (this.backupService.isHotExitEnabled) { - return this.backupService.backupBeforeShutdown(this.getDirty(), this.models, this.confirmBeforeShutdown.bind(this)); + return this.backupService.backupBeforeShutdown(this.getDirty(), this.models, quitRequested, this.confirmBeforeShutdown.bind(this)); } // Dirty files need treatment on shutdown diff --git a/src/vs/workbench/services/textfile/test/textFileService.test.ts b/src/vs/workbench/services/textfile/test/textFileService.test.ts index 51f01d730e6..9649e137495 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.test.ts @@ -28,6 +28,7 @@ class ServiceAccessor { class ShutdownEventImpl implements ShutdownEvent { public value: boolean | TPromise; + public quitRequested: boolean = false; veto(value: boolean | TPromise): void { this.value = value;