@@ -36,7 +36,7 @@ import { alert } from '../../../../base/browser/ui/aria/aria.js';
36
36
import { IListContextMenuEvent } from '../../../../base/browser/ui/list/list.js' ;
37
37
import { CancellationToken , CancellationTokenSource } from '../../../../base/common/cancellation.js' ;
38
38
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' ;
40
40
import { CancelablePromise , createCancelablePromise , ThrottledDelayer } from '../../../../base/common/async.js' ;
41
41
import { IProductService } from '../../../../platform/product/common/productService.js' ;
42
42
import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js' ;
@@ -794,37 +794,58 @@ export class ExtensionsListView extends ViewPane {
794
794
return new PagedModel ( pager ) ;
795
795
}
796
796
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 ) ;
805
818
}
806
819
}
820
+ if ( extesionsToFetch . length ) {
821
+ this . extensionsWorkbenchService . getExtensions ( extesionsToFetch , CancellationToken . None ) . catch ( e => null /*ignore error*/ ) ;
822
+ }
807
823
}
808
824
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 ;
821
833
}
822
- break ;
823
834
}
824
835
}
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 ) ;
825
846
}
826
847
827
- return preferredExtensions . length ? new PreferredExtensionsPagedModel ( preferredExtensions , pager ) : new PagedModel ( pager ) ;
848
+ return preferredExtensions ;
828
849
}
829
850
830
851
private sortExtensions ( extensions : IExtension [ ] , options : IQueryOptions ) : IExtension [ ] {
@@ -1579,7 +1600,7 @@ export function getAriaLabelForExtension(extension: IExtension | null): string {
1579
1600
return `${ extension . displayName } , ${ deprecated ? `${ deprecated } , ` : '' } ${ extension . version } , ${ publisher } , ${ extension . description } ${ rating ? `, ${ rating } ` : '' } ` ;
1580
1601
}
1581
1602
1582
- class PreferredExtensionsPagedModel implements IPagedModel < IExtension > {
1603
+ export class PreferredExtensionsPagedModel implements IPagedModel < IExtension > {
1583
1604
1584
1605
private readonly resolved = new Map < number , IExtension > ( ) ;
1585
1606
private preferredGalleryExtensions = new Set < string > ( ) ;
@@ -1601,16 +1622,17 @@ class PreferredExtensionsPagedModel implements IPagedModel<IExtension> {
1601
1622
}
1602
1623
1603
1624
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 ) ;
1606
1627
}
1607
1628
}
1608
1629
1609
1630
// expected that all preferred gallery extensions will be part of the query results
1610
1631
this . length = ( preferredExtensions . length - this . preferredGalleryExtensions . size ) + this . pager . total ;
1611
1632
1612
1633
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 ( ( ) => ( {
1614
1636
promise : null ,
1615
1637
cts : null ,
1616
1638
promiseIndexes : new Set < number > ( ) ,
@@ -1640,7 +1662,8 @@ class PreferredExtensionsPagedModel implements IPagedModel<IExtension> {
1640
1662
1641
1663
if ( ! page . promise ) {
1642
1664
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 ) )
1644
1667
. catch ( e => { page . promise = null ; throw e ; } )
1645
1668
. finally ( ( ) => page . cts = null ) ;
1646
1669
}
@@ -1666,9 +1689,7 @@ class PreferredExtensionsPagedModel implements IPagedModel<IExtension> {
1666
1689
return this . get ( index ) ;
1667
1690
}
1668
1691
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 {
1672
1693
let adjustIndexOfNextPagesBy = 0 ;
1673
1694
const pageStartIndex = pageIndex * this . pager . pageSize ;
1674
1695
for ( let i = 0 ; i < extensions . length ; i ++ ) {
@@ -1680,7 +1701,11 @@ class PreferredExtensionsPagedModel implements IPagedModel<IExtension> {
1680
1701
this . resolved . set ( this . preferredExtensions . length - this . resolvedGalleryExtensionsFromQuery . length + pageStartIndex + i , e ) ;
1681
1702
}
1682
1703
}
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 ) {
1684
1709
const nextPageStartIndex = ( pageIndex + 1 ) * this . pager . pageSize ;
1685
1710
const indices = [ ...this . resolved . keys ( ) ] . sort ( ) ;
1686
1711
for ( const index of indices ) {
0 commit comments