diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 7aafa8100b7acd82db3057ef3fb5686e9ee4b603..4757ede043cea60692a1b0dc5ed66aaa54812a08 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -99,7 +99,8 @@ function createPaths(environmentService: IEnvironmentService): TPromise { environmentService.appSettingsHome, environmentService.extensionsPath, environmentService.nodeCachedDataDir, - environmentService.logsPath + environmentService.logsPath, + environmentService.storageHome ]; return TPromise.join(paths.map(p => p && mkdirp(p))) as TPromise; diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 1057f3c6248441e414b2943d11640ce7a4bc0924..92708c50b7eef2afa19082a2115d3df513734acc 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -1174,6 +1174,7 @@ export class WindowsManager implements IWindowsMainService { const configuration: IWindowConfiguration = mixin({}, options.cli); // inherit all properties from CLI configuration.appRoot = this.environmentService.appRoot; configuration.machineId = this.machineId; + configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir; configuration.mainPid = process.pid; configuration.execPath = process.execPath; configuration.userEnv = assign({}, this.initialUserEnv, options.userEnv || {}); @@ -1188,7 +1189,6 @@ export class WindowsManager implements IWindowsMainService { configuration.filesToDiff = fileInputs.filesToDiff; configuration.filesToWait = fileInputs.filesToWait; } - configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir; // if we know the backup folder upfront (for empty windows to restore), we can set it // directly here which helps for restoring UI state associated with that window. diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 5ecc09395ae88e37481bc3ba6a40d4e028da5ab0..3370aac5de16447f41e27cb8eb036ee75d4d4dcd 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -105,6 +105,8 @@ export interface IEnvironmentService { backupHome: string; backupWorkspacesPath: string; + storageHome: string; + workspacesHome: string; isExtensionDevelopment: boolean; diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index a67945a33d97e81ace137a36424d539effbc258f..ceb11a2b4553b86e78c4989cb13c398b282d002a 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -124,6 +124,9 @@ export class EnvironmentService implements IEnvironmentService { @memoize get backupHome(): string { return path.join(this.userDataPath, 'Backups'); } + @memoize + get storageHome(): string { return path.join(this.userDataPath, 'Storage'); } + @memoize get backupWorkspacesPath(): string { return path.join(this.backupHome, 'workspaces.json'); } diff --git a/src/vs/platform/storage2/common/nextStorageService.ts b/src/vs/platform/storage2/common/nextStorageService.ts deleted file mode 100644 index fa8e24c409e118ba3504165b96e57ff24004ebf7..0000000000000000000000000000000000000000 --- a/src/vs/platform/storage2/common/nextStorageService.ts +++ /dev/null @@ -1,48 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event } from 'vs/base/common/event'; - -export const ID = 'nextStorageService'; - -export const INextStorageService = createDecorator(ID); - -export interface INextStorageService { - _serviceBrand: any; - - readonly onDidChangeStorage: Event>; - - /** - * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. - */ - get(key: string, fallbackValue?: string): string; - - /** - * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a boolean. - */ - getBoolean(key: string, fallbackValue?: boolean): boolean; - - /** - * Retrieve an element stored with the given key from storage. Use - * the provided defaultValue if the element is null or undefined. The element - * will be converted to a number using parseInt with a base of 10. - */ - getInteger(key: string, fallbackValue?: number): number; - - /** - * Store a string value under the given key to storage. The value will - * be converted to a string. - */ - set(key: string, value: any): Promise; - - /** - * Delete an element stored under the provided key from storage. - */ - delete(key: string): Promise; -} \ No newline at end of file diff --git a/src/vs/platform/storage2/common/nextWorkspaceStorageService.ts b/src/vs/platform/storage2/common/storage2.ts similarity index 66% rename from src/vs/platform/storage2/common/nextWorkspaceStorageService.ts rename to src/vs/platform/storage2/common/storage2.ts index bedb8bc663e2183e87afed153c60601df2f65fbe..8cfb683efd5074137a675262ce8db6be2f95fcac 100644 --- a/src/vs/platform/storage2/common/nextWorkspaceStorageService.ts +++ b/src/vs/platform/storage2/common/storage2.ts @@ -6,31 +6,56 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; -export const ID = 'nextWorkspaceStorageService'; +export const INextStorageService = createDecorator('nextStorageService'); -export const INextWorkspaceStorageService = createDecorator(ID); +export interface INextStorageService { + _serviceBrand: any; -export const enum StorageScope { + /** + * Emitted whenever data is updated or deleted. + */ + readonly onDidChangeStorage: Event>; /** - * The stored data will be scoped to all workspaces. + * Retrieve an element stored with the given key from storage. Use + * the provided defaultValue if the element is null or undefined. */ - GLOBAL, + get(key: string, fallbackValue?: string): string; /** - * The stored data will be scoped to the current workspace. + * Retrieve an element stored with the given key from storage. Use + * the provided defaultValue if the element is null or undefined. The element + * will be converted to a boolean. */ - WORKSPACE -} + getBoolean(key: string, fallbackValue?: boolean): boolean; -export interface IWorkspaceStorageChangeEvent { - keys: Set; - scope: StorageScope; + /** + * Retrieve an element stored with the given key from storage. Use + * the provided defaultValue if the element is null or undefined. The element + * will be converted to a number using parseInt with a base of 10. + */ + getInteger(key: string, fallbackValue?: number): number; + + /** + * Store a string value under the given key to storage. The value will + * be converted to a string. + */ + set(key: string, value: any): Promise; + + /** + * Delete an element stored under the provided key from storage. + */ + delete(key: string): Promise; } +export const INextWorkspaceStorageService = createDecorator('nextWorkspaceStorageService'); + export interface INextWorkspaceStorageService { _serviceBrand: any; + /** + * Emitted whenever data is updated or deleted. + */ readonly onDidChangeStorage: Event; /** @@ -78,4 +103,22 @@ export interface INextWorkspaceStorageService { * operation to either the current workspace only or all workspaces. */ delete(key: string, scope?: StorageScope): Promise; +} + +export const enum StorageScope { + + /** + * The stored data will be scoped to all workspaces. + */ + GLOBAL, + + /** + * The stored data will be scoped to the current workspace. + */ + WORKSPACE +} + +export interface IWorkspaceStorageChangeEvent { + keys: Set; + scope: StorageScope; } \ No newline at end of file diff --git a/src/vs/platform/storage2/node/nextStorageServiceImpl.ts b/src/vs/platform/storage2/node/nextStorageServiceImpl.ts index be049ba147886933bb564c3cff51b64caae140db..8f24402dadc3fec454092f5df38d8e9f10855014 100644 --- a/src/vs/platform/storage2/node/nextStorageServiceImpl.ts +++ b/src/vs/platform/storage2/node/nextStorageServiceImpl.ts @@ -6,7 +6,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { isUndefinedOrNull } from 'vs/base/common/types'; -import { INextStorageService } from 'vs/platform/storage2/common/nextStorageService'; +import { INextStorageService } from 'vs/platform/storage2/common/storage2'; import { SQLiteStorage } from 'vs/base/node/storage'; import { ILogService } from 'vs/platform/log/common/log'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; diff --git a/src/vs/platform/storage2/node/nextWorkspaceServiceImpl.ts b/src/vs/platform/storage2/node/nextWorkspaceStorageServiceImpl.ts similarity index 83% rename from src/vs/platform/storage2/node/nextWorkspaceServiceImpl.ts rename to src/vs/platform/storage2/node/nextWorkspaceStorageServiceImpl.ts index a4bbd9c6207aef372f653371635e7721bf3043c5..1ad4c513a5344f2322a63e84d54e9811972b8da5 100644 --- a/src/vs/platform/storage2/node/nextWorkspaceServiceImpl.ts +++ b/src/vs/platform/storage2/node/nextWorkspaceStorageServiceImpl.ts @@ -7,10 +7,8 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { ILogService } from 'vs/platform/log/common/log'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IWorkspaceStorageChangeEvent, INextWorkspaceStorageService, StorageScope } from 'vs/platform/storage2/common/nextWorkspaceStorageService'; +import { INextStorageService, IWorkspaceStorageChangeEvent, INextWorkspaceStorageService, StorageScope } from 'vs/platform/storage2/common/storage2'; import { NextStorageServiceImpl } from 'vs/platform/storage2/node/nextStorageServiceImpl'; -import { INextStorageService } from 'vs/platform/storage2/common/nextStorageService'; -import { ILifecycleService, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; export class NextWorkspaceStorageServiceImpl extends Disposable implements INextWorkspaceStorageService { _serviceBrand: any; @@ -24,8 +22,7 @@ export class NextWorkspaceStorageServiceImpl extends Disposable implements INext constructor( workspaceDBPath: string, @ILogService logService: ILogService, - @IEnvironmentService environmentService: IEnvironmentService, - @ILifecycleService private lifecycleService: ILifecycleService + @IEnvironmentService environmentService: IEnvironmentService ) { super(); @@ -38,18 +35,12 @@ export class NextWorkspaceStorageServiceImpl extends Disposable implements INext private registerListeners(): void { this._register(this.globalStorage.onDidChangeStorage(keys => this.handleDidChangeStorage(keys, StorageScope.GLOBAL))); this._register(this.workspaceStorage.onDidChangeStorage(keys => this.handleDidChangeStorage(keys, StorageScope.WORKSPACE))); - - this._register(this.lifecycleService.onShutdown(event => this.onShutdown(event))); } private handleDidChangeStorage(keys: Set, scope: StorageScope): void { this._onDidChangeStorage.fire({ keys, scope }); } - private onShutdown(event: ShutdownEvent): void { - event.join(this.close()); - } - init(): Promise { return Promise.all([this.globalStorage.init(), this.workspaceStorage.init()]).then(() => void 0); } @@ -68,7 +59,6 @@ export class NextWorkspaceStorageServiceImpl extends Disposable implements INext set(key: string, value: any, scope: StorageScope = StorageScope.GLOBAL): Promise { return this.getStorage(scope).set(key, value); - } delete(key: string, scope: StorageScope = StorageScope.GLOBAL): Promise { diff --git a/src/vs/platform/storage2/test/node/nextStorageService.test.ts b/src/vs/platform/storage2/test/node/nextStorageService.test.ts index 74f2dba5c7a8679f77cae64a3ae22c119c38b3b3..2c492bf4149f986c724abb92c4fc04dd169cd64b 100644 --- a/src/vs/platform/storage2/test/node/nextStorageService.test.ts +++ b/src/vs/platform/storage2/test/node/nextStorageService.test.ts @@ -10,7 +10,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'path'; import { tmpdir } from 'os'; import { equal, ok } from 'assert'; -import { INextStorageService } from 'vs/platform/storage2/common/nextStorageService'; +import { INextStorageService } from 'vs/platform/storage2/common/storage2'; import { del, mkdirp } from 'vs/base/node/pfs'; suite('Workbench NextStorageService', () => { diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index fd7aa2c06150fb432bf691d7f1d51d562ffaaccc..23d32baf3f6987b138ea3ae4b1659292e85d49b1 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import * as perf from 'vs/base/common/performance'; -import { TPromise } from 'vs/base/common/winjs.base'; import { WorkbenchShell } from 'vs/workbench/electron-browser/shell'; import * as browser from 'vs/base/browser/browser'; import { domContentLoaded } from 'vs/base/browser/dom'; @@ -37,6 +36,7 @@ import { IWorkspacesService, ISingleFolderWorkspaceIdentifier } from 'vs/platfor import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import * as fs from 'fs'; import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log'; +import { NextWorkspaceStorageServiceImpl } from 'vs/platform/storage2/node/nextWorkspaceStorageServiceImpl'; import { IssueChannelClient } from 'vs/platform/issue/node/issueIpc'; import { IIssueService } from 'vs/platform/issue/common/issue'; import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; @@ -48,26 +48,24 @@ import { sanitizeFilePath } from 'vs/base/node/extfs'; gracefulFs.gracefulify(fs); // enable gracefulFs -export function startup(configuration: IWindowConfiguration): TPromise { +export function startup(configuration: IWindowConfiguration): Promise { + // Massage configuration file URIs revive(configuration); + // Setup perf perf.importEntries(configuration.perfEntries); - // Ensure others can listen to zoom level changes - browser.setZoomFactor(webFrame.getZoomFactor()); - - // See https://github.com/Microsoft/vscode/issues/26151 - // Can be trusted because we are not setting it ourselves. - browser.setZoomLevel(webFrame.getZoomLevel(), true /* isTrusted */); - + // Browser config + browser.setZoomFactor(webFrame.getZoomFactor()); // Ensure others can listen to zoom level changes + browser.setZoomLevel(webFrame.getZoomLevel(), true /* isTrusted */); // Can be trusted because we are not setting it ourselves (https://github.com/Microsoft/vscode/issues/26151) browser.setFullscreen(!!configuration.fullscreen); + browser.setAccessibilitySupport(configuration.accessibilitySupport ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled); + // Keyboard support KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(); - browser.setAccessibilitySupport(configuration.accessibilitySupport ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled); - - // Setup Intl + // Setup Intl for comparers comparer.setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })); // Open workbench @@ -78,6 +76,7 @@ function revive(workbench: IWindowConfiguration) { if (workbench.folderUri) { workbench.folderUri = uri.revive(workbench.folderUri); } + const filesToWaitPaths = workbench.filesToWait && workbench.filesToWait.paths; [filesToWaitPaths, workbench.filesToOpen, workbench.filesToCreate, workbench.filesToDiff].forEach(paths => { if (Array.isArray(paths)) { @@ -90,30 +89,42 @@ function revive(workbench: IWindowConfiguration) { }); } -function openWorkbench(configuration: IWindowConfiguration): TPromise { +function openWorkbench(configuration: IWindowConfiguration): Promise { const mainProcessClient = new ElectronIPCClient(`window:${configuration.windowId}`); const mainServices = createMainProcessServices(mainProcessClient, configuration); const environmentService = new EnvironmentService(configuration, configuration.execPath); + const logService = createLogService(mainProcessClient, configuration, environmentService); logService.trace('openWorkbench configuration', JSON.stringify(configuration)); - // Since the configuration service is one of the core services that is used in so many places, we initialize it - // right before startup of the workbench shell to have its data ready for consumers - return createAndInitializeWorkspaceService(configuration, environmentService).then(workspaceService => { + return Promise.all([ + createAndInitializeWorkspaceService(configuration, environmentService), + createNextWorkspaceStorageService(environmentService, logService) + ]).then(services => { + const workspaceService = services[0]; + const nextStorageService = services[1]; const storageService = createStorageService(workspaceService, environmentService); return domContentLoaded().then(() => { - - // Open Shell perf.mark('willStartWorkbench'); + + // Create Shell const shell = new WorkbenchShell(document.body, { contextService: workspaceService, configurationService: workspaceService, environmentService, logService, - storageService + storageService, + nextStorageService }, mainServices, mainProcessClient, configuration); + + // Gracefully Shutdown Storage + shell.onShutdown(event => { + event.join(nextStorageService.close()); + }); + + // Open Shell shell.open(); // Inform user about loading issues from the loader @@ -128,20 +139,19 @@ function openWorkbench(configuration: IWindowConfiguration): TPromise { }); } -function createAndInitializeWorkspaceService(configuration: IWindowConfiguration, environmentService: EnvironmentService): TPromise { +function createAndInitializeWorkspaceService(configuration: IWindowConfiguration, environmentService: EnvironmentService): Promise { return validateFolderUri(configuration.folderUri, configuration.verbose).then(validatedFolderUri => { - const workspaceService = new WorkspaceService(environmentService); return workspaceService.initialize(configuration.workspace || validatedFolderUri || configuration).then(() => workspaceService, error => workspaceService); }); } -function validateFolderUri(folderUri: ISingleFolderWorkspaceIdentifier, verbose: boolean): TPromise { +function validateFolderUri(folderUri: ISingleFolderWorkspaceIdentifier, verbose: boolean): Promise { // Return early if we do not have a single folder uri or if it is a non file uri if (!folderUri || folderUri.scheme !== Schemas.file) { - return TPromise.as(folderUri); + return Promise.resolve(folderUri); } // Ensure absolute existing folder path @@ -156,6 +166,12 @@ function validateFolderUri(folderUri: ISingleFolderWorkspaceIdentifier, verbose: }); } +function createNextWorkspaceStorageService(environmentService: IEnvironmentService, logService: ILogService): Promise { + const nextStorageService = new NextWorkspaceStorageServiceImpl(':memory:', logService, environmentService); + + return nextStorageService.init().then(() => nextStorageService); +} + function createStorageService(workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageService { let workspaceId: string; let secondaryWorkspaceId: number; @@ -203,6 +219,7 @@ function createLogService(mainProcessClient: ElectronIPCClient, configuration: I const consoleLogService = new ConsoleLogService(configuration.logLevel); const logService = new MultiplexLogService([consoleLogService, spdlogService]); const logLevelClient = new LogLevelSetterChannelClient(mainProcessClient.getChannel('loglevel')); + return new FollowerLogService(logLevelClient, logService); } diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 5a8a149142d1f939e8bd29afc7a0508789146079..09047d7fb18c9473ab003e204bb71f1dc8aa2ec4 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -43,8 +43,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { InstantiationService } from 'vs/platform/instantiation/node/instantiationService'; -// import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -import { ILifecycleService, LifecyclePhase, ShutdownReason, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService, LifecyclePhase, ShutdownReason, StartupKind, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ISearchService, ISearchHistoryService } from 'vs/platform/search/common/search'; @@ -76,6 +75,8 @@ import { IBroadcastService, BroadcastService } from 'vs/platform/broadcast/elect import { HashService } from 'vs/workbench/services/hash/node/hashService'; import { IHashService } from 'vs/workbench/services/hash/common/hashService'; import { ILogService } from 'vs/platform/log/common/log'; +import { INextWorkspaceStorageService } from 'vs/platform/storage2/common/storage2'; +import { Event, Emitter } from 'vs/base/common/event'; import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; import { stat } from 'fs'; import { join } from 'path'; @@ -109,6 +110,7 @@ export interface ICoreServices { environmentService: IEnvironmentService; logService: ILogService; storageService: IStorageService; + nextStorageService: INextWorkspaceStorageService; } /** @@ -116,7 +118,12 @@ export interface ICoreServices { * With the Shell being the top level element in the page, it is also responsible for driving the layouting. */ export class WorkbenchShell extends Disposable { + + private readonly _onShutdown = this._register(new Emitter()); + get onShutdown(): Event { return this._onShutdown.event; } + private storageService: IStorageService; + private nextStorageService: INextWorkspaceStorageService; private environmentService: IEnvironmentService; private logService: ILogService; private configurationService: IConfigurationService; @@ -147,6 +154,7 @@ export class WorkbenchShell extends Disposable { this.environmentService = coreServices.environmentService; this.logService = coreServices.logService; this.storageService = coreServices.storageService; + this.nextStorageService = coreServices.nextStorageService; this.mainProcessServices = mainProcessServices; @@ -326,8 +334,9 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(IEnvironmentService, this.environmentService); serviceCollection.set(ILabelService, new SyncDescriptor(LabelService)); serviceCollection.set(ILogService, this._register(this.logService)); - serviceCollection.set(IStorageService, this.storageService); + serviceCollection.set(INextWorkspaceStorageService, this.nextStorageService); + this.mainProcessServices.forEach((serviceIdentifier, serviceInstance) => { serviceCollection.set(serviceIdentifier, serviceInstance); }); @@ -353,7 +362,6 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(IHashService, new SyncDescriptor(HashService)); // Telemetry - if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); const config: ITelemetryServiceConfig = { @@ -380,7 +388,11 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(IDialogService, instantiationService.createInstance(DialogService)); const lifecycleService = instantiationService.createInstance(LifecycleService); - this._register(lifecycleService.onShutdown(event => this.dispose(event.reason))); + this._register(lifecycleService.onShutdown(event => { + this._onShutdown.fire(event); + + this.dispose(event.reason); + })); serviceCollection.set(ILifecycleService, lifecycleService); this.lifecycleService = lifecycleService;