@@ -36,7 +36,6 @@ export const DirtyDiffWidgetProps = Symbol('DirtyDiffWidgetProps');
3636export interface DirtyDiffWidgetProps {
3737 readonly editor : MonacoEditor ;
3838 readonly previousRevisionUri : URI ;
39- readonly changes : readonly Change [ ] ;
4039}
4140
4241export const DirtyDiffWidgetFactory = Symbol ( 'DirtyDiffWidgetFactory' ) ;
@@ -48,8 +47,9 @@ export class DirtyDiffWidget implements Disposable {
4847 private readonly onDidCloseEmitter = new Emitter < unknown > ( ) ;
4948 readonly onDidClose : Event < unknown > = this . onDidCloseEmitter . event ;
5049 protected index : number = - 1 ;
51- private peekView ?: DirtyDiffPeekView ;
52- private diffEditorPromise ?: Promise < MonacoDiffEditor > ;
50+ private peekView : DirtyDiffPeekView ;
51+ private diffEditorPromise : Promise < MonacoDiffEditor > ;
52+ protected _changes ?: readonly Change [ ] ;
5353
5454 constructor (
5555 @inject ( DirtyDiffWidgetProps ) protected readonly props : DirtyDiffWidgetProps ,
@@ -66,6 +66,14 @@ export class DirtyDiffWidget implements Disposable {
6666 this . diffEditorPromise = this . peekView . create ( ) ;
6767 }
6868
69+ get changes ( ) : readonly Change [ ] {
70+ return this . _changes ?? [ ] ;
71+ }
72+
73+ set changes ( changes : readonly Change [ ] ) {
74+ this . handleChangedChanges ( changes ) ;
75+ }
76+
6977 get editor ( ) : MonacoEditor {
7078 return this . props . editor ;
7179 }
@@ -78,10 +86,6 @@ export class DirtyDiffWidget implements Disposable {
7886 return this . props . previousRevisionUri ;
7987 }
8088
81- get changes ( ) : readonly Change [ ] {
82- return this . props . changes ;
83- }
84-
8589 get currentChange ( ) : Change | undefined {
8690 return this . changes [ this . index ] ;
8791 }
@@ -90,8 +94,30 @@ export class DirtyDiffWidget implements Disposable {
9094 return this . index ;
9195 }
9296
93- showChange ( index : number ) : void {
94- this . checkCreated ( ) ;
97+ protected handleChangedChanges ( updated : readonly Change [ ] ) : void {
98+ if ( ! updated . length ) {
99+ return this . dispose ( ) ;
100+ }
101+ if ( this . currentChange ) {
102+ const { previousRange : { start, end } } = this . currentChange ;
103+ // Same change or first after it.
104+ const newIndex = updated . findIndex ( candidate => ( candidate . previousRange . start === start && candidate . previousRange . end === end )
105+ || candidate . previousRange . start > start ) ;
106+ if ( newIndex !== - 1 ) {
107+ this . index = newIndex ;
108+ } else {
109+ this . index = Math . min ( this . index , updated . length - 1 ) ;
110+ }
111+ this . showCurrentChange ( ) ;
112+ } else {
113+ this . index = - 1 ;
114+ }
115+ this . _changes = updated ;
116+ this . updateHeading ( ) ;
117+ }
118+
119+ async showChange ( index : number ) : Promise < void > {
120+ await this . checkCreated ( ) ;
95121 if ( index >= 0 && index < this . changes . length ) {
96122 this . index = index ;
97123 this . showCurrentChange ( ) ;
@@ -119,7 +145,7 @@ export class DirtyDiffWidget implements Disposable {
119145 }
120146
121147 async getContentWithSelectedChanges ( predicate : ( change : Change , index : number , changes : readonly Change [ ] ) => boolean ) : Promise < string > {
122- this . checkCreated ( ) ;
148+ await this . checkCreated ( ) ;
123149 const changes = this . changes . filter ( predicate ) ;
124150 const { diffEditor } = await this . diffEditorPromise ! ;
125151 const diffEditorModel = diffEditor . getModel ( ) ! ;
@@ -132,11 +158,11 @@ export class DirtyDiffWidget implements Disposable {
132158 }
133159
134160 protected showCurrentChange ( ) : void {
135- this . peekView ! . setTitle ( this . computePrimaryHeading ( ) , this . computeSecondaryHeading ( ) ) ;
161+ this . updateHeading ( ) ;
136162 const { previousRange, currentRange } = this . changes [ this . index ] ;
137- this . peekView ! . show ( Position . create ( LineRange . getEndPosition ( currentRange ) . line , 0 ) ,
163+ this . peekView . show ( Position . create ( LineRange . getEndPosition ( currentRange ) . line , 0 ) ,
138164 this . computeHeightInLines ( ) ) ;
139- this . diffEditorPromise ! . then ( ( { diffEditor } ) => {
165+ this . diffEditorPromise . then ( ( { diffEditor } ) => {
140166 let startLine = LineRange . getStartPosition ( currentRange ) . line ;
141167 let endLine = LineRange . getEndPosition ( currentRange ) . line ;
142168 if ( LineRange . isEmpty ( currentRange ) ) { // the change is a removal
@@ -151,6 +177,10 @@ export class DirtyDiffWidget implements Disposable {
151177 this . editor . focus ( ) ;
152178 }
153179
180+ protected updateHeading ( ) : void {
181+ this . peekView . setTitle ( this . computePrimaryHeading ( ) , this . computeSecondaryHeading ( ) ) ;
182+ }
183+
154184 protected computePrimaryHeading ( ) : string {
155185 return this . uri . path . base ;
156186 }
@@ -174,10 +204,8 @@ export class DirtyDiffWidget implements Disposable {
174204 return Math . min ( changeHeightInLines + /* padding */ 8 , Math . floor ( editorHeightInLines / 3 ) ) ;
175205 }
176206
177- protected checkCreated ( ) : void {
178- if ( ! this . peekView ) {
179- throw new Error ( 'create() method needs to be called first.' ) ;
180- }
207+ protected async checkCreated ( ) : Promise < MonacoDiffEditor > {
208+ return this . diffEditorPromise ;
181209 }
182210}
183211
@@ -250,7 +278,7 @@ function applyChanges(changes: readonly Change[], original: monaco.editor.ITextM
250278
251279class DirtyDiffPeekView extends MonacoEditorPeekViewWidget {
252280
253- private diffEditorPromise ?: Promise < MonacoDiffEditor > ;
281+ private diffEditor ?: MonacoDiffEditor ;
254282 private height ?: number ;
255283
256284 constructor ( readonly widget : DirtyDiffWidget ) {
@@ -259,12 +287,16 @@ class DirtyDiffPeekView extends MonacoEditorPeekViewWidget {
259287
260288 override async create ( ) : Promise < MonacoDiffEditor > {
261289 try {
290+ this . bodyElement = document . createElement ( 'div' ) ;
291+ this . bodyElement . classList . add ( 'body' ) ;
292+ const diffEditor = await this . widget . editorProvider . createEmbeddedDiffEditor ( this . editor , this . bodyElement , this . widget . previousRevisionUri ) ;
293+ this . diffEditor = diffEditor ;
294+ this . toDispose . push ( diffEditor ) ;
262295 super . create ( ) ;
263- const diffEditor = await this . diffEditorPromise ! ;
264296 return new Promise ( resolve => {
265- // setTimeout is needed here because the non-side-by-side diff editor might still not have created the view zones;
266- // otherwise, the first change shown might not be properly revealed in the diff editor.
267- // see also https://github.com/microsoft/vscode/blob/b30900b56c4b3ca6c65d7ab92032651f4cb23f15/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts#L248
297+ // setTimeout is needed here because the non-side-by-side diff editor might still not have created the view zones;
298+ // otherwise, the first change shown might not be properly revealed in the diff editor.
299+ // see also https://github.com/microsoft/vscode/blob/b30900b56c4b3ca6c65d7ab92032651f4cb23f15/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts#L248
268300 const disposable = diffEditor . diffEditor . onDidUpdateDiff ( ( ) => setTimeout ( ( ) => {
269301 resolve ( diffEditor ) ;
270302 disposable . dispose ( ) ;
@@ -313,8 +345,10 @@ class DirtyDiffPeekView extends MonacoEditorPeekViewWidget {
313345 if ( item instanceof ActionMenuNode ) {
314346 const { command, id, label, icon, when } = item ;
315347 if ( icon && menuCommandExecutor . isVisible ( menuPath , command , this . widget ) && ( ! when || contextKeyService . match ( when ) ) ) {
348+ // Close editor on successful contributed action.
349+ // https://github.com/microsoft/vscode/blob/11b1500e0a2e8b5ba12e98a3905f9d120b8646a0/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts#L356-L361
316350 this . addAction ( id , label , icon , menuCommandExecutor . isEnabled ( menuPath , command , this . widget ) , ( ) => {
317- menuCommandExecutor . executeCommand ( menuPath , command , this . widget ) ;
351+ menuCommandExecutor . executeCommand ( menuPath , command , this . widget ) . then ( ( ) => this . dispose ( ) ) ;
318352 } ) ;
319353 }
320354 }
@@ -329,15 +363,20 @@ class DirtyDiffPeekView extends MonacoEditorPeekViewWidget {
329363 ( ) => this . dispose ( ) ) ;
330364 }
331365
332- protected override fillHead ( container : HTMLElement ) : void {
333- super . fillHead ( container , true ) ;
366+ protected override fillContainer ( container : HTMLElement ) : void {
367+ this . setCssClass ( 'peekview-widget' ) ;
368+
369+ this . headElement = document . createElement ( 'div' ) ;
370+ this . headElement . classList . add ( 'head' ) ;
371+
372+ container . appendChild ( this . headElement ) ;
373+ container . appendChild ( this . bodyElement ! ) ;
374+
375+ this . fillHead ( this . headElement ) ;
334376 }
335377
336- protected override fillBody ( container : HTMLElement ) : void {
337- this . diffEditorPromise = this . widget . editorProvider . createEmbeddedDiffEditor ( this . editor , container , this . widget . previousRevisionUri ) . then ( diffEditor => {
338- this . toDispose . push ( diffEditor ) ;
339- return diffEditor ;
340- } ) ;
378+ protected override fillHead ( container : HTMLElement ) : void {
379+ super . fillHead ( container , true ) ;
341380 }
342381
343382 protected override doLayoutBody ( height : number , width : number ) : void {
@@ -355,7 +394,7 @@ class DirtyDiffPeekView extends MonacoEditorPeekViewWidget {
355394 }
356395
357396 private layout ( height : number , width : number ) : void {
358- this . diffEditorPromise ?. then ( ( { diffEditor } ) => diffEditor . layout ( { height, width } ) ) ;
397+ this . diffEditor ?. diffEditor . layout ( { height, width } ) ;
359398 }
360399
361400 protected override doRevealRange ( range : Range ) : void {
0 commit comments