mirror of
https://github.com/Microsoft/vscode
synced 2024-09-18 01:58:27 +00:00
fix #13734
This commit is contained in:
parent
1fce4258a9
commit
e6d385f258
|
@ -61,14 +61,6 @@
|
||||||
{
|
{
|
||||||
"fileMatch": "/.vscode/extensions.json",
|
"fileMatch": "/.vscode/extensions.json",
|
||||||
"url": "vscode://schemas/extensions"
|
"url": "vscode://schemas/extensions"
|
||||||
},
|
|
||||||
{
|
|
||||||
"fileMatch": "%APP_SETTINGS_HOME%/extensions.json",
|
|
||||||
"url": "vscode://schemas/extensionsstorage"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fileMatch": "%APP_SETTINGS_HOME%/**/extensions.json",
|
|
||||||
"url": "vscode://schemas/extensionsstorage"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,14 +64,10 @@ export interface IExtensionService {
|
||||||
getExtensionsStatus(): { [id: string]: IExtensionsStatus };
|
getExtensionsStatus(): { [id: string]: IExtensionsStatus };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IExtensionsStorageData {
|
|
||||||
disabled?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const IExtensionsRuntimeService = createDecorator<IExtensionsRuntimeService>('extensionsRuntimeService');
|
export const IExtensionsRuntimeService = createDecorator<IExtensionsRuntimeService>('extensionsRuntimeService');
|
||||||
|
|
||||||
export interface IExtensionsRuntimeService {
|
export interface IExtensionsRuntimeService {
|
||||||
_serviceBrand: any;
|
_serviceBrand: any;
|
||||||
getStoragePath(scope: StorageScope): string;
|
// setEnablement(id: string, enable: boolean, displayName: string): TPromise<boolean>;
|
||||||
getDisabledExtensions(scope?: StorageScope): string[];
|
getDisabledExtensions(scope?: StorageScope): string[];
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,17 +61,6 @@ export interface IStorageService {
|
||||||
* The optional scope argument allows to define the scope of the operation.
|
* The optional scope argument allows to define the scope of the operation.
|
||||||
*/
|
*/
|
||||||
getBoolean(key: string, scope?: StorageScope, defaultValue?: boolean): boolean;
|
getBoolean(key: string, scope?: StorageScope, defaultValue?: boolean): boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an absolute file path in which private state can be stored as JSON data
|
|
||||||
* in separate modules.
|
|
||||||
*
|
|
||||||
* For workspace scope {StorageScope.WORKSPACE}, a workspace specific directory
|
|
||||||
* under global scope is returned.
|
|
||||||
*
|
|
||||||
* NOTE: This is not the same as the local storage used by the other APIs.
|
|
||||||
*/
|
|
||||||
getStoragePath(scope: StorageScope): string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum StorageScope {
|
export enum StorageScope {
|
||||||
|
@ -95,6 +84,5 @@ export const NullStorageService: IStorageService = {
|
||||||
remove() { return undefined; },
|
remove() { return undefined; },
|
||||||
get(a, b, defaultValue) { return defaultValue; },
|
get(a, b, defaultValue) { return defaultValue; },
|
||||||
getInteger(a, b, defaultValue) { return defaultValue; },
|
getInteger(a, b, defaultValue) { return defaultValue; },
|
||||||
getBoolean(a, b, defaultValue) { return defaultValue; },
|
getBoolean(a, b, defaultValue) { return defaultValue; }
|
||||||
getStoragePath(scope) { return void 0; },
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||||
import * as paths from 'vs/base/common/paths';
|
import * as paths from 'vs/base/common/paths';
|
||||||
import URI from 'vs/base/common/uri';
|
import URI from 'vs/base/common/uri';
|
||||||
import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
import { ITelemetryService, NullTelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||||
import { Storage, InMemoryLocalStorage } from 'vs/workbench/node/storage';
|
import { Storage, InMemoryLocalStorage } from 'vs/workbench/common/storage';
|
||||||
import { IEditorGroup, ConfirmResult } from 'vs/workbench/common/editor';
|
import { IEditorGroup, ConfirmResult } from 'vs/workbench/common/editor';
|
||||||
import Event, { Emitter } from 'vs/base/common/event';
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
import Severity from 'vs/base/common/severity';
|
import Severity from 'vs/base/common/severity';
|
||||||
|
@ -304,11 +304,6 @@ export class TestStorageService extends EventEmitter implements IStorageService
|
||||||
getBoolean(key: string, scope: StorageScope = StorageScope.GLOBAL, defaultValue?: boolean): boolean {
|
getBoolean(key: string, scope: StorageScope = StorageScope.GLOBAL, defaultValue?: boolean): boolean {
|
||||||
return this.storage.getBoolean(key, scope, defaultValue);
|
return this.storage.getBoolean(key, scope, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
getStoragePath(scope: StorageScope = StorageScope.GLOBAL): string {
|
|
||||||
return this.storage.getStoragePath(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TestEditorGroupService implements IEditorGroupService {
|
export class TestEditorGroupService implements IEditorGroupService {
|
||||||
|
|
|
@ -4,9 +4,6 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as crypto from 'crypto';
|
|
||||||
import * as paths from 'vs/base/common/paths';
|
|
||||||
import types = require('vs/base/common/types');
|
import types = require('vs/base/common/types');
|
||||||
import errors = require('vs/base/common/errors');
|
import errors = require('vs/base/common/errors');
|
||||||
import strings = require('vs/base/common/strings');
|
import strings = require('vs/base/common/strings');
|
||||||
|
@ -38,7 +35,6 @@ export class Storage implements IStorageService {
|
||||||
private globalStorage: IStorage;
|
private globalStorage: IStorage;
|
||||||
|
|
||||||
private workspaceKey: string;
|
private workspaceKey: string;
|
||||||
private workspaceStoragePath: string;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
globalStorage: IStorage,
|
globalStorage: IStorage,
|
||||||
|
@ -196,63 +192,6 @@ export class Storage implements IStorageService {
|
||||||
return value ? true : false;
|
return value ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getStoragePath(scope: StorageScope): string {
|
|
||||||
if (StorageScope.GLOBAL === scope) {
|
|
||||||
return this.environmentService.appSettingsHome;
|
|
||||||
}
|
|
||||||
|
|
||||||
const workspace = this.contextService.getWorkspace();
|
|
||||||
|
|
||||||
if (!workspace) {
|
|
||||||
return void 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.workspaceStoragePath) {
|
|
||||||
return this.workspaceStoragePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
function rmkDir(directory: string): boolean {
|
|
||||||
try {
|
|
||||||
fs.mkdirSync(directory);
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
if (err.code === 'ENOENT') {
|
|
||||||
if (rmkDir(paths.dirname(directory))) {
|
|
||||||
fs.mkdirSync(directory);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return fs.statSync(directory).isDirectory();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (workspace) {
|
|
||||||
const hash = crypto.createHash('md5');
|
|
||||||
hash.update(workspace.resource.fsPath);
|
|
||||||
if (workspace.uid) {
|
|
||||||
hash.update(workspace.uid.toString());
|
|
||||||
}
|
|
||||||
this.workspaceStoragePath = paths.join(this.environmentService.appSettingsHome, 'workspaceStorage', hash.digest('hex'));
|
|
||||||
if (!fs.existsSync(this.workspaceStoragePath)) {
|
|
||||||
try {
|
|
||||||
if (rmkDir(this.workspaceStoragePath)) {
|
|
||||||
fs.writeFileSync(paths.join(this.workspaceStoragePath, 'meta.json'), JSON.stringify({
|
|
||||||
workspacePath: workspace.resource.fsPath,
|
|
||||||
uid: workspace.uid ? workspace.uid : null
|
|
||||||
}, null, 4));
|
|
||||||
} else {
|
|
||||||
this.workspaceStoragePath = void 0;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
this.workspaceStoragePath = void 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.workspaceStoragePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private toStorageKey(key: string, scope: StorageScope): string {
|
private toStorageKey(key: string, scope: StorageScope): string {
|
||||||
if (scope === StorageScope.GLOBAL) {
|
if (scope === StorageScope.GLOBAL) {
|
||||||
return Storage.GLOBAL_PREFIX + key.toLowerCase();
|
return Storage.GLOBAL_PREFIX + key.toLowerCase();
|
|
@ -21,7 +21,7 @@ import pkg from 'vs/platform/package';
|
||||||
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
|
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
|
||||||
import timer = require('vs/base/common/timer');
|
import timer = require('vs/base/common/timer');
|
||||||
import { Workbench } from 'vs/workbench/electron-browser/workbench';
|
import { Workbench } from 'vs/workbench/electron-browser/workbench';
|
||||||
import { Storage, inMemoryLocalStorageInstance } from 'vs/workbench/node/storage';
|
import { Storage, inMemoryLocalStorageInstance } from 'vs/workbench/common/storage';
|
||||||
import { ITelemetryService, NullTelemetryService, loadExperiments } from 'vs/platform/telemetry/common/telemetry';
|
import { ITelemetryService, NullTelemetryService, loadExperiments } from 'vs/platform/telemetry/common/telemetry';
|
||||||
import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
|
import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc';
|
||||||
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
|
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/co
|
||||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||||
import { VIEWLET_ID, IExtensionsWorkbenchService } from './extensions';
|
import { VIEWLET_ID, IExtensionsWorkbenchService } from './extensions';
|
||||||
import { ExtensionsWorkbenchService } from './extensionsWorkbenchService';
|
import { ExtensionsWorkbenchService } from './extensionsWorkbenchService';
|
||||||
import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, UpdateAllAction, OpenExtensionsFolderAction, ConfigureWorkspaceRecommendedExtensionsAction, OpenWorkspaceExtensionsStorageFile, OpenGlobalExtensionsStorageFile, InstallVSIXAction } from './extensionsActions';
|
import { OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowWorkspaceRecommendedExtensionsAction, ShowPopularExtensionsAction, ShowInstalledExtensionsAction, ShowDisabledExtensionsAction, UpdateAllAction, OpenExtensionsFolderAction, ConfigureWorkspaceRecommendedExtensionsAction, InstallVSIXAction } from './extensionsActions';
|
||||||
import { ExtensionsInput } from './extensionsInput';
|
import { ExtensionsInput } from './extensionsInput';
|
||||||
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet';
|
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet';
|
||||||
import { ExtensionEditor } from './extensionEditor';
|
import { ExtensionEditor } from './extensionEditor';
|
||||||
|
@ -28,7 +28,7 @@ import { StatusUpdater } from './extensionsViewlet';
|
||||||
import { IQuickOpenRegistry, Extensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
|
import { IQuickOpenRegistry, Extensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
|
||||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||||
import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry');
|
import jsonContributionRegistry = require('vs/platform/jsonschemas/common/jsonContributionRegistry');
|
||||||
import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId, ExtensionsStorageSchema, ExtensionsStorageSchemaId } from 'vs/workbench/parts/extensions/electron-browser/extensionsFileTemplate';
|
import { ExtensionsConfigurationSchema, ExtensionsConfigurationSchemaId } from 'vs/workbench/parts/extensions/electron-browser/extensionsFileTemplate';
|
||||||
|
|
||||||
// Singletons
|
// Singletons
|
||||||
registerSingleton(IExtensionGalleryService, ExtensionGalleryService);
|
registerSingleton(IExtensionGalleryService, ExtensionGalleryService);
|
||||||
|
@ -122,12 +122,6 @@ actionRegistry.registerWorkbenchAction(openExtensionsFolderActionDescriptor, 'Ex
|
||||||
const openExtensionsFileActionDescriptor = new SyncActionDescriptor(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL);
|
const openExtensionsFileActionDescriptor = new SyncActionDescriptor(ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceRecommendedExtensionsAction.ID, ConfigureWorkspaceRecommendedExtensionsAction.LABEL);
|
||||||
actionRegistry.registerWorkbenchAction(openExtensionsFileActionDescriptor, 'Extensions: Open Extensions File', ExtensionsLabel);
|
actionRegistry.registerWorkbenchAction(openExtensionsFileActionDescriptor, 'Extensions: Open Extensions File', ExtensionsLabel);
|
||||||
|
|
||||||
const disableExtensionsActionDescriptor = new SyncActionDescriptor(OpenGlobalExtensionsStorageFile, OpenGlobalExtensionsStorageFile.ID, localize('disableGlobalExtensions', "Configure Disabled Extensions"));
|
|
||||||
actionRegistry.registerWorkbenchAction(disableExtensionsActionDescriptor, 'Extensions: Configure Disabled Extensions', ExtensionsLabel);
|
|
||||||
|
|
||||||
const disableWorkspaceExtensionsActionDescriptor = new SyncActionDescriptor(OpenWorkspaceExtensionsStorageFile, OpenWorkspaceExtensionsStorageFile.ID, localize('disableWorkspaceExtensions', "Configure Disabled Extensions (Workspace)"));
|
|
||||||
actionRegistry.registerWorkbenchAction(disableWorkspaceExtensionsActionDescriptor, 'Extensions: Configure Disabled Extensions (Workspace)', ExtensionsLabel);
|
|
||||||
|
|
||||||
const installVSIXActionDescriptor = new SyncActionDescriptor(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL);
|
const installVSIXActionDescriptor = new SyncActionDescriptor(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL);
|
||||||
actionRegistry.registerWorkbenchAction(installVSIXActionDescriptor, 'Extensions: Install from VSIX...', ExtensionsLabel);
|
actionRegistry.registerWorkbenchAction(installVSIXActionDescriptor, 'Extensions: Install from VSIX...', ExtensionsLabel);
|
||||||
|
|
||||||
|
@ -148,4 +142,3 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||||
|
|
||||||
const jsonRegistry = <jsonContributionRegistry.IJSONContributionRegistry>Registry.as(jsonContributionRegistry.Extensions.JSONContribution);
|
const jsonRegistry = <jsonContributionRegistry.IJSONContributionRegistry>Registry.as(jsonContributionRegistry.Extensions.JSONContribution);
|
||||||
jsonRegistry.registerSchema(ExtensionsConfigurationSchemaId, ExtensionsConfigurationSchema);
|
jsonRegistry.registerSchema(ExtensionsConfigurationSchemaId, ExtensionsConfigurationSchema);
|
||||||
jsonRegistry.registerSchema(ExtensionsStorageSchemaId, ExtensionsStorageSchema);
|
|
|
@ -14,7 +14,6 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||||
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
|
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
|
||||||
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, ConfigurationKey } from './extensions';
|
import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, ConfigurationKey } from './extensions';
|
||||||
import { LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
|
import { LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||||
import { IExtensionsRuntimeService } from 'vs/platform/extensions/common/extensions';
|
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { IMessageService, LaterAction } from 'vs/platform/message/common/message';
|
import { IMessageService, LaterAction } from 'vs/platform/message/common/message';
|
||||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||||
|
@ -23,11 +22,10 @@ import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletSer
|
||||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||||
import { Query } from '../common/extensionQuery';
|
import { Query } from '../common/extensionQuery';
|
||||||
import { shell, remote } from 'electron';
|
import { shell, remote } from 'electron';
|
||||||
import { ExtensionsConfigurationInitialContent, ExtensionStorageInitialContent } from 'vs/workbench/parts/extensions/electron-browser/extensionsFileTemplate';
|
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/electron-browser/extensionsFileTemplate';
|
||||||
import { IFileService } from 'vs/platform/files/common/files';
|
import { IFileService } from 'vs/platform/files/common/files';
|
||||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||||
import URI from 'vs/base/common/uri';
|
import URI from 'vs/base/common/uri';
|
||||||
import { StorageScope } from 'vs/platform/storage/common/storage';
|
|
||||||
|
|
||||||
const dialog = remote.dialog;
|
const dialog = remote.dialog;
|
||||||
|
|
||||||
|
@ -662,80 +660,6 @@ export class ConfigureWorkspaceRecommendedExtensionsAction extends Action {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class OpenExtensionsStorageFile extends Action {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
id: string,
|
|
||||||
label: string,
|
|
||||||
enabled: boolean,
|
|
||||||
private scope: StorageScope,
|
|
||||||
@IFileService private fileService: IFileService,
|
|
||||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
|
|
||||||
@IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService
|
|
||||||
) {
|
|
||||||
super(id, label, null, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public run(event: any): TPromise<any> {
|
|
||||||
return this.openExtensionsStorageFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
private openExtensionsStorageFile(): TPromise<any> {
|
|
||||||
return this.getOrCreateExtensionsFile().then(value => {
|
|
||||||
return this.editorService.openEditor({
|
|
||||||
resource: value.extensionsFileResource,
|
|
||||||
options: {
|
|
||||||
forceOpen: true,
|
|
||||||
pinned: value.created
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}, (error) => TPromise.wrapError(new Error(localize('OpenGlobalExtensionsStorageFile.failed', "Unable to create 'extensions.json' file inside the '{0}' folder ({1}).", this.extensionsRuntimeService.getStoragePath(this.scope), error))));
|
|
||||||
}
|
|
||||||
|
|
||||||
private getOrCreateExtensionsFile(): TPromise<{ created: boolean, extensionsFileResource: URI }> {
|
|
||||||
const extensionsFileResource = URI.file(this.extensionsRuntimeService.getStoragePath(this.scope));
|
|
||||||
|
|
||||||
return this.fileService.resolveContent(extensionsFileResource).then(content => {
|
|
||||||
return { created: false, extensionsFileResource };
|
|
||||||
}, err => {
|
|
||||||
return this.fileService.updateContent(extensionsFileResource, ExtensionStorageInitialContent).then(() => {
|
|
||||||
return { created: true, extensionsFileResource };
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class OpenWorkspaceExtensionsStorageFile extends OpenExtensionsStorageFile {
|
|
||||||
|
|
||||||
static ID = 'workbench.extensions.action.openWorkspaceExtensionsStorageFile';
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
id: string,
|
|
||||||
label: string,
|
|
||||||
@IFileService fileService: IFileService,
|
|
||||||
@IWorkspaceContextService contextService: IWorkspaceContextService,
|
|
||||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
|
|
||||||
@IExtensionsRuntimeService extensionsRuntimeService: IExtensionsRuntimeService
|
|
||||||
) {
|
|
||||||
super(id, label, !!contextService.getWorkspace(), StorageScope.WORKSPACE, fileService, editorService, extensionsRuntimeService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class OpenGlobalExtensionsStorageFile extends OpenExtensionsStorageFile {
|
|
||||||
|
|
||||||
static ID = 'workbench.extensions.action.openGlobalExtensionsStorageFile';
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
id: string,
|
|
||||||
label: string,
|
|
||||||
@IFileService fileService: IFileService,
|
|
||||||
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
|
|
||||||
@IExtensionsRuntimeService extensionsRuntimeService: IExtensionsRuntimeService
|
|
||||||
) {
|
|
||||||
super(id, label, true, StorageScope.GLOBAL, fileService, editorService, extensionsRuntimeService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class InstallVSIXAction extends Action {
|
export class InstallVSIXAction extends Action {
|
||||||
|
|
||||||
static ID = 'workbench.extensions.action.installVSIX';
|
static ID = 'workbench.extensions.action.installVSIX';
|
||||||
|
|
|
@ -36,31 +36,3 @@ export const ExtensionsConfigurationInitialContent: string = [
|
||||||
'\t]',
|
'\t]',
|
||||||
'}'
|
'}'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
export const ExtensionsStorageSchemaId = 'vscode://schemas/extensionsstorage';
|
|
||||||
export const ExtensionsStorageSchema: IJSONSchema = {
|
|
||||||
id: ExtensionsStorageSchemaId,
|
|
||||||
type: 'object',
|
|
||||||
title: localize('app.extensionsstorage.json.title', "Extensions Storage"),
|
|
||||||
properties: {
|
|
||||||
disabled: {
|
|
||||||
type: 'array',
|
|
||||||
description: localize('app.extensionsstorage.json.disabled', "List of disabled extensions. The identifier of an extension is always '${publisher}.${name}'. For example: 'vscode.csharp'."),
|
|
||||||
items: {
|
|
||||||
type: 'string',
|
|
||||||
defaultSnippets: [{ label: 'Example', body: 'vscode.csharp' }],
|
|
||||||
pattern: EXTENSION_IDENTIFIER_PATTERN,
|
|
||||||
errorMessage: localize('app.extension.identifier.errorMessage', "Expected format '${publisher}.${name}'. Example: 'vscode.csharp'.")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ExtensionStorageInitialContent: string = [
|
|
||||||
'{',
|
|
||||||
'\t"disabled": [',
|
|
||||||
'\t\t// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp',
|
|
||||||
'\t\t',
|
|
||||||
'\t]',
|
|
||||||
'}'
|
|
||||||
].join('\n');
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
import pkg from 'vs/platform/package';
|
import pkg from 'vs/platform/package';
|
||||||
import paths = require('vs/base/common/paths');
|
import paths = require('vs/base/common/paths');
|
||||||
|
@ -28,7 +30,6 @@ import { IExtensionDescription, IMessage, IExtensionsRuntimeService } from 'vs/p
|
||||||
import { ExtensionScanner, MessagesCollector } from 'vs/workbench/node/extensionPoints';
|
import { ExtensionScanner, MessagesCollector } from 'vs/workbench/node/extensionPoints';
|
||||||
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
|
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
|
||||||
import Event, { Emitter } from 'vs/base/common/event';
|
import Event, { Emitter } from 'vs/base/common/event';
|
||||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
|
||||||
import { createQueuedSender, IQueuedSender } from 'vs/base/node/processes';
|
import { createQueuedSender, IQueuedSender } from 'vs/base/node/processes';
|
||||||
|
|
||||||
export const EXTENSION_LOG_BROADCAST_CHANNEL = 'vscode:extensionLog';
|
export const EXTENSION_LOG_BROADCAST_CHANNEL = 'vscode:extensionLog';
|
||||||
|
@ -51,6 +52,7 @@ export class ExtensionHostProcessWorker {
|
||||||
private extensionHostProcessQueuedSender: IQueuedSender;
|
private extensionHostProcessQueuedSender: IQueuedSender;
|
||||||
private extensionHostProcessReady: boolean;
|
private extensionHostProcessReady: boolean;
|
||||||
private initializeTimer: number;
|
private initializeTimer: number;
|
||||||
|
private workspaceStoragePath: string;
|
||||||
|
|
||||||
private lastExtensionHostError: string;
|
private lastExtensionHostError: string;
|
||||||
private unsentMessages: any[];
|
private unsentMessages: any[];
|
||||||
|
@ -72,7 +74,6 @@ export class ExtensionHostProcessWorker {
|
||||||
@ILifecycleService lifecycleService: ILifecycleService,
|
@ILifecycleService lifecycleService: ILifecycleService,
|
||||||
@IInstantiationService private instantiationService: IInstantiationService,
|
@IInstantiationService private instantiationService: IInstantiationService,
|
||||||
@IEnvironmentService private environmentService: IEnvironmentService,
|
@IEnvironmentService private environmentService: IEnvironmentService,
|
||||||
@IStorageService private storageService: IStorageService,
|
|
||||||
@IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService
|
@IExtensionsRuntimeService private extensionsRuntimeService: IExtensionsRuntimeService
|
||||||
) {
|
) {
|
||||||
// handle extension host lifecycle a bit special when we know we are developing an extension that runs inside
|
// handle extension host lifecycle a bit special when we know we are developing an extension that runs inside
|
||||||
|
@ -221,7 +222,7 @@ export class ExtensionHostProcessWorker {
|
||||||
contextService: {
|
contextService: {
|
||||||
workspace: this.contextService.getWorkspace()
|
workspace: this.contextService.getWorkspace()
|
||||||
},
|
},
|
||||||
workspaceStoragePath: this.storageService.getStoragePath(StorageScope.WORKSPACE),
|
workspaceStoragePath: this.getStoragePath(),
|
||||||
extensions: extensionDescriptors
|
extensions: extensionDescriptors
|
||||||
});
|
});
|
||||||
this.extensionHostProcessQueuedSender.send(initPayload);
|
this.extensionHostProcessQueuedSender.send(initPayload);
|
||||||
|
@ -408,4 +409,57 @@ export class ExtensionHostProcessWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getStoragePath(): string {
|
||||||
|
const workspace = this.contextService.getWorkspace();
|
||||||
|
|
||||||
|
if (!workspace) {
|
||||||
|
return void 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.workspaceStoragePath) {
|
||||||
|
return this.workspaceStoragePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rmkDir(directory: string): boolean {
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(directory);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
if (rmkDir(paths.dirname(directory))) {
|
||||||
|
fs.mkdirSync(directory);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fs.statSync(directory).isDirectory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workspace) {
|
||||||
|
const hash = crypto.createHash('md5');
|
||||||
|
hash.update(workspace.resource.fsPath);
|
||||||
|
if (workspace.uid) {
|
||||||
|
hash.update(workspace.uid.toString());
|
||||||
|
}
|
||||||
|
this.workspaceStoragePath = paths.join(this.environmentService.appSettingsHome, 'workspaceStorage', hash.digest('hex'));
|
||||||
|
if (!fs.existsSync(this.workspaceStoragePath)) {
|
||||||
|
try {
|
||||||
|
if (rmkDir(this.workspaceStoragePath)) {
|
||||||
|
fs.writeFileSync(paths.join(this.workspaceStoragePath, 'meta.json'), JSON.stringify({
|
||||||
|
workspacePath: workspace.resource.fsPath,
|
||||||
|
uid: workspace.uid ? workspace.uid : null
|
||||||
|
}, null, 4));
|
||||||
|
} else {
|
||||||
|
this.workspaceStoragePath = void 0;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.workspaceStoragePath = void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.workspaceStoragePath;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -4,95 +4,56 @@
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { distinct } from 'vs/base/common/arrays';
|
import { distinct } from 'vs/base/common/arrays';
|
||||||
import * as paths from 'vs/base/common/paths';
|
import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace';
|
||||||
import { ConfigWatcher } from 'vs/base/node/config';
|
import { IExtensionsRuntimeService } from 'vs/platform/extensions/common/extensions';
|
||||||
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';
|
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||||
|
import { IChoiceService } from 'vs/platform/message/common/message';
|
||||||
|
|
||||||
|
const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensions/disabled';
|
||||||
|
|
||||||
export class ExtensionsRuntimeService implements IExtensionsRuntimeService {
|
export class ExtensionsRuntimeService implements IExtensionsRuntimeService {
|
||||||
|
|
||||||
_serviceBrand: any;
|
_serviceBrand: any;
|
||||||
|
|
||||||
private workspaceStorage: ExtensionsStorage;
|
private workspace: IWorkspace;
|
||||||
private globalStorage: ExtensionsStorage;
|
private allDisabledExtensions: string[];
|
||||||
|
private globalDisabledExtensions: string[];
|
||||||
|
private workspaceDisabledExtensions: string[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IStorageService private storageService: IStorageService
|
@IStorageService private storageService: IStorageService,
|
||||||
|
@IChoiceService private choiceService: IChoiceService,
|
||||||
|
@IWorkspaceContextService contextService: IWorkspaceContextService
|
||||||
) {
|
) {
|
||||||
}
|
this.workspace = contextService.getWorkspace();
|
||||||
|
|
||||||
public getStoragePath(scope: StorageScope): string {
|
|
||||||
return this.getPath(scope);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDisabledExtensions(scope?: StorageScope): string[] {
|
public getDisabledExtensions(scope?: StorageScope): string[] {
|
||||||
if (scope) {
|
if (!this.allDisabledExtensions) {
|
||||||
return this.getData(scope).disabled || [];
|
this.globalDisabledExtensions = this.getDisabledExtensionsFromStorage(StorageScope.GLOBAL);
|
||||||
|
this.workspaceDisabledExtensions = this.getDisabledExtensionsFromStorage(StorageScope.WORKSPACE);
|
||||||
|
this.allDisabledExtensions = distinct([...this.globalDisabledExtensions, ...this.workspaceDisabledExtensions]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const globalData = this.getData(StorageScope.GLOBAL).disabled || [];
|
switch (scope) {
|
||||||
const workspaceData = this.getData(StorageScope.WORKSPACE).disabled || [];
|
case StorageScope.GLOBAL: return this.globalDisabledExtensions;
|
||||||
return distinct([...globalData, ...workspaceData]);
|
case StorageScope.WORKSPACE: return this.workspaceDisabledExtensions;
|
||||||
}
|
|
||||||
|
|
||||||
private getData(scope: StorageScope): IExtensionsStorageData {
|
|
||||||
const extensionsStorage = this.getStorage(scope);
|
|
||||||
return extensionsStorage ? extensionsStorage.data : {};
|
|
||||||
}
|
|
||||||
|
|
||||||
private getStorage(scope: StorageScope): ExtensionsStorage {
|
|
||||||
const path = this.getPath(scope);
|
|
||||||
if (path) {
|
|
||||||
if (StorageScope.WORKSPACE === scope) {
|
|
||||||
return this.getWorkspaceStorage(path);
|
|
||||||
}
|
|
||||||
return this.getGlobalStorage(path);
|
|
||||||
}
|
}
|
||||||
return null;
|
return this.allDisabledExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getGlobalStorage(path: string): ExtensionsStorage {
|
private getDisabledExtensionsFromStorage(scope?: StorageScope): string[] {
|
||||||
if (!this.globalStorage) {
|
if (scope !== void 0) {
|
||||||
this.globalStorage = new ExtensionsStorage(path);
|
return this._getDisabledExtensions(scope);
|
||||||
}
|
}
|
||||||
return this.globalStorage;
|
|
||||||
|
const globallyDisabled = this._getDisabledExtensions(StorageScope.GLOBAL);
|
||||||
|
const workspaceDisabled = this._getDisabledExtensions(StorageScope.WORKSPACE);
|
||||||
|
return [...globallyDisabled, ...workspaceDisabled];
|
||||||
}
|
}
|
||||||
|
|
||||||
private getWorkspaceStorage(path: string): ExtensionsStorage {
|
private _getDisabledExtensions(scope: StorageScope): string[] {
|
||||||
if (!this.workspaceStorage) {
|
const value = this.storageService.get(DISABLED_EXTENSIONS_STORAGE_PATH, scope, '');
|
||||||
this.workspaceStorage = new ExtensionsStorage(path);
|
return value ? distinct(value.split(',')) : [];
|
||||||
}
|
|
||||||
return this.workspaceStorage;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getPath(scope: StorageScope): string {
|
|
||||||
const path = this.storageService.getStoragePath(scope);
|
|
||||||
return path ? paths.join(path, 'extensions.json') : void 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public dispose() {
|
|
||||||
if (this.workspaceStorage) {
|
|
||||||
this.workspaceStorage.dispose();
|
|
||||||
this.workspaceStorage = null;
|
|
||||||
}
|
|
||||||
if (this.globalStorage) {
|
|
||||||
this.globalStorage.dispose();
|
|
||||||
this.globalStorage = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ExtensionsStorage extends Disposable {
|
|
||||||
|
|
||||||
private _watcher: ConfigWatcher<IExtensionsStorageData>;
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ import * as Types from 'vs/base/common/types';
|
||||||
import * as TestUtils from 'vs/test/utils/servicesTestUtils';
|
import * as TestUtils from 'vs/test/utils/servicesTestUtils';
|
||||||
import { IWorkspaceContextService, WorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
import { IWorkspaceContextService, WorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||||
import { Storage, InMemoryLocalStorage } from 'vs/workbench/node/storage';
|
import { Storage, InMemoryLocalStorage } from 'vs/workbench/common/storage';
|
||||||
|
|
||||||
class MyPart extends Part {
|
class MyPart extends Part {
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { WorkspaceContextService } from 'vs/platform/workspace/common/workspace'
|
||||||
import { StorageScope } from 'vs/platform/storage/common/storage';
|
import { StorageScope } from 'vs/platform/storage/common/storage';
|
||||||
import * as TestUtils from 'vs/test/utils/servicesTestUtils';
|
import * as TestUtils from 'vs/test/utils/servicesTestUtils';
|
||||||
import { Memento, Scope } from 'vs/workbench/common/memento';
|
import { Memento, Scope } from 'vs/workbench/common/memento';
|
||||||
import { Storage, InMemoryLocalStorage } from 'vs/workbench/node/storage';
|
import { Storage, InMemoryLocalStorage } from 'vs/workbench/common/storage';
|
||||||
|
|
||||||
suite('Workbench Memento', () => {
|
suite('Workbench Memento', () => {
|
||||||
let context;
|
let context;
|
||||||
|
|
|
@ -9,7 +9,7 @@ import * as assert from 'assert';
|
||||||
import { clone } from 'vs/base/common/objects';
|
import { clone } from 'vs/base/common/objects';
|
||||||
import { StorageScope } from 'vs/platform/storage/common/storage';
|
import { StorageScope } from 'vs/platform/storage/common/storage';
|
||||||
import { TestContextService, TestWorkspace, TestEnvironmentService } from 'vs/test/utils/servicesTestUtils';
|
import { TestContextService, TestWorkspace, TestEnvironmentService } from 'vs/test/utils/servicesTestUtils';
|
||||||
import { Storage, InMemoryLocalStorage } from 'vs/workbench/node/storage';
|
import { Storage, InMemoryLocalStorage } from 'vs/workbench/common/storage';
|
||||||
|
|
||||||
suite('Workbench Storage', () => {
|
suite('Workbench Storage', () => {
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue