diff --git a/src/vs/code/common/window.ts b/src/vs/code/common/window.ts new file mode 100644 index 00000000000..b932e63f7c3 --- /dev/null +++ b/src/vs/code/common/window.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +export enum ReadyState { + + /** + * This window has not loaded any HTML yet + */ + NONE, + + /** + * This window is loading HTML + */ + LOADING, + + /** + * This window is navigating to another HTML + */ + NAVIGATING, + + /** + * This window is done loading HTML + */ + READY +} + +export interface IVSCodeWindow { + id: number; + readyState: ReadyState; + win: Electron.BrowserWindow; + + send(channel: string, ...args: any[]): void; +} \ No newline at end of file diff --git a/src/vs/code/electron-main/lifecycle.ts b/src/vs/code/electron-main/lifecycle.ts index bad48621aff..6b3d5c43032 100644 --- a/src/vs/code/electron-main/lifecycle.ts +++ b/src/vs/code/electron-main/lifecycle.ts @@ -5,39 +5,21 @@ 'use strict'; -import Uri from 'vs/base/common/uri'; import { EventEmitter } from 'events'; import { ipcMain as ipc, app } from 'electron'; import { TPromise, TValueCallback } from 'vs/base/common/winjs.base'; -import { ReadyState, VSCodeWindow } from 'vs/code/electron-main/window'; +import { ReadyState, IVSCodeWindow } from 'vs/code/common/window'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IBackupMainService } from 'vs/platform/backup/common/backup'; import { ILogService } from 'vs/code/electron-main/log'; import { IStorageService } from 'vs/code/electron-main/storage'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle'; const EventTypes = { + BEFORE_CLOSE: 'before-close', BEFORE_QUIT: 'before-quit' }; -export const ILifecycleService = createDecorator('lifecycleService'); - -export interface ILifecycleService { - _serviceBrand: any; - - /** - * Will be true if an update was applied. Will only be true for each update once. - */ - wasUpdated: boolean; - - onBeforeQuit(clb: () => void): () => void; - ready(): void; - registerWindow(vscodeWindow: VSCodeWindow): void; - unload(vscodeWindow: VSCodeWindow): TPromise; - quit(fromUpdate?: boolean): TPromise; -} - -export class LifecycleService implements ILifecycleService { +export class LifecycleService implements ILifecycleMainService { _serviceBrand: any; @@ -54,8 +36,7 @@ export class LifecycleService implements ILifecycleService { constructor( @IEnvironmentService private environmentService: IEnvironmentService, @ILogService private logService: ILogService, - @IStorageService private storageService: IStorageService, - @IBackupMainService private backupService: IBackupMainService + @IStorageService private storageService: IStorageService ) { this.windowToCloseRequest = Object.create(null); this.quitRequested = false; @@ -88,6 +69,12 @@ export class LifecycleService implements ILifecycleService { return () => this.eventEmitter.removeListener(EventTypes.BEFORE_QUIT, clb); } + onAfterUnload(clb: (vscodeWindow: IVSCodeWindow) => void): () => void { + this.eventEmitter.addListener(EventTypes.BEFORE_CLOSE, clb); + + return () => this.eventEmitter.removeListener(EventTypes.BEFORE_CLOSE, clb); + } + public ready(): void { this.registerListeners(); } @@ -118,7 +105,7 @@ export class LifecycleService implements ILifecycleService { }); } - public registerWindow(vscodeWindow: VSCodeWindow): void { + public registerWindow(vscodeWindow: IVSCodeWindow): void { // Window Before Closing: Main -> Renderer vscodeWindow.win.on('close', (e) => { @@ -148,7 +135,7 @@ export class LifecycleService implements ILifecycleService { }); } - public unload(vscodeWindow: VSCodeWindow): TPromise { + public unload(vscodeWindow: IVSCodeWindow): TPromise { // Always allow to unload a window that is not yet ready if (vscodeWindow.readyState !== ReadyState.READY) { @@ -163,13 +150,7 @@ export class LifecycleService implements ILifecycleService { const oneTimeCancelEvent = 'vscode:cancel' + oneTimeEventToken; ipc.once(oneTimeOkEvent, () => { - // Clear out any workspace backups from workspaces.json that don't have any backups - if (vscodeWindow.openedWorkspacePath) { - const workspaceResource = Uri.file(vscodeWindow.openedWorkspacePath); - if (!this.backupService.hasWorkspaceBackup(workspaceResource)) { - this.backupService.removeWorkspaceBackupPathSync(workspaceResource); - } - } + this.eventEmitter.emit(EventTypes.BEFORE_CLOSE, vscodeWindow); c(false); // no veto }); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index d9e6f8841be..092c680983f 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -15,7 +15,8 @@ import { IWindowsMainService, WindowsManager } from 'vs/code/electron-main/windo import { IWindowsService } from 'vs/platform/windows/common/windows'; import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc'; import { WindowsService } from 'vs/platform/windows/electron-main/windowsService'; -import { ILifecycleService, LifecycleService } from 'vs/code/electron-main/lifecycle'; +import { LifecycleService } from 'vs/code/electron-main/lifecycle'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle'; import { VSCodeMenu } from 'vs/code/electron-main/menus'; import { IUpdateService } from 'vs/platform/update/common/update'; import { UpdateChannel } from 'vs/platform/update/common/updateIpc'; @@ -35,7 +36,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ILogService, MainLogService } from 'vs/code/electron-main/log'; import { IStorageService, StorageService } from 'vs/code/electron-main/storage'; import { IBackupMainService } from 'vs/platform/backup/common/backup'; -import { BackupMainService } from 'vs/platform/backup/node/backupMainService'; +import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -81,7 +82,7 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo const instantiationService = accessor.get(IInstantiationService); const logService = accessor.get(ILogService); const environmentService = accessor.get(IEnvironmentService); - const lifecycleService = accessor.get(ILifecycleService); + const lifecycleService = accessor.get(ILifecycleMainService); const configurationService = accessor.get(IConfigurationService) as ConfigurationService; let windowsMainService: IWindowsMainService; @@ -423,7 +424,7 @@ function createServices(args): IInstantiationService { services.set(IEnvironmentService, new SyncDescriptor(EnvironmentService, args, process.execPath)); services.set(ILogService, new SyncDescriptor(MainLogService)); - services.set(ILifecycleService, new SyncDescriptor(LifecycleService)); + services.set(ILifecycleMainService, new SyncDescriptor(LifecycleService)); services.set(IStorageService, new SyncDescriptor(StorageService)); services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); services.set(IRequestService, new SyncDescriptor(RequestService)); diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 4ad189405ea..c4745c79c4b 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -20,6 +20,7 @@ import { getCommonHTTPHeaders } from 'vs/platform/environment/node/http'; import { IBackupMainService } from 'vs/platform/backup/common/backup'; import { IWindowSettings } from 'vs/platform/windows/common/windows'; import Uri from 'vs/base/common/uri'; +import { ReadyState, IVSCodeWindow } from 'vs/code/common/window'; export interface IWindowState { width?: number; @@ -51,29 +52,6 @@ export const defaultWindowState = function (mode = WindowMode.Normal): IWindowSt }; }; -export enum ReadyState { - - /** - * This window has not loaded any HTML yet - */ - NONE, - - /** - * This window is loading HTML - */ - LOADING, - - /** - * This window is navigating to another HTML - */ - NAVIGATING, - - /** - * This window is done loading HTML - */ - READY -} - export interface IPath { // the workspace spath for a VSCode instance which can be null @@ -116,7 +94,7 @@ export interface IWindowConfiguration extends ParsedArgs { untitledToRestore?: IPath[]; } -export class VSCodeWindow { +export class VSCodeWindow implements IVSCodeWindow { public static menuBarHiddenKey = 'menuBarHidden'; public static colorThemeStorageKey = 'theme'; diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 2b3a98996a9..dea03fe02e4 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -17,10 +17,11 @@ import { IBackupMainService } from 'vs/platform/backup/common/backup'; import { trim } from 'vs/base/common/strings'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { IStorageService } from 'vs/code/electron-main/storage'; -import { IPath, VSCodeWindow, ReadyState, IWindowConfiguration, IWindowState as ISingleWindowState, defaultWindowState } from 'vs/code/electron-main/window'; +import { IPath, VSCodeWindow, IWindowConfiguration, IWindowState as ISingleWindowState, defaultWindowState } from 'vs/code/electron-main/window'; +import { ReadyState } from 'vs/code/common/window'; import { ipcMain as ipc, app, screen, BrowserWindow, dialog } from 'electron'; import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/electron-main/paths'; -import { ILifecycleService } from 'vs/code/electron-main/lifecycle'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/code/electron-main/log'; import { getPathLabel } from 'vs/base/common/labels'; @@ -152,7 +153,7 @@ export class WindowsManager implements IWindowsMainService { @ILogService private logService: ILogService, @IStorageService private storageService: IStorageService, @IEnvironmentService private environmentService: IEnvironmentService, - @ILifecycleService private lifecycleService: ILifecycleService, + @ILifecycleMainService private lifecycleService: ILifecycleMainService, @IBackupMainService private backupService: IBackupMainService, @IConfigurationService private configurationService: IConfigurationService, @ITelemetryService private telemetryService: ITelemetryService diff --git a/src/vs/code/test/electron-main/servicesTestUtils.ts b/src/vs/code/test/electron-main/servicesTestUtils.ts new file mode 100644 index 00000000000..5eb62ad9f9a --- /dev/null +++ b/src/vs/code/test/electron-main/servicesTestUtils.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle'; +import { IVSCodeWindow } from 'vs/code/common/window'; +import { TPromise } from 'vs/base/common/winjs.base'; + +export class TestLifecycleService implements ILifecycleMainService { + public _serviceBrand: any; + + public get wasUpdated(): boolean { + return false; + } + + public onBeforeQuit(clb: () => void): () => void { + return () => { }; + } + + public onAfterUnload(clb: (vscodeWindow: IVSCodeWindow) => void): () => void { + return () => { }; + } + + public ready(): void { + } + + public registerWindow(vscodeWindow: IVSCodeWindow): void { + } + + public unload(vscodeWindow: IVSCodeWindow): TPromise { + return TPromise.as(false); + } + + public quit(fromUpdate?: boolean): TPromise { + return TPromise.as(false); + } +} \ No newline at end of file diff --git a/src/vs/platform/backup/node/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts similarity index 83% rename from src/vs/platform/backup/node/backupMainService.ts rename to src/vs/platform/backup/electron-main/backupMainService.ts index b7edd360807..9094003b4af 100644 --- a/src/vs/platform/backup/node/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -10,6 +10,8 @@ import Uri from 'vs/base/common/uri'; import { readdirSync } from 'vs/base/node/extfs'; import { IBackupWorkspacesFormat, IBackupMainService } from 'vs/platform/backup/common/backup'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle'; +import { VSCodeWindow } from 'vs/code/electron-main/window'; export class BackupMainService implements IBackupMainService { @@ -21,13 +23,27 @@ export class BackupMainService implements IBackupMainService { private workspacesJsonContent: IBackupWorkspacesFormat; constructor( - @IEnvironmentService environmentService: IEnvironmentService + @IEnvironmentService environmentService: IEnvironmentService, + @ILifecycleMainService lifecycleService: ILifecycleMainService ) { this.backupHome = environmentService.backupHome; this.workspacesJsonPath = environmentService.backupWorkspacesPath; + + lifecycleService.onAfterUnload(this.onAfterUnloadWindow.bind(this)); + this.loadSync(); } + private onAfterUnloadWindow(vscodeWindow: VSCodeWindow) { + if (vscodeWindow.openedWorkspacePath) { + // Clear out workspace from workspaces.json if it doesn't have any backups + const workspaceResource = Uri.file(vscodeWindow.openedWorkspacePath); + if (!this.hasWorkspaceBackup(workspaceResource)) { + this.removeWorkspaceBackupPathSync(workspaceResource); + } + } + } + public getWorkspaceBackupPaths(): string[] { return this.workspacesJsonContent.folderWorkspaces; } diff --git a/src/vs/platform/backup/test/node/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts similarity index 96% rename from src/vs/platform/backup/test/node/backupMainService.test.ts rename to src/vs/platform/backup/test/electron-main/backupMainService.test.ts index 38ef5165de8..a1f7d3c0f79 100644 --- a/src/vs/platform/backup/test/node/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -15,12 +15,13 @@ import extfs = require('vs/base/node/extfs'); import pfs = require('vs/base/node/pfs'); import Uri from 'vs/base/common/uri'; import { TestEnvironmentService } from 'vs/test/utils/servicesTestUtils'; -import { BackupMainService } from 'vs/platform/backup/node/backupMainService'; +import { TestLifecycleService } from 'vs/code/test/electron-main/servicesTestUtils'; +import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; import { IBackupWorkspacesFormat } from 'vs/platform/backup/common/backup'; class TestBackupMainService extends BackupMainService { constructor(backupHome: string, backupWorkspacesPath: string) { - super(TestEnvironmentService); + super(TestEnvironmentService, new TestLifecycleService()); this.backupHome = backupHome; this.workspacesJsonPath = backupWorkspacesPath; diff --git a/src/vs/platform/lifecycle/common/mainLifecycle.ts b/src/vs/platform/lifecycle/common/mainLifecycle.ts new file mode 100644 index 00000000000..cbe34ac0169 --- /dev/null +++ b/src/vs/platform/lifecycle/common/mainLifecycle.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IVSCodeWindow } from 'vs/code/common/window'; +import { TPromise } from 'vs/base/common/winjs.base'; + +export const ILifecycleMainService = createDecorator('lifecycleMainService'); + +export interface ILifecycleMainService { + _serviceBrand: any; + + /** + * Will be true if an update was applied. Will only be true for each update once. + */ + wasUpdated: boolean; + + onBeforeQuit(clb: () => void): () => void; + onAfterUnload(clb: (vscodeWindow: IVSCodeWindow) => void): () => void; + ready(): void; + registerWindow(vscodeWindow: IVSCodeWindow): void; + unload(vscodeWindow: IVSCodeWindow): TPromise; + quit(fromUpdate?: boolean): TPromise; +} \ No newline at end of file diff --git a/src/vs/platform/update/electron-main/auto-updater.win32.ts b/src/vs/platform/update/electron-main/auto-updater.win32.ts index a1526cfad65..3d498f70da1 100644 --- a/src/vs/platform/update/electron-main/auto-updater.win32.ts +++ b/src/vs/platform/update/electron-main/auto-updater.win32.ts @@ -15,7 +15,7 @@ import { mkdirp } from 'vs/base/node/extfs'; import { isString } from 'vs/base/common/types'; import { Promise, TPromise } from 'vs/base/common/winjs.base'; import { download, asJson } from 'vs/base/node/request'; -import { ILifecycleService } from 'vs/code/electron-main/lifecycle'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle'; import { IRequestService } from 'vs/platform/request/node/request'; import { IAutoUpdater } from 'vs/platform/update/common/update'; import product from 'vs/platform/product'; @@ -36,7 +36,7 @@ export class Win32AutoUpdaterImpl extends EventEmitter implements IAutoUpdater { private updatePackagePath: string = null; constructor( - @ILifecycleService private lifecycleService: ILifecycleService, + @ILifecycleMainService private lifecycleService: ILifecycleMainService, @IRequestService private requestService: IRequestService ) { super(); diff --git a/src/vs/platform/update/electron-main/updateService.ts b/src/vs/platform/update/electron-main/updateService.ts index 0f27826a29d..462d60ce292 100644 --- a/src/vs/platform/update/electron-main/updateService.ts +++ b/src/vs/platform/update/electron-main/updateService.ts @@ -16,7 +16,7 @@ import { fromEventEmitter } from 'vs/base/node/event'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Win32AutoUpdaterImpl } from './auto-updater.win32'; import { LinuxAutoUpdaterImpl } from './auto-updater.linux'; -import { ILifecycleService } from 'vs/code/electron-main/lifecycle'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/common/mainLifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import product from 'vs/platform/product'; import { TPromise } from 'vs/base/common/winjs.base'; @@ -85,7 +85,7 @@ export class UpdateService implements IUpdateService { constructor( @IInstantiationService instantiationService: IInstantiationService, - @ILifecycleService private lifecycleService: ILifecycleService, + @ILifecycleMainService private lifecycleService: ILifecycleMainService, @IConfigurationService private configurationService: IConfigurationService, @ITelemetryService private telemetryService: ITelemetryService ) {