@@ -154,11 +154,13 @@ class ResourceEditStack {
154154 public resource : URI ;
155155 public past : StackElement [ ] ;
156156 public future : StackElement [ ] ;
157+ public locked : boolean ;
157158
158159 constructor ( resource : URI ) {
159160 this . resource = resource ;
160161 this . past = [ ] ;
161162 this . future = [ ] ;
163+ this . locked = false ;
162164 }
163165}
164166
@@ -363,16 +365,52 @@ export class UndoRedoService implements IUndoRedoService {
363365 this . _notificationService . error ( err ) ;
364366 }
365367
366- private _safeInvoke ( element : StackElement , invoke : ( ) => Promise < void > | void ) : Promise < void > | void {
368+ private _acquireLocks ( affectedEditStacks : ResourceEditStack [ ] ) : ( ) => void {
369+ // first, check if all locks can be acquired
370+ for ( const editStack of affectedEditStacks ) {
371+ if ( editStack . locked ) {
372+ throw new Error ( 'Cannot acquire edit stack lock' ) ;
373+ }
374+ }
375+
376+ // can acquire all locks
377+ for ( const editStack of affectedEditStacks ) {
378+ editStack . locked = true ;
379+ }
380+
381+ return ( ) => {
382+ // release all locks
383+ for ( const editStack of affectedEditStacks ) {
384+ editStack . locked = false ;
385+ }
386+ } ;
387+ }
388+
389+ private _safeInvokeWithLocks ( element : StackElement , invoke : ( ) => Promise < void > | void , affectedEditStacks : ResourceEditStack [ ] ) : Promise < void > | void {
390+ const releaseLocks = this . _acquireLocks ( affectedEditStacks ) ;
391+
367392 let result : Promise < void > | void ;
368393 try {
369394 result = invoke ( ) ;
370395 } catch ( err ) {
396+ releaseLocks ( ) ;
371397 return this . _onError ( err , element ) ;
372398 }
373399
374400 if ( result ) {
375- return result . then ( undefined , ( err ) => this . _onError ( err , element ) ) ;
401+ // result is Promise<void>
402+ return result . then (
403+ ( ) => {
404+ releaseLocks ( ) ;
405+ } ,
406+ ( err ) => {
407+ releaseLocks ( ) ;
408+ return this . _onError ( err , element ) ;
409+ }
410+ ) ;
411+ } else {
412+ // result is void
413+ releaseLocks ( ) ;
376414 }
377415 }
378416
@@ -405,7 +443,6 @@ export class UndoRedoService implements IUndoRedoService {
405443 cannotUndoDueToResources . push ( editStack . resource ) ;
406444 }
407445 }
408-
409446 if ( cannotUndoDueToResources . length > 0 ) {
410447 this . _splitPastWorkspaceElement ( element , null ) ;
411448 const paths = cannotUndoDueToResources . map ( r => r . scheme === Schemas . file ? r . fsPath : r . path ) ;
@@ -414,6 +451,20 @@ export class UndoRedoService implements IUndoRedoService {
414451 return new WorkspaceVerificationError ( this . undo ( resource ) ) ;
415452 }
416453
454+ const cannotLockDueToResources : URI [ ] = [ ] ;
455+ for ( const editStack of affectedEditStacks ) {
456+ if ( editStack . locked ) {
457+ cannotLockDueToResources . push ( editStack . resource ) ;
458+ }
459+ }
460+ if ( cannotLockDueToResources . length > 0 ) {
461+ this . _splitPastWorkspaceElement ( element , null ) ;
462+ const paths = cannotLockDueToResources . map ( r => r . scheme === Schemas . file ? r . fsPath : r . path ) ;
463+ const message = nls . localize ( 'cannotWorkspaceUndoDueToInProgressUndoRedo' , "Could not undo '{0}' across all files because there is already an undo or redo operation running on {1}" , element . label , paths . join ( ', ' ) ) ;
464+ this . _notificationService . info ( message ) ;
465+ return new WorkspaceVerificationError ( this . undo ( resource ) ) ;
466+ }
467+
417468 return null ;
418469 }
419470
@@ -445,21 +496,25 @@ export class UndoRedoService implements IUndoRedoService {
445496 return ;
446497 }
447498
448- if ( result . choice === 0 ) {
449- // At this point, it is possible that the element has been made invalid in the meantime (due to the confirmation await)
450- const verificationError = this . _checkWorkspaceUndo ( resource , element , affectedEditStacks ) ;
451- if ( verificationError ) {
452- return verificationError . returnValue ;
453- }
454- for ( const editStack of affectedEditStacks ) {
455- editStack . past . pop ( ) ;
456- editStack . future . push ( element ) ;
457- }
458- return this . _safeInvoke ( element , ( ) => element . actual . undo ( ) ) ;
459- } else {
499+ if ( result . choice === 1 ) {
500+ // undo this file
460501 this . _splitPastWorkspaceElement ( element , null ) ;
461502 return this . undo ( resource ) ;
462503 }
504+
505+ // undo in all files
506+ // At this point, it is possible that the element has been made invalid in the meantime (due to the confirmation await)
507+ const verificationError = this . _checkWorkspaceUndo ( resource , element , affectedEditStacks ) ;
508+ if ( verificationError ) {
509+ return verificationError . returnValue ;
510+ }
511+
512+ for ( const editStack of affectedEditStacks ) {
513+ editStack . past . pop ( ) ;
514+ editStack . future . push ( element ) ;
515+ }
516+
517+ return this . _safeInvokeWithLocks ( element , ( ) => element . actual . undo ( ) , affectedEditStacks ) ;
463518 }
464519
465520 private _resourceUndo ( editStack : ResourceEditStack , element : ResourceStackElement ) : Promise < void > | void {
@@ -469,9 +524,14 @@ export class UndoRedoService implements IUndoRedoService {
469524 editStack . future = [ ] ;
470525 return ;
471526 }
527+ if ( editStack . locked ) {
528+ const message = nls . localize ( 'cannotResourceUndoDueToInProgressUndoRedo' , "Could not undo '{0}' because there is already an undo or redo operation running." , element . label ) ;
529+ this . _notificationService . info ( message ) ;
530+ return ;
531+ }
472532 editStack . past . pop ( ) ;
473533 editStack . future . push ( element ) ;
474- return this . _safeInvoke ( element , ( ) => element . actual . undo ( ) ) ;
534+ return this . _safeInvokeWithLocks ( element , ( ) => element . actual . undo ( ) , [ editStack ] ) ;
475535 }
476536
477537 public undo ( resource : URI ) : Promise < void > | void {
@@ -523,7 +583,6 @@ export class UndoRedoService implements IUndoRedoService {
523583 cannotRedoDueToResources . push ( editStack . resource ) ;
524584 }
525585 }
526-
527586 if ( cannotRedoDueToResources . length > 0 ) {
528587 this . _splitFutureWorkspaceElement ( element , null ) ;
529588 const paths = cannotRedoDueToResources . map ( r => r . scheme === Schemas . file ? r . fsPath : r . path ) ;
@@ -532,6 +591,20 @@ export class UndoRedoService implements IUndoRedoService {
532591 return new WorkspaceVerificationError ( this . redo ( resource ) ) ;
533592 }
534593
594+ const cannotLockDueToResources : URI [ ] = [ ] ;
595+ for ( const editStack of affectedEditStacks ) {
596+ if ( editStack . locked ) {
597+ cannotLockDueToResources . push ( editStack . resource ) ;
598+ }
599+ }
600+ if ( cannotLockDueToResources . length > 0 ) {
601+ this . _splitFutureWorkspaceElement ( element , null ) ;
602+ const paths = cannotLockDueToResources . map ( r => r . scheme === Schemas . file ? r . fsPath : r . path ) ;
603+ const message = nls . localize ( 'cannotWorkspaceRedoDueToInProgressUndoRedo' , "Could not redo '{0}' across all files because there is already an undo or redo operation running on {1}" , element . label , paths . join ( ', ' ) ) ;
604+ this . _notificationService . info ( message ) ;
605+ return new WorkspaceVerificationError ( this . redo ( resource ) ) ;
606+ }
607+
535608 return null ;
536609 }
537610
@@ -546,7 +619,7 @@ export class UndoRedoService implements IUndoRedoService {
546619 editStack . future . pop ( ) ;
547620 editStack . past . push ( element ) ;
548621 }
549- return this . _safeInvoke ( element , ( ) => element . actual . redo ( ) ) ;
622+ return this . _safeInvokeWithLocks ( element , ( ) => element . actual . redo ( ) , affectedEditStacks ) ;
550623 }
551624
552625 private _resourceRedo ( editStack : ResourceEditStack , element : ResourceStackElement ) : Promise < void > | void {
@@ -556,9 +629,14 @@ export class UndoRedoService implements IUndoRedoService {
556629 editStack . future = [ ] ;
557630 return ;
558631 }
632+ if ( editStack . locked ) {
633+ const message = nls . localize ( 'cannotResourceRedoDueToInProgressUndoRedo' , "Could not redo '{0}' because there is already an undo or redo operation running." , element . label ) ;
634+ this . _notificationService . info ( message ) ;
635+ return ;
636+ }
559637 editStack . future . pop ( ) ;
560638 editStack . past . push ( element ) ;
561- return this . _safeInvoke ( element , ( ) => element . actual . redo ( ) ) ;
639+ return this . _safeInvokeWithLocks ( element , ( ) => element . actual . redo ( ) , [ editStack ] ) ;
562640 }
563641
564642 public redo ( resource : URI ) : Promise < void > | void {
0 commit comments