Skip to content

Commit

Permalink
make workspace extensions feature experimental and insiders only (#20…
Browse files Browse the repository at this point in the history
…8021)

* make workspace extensions feature experimental and insiders only

* fix tests
  • Loading branch information
sandy081 committed Mar 18, 2024
1 parent c202946 commit 987d928
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,12 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
type: 'boolean',
description: localize('extensionsInQuickAccess', "When enabled, extensions can be searched for via Quick Access and report issues from there."),
default: true
}
},
'extensions.experimental.supportWorkspaceExtensions': {
type: 'boolean',
description: localize('extensions.experimental.supportWorkspaceExtensions', "Enables support for workspace specific local extensions."),
default: false
},
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,15 @@ export class WorkspaceRecommendations extends ExtensionRecommendations {
this._register(this.fileService.watch(this.uriIdentityService.extUri.joinPath(folder.uri, WORKSPACE_EXTENSIONS_FOLDER)));
}

this._register(this.fileService.onDidFilesChange(e => {
if (this.contextService.getWorkspace().folders.some(folder =>
e.affects(this.uriIdentityService.extUri.joinPath(folder.uri, WORKSPACE_EXTENSIONS_FOLDER), FileChangeType.ADDED, FileChangeType.DELETED))
) {
this.onDidChangeWorkspaceExtensionsScheduler.schedule();
}
}));
if (this.workbenchExtensionManagementService.isWorkspaceExtensionsSupported()) {
this._register(this.fileService.onDidFilesChange(e => {
if (this.contextService.getWorkspace().folders.some(folder =>
e.affects(this.uriIdentityService.extUri.joinPath(folder.uri, WORKSPACE_EXTENSIONS_FOLDER), FileChangeType.ADDED, FileChangeType.DELETED))
) {
this.onDidChangeWorkspaceExtensionsScheduler.schedule();
}
}));
}
}

private async onDidChangeWorkspaceExtensionsFolders(): Promise<void> {
Expand All @@ -73,6 +75,9 @@ export class WorkspaceRecommendations extends ExtensionRecommendations {
}

private async fetchWorkspaceExtensions(): Promise<URI[]> {
if (!this.workbenchExtensionManagementService.isWorkspaceExtensionsSupported()) {
return [];
}
const workspaceExtensions: URI[] = [];
for (const workspaceFolder of this.contextService.getWorkspace().folders) {
const extensionsLocaiton = this.uriIdentityService.extUri.joinPath(workspaceFolder.uri, WORKSPACE_EXTENSIONS_FOLDER);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestContextService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
import { TestContextService, TestProductService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
import { TestExtensionTipsService, TestSharedProcessService } from 'vs/workbench/test/electron-sandbox/workbenchTestServices';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
Expand Down Expand Up @@ -212,6 +212,7 @@ suite('ExtensionRecommendationsService Test', () => {
instantiationService.stub(ILifecycleService, disposableStore.add(new TestLifecycleService()));
testConfigurationService = new TestConfigurationService();
instantiationService.stub(IConfigurationService, testConfigurationService);
instantiationService.stub(IProductService, TestProductService);
instantiationService.stub(ILogService, NullLogService);
const fileService = new FileService(instantiationService.get(ILogService));
instantiationService.stub(IFileService, disposableStore.add(fileService));
Expand All @@ -230,7 +231,8 @@ suite('ExtensionRecommendationsService Test', () => {
async getInstalled() { return []; },
async canInstall() { return true; },
async getExtensionsControlManifest() { return { malicious: [], deprecated: {}, search: [] }; },
async getTargetPlatform() { return getTargetPlatform(platform, arch); }
async getTargetPlatform() { return getTargetPlatform(platform, arch); },
isWorkspaceExtensionsSupported() { return false; },
});
instantiationService.stub(IExtensionService, {
onDidChangeExtensions: Event.None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export interface IWorkbenchExtensionManagementService extends IProfileAwareExten
onDidChangeProfile: Event<DidChangeProfileForServerEvent>;
onDidEnableExtensions: Event<IExtension[]>;

isWorkspaceExtensionsSupported(): boolean;
getExtensions(locations: URI[]): Promise<IResourceExtension[]>;
getInstalledWorkspaceExtensions(includeInvalid: boolean): Promise<ILocalExtension[]>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench

protected readonly servers: IExtensionManagementServer[] = [];

private readonly workspaceExtensionManagementService: WorkspaceExtensionsManagementService;
private readonly workspaceExtensionManagementService?: WorkspaceExtensionsManagementService;

constructor(
@IExtensionManagementServerService protected readonly extensionManagementServerService: IExtensionManagementServerService,
Expand All @@ -83,8 +83,12 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
) {
super();

this.workspaceExtensionManagementService = this._register(this.instantiationService.createInstance(WorkspaceExtensionsManagementService));
this.onDidEnableExtensions = this.workspaceExtensionManagementService.onDidChangeInvalidExtensions;
if (this.productService.quality !== 'stable' && this.configurationService.getValue('extensions.experimental.supportWorkspaceExtensions') === true) {
this.workspaceExtensionManagementService = this._register(this.instantiationService.createInstance(WorkspaceExtensionsManagementService));
this.onDidEnableExtensions = this.workspaceExtensionManagementService.onDidChangeInvalidExtensions;
} else {
this.onDidEnableExtensions = Event.None;
}

if (this.extensionManagementServerService.localExtensionManagementServer) {
this.servers.push(this.extensionManagementServerService.localExtensionManagementServer);
Expand Down Expand Up @@ -128,6 +132,10 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
}
}

isWorkspaceExtensionsSupported(): boolean {
return !!this.workspaceExtensionManagementService;
}

async getInstalled(type?: ExtensionType, profileLocation?: URI, productVersion?: IProductVersion): Promise<ILocalExtension[]> {
const result: ILocalExtension[] = [];
await Promise.all(this.servers.map(async server => {
Expand Down Expand Up @@ -405,10 +413,13 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
}

async getExtensions(locations: URI[]): Promise<IResourceExtension[]> {
if (!this.workspaceExtensionManagementService) {
return [];
}
const scannedExtensions = await this.extensionsScannerService.scanMultipleExtensions(locations, ExtensionType.User, { includeInvalid: true });
const result: IResourceExtension[] = [];
await Promise.all(scannedExtensions.map(async scannedExtension => {
const workspaceExtension = await this.workspaceExtensionManagementService.toLocalWorkspaceExtension(scannedExtension);
const workspaceExtension = await this.workspaceExtensionManagementService?.toLocalWorkspaceExtension(scannedExtension);
if (workspaceExtension) {
result.push({
identifier: workspaceExtension.identifier,
Expand All @@ -422,15 +433,19 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
return result;
}

getInstalledWorkspaceExtensions(includeInvalid: boolean): Promise<ILocalExtension[]> {
return this.workspaceExtensionManagementService.getInstalled(includeInvalid);
async getInstalledWorkspaceExtensions(includeInvalid: boolean): Promise<ILocalExtension[]> {
return this.workspaceExtensionManagementService?.getInstalled(includeInvalid) ?? [];
}

async installResourceExtension(extension: IResourceExtension, installOptions: InstallOptions): Promise<ILocalExtension> {
if (!installOptions.isWorkspaceScoped) {
return this.installFromLocation(extension.location);
}

if (!this.workspaceExtensionManagementService) {
throw new Error('Workspace Extensions are not supported');
}

this.logService.info(`Installing the extension ${extension.identifier.id} from ${extension.location.toString()} in workspace`);
const server = this.getWorkspaceExtensionsServer();
this._onInstallExtension.fire({
Expand Down Expand Up @@ -478,6 +493,10 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
throw new Error('The extension is not a workspace extension');
}

if (!this.workspaceExtensionManagementService) {
throw new Error('Workspace Extensions are not supported');
}

this.logService.info(`Uninstalling the workspace extension ${extension.identifier.id} from ${extension.location.toString()}`);
const server = this.getWorkspaceExtensionsServer();
this._onUninstallExtension.fire({
Expand All @@ -488,7 +507,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench
});

try {
this.workspaceExtensionManagementService.uninstall(extension);
await this.workspaceExtensionManagementService.uninstall(extension);
this.logService.info(`Successfully uninstalled the workspace extension ${extension.identifier.id} from ${extension.location.toString()}`);
this.telemetryService.publicLog2<{}, {
owner: 'sandy081';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
import { IFileService } from 'vs/platform/files/common/files';
import { FileService } from 'vs/platform/files/common/fileService';
import { IProductService } from 'vs/platform/product/common/productService';

function createStorageService(instantiationService: TestInstantiationService, disposableStore: DisposableStore): IStorageService {
let service = instantiationService.get(IStorageService);
Expand Down Expand Up @@ -132,6 +133,7 @@ suite('ExtensionEnablementService Test', () => {
installed.splice(0, installed.length);
instantiationService = disposableStore.add(new TestInstantiationService());
instantiationService.stub(IFileService, disposableStore.add(new FileService(new NullLogService())));
instantiationService.stub(IProductService, TestProductService);
instantiationService.stub(IConfigurationService, new TestConfigurationService());
instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService({
id: 'local',
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/test/browser/workbenchTestServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2153,6 +2153,7 @@ export class TestWorkbenchExtensionManagementService implements IWorkbenchExtens
getInstalledWorkspaceExtensions(): Promise<ILocalExtension[]> { throw new Error('Method not implemented.'); }
installResourceExtension(): Promise<ILocalExtension> { throw new Error('Method not implemented.'); }
getExtensions(): Promise<IResourceExtension[]> { throw new Error('Method not implemented.'); }
isWorkspaceExtensionsSupported(): boolean { throw new Error('Method not implemented.'); }
}

export class TestUserDataProfileService implements IUserDataProfileService {
Expand Down

0 comments on commit 987d928

Please sign in to comment.