mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-04 17:23:45 -05:00
Merge VS Code 1.23.1 (#1520)
This commit is contained in:
@@ -5,11 +5,10 @@
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { distinct, coalesce } from 'vs/base/common/arrays';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, ILocalExtension, isIExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { adoptToGalleryExtensionId, getIdFromLocalExtensionId, areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, ILocalExtension, isIExtensionIdentifier, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { getIdFromLocalExtensionId, areSameExtensions, getGalleryExtensionIdFromLocal } 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';
|
||||
@@ -24,7 +23,7 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
|
||||
private disposables: IDisposable[] = [];
|
||||
|
||||
private _onEnablementChanged = new Emitter<IExtensionIdentifier>();
|
||||
public onEnablementChanged: Event<IExtensionIdentifier> = this._onEnablementChanged.event;
|
||||
public readonly onEnablementChanged: Event<IExtensionIdentifier> = this._onEnablementChanged.event;
|
||||
|
||||
constructor(
|
||||
@IStorageService private storageService: IStorageService,
|
||||
@@ -58,10 +57,11 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
|
||||
return TPromise.as(result);
|
||||
}
|
||||
|
||||
getEnablementState(identifier: IExtensionIdentifier): EnablementState {
|
||||
if (this.environmentService.disableExtensions) {
|
||||
getEnablementState(extension: ILocalExtension): EnablementState {
|
||||
if (this.environmentService.disableExtensions && extension.type === LocalExtensionType.User) {
|
||||
return EnablementState.Disabled;
|
||||
}
|
||||
const identifier = this._getIdentifier(extension);
|
||||
if (this.hasWorkspace) {
|
||||
if (this._getEnabledExtensions(StorageScope.WORKSPACE).filter(e => areSameExtensions(e, identifier))[0]) {
|
||||
return EnablementState.WorkspaceEnabled;
|
||||
@@ -78,18 +78,24 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
|
||||
}
|
||||
|
||||
canChangeEnablement(extension: ILocalExtension): boolean {
|
||||
return !this.environmentService.disableExtensions && !(extension.manifest && extension.manifest.contributes && extension.manifest.contributes.localizations && extension.manifest.contributes.localizations.length);
|
||||
if (extension.manifest && extension.manifest.contributes && extension.manifest.contributes.localizations && extension.manifest.contributes.localizations.length) {
|
||||
return false;
|
||||
}
|
||||
if (extension.type === LocalExtensionType.User && this.environmentService.disableExtensions) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
setEnablement(arg: ILocalExtension | IExtensionIdentifier, newState: EnablementState): TPromise<boolean> {
|
||||
let identifier;
|
||||
let identifier: IExtensionIdentifier;
|
||||
if (isIExtensionIdentifier(arg)) {
|
||||
identifier = arg;
|
||||
} else {
|
||||
if (!this.canChangeEnablement(arg)) {
|
||||
return TPromise.wrap(false);
|
||||
}
|
||||
identifier = { id: getGalleryExtensionIdFromLocal(arg), uuid: arg.identifier.uuid };
|
||||
identifier = this._getIdentifier(arg);
|
||||
}
|
||||
|
||||
const workspace = newState === EnablementState.WorkspaceDisabled || newState === EnablementState.WorkspaceEnabled;
|
||||
@@ -97,7 +103,7 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
|
||||
return TPromise.wrapError<boolean>(new Error(localize('noWorkspace', "No workspace.")));
|
||||
}
|
||||
|
||||
const currentState = this.getEnablementState(identifier);
|
||||
const currentState = this._getEnablementState(identifier);
|
||||
|
||||
if (currentState === newState) {
|
||||
return TPromise.as(false);
|
||||
@@ -123,16 +129,29 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
|
||||
return TPromise.as(true);
|
||||
}
|
||||
|
||||
isEnabled(identifier: IExtensionIdentifier): boolean {
|
||||
const enablementState = this.getEnablementState(identifier);
|
||||
isEnabled(extension: ILocalExtension): boolean {
|
||||
const enablementState = this.getEnablementState(extension);
|
||||
return enablementState === EnablementState.WorkspaceEnabled || enablementState === EnablementState.Enabled;
|
||||
}
|
||||
|
||||
migrateToIdentifiers(installed: IExtensionIdentifier[]): void {
|
||||
this._migrateDisabledExtensions(installed, StorageScope.GLOBAL);
|
||||
private _getEnablementState(identifier: IExtensionIdentifier): EnablementState {
|
||||
if (this.hasWorkspace) {
|
||||
this._migrateDisabledExtensions(installed, StorageScope.WORKSPACE);
|
||||
if (this._getEnabledExtensions(StorageScope.WORKSPACE).filter(e => areSameExtensions(e, identifier))[0]) {
|
||||
return EnablementState.WorkspaceEnabled;
|
||||
}
|
||||
|
||||
if (this._getDisabledExtensions(StorageScope.WORKSPACE).filter(e => areSameExtensions(e, identifier))[0]) {
|
||||
return EnablementState.WorkspaceDisabled;
|
||||
}
|
||||
}
|
||||
if (this._getDisabledExtensions(StorageScope.GLOBAL).filter(e => areSameExtensions(e, identifier))[0]) {
|
||||
return EnablementState.Disabled;
|
||||
}
|
||||
return EnablementState.Enabled;
|
||||
}
|
||||
|
||||
private _getIdentifier(extension: ILocalExtension): IExtensionIdentifier {
|
||||
return { id: getGalleryExtensionIdFromLocal(extension), uuid: extension.identifier.uuid };
|
||||
}
|
||||
|
||||
private _enableExtension(identifier: IExtensionIdentifier): void {
|
||||
@@ -227,8 +246,8 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
|
||||
return this._getExtensions(DISABLED_EXTENSIONS_STORAGE_PATH, scope);
|
||||
}
|
||||
|
||||
private _setDisabledExtensions(disabledExtensions: IExtensionIdentifier[], scope: StorageScope, extension: IExtensionIdentifier, fireEvent = true): void {
|
||||
this._setExtensions(DISABLED_EXTENSIONS_STORAGE_PATH, disabledExtensions, scope, extension, fireEvent);
|
||||
private _setDisabledExtensions(disabledExtensions: IExtensionIdentifier[], scope: StorageScope, extension: IExtensionIdentifier): void {
|
||||
this._setExtensions(DISABLED_EXTENSIONS_STORAGE_PATH, disabledExtensions, scope, extension);
|
||||
}
|
||||
|
||||
private _getExtensions(storageId: string, scope: StorageScope): IExtensionIdentifier[] {
|
||||
@@ -239,30 +258,12 @@ export class ExtensionEnablementService implements IExtensionEnablementService {
|
||||
return value ? JSON.parse(value) : [];
|
||||
}
|
||||
|
||||
private _setExtensions(storageId: string, extensions: IExtensionIdentifier[], scope: StorageScope, extension: IExtensionIdentifier, fireEvent = true): void {
|
||||
private _setExtensions(storageId: string, extensions: IExtensionIdentifier[], scope: StorageScope, extension: IExtensionIdentifier): void {
|
||||
if (extensions.length) {
|
||||
this.storageService.store(storageId, JSON.stringify(extensions.map(({ id, uuid }) => (<IExtensionIdentifier>{ id, uuid }))), scope);
|
||||
} else {
|
||||
this.storageService.remove(storageId, scope);
|
||||
}
|
||||
if (fireEvent) {
|
||||
this._onEnablementChanged.fire(extension);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import Event from 'vs/base/common/event';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IPager } from 'vs/base/common/paging';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILocalization } from 'vs/platform/localizations/common/localizations';
|
||||
@@ -118,6 +118,12 @@ export interface IExtensionManifest {
|
||||
activationEvents?: string[];
|
||||
extensionDependencies?: string[];
|
||||
contributes?: IExtensionContributions;
|
||||
repository?: {
|
||||
url: string;
|
||||
};
|
||||
bugs?: {
|
||||
url: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IGalleryExtensionProperties {
|
||||
@@ -149,6 +155,12 @@ export function isIExtensionIdentifier(thing: any): thing is IExtensionIdentifie
|
||||
&& (!thing.uuid || typeof thing.uuid === 'string');
|
||||
}
|
||||
|
||||
/* __GDPR__FRAGMENT__
|
||||
"ExtensionIdentifier" : {
|
||||
"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"uuid": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
export interface IExtensionIdentifier {
|
||||
id: string;
|
||||
uuid?: string;
|
||||
@@ -274,10 +286,10 @@ export interface IExtensionManagementService {
|
||||
onUninstallExtension: Event<IExtensionIdentifier>;
|
||||
onDidUninstallExtension: Event<DidUninstallExtensionEvent>;
|
||||
|
||||
install(zipPath: string): TPromise<void>;
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void>;
|
||||
install(zipPath: string): TPromise<ILocalExtension>;
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension>;
|
||||
uninstall(extension: ILocalExtension, force?: boolean): TPromise<void>;
|
||||
reinstall(extension: ILocalExtension): TPromise<void>;
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<ILocalExtension>;
|
||||
getInstalled(type?: LocalExtensionType): TPromise<ILocalExtension[]>;
|
||||
getExtensionsReport(): TPromise<IReportedExtension[]>;
|
||||
|
||||
@@ -311,7 +323,7 @@ export interface IExtensionEnablementService {
|
||||
/**
|
||||
* Returns the enablement state for the given extension
|
||||
*/
|
||||
getEnablementState(identifier: IExtensionIdentifier): EnablementState;
|
||||
getEnablementState(extension: ILocalExtension): EnablementState;
|
||||
|
||||
/**
|
||||
* Returns `true` if the enablement can be changed.
|
||||
@@ -321,7 +333,7 @@ export interface IExtensionEnablementService {
|
||||
/**
|
||||
* Returns `true` if the given extension identifier is enabled.
|
||||
*/
|
||||
isEnabled(identifier: IExtensionIdentifier): boolean;
|
||||
isEnabled(extension: ILocalExtension): boolean;
|
||||
|
||||
/**
|
||||
* Enable or disable the given extension.
|
||||
@@ -333,12 +345,6 @@ export interface IExtensionEnablementService {
|
||||
* Throws error if enablement is requested for workspace and there is no workspace
|
||||
*/
|
||||
setEnablement(extension: ILocalExtension, state: EnablementState): TPromise<boolean>;
|
||||
/**
|
||||
* TODO: @Sandy. Use setEnablement(extension: ILocalExtension, state: EnablementState): TPromise<boolean>. Use one model for extension management and runtime
|
||||
*/
|
||||
setEnablement(identifier: IExtensionIdentifier, state: EnablementState): TPromise<boolean>;
|
||||
|
||||
migrateToIdentifiers(installed: IExtensionIdentifier[]): void;
|
||||
}
|
||||
|
||||
export const IExtensionTipsService = createDecorator<IExtensionTipsService>('extensionTipsService');
|
||||
|
||||
@@ -8,16 +8,17 @@
|
||||
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, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from './extensionManagement';
|
||||
import Event, { buffer } from 'vs/base/common/event';
|
||||
import { Event, buffer } from 'vs/base/common/event';
|
||||
|
||||
export interface IExtensionManagementChannel extends IChannel {
|
||||
call(command: 'event:onInstallExtension'): TPromise<void>;
|
||||
call(command: 'event:onDidInstallExtension'): TPromise<void>;
|
||||
call(command: 'event:onUninstallExtension'): TPromise<void>;
|
||||
call(command: 'event:onDidUninstallExtension'): TPromise<void>;
|
||||
call(command: 'install', path: string): TPromise<void>;
|
||||
call(command: 'installFromGallery', extension: IGalleryExtension): TPromise<void>;
|
||||
call(command: 'install', path: string): TPromise<ILocalExtension>;
|
||||
call(command: 'installFromGallery', extension: IGalleryExtension): TPromise<ILocalExtension>;
|
||||
call(command: 'uninstall', args: [ILocalExtension, boolean]): TPromise<void>;
|
||||
call(command: 'reinstallFromGallery', args: [ILocalExtension]): TPromise<ILocalExtension>;
|
||||
call(command: 'getInstalled'): TPromise<ILocalExtension[]>;
|
||||
call(command: 'getExtensionsReport'): TPromise<IReportedExtension[]>;
|
||||
call(command: string, arg?: any): TPromise<any>;
|
||||
@@ -46,7 +47,7 @@ export class ExtensionManagementChannel implements IExtensionManagementChannel {
|
||||
case 'install': return this.service.install(arg);
|
||||
case 'installFromGallery': return this.service.installFromGallery(arg[0]);
|
||||
case 'uninstall': return this.service.uninstall(arg[0], arg[1]);
|
||||
case 'reinstall': return this.service.reinstall(arg[0]);
|
||||
case 'reinstallFromGallery': return this.service.reinstallFromGallery(arg[0]);
|
||||
case 'getInstalled': return this.service.getInstalled(arg);
|
||||
case 'updateMetadata': return this.service.updateMetadata(arg[0], arg[1]);
|
||||
case 'getExtensionsReport': return this.service.getExtensionsReport();
|
||||
@@ -73,11 +74,11 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
|
||||
private _onDidUninstallExtension = eventFromCall<DidUninstallExtensionEvent>(this.channel, 'event:onDidUninstallExtension');
|
||||
get onDidUninstallExtension(): Event<DidUninstallExtensionEvent> { return this._onDidUninstallExtension; }
|
||||
|
||||
install(zipPath: string): TPromise<void> {
|
||||
install(zipPath: string): TPromise<ILocalExtension> {
|
||||
return this.channel.call('install', zipPath);
|
||||
}
|
||||
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void> {
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension> {
|
||||
return this.channel.call('installFromGallery', [extension]);
|
||||
}
|
||||
|
||||
@@ -85,8 +86,8 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer
|
||||
return this.channel.call('uninstall', [extension, force]);
|
||||
}
|
||||
|
||||
reinstall(extension: ILocalExtension): TPromise<void> {
|
||||
return this.channel.call('reinstall', [extension]);
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<ILocalExtension> {
|
||||
return this.channel.call('reinstallFromGallery', [extension]);
|
||||
}
|
||||
|
||||
getInstalled(type: LocalExtensionType = null): TPromise<ILocalExtension[]> {
|
||||
|
||||
@@ -82,10 +82,10 @@ export function getLocalExtensionTelemetryData(extension: ILocalExtension): any
|
||||
"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"name": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"galleryId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"publisherId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
|
||||
"publisherName": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
|
||||
"publisherDisplayName": { "classification": "PublicPersonalData", "purpose": "FeatureInsight" },
|
||||
"dependencies": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"publisherId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"publisherName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"dependencies": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData2}"
|
||||
]
|
||||
|
||||
@@ -18,7 +18,7 @@ import { IPager } from 'vs/base/common/paging';
|
||||
import { IRequestOptions, IRequestContext, download, asJson, asText } from 'vs/base/node/request';
|
||||
import pkg from 'vs/platform/node/package';
|
||||
import product from 'vs/platform/node/product';
|
||||
import { isVersionValid } from 'vs/platform/extensions/node/extensionValidator';
|
||||
import { isEngineValid } from 'vs/platform/extensions/node/extensionValidator';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { readFile } from 'vs/base/node/pfs';
|
||||
import { writeFileAndFlushSync } from 'vs/base/node/extfs';
|
||||
@@ -324,7 +324,7 @@ function toExtension(galleryExtension: IRawGalleryExtension, extensionsGalleryUr
|
||||
},
|
||||
/* __GDPR__FRAGMENT__
|
||||
"GalleryExtensionTelemetryData2" : {
|
||||
"index" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"index" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"searchText": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"querySource": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
@@ -586,7 +586,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
const startTime = new Date().getTime();
|
||||
/* __GDPR__
|
||||
"galleryService:downloadVSIX" : {
|
||||
"duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
|
||||
"duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData}"
|
||||
]
|
||||
@@ -622,7 +622,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
}
|
||||
|
||||
loadCompatibleVersion(extension: IGalleryExtension): TPromise<IGalleryExtension> {
|
||||
if (extension.properties.engine && this.isEngineValid(extension.properties.engine)) {
|
||||
if (extension.properties.engine && isEngineValid(extension.properties.engine)) {
|
||||
return TPromise.wrap(extension);
|
||||
}
|
||||
|
||||
@@ -738,7 +738,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
/* __GDPR__
|
||||
"galleryService:requestError" : {
|
||||
"url" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"cdn": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"cdn": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
|
||||
"message": { "classification": "CallstackOrException", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
@@ -786,7 +786,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
if (!engine) {
|
||||
return null;
|
||||
}
|
||||
if (this.isEngineValid(engine)) {
|
||||
if (isEngineValid(engine)) {
|
||||
return TPromise.wrap(version);
|
||||
}
|
||||
}
|
||||
@@ -807,7 +807,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
.then(manifest => {
|
||||
const engine = manifest.engines.vscode;
|
||||
|
||||
if (!this.isEngineValid(engine)) {
|
||||
if (!isEngineValid(engine)) {
|
||||
return this.getLastValidExtensionVersionReccursively(extension, versions.slice(1));
|
||||
}
|
||||
|
||||
@@ -817,11 +817,6 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
});
|
||||
}
|
||||
|
||||
private isEngineValid(engine: string): boolean {
|
||||
// TODO@joao: discuss with alex '*' doesn't seem to be a valid engine version
|
||||
return engine === '*' || isVersionValid(pkg.version, engine);
|
||||
}
|
||||
|
||||
private static hasExtensionByName(extensions: IGalleryExtension[], name: string): boolean {
|
||||
for (const extension of extensions) {
|
||||
if (`${extension.publisher}.${extension.name}` === name) {
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'path';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { toDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { flatten, distinct, coalesce } from 'vs/base/common/arrays';
|
||||
import { extract, buffer } from 'vs/base/node/zip';
|
||||
import { flatten, distinct } from 'vs/base/common/arrays';
|
||||
import { extract, buffer, ExtractError } from 'vs/base/node/zip';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import {
|
||||
IExtensionManagementService, IExtensionGalleryService, ILocalExtension,
|
||||
@@ -22,21 +22,23 @@ import {
|
||||
IExtensionIdentifier,
|
||||
IReportedExtension
|
||||
} from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { localizeManifest } from '../common/extensionNls';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { Limiter } from 'vs/base/common/async';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Limiter, always } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import * as semver from 'semver';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import pkg from 'vs/platform/node/package';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ExtensionsManifestCache } from 'vs/platform/extensionManagement/node/extensionsManifestCache';
|
||||
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { isEngineValid } from 'vs/platform/extensions/node/extensionValidator';
|
||||
|
||||
const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));
|
||||
const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem';
|
||||
@@ -48,10 +50,9 @@ const INSTALL_ERROR_VALIDATING = 'validating';
|
||||
const INSTALL_ERROR_GALLERY = 'gallery';
|
||||
const INSTALL_ERROR_LOCAL = 'local';
|
||||
const INSTALL_ERROR_EXTRACTING = 'extracting';
|
||||
const INSTALL_ERROR_RENAMING = 'renaming';
|
||||
const INSTALL_ERROR_DELETING = 'deleting';
|
||||
const INSTALL_ERROR_READING_EXTENSION_FROM_DISK = 'readingExtension';
|
||||
const INSTALL_ERROR_SAVING_METADATA = 'savingMetadata';
|
||||
const INSTALL_ERROR_UNKNOWN = 'unknown';
|
||||
const ERROR_UNKNOWN = 'unknown';
|
||||
|
||||
export class ExtensionManagementError extends Error {
|
||||
constructor(message: string, readonly code: string) {
|
||||
@@ -101,6 +102,11 @@ interface InstallableExtension {
|
||||
metadata?: IGalleryMetadata;
|
||||
}
|
||||
|
||||
enum Operation {
|
||||
Install = 1,
|
||||
Update
|
||||
}
|
||||
|
||||
export class ExtensionManagementService extends Disposable implements IExtensionManagementService {
|
||||
|
||||
_serviceBrand: any;
|
||||
@@ -110,6 +116,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
private uninstalledFileLimiter: Limiter<void>;
|
||||
private reportedExtensions: TPromise<IReportedExtension[]> | undefined;
|
||||
private lastReportTimestamp = 0;
|
||||
private readonly installationStartTime: Map<string, number> = new Map<string, number>();
|
||||
private readonly installingExtensions: Map<string, TPromise<ILocalExtension>> = new Map<string, TPromise<ILocalExtension>>();
|
||||
private readonly manifestCache: ExtensionsManifestCache;
|
||||
private readonly extensionLifecycle: ExtensionsLifecycle;
|
||||
@@ -128,9 +135,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
|
||||
constructor(
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IChoiceService private choiceService: IChoiceService,
|
||||
@IDialogService private dialogService: IDialogService,
|
||||
@IExtensionGalleryService private galleryService: IExtensionGalleryService,
|
||||
@ILogService private logService: ILogService
|
||||
@ILogService private logService: ILogService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService,
|
||||
) {
|
||||
super();
|
||||
this.extensionsPath = environmentService.extensionsPath;
|
||||
@@ -141,13 +149,17 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
this.extensionLifecycle = this._register(new ExtensionsLifecycle(this.logService));
|
||||
}
|
||||
|
||||
install(zipPath: string): TPromise<void> {
|
||||
install(zipPath: string): TPromise<ILocalExtension> {
|
||||
zipPath = path.resolve(zipPath);
|
||||
|
||||
return validateLocalExtension(zipPath)
|
||||
.then(manifest => {
|
||||
const identifier = { id: getLocalExtensionIdFromManifest(manifest) };
|
||||
return this.unsetUninstalledAndRemove(identifier.id)
|
||||
// {{SQL CARBON EDIT - Remove VS Code version check}}
|
||||
// if (manifest.engines && manifest.engines.vscode && !isEngineValid(manifest.engines.vscode)) {
|
||||
// return TPromise.wrapError<ILocalExtension>(new Error(nls.localize('incompatible', "Unable to install Extension '{0}' as it is not compatible with Code '{1}'.", identifier.id, pkg.version)));
|
||||
// }
|
||||
return this.removeIfExists(identifier.id)
|
||||
.then(
|
||||
() => this.checkOutdated(manifest)
|
||||
.then(validated => {
|
||||
@@ -175,18 +187,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
});
|
||||
}
|
||||
|
||||
private unsetUninstalledAndRemove(id: string): TPromise<void> {
|
||||
return this.isUninstalled(id)
|
||||
.then(isUninstalled => {
|
||||
if (isUninstalled) {
|
||||
this.logService.trace('Removing the extension:', id);
|
||||
const extensionPath = path.join(this.extensionsPath, id);
|
||||
return pfs.rimraf(extensionPath)
|
||||
.then(() => this.unsetUninstalled(id))
|
||||
.then(() => this.logService.info('Removed the extension:', id));
|
||||
}
|
||||
return null;
|
||||
});
|
||||
private removeIfExists(id: string): TPromise<void> {
|
||||
return this.getInstalled(LocalExtensionType.User)
|
||||
.then(installed => installed.filter(i => i.identifier.id === id)[0])
|
||||
.then(existing => existing ? this.removeExtension(existing, 'existing') : null);
|
||||
}
|
||||
|
||||
private checkOutdated(manifest: IExtensionManifest): TPromise<boolean> {
|
||||
@@ -196,11 +200,11 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
const newer = installedExtensions.filter(local => areSameExtensions(extensionIdentifier, { id: getGalleryExtensionIdFromLocal(local) }) && semver.gt(local.manifest.version, manifest.version))[0];
|
||||
if (newer) {
|
||||
const message = nls.localize('installingOutdatedExtension', "A newer version of this extension is already installed. Would you like to override this with the older version?");
|
||||
const options = [
|
||||
const buttons = [
|
||||
nls.localize('override', "Override"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
];
|
||||
return this.choiceService.choose(Severity.Info, message, options, 1, true)
|
||||
return this.dialogService.show(Severity.Info, message, buttons, { cancelId: 1 })
|
||||
.then<boolean>(value => {
|
||||
if (value === 0) {
|
||||
return this.uninstall(newer, true).then(() => true);
|
||||
@@ -212,58 +216,65 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
});
|
||||
}
|
||||
|
||||
private installFromZipPath(identifier: IExtensionIdentifier, zipPath: string, metadata: IGalleryMetadata, manifest: IExtensionManifest): TPromise<void> {
|
||||
private installFromZipPath(identifier: IExtensionIdentifier, zipPath: string, metadata: IGalleryMetadata, manifest: IExtensionManifest): TPromise<ILocalExtension> {
|
||||
return this.installExtension({ zipPath, id: identifier.id, metadata })
|
||||
.then(local => {
|
||||
if (this.galleryService.isEnabled() && local.manifest.extensionDependencies && local.manifest.extensionDependencies.length) {
|
||||
return this.getDependenciesToInstall(local.manifest.extensionDependencies)
|
||||
.then(dependenciesToInstall => this.downloadAndInstallExtensions(metadata ? dependenciesToInstall.filter(d => d.identifier.uuid !== metadata.id) : dependenciesToInstall))
|
||||
.then(() => local, error => {
|
||||
this.uninstallExtension(local);
|
||||
this.setUninstalled(local);
|
||||
return TPromise.wrapError(new Error(nls.localize('errorInstallingDependencies', "Error while installing dependencies. {0}", error instanceof Error ? error.message : error)));
|
||||
});
|
||||
}
|
||||
return local;
|
||||
})
|
||||
.then(
|
||||
local => this._onDidInstallExtension.fire({ identifier, zipPath, local }),
|
||||
error => { this._onDidInstallExtension.fire({ identifier, zipPath, error }); return TPromise.wrapError(error); }
|
||||
local => { this._onDidInstallExtension.fire({ identifier, zipPath, local }); return local; },
|
||||
error => { this._onDidInstallExtension.fire({ identifier, zipPath, error }); return TPromise.wrapError(error); }
|
||||
);
|
||||
}
|
||||
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<void> {
|
||||
installFromGallery(extension: IGalleryExtension): TPromise<ILocalExtension> {
|
||||
this.onInstallExtensions([extension]);
|
||||
return this.collectExtensionsToInstall(extension)
|
||||
.then(
|
||||
extensionsToInstall => {
|
||||
if (extensionsToInstall.length > 1) {
|
||||
this.onInstallExtensions(extensionsToInstall.slice(1));
|
||||
}
|
||||
return this.downloadAndInstallExtensions(extensionsToInstall)
|
||||
.then(
|
||||
locals => this.onDidInstallExtensions(extensionsToInstall, locals, []),
|
||||
errors => this.onDidInstallExtensions(extensionsToInstall, [], errors));
|
||||
},
|
||||
error => this.onDidInstallExtensions([extension], [], [error]));
|
||||
return this.getInstalled(LocalExtensionType.User)
|
||||
.then(installed => this.collectExtensionsToInstall(extension)
|
||||
.then(
|
||||
extensionsToInstall => {
|
||||
if (extensionsToInstall.length > 1) {
|
||||
this.onInstallExtensions(extensionsToInstall.slice(1));
|
||||
}
|
||||
const operataions: Operation[] = extensionsToInstall.map(e => this.getOperation(e, installed));
|
||||
return this.downloadAndInstallExtensions(extensionsToInstall)
|
||||
.then(
|
||||
locals => this.onDidInstallExtensions(extensionsToInstall, locals, operataions, [])
|
||||
.then(() => locals.filter(l => areSameExtensions({ id: getGalleryExtensionIdFromLocal(l), uuid: l.identifier.uuid }, extension.identifier)[0])),
|
||||
errors => this.onDidInstallExtensions(extensionsToInstall, [], operataions, errors));
|
||||
},
|
||||
error => this.onDidInstallExtensions([extension], [], [this.getOperation(extension, installed)], [error])));
|
||||
}
|
||||
|
||||
reinstall(extension: ILocalExtension): TPromise<void> {
|
||||
reinstallFromGallery(extension: ILocalExtension): TPromise<ILocalExtension> {
|
||||
if (!this.galleryService.isEnabled()) {
|
||||
return TPromise.wrapError(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled")));
|
||||
}
|
||||
return this.findGalleryExtension(extension)
|
||||
.then(galleryExtension => {
|
||||
if (galleryExtension) {
|
||||
return this.uninstallExtension(extension)
|
||||
return this.setUninstalled(extension)
|
||||
.then(() => this.removeUninstalledExtension(extension)
|
||||
.then(
|
||||
() => this.installFromGallery(galleryExtension),
|
||||
e => TPromise.wrapError(new Error(nls.localize('removeError', "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", toErrorMessage(e))))));
|
||||
() => this.installFromGallery(galleryExtension),
|
||||
e => TPromise.wrapError(new Error(nls.localize('removeError', "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", toErrorMessage(e))))));
|
||||
}
|
||||
return TPromise.wrapError(new Error(nls.localize('Not Market place extension', "Only Market place Extensions can be reinstalled")));
|
||||
return TPromise.wrapError(new Error(nls.localize('Not a Marketplace extension', "Only Marketplace Extensions can be reinstalled")));
|
||||
});
|
||||
}
|
||||
|
||||
private getOperation(extensionToInstall: IGalleryExtension, installed: ILocalExtension[]): Operation {
|
||||
return installed.some(i => areSameExtensions({ id: getGalleryExtensionIdFromLocal(i), uuid: i.identifier.uuid }, extensionToInstall.identifier)) ? Operation.Update : Operation.Install;
|
||||
}
|
||||
|
||||
private collectExtensionsToInstall(extension: IGalleryExtension): TPromise<IGalleryExtension[]> {
|
||||
return this.galleryService.loadCompatibleVersion(extension)
|
||||
.then(compatible => {
|
||||
@@ -273,10 +284,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
}
|
||||
return this.getDependenciesToInstall(compatible.properties.dependencies)
|
||||
.then(
|
||||
dependenciesToInstall => ([compatible, ...dependenciesToInstall.filter(d => d.identifier.uuid !== compatible.identifier.uuid)]),
|
||||
error => TPromise.wrapError<IGalleryExtension[]>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
dependenciesToInstall => ([compatible, ...dependenciesToInstall.filter(d => d.identifier.uuid !== compatible.identifier.uuid)]),
|
||||
error => TPromise.wrapError<IGalleryExtension[]>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
},
|
||||
error => TPromise.wrapError<IGalleryExtension[]>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
error => TPromise.wrapError<IGalleryExtension[]>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
}
|
||||
|
||||
private downloadAndInstallExtensions(extensions: IGalleryExtension[]): TPromise<ILocalExtension[]> {
|
||||
@@ -298,8 +309,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
.then(extension => this.downloadInstallableExtension(extension))
|
||||
.then(installableExtension => this.installExtension(installableExtension))
|
||||
.then(
|
||||
local => { this.installingExtensions.delete(extension.identifier.id); return local; },
|
||||
e => { this.installingExtensions.delete(extension.identifier.id); return TPromise.wrapError(e); }
|
||||
local => { this.installingExtensions.delete(extension.identifier.id); return local; },
|
||||
e => { this.installingExtensions.delete(extension.identifier.id); return TPromise.wrapError(e); }
|
||||
);
|
||||
|
||||
this.installingExtensions.set(extension.identifier.id, installingExtension);
|
||||
@@ -316,36 +327,37 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
|
||||
return this.galleryService.loadCompatibleVersion(extension)
|
||||
.then(
|
||||
compatible => {
|
||||
if (compatible) {
|
||||
this.logService.trace('Started downloading extension:', extension.name);
|
||||
return this.galleryService.download(extension)
|
||||
.then(
|
||||
zipPath => {
|
||||
this.logService.info('Downloaded extension:', extension.name);
|
||||
return validateLocalExtension(zipPath)
|
||||
.then(
|
||||
manifest => (<InstallableExtension>{ zipPath, id: getLocalExtensionIdFromManifest(manifest), metadata }),
|
||||
error => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_VALIDATING))
|
||||
);
|
||||
},
|
||||
error => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_DOWNLOADING)));
|
||||
} else {
|
||||
return TPromise.wrapError<InstallableExtension>(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the depending extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE));
|
||||
}
|
||||
},
|
||||
error => TPromise.wrapError<InstallableExtension>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
compatible => {
|
||||
if (compatible) {
|
||||
this.logService.trace('Started downloading extension:', extension.name);
|
||||
return this.galleryService.download(extension)
|
||||
.then(
|
||||
zipPath => {
|
||||
this.logService.info('Downloaded extension:', extension.name);
|
||||
return validateLocalExtension(zipPath)
|
||||
.then(
|
||||
manifest => (<InstallableExtension>{ zipPath, id: getLocalExtensionIdFromManifest(manifest), metadata }),
|
||||
error => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_VALIDATING))
|
||||
);
|
||||
},
|
||||
error => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_DOWNLOADING)));
|
||||
} else {
|
||||
return TPromise.wrapError<InstallableExtension>(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the depending extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE));
|
||||
}
|
||||
},
|
||||
error => TPromise.wrapError<InstallableExtension>(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY)));
|
||||
}
|
||||
|
||||
private onInstallExtensions(extensions: IGalleryExtension[]): void {
|
||||
for (const extension of extensions) {
|
||||
this.logService.info('Installing extension:', extension.name);
|
||||
this.installationStartTime.set(extension.identifier.id, new Date().getTime());
|
||||
const id = getLocalExtensionIdFromGallery(extension, extension.version);
|
||||
this._onInstallExtension.fire({ identifier: { id, uuid: extension.identifier.uuid }, gallery: extension });
|
||||
}
|
||||
}
|
||||
|
||||
private onDidInstallExtensions(extensions: IGalleryExtension[], locals: ILocalExtension[], errors: Error[]): TPromise<any> {
|
||||
private onDidInstallExtensions(extensions: IGalleryExtension[], locals: ILocalExtension[], operations: Operation[], errors: Error[]): TPromise<any> {
|
||||
extensions.forEach((gallery, index) => {
|
||||
const identifier = { id: getLocalExtensionIdFromGallery(gallery, gallery.version), uuid: gallery.identifier.uuid };
|
||||
const local = locals[index];
|
||||
@@ -354,10 +366,13 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
this.logService.info(`Extensions installed successfully:`, gallery.identifier.id);
|
||||
this._onDidInstallExtension.fire({ identifier, gallery, local });
|
||||
} else {
|
||||
const errorCode = error && (<ExtensionManagementError>error).code ? (<ExtensionManagementError>error).code : INSTALL_ERROR_UNKNOWN;
|
||||
const errorCode = error && (<ExtensionManagementError>error).code ? (<ExtensionManagementError>error).code : ERROR_UNKNOWN;
|
||||
this.logService.error(`Failed to install extension:`, gallery.identifier.id, error ? error.message : errorCode);
|
||||
this._onDidInstallExtension.fire({ identifier, gallery, error: errorCode });
|
||||
}
|
||||
const startTime = this.installationStartTime.get(gallery.identifier.id);
|
||||
this.reportTelemetry(operations[index] === Operation.Install ? 'extensionGallery:install' : 'extensionGallery:update', getGalleryExtensionTelemetryData(gallery), startTime ? new Date().getTime() - startTime : void 0, error);
|
||||
this.installationStartTime.delete(gallery.identifier.id);
|
||||
});
|
||||
return errors.length ? TPromise.wrapError(this.joinErrors(errors)) : TPromise.as(null);
|
||||
}
|
||||
@@ -383,18 +398,18 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
private installExtension(installableExtension: InstallableExtension): TPromise<ILocalExtension> {
|
||||
return this.unsetUninstalledAndGetLocal(installableExtension.id)
|
||||
.then(
|
||||
local => {
|
||||
if (local) {
|
||||
return local;
|
||||
}
|
||||
return this.extractAndInstall(installableExtension);
|
||||
},
|
||||
e => {
|
||||
if (isMacintosh) {
|
||||
return TPromise.wrapError<ILocalExtension>(new ExtensionManagementError(nls.localize('quitCode', "Unable to install the extension. Please Quit and Start VS Code before reinstalling."), INSTALL_ERROR_UNSET_UNINSTALLED));
|
||||
}
|
||||
return TPromise.wrapError<ILocalExtension>(new ExtensionManagementError(nls.localize('exitCode', "Unable to install the extension. Please Exit and Start VS Code before reinstalling."), INSTALL_ERROR_UNSET_UNINSTALLED));
|
||||
});
|
||||
local => {
|
||||
if (local) {
|
||||
return local;
|
||||
}
|
||||
return this.extractAndInstall(installableExtension);
|
||||
},
|
||||
e => {
|
||||
if (isMacintosh) {
|
||||
return TPromise.wrapError<ILocalExtension>(new ExtensionManagementError(nls.localize('quitCode', "Unable to install the extension. Please Quit and Start VS Code before reinstalling."), INSTALL_ERROR_UNSET_UNINSTALLED));
|
||||
}
|
||||
return TPromise.wrapError<ILocalExtension>(new ExtensionManagementError(nls.localize('exitCode', "Unable to install the extension. Please Exit and Start VS Code before reinstalling."), INSTALL_ERROR_UNSET_UNINSTALLED));
|
||||
});
|
||||
}
|
||||
|
||||
private unsetUninstalledAndGetLocal(id: string): TPromise<ILocalExtension> {
|
||||
@@ -415,43 +430,53 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
}
|
||||
|
||||
private extractAndInstall({ zipPath, id, metadata }: InstallableExtension): TPromise<ILocalExtension> {
|
||||
const tempPath = path.join(this.extensionsPath, `.${id}`);
|
||||
const extensionPath = path.join(this.extensionsPath, id);
|
||||
return pfs.rimraf(extensionPath)
|
||||
return this.extractAndRename(id, zipPath, tempPath, extensionPath)
|
||||
.then(() => {
|
||||
this.logService.trace(`Started extracting the extension from ${zipPath} to ${extensionPath}`);
|
||||
return extract(zipPath, extensionPath, { sourcePath: 'extension', overwrite: true })
|
||||
.then(
|
||||
() => {
|
||||
this.logService.info(`Extracted extension to ${extensionPath}:`, id);
|
||||
return this.completeInstall(id, extensionPath, metadata);
|
||||
},
|
||||
e => TPromise.wrapError(new ExtensionManagementError(e.message, INSTALL_ERROR_EXTRACTING)))
|
||||
.then(null, e => {
|
||||
this.logService.info('Deleting the extracted extension', id);
|
||||
return pfs.rimraf(extensionPath).then(() => TPromise.wrapError(e), () => TPromise.wrapError(e));
|
||||
});
|
||||
}, e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING)));
|
||||
this.logService.info('Installation completed.', id);
|
||||
return this.scanExtension(id, this.extensionsPath, LocalExtensionType.User);
|
||||
})
|
||||
.then(local => {
|
||||
if (metadata) {
|
||||
local.metadata = metadata;
|
||||
return this.saveMetadataForLocalExtension(local);
|
||||
}
|
||||
return local;
|
||||
});
|
||||
}
|
||||
|
||||
private completeInstall(id: string, extensionPath: string, metadata: IGalleryMetadata): TPromise<ILocalExtension> {
|
||||
return TPromise.join([readManifest(extensionPath), pfs.readdir(extensionPath)])
|
||||
.then(null, e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_READING_EXTENSION_FROM_DISK)))
|
||||
.then(([{ manifest }, children]) => {
|
||||
const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0];
|
||||
const readmeUrl = readme ? URI.file(path.join(extensionPath, readme)).toString() : null;
|
||||
const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0];
|
||||
const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)).toString() : null;
|
||||
const type = LocalExtensionType.User;
|
||||
const identifier = { id, uuid: metadata ? metadata.id : null };
|
||||
private extractAndRename(id: string, zipPath: string, extractPath: string, renamePath: string): TPromise<void> {
|
||||
return this.extract(id, zipPath, extractPath)
|
||||
.then(() => this.rename(id, extractPath, renamePath, Date.now() + (30 * 1000) /* Retry for 30 seconds */)
|
||||
.then(
|
||||
() => this.logService.info('Renamed to', renamePath),
|
||||
e => {
|
||||
this.logService.info('Rename failed. Deleting from extracted location', extractPath);
|
||||
return always(pfs.rimraf(extractPath), () => null).then(() => TPromise.wrapError(e));
|
||||
}));
|
||||
}
|
||||
|
||||
const local: ILocalExtension = { type, identifier, manifest, metadata, path: extensionPath, readmeUrl, changelogUrl };
|
||||
private extract(id: string, zipPath: string, extractPath: string): TPromise<void> {
|
||||
this.logService.trace(`Started extracting the extension from ${zipPath} to ${extractPath}`);
|
||||
return pfs.rimraf(extractPath)
|
||||
.then(
|
||||
() => extract(zipPath, extractPath, { sourcePath: 'extension', overwrite: true }, this.logService)
|
||||
.then(
|
||||
() => this.logService.info(`Extracted extension to ${extractPath}:`, id),
|
||||
e => always(pfs.rimraf(extractPath), () => null)
|
||||
.then(() => TPromise.wrapError(new ExtensionManagementError(e.message, e instanceof ExtractError ? e.type : INSTALL_ERROR_EXTRACTING)))),
|
||||
e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_DELETING)));
|
||||
}
|
||||
|
||||
this.logService.trace(`Updating metadata of the extension:`, id);
|
||||
return this.saveMetadataForLocalExtension(local)
|
||||
.then(() => {
|
||||
this.logService.info(`Updated metadata of the extension:`, id);
|
||||
return local;
|
||||
}, e => TPromise.wrapError(new ExtensionManagementError(this.joinErrors(e).message, INSTALL_ERROR_SAVING_METADATA)));
|
||||
private rename(id: string, extractPath: string, renamePath: string, retryUntil: number): TPromise<void> {
|
||||
return pfs.rename(extractPath, renamePath)
|
||||
.then(null, error => {
|
||||
if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) {
|
||||
this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`);
|
||||
return this.rename(id, extractPath, renamePath, retryUntil);
|
||||
}
|
||||
return TPromise.wrapError(new ExtensionManagementError(error.message || nls.localize('renameError', "Unknown error while renaming {0} to {1}", extractPath, renamePath), error.code || INSTALL_ERROR_RENAMING));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -459,7 +484,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
return this.getInstalled(LocalExtensionType.User)
|
||||
.then(installed =>
|
||||
TPromise.join(installed.filter(local => extensions.some(galleryExtension => local.identifier.id === getLocalExtensionIdFromGallery(galleryExtension, galleryExtension.version))) // Only check id (pub.name-version) because we want to rollback the exact version
|
||||
.map(local => this.uninstallExtension(local))))
|
||||
.map(local => this.setUninstalled(local))))
|
||||
.then(() => null, () => null);
|
||||
}
|
||||
|
||||
@@ -529,10 +554,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
return this.preUninstallExtension(extension)
|
||||
.then(() => this.hasDependencies(extension, installed) ? this.promptForDependenciesAndUninstall(extension, installed, force) : this.promptAndUninstall(extension, installed, force))
|
||||
.then(() => this.postUninstallExtension(extension),
|
||||
error => {
|
||||
this.postUninstallExtension(extension, INSTALL_ERROR_LOCAL);
|
||||
return TPromise.wrapError(error);
|
||||
});
|
||||
error => {
|
||||
this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL));
|
||||
return TPromise.wrapError(error);
|
||||
});
|
||||
}
|
||||
|
||||
private hasDependencies(extension: ILocalExtension, installed: ILocalExtension[]): boolean {
|
||||
@@ -549,13 +574,13 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
}
|
||||
|
||||
const message = nls.localize('uninstallDependeciesConfirmation', "Would you like to uninstall '{0}' only or its dependencies also?", extension.manifest.displayName || extension.manifest.name);
|
||||
const options = [
|
||||
nls.localize('uninstallOnly', "Only"),
|
||||
nls.localize('uninstallAll', "All"),
|
||||
const buttons = [
|
||||
nls.localize('uninstallOnly', "Extension Only"),
|
||||
nls.localize('uninstallAll', "Uninstall All"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
];
|
||||
this.logService.info('Requesting for confirmation to uninstall extension with dependencies', extension.identifier.id);
|
||||
return this.choiceService.choose(Severity.Info, message, options, 2, true)
|
||||
return this.dialogService.show(Severity.Info, message, buttons, { cancelId: 2 })
|
||||
.then<void>(value => {
|
||||
if (value === 0) {
|
||||
return this.uninstallWithDependencies(extension, [], installed);
|
||||
@@ -575,12 +600,12 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
}
|
||||
|
||||
const message = nls.localize('uninstallConfirmation', "Are you sure you want to uninstall '{0}'?", extension.manifest.displayName || extension.manifest.name);
|
||||
const options = [
|
||||
const buttons = [
|
||||
nls.localize('ok', "OK"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
];
|
||||
this.logService.info('Requesting for confirmation to uninstall extension', extension.identifier.id);
|
||||
return this.choiceService.choose(Severity.Info, message, options, 1, true)
|
||||
return this.dialogService.show(Severity.Info, message, buttons, { cancelId: 1 })
|
||||
.then<void>(value => {
|
||||
if (value === 0) {
|
||||
return this.uninstallWithDependencies(extension, [], installed);
|
||||
@@ -649,10 +674,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
return this.preUninstallExtension(extension)
|
||||
.then(() => this.uninstallExtension(extension))
|
||||
.then(() => this.postUninstallExtension(extension),
|
||||
error => {
|
||||
this.postUninstallExtension(extension, INSTALL_ERROR_LOCAL);
|
||||
return TPromise.wrapError(error);
|
||||
});
|
||||
error => {
|
||||
this.postUninstallExtension(extension, new ExtensionManagementError(error instanceof Error ? error.message : error, INSTALL_ERROR_LOCAL));
|
||||
return TPromise.wrapError(error);
|
||||
});
|
||||
}
|
||||
|
||||
private preUninstallExtension(extension: ILocalExtension): TPromise<void> {
|
||||
@@ -665,12 +690,14 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
}
|
||||
|
||||
private uninstallExtension(local: ILocalExtension): TPromise<void> {
|
||||
return this.setUninstalled(local.identifier.id);
|
||||
// Set all versions of the extension as uninstalled
|
||||
return this.scanUserExtensions(false)
|
||||
.then(userExtensions => this.setUninstalled(...userExtensions.filter(u => areSameExtensions({ id: getGalleryExtensionIdFromLocal(u), uuid: u.identifier.uuid }, { id: getGalleryExtensionIdFromLocal(local), uuid: local.identifier.uuid }))));
|
||||
}
|
||||
|
||||
private async postUninstallExtension(extension: ILocalExtension, error?: string): TPromise<void> {
|
||||
private async postUninstallExtension(extension: ILocalExtension, error?: Error): TPromise<void> {
|
||||
if (error) {
|
||||
this.logService.error('Failed to uninstall extension:', extension.identifier.id, error);
|
||||
this.logService.error('Failed to uninstall extension:', extension.identifier.id, error.message);
|
||||
} else {
|
||||
this.logService.info('Successfully uninstalled extension:', extension.identifier.id);
|
||||
// only report if extension has a mapped gallery extension. UUID identifies the gallery extension.
|
||||
@@ -678,7 +705,9 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
await this.galleryService.reportStatistic(extension.manifest.publisher, extension.manifest.name, extension.manifest.version, StatisticType.Uninstall);
|
||||
}
|
||||
}
|
||||
this._onDidUninstallExtension.fire({ identifier: extension.identifier, error });
|
||||
this.reportTelemetry('extensionGallery:uninstall', getLocalExtensionTelemetryData(extension), void 0, error);
|
||||
const errorcode = error ? error instanceof ExtensionManagementError ? error.code : ERROR_UNKNOWN : void 0;
|
||||
this._onDidUninstallExtension.fire({ identifier: extension.identifier, error: errorcode });
|
||||
}
|
||||
|
||||
getInstalled(type: LocalExtensionType = null): TPromise<ILocalExtension[]> {
|
||||
@@ -721,11 +750,14 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
private scanExtensions(root: string, type: LocalExtensionType): TPromise<ILocalExtension[]> {
|
||||
const limiter = new Limiter(10);
|
||||
return pfs.readdir(root)
|
||||
.then(extensionsFolders => TPromise.join(extensionsFolders.map(extensionFolder => limiter.queue(() => this.scanExtension(extensionFolder, root, type)))))
|
||||
.then(extensions => coalesce(extensions));
|
||||
.then(extensionsFolders => TPromise.join<ILocalExtension>(extensionsFolders.map(extensionFolder => limiter.queue(() => this.scanExtension(extensionFolder, root, type)))))
|
||||
.then(extensions => extensions.filter(e => e && e.identifier));
|
||||
}
|
||||
|
||||
private scanExtension(folderName: string, root: string, type: LocalExtensionType): TPromise<ILocalExtension> {
|
||||
if (type === LocalExtensionType.User && folderName.indexOf('.') === 0) { // Do not consider user exension folder starting with `.`
|
||||
return TPromise.as(null);
|
||||
}
|
||||
const extensionPath = path.join(root, folderName);
|
||||
return pfs.readdir(extensionPath)
|
||||
.then(children => readManifest(extensionPath)
|
||||
@@ -798,7 +830,8 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
});
|
||||
}
|
||||
|
||||
private setUninstalled(...ids: string[]): TPromise<void> {
|
||||
private setUninstalled(...extensions: ILocalExtension[]): TPromise<void> {
|
||||
const ids = extensions.map(e => e.identifier.id);
|
||||
return this.withUninstalledExtensions(uninstalled => assign(uninstalled, ids.reduce((result, id) => { result[id] = true; return result; }, {})));
|
||||
}
|
||||
|
||||
@@ -852,6 +885,31 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
private reportTelemetry(eventName: string, extensionData: any, duration: number, error?: Error): void {
|
||||
const errorcode = error ? error instanceof ExtensionManagementError ? error.code : ERROR_UNKNOWN : void 0;
|
||||
/* __GDPR__
|
||||
"extensionGallery:install" : {
|
||||
"success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"errorcode": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
/* __GDPR__
|
||||
"extensionGallery:uninstall" : {
|
||||
"success": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
|
||||
"errorcode": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
||||
"${include}": [
|
||||
"${GalleryExtensionTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this.telemetryService.publicLog(eventName, assign(extensionData, { success: !error, duration, errorcode }));
|
||||
}
|
||||
}
|
||||
|
||||
export function getLocalExtensionIdFromGallery(extension: IGalleryExtension, version: string): string {
|
||||
@@ -860,4 +918,4 @@ export function getLocalExtensionIdFromGallery(extension: IGalleryExtension, ver
|
||||
|
||||
export function getLocalExtensionIdFromManifest(manifest: IExtensionManifest): string {
|
||||
return getLocalExtensionId(getGalleryExtensionId(manifest.publisher, manifest.name), manifest.version);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as sinon from 'sinon';
|
||||
import { IExtensionManagementService, IExtensionEnablementService, DidUninstallExtensionEvent, EnablementState, IExtensionContributions, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IExtensionManagementService, IExtensionEnablementService, DidUninstallExtensionEvent, EnablementState, IExtensionContributions, ILocalExtension, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
@@ -98,13 +98,13 @@ suite('ExtensionEnablementService Test', () => {
|
||||
|
||||
test('test state of globally disabled extension', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
|
||||
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.Disabled));
|
||||
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Disabled));
|
||||
});
|
||||
|
||||
test('test state of globally enabled extension', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled))
|
||||
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.Enabled));
|
||||
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Enabled));
|
||||
});
|
||||
|
||||
test('test disable an extension for workspace', () => {
|
||||
@@ -126,59 +126,59 @@ suite('ExtensionEnablementService Test', () => {
|
||||
|
||||
test('test state of workspace disabled extension', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
|
||||
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.WorkspaceDisabled));
|
||||
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceDisabled));
|
||||
});
|
||||
|
||||
test('test state of workspace and globally disabled extension', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled))
|
||||
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.WorkspaceDisabled));
|
||||
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceDisabled));
|
||||
});
|
||||
|
||||
test('test state of workspace enabled extension', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled))
|
||||
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.WorkspaceEnabled));
|
||||
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceEnabled));
|
||||
});
|
||||
|
||||
test('test state of globally disabled and workspace enabled extension', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled))
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled))
|
||||
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.WorkspaceEnabled));
|
||||
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceEnabled));
|
||||
});
|
||||
|
||||
test('test state of an extension when disabled for workspace from workspace enabled', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled))
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled))
|
||||
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.WorkspaceDisabled));
|
||||
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceDisabled));
|
||||
});
|
||||
|
||||
test('test state of an extension when disabled globally from workspace enabled', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled))
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled))
|
||||
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.Disabled));
|
||||
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Disabled));
|
||||
});
|
||||
|
||||
test('test state of an extension when disabled globally from workspace disabled', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled))
|
||||
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.Disabled));
|
||||
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Disabled));
|
||||
});
|
||||
|
||||
test('test state of an extension when enabled globally from workspace enabled', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled))
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled))
|
||||
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.Enabled));
|
||||
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Enabled));
|
||||
});
|
||||
|
||||
test('test state of an extension when enabled globally from workspace disabled', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled))
|
||||
.then(() => assert.equal(testObject.getEnablementState({ id: 'pub.a' }), EnablementState.Enabled));
|
||||
.then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Enabled));
|
||||
});
|
||||
|
||||
test('test disable an extension for workspace and then globally', () => {
|
||||
@@ -223,11 +223,10 @@ suite('ExtensionEnablementService Test', () => {
|
||||
.then(() => assert.ok(target.calledWithExactly({ id: 'pub.a', uuid: void 0 })));
|
||||
});
|
||||
|
||||
test('test disable an extension for workspace when there is no workspace throws error', (done) => {
|
||||
test('test disable an extension for workspace when there is no workspace throws error', () => {
|
||||
instantiationService.stub(IWorkspaceContextService, 'getWorkbenchState', WorkbenchState.EMPTY);
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
|
||||
.then(() => assert.fail('should throw an error'), error => assert.ok(error))
|
||||
.then(done, done);
|
||||
.then(() => assert.fail('should throw an error'), error => assert.ok(error));
|
||||
});
|
||||
|
||||
test('test enable an extension globally', () => {
|
||||
@@ -237,26 +236,23 @@ suite('ExtensionEnablementService Test', () => {
|
||||
.then(extensions => assert.deepEqual([], extensions));
|
||||
});
|
||||
|
||||
test('test enable an extension globally return truthy promise', (done) => {
|
||||
testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
|
||||
test('test enable an extension globally return truthy promise', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled))
|
||||
.then(value => assert.ok(value))
|
||||
.then(done, done);
|
||||
.then(value => assert.ok(value));
|
||||
});
|
||||
|
||||
test('test enable an extension globally triggers change event', (done) => {
|
||||
test('test enable an extension globally triggers change event', () => {
|
||||
const target = sinon.spy();
|
||||
testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
|
||||
.then(() => testObject.onEnablementChanged(target))
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled))
|
||||
.then(() => assert.ok(target.calledWithExactly({ id: 'pub.a', uuid: void 0 })))
|
||||
.then(done, done);
|
||||
.then(() => assert.ok(target.calledWithExactly({ id: 'pub.a', uuid: void 0 })));
|
||||
});
|
||||
|
||||
test('test enable an extension globally when already enabled return falsy promise', (done) => {
|
||||
testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)
|
||||
.then(value => assert.ok(!value))
|
||||
.then(done, done);
|
||||
test('test enable an extension globally when already enabled return falsy promise', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)
|
||||
.then(value => assert.ok(!value));
|
||||
});
|
||||
|
||||
test('test enable an extension for workspace', () => {
|
||||
@@ -311,23 +307,37 @@ suite('ExtensionEnablementService Test', () => {
|
||||
|
||||
test('test isEnabled return false extension is disabled globally', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)
|
||||
.then(() => assert.ok(!testObject.isEnabled({ id: 'pub.a' })));
|
||||
.then(() => assert.ok(!testObject.isEnabled(aLocalExtension('pub.a'))));
|
||||
});
|
||||
|
||||
test('test isEnabled return false extension is disabled in workspace', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
|
||||
.then(() => assert.ok(!testObject.isEnabled({ id: 'pub.a' })));
|
||||
.then(() => assert.ok(!testObject.isEnabled(aLocalExtension('pub.a'))));
|
||||
});
|
||||
|
||||
test('test isEnabled return true extension is not disabled', () => {
|
||||
return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)
|
||||
.then(() => testObject.setEnablement(aLocalExtension('pub.c'), EnablementState.Disabled))
|
||||
.then(() => assert.ok(testObject.isEnabled({ id: 'pub.b' })));
|
||||
.then(() => assert.ok(testObject.isEnabled(aLocalExtension('pub.b'))));
|
||||
});
|
||||
|
||||
test('test canChangeEnablement return false for language packs', () => {
|
||||
assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a', { localizations: [{ languageId: 'gr', translations: [{ id: 'vscode', path: 'path' }] }] })), false);
|
||||
});
|
||||
|
||||
test('test canChangeEnablement return false when extensions are disabled in environment', () => {
|
||||
instantiationService.stub(IEnvironmentService, { disableExtensions: true } as IEnvironmentService);
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a')), false);
|
||||
});
|
||||
|
||||
test('test canChangeEnablement return true for system extensions when extensions are disabled in environment', () => {
|
||||
instantiationService.stub(IEnvironmentService, { disableExtensions: true } as IEnvironmentService);
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
const extension = aLocalExtension('pub.a');
|
||||
extension.type = LocalExtensionType.System;
|
||||
assert.equal(testObject.canChangeEnablement(extension), true);
|
||||
});
|
||||
});
|
||||
|
||||
function aLocalExtension(id: string, contributes?: IExtensionContributions): ILocalExtension {
|
||||
@@ -338,6 +348,7 @@ function aLocalExtension(id: string, contributes?: IExtensionContributions): ILo
|
||||
name,
|
||||
publisher,
|
||||
contributes
|
||||
}
|
||||
},
|
||||
type: LocalExtensionType.User
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as os from 'os';
|
||||
import extfs = require('vs/base/node/extfs');
|
||||
import * as extfs from 'vs/base/node/extfs';
|
||||
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
|
||||
import { parseArgs } from 'vs/platform/environment/node/argv';
|
||||
import { getRandomTestPath } from 'vs/workbench/test/workbenchTestServices';
|
||||
@@ -27,7 +27,7 @@ suite('Extension Gallery Service', () => {
|
||||
extfs.del(marketplaceHome, os.tmpdir(), () => {
|
||||
mkdirp(marketplaceHome).then(() => {
|
||||
done();
|
||||
});
|
||||
}, error => done(error));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ suite('Extension Gallery Service', () => {
|
||||
extfs.del(marketplaceHome, os.tmpdir(), done);
|
||||
});
|
||||
|
||||
test('marketplace machine id', done => {
|
||||
test('marketplace machine id', () => {
|
||||
const args = ['--user-data-dir', marketplaceHome];
|
||||
const environmentService = new EnvironmentService(parseArgs(args), process.execPath);
|
||||
|
||||
@@ -44,8 +44,6 @@ suite('Extension Gallery Service', () => {
|
||||
|
||||
return resolveMarketplaceHeaders(environmentService).then(headers2 => {
|
||||
assert.equal(headers['X-Market-User-Id'], headers2['X-Market-User-Id']);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user