Skip to content

Commit e252fce

Browse files
authored
1 parent 71b706f commit e252fce

File tree

5 files changed

+73
-16
lines changed

5 files changed

+73
-16
lines changed

src/vs/platform/extensionManagement/common/extensionGalleryService.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,7 +1652,15 @@ export abstract class AbstractExtensionGalleryService implements IExtensionGalle
16521652
return '';
16531653
}
16541654

1655+
async getAllVersions(extensionIdentifier: IExtensionIdentifier): Promise<IGalleryExtensionVersion[]> {
1656+
return this.getVersions(extensionIdentifier);
1657+
}
1658+
16551659
async getAllCompatibleVersions(extensionIdentifier: IExtensionIdentifier, includePreRelease: boolean, targetPlatform: TargetPlatform): Promise<IGalleryExtensionVersion[]> {
1660+
return this.getVersions(extensionIdentifier, { version: includePreRelease ? VersionKind.Latest : VersionKind.Release, targetPlatform });
1661+
}
1662+
1663+
private async getVersions(extensionIdentifier: IExtensionIdentifier, onlyCompatible?: { version: VersionKind; targetPlatform: TargetPlatform }): Promise<IGalleryExtensionVersion[]> {
16561664
const extensionGalleryManifest = await this.extensionGalleryManifestService.getExtensionGalleryManifest();
16571665
if (!extensionGalleryManifest) {
16581666
throw new Error('No extension gallery service configured.');
@@ -1674,11 +1682,11 @@ export abstract class AbstractExtensionGalleryService implements IExtensionGalle
16741682
}
16751683

16761684
const allTargetPlatforms = getAllTargetPlatforms(galleryExtensions[0]);
1677-
if (isNotWebExtensionInWebTargetPlatform(allTargetPlatforms, targetPlatform)) {
1685+
if (onlyCompatible && isNotWebExtensionInWebTargetPlatform(allTargetPlatforms, onlyCompatible.targetPlatform)) {
16781686
return [];
16791687
}
16801688

1681-
const compatibleVersions: IRawGalleryExtensionVersion[] = [];
1689+
const versions: IRawGalleryExtensionVersion[] = [];
16821690
const productVersion = { version: this.productService.version, date: this.productService.date };
16831691
await Promise.all(galleryExtensions[0].versions.map(async (version) => {
16841692
try {
@@ -1694,22 +1702,22 @@ export abstract class AbstractExtensionGalleryService implements IExtensionGalle
16941702
enabledApiProposals: getEnabledApiProposals(version)
16951703
},
16961704
{
1697-
compatible: true,
1705+
compatible: !!onlyCompatible,
16981706
productVersion,
1699-
targetPlatform,
1700-
version: includePreRelease ? VersionKind.Latest : VersionKind.Release
1707+
targetPlatform: onlyCompatible?.targetPlatform ?? CURRENT_TARGET_PLATFORM,
1708+
version: onlyCompatible?.version ?? version.version
17011709
},
17021710
galleryExtensions[0].publisher.displayName,
17031711
allTargetPlatforms))
17041712
) {
1705-
compatibleVersions.push(version);
1713+
versions.push(version);
17061714
}
17071715
} catch (error) { /* Ignore error and skip version */ }
17081716
}));
17091717

17101718
const result: IGalleryExtensionVersion[] = [];
17111719
const seen = new Set<string>();
1712-
for (const version of sortExtensionVersions(compatibleVersions, targetPlatform)) {
1720+
for (const version of sortExtensionVersions(versions, onlyCompatible?.targetPlatform ?? CURRENT_TARGET_PLATFORM)) {
17131721
if (!seen.has(version.version)) {
17141722
seen.add(version.version);
17151723
result.push({ version: version.version, date: version.lastUpdated, isPreReleaseVersion: isPreReleaseVersion(version) });

src/vs/platform/extensionManagement/common/extensionManagement.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ export interface IExtensionGalleryService {
408408
isExtensionCompatible(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform, productVersion?: IProductVersion): Promise<boolean>;
409409
getCompatibleExtension(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform, productVersion?: IProductVersion): Promise<IGalleryExtension | null>;
410410
getAllCompatibleVersions(extensionIdentifier: IExtensionIdentifier, includePreRelease: boolean, targetPlatform: TargetPlatform): Promise<IGalleryExtensionVersion[]>;
411+
getAllVersions(extensionIdentifier: IExtensionIdentifier): Promise<IGalleryExtensionVersion[]>;
411412
download(extension: IGalleryExtension, location: URI, operation: InstallOperation): Promise<void>;
412413
downloadSignatureArchive(extension: IGalleryExtension, location: URI): Promise<void>;
413414
reportStatistic(publisher: string, name: string, version: string, type: StatisticType): Promise<void>;

src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,11 +1655,11 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
16551655
title: localize('download VSIX', "Download VSIX"),
16561656
menu: {
16571657
id: MenuId.ExtensionContext,
1658-
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension')),
1658+
when: ContextKeyExpr.and(ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension')),
16591659
order: this.productService.quality === 'stable' ? 0 : 1
16601660
},
16611661
run: async (accessor: ServicesAccessor, extensionId: string) => {
1662-
accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, false);
1662+
accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, 'release');
16631663
}
16641664
});
16651665

@@ -1668,11 +1668,24 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi
16681668
title: localize('download pre-release', "Download Pre-Release VSIX"),
16691669
menu: {
16701670
id: MenuId.ExtensionContext,
1671-
when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('extensionHasPreReleaseVersion')),
1671+
when: ContextKeyExpr.and(ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('extensionHasPreReleaseVersion')),
16721672
order: this.productService.quality === 'stable' ? 1 : 0
16731673
},
16741674
run: async (accessor: ServicesAccessor, extensionId: string) => {
1675-
accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, true);
1675+
accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, 'prerelease');
1676+
}
1677+
});
1678+
1679+
this.registerExtensionAction({
1680+
id: 'workbench.extensions.action.downloadSpecificVersion',
1681+
title: localize('download specific version', "Download Specific Version VSIX..."),
1682+
menu: {
1683+
id: MenuId.ExtensionContext,
1684+
when: ContextKeyExpr.and(ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension')),
1685+
order: 2
1686+
},
1687+
run: async (accessor: ServicesAccessor, extensionId: string) => {
1688+
accessor.get(IExtensionsWorkbenchService).downloadVSIX(extensionId, 'any');
16761689
}
16771690
});
16781691

src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import { IViewsService } from '../../../services/views/common/viewsService.js';
7171
import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js';
7272
import { IMarkdownString, MarkdownString } from '../../../../base/common/htmlContent.js';
7373
import { ExtensionGalleryResourceType, getExtensionGalleryManifestResourceUri, IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js';
74+
import { fromNow } from '../../../../base/common/date.js';
7475

7576
interface IExtensionStateProvider<T> {
7677
(extension: Extension): T;
@@ -1895,8 +1896,19 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
18951896
return this.extensionManagementService.installGalleryExtensions(toUpdate);
18961897
}
18971898

1898-
async downloadVSIX(extensionId: string, preRelease: boolean): Promise<void> {
1899-
let [galleryExtension] = await this.galleryService.getExtensions([{ id: extensionId, preRelease }], { compatible: true }, CancellationToken.None);
1899+
async downloadVSIX(extensionId: string, versionKind: 'prerelease' | 'release' | 'any'): Promise<void> {
1900+
let version: string | undefined;
1901+
if (versionKind === 'any') {
1902+
version = await this.pickVersionToDownload(extensionId);
1903+
if (!version) {
1904+
return;
1905+
}
1906+
}
1907+
1908+
const extensionInfo = version ? { id: extensionId, version } : { id: extensionId, preRelease: versionKind === 'prerelease' };
1909+
const queryOptions: IExtensionQueryOptions = version ? {} : { compatible: true };
1910+
1911+
let [galleryExtension] = await this.galleryService.getExtensions([extensionInfo], queryOptions, CancellationToken.None);
19001912
if (!galleryExtension) {
19011913
throw new Error(nls.localize('extension not found', "Extension '{0}' not found.", extensionId));
19021914
}
@@ -1911,7 +1923,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
19111923
});
19121924
}
19131925
}
1914-
if (options.length) {
1926+
if (options.length > 1) {
19151927
const message = nls.localize('platform placeholder', "Please select the platform for which you want to download the VSIX");
19161928
const option = await this.quickInputService.pick(options.sort((a, b) => a.label.localeCompare(b.label)), { placeHolder: message });
19171929
if (!option) {
@@ -1921,7 +1933,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
19211933
}
19221934

19231935
if (targetPlatform !== galleryExtension.properties.targetPlatform) {
1924-
[galleryExtension] = await this.galleryService.getExtensions([{ id: extensionId, preRelease }], { compatible: true, targetPlatform }, CancellationToken.None);
1936+
[galleryExtension] = await this.galleryService.getExtensions([extensionInfo], { ...queryOptions, targetPlatform }, CancellationToken.None);
19251937
}
19261938

19271939
const result = await this.fileDialogService.showOpenDialog({
@@ -1944,6 +1956,29 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
19441956
});
19451957
}
19461958

1959+
private async pickVersionToDownload(extensionId: string): Promise<string | undefined> {
1960+
const allVersions = await this.galleryService.getAllVersions({ id: extensionId });
1961+
if (!allVersions.length) {
1962+
await this.dialogService.info(nls.localize('no versions', "This extension has no other versions."));
1963+
return;
1964+
}
1965+
1966+
const picks = allVersions.map((v, i) => {
1967+
return {
1968+
id: v.version,
1969+
label: v.version,
1970+
description: `${fromNow(new Date(Date.parse(v.date)), true)}${v.isPreReleaseVersion ? ` (${nls.localize('pre-release', "pre-release")})` : ''}`,
1971+
ariaLabel: `${v.isPreReleaseVersion ? 'Pre-Release version' : 'Release version'} ${v.version}`,
1972+
};
1973+
});
1974+
const pick = await this.quickInputService.pick(picks,
1975+
{
1976+
placeHolder: nls.localize('selectVersion', "Select Version to Download"),
1977+
matchOnDetail: true
1978+
});
1979+
return pick?.id;
1980+
}
1981+
19471982
private async syncInstalledExtensionsWithGallery(gallery: IGalleryExtension[]): Promise<void> {
19481983
const extensions: Extensions[] = [];
19491984
if (this.localExtensions) {

src/vs/workbench/contrib/extensions/common/extensions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ export interface IExtensionsWorkbenchService {
144144
install(vsix: URI, installOptions?: InstallExtensionOptions, progressLocation?: ProgressLocation | string): Promise<IExtension>;
145145
install(extension: IExtension, installOptions?: InstallExtensionOptions, progressLocation?: ProgressLocation | string): Promise<IExtension>;
146146
installInServer(extension: IExtension, server: IExtensionManagementServer, installOptions?: InstallOptions): Promise<void>;
147-
downloadVSIX(extension: string, prerelease: boolean): Promise<void>;
147+
downloadVSIX(extension: string, versionKind: 'prerelease' | 'release' | 'any'): Promise<void>;
148148
uninstall(extension: IExtension): Promise<void>;
149149
togglePreRelease(extension: IExtension): Promise<void>;
150150
canSetLanguage(extension: IExtension): boolean;

0 commit comments

Comments
 (0)