@@ -12,6 +12,7 @@ import { Button } from '../../../../base/browser/ui/button/button.js';
1212import { IHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegate.js' ;
1313import { createInstantHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js' ;
1414import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js' ;
15+ import { ProgressBar } from '../../../../base/browser/ui/progressbar/progressbar.js' ;
1516import { IAction } from '../../../../base/common/actions.js' ;
1617import { Codicon } from '../../../../base/common/codicons.js' ;
1718import { Emitter , Event } from '../../../../base/common/event.js' ;
@@ -61,7 +62,7 @@ import { AccessibilityCommandId } from '../../accessibility/common/accessibility
6162import { getSimpleCodeEditorWidgetOptions , getSimpleEditorOptions , setupSimpleEditorSelectionStyling } from '../../codeEditor/browser/simpleEditorOptions.js' ;
6263import { ChatAgentLocation , IChatAgentService } from '../common/chatAgents.js' ;
6364import { CONTEXT_CHAT_INPUT_CURSOR_AT_TOP , CONTEXT_CHAT_INPUT_HAS_FOCUS , CONTEXT_CHAT_INPUT_HAS_TEXT , CONTEXT_IN_CHAT_INPUT } from '../common/chatContextKeys.js' ;
64- import { IChatEditingSession , ModifiedFileEntryState } from '../common/chatEditingService.js' ;
65+ import { ChatEditingSessionState , IChatEditingSession , ModifiedFileEntryState } from '../common/chatEditingService.js' ;
6566import { IChatRequestVariableEntry } from '../common/chatModel.js' ;
6667import { IChatFollowup } from '../common/chatService.js' ;
6768import { IChatResponseViewModel } from '../common/chatViewModel.js' ;
@@ -199,6 +200,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
199200 readonly inputUri = URI . parse ( `${ ChatInputPart . INPUT_SCHEME } :input-${ ChatInputPart . _counter ++ } ` ) ;
200201
201202 private readonly _chatEditsDisposables = this . _register ( new DisposableStore ( ) ) ;
203+ private _chatEditsProgress : ProgressBar | undefined ;
202204 private _chatEditsListPool : CollapsibleListPool ;
203205 private _chatEditList : IDisposableReference < WorkbenchList < IChatCollapsibleListItem > > | undefined ;
204206 get selectedElements ( ) : URI [ ] {
@@ -839,30 +841,40 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
839841 }
840842
841843 async renderChatEditingSessionState ( chatEditingSession : IChatEditingSession | null ) {
842- dom . clearNode ( this . chatEditingSessionWidgetContainer ) ;
843844 dom . setVisibility ( Boolean ( chatEditingSession ) , this . chatEditingSessionWidgetContainer ) ;
844- this . _chatEditsDisposables . clear ( ) ;
845- this . _chatEditList = undefined ;
845+
846846 if ( ! chatEditingSession ) {
847+ dom . clearNode ( this . chatEditingSessionWidgetContainer ) ;
848+ this . _chatEditsDisposables . clear ( ) ;
849+ this . _chatEditList = undefined ;
850+ this . _chatEditsProgress ?. dispose ( ) ;
851+ this . _chatEditsProgress = undefined ;
847852 return ;
848853 }
849854
850- const innerContainer = dom . append ( this . chatEditingSessionWidgetContainer , $ ( '.chat-editing-session-container.show-file-icons' ) ) ;
855+ if ( this . _chatEditList && chatEditingSession . state . get ( ) === ChatEditingSessionState . Idle ) {
856+ this . _chatEditsProgress ?. stop ( ) ;
857+ return ;
858+ }
851859
852860 // Summary of number of files changed
861+ const innerContainer = this . chatEditingSessionWidgetContainer . querySelector ( '.chat-editing-session-container.show-file-icons' ) as HTMLElement ?? dom . append ( this . chatEditingSessionWidgetContainer , $ ( '.chat-editing-session-container.show-file-icons' ) ) ;
853862 const editedFiles = chatEditingSession . entries . get ( ) ;
854863 const numberOfEditedEntries = editedFiles . length ;
855- const overviewRegion = dom . append ( innerContainer , $ ( '.chat-editing-session-overview' ) ) ;
856- const overviewText = dom . append ( overviewRegion , $ ( 'span' ) ) ;
857- overviewText . textContent = numberOfEditedEntries === 1
858- ? localize ( 'chatEditingSessionOverview.oneFileChanged' , "1 file changed" )
859- : localize ( 'chatEditingSessionOverview' , "{0} files changed" , numberOfEditedEntries ) ;
864+ const overviewRegion = innerContainer . querySelector ( '.chat-editing-session-overview' ) as HTMLElement ?? dom . append ( innerContainer , $ ( '.chat-editing-session-overview' ) ) ;
865+ if ( numberOfEditedEntries !== this . _chatEditList ?. object . length ) {
866+ const overviewText = overviewRegion . querySelector ( 'span' ) ?? dom . append ( overviewRegion , $ ( 'span' ) ) ;
867+ overviewText . textContent = numberOfEditedEntries === 1
868+ ? localize ( 'chatEditingSessionOverview.oneFileChanged' , "1 file changed" )
869+ : localize ( 'chatEditingSessionOverview' , "{0} files changed" , numberOfEditedEntries ) ;
870+ }
860871
861872 // Chat editing session actions
862- const actionsContainer = dom . append ( overviewRegion , $ ( '.chat-editing-session-actions' ) ) ;
873+ let actionsContainer = overviewRegion . querySelector ( '.chat-editing-session-actions' ) as HTMLElement ;
863874 const actions = [ ] ;
864- // Don't show Accept All / Discard All actions if user already selected Accept All / Discard All
865- if ( editedFiles . find ( ( e ) => e . state . get ( ) === ModifiedFileEntryState . Undecided ) ) {
875+ if ( ! actionsContainer && editedFiles . find ( ( e ) => e . state . get ( ) === ModifiedFileEntryState . Undecided ) ) {
876+ // Don't show Accept All / Discard All actions if user already selected Accept All / Discard All
877+ actionsContainer = dom . append ( overviewRegion , $ ( '.chat-editing-session-actions' ) ) ;
866878 actions . push (
867879 {
868880 command : ChatEditingShowChangesAction . ID ,
@@ -880,60 +892,68 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
880892 isSecondary : false
881893 }
882894 ) ;
883- }
884895
885- for ( const action of actions ) {
886- const button = this . _register ( new Button ( actionsContainer , {
887- supportIcons : false ,
888- secondary : action . isSecondary
889- } ) ) ;
890- button . label = action . label ;
891- this . _register ( button . onDidClick ( ( ) => {
892- this . commandService . executeCommand ( action . command ) ;
893- } ) ) ;
894- dom . append ( actionsContainer , button . element ) ;
896+ for ( const action of actions ) {
897+ const button = this . _register ( new Button ( actionsContainer , {
898+ supportIcons : false ,
899+ secondary : action . isSecondary
900+ } ) ) ;
901+ button . label = action . label ;
902+ this . _register ( button . onDidClick ( ( ) => {
903+ this . commandService . executeCommand ( action . command ) ;
904+ } ) ) ;
905+ dom . append ( actionsContainer , button . element ) ;
906+ }
907+
908+ const clearButton = new Button ( actionsContainer , { supportIcons : true } ) ;
909+ this . _chatEditsDisposables . add ( clearButton ) ;
910+ clearButton . icon = Codicon . close ;
911+ const disp = clearButton . onDidClick ( ( e ) => {
912+ disp . dispose ( ) ;
913+ chatEditingSession . dispose ( ) ;
914+ } ) ;
915+ dom . append ( actionsContainer , clearButton . element ) ;
895916 }
896917
897- const clearButton = new Button ( actionsContainer , { supportIcons : true } ) ;
898- this . _chatEditsDisposables . add ( clearButton ) ;
899- clearButton . icon = Codicon . close ;
900- const disp = clearButton . onDidClick ( ( e ) => {
901- disp . dispose ( ) ;
902- chatEditingSession . dispose ( ) ;
903- } ) ;
904- dom . append ( actionsContainer , clearButton . element ) ;
918+ if ( ! this . _chatEditsProgress && chatEditingSession . state . get ( ) === ChatEditingSessionState . StreamingEdits ) {
919+ this . _chatEditsProgress = new ProgressBar ( innerContainer ) ;
920+ this . _chatEditsProgress . infinite ( ) . show ( 500 ) ;
921+ }
905922
906923 // List of edited files
907- if ( ! editedFiles . length ) {
924+ if ( ! editedFiles . length || editedFiles . length === this . _chatEditList ?. object . length ) {
908925 return ;
909926 }
910927 const entries : IChatCollapsibleListItem [ ] = editedFiles . map ( ( entry ) => ( {
911928 reference : entry . modifiedURI ,
912929 kind : 'reference' ,
913930 } ) ) ;
914- const editedFilesList = this . _chatEditsListPool . get ( ) ;
915- this . _chatEditList = editedFilesList ;
916- const list = editedFilesList . object ;
917- this . _chatEditsDisposables . add ( editedFilesList ) ;
918- this . _chatEditsDisposables . add ( list . onDidOpen ( ( e ) => {
919- if ( e . element ?. kind === 'reference' && URI . isUri ( e . element . reference ) ) {
920- const modifiedFileUri = e . element . reference ;
921- const editedFile = editedFiles . find ( ( e ) => e . modifiedURI . toString ( ) === modifiedFileUri . toString ( ) ) ;
922- if ( editedFile ) {
923- void this . editorService . openEditor ( {
924- original : { resource : URI . from ( editedFile . originalURI , true ) } ,
925- modified : { resource : URI . from ( editedFile . modifiedURI , true ) } ,
926- } ) ;
931+ if ( ! this . _chatEditList ) {
932+ this . _chatEditList = this . _chatEditsListPool . get ( ) ;
933+ const list = this . _chatEditList . object ;
934+ this . _chatEditsDisposables . add ( this . _chatEditList ) ;
935+ this . _chatEditsDisposables . add ( list . onDidOpen ( ( e ) => {
936+ if ( e . element ?. kind === 'reference' && URI . isUri ( e . element . reference ) ) {
937+ const modifiedFileUri = e . element . reference ;
938+ const editedFile = editedFiles . find ( ( e ) => e . modifiedURI . toString ( ) === modifiedFileUri . toString ( ) ) ;
939+ if ( editedFile ) {
940+ void this . editorService . openEditor ( {
941+ original : { resource : URI . from ( editedFile . originalURI , true ) } ,
942+ modified : { resource : URI . from ( editedFile . modifiedURI , true ) } ,
943+ } ) ;
944+ }
927945 }
928- }
929- } ) ) ;
946+ } ) ) ;
947+ dom . append ( innerContainer , list . getHTMLElement ( ) ) ;
948+ }
949+
930950 const maxItemsShown = 6 ;
931951 const itemsShown = Math . min ( numberOfEditedEntries , maxItemsShown ) ;
932952 const height = itemsShown * 22 ;
953+ const list = this . _chatEditList . object ;
933954 list . layout ( height ) ;
934955 list . getHTMLElement ( ) . style . height = `${ height } px` ;
935956 list . splice ( 0 , list . length , entries ) ;
936- dom . append ( innerContainer , editedFilesList . object . getHTMLElement ( ) ) ;
937957 }
938958
939959 async renderFollowups ( items : IChatFollowup [ ] | undefined , response : IChatResponseViewModel | undefined ) : Promise < void > {
0 commit comments