mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79 (#14050)
* Merge from vscode 2c306f762bf9c3db82dc06c7afaa56ef46d72f79 * Fix breaks * Extension management fixes * Fix breaks in windows bundling * Fix/skip failing tests * Update distro * Add clear to nuget.config * Add hygiene task * Bump distro * Fix hygiene issue * Add build to hygiene exclusion * Update distro * Update hygiene * Hygiene exclusions * Update tsconfig * Bump distro for server breaks * Update build config * Update darwin path * Add done calls to notebook tests * Skip failing tests * Disable smoke tests
This commit is contained in:
@@ -10,6 +10,7 @@ import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/ur
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { getUriFromAmdModule } from 'vs/base/common/amd';
|
||||
|
||||
interface IScannedBuiltinExtension {
|
||||
extensionPath: string;
|
||||
@@ -62,17 +63,14 @@ export class BuiltinExtensionsScannerService implements IBuiltinExtensionsScanne
|
||||
}
|
||||
|
||||
private _getBuiltinExtensionsUrl(environmentService: IWorkbenchEnvironmentService): URI | undefined {
|
||||
if (environmentService.options?.builtinExtensionsServiceUrl) {
|
||||
return URI.parse(environmentService.options?.builtinExtensionsServiceUrl);
|
||||
}
|
||||
let enableBuiltinExtensions: boolean;
|
||||
if (environmentService.options && typeof environmentService.options._enableBuiltinExtensions !== 'undefined') {
|
||||
enableBuiltinExtensions = environmentService.options._enableBuiltinExtensions;
|
||||
} else {
|
||||
enableBuiltinExtensions = environmentService.configuration.remoteAuthority ? false : true;
|
||||
enableBuiltinExtensions = environmentService.remoteAuthority ? false : true;
|
||||
}
|
||||
if (enableBuiltinExtensions) {
|
||||
return URI.parse(require.toUrl('../../../../../../extensions'));
|
||||
return getUriFromAmdModule(require, '../../../../../../extensions');
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { IExtensionManagementService, IGlobalExtensionEnablementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue';
|
||||
|
||||
// --- bisect service
|
||||
|
||||
export const IExtensionBisectService = createDecorator<IExtensionBisectService>('IExtensionBisectService');
|
||||
|
||||
export interface IExtensionBisectService {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
isDisabledByBisect(extension: IExtension): boolean;
|
||||
isActive: boolean;
|
||||
disabledCount: number;
|
||||
start(extensions: ILocalExtension[]): Promise<void>;
|
||||
next(seeingBad: boolean): Promise<{ id: string, bad: boolean } | undefined>;
|
||||
reset(): Promise<void>;
|
||||
}
|
||||
|
||||
class BisectState {
|
||||
|
||||
static fromJSON(raw: string | undefined): BisectState | undefined {
|
||||
if (!raw) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
interface Raw extends BisectState { }
|
||||
const data: Raw = JSON.parse(raw);
|
||||
return new BisectState(data.extensions, data.low, data.high);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
readonly mid: number;
|
||||
|
||||
constructor(
|
||||
readonly extensions: string[],
|
||||
readonly low: number,
|
||||
readonly high: number,
|
||||
) {
|
||||
this.mid = ((low + high) / 2) | 0;
|
||||
}
|
||||
}
|
||||
|
||||
class ExtensionBisectService implements IExtensionBisectService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private static readonly _storageKey = 'extensionBisectState';
|
||||
|
||||
private readonly _state: BisectState | undefined;
|
||||
private readonly _disabled = new Map<string, boolean>();
|
||||
|
||||
constructor(
|
||||
@ILogService logService: ILogService,
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
) {
|
||||
const raw = _storageService.get(ExtensionBisectService._storageKey, StorageScope.GLOBAL);
|
||||
this._state = BisectState.fromJSON(raw);
|
||||
|
||||
if (this._state) {
|
||||
const { mid, high } = this._state;
|
||||
for (let i = 0; i < this._state.extensions.length; i++) {
|
||||
const isDisabled = i >= mid && i < high;
|
||||
this._disabled.set(this._state.extensions[i], isDisabled);
|
||||
}
|
||||
logService.warn('extension BISECT active', [...this._disabled]);
|
||||
}
|
||||
}
|
||||
|
||||
get isActive() {
|
||||
return !!this._state;
|
||||
}
|
||||
|
||||
get disabledCount() {
|
||||
return this._state ? this._state.high - this._state.mid : -1;
|
||||
}
|
||||
|
||||
isDisabledByBisect(extension: IExtension): boolean {
|
||||
if (!this._state) {
|
||||
return false;
|
||||
}
|
||||
const disabled = this._disabled.get(extension.identifier.id);
|
||||
return disabled ?? false;
|
||||
}
|
||||
|
||||
async start(extensions: ILocalExtension[]): Promise<void> {
|
||||
if (this._state) {
|
||||
throw new Error('invalid state');
|
||||
}
|
||||
const extensionIds = extensions.map(ext => ext.identifier.id);
|
||||
const newState = new BisectState(extensionIds, 0, extensionIds.length);
|
||||
this._storageService.store(ExtensionBisectService._storageKey, JSON.stringify(newState), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
await this._storageService.flush();
|
||||
}
|
||||
|
||||
async next(seeingBad: boolean): Promise<{ id: string; bad: boolean; } | undefined> {
|
||||
if (!this._state) {
|
||||
throw new Error('invalid state');
|
||||
}
|
||||
// check if there is only one left
|
||||
if (this._state.low === this._state.high - 1) {
|
||||
await this.reset();
|
||||
return { id: this._state.extensions[this._state.low], bad: seeingBad };
|
||||
}
|
||||
// the second half is disabled so if there is still bad it must be
|
||||
// in the first half
|
||||
const nextState = new BisectState(
|
||||
this._state.extensions,
|
||||
seeingBad ? this._state.low : this._state.mid,
|
||||
seeingBad ? this._state.mid : this._state.high,
|
||||
);
|
||||
this._storageService.store(ExtensionBisectService._storageKey, JSON.stringify(nextState), StorageScope.GLOBAL, StorageTarget.MACHINE);
|
||||
await this._storageService.flush();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async reset(): Promise<void> {
|
||||
this._storageService.remove(ExtensionBisectService._storageKey, StorageScope.GLOBAL);
|
||||
await this._storageService.flush();
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IExtensionBisectService, ExtensionBisectService, true);
|
||||
|
||||
// --- bisect UI
|
||||
|
||||
class ExtensionBisectUi {
|
||||
|
||||
static ctxIsBisectActive = new RawContextKey('isExtensionBisectActive', false);
|
||||
|
||||
constructor(
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IExtensionBisectService private readonly _extensionBisectService: IExtensionBisectService,
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
) {
|
||||
if (_extensionBisectService.isActive) {
|
||||
ExtensionBisectUi.ctxIsBisectActive.bindTo(contextKeyService).set(true);
|
||||
this._showBisectPrompt();
|
||||
}
|
||||
}
|
||||
|
||||
private _showBisectPrompt(): void {
|
||||
|
||||
const goodPrompt: IPromptChoice = {
|
||||
label: 'Good now',
|
||||
run: () => this._commandService.executeCommand('extension.bisect.next', false)
|
||||
};
|
||||
const badPrompt: IPromptChoice = {
|
||||
label: 'This is bad',
|
||||
run: () => this._commandService.executeCommand('extension.bisect.next', true)
|
||||
};
|
||||
const stop: IPromptChoice = {
|
||||
label: 'Stop Bisect',
|
||||
run: () => this._commandService.executeCommand('extension.bisect.stop')
|
||||
};
|
||||
|
||||
this._notificationService.prompt(
|
||||
Severity.Info,
|
||||
localize('bisect', "Extension Bisect is active and has disabled {0} extensions. Check if you can still reproduce the problem and proceed by selecting from these options.", this._extensionBisectService.disabledCount),
|
||||
[goodPrompt, badPrompt, stop],
|
||||
{ sticky: true }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(
|
||||
ExtensionBisectUi,
|
||||
LifecyclePhase.Restored
|
||||
);
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'extension.bisect.start',
|
||||
title: localize('title.start', "Start Extension Bisect"),
|
||||
category: localize('help', "Help"),
|
||||
f1: true,
|
||||
precondition: ExtensionBisectUi.ctxIsBisectActive.negate()
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const dialogService = accessor.get(IDialogService);
|
||||
const hostService = accessor.get(IHostService);
|
||||
const extensionManagement = accessor.get(IExtensionManagementService);
|
||||
const extensionEnablementService = accessor.get(IGlobalExtensionEnablementService);
|
||||
const extensionsBisect = accessor.get(IExtensionBisectService);
|
||||
|
||||
const disabled = new Set(extensionEnablementService.getDisabledExtensions().map(id => id.id));
|
||||
const extensions = (await extensionManagement.getInstalled(ExtensionType.User)).filter(ext => !disabled.has(ext.identifier.id));
|
||||
|
||||
const res = await dialogService.confirm({
|
||||
message: localize('msg.start', "Extension Bisect"),
|
||||
detail: localize('detail.start', "Extension Bisect will use binary search to find an extension that causes a problem. During the process the window reloads repeatedly (~{0} times). Each time you must confirm if you are still seeing problems.", 1 + Math.log2(extensions.length) | 0),
|
||||
primaryButton: localize('msg2', "Start Extension Bisect")
|
||||
});
|
||||
|
||||
if (res.confirmed) {
|
||||
await extensionsBisect.start(extensions);
|
||||
hostService.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'extension.bisect.next',
|
||||
title: localize('title.isBad', "Continue Extension Bisect"),
|
||||
category: localize('help', "Help"),
|
||||
f1: true,
|
||||
precondition: ExtensionBisectUi.ctxIsBisectActive
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor, seeingBad: boolean | undefined): Promise<void> {
|
||||
const dialogService = accessor.get(IDialogService);
|
||||
const hostService = accessor.get(IHostService);
|
||||
const bisectService = accessor.get(IExtensionBisectService);
|
||||
const productService = accessor.get(IProductService);
|
||||
const extensionEnablementService = accessor.get(IGlobalExtensionEnablementService);
|
||||
const issueService = accessor.get(IWorkbenchIssueService);
|
||||
|
||||
if (!bisectService.isActive) {
|
||||
return;
|
||||
}
|
||||
if (seeingBad === undefined) {
|
||||
seeingBad = await this._checkForBad(dialogService);
|
||||
}
|
||||
if (seeingBad === undefined) {
|
||||
await bisectService.reset();
|
||||
hostService.reload();
|
||||
return;
|
||||
}
|
||||
const done = await bisectService.next(seeingBad);
|
||||
if (!done) {
|
||||
hostService.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
if (done.bad) {
|
||||
// DONE but nothing found
|
||||
await dialogService.show(Severity.Info, localize('done.msg', "Extension Bisect"), [], {
|
||||
detail: localize('done.detail2', "Extension Bisect is done but no extension has been identified. This might be a problem with {0}", productService.nameShort)
|
||||
});
|
||||
|
||||
} else {
|
||||
// DONE and identified extension
|
||||
const res = await dialogService.show(Severity.Info, localize('done.msg', "Extension Bisect"),
|
||||
[localize('report', "Report Issue & Continue"), localize('done', "Continue")],
|
||||
// [],
|
||||
{
|
||||
detail: localize('done.detail', "Extension Bisect is done and has identified {0} as the extension causing the problem.", done.id),
|
||||
checkbox: { label: localize('done.disbale', "Keep this extension disabled"), checked: true },
|
||||
cancelId: 1
|
||||
}
|
||||
);
|
||||
if (res.checkboxChecked) {
|
||||
await extensionEnablementService.disableExtension({ id: done.id }, undefined);
|
||||
}
|
||||
if (res.choice === 0) {
|
||||
await issueService.openReporter({ extensionId: done.id });
|
||||
}
|
||||
}
|
||||
await bisectService.reset();
|
||||
hostService.reload();
|
||||
}
|
||||
|
||||
private async _checkForBad(dialogService: IDialogService) {
|
||||
const options = {
|
||||
cancelId: 2,
|
||||
detail: localize('detail.next', "Are you still seeing the problem for which you have started extension bisect?")
|
||||
};
|
||||
const res = await dialogService.show(
|
||||
Severity.Info,
|
||||
localize('msg.next', "Extension Bisect"),
|
||||
[localize('next.good', "Good now"), localize('next.bad', "This is bad"), localize('next.stop', "Stop Bisect")],
|
||||
options
|
||||
);
|
||||
if (res.choice === options.cancelId) {
|
||||
return undefined;
|
||||
}
|
||||
return res.choice === 1;
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'extension.bisect.stop',
|
||||
title: localize('title.stop', "Stop Extension Bisect"),
|
||||
category: localize('help', "Help"),
|
||||
f1: true,
|
||||
precondition: ExtensionBisectUi.ctxIsBisectActive
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const extensionsBisect = accessor.get(IExtensionBisectService);
|
||||
const hostService = accessor.get(IHostService);
|
||||
await extensionsBisect.reset();
|
||||
hostService.reload();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,382 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionIdentifier, IGlobalExtensionEnablementService, ENABLED_EXTENSIONS_STORAGE_PATH, DISABLED_EXTENSIONS_STORAGE_PATH } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { 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 { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IExtension, isAuthenticaionProviderExtension, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { StorageManager } from 'vs/platform/extensionManagement/common/extensionEnablementService';
|
||||
import { webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount';
|
||||
import { IUserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect';
|
||||
|
||||
const SOURCE = 'IWorkbenchExtensionEnablementService';
|
||||
|
||||
export class ExtensionEnablementService extends Disposable implements IWorkbenchExtensionEnablementService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private readonly _onEnablementChanged = new Emitter<readonly IExtension[]>();
|
||||
public readonly onEnablementChanged: Event<readonly IExtension[]> = this._onEnablementChanged.event;
|
||||
|
||||
private readonly storageManger: StorageManager;
|
||||
|
||||
constructor(
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IGlobalExtensionEnablementService protected readonly globalExtensionEnablementService: IGlobalExtensionEnablementService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService,
|
||||
@IUserDataSyncAccountService private readonly userDataSyncAccountService: IUserDataSyncAccountService,
|
||||
@ILifecycleService private readonly lifecycleService: ILifecycleService,
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IHostService hostService: IHostService,
|
||||
@IExtensionBisectService private readonly extensionBisectService: IExtensionBisectService,
|
||||
) {
|
||||
super();
|
||||
this.storageManger = this._register(new StorageManager(storageService));
|
||||
this._register(this.globalExtensionEnablementService.onDidChangeEnablement(({ extensions, source }) => this.onDidChangeExtensions(extensions, source)));
|
||||
this._register(extensionManagementService.onDidUninstallExtension(this._onDidUninstallExtension, this));
|
||||
|
||||
// delay notification for extensions disabled until workbench restored
|
||||
if (this.allUserExtensionsDisabled) {
|
||||
this.lifecycleService.when(LifecyclePhase.Restored).then(() => {
|
||||
this.notificationService.prompt(Severity.Info, localize('extensionsDisabled', "All installed extensions are temporarily disabled."), [{
|
||||
label: localize('Reload', "Reload and Enable Extensions"),
|
||||
run: () => hostService.reload({ disableExtensions: false })
|
||||
}]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private get hasWorkspace(): boolean {
|
||||
return this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY;
|
||||
}
|
||||
|
||||
private get allUserExtensionsDisabled(): boolean {
|
||||
return this.environmentService.disableExtensions === true;
|
||||
}
|
||||
|
||||
getEnablementState(extension: IExtension): EnablementState {
|
||||
if (this.extensionBisectService.isDisabledByBisect(extension)) {
|
||||
return EnablementState.DisabledByEnvironemt;
|
||||
}
|
||||
if (this._isDisabledInEnv(extension)) {
|
||||
return EnablementState.DisabledByEnvironemt;
|
||||
}
|
||||
if (this._isDisabledByExtensionKind(extension)) {
|
||||
return EnablementState.DisabledByExtensionKind;
|
||||
}
|
||||
return this._getEnablementState(extension.identifier);
|
||||
}
|
||||
|
||||
canChangeEnablement(extension: IExtension): boolean {
|
||||
try {
|
||||
this.throwErrorIfCannotChangeEnablement(extension);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
const enablementState = this.getEnablementState(extension);
|
||||
if (enablementState === EnablementState.DisabledByEnvironemt || enablementState === EnablementState.DisabledByExtensionKind) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private throwErrorIfCannotChangeEnablement(extension: IExtension): void {
|
||||
if (isLanguagePackExtension(extension.manifest)) {
|
||||
throw new Error(localize('cannot disable language pack extension', "Cannot change enablement of {0} extension because it contributes language packs.", extension.manifest.displayName || extension.identifier.id));
|
||||
}
|
||||
|
||||
if (this.userDataAutoSyncEnablementService.isEnabled() && this.userDataSyncAccountService.account &&
|
||||
isAuthenticaionProviderExtension(extension.manifest) && extension.manifest.contributes!.authentication!.some(a => a.id === this.userDataSyncAccountService.account!.authenticationProviderId)) {
|
||||
throw new Error(localize('cannot disable auth extension', "Cannot change enablement {0} extension because Settings Sync depends on it.", extension.manifest.displayName || extension.identifier.id));
|
||||
}
|
||||
}
|
||||
|
||||
canChangeWorkspaceEnablement(extension: IExtension): boolean {
|
||||
if (!this.canChangeEnablement(extension)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
this.throwErrorIfCannotChangeWorkspaceEnablement(extension);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private throwErrorIfCannotChangeWorkspaceEnablement(extension: IExtension): void {
|
||||
if (!this.hasWorkspace) {
|
||||
throw new Error(localize('noWorkspace', "No workspace."));
|
||||
}
|
||||
if (isAuthenticaionProviderExtension(extension.manifest)) {
|
||||
throw new Error(localize('cannot disable auth extension in workspace', "Cannot change enablement of {0} extension in workspace because it contributes authentication providers", extension.manifest.displayName || extension.identifier.id));
|
||||
}
|
||||
}
|
||||
|
||||
async setEnablement(extensions: IExtension[], newState: EnablementState): Promise<boolean[]> {
|
||||
|
||||
const workspace = newState === EnablementState.DisabledWorkspace || newState === EnablementState.EnabledWorkspace;
|
||||
for (const extension of extensions) {
|
||||
if (workspace) {
|
||||
this.throwErrorIfCannotChangeWorkspaceEnablement(extension);
|
||||
} else {
|
||||
this.throwErrorIfCannotChangeEnablement(extension);
|
||||
}
|
||||
}
|
||||
|
||||
const result = await Promise.all(extensions.map(e => this._setEnablement(e, newState)));
|
||||
const changedExtensions = extensions.filter((e, index) => result[index]);
|
||||
if (changedExtensions.length) {
|
||||
this._onEnablementChanged.fire(changedExtensions);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _setEnablement(extension: IExtension, newState: EnablementState): Promise<boolean> {
|
||||
|
||||
const currentState = this._getEnablementState(extension.identifier);
|
||||
|
||||
if (currentState === newState) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
switch (newState) {
|
||||
case EnablementState.EnabledGlobally:
|
||||
this._enableExtension(extension.identifier);
|
||||
break;
|
||||
case EnablementState.DisabledGlobally:
|
||||
this._disableExtension(extension.identifier);
|
||||
break;
|
||||
case EnablementState.EnabledWorkspace:
|
||||
this._enableExtensionInWorkspace(extension.identifier);
|
||||
break;
|
||||
case EnablementState.DisabledWorkspace:
|
||||
this._disableExtensionInWorkspace(extension.identifier);
|
||||
break;
|
||||
}
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
isEnabled(extension: IExtension): boolean {
|
||||
const enablementState = this.getEnablementState(extension);
|
||||
return enablementState === EnablementState.EnabledWorkspace || enablementState === EnablementState.EnabledGlobally;
|
||||
}
|
||||
|
||||
isDisabledGlobally(extension: IExtension): boolean {
|
||||
return this._isDisabledGlobally(extension.identifier);
|
||||
}
|
||||
|
||||
private _isDisabledInEnv(extension: IExtension): boolean {
|
||||
if (this.allUserExtensionsDisabled) {
|
||||
return !extension.isBuiltin;
|
||||
}
|
||||
const disabledExtensions = this.environmentService.disableExtensions;
|
||||
if (Array.isArray(disabledExtensions)) {
|
||||
return disabledExtensions.some(id => areSameExtensions({ id }, extension.identifier));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private _isDisabledByExtensionKind(extension: IExtension): boolean {
|
||||
if (this.extensionManagementServerService.remoteExtensionManagementServer || this.extensionManagementServerService.webExtensionManagementServer) {
|
||||
const server = this.extensionManagementServerService.getExtensionManagementServer(extension);
|
||||
for (const extensionKind of getExtensionKind(extension.manifest, this.productService, this.configurationService)) {
|
||||
if (extensionKind === 'ui') {
|
||||
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === server) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (extensionKind === 'workspace') {
|
||||
if (server === this.extensionManagementServerService.remoteExtensionManagementServer) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (extensionKind === 'web') {
|
||||
const enableLocalWebWorker = this.configurationService.getValue<boolean>(webWorkerExtHostConfig);
|
||||
if (enableLocalWebWorker) {
|
||||
// Web extensions are enabled on all configurations
|
||||
return false;
|
||||
}
|
||||
if (this.extensionManagementServerService.localExtensionManagementServer === null) {
|
||||
// Web extensions run only in the web
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private _getEnablementState(identifier: IExtensionIdentifier): EnablementState {
|
||||
if (this.hasWorkspace) {
|
||||
if (this._getWorkspaceEnabledExtensions().filter(e => areSameExtensions(e, identifier))[0]) {
|
||||
return EnablementState.EnabledWorkspace;
|
||||
}
|
||||
|
||||
if (this._getWorkspaceDisabledExtensions().filter(e => areSameExtensions(e, identifier))[0]) {
|
||||
return EnablementState.DisabledWorkspace;
|
||||
}
|
||||
}
|
||||
if (this._isDisabledGlobally(identifier)) {
|
||||
return EnablementState.DisabledGlobally;
|
||||
}
|
||||
return EnablementState.EnabledGlobally;
|
||||
}
|
||||
|
||||
private _isDisabledGlobally(identifier: IExtensionIdentifier): boolean {
|
||||
return this.globalExtensionEnablementService.getDisabledExtensions().some(e => areSameExtensions(e, identifier));
|
||||
}
|
||||
|
||||
private _enableExtension(identifier: IExtensionIdentifier): Promise<boolean> {
|
||||
this._removeFromWorkspaceDisabledExtensions(identifier);
|
||||
this._removeFromWorkspaceEnabledExtensions(identifier);
|
||||
return this.globalExtensionEnablementService.enableExtension(identifier, SOURCE);
|
||||
}
|
||||
|
||||
private _disableExtension(identifier: IExtensionIdentifier): Promise<boolean> {
|
||||
this._removeFromWorkspaceDisabledExtensions(identifier);
|
||||
this._removeFromWorkspaceEnabledExtensions(identifier);
|
||||
return this.globalExtensionEnablementService.disableExtension(identifier, SOURCE);
|
||||
}
|
||||
|
||||
private _enableExtensionInWorkspace(identifier: IExtensionIdentifier): void {
|
||||
this._removeFromWorkspaceDisabledExtensions(identifier);
|
||||
this._addToWorkspaceEnabledExtensions(identifier);
|
||||
}
|
||||
|
||||
private _disableExtensionInWorkspace(identifier: IExtensionIdentifier): void {
|
||||
this._addToWorkspaceDisabledExtensions(identifier);
|
||||
this._removeFromWorkspaceEnabledExtensions(identifier);
|
||||
}
|
||||
|
||||
private _addToWorkspaceDisabledExtensions(identifier: IExtensionIdentifier): Promise<boolean> {
|
||||
if (!this.hasWorkspace) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
let disabledExtensions = this._getWorkspaceDisabledExtensions();
|
||||
if (disabledExtensions.every(e => !areSameExtensions(e, identifier))) {
|
||||
disabledExtensions.push(identifier);
|
||||
this._setDisabledExtensions(disabledExtensions);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
private async _removeFromWorkspaceDisabledExtensions(identifier: IExtensionIdentifier): Promise<boolean> {
|
||||
if (!this.hasWorkspace) {
|
||||
return false;
|
||||
}
|
||||
let disabledExtensions = this._getWorkspaceDisabledExtensions();
|
||||
for (let index = 0; index < disabledExtensions.length; index++) {
|
||||
const disabledExtension = disabledExtensions[index];
|
||||
if (areSameExtensions(disabledExtension, identifier)) {
|
||||
disabledExtensions.splice(index, 1);
|
||||
this._setDisabledExtensions(disabledExtensions);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private _addToWorkspaceEnabledExtensions(identifier: IExtensionIdentifier): boolean {
|
||||
if (!this.hasWorkspace) {
|
||||
return false;
|
||||
}
|
||||
let enabledExtensions = this._getWorkspaceEnabledExtensions();
|
||||
if (enabledExtensions.every(e => !areSameExtensions(e, identifier))) {
|
||||
enabledExtensions.push(identifier);
|
||||
this._setEnabledExtensions(enabledExtensions);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private _removeFromWorkspaceEnabledExtensions(identifier: IExtensionIdentifier): boolean {
|
||||
if (!this.hasWorkspace) {
|
||||
return false;
|
||||
}
|
||||
let enabledExtensions = this._getWorkspaceEnabledExtensions();
|
||||
for (let index = 0; index < enabledExtensions.length; index++) {
|
||||
const disabledExtension = enabledExtensions[index];
|
||||
if (areSameExtensions(disabledExtension, identifier)) {
|
||||
enabledExtensions.splice(index, 1);
|
||||
this._setEnabledExtensions(enabledExtensions);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected _getWorkspaceEnabledExtensions(): IExtensionIdentifier[] {
|
||||
return this._getExtensions(ENABLED_EXTENSIONS_STORAGE_PATH);
|
||||
}
|
||||
|
||||
private _setEnabledExtensions(enabledExtensions: IExtensionIdentifier[]): void {
|
||||
this._setExtensions(ENABLED_EXTENSIONS_STORAGE_PATH, enabledExtensions);
|
||||
}
|
||||
|
||||
protected _getWorkspaceDisabledExtensions(): IExtensionIdentifier[] {
|
||||
return this._getExtensions(DISABLED_EXTENSIONS_STORAGE_PATH);
|
||||
}
|
||||
|
||||
private _setDisabledExtensions(disabledExtensions: IExtensionIdentifier[]): void {
|
||||
this._setExtensions(DISABLED_EXTENSIONS_STORAGE_PATH, disabledExtensions);
|
||||
}
|
||||
|
||||
private _getExtensions(storageId: string): IExtensionIdentifier[] {
|
||||
if (!this.hasWorkspace) {
|
||||
return [];
|
||||
}
|
||||
return this.storageManger.get(storageId, StorageScope.WORKSPACE);
|
||||
}
|
||||
|
||||
private _setExtensions(storageId: string, extensions: IExtensionIdentifier[]): void {
|
||||
this.storageManger.set(storageId, extensions, StorageScope.WORKSPACE);
|
||||
}
|
||||
|
||||
private async onDidChangeExtensions(extensionIdentifiers: ReadonlyArray<IExtensionIdentifier>, source?: string): Promise<void> {
|
||||
if (source !== SOURCE) {
|
||||
const installedExtensions = await this.extensionManagementService.getInstalled();
|
||||
const extensions = installedExtensions.filter(installedExtension => extensionIdentifiers.some(identifier => areSameExtensions(identifier, installedExtension.identifier)));
|
||||
this._onEnablementChanged.fire(extensions);
|
||||
}
|
||||
}
|
||||
|
||||
private _onDidUninstallExtension({ identifier, error }: DidUninstallExtensionEvent): void {
|
||||
if (!error) {
|
||||
this._reset(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
private _reset(extension: IExtensionIdentifier) {
|
||||
this._removeFromWorkspaceDisabledExtensions(extension);
|
||||
this._removeFromWorkspaceEnabledExtensions(extension);
|
||||
this.globalExtensionEnablementService.enableExtension(extension);
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IWorkbenchExtensionEnablementService, ExtensionEnablementService);
|
||||
@@ -0,0 +1,18 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
class ExtensionUrlTrustService implements IExtensionUrlTrustService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
async isExtensionUrlTrusted(): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IExtensionUrlTrustService, ExtensionUrlTrustService);
|
||||
Reference in New Issue
Block a user