@@ -19,15 +19,18 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio
19
19
import { IWorkbenchContribution } from 'vs/workbench/common/contributions' ;
20
20
import { RenameProfileAction } from 'vs/workbench/contrib/userDataProfile/browser/userDataProfileActions' ;
21
21
import { ILifecycleService , LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle' ;
22
- import { CURRENT_PROFILE_CONTEXT , HAS_PROFILES_CONTEXT , IS_CURRENT_PROFILE_TRANSIENT_CONTEXT , IUserDataProfileImportExportService , IUserDataProfileManagementService , IUserDataProfileService , ManageProfilesSubMenu , PROFILES_CATEGORY , PROFILES_ENABLEMENT_CONTEXT , PROFILES_TTILE , PROFILE_EXTENSION , PROFILE_FILTER } from 'vs/workbench/services/userDataProfile/common/userDataProfile' ;
23
- import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput' ;
22
+ import { CURRENT_PROFILE_CONTEXT , HAS_PROFILES_CONTEXT , isUserDataProfileTemplate , IS_CURRENT_PROFILE_TRANSIENT_CONTEXT , IUserDataProfileImportExportService , IUserDataProfileManagementService , IUserDataProfileService , IUserDataProfileTemplate , ManageProfilesSubMenu , PROFILES_CATEGORY , PROFILES_ENABLEMENT_CONTEXT , PROFILES_TTILE , PROFILE_EXTENSION , PROFILE_FILTER } from 'vs/workbench/services/userDataProfile/common/userDataProfile' ;
23
+ import { IQuickInputService , IQuickPickItem } from 'vs/platform/quickinput/common/quickInput' ;
24
24
import { INotificationService } from 'vs/platform/notification/common/notification' ;
25
25
import { charCount } from 'vs/base/common/strings' ;
26
26
import { ThemeIcon } from 'vs/platform/theme/common/themeService' ;
27
27
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles' ;
28
- import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs' ;
28
+ import { IDialogService , IFileDialogService } from 'vs/platform/dialogs/common/dialogs' ;
29
29
import { joinPath } from 'vs/base/common/resources' ;
30
30
import { Codicon } from 'vs/base/common/codicons' ;
31
+ import { IFileService } from 'vs/platform/files/common/files' ;
32
+ import { asJson , asText , IRequestService } from 'vs/platform/request/common/request' ;
33
+ import { CancellationToken } from 'vs/base/common/cancellation' ;
31
34
32
35
export class UserDataProfilesWorkbenchContribution extends Disposable implements IWorkbenchContribution {
33
36
@@ -159,6 +162,7 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements
159
162
this . currentprofileActionsDisposable . value . add ( this . registerUpdateCurrentProfileShortNameAction ( ) ) ;
160
163
this . currentprofileActionsDisposable . value . add ( this . registerRenameCurrentProfileAction ( ) ) ;
161
164
this . currentprofileActionsDisposable . value . add ( this . registerExportCurrentProfileAction ( ) ) ;
165
+ this . currentprofileActionsDisposable . value . add ( this . registerImportProfileAction ( ) ) ;
162
166
}
163
167
164
168
private registerUpdateCurrentProfileShortNameAction ( ) : IDisposable {
@@ -299,10 +303,128 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements
299
303
title : {
300
304
value : localize ( 'export settings profile' , "Export Settings Profile ({0})..." , that . userDataProfileService . currentProfile . name ) ,
301
305
original : `Export Settings Profile (${ that . userDataProfileService . currentProfile . name } )...`
302
- }
306
+ } ,
307
+ precondition : PROFILES_ENABLEMENT_CONTEXT ,
303
308
} ,
304
309
} ) ) ;
305
310
return disposables ;
306
311
}
307
312
313
+ private registerImportProfileAction ( ) : IDisposable {
314
+ const disposables = new DisposableStore ( ) ;
315
+ const id = 'workbench.profiles.actions.importProfile' ;
316
+ disposables . add ( registerAction2 ( class ImportProfileAction extends Action2 {
317
+ constructor ( ) {
318
+ super ( {
319
+ id,
320
+ title : {
321
+ value : localize ( 'import profile' , "Import..." ) ,
322
+ original : 'Import...'
323
+ } ,
324
+ category : PROFILES_CATEGORY ,
325
+ f1 : true ,
326
+ menu : [
327
+ {
328
+ id : ManageProfilesSubMenu ,
329
+ group : '4_import_export_profiles' ,
330
+ when : PROFILES_ENABLEMENT_CONTEXT ,
331
+ order : 2
332
+ }
333
+ ]
334
+ } ) ;
335
+ }
336
+
337
+ async run ( accessor : ServicesAccessor ) {
338
+ const fileDialogService = accessor . get ( IFileDialogService ) ;
339
+ const quickInputService = accessor . get ( IQuickInputService ) ;
340
+ const fileService = accessor . get ( IFileService ) ;
341
+ const requestService = accessor . get ( IRequestService ) ;
342
+ const userDataProfileImportExportService = accessor . get ( IUserDataProfileImportExportService ) ;
343
+ const dialogService = accessor . get ( IDialogService ) ;
344
+ const contextKeyService = accessor . get ( IContextKeyService ) ;
345
+ const notificationService = accessor . get ( INotificationService ) ;
346
+
347
+ const isSettingProfilesEnabled = contextKeyService . contextMatchesRules ( PROFILES_ENABLEMENT_CONTEXT ) ;
348
+
349
+ if ( ! isSettingProfilesEnabled ) {
350
+ if ( ! ( await dialogService . confirm ( {
351
+ title : localize ( 'import profile title' , "Import Settings from a Profile" ) ,
352
+ message : localize ( 'confiirmation message' , "This will replace your current settings. Are you sure you want to continue?" ) ,
353
+ } ) ) . confirmed ) {
354
+ return ;
355
+ }
356
+ }
357
+
358
+ const disposables = new DisposableStore ( ) ;
359
+ const quickPick = disposables . add ( quickInputService . createQuickPick ( ) ) ;
360
+ const updateQuickPickItems = ( value ?: string ) => {
361
+ const selectFromFileItem : IQuickPickItem = { label : isSettingProfilesEnabled ? localize ( 'select from file' , "Select Settings Profile template file" ) : localize ( 'import from file' , "Import from profile file" ) } ;
362
+ quickPick . items = value ? [ { label : isSettingProfilesEnabled ? localize ( 'select from url' , "Create from template URL" ) : localize ( 'import from url' , "Import from URL" ) , description : quickPick . value } , selectFromFileItem ] : [ selectFromFileItem ] ;
363
+ } ;
364
+ quickPick . title = isSettingProfilesEnabled ? localize ( 'create from profile template quick pick title' , "Create from Settings Profile Template" ) : localize ( 'import profile quick pick title' , "Import Settings from a Profile" ) ;
365
+ quickPick . placeholder = isSettingProfilesEnabled ? localize ( 'create from profile template placeholder' , "Provide a template URL or Select a template file" ) : localize ( 'import profile placeholder' , "Provide profile URL or select profile file to import" ) ;
366
+ quickPick . ignoreFocusOut = true ;
367
+ disposables . add ( quickPick . onDidChangeValue ( updateQuickPickItems ) ) ;
368
+ updateQuickPickItems ( ) ;
369
+ quickPick . matchOnLabel = false ;
370
+ quickPick . matchOnDescription = false ;
371
+ disposables . add ( quickPick . onDidAccept ( async ( ) => {
372
+ try {
373
+ quickPick . hide ( ) ;
374
+ const profile = quickPick . selectedItems [ 0 ] . description ? await this . getProfileFromURL ( quickPick . value , requestService ) : await this . getProfileFromFileSystem ( fileDialogService , fileService ) ;
375
+ if ( profile ) {
376
+ if ( isSettingProfilesEnabled ) {
377
+ await userDataProfileImportExportService . importProfile ( profile ) ;
378
+ } else {
379
+ await userDataProfileImportExportService . setProfile ( profile ) ;
380
+ }
381
+ }
382
+ } catch ( error ) {
383
+ notificationService . error ( error ) ;
384
+ }
385
+ } ) ) ;
386
+ disposables . add ( quickPick . onDidHide ( ( ) => disposables . dispose ( ) ) ) ;
387
+ quickPick . show ( ) ;
388
+ }
389
+
390
+ private async getProfileFromFileSystem ( fileDialogService : IFileDialogService , fileService : IFileService ) : Promise < IUserDataProfileTemplate | null > {
391
+ const profileLocation = await fileDialogService . showOpenDialog ( {
392
+ canSelectFolders : false ,
393
+ canSelectFiles : true ,
394
+ canSelectMany : false ,
395
+ filters : PROFILE_FILTER ,
396
+ title : localize ( 'import profile dialog' , "Import Profile" ) ,
397
+ } ) ;
398
+ if ( ! profileLocation ) {
399
+ return null ;
400
+ }
401
+ const content = ( await fileService . readFile ( profileLocation [ 0 ] ) ) . value . toString ( ) ;
402
+ const parsed = JSON . parse ( content ) ;
403
+ return isUserDataProfileTemplate ( parsed ) ? parsed : null ;
404
+ }
405
+
406
+ private async getProfileFromURL ( url : string , requestService : IRequestService ) : Promise < IUserDataProfileTemplate | null > {
407
+ const options = { type : 'GET' , url } ;
408
+ const context = await requestService . request ( options , CancellationToken . None ) ;
409
+ if ( context . res . statusCode === 200 ) {
410
+ const result = await asJson ( context ) ;
411
+ return isUserDataProfileTemplate ( result ) ? result : null ;
412
+ } else {
413
+ const message = await asText ( context ) ;
414
+ throw new Error ( `Expected 200, got back ${ context . res . statusCode } instead.\n\n${ message } ` ) ;
415
+ }
416
+ }
417
+ } ) ) ;
418
+ disposables . add ( MenuRegistry . appendMenuItem ( MenuId . MenubarShare , {
419
+ command : {
420
+ id,
421
+ title : {
422
+ value : localize ( 'import settings profile' , "Import Settings Profile..." , ) ,
423
+ original : 'Import Settings Profile...'
424
+ } ,
425
+ precondition : PROFILES_ENABLEMENT_CONTEXT ,
426
+ } ,
427
+ } ) ) ;
428
+ return disposables ;
429
+ }
308
430
}
0 commit comments