Skip to content

Commit

Permalink
Implement #34812
Browse files Browse the repository at this point in the history
  • Loading branch information
sandy081 committed Oct 24, 2017
1 parent af1e604 commit 7809796
Show file tree
Hide file tree
Showing 17 changed files with 481 additions and 389 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,25 @@

import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { distinct } from 'vs/base/common/arrays';
import { distinct, coalesce } from 'vs/base/common/arrays';
import Event, { Emitter } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { adoptToGalleryExtensionId, getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
import { adoptToGalleryExtensionId, getIdAndVersionFromLocalExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';

const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensions/disabled';
const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/disabled';

export class ExtensionEnablementService implements IExtensionEnablementService {

_serviceBrand: any;

private disposables: IDisposable[] = [];

private _onEnablementChanged = new Emitter<string>();
public onEnablementChanged: Event<string> = this._onEnablementChanged.event;
private _onEnablementChanged = new Emitter<IExtensionIdentifier>();
public onEnablementChanged: Event<IExtensionIdentifier> = this._onEnablementChanged.event;

constructor(
@IStorageService private storageService: IStorageService,
Expand All @@ -38,28 +38,28 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
return this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY;
}

public getGloballyDisabledExtensions(): string[] {
getGloballyDisabledExtensions(): IExtensionIdentifier[] {
return this.getDisabledExtensions(StorageScope.GLOBAL);
}

public getWorkspaceDisabledExtensions(): string[] {
getWorkspaceDisabledExtensions(): IExtensionIdentifier[] {
return this.getDisabledExtensions(StorageScope.WORKSPACE);
}

public canEnable(identifier: string): boolean {
canEnable(identifier: IExtensionIdentifier): boolean {
if (this.environmentService.disableExtensions) {
return false;
}
if (this.getGloballyDisabledExtensions().indexOf(identifier) !== -1) {
if (this.getGloballyDisabledExtensions().some(d => areSameExtensions(d, identifier))) {
return true;
}
if (this.getWorkspaceDisabledExtensions().indexOf(identifier) !== -1) {
if (this.getWorkspaceDisabledExtensions().some(d => areSameExtensions(d, identifier))) {
return true;
}
return false;
}

public setEnablement(identifier: string, enable: boolean, workspace: boolean = false): TPromise<boolean> {
setEnablement(identifier: IExtensionIdentifier, enable: boolean, workspace: boolean = false): TPromise<boolean> {
if (workspace && !this.hasWorkspace) {
return TPromise.wrapError<boolean>(new Error(localize('noWorkspace', "No workspace.")));
}
Expand All @@ -83,39 +83,47 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
}
}

private disableExtension(identifier: string, scope: StorageScope): TPromise<boolean> {
migrateToIdentifiers(installed: IExtensionIdentifier[]): void {
this.migrateDisabledExtensions(installed, StorageScope.GLOBAL);
if (this.hasWorkspace) {
this.migrateDisabledExtensions(installed, StorageScope.WORKSPACE);
}
}

private disableExtension(identifier: IExtensionIdentifier, scope: StorageScope): TPromise<boolean> {
let disabledExtensions = this.getDisabledExtensions(scope);
const index = disabledExtensions.indexOf(identifier);
if (index === -1) {
if (disabledExtensions.every(e => !areSameExtensions(e, identifier))) {
disabledExtensions.push(identifier);
this.setDisabledExtensions(disabledExtensions, scope, identifier);
return TPromise.wrap(true);
}
return TPromise.wrap(false);
}

private enableExtension(identifier: string, scope: StorageScope, fireEvent = true): TPromise<boolean> {
private enableExtension(identifier: IExtensionIdentifier, scope: StorageScope, fireEvent = true): TPromise<boolean> {
let disabledExtensions = this.getDisabledExtensions(scope);
const index = disabledExtensions.indexOf(identifier);
if (index !== -1) {
disabledExtensions.splice(index, 1);
this.setDisabledExtensions(disabledExtensions, scope, identifier, fireEvent);
return TPromise.wrap(true);
for (let index = 0; index < disabledExtensions.length; index++) {
const disabledExtension = disabledExtensions[index];
if (areSameExtensions(disabledExtension, identifier)) {
disabledExtensions.splice(index, 1);
this.setDisabledExtensions(disabledExtensions, scope, identifier, fireEvent);
return TPromise.wrap(true);
}
}
return TPromise.wrap(false);
}

private getDisabledExtensions(scope: StorageScope): string[] {
private getDisabledExtensions(scope: StorageScope): IExtensionIdentifier[] {
if (scope === StorageScope.WORKSPACE && !this.hasWorkspace) {
return [];
}
const value = this.storageService.get(DISABLED_EXTENSIONS_STORAGE_PATH, scope, '');
return value ? distinct(value.split(',')).map(id => adoptToGalleryExtensionId(id)) : [];
return value ? JSON.parse(value) : [];
}

private setDisabledExtensions(disabledExtensions: string[], scope: StorageScope, extension: string, fireEvent = true): void {
private setDisabledExtensions(disabledExtensions: IExtensionIdentifier[], scope: StorageScope, extension: IExtensionIdentifier, fireEvent = true): void {
if (disabledExtensions.length) {
this.storageService.store(DISABLED_EXTENSIONS_STORAGE_PATH, disabledExtensions.join(','), scope);
this.storageService.store(DISABLED_EXTENSIONS_STORAGE_PATH, JSON.stringify(disabledExtensions.map(({ id, uuid }) => (<IExtensionIdentifier>{ id, uuid }))), scope);
} else {
this.storageService.remove(DISABLED_EXTENSIONS_STORAGE_PATH, scope);
}
Expand All @@ -124,12 +132,28 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
}
}

private onDidUninstallExtension({ id, error }: DidUninstallExtensionEvent): void {
private migrateDisabledExtensions(installedExtensions: IExtensionIdentifier[], scope: StorageScope): void {
const oldValue = this.storageService.get('extensions/disabled', scope, '');
if (oldValue) {
const extensionIdentifiers = coalesce(distinct(oldValue.split(',')).map(id => {
id = adoptToGalleryExtensionId(id);
const matched = installedExtensions.filter(installed => areSameExtensions({ id }, { id: installed.id }))[0];
return matched ? { id: matched.id, uuid: matched.uuid } : null;
}));
if (extensionIdentifiers.length) {
this.storageService.store(DISABLED_EXTENSIONS_STORAGE_PATH, JSON.stringify(extensionIdentifiers), scope);
}
}
this.storageService.remove('extensions/disabled', scope);
}

private onDidUninstallExtension({ identifier, error }: DidUninstallExtensionEvent): void {
if (!error) {
id = getIdAndVersionFromLocalExtensionId(id).id;
const id = getIdAndVersionFromLocalExtensionId(identifier.id).id;
if (id) {
this.enableExtension(id, StorageScope.WORKSPACE, false);
this.enableExtension(id, StorageScope.GLOBAL, false);
const extension = { id, uuid: identifier.uuid };
this.enableExtension(extension, StorageScope.WORKSPACE, false);
this.enableExtension(extension, StorageScope.GLOBAL, false);
}
}
}
Expand Down
32 changes: 19 additions & 13 deletions src/vs/platform/extensionManagement/common/extensionManagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,14 @@ export interface IGalleryExtensionAssets {
license: IGalleryExtensionAsset;
}

export interface IGalleryExtension {
uuid: string;
export interface IExtensionIdentifier {
id: string;
uuid?: string;
}

export interface IGalleryExtension {
name: string;
identifier: IExtensionIdentifier;
version: string;
date: string;
displayName: string;
Expand Down Expand Up @@ -167,7 +171,7 @@ export enum LocalExtensionType {

export interface ILocalExtension {
type: LocalExtensionType;
id: string;
identifier: IExtensionIdentifier;
manifest: IExtensionManifest;
metadata: IGalleryMetadata;
path: string;
Expand Down Expand Up @@ -217,27 +221,27 @@ export interface IExtensionGalleryService {
reportStatistic(publisher: string, name: string, version: string, type: StatisticType): TPromise<void>;
getReadme(extension: IGalleryExtension): TPromise<string>;
getManifest(extension: IGalleryExtension): TPromise<IExtensionManifest>;
getChangelog(extension: IGalleryMetadata): TPromise<string>;
getChangelog(extension: IGalleryExtension): TPromise<string>;
loadCompatibleVersion(extension: IGalleryExtension): TPromise<IGalleryExtension>;
getAllDependencies(extension: IGalleryExtension): TPromise<IGalleryExtension[]>;
}

export interface InstallExtensionEvent {
id: string;
identifier: IExtensionIdentifier;
zipPath?: string;
gallery?: IGalleryExtension;
}

export interface DidInstallExtensionEvent {
id: string;
identifier: IExtensionIdentifier;
zipPath?: string;
gallery?: IGalleryExtension;
local?: ILocalExtension;
error?: string;
}

export interface DidUninstallExtensionEvent {
id: string;
identifier: IExtensionIdentifier;
error?: string;
}

Expand All @@ -246,7 +250,7 @@ export interface IExtensionManagementService {

onInstallExtension: Event<InstallExtensionEvent>;
onDidInstallExtension: Event<DidInstallExtensionEvent>;
onUninstallExtension: Event<string>;
onUninstallExtension: Event<IExtensionIdentifier>;
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;

install(zipPath: string): TPromise<void>;
Expand All @@ -264,24 +268,24 @@ export interface IExtensionEnablementService {
/**
* Event to listen on for extension enablement changes
*/
onEnablementChanged: Event<string>;
onEnablementChanged: Event<IExtensionIdentifier>;

/**
* Returns all globally disabled extension identifiers.
* Returns an empty array if none exist.
*/
getGloballyDisabledExtensions(): string[];
getGloballyDisabledExtensions(): IExtensionIdentifier[];

/**
* Returns all workspace disabled extension identifiers.
* Returns an empty array if none exist or workspace does not exist.
*/
getWorkspaceDisabledExtensions(): string[];
getWorkspaceDisabledExtensions(): IExtensionIdentifier[];

/**
* Returns `true` if given extension can be enabled by calling `setEnablement`, otherwise false`.
*/
canEnable(identifier: string): boolean;
canEnable(identifier: IExtensionIdentifier): boolean;

/**
* Enable or disable the given extension.
Expand All @@ -292,7 +296,9 @@ export interface IExtensionEnablementService {
*
* Throws error if enablement is requested for workspace and there is no workspace
*/
setEnablement(identifier: string, enable: boolean, workspace?: boolean): TPromise<boolean>;
setEnablement(identifier: IExtensionIdentifier, enable: boolean, workspace?: boolean): TPromise<boolean>;

migrateToIdentifiers(installed: IExtensionIdentifier[]): void;
}

export const IExtensionTipsService = createDecorator<IExtensionTipsService>('extensionTipsService');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { TPromise } from 'vs/base/common/winjs.base';
import { IChannel, eventToCall, eventFromCall } from 'vs/base/parts/ipc/common/ipc';
import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, LocalExtensionType, DidUninstallExtensionEvent } from './extensionManagement';
import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, LocalExtensionType, DidUninstallExtensionEvent, IExtensionIdentifier } from './extensionManagement';
import Event, { buffer } from 'vs/base/common/event';

export interface IExtensionManagementChannel extends IChannel {
Expand All @@ -26,7 +26,7 @@ export class ExtensionManagementChannel implements IExtensionManagementChannel {

onInstallExtension: Event<InstallExtensionEvent>;
onDidInstallExtension: Event<DidInstallExtensionEvent>;
onUninstallExtension: Event<string>;
onUninstallExtension: Event<IExtensionIdentifier>;
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;

constructor(private service: IExtensionManagementService) {
Expand Down Expand Up @@ -63,8 +63,8 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
private _onDidInstallExtension = eventFromCall<DidInstallExtensionEvent>(this.channel, 'event:onDidInstallExtension');
get onDidInstallExtension(): Event<DidInstallExtensionEvent> { return this._onDidInstallExtension; }

private _onUninstallExtension = eventFromCall<string>(this.channel, 'event:onUninstallExtension');
get onUninstallExtension(): Event<string> { return this._onUninstallExtension; }
private _onUninstallExtension = eventFromCall<IExtensionIdentifier>(this.channel, 'event:onUninstallExtension');
get onUninstallExtension(): Event<IExtensionIdentifier> { return this._onUninstallExtension; }

private _onDidUninstallExtension = eventFromCall<DidUninstallExtensionEvent>(this.channel, 'event:onDidUninstallExtension');
get onDidUninstallExtension(): Event<DidUninstallExtensionEvent> { return this._onDidUninstallExtension; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@

'use strict';

import { ILocalExtension, IGalleryExtension, IExtensionManifest, EXTENSION_IDENTIFIER_REGEX, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ILocalExtension, IGalleryExtension, EXTENSION_IDENTIFIER_REGEX, IExtensionEnablementService, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';

export function areSameExtensions(a: { id: string }, b: { id: string }): boolean {
export function areSameExtensions(a: IExtensionIdentifier, b: IExtensionIdentifier): boolean {
if (a.uuid && b.uuid) {
return a.uuid === b.uuid;
}
if (a.id === b.id) {
return true;
}
Expand All @@ -19,14 +22,6 @@ export function getGalleryExtensionId(publisher: string, name: string): string {
return `${publisher}.${name.toLocaleLowerCase()}`;
}

export function getLocalExtensionIdFromGallery(extension: IGalleryExtension, version: string): string {
return getLocalExtensionId(extension.id, version);
}

export function getLocalExtensionIdFromManifest(manifest: IExtensionManifest): string {
return getLocalExtensionId(getGalleryExtensionId(manifest.publisher, manifest.name), manifest.version);
}

export function getGalleryExtensionIdFromLocal(local: ILocalExtension): string {
return getGalleryExtensionId(local.manifest.publisher, local.manifest.name);
}
Expand All @@ -46,10 +41,6 @@ export function adoptToGalleryExtensionId(id: string): string {
return id.replace(EXTENSION_IDENTIFIER_REGEX, (match, publisher: string, name: string) => getGalleryExtensionId(publisher, name));
}

function getLocalExtensionId(id: string, version: string): string {
return `${id}-${version}`;
}

export function getLocalExtensionTelemetryData(extension: ILocalExtension): any {
return {
id: getGalleryExtensionIdFromLocal(extension),
Expand Down Expand Up @@ -79,9 +70,9 @@ export function getLocalExtensionTelemetryData(extension: ILocalExtension): any
*/
export function getGalleryExtensionTelemetryData(extension: IGalleryExtension): any {
return {
id: extension.id,
id: extension.identifier.id,
name: extension.name,
galleryId: extension.uuid,
galleryId: extension.identifier.uuid,
publisherId: extension.publisherId,
publisherName: extension.publisher,
publisherDisplayName: extension.publisherDisplayName,
Expand All @@ -102,9 +93,9 @@ export function getGloballyDisabledExtensions(extensionEnablementService: IExten
const globallyDisabled = extensionEnablementService.getGloballyDisabledExtensions();
if (!storageService.getBoolean(BetterMergeCheckKey, StorageScope.GLOBAL, false)) {
storageService.store(BetterMergeCheckKey, true);
if (globallyDisabled.indexOf(BetterMergeId) === -1 && installedExtensions.some(d => d.id === BetterMergeId)) {
globallyDisabled.push(BetterMergeId);
extensionEnablementService.setEnablement(BetterMergeId, false);
if (globallyDisabled.every(disabled => disabled.id !== BetterMergeId) && installedExtensions.some(d => d.id === BetterMergeId)) {
globallyDisabled.push({ id: BetterMergeId });
extensionEnablementService.setEnablement({ id: BetterMergeId }, false);
storageService.store(BetterMergeDisabledNowKey, true);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,10 @@ function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUr
};

return {
uuid: galleryExtension.extensionId,
id: getGalleryExtensionId(galleryExtension.publisher.publisherName, galleryExtension.extensionName),
identifier: {
id: getGalleryExtensionId(galleryExtension.publisher.publisherName, galleryExtension.extensionName),
uuid: galleryExtension.extensionId
},
name: galleryExtension.extensionName,
version: version.version,
date: version.lastUpdated,
Expand Down Expand Up @@ -488,7 +490,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
.withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code')
.withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished))
.withAssetTypes(AssetType.Manifest, AssetType.VSIX)
.withFilter(FilterType.ExtensionId, extension.uuid);
.withFilter(FilterType.ExtensionId, extension.identifier.uuid);

return this.queryGallery(query).then(({ galleryExtensions }) => {
const [rawExtension] = galleryExtensions;
Expand Down Expand Up @@ -556,7 +558,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
dep.properties.dependencies.forEach(d => dependenciesSet.add(d));
}
}
result = distinct(result.concat(loadedDependencies), d => d.uuid);
result = distinct(result.concat(loadedDependencies), d => d.identifier.uuid);
const dependencies: string[] = [];
dependenciesSet.forEach(d => !ExtensionGalleryService.hasExtensionByName(result, d) && dependencies.push(d));
return this.getDependenciesReccursively(dependencies, result, root);
Expand Down
Loading

0 comments on commit 7809796

Please sign in to comment.