From 5103b15943e20ddef9828863dae460fda5959f42 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 12 Oct 2016 16:57:55 +0200 Subject: [PATCH] Implement IExtensionRuntimeService --- .../common/extensionManagement.ts | 7 +- .../node/extensionManagementService.ts | 2 +- .../platform/extensions/common/extensions.ts | 13 ++++ src/vs/platform/extensions/node/extensions.ts | 65 +++++++++++++++++++ src/vs/platform/storage/common/storage.ts | 11 +--- src/vs/test/utils/servicesTestUtils.ts | 4 -- .../electron-browser/extensionHost.ts | 21 ++---- src/vs/workbench/electron-browser/shell.ts | 6 +- src/vs/workbench/node/storage.ts | 35 +--------- 9 files changed, 95 insertions(+), 69 deletions(-) create mode 100644 src/vs/platform/extensions/node/extensions.ts diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 3ca0a4753d9..f4f7320de84 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -234,9 +234,4 @@ export interface IExtensionTipsService { } export const ExtensionsLabel = nls.localize('extensions', "Extensions"); -export const ExtensionsChannelId = 'extensions'; - -export const ExtensionsStorageModule = 'extensions'; -export interface IExtensionsStorageData { - disabledExtensions?: string[]; -} \ No newline at end of file +export const ExtensionsChannelId = 'extensions'; \ No newline at end of file diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 897dc4b967c..bb54bf60661 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -93,7 +93,7 @@ export class ExtensionManagementService implements IExtensionManagementService { private extensionsPath: string; private obsoletePath: string; private obsoleteFileLimiter: Limiter; - private disposables: IDisposable[]; + private disposables: IDisposable[] = []; private _onInstallExtension = new Emitter(); onInstallExtension: Event = this._onInstallExtension.event; diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index a45442f2ad9..b9a8424c9e5 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -7,6 +7,7 @@ import Severity from 'vs/base/common/severity'; import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { StorageScope } from 'vs/platform/storage/common/storage'; export interface IExtensionDescription { id: string; @@ -62,3 +63,15 @@ export interface IExtensionService { */ getExtensionsStatus(): { [id: string]: IExtensionsStatus }; } + +export interface IExtensionsStorageData { + disabled?: string[]; +} + +export const IExtensionsRuntimeService = createDecorator('extensionsRuntimeService'); + +export interface IExtensionsRuntimeService { + _serviceBrand: any; + getStoragePath(scope: StorageScope): string; + getDisabledExtensions(scope?: StorageScope): string[]; +} diff --git a/src/vs/platform/extensions/node/extensions.ts b/src/vs/platform/extensions/node/extensions.ts new file mode 100644 index 00000000000..2ac9c8e5ccf --- /dev/null +++ b/src/vs/platform/extensions/node/extensions.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { distinct } from 'vs/base/common/arrays'; +import * as paths from 'vs/base/common/paths'; +import { ConfigWatcher } from 'vs/base/node/config'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IExtensionsRuntimeService, IExtensionsStorageData } from 'vs/platform/extensions/common/extensions'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; + +export class ExtensionsRuntimeService implements IExtensionsRuntimeService { + + _serviceBrand: any; + + constructor( + @IStorageService private storageService: IStorageService + ) { + } + + public getStoragePath(scope: StorageScope): string { + return this.getPath(scope); + } + + public getDisabledExtensions(scope?: StorageScope): string[] { + if (scope) { + return this.getData(scope).disabled || []; + } + + const globalData = this.getData(StorageScope.GLOBAL).disabled || []; + const workspaceData = this.getData(StorageScope.WORKSPACE).disabled || []; + return distinct([...globalData, ...workspaceData]); + } + + private getData(scope: StorageScope): IExtensionsStorageData { + const path = this.getPath(scope); + if (path) { + const extensionsStorage = new ExtensionsStorage(path); + const data = extensionsStorage.data; + extensionsStorage.dispose(); + return data; + } + return {}; + } + + private getPath(scope: StorageScope): string { + const path = this.storageService.getStoragePath(scope); + return path ? paths.join(path, 'extensions.json') : void 0; + } +} + +export class ExtensionsStorage extends Disposable { + + private _watcher: ConfigWatcher; + + constructor(path: string) { + super(); + this._watcher = this._register(new ConfigWatcher(path, { changeBufferDelay: 300, defaultConfig: Object.create(null) })); + } + + public get data(): IExtensionsStorageData { + return this._watcher.getConfig(); + } +} \ No newline at end of file diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 81886b24abd..74b58bc651b 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -62,9 +62,6 @@ export interface IStorageService { */ getBoolean(key: string, scope?: StorageScope, defaultValue?: boolean): boolean; - // TODO:sandy: Following APIs are implemented only for Workspace scope. Yet to implement for - // global scope. - /** * Returns an absolute file path in which private state can be stored as JSON data * in separate modules. @@ -75,11 +72,6 @@ export interface IStorageService { * NOTE: This is not the same as the local storage used by the other APIs. */ getStoragePath(scope: StorageScope): string; - - /** - * Returns the data stored for the given module in the storage path {#getStoragePath()} - */ - getStorageData(module: string, scope?: StorageScope, defaultValue?: T): T; } export enum StorageScope { @@ -104,6 +96,5 @@ export const NullStorageService: IStorageService = { get(a, b, defaultValue) { return defaultValue; }, getInteger(a, b, defaultValue) { return defaultValue; }, getBoolean(a, b, defaultValue) { return defaultValue; }, - getStoragePath() { return void 0; }, - getStorageData(module: string, scope?: StorageScope, defaultValue?: T) { return defaultValue; } + getStoragePath(scope) { return void 0; }, }; diff --git a/src/vs/test/utils/servicesTestUtils.ts b/src/vs/test/utils/servicesTestUtils.ts index b2d555b044f..bcb8b63c1a1 100644 --- a/src/vs/test/utils/servicesTestUtils.ts +++ b/src/vs/test/utils/servicesTestUtils.ts @@ -309,10 +309,6 @@ export class TestStorageService extends EventEmitter implements IStorageService return this.storage.getStoragePath(scope); } - getStorageData(module: string, scope: StorageScope = StorageScope.GLOBAL, defaultValue?: T): T { - return this.storage.getStorageData(module, scope, defaultValue); - } - } export class TestEditorGroupService implements IEditorGroupService { diff --git a/src/vs/workbench/electron-browser/extensionHost.ts b/src/vs/workbench/electron-browser/extensionHost.ts index b79e70fa347..6f871040a56 100644 --- a/src/vs/workbench/electron-browser/extensionHost.ts +++ b/src/vs/workbench/electron-browser/extensionHost.ts @@ -24,8 +24,7 @@ import { ipcRenderer as ipc } from 'electron'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionDescription, IMessage } from 'vs/platform/extensions/common/extensions'; -import { IExtensionsStorageData, ExtensionsStorageModule } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionDescription, IMessage, IExtensionsRuntimeService } from 'vs/platform/extensions/common/extensions'; import { ExtensionScanner, MessagesCollector } from 'vs/workbench/node/extensionPoints'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import Event, { Emitter } from 'vs/base/common/event'; @@ -71,7 +70,8 @@ export class ExtensionHostProcessWorker { @ILifecycleService lifecycleService: ILifecycleService, @IInstantiationService private instantiationService: IInstantiationService, @IEnvironmentService private environmentService: IEnvironmentService, - @IStorageService private storageService: IStorageService + @IStorageService private storageService: IStorageService, + @IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService ) { // handle extension host lifecycle a bit special when we know we are developing an extension that runs inside this.isExtensionDevelopmentHost = !!environmentService.extensionDevelopmentPath; @@ -268,17 +268,10 @@ export class ExtensionHostProcessWorker { private getUserExtensions(version: string, collector: MessagesCollector): TPromise { return ExtensionScanner.scanExtensions(version, collector, this.environmentService.extensionsPath, false) - .then(extensionDescriptions => this.getDisabledExtensions() - .then(disabledExtensions => extensionDescriptions.filter(e => disabledExtensions.indexOf(`${e.publisher}.${e.name}`) === -1))); - } - - private getDisabledExtensions(): TPromise { - return this.getWorkspaceDisabledExtensions(); - } - - private getWorkspaceDisabledExtensions(): TPromise { - const extensionsData = this.storageService.getStorageData(ExtensionsStorageModule, StorageScope.WORKSPACE, {}); - return TPromise.wrap(extensionsData.disabledExtensions || []); + .then(extensionDescriptions => { + const disabledExtensions = this.extensionsRuntimeService.getDisabledExtensions(); + return disabledExtensions.length ? extensionDescriptions.filter(e => disabledExtensions.indexOf(`${e.publisher}.${e.name}`) === -1) : extensionDescriptions; + }); } private logExtensionHostMessage(logEntry: ILogEntry) { diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index eb132520cb2..2095d824b4c 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -65,7 +65,8 @@ import { IThreadService } from 'vs/workbench/services/thread/common/threadServic import { ICommandService } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/platform/commands/common/commandService'; import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace'; -import { IExtensionService } from 'vs/platform/extensions/common/extensions'; +import { IExtensionService, IExtensionsRuntimeService } from 'vs/platform/extensions/common/extensions'; +import { ExtensionsRuntimeService } from 'vs/platform/extensions/node/extensions'; import { MainThreadModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; @@ -291,6 +292,9 @@ export class WorkbenchShell { this.toUnbind.push(lifecycleService.onShutdown(() => disposables.dispose())); serviceCollection.set(ILifecycleService, lifecycleService); + const extensionsRuntimeService = instantiationService.createInstance(ExtensionsRuntimeService); + serviceCollection.set(IExtensionsRuntimeService, extensionsRuntimeService); + const extensionHostProcessWorker = this.startExtensionHost(instantiationService); this.threadService = instantiationService.createInstance(MainThreadService, extensionHostProcessWorker.messagingProtocol); serviceCollection.set(IThreadService, this.threadService); diff --git a/src/vs/workbench/node/storage.ts b/src/vs/workbench/node/storage.ts index 56b6d8dde17..f400d8a83d2 100644 --- a/src/vs/workbench/node/storage.ts +++ b/src/vs/workbench/node/storage.ts @@ -7,14 +7,12 @@ import * as fs from 'fs'; import * as crypto from 'crypto'; import * as paths from 'vs/base/common/paths'; -import { IDisposable } from 'vs/base/common/lifecycle'; import types = require('vs/base/common/types'); import errors = require('vs/base/common/errors'); import strings = require('vs/base/common/strings'); import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ConfigWatcher } from 'vs/base/node/config'; // Browser localStorage interface export interface IStorage { @@ -200,7 +198,7 @@ export class Storage implements IStorageService { public getStoragePath(scope: StorageScope): string { if (StorageScope.GLOBAL === scope) { - return void 0; + return this.environmentService.appSettingsHome; } const workspace = this.contextService.getWorkspace(); @@ -255,18 +253,6 @@ export class Storage implements IStorageService { return this.workspaceStoragePath; } - getStorageData(module: string, scope: StorageScope = StorageScope.GLOBAL, defaultValue: T = Object.create(null)): T { - const storagePath = this.getStoragePath(scope); - if (!storagePath) { - return defaultValue; - } - const path = paths.join(storagePath, module + '.json'); - const storageData = new StorageData(path, defaultValue); - const data: T = storageData.getData(); - storageData.dispose(); - return data; - } - private toStorageKey(key: string, scope: StorageScope): string { if (scope === StorageScope.GLOBAL) { return Storage.GLOBAL_PREFIX + key.toLowerCase(); @@ -319,21 +305,4 @@ export class InMemoryLocalStorage implements IStorage { } } -export const inMemoryLocalStorageInstance = new InMemoryLocalStorage(); - -export class StorageData implements IDisposable { - - private _rawConfig: ConfigWatcher; - - constructor(path: string, defaultValue: T) { - this._rawConfig = new ConfigWatcher(path, { changeBufferDelay: 300, defaultConfig: defaultValue }); - } - - public getData(): T { - return this._rawConfig.getConfig(); - } - - public dispose() { - this._rawConfig.dispose(); - } -} \ No newline at end of file +export const inMemoryLocalStorageInstance = new InMemoryLocalStorage(); \ No newline at end of file