提交 2d2d7154 编写于 作者: B Benjamin Pasero

storage - write workspace.json from renderer

上级 f7adbed3
......@@ -48,7 +48,7 @@ export interface IEnvironment {
appSettingsHome: URI;
extensionDevelopmentLocationURI: URI;
extensionTestsPath: string;
workspaceStorageHome: string;
workspaceStoragePath: string;
}
export interface IWorkspaceData {
......
......@@ -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<string>;
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<any> {
return this._ready;
}
value(extension: IExtensionDescription): string {
if (this._value) {
return join(this._value, extension.id);
}
return undefined;
}
private async _getOrCreateWorkspaceStoragePath(): Promise<string> {
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(<IExtensionContext>{
......@@ -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)
});
......
......@@ -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<void> {
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<void> {
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<string> {
// 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<WorkspaceService> {
const workspaceService = new WorkspaceService(environmentService);
......
......@@ -122,6 +122,9 @@ export class WorkbenchShell extends Disposable {
private readonly _onShutdown = this._register(new Emitter<ShutdownEvent>());
get onShutdown(): Event<ShutdownEvent> { return this._onShutdown.event; }
private readonly _onRunning = this._register(new Emitter<void>());
get onRunning(): Event<void> { 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);
......
......@@ -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,
......
......@@ -28,7 +28,7 @@ class ResourceModelCollection extends ReferenceCollection<TPromise<ITextEditorMo
@IInstantiationService private instantiationService: IInstantiationService,
@ITextFileService private textFileService: ITextFileService,
@IFileService private fileService: IFileService,
@IExtensionService private readonly _extensionService: IExtensionService,
@IExtensionService private extensionService: IExtensionService,
) {
super();
}
......@@ -50,7 +50,7 @@ class ResourceModelCollection extends ReferenceCollection<TPromise<ITextEditorMo
// Either unknown schema, or not yet registered
if (!skipActivateExtensions) {
return this._extensionService.activateByEvent('onFileSystem:' + resource.scheme).then(() => this.createReferencedObject(key, true));
return this.extensionService.activateByEvent('onFileSystem:' + resource.scheme).then(() => this.createReferencedObject(key, true));
}
return TPromise.wrapError<ITextEditorModel>(new Error('resource is not available'));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册