44 *--------------------------------------------------------------------------------------------*/
55
66import * as nls from 'vs/nls' ;
7- import { IUndoRedoService , IWorkspaceUndoRedoElement , UndoRedoElementType , IUndoRedoElement , IPastFutureElements , ResourceEditStackSnapshot , UriComparisonKeyComputer , IResourceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo' ;
7+ import { IUndoRedoService , IWorkspaceUndoRedoElement , UndoRedoElementType , IUndoRedoElement , IPastFutureElements , ResourceEditStackSnapshot , UriComparisonKeyComputer , IResourceUndoRedoElement , UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo' ;
88import { URI } from 'vs/base/common/uri' ;
99import { onUnexpectedError } from 'vs/base/common/errors' ;
1010import { registerSingleton } from 'vs/platform/instantiation/common/extensions' ;
@@ -32,15 +32,17 @@ class ResourceStackElement {
3232 public readonly strResource : string ;
3333 public readonly resourceLabels : string [ ] ;
3434 public readonly strResources : string [ ] ;
35+ public readonly groupId : number ;
3536 public isValid : boolean ;
3637
37- constructor ( actual : IUndoRedoElement , resourceLabel : string , strResource : string ) {
38+ constructor ( actual : IUndoRedoElement , resourceLabel : string , strResource : string , groupId : number ) {
3839 this . actual = actual ;
3940 this . label = actual . label ;
4041 this . resourceLabel = resourceLabel ;
4142 this . strResource = strResource ;
4243 this . resourceLabels = [ this . resourceLabel ] ;
4344 this . strResources = [ this . strResource ] ;
45+ this . groupId = groupId ;
4446 this . isValid = true ;
4547 }
4648
@@ -124,14 +126,16 @@ class WorkspaceStackElement {
124126
125127 public readonly resourceLabels : string [ ] ;
126128 public readonly strResources : string [ ] ;
129+ public readonly groupId : number ;
127130 public removedResources : RemovedResources | null ;
128131 public invalidatedResources : RemovedResources | null ;
129132
130- constructor ( actual : IWorkspaceUndoRedoElement , resourceLabels : string [ ] , strResources : string [ ] ) {
133+ constructor ( actual : IWorkspaceUndoRedoElement , resourceLabels : string [ ] , strResources : string [ ] , groupId : number ) {
131134 this . actual = actual ;
132135 this . label = actual . label ;
133136 this . resourceLabels = resourceLabels ;
134137 this . strResources = strResources ;
138+ this . groupId = groupId ;
135139 this . removedResources = null ;
136140 this . invalidatedResources = null ;
137141 }
@@ -349,6 +353,13 @@ class ResourceEditStack {
349353 return this . _past [ this . _past . length - 1 ] ;
350354 }
351355
356+ public getSecondClosestPastElement ( ) : StackElement | null {
357+ if ( this . _past . length < 2 ) {
358+ return null ;
359+ }
360+ return this . _past [ this . _past . length - 2 ] ;
361+ }
362+
352363 public getClosestFutureElement ( ) : StackElement | null {
353364 if ( this . _future . length === 0 ) {
354365 return null ;
@@ -482,11 +493,11 @@ export class UndoRedoService implements IUndoRedoService {
482493 console . log ( str . join ( '\n' ) ) ;
483494 }
484495
485- public pushElement ( element : IUndoRedoElement ) : void {
496+ public pushElement ( element : IUndoRedoElement , group : UndoRedoGroup = UndoRedoGroup . None ) : void {
486497 if ( element . type === UndoRedoElementType . Resource ) {
487498 const resourceLabel = getResourceLabel ( element . resource ) ;
488499 const strResource = this . getUriComparisonKey ( element . resource ) ;
489- this . _pushElement ( new ResourceStackElement ( element , resourceLabel , strResource ) ) ;
500+ this . _pushElement ( new ResourceStackElement ( element , resourceLabel , strResource , group . id ) ) ;
490501 } else {
491502 const seen = new Set < string > ( ) ;
492503 const resourceLabels : string [ ] = [ ] ;
@@ -504,9 +515,9 @@ export class UndoRedoService implements IUndoRedoService {
504515 }
505516
506517 if ( resourceLabels . length === 1 ) {
507- this . _pushElement ( new ResourceStackElement ( element , resourceLabels [ 0 ] , strResources [ 0 ] ) ) ;
518+ this . _pushElement ( new ResourceStackElement ( element , resourceLabels [ 0 ] , strResources [ 0 ] , group . id ) ) ;
508519 } else {
509- this . _pushElement ( new WorkspaceStackElement ( element , resourceLabels , strResources ) ) ;
520+ this . _pushElement ( new WorkspaceStackElement ( element , resourceLabels , strResources , group . id ) ) ;
510521 }
511522 }
512523 if ( DEBUG ) {
@@ -550,7 +561,7 @@ export class UndoRedoService implements IUndoRedoService {
550561 for ( const _element of individualArr ) {
551562 const resourceLabel = getResourceLabel ( _element . resource ) ;
552563 const strResource = this . getUriComparisonKey ( _element . resource ) ;
553- const element = new ResourceStackElement ( _element , resourceLabel , strResource ) ;
564+ const element = new ResourceStackElement ( _element , resourceLabel , strResource , 0 ) ;
554565 individualMap . set ( element . strResource , element ) ;
555566 }
556567
@@ -569,7 +580,7 @@ export class UndoRedoService implements IUndoRedoService {
569580 for ( const _element of individualArr ) {
570581 const resourceLabel = getResourceLabel ( _element . resource ) ;
571582 const strResource = this . getUriComparisonKey ( _element . resource ) ;
572- const element = new ResourceStackElement ( _element , resourceLabel , strResource ) ;
583+ const element = new ResourceStackElement ( _element , resourceLabel , strResource , 0 ) ;
573584 individualMap . set ( element . strResource , element ) ;
574585 }
575586
@@ -688,7 +699,7 @@ export class UndoRedoService implements IUndoRedoService {
688699 } ;
689700 }
690701
691- private _safeInvokeWithLocks ( element : StackElement , invoke : ( ) => Promise < void > | void , editStackSnapshot : EditStackSnapshot , cleanup : IDisposable = Disposable . None ) : Promise < void > | void {
702+ private _safeInvokeWithLocks ( element : StackElement , invoke : ( ) => Promise < void > | void , editStackSnapshot : EditStackSnapshot , cleanup : IDisposable , continuation : ( ) => Promise < void > | void ) : Promise < void > | void {
692703 const releaseLocks = this . _acquireLocks ( editStackSnapshot ) ;
693704
694705 let result : Promise < void > | void ;
@@ -706,6 +717,7 @@ export class UndoRedoService implements IUndoRedoService {
706717 ( ) => {
707718 releaseLocks ( ) ;
708719 cleanup . dispose ( ) ;
720+ return continuation ( ) ;
709721 } ,
710722 ( err ) => {
711723 releaseLocks ( ) ;
@@ -717,6 +729,7 @@ export class UndoRedoService implements IUndoRedoService {
717729 // result is void
718730 releaseLocks ( ) ;
719731 cleanup . dispose ( ) ;
732+ return continuation ( ) ;
720733 }
721734 }
722735
@@ -864,9 +877,34 @@ export class UndoRedoService implements IUndoRedoService {
864877 return this . _confirmAndExecuteWorkspaceUndo ( strResource , element , affectedEditStacks ) ;
865878 }
866879
880+ private _isPartOfUndoGroup ( element : WorkspaceStackElement ) : boolean {
881+ if ( ! element . groupId ) {
882+ return false ;
883+ }
884+ // check that there is at least another element with the same groupId ready to be undone
885+ for ( const [ , editStack ] of this . _editStacks ) {
886+ const pastElement = editStack . getClosestPastElement ( ) ;
887+ if ( ! pastElement ) {
888+ continue ;
889+ }
890+ if ( pastElement === element ) {
891+ const secondPastElement = editStack . getSecondClosestPastElement ( ) ;
892+ if ( secondPastElement && secondPastElement . groupId === element . groupId ) {
893+ // there is another element with the same group id in the same stack!
894+ return true ;
895+ }
896+ }
897+ if ( pastElement . groupId === element . groupId ) {
898+ // there is another element with the same group id in another stack!
899+ return true ;
900+ }
901+ }
902+ return false ;
903+ }
904+
867905 private async _confirmAndExecuteWorkspaceUndo ( strResource : string , element : WorkspaceStackElement , editStackSnapshot : EditStackSnapshot ) : Promise < void > {
868906
869- if ( element . canSplit ( ) ) {
907+ if ( element . canSplit ( ) && ! this . _isPartOfUndoGroup ( element ) ) {
870908 // this element can be split
871909
872910 const result = await this . _dialogService . show (
@@ -920,7 +958,7 @@ export class UndoRedoService implements IUndoRedoService {
920958 for ( const editStack of editStackSnapshot . editStacks ) {
921959 editStack . moveBackward ( element ) ;
922960 }
923- return this . _safeInvokeWithLocks ( element , ( ) => element . actual . undo ( ) , editStackSnapshot , cleanup ) ;
961+ return this . _safeInvokeWithLocks ( element , ( ) => element . actual . undo ( ) , editStackSnapshot , cleanup , ( ) => this . _continueUndoInGroup ( element . groupId ) ) ;
924962 }
925963
926964 private _resourceUndo ( editStack : ResourceEditStack , element : ResourceStackElement ) : Promise < void > | void {
@@ -939,10 +977,26 @@ export class UndoRedoService implements IUndoRedoService {
939977 }
940978 return this . _invokeResourcePrepare ( element , ( cleanup ) => {
941979 editStack . moveBackward ( element ) ;
942- return this . _safeInvokeWithLocks ( element , ( ) => element . actual . undo ( ) , new EditStackSnapshot ( [ editStack ] ) , cleanup ) ;
980+ return this . _safeInvokeWithLocks ( element , ( ) => element . actual . undo ( ) , new EditStackSnapshot ( [ editStack ] ) , cleanup , ( ) => this . _continueUndoInGroup ( element . groupId ) ) ;
943981 } ) ;
944982 }
945983
984+ private _continueUndoInGroup ( groupId : number ) : Promise < void > | void {
985+ if ( ! groupId ) {
986+ return ;
987+ }
988+ // find another element with the same groupId ready to be undone
989+ for ( const [ strResource , editStack ] of this . _editStacks ) {
990+ const pastElement = editStack . getClosestPastElement ( ) ;
991+ if ( ! pastElement ) {
992+ continue ;
993+ }
994+ if ( pastElement . groupId === groupId ) {
995+ return this . undo ( strResource ) ;
996+ }
997+ }
998+ }
999+
9461000 public undo ( resource : URI | string ) : Promise < void > | void {
9471001 const strResource = typeof resource === 'string' ? resource : this . getUriComparisonKey ( resource ) ;
9481002 if ( ! this . _editStacks . has ( strResource ) ) {
@@ -1097,7 +1151,7 @@ export class UndoRedoService implements IUndoRedoService {
10971151 for ( const editStack of editStackSnapshot . editStacks ) {
10981152 editStack . moveForward ( element ) ;
10991153 }
1100- return this . _safeInvokeWithLocks ( element , ( ) => element . actual . redo ( ) , editStackSnapshot , cleanup ) ;
1154+ return this . _safeInvokeWithLocks ( element , ( ) => element . actual . redo ( ) , editStackSnapshot , cleanup , ( ) => this . _continueRedoInGroup ( element . groupId ) ) ;
11011155 }
11021156
11031157 private _resourceRedo ( editStack : ResourceEditStack , element : ResourceStackElement ) : Promise < void > | void {
@@ -1117,10 +1171,26 @@ export class UndoRedoService implements IUndoRedoService {
11171171
11181172 return this . _invokeResourcePrepare ( element , ( cleanup ) => {
11191173 editStack . moveForward ( element ) ;
1120- return this . _safeInvokeWithLocks ( element , ( ) => element . actual . redo ( ) , new EditStackSnapshot ( [ editStack ] ) , cleanup ) ;
1174+ return this . _safeInvokeWithLocks ( element , ( ) => element . actual . redo ( ) , new EditStackSnapshot ( [ editStack ] ) , cleanup , ( ) => this . _continueRedoInGroup ( element . groupId ) ) ;
11211175 } ) ;
11221176 }
11231177
1178+ private _continueRedoInGroup ( groupId : number ) : Promise < void > | void {
1179+ if ( ! groupId ) {
1180+ return ;
1181+ }
1182+ // find another element with the same groupId ready to be redone
1183+ for ( const [ strResource , editStack ] of this . _editStacks ) {
1184+ const futureElement = editStack . getClosestFutureElement ( ) ;
1185+ if ( ! futureElement ) {
1186+ continue ;
1187+ }
1188+ if ( futureElement . groupId === groupId ) {
1189+ return this . redo ( strResource ) ;
1190+ }
1191+ }
1192+ }
1193+
11241194 public redo ( resource : URI | string ) : Promise < void > | void {
11251195 const strResource = typeof resource === 'string' ? resource : this . getUriComparisonKey ( resource ) ;
11261196 if ( ! this . _editStacks . has ( strResource ) ) {
0 commit comments