From 2d2d7154961013e6de30c71700786cf3352513e5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 17 Oct 2018 19:03:20 +0200 Subject: [PATCH] storage - write workspace.json from renderer --- src/vs/workbench/api/node/extHost.protocol.ts | 2 +- .../api/node/extHostExtensionService.ts | 71 ++----------------- src/vs/workbench/electron-browser/main.ts | 53 ++++++++++---- src/vs/workbench/electron-browser/shell.ts | 7 +- .../electron-browser/extensionHost.ts | 3 +- .../common/textModelResolverService.ts | 4 +- 6 files changed, 58 insertions(+), 82 deletions(-) diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 24e06f4a85b..0736bcc56dd 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -48,7 +48,7 @@ export interface IEnvironment { appSettingsHome: URI; extensionDevelopmentLocationURI: URI; extensionTestsPath: string; - workspaceStorageHome: string; + workspaceStoragePath: string; } export interface IWorkspaceData { diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index aa238c9897d..f1ff54df8a1 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -5,14 +5,14 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { join } from 'path'; -import { mkdirp, dirExists, realpath, writeFile } from 'vs/base/node/pfs'; +import { realpath } from 'vs/base/node/pfs'; import Severity from 'vs/base/common/severity'; import { TPromise } from 'vs/base/common/winjs.base'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; import { createApiFactory, initializeExtensionApi } from 'vs/workbench/api/node/extHost.api.impl'; -import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape, IMainContext } from './extHost.protocol'; +import { MainContext, MainThreadExtensionServiceShape, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape, IMainContext } from './extHost.protocol'; import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule, ExtensionActivationTimesBuilder, ExtensionActivationTimes, ExtensionActivationReason, ExtensionActivatedByEvent, ExtensionActivatedByAPI } from 'vs/workbench/api/node/extHostExtensionActivator'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; @@ -73,71 +73,13 @@ class ExtensionMemento implements IExtensionMemento { } } -class ExtensionStoragePath { - - private readonly _workspace: IWorkspaceData; - private readonly _environment: IEnvironment; - - private readonly _ready: Promise; - private _value: string; - - constructor(workspace: IWorkspaceData, environment: IEnvironment) { - this._workspace = workspace; - this._environment = environment; - this._ready = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value); - } - - get whenReady(): Promise { - return this._ready; - } - - value(extension: IExtensionDescription): string { - if (this._value) { - return join(this._value, extension.id); - } - return undefined; - } - - private async _getOrCreateWorkspaceStoragePath(): Promise { - if (!this._workspace) { - return TPromise.as(undefined); - } - - const storageName = this._workspace.id; - const storagePath = join(this._environment.workspaceStorageHome, storageName); - - const exists = await dirExists(storagePath); - - if (exists) { - return storagePath; - } - - try { - await mkdirp(storagePath); - await writeFile( - join(storagePath, 'meta.json'), - JSON.stringify({ - id: this._workspace.id, - configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(), - name: this._workspace.name - }, undefined, 2) - ); - return storagePath; - - } catch (e) { - console.error(e); - return undefined; - } - } -} - export class ExtHostExtensionService implements ExtHostExtensionServiceShape { private readonly _barrier: Barrier; private readonly _registry: ExtensionDescriptionRegistry; private readonly _mainThreadTelemetry: MainThreadTelemetryShape; private readonly _storage: ExtHostStorage; - private readonly _storagePath: ExtensionStoragePath; + private readonly _workspaceStoragePath: string; private readonly _proxy: MainThreadExtensionServiceShape; private readonly _extHostLogService: ExtHostLogService; private _activator: ExtensionsActivator; @@ -156,7 +98,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { this._extHostLogService = extHostLogService; this._mainThreadTelemetry = extHostContext.getProxy(MainContext.MainThreadTelemetry); this._storage = new ExtHostStorage(extHostContext); - this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment); + this._workspaceStoragePath = initData.environment.workspaceStoragePath; this._proxy = extHostContext.getProxy(MainContext.MainThreadExtensionService); this._activator = null; @@ -360,8 +302,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.id}`); return TPromise.join([ globalState.whenReady, - workspaceState.whenReady, - this._storagePath.whenReady + workspaceState.whenReady ]).then(() => { const that = this; return Object.freeze({ @@ -369,7 +310,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { workspaceState, subscriptions: [], get extensionPath() { return extensionDescription.extensionLocation.fsPath; }, - storagePath: this._storagePath.value(extensionDescription), + storagePath: this._workspaceStoragePath ? join(this._workspaceStoragePath, extensionDescription.id) : undefined, asAbsolutePath: (relativePath: string) => { return join(extensionDescription.extensionLocation.fsPath, relativePath); }, logPath: that._extHostLogService.getLogDirectory(extensionDescription.id) }); diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index c9d6814d9a8..05ab6b66aa3 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -16,7 +16,7 @@ import { IWorkspaceContextService, Workspace, WorkbenchState } from 'vs/platform import { WorkspaceService, ISingleFolderWorkspaceInitializationPayload, IMultiFolderWorkspaceInitializationPayload, IEmptyWorkspaceInitializationPayload, IWorkspaceInitializationPayload } from 'vs/workbench/services/configuration/node/configurationService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { stat, exists } from 'vs/base/node/pfs'; +import { stat, exists, writeFile } from 'vs/base/node/pfs'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import * as gracefulFs from 'graceful-fs'; import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; @@ -103,14 +103,14 @@ function openWorkbench(configuration: IWindowConfiguration): Promise { return createWorkspaceInitializationPayload(configuration, environmentService).then(payload => { // Prepare the workspace storage folder - return prepareWorkspaceStorageFolder(payload, environmentService).then(storagePath => { + return prepareWorkspaceStorageFolder(payload, environmentService).then(workspaceStoragePath => { return Promise.all([ // Create and load workspace/configuration service createWorkspaceService(payload, environmentService), // Create and load storage service - createStorageService(storagePath, environmentService, logService) + createStorageService(workspaceStoragePath, environmentService, logService) ]).then(services => { const workspaceService = services[0]; const storageLegacyService = createStorageLegacyService(workspaceService, environmentService); @@ -129,6 +129,11 @@ function openWorkbench(configuration: IWindowConfiguration): Promise { storageService }, mainServices, mainProcessClient, configuration); + // Store meta file in workspace storage after workbench is running + shell.onRunning(() => { + ensureWorkspaceStorageFolderMeta(workspaceStoragePath, workspaceService); + }); + // Gracefully Shutdown Storage shell.onShutdown(event => { event.join(storageService.close()); @@ -166,14 +171,16 @@ function createWorkspaceInitializationPayload(configuration: IWindowConfiguratio return workspaceInitializationPayload.then(payload => { - // Fallback to empty workspace if we have no payload yet. We know the - // backupPath must be a unique path so we leverage its name as workspace ID + // Fallback to empty workspace if we have no payload yet. if (!payload) { - if (!configuration.backupPath) { - return Promise.reject(new Error('Unexpected missing backupPath for empty window')); // should not happen in this case + let id: string; + if (configuration.backupPath) { + id = basename(configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID + } else { + id = (Date.now() + Math.round(Math.random() * 1000)).toString(); // fallback to a random number otherwise (can happen in extension development window) } - payload = { id: basename(configuration.backupPath) } as IEmptyWorkspaceInitializationPayload; + payload = { id } as IEmptyWorkspaceInitializationPayload; } return payload; @@ -231,17 +238,39 @@ function resolveSingleFolderWorkspaceInitializationPayload(folderUri: ISingleFol function prepareWorkspaceStorageFolder(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService): Thenable { // Workspace storage: scope by workspace identifier - const storagePath = join(environmentService.workspaceStorageHome, payload.id); + const workspaceStoragePath = join(environmentService.workspaceStorageHome, payload.id); - return exists(storagePath).then(exists => { + return exists(workspaceStoragePath).then(exists => { if (exists) { - return storagePath; + return workspaceStoragePath; } - return mkdirp(storagePath).then(() => storagePath); + return mkdirp(workspaceStoragePath).then(() => workspaceStoragePath); }); } +function ensureWorkspaceStorageFolderMeta(workspaceStoragePath: string, workspaceService: IWorkspaceContextService): void { + const state = workspaceService.getWorkbenchState(); + if (state === WorkbenchState.EMPTY) { + return; // no storage meta for empty workspaces + } + + const workspaceStorageMetaPath = join(workspaceStoragePath, 'workspace.json'); + + exists(workspaceStorageMetaPath).then(exists => { + if (exists) { + return void 0; // already existing + } + + const workspace = workspaceService.getWorkspace(); + + return writeFile(workspaceStorageMetaPath, JSON.stringify({ + configuration: workspace.configuration ? uri.revive(workspace.configuration).toString() : void 0, + folder: state === WorkbenchState.FOLDER ? uri.revive(workspace.folders[0].uri).toString() : void 0 + }, undefined, 2)); + }).then(null, error => errors.onUnexpectedError(error)); +} + function createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService): Promise { const workspaceService = new WorkspaceService(environmentService); diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 56d2972a7a0..f9a48e4c942 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -122,6 +122,9 @@ export class WorkbenchShell extends Disposable { private readonly _onShutdown = this._register(new Emitter()); get onShutdown(): Event { return this._onShutdown.event; } + private readonly _onRunning = this._register(new Emitter()); + get onRunning(): Event { return this._onRunning.event; } + private storageLegacyService: IStorageLegacyService; private storageService: DelegatingStorageService; private environmentService: IEnvironmentService; @@ -212,8 +215,10 @@ export class WorkbenchShell extends Disposable { // Startup Workbench workbench.startup().then(startupInfos => { - // Set lifecycle phase to `Runnning` so that other contributions can now do something + // Set lifecycle phase to `Runnning` so that other contributions can + // now do something we also emit this as event to interested parties outside this.lifecycleService.phase = LifecyclePhase.Running; + this._onRunning.fire(); // Startup Telemetry this.logStartupTelemetry(startupInfos); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 256dcd4b2bb..d7339bc1891 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -40,6 +40,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { join } from 'path'; export interface IExtensionHostStarter { readonly onCrashed: Event<[number, string]>; @@ -412,7 +413,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { appSettingsHome: this._environmentService.appSettingsHome ? URI.file(this._environmentService.appSettingsHome) : void 0, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsPath: this._environmentService.extensionTestsPath, - workspaceStorageHome: this._environmentService.workspaceStorageHome + workspaceStoragePath: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? void 0 : join(this._environmentService.workspaceStorageHome, workspace.id) }, workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : { configuration: workspace.configuration, diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index 6245c20d21c..2ccbcb81959 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -28,7 +28,7 @@ class ResourceModelCollection extends ReferenceCollection this.createReferencedObject(key, true)); + return this.extensionService.activateByEvent('onFileSystem:' + resource.scheme).then(() => this.createReferencedObject(key, true)); } return TPromise.wrapError(new Error('resource is not available')); -- GitLab