Skip to content

Commit fc121ef

Browse files
authored
do not resolve first page again (microsoft#234085)
* microsoft#232991 do not resolve first page again * fix tests
1 parent 94b3d8e commit fc121ef

File tree

2 files changed

+153
-133
lines changed

2 files changed

+153
-133
lines changed

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

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import { alert } from '../../../../base/browser/ui/aria/aria.js';
3636
import { IListContextMenuEvent } from '../../../../base/browser/ui/list/list.js';
3737
import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';
3838
import { IAction, Action, Separator, ActionRunner } from '../../../../base/common/actions.js';
39-
import { ExtensionIdentifier, ExtensionIdentifierMap, ExtensionUntrustedWorkspaceSupportType, ExtensionVirtualWorkspaceSupportType, IExtensionDescription, isLanguagePackExtension } from '../../../../platform/extensions/common/extensions.js';
39+
import { ExtensionIdentifier, ExtensionIdentifierMap, ExtensionUntrustedWorkspaceSupportType, ExtensionVirtualWorkspaceSupportType, IExtensionDescription, IExtensionIdentifier, isLanguagePackExtension } from '../../../../platform/extensions/common/extensions.js';
4040
import { CancelablePromise, createCancelablePromise, ThrottledDelayer } from '../../../../base/common/async.js';
4141
import { IProductService } from '../../../../platform/product/common/productService.js';
4242
import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js';
@@ -794,37 +794,58 @@ export class ExtensionsListView extends ViewPane {
794794
return new PagedModel(pager);
795795
}
796796

797-
let preferredResults: string[] = [];
798-
const manifest = await this.extensionManagementService.getExtensionsControlManifest();
799-
const search = manifest.search;
800-
if (Array.isArray(search)) {
801-
for (const s of search) {
802-
if (s.query && s.query.toLowerCase() === text.toLowerCase() && Array.isArray(s.preferredResults)) {
803-
preferredResults = s.preferredResults;
804-
break;
797+
const [pager, preferredExtensions] = await Promise.all([
798+
this.extensionsWorkbenchService.queryGallery(options, token),
799+
this.getPreferredExtensions(options.text.toLowerCase(), token).catch(() => [])
800+
]);
801+
802+
return preferredExtensions.length ? new PreferredExtensionsPagedModel(preferredExtensions, pager) : new PagedModel(pager);
803+
}
804+
805+
private async getPreferredExtensions(searchText: string, token: CancellationToken): Promise<IExtension[]> {
806+
const preferredExtensions = this.extensionsWorkbenchService.local.filter(e => !e.isBuiltin && (e.name.toLowerCase().indexOf(searchText) > -1 || e.displayName.toLowerCase().indexOf(searchText) > -1 || e.description.toLowerCase().indexOf(searchText) > -1));
807+
const preferredExtensionUUIDs = new Set<string>();
808+
809+
if (preferredExtensions.length) {
810+
// Update gallery data for preferred extensions if they are not yet fetched
811+
const extesionsToFetch: IExtensionIdentifier[] = [];
812+
for (const extension of preferredExtensions) {
813+
if (extension.identifier.uuid) {
814+
preferredExtensionUUIDs.add(extension.identifier.uuid);
815+
}
816+
if (!extension.gallery && extension.identifier.uuid) {
817+
extesionsToFetch.push(extension.identifier);
805818
}
806819
}
820+
if (extesionsToFetch.length) {
821+
this.extensionsWorkbenchService.getExtensions(extesionsToFetch, CancellationToken.None).catch(e => null/*ignore error*/);
822+
}
807823
}
808824

809-
const searchText = options.text.toLowerCase();
810-
const preferredExtensions = this.extensionsWorkbenchService.local.filter(e => !e.isBuiltin && (e.name.toLowerCase().indexOf(searchText) > -1 || e.displayName.toLowerCase().indexOf(searchText) > -1 || e.description.toLowerCase().indexOf(searchText) > -1));
811-
const pager = await this.extensionsWorkbenchService.queryGallery(options, token);
812-
813-
let positionToUpdate = 0;
814-
for (const preferredResult of preferredResults) {
815-
for (let j = positionToUpdate; j < pager.firstPage.length; j++) {
816-
if (areSameExtensions(pager.firstPage[j].identifier, { id: preferredResult })) {
817-
if (positionToUpdate !== j) {
818-
const preferredExtension = pager.firstPage.splice(j, 1)[0];
819-
pager.firstPage.splice(positionToUpdate, 0, preferredExtension);
820-
positionToUpdate++;
825+
const preferredResults: string[] = [];
826+
try {
827+
const manifest = await this.extensionManagementService.getExtensionsControlManifest();
828+
if (Array.isArray(manifest.search)) {
829+
for (const s of manifest.search) {
830+
if (s.query && s.query.toLowerCase() === searchText && Array.isArray(s.preferredResults)) {
831+
preferredResults.push(...s.preferredResults);
832+
break;
821833
}
822-
break;
823834
}
824835
}
836+
if (preferredResults.length) {
837+
const result = await this.extensionsWorkbenchService.getExtensions(preferredResults.map(id => ({ id })), token);
838+
for (const extension of result) {
839+
if (extension.identifier.uuid && !preferredExtensionUUIDs.has(extension.identifier.uuid)) {
840+
preferredExtensions.push(extension);
841+
}
842+
}
843+
}
844+
} catch (e) {
845+
this.logService.warn('Failed to get preferred results from the extensions control manifest.', e);
825846
}
826847

827-
return preferredExtensions.length ? new PreferredExtensionsPagedModel(preferredExtensions, pager) : new PagedModel(pager);
848+
return preferredExtensions;
828849
}
829850

830851
private sortExtensions(extensions: IExtension[], options: IQueryOptions): IExtension[] {
@@ -1579,7 +1600,7 @@ export function getAriaLabelForExtension(extension: IExtension | null): string {
15791600
return `${extension.displayName}, ${deprecated ? `${deprecated}, ` : ''}${extension.version}, ${publisher}, ${extension.description} ${rating ? `, ${rating}` : ''}`;
15801601
}
15811602

1582-
class PreferredExtensionsPagedModel implements IPagedModel<IExtension> {
1603+
export class PreferredExtensionsPagedModel implements IPagedModel<IExtension> {
15831604

15841605
private readonly resolved = new Map<number, IExtension>();
15851606
private preferredGalleryExtensions = new Set<string>();
@@ -1601,16 +1622,17 @@ class PreferredExtensionsPagedModel implements IPagedModel<IExtension> {
16011622
}
16021623

16031624
for (const e of preferredExtensions) {
1604-
if (e.gallery?.identifier.uuid) {
1605-
this.preferredGalleryExtensions.add(e.gallery.identifier.uuid);
1625+
if (e.identifier.uuid) {
1626+
this.preferredGalleryExtensions.add(e.identifier.uuid);
16061627
}
16071628
}
16081629

16091630
// expected that all preferred gallery extensions will be part of the query results
16101631
this.length = (preferredExtensions.length - this.preferredGalleryExtensions.size) + this.pager.total;
16111632

16121633
const totalPages = Math.ceil(this.pager.total / this.pager.pageSize);
1613-
this.pages = range(totalPages).map(() => ({
1634+
this.populateResolvedExtensions(0, this.pager.firstPage);
1635+
this.pages = range(totalPages - 1).map(() => ({
16141636
promise: null,
16151637
cts: null,
16161638
promiseIndexes: new Set<number>(),
@@ -1640,7 +1662,8 @@ class PreferredExtensionsPagedModel implements IPagedModel<IExtension> {
16401662

16411663
if (!page.promise) {
16421664
page.cts = new CancellationTokenSource();
1643-
page.promise = this.resolvePage(pageIndex, page.cts.token)
1665+
page.promise = this.pager.getPage(pageIndex, page.cts.token)
1666+
.then(extensions => this.populateResolvedExtensions(pageIndex, extensions))
16441667
.catch(e => { page.promise = null; throw e; })
16451668
.finally(() => page.cts = null);
16461669
}
@@ -1666,9 +1689,7 @@ class PreferredExtensionsPagedModel implements IPagedModel<IExtension> {
16661689
return this.get(index);
16671690
}
16681691

1669-
private async resolvePage(pageIndex: number, cancellationToken: CancellationToken): Promise<void> {
1670-
const extensions = await this.pager.getPage(pageIndex, cancellationToken);
1671-
1692+
private populateResolvedExtensions(pageIndex: number, extensions: IExtension[]): void {
16721693
let adjustIndexOfNextPagesBy = 0;
16731694
const pageStartIndex = pageIndex * this.pager.pageSize;
16741695
for (let i = 0; i < extensions.length; i++) {
@@ -1680,7 +1701,11 @@ class PreferredExtensionsPagedModel implements IPagedModel<IExtension> {
16801701
this.resolved.set(this.preferredExtensions.length - this.resolvedGalleryExtensionsFromQuery.length + pageStartIndex + i, e);
16811702
}
16821703
}
1683-
if (adjustIndexOfNextPagesBy) {
1704+
// If this page has preferred gallery extensions, then adjust the index of the next pages
1705+
// by the number of preferred gallery extensions found in this page. Because these preferred extensions
1706+
// are already in the resolved list and since we did not add them now, we need to adjust the indices of the next pages.
1707+
// Skip first page as the preferred extensions are always in the first page
1708+
if (pageIndex !== 0 && adjustIndexOfNextPagesBy) {
16841709
const nextPageStartIndex = (pageIndex + 1) * this.pager.pageSize;
16851710
const indices = [...this.resolved.keys()].sort();
16861711
for (const index of indices) {

0 commit comments

Comments
 (0)