@@ -16,16 +16,17 @@ import * as nls from 'vs/nls';
1616import {
1717 ExtensionManagementError , IExtensionGalleryService , IExtensionIdentifier , IExtensionManagementParticipant , IGalleryExtension , ILocalExtension , InstallOperation ,
1818 IExtensionsControlManifest , StatisticType , isTargetPlatformCompatible , TargetPlatformToString , ExtensionManagementErrorCode ,
19- InstallOptions , InstallVSIXOptions , UninstallOptions , Metadata , InstallExtensionEvent , DidUninstallExtensionEvent , InstallExtensionResult , UninstallExtensionEvent , IExtensionManagementService
19+ InstallOptions , InstallVSIXOptions , UninstallOptions , Metadata , InstallExtensionEvent , DidUninstallExtensionEvent , InstallExtensionResult , UninstallExtensionEvent , IExtensionManagementService , InstallExtensionInfo
2020} from 'vs/platform/extensionManagement/common/extensionManagement' ;
21- import { areSameExtensions , ExtensionKey , getGalleryExtensionTelemetryData , getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil' ;
21+ import { areSameExtensions , ExtensionKey , getGalleryExtensionId , getGalleryExtensionTelemetryData , getLocalExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil' ;
2222import { ExtensionType , IExtensionManifest , isApplicationScopedExtension , TargetPlatform } from 'vs/platform/extensions/common/extensions' ;
2323import { ILogService } from 'vs/platform/log/common/log' ;
2424import { IProductService } from 'vs/platform/product/common/productService' ;
2525import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry' ;
2626import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile' ;
2727
2828export type ExtensionVerificationStatus = boolean | string ;
29+ export type InstallableExtension = { readonly manifest : IExtensionManifest ; extension : IGalleryExtension | URI ; options : InstallOptions & InstallVSIXOptions } ;
2930
3031export type InstallExtensionTaskOptions = InstallOptions & InstallVSIXOptions & { readonly profileLocation : URI } ;
3132export interface IInstallExtensionTask {
@@ -93,19 +94,54 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
9394
9495 async installFromGallery ( extension : IGalleryExtension , options : InstallOptions = { } ) : Promise < ILocalExtension > {
9596 try {
96- if ( ! this . galleryService . isEnabled ( ) ) {
97- throw new ExtensionManagementError ( nls . localize ( 'MarketPlaceDisabled' , "Marketplace is not enabled" ) , ExtensionManagementErrorCode . Internal ) ;
97+ const results = await this . installGalleryExtensions ( [ { extension, options } ] ) ;
98+ const result = results . find ( ( { identifier } ) => areSameExtensions ( identifier , extension . identifier ) ) ;
99+ if ( result ?. local ) {
100+ return result ?. local ;
98101 }
99- const compatible = await this . checkAndGetCompatibleVersion ( extension , ! ! options . installGivenVersion , ! ! options . installPreReleaseVersion ) ;
100- return await this . installExtension ( compatible . manifest , compatible . extension , options ) ;
102+ if ( result ?. error ) {
103+ throw result . error ;
104+ }
105+ throw toExtensionManagementError ( new Error ( `Unknown error while installing extension ${ extension . identifier . id } ` ) ) ;
101106 } catch ( error ) {
102- reportTelemetry ( this . telemetryService , 'extensionGallery:install' , { extensionData : getGalleryExtensionTelemetryData ( extension ) , error } ) ;
103- this . logService . error ( `Failed to install extension.` , extension . identifier . id ) ;
104- this . logService . error ( error ) ;
105107 throw toExtensionManagementError ( error ) ;
106108 }
107109 }
108110
111+ async installGalleryExtensions ( extensions : InstallExtensionInfo [ ] ) : Promise < InstallExtensionResult [ ] > {
112+ if ( ! this . galleryService . isEnabled ( ) ) {
113+ throw new ExtensionManagementError ( nls . localize ( 'MarketPlaceDisabled' , "Marketplace is not enabled" ) , ExtensionManagementErrorCode . Internal ) ;
114+ }
115+
116+ const results : InstallExtensionResult [ ] = [ ] ;
117+ const installableExtensions : InstallableExtension [ ] = [ ] ;
118+
119+ await Promise . allSettled ( extensions . map ( async ( { extension, options } ) => {
120+ try {
121+ const compatible = await this . checkAndGetCompatibleVersion ( extension , ! ! options ?. installGivenVersion , ! ! options ?. installPreReleaseVersion ) ;
122+ installableExtensions . push ( { ...compatible , options } ) ;
123+ } catch ( error ) {
124+ results . push ( { identifier : extension . identifier , operation : InstallOperation . Install , source : extension , error } ) ;
125+ }
126+ } ) ) ;
127+
128+ if ( installableExtensions . length ) {
129+ results . push ( ...await this . installExtensions ( installableExtensions ) ) ;
130+ }
131+
132+ for ( const result of results ) {
133+ if ( result . error ) {
134+ this . logService . error ( `Failed to install extension.` , result . identifier . id ) ;
135+ this . logService . error ( result . error ) ;
136+ if ( result . source && ! URI . isUri ( result . source ) ) {
137+ reportTelemetry ( this . telemetryService , 'extensionGallery:install' , { extensionData : getGalleryExtensionTelemetryData ( result . source ) , error : result . error } ) ;
138+ }
139+ }
140+ }
141+
142+ return results ;
143+ }
144+
109145 async uninstall ( extension : ILocalExtension , options : UninstallOptions = { } ) : Promise < void > {
110146 this . logService . trace ( 'ExtensionManagementService#uninstall' , extension . identifier . id ) ;
111147 return this . uninstallExtension ( extension , options ) ;
@@ -126,7 +162,21 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
126162 this . participants . push ( participant ) ;
127163 }
128164
129- protected async installExtension ( manifest : IExtensionManifest , extension : URI | IGalleryExtension , options : InstallOptions & InstallVSIXOptions ) : Promise < ILocalExtension > {
165+ protected async installExtensions ( extensions : InstallableExtension [ ] ) : Promise < InstallExtensionResult [ ] > {
166+ const results : InstallExtensionResult [ ] = [ ] ;
167+ await Promise . allSettled ( extensions . map ( async e => {
168+ try {
169+ const result = await this . installExtension ( e ) ;
170+ results . push ( ...result ) ;
171+ } catch ( error ) {
172+ results . push ( { identifier : { id : getGalleryExtensionId ( e . manifest . publisher , e . manifest . name ) } , operation : InstallOperation . Install , source : e . extension , error } ) ;
173+ }
174+ } ) ) ;
175+ this . _onDidInstallExtensions . fire ( results ) ;
176+ return results ;
177+ }
178+
179+ private async installExtension ( { manifest, extension, options } : InstallableExtension ) : Promise < InstallExtensionResult [ ] > {
130180
131181 const isApplicationScoped = options . isApplicationScoped || options . isBuiltin || isApplicationScopedExtension ( manifest ) ;
132182 const installExtensionTaskOptions : InstallExtensionTaskOptions = {
@@ -142,7 +192,8 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
142192 const installingExtension = this . installingExtensions . get ( getInstallExtensionTaskKey ( extension ) ) ;
143193 if ( installingExtension ) {
144194 this . logService . info ( 'Extensions is already requested to install' , extension . identifier . id ) ;
145- return installingExtension . task . waitUntilTaskIsFinished ( ) ;
195+ await installingExtension . task . waitUntilTaskIsFinished ( ) ;
196+ return [ ] ;
146197 }
147198 }
148199
@@ -272,8 +323,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
272323 }
273324
274325 installResults . forEach ( ( { identifier } ) => this . logService . info ( `Extension installed successfully:` , identifier . id ) ) ;
275- this . _onDidInstallExtensions . fire ( installResults ) ;
276- return installResults . filter ( ( { identifier } ) => areSameExtensions ( identifier , installExtensionTask . identifier ) ) [ 0 ] . local ;
326+ return installResults ;
277327
278328 } catch ( error ) {
279329
@@ -299,8 +349,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
299349 }
300350 }
301351
302- this . _onDidInstallExtensions . fire ( allInstallExtensionTasks . map ( ( { task } ) => ( { identifier : task . identifier , operation : InstallOperation . Install , source : task . source , context : installExtensionTaskOptions . context , profileLocation : installExtensionTaskOptions . profileLocation } ) ) ) ;
303- throw error ;
352+ return allInstallExtensionTasks . map ( ( { task } ) => ( { identifier : task . identifier , operation : InstallOperation . Install , source : task . source , context : installExtensionTaskOptions . context , profileLocation : installExtensionTaskOptions . profileLocation , error } ) ) ;
304353 } finally {
305354 // Finally, remove all the tasks from the cache
306355 for ( const { task } of allInstallExtensionTasks ) {
@@ -678,7 +727,7 @@ export function joinErrors(errorOrErrors: (Error | string) | (Array<Error | stri
678727 } , new Error ( '' ) ) ;
679728}
680729
681- function toExtensionManagementError ( error : Error ) : ExtensionManagementError {
730+ export function toExtensionManagementError ( error : Error ) : ExtensionManagementError {
682731 if ( error instanceof ExtensionManagementError ) {
683732 return error ;
684733 }
0 commit comments