@@ -32,7 +32,7 @@ import { TasksResource, TasksResourceTreeItem } from 'vs/workbench/services/user
32
32
import { ExtensionsResource , ExtensionsResourceExportTreeItem , ExtensionsResourceImportTreeItem , ExtensionsResourceTreeItem } from 'vs/workbench/services/userDataProfile/browser/extensionsResource' ;
33
33
import { GlobalStateResource , GlobalStateResourceExportTreeItem , GlobalStateResourceImportTreeItem , GlobalStateResourceTreeItem } from 'vs/workbench/services/userDataProfile/browser/globalStateResource' ;
34
34
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider' ;
35
- import { Button , ButtonWithDropdown } from 'vs/base/browser/ui/button/button' ;
35
+ import { Button } from 'vs/base/browser/ui/button/button' ;
36
36
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet' ;
37
37
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding' ;
38
38
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView' ;
@@ -44,7 +44,7 @@ import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
44
44
import { generateUuid } from 'vs/base/common/uuid' ;
45
45
import { IEditorService } from 'vs/workbench/services/editor/common/editorService' ;
46
46
import { EditorsOrder } from 'vs/workbench/common/editor' ;
47
- import { getErrorMessage } from 'vs/base/common/errors' ;
47
+ import { getErrorMessage , onUnexpectedError } from 'vs/base/common/errors' ;
48
48
import { IProgressService , ProgressLocation } from 'vs/platform/progress/common/progress' ;
49
49
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions' ;
50
50
import { IQuickInputService , QuickPickItem } from 'vs/platform/quickinput/common/quickInput' ;
@@ -68,6 +68,8 @@ import { Barrier } from 'vs/base/common/async';
68
68
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement' ;
69
69
import { ExtensionType } from 'vs/platform/extensions/common/extensions' ;
70
70
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil' ;
71
+ import { MarkdownString } from 'vs/base/common/htmlContent' ;
72
+ import { renderMarkdown } from 'vs/base/browser/markdownRenderer' ;
71
73
72
74
interface IUserDataProfileTemplate {
73
75
readonly name : string ;
@@ -231,7 +233,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
231
233
return this . doExportProfile ( userDataProfilesExportState ) ;
232
234
} ) ) ;
233
235
const closeAction = new BarrierAction ( barrier , new Action ( 'close' , localize ( 'close' , "Close" ) ) ) ;
234
- await this . showProfilePreviewView ( EXPORT_PROFILE_PREVIEW_VIEW , userDataProfilesExportState . profile . name , [ exportAction ] , closeAction , true , userDataProfilesExportState ) ;
236
+ await this . showProfilePreviewView ( EXPORT_PROFILE_PREVIEW_VIEW , userDataProfilesExportState . profile . name , exportAction , closeAction , true , userDataProfilesExportState ) ;
235
237
disposables . add ( this . userDataProfileService . onDidChangeCurrentProfile ( e => barrier . open ( ) ) ) ;
236
238
await barrier . wait ( ) ;
237
239
await this . hideProfilePreviewView ( EXPORT_PROFILE_PREVIEW_VIEW ) ;
@@ -327,19 +329,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
327
329
const userDataProfileImportState = disposables . add ( this . instantiationService . createInstance ( UserDataProfileImportState , profileTemplate ) ) ;
328
330
profileTemplate = await userDataProfileImportState . getProfileTemplateToImport ( ) ;
329
331
330
- let extensions = false ;
331
- if ( profileTemplate . extensions ) {
332
- const result = await this . dialogService . confirm ( {
333
- title : localize ( 'preview profile' , "Preview Profile" ) ,
334
- message : localize ( 'apply extensions' , "Would you like to apply the extensions from the profile you are previewing or go through them manually?" ) ,
335
- type : 'info' ,
336
- primaryButton : localize ( 'apply extensions automatically' , "Apply Extensions" ) ,
337
- secondaryButton : localize ( 'apply extensions manually' , "Apply Extensions (Manually)" ) ,
338
- } ) ;
339
- extensions = result . confirmed ;
340
- }
341
-
342
- const importedProfile = await this . importAndSwitch ( profileTemplate , true , extensions , localize ( 'preview profile' , "Preview Profile" ) ) ;
332
+ const importedProfile = await this . importAndSwitch ( profileTemplate , true , false , localize ( 'preview profile' , "Preview Profile" ) ) ;
343
333
344
334
if ( ! importedProfile ) {
345
335
return ;
@@ -348,55 +338,53 @@ export class UserDataProfileImportExportService extends Disposable implements IU
348
338
const barrier = new Barrier ( ) ;
349
339
const importAction = this . getImportAction ( barrier , userDataProfileImportState ) ;
350
340
const secondaryAction = isWeb
351
- ? new Action ( 'importInDesktop' , localize ( 'import in desktop' , "Import {0} profile in {1}" , importedProfile . name , this . productService . nameLong ) , undefined , true , async ( ) => this . openerService . open ( uri , { openExternal : true } ) )
341
+ ? new Action ( 'importInDesktop' , localize ( 'import in desktop' , "Import Profile in {1}" , importedProfile . name , this . productService . nameLong ) , undefined , true , async ( ) => this . openerService . open ( uri , { openExternal : true } ) )
352
342
: new BarrierAction ( barrier , new Action ( 'close' , localize ( 'close' , "Close" ) ) ) ;
353
343
354
- const view = await this . showProfilePreviewView ( IMPORT_PROFILE_PREVIEW_VIEW , importedProfile . name , [ importAction ] , secondaryAction , false , userDataProfileImportState ) ;
355
- if ( ! extensions ) {
356
- userDataProfileImportState . setDescription ( ProfileResourceType . Extensions , localize ( 'not applied' , "Not Applied" ) ) ;
357
- const that = this ;
358
- const disposable = disposables . add ( registerAction2 ( class extends Action2 {
359
- constructor ( ) {
360
- super ( {
361
- id : 'previewProfile.applyExtensions' ,
362
- title : localize ( 'apply extensions title' , "Apply Extensions" ) ,
363
- icon : Codicon . cloudDownload ,
364
- menu : {
365
- id : MenuId . ViewItemContext ,
366
- group : 'inline' ,
367
- when : ContextKeyExpr . and ( ContextKeyExpr . equals ( 'view' , IMPORT_PROFILE_PREVIEW_VIEW ) , ContextKeyExpr . equals ( 'viewItem' , ProfileResourceType . Extensions ) ) ,
368
- }
369
- } ) ;
370
- }
371
- override async run ( ) : Promise < void > {
372
- return that . progressService . withProgress ( {
373
- location : IMPORT_PROFILE_PREVIEW_VIEW ,
374
- } , async progress => {
375
- disposable . dispose ( ) ;
376
- userDataProfileImportState . setDescription ( ProfileResourceType . Extensions , localize ( 'applying' , "Applying..." ) ) ;
377
- view . refresh ( ) ;
378
- const profileTemplate = await userDataProfileImportState . getProfileTemplateToImport ( ) ;
379
- if ( profileTemplate . extensions ) {
380
- await that . instantiationService . createInstance ( ExtensionsResource ) . apply ( profileTemplate . extensions , importedProfile ) ;
381
- userDataProfileImportState . setDescription ( ProfileResourceType . Extensions , undefined ) ;
382
- await view . refresh ( ) ;
383
- }
384
- } ) ;
385
- }
386
- } ) ) ;
387
- disposables . add ( Event . debounce ( this . extensionManagementService . onDidInstallExtensions , ( ) => undefined , 100 ) ( async ( ) => {
388
- const profileTemplate = await userDataProfileImportState . getProfileTemplateToImport ( ) ;
389
- if ( profileTemplate . extensions ) {
390
- const profileExtensions = await that . instantiationService . createInstance ( ExtensionsResource ) . getProfileExtensions ( profileTemplate . extensions ! ) ;
391
- const installed = await this . extensionManagementService . getInstalled ( ExtensionType . User ) ;
392
- if ( profileExtensions . every ( e => installed . some ( i => areSameExtensions ( e . identifier , i . identifier ) ) ) ) {
393
- disposable . dispose ( ) ;
394
- userDataProfileImportState . setDescription ( ProfileResourceType . Extensions , undefined ) ;
395
- await view . refresh ( ) ;
344
+ const view = await this . showProfilePreviewView ( IMPORT_PROFILE_PREVIEW_VIEW , importedProfile . name , importAction , secondaryAction , false , userDataProfileImportState ) ;
345
+ const message = new MarkdownString ( ) ;
346
+ message . appendMarkdown ( localize ( 'preview profile message' , "By default, extensions aren't installed when previewing a profile on the web. You can still install them manually before importing the profile. " ) ) ;
347
+ message . appendMarkdown ( `[${ localize ( 'learn more' , "Learn more" ) } ](https://aka.ms/vscode-extension-marketplace#_can-i-trust-extensions-from-the-marketplace).` ) ;
348
+ view . setMessage ( message ) ;
349
+
350
+ const that = this ;
351
+ const disposable = disposables . add ( registerAction2 ( class extends Action2 {
352
+ constructor ( ) {
353
+ super ( {
354
+ id : 'previewProfile.installExtensions' ,
355
+ title : localize ( 'install extensions title' , "Install Extensions" ) ,
356
+ icon : Codicon . cloudDownload ,
357
+ menu : {
358
+ id : MenuId . ViewItemContext ,
359
+ group : 'inline' ,
360
+ when : ContextKeyExpr . and ( ContextKeyExpr . equals ( 'view' , IMPORT_PROFILE_PREVIEW_VIEW ) , ContextKeyExpr . equals ( 'viewItem' , ProfileResourceType . Extensions ) ) ,
361
+ }
362
+ } ) ;
363
+ }
364
+ override async run ( ) : Promise < void > {
365
+ return that . progressService . withProgress ( {
366
+ location : IMPORT_PROFILE_PREVIEW_VIEW ,
367
+ } , async progress => {
368
+ disposable . dispose ( ) ;
369
+ view . setMessage ( undefined ) ;
370
+ const profileTemplate = await userDataProfileImportState . getProfileTemplateToImport ( ) ;
371
+ if ( profileTemplate . extensions ) {
372
+ await that . instantiationService . createInstance ( ExtensionsResource ) . apply ( profileTemplate . extensions , importedProfile ) ;
396
373
}
374
+ } ) ;
375
+ }
376
+ } ) ) ;
377
+ disposables . add ( Event . debounce ( this . extensionManagementService . onDidInstallExtensions , ( ) => undefined , 100 ) ( async ( ) => {
378
+ const profileTemplate = await userDataProfileImportState . getProfileTemplateToImport ( ) ;
379
+ if ( profileTemplate . extensions ) {
380
+ const profileExtensions = await that . instantiationService . createInstance ( ExtensionsResource ) . getProfileExtensions ( profileTemplate . extensions ! ) ;
381
+ const installed = await this . extensionManagementService . getInstalled ( ExtensionType . User ) ;
382
+ if ( profileExtensions . every ( e => installed . some ( i => areSameExtensions ( e . identifier , i . identifier ) ) ) ) {
383
+ disposable . dispose ( ) ;
397
384
}
398
- } ) ) ;
399
- }
385
+ }
386
+ } ) ) ;
387
+
400
388
await barrier . wait ( ) ;
401
389
await this . hideProfilePreviewView ( IMPORT_PROFILE_PREVIEW_VIEW ) ;
402
390
} finally {
@@ -414,7 +402,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
414
402
if ( userDataProfileImportState . isEmpty ( ) ) {
415
403
await importAction . run ( ) ;
416
404
} else {
417
- await this . showProfilePreviewView ( IMPORT_PROFILE_PREVIEW_VIEW , profileTemplate . name , [ importAction ] , new BarrierAction ( barrier , new Action ( 'cancel' , localize ( 'cancel' , "Cancel" ) ) ) , false , userDataProfileImportState ) ;
405
+ await this . showProfilePreviewView ( IMPORT_PROFILE_PREVIEW_VIEW , profileTemplate . name , importAction , new BarrierAction ( barrier , new Action ( 'cancel' , localize ( 'cancel' , "Cancel" ) ) ) , false , userDataProfileImportState ) ;
418
406
}
419
407
await barrier . wait ( ) ;
420
408
await this . hideProfilePreviewView ( IMPORT_PROFILE_PREVIEW_VIEW ) ;
@@ -424,7 +412,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
424
412
}
425
413
426
414
private getImportAction ( barrier : Barrier , userDataProfileImportState : UserDataProfileImportState ) : IAction {
427
- const title = localize ( 'import' , "Import {0} profile " , userDataProfileImportState . profile . name ) ;
415
+ const title = localize ( 'import' , "Import Profile " , userDataProfileImportState . profile . name ) ;
428
416
const importAction = new BarrierAction ( barrier , new Action ( 'import' , title , undefined , true , ( ) => {
429
417
const importProfileFn = async ( ) => {
430
418
importAction . enabled = false ;
@@ -599,7 +587,7 @@ export class UserDataProfileImportExportService extends Disposable implements IU
599
587
return nameIndex + 1 ;
600
588
}
601
589
602
- private async showProfilePreviewView ( id : string , name : string , primary : IAction [ ] , secondary : IAction , refreshAction : boolean , userDataProfilesData : UserDataProfileImportExportState ) : Promise < UserDataProfilePreviewViewPane > {
590
+ private async showProfilePreviewView ( id : string , name : string , primary : IAction , secondary : IAction , refreshAction : boolean , userDataProfilesData : UserDataProfileImportExportState ) : Promise < UserDataProfilePreviewViewPane > {
603
591
const viewsRegistry = Registry . as < IViewsRegistry > ( Extensions . ViewsRegistry ) ;
604
592
const treeView = this . instantiationService . createInstance ( TreeView , id , name ) ;
605
593
if ( refreshAction ) {
@@ -705,15 +693,16 @@ class FileUserDataProfileContentHandler implements IUserDataProfileContentHandle
705
693
class UserDataProfilePreviewViewPane extends TreeViewPane {
706
694
707
695
private buttonsContainer ! : HTMLElement ;
708
- private confirmButton ! : Button | ButtonWithDropdown ;
709
- private cancelButton ! : Button ;
696
+ private primaryButton ! : Button ;
697
+ private secondaryButton ! : Button ;
698
+ private messageContainer ! : HTMLElement ;
710
699
private dimension : DOM . Dimension | undefined ;
711
700
private totalTreeItemsCount : number = 0 ;
712
701
713
702
constructor (
714
703
private readonly userDataProfileData : UserDataProfileImportExportState ,
715
- private readonly confirmActions : Action [ ] ,
716
- private readonly cancelAction : Action ,
704
+ private readonly primaryAction : Action ,
705
+ private readonly secondaryAction : Action ,
717
706
private readonly actionRunner : IActionRunner ,
718
707
options : IViewletViewOptions ,
719
708
@IKeybindingService keybindingService : IKeybindingService ,
@@ -732,7 +721,8 @@ class UserDataProfilePreviewViewPane extends TreeViewPane {
732
721
733
722
protected override renderTreeView ( container : HTMLElement ) : void {
734
723
this . treeView . dataProvider = this . userDataProfileData ;
735
- super . renderTreeView ( DOM . append ( container , DOM . $ ( '' ) ) ) ;
724
+ super . renderTreeView ( DOM . append ( container , DOM . $ ( '.profile-view-tree-container' ) ) ) ;
725
+ this . messageContainer = DOM . append ( container , DOM . $ ( '.profile-view-message-container.hide' ) ) ;
736
726
this . createButtons ( container ) ;
737
727
this . _register ( this . treeView . onDidChangeCheckboxState ( items => {
738
728
this . treeView . refresh ( this . userDataProfileData . onDidChangeCheckboxState ( items ) ) ;
@@ -765,42 +755,64 @@ class UserDataProfilePreviewViewPane extends TreeViewPane {
765
755
private createButtons ( container : HTMLElement ) : void {
766
756
this . buttonsContainer = DOM . append ( container , DOM . $ ( '.profile-view-buttons-container' ) ) ;
767
757
768
- this . confirmButton = this . _register ( this . confirmActions . length > 1
769
- ? new ButtonWithDropdown ( this . buttonsContainer , { ...defaultButtonStyles , actions : this . confirmActions . slice ( 1 ) , contextMenuProvider : this . contextMenuService , actionRunner : this . actionRunner , addPrimaryActionToDropdown : false } )
770
- : new Button ( this . buttonsContainer , { ...defaultButtonStyles } ) ) ;
771
- this . confirmButton . element . classList . add ( 'profile-view-button' ) ;
772
- this . confirmButton . label = this . confirmActions [ 0 ] . label ;
773
- this . confirmButton . enabled = this . confirmActions [ 0 ] . enabled ;
774
- this . _register ( this . confirmButton . onDidClick ( ( ) => this . actionRunner . run ( this . confirmActions [ 0 ] ) ) ) ;
775
- this . _register ( this . confirmActions [ 0 ] . onDidChange ( e => {
758
+ this . primaryButton = this . _register ( new Button ( this . buttonsContainer , { ...defaultButtonStyles } ) ) ;
759
+ this . primaryButton . element . classList . add ( 'profile-view-button' ) ;
760
+ this . primaryButton . label = this . primaryAction . label ;
761
+ this . primaryButton . enabled = this . primaryAction . enabled ;
762
+ this . _register ( this . primaryButton . onDidClick ( ( ) => this . actionRunner . run ( this . primaryAction ) ) ) ;
763
+ this . _register ( this . primaryAction . onDidChange ( e => {
776
764
if ( e . enabled !== undefined ) {
777
- this . confirmButton . enabled = e . enabled ;
765
+ this . primaryButton . enabled = e . enabled ;
778
766
}
779
767
} ) ) ;
780
768
781
- this . cancelButton = this . _register ( new Button ( this . buttonsContainer , { secondary : true , ...defaultButtonStyles } ) ) ;
782
- this . cancelButton . label = this . cancelAction . label ;
783
- this . cancelButton . element . classList . add ( 'profile-view-button' ) ;
784
- this . cancelButton . enabled = this . cancelAction . enabled ;
785
- this . _register ( this . cancelButton . onDidClick ( ( ) => this . actionRunner . run ( this . cancelAction ) ) ) ;
786
- this . _register ( this . cancelAction . onDidChange ( e => {
769
+ this . secondaryButton = this . _register ( new Button ( this . buttonsContainer , { secondary : true , ...defaultButtonStyles } ) ) ;
770
+ this . secondaryButton . label = this . secondaryAction . label ;
771
+ this . secondaryButton . element . classList . add ( 'profile-view-button' ) ;
772
+ this . secondaryButton . enabled = this . secondaryAction . enabled ;
773
+ this . _register ( this . secondaryButton . onDidClick ( ( ) => this . actionRunner . run ( this . secondaryAction ) ) ) ;
774
+ this . _register ( this . secondaryAction . onDidChange ( e => {
787
775
if ( e . enabled !== undefined ) {
788
- this . cancelButton . enabled = e . enabled ;
776
+ this . secondaryButton . enabled = e . enabled ;
789
777
}
790
778
} ) ) ;
791
779
}
792
780
793
781
protected override layoutTreeView ( height : number , width : number ) : void {
794
782
this . dimension = new DOM . Dimension ( width , height ) ;
783
+
784
+ let messageContainerHeight = 0 ;
785
+ if ( ! this . messageContainer . classList . contains ( 'hide' ) ) {
786
+ messageContainerHeight = DOM . getClientArea ( this . messageContainer ) . height ;
787
+ }
788
+
795
789
const buttonContainerHeight = 108 ;
796
790
this . buttonsContainer . style . height = `${ buttonContainerHeight } px` ;
797
791
this . buttonsContainer . style . width = `${ width } px` ;
798
792
799
- super . layoutTreeView ( Math . min ( height - buttonContainerHeight , 22 * this . totalTreeItemsCount ) , width ) ;
793
+ super . layoutTreeView ( Math . min ( height - buttonContainerHeight - messageContainerHeight , 22 * this . totalTreeItemsCount ) , width ) ;
800
794
}
801
795
802
796
private updateConfirmButtonEnablement ( ) : void {
803
- this . confirmButton . enabled = this . confirmActions [ 0 ] . enabled && this . userDataProfileData . isEnabled ( ) ;
797
+ this . primaryButton . enabled = this . primaryAction . enabled && this . userDataProfileData . isEnabled ( ) ;
798
+ }
799
+
800
+ private readonly renderDisposables = this . _register ( new DisposableStore ( ) ) ;
801
+ setMessage ( message : MarkdownString | undefined ) : void {
802
+ this . messageContainer . classList . toggle ( 'hide' , ! message ) ;
803
+ DOM . clearNode ( this . messageContainer ) ;
804
+ if ( message ) {
805
+ this . renderDisposables . clear ( ) ;
806
+ const rendered = this . renderDisposables . add ( renderMarkdown ( message , {
807
+ actionHandler : {
808
+ callback : ( content ) => {
809
+ this . openerService . open ( content , { allowCommands : true } ) . catch ( onUnexpectedError ) ;
810
+ } ,
811
+ disposables : this . renderDisposables
812
+ }
813
+ } ) ) ;
814
+ DOM . append ( this . messageContainer , rendered . element ) ;
815
+ }
804
816
}
805
817
806
818
refresh ( ) : Promise < void > {
0 commit comments