@@ -11,11 +11,14 @@ import { TextModel } from 'vs/editor/common/model/textModel';
1111import { IUndoRedoService , IUndoRedoElement , IUndoRedoContext } from 'vs/platform/undoRedo/common/undoRedo' ;
1212import { URI } from 'vs/base/common/uri' ;
1313import { IDialogService } from 'vs/platform/dialogs/common/dialogs' ;
14+ import { getComparisonKey as uriGetComparisonKey } from 'vs/base/common/resources' ;
15+ import { Severity } from 'vs/platform/notification/common/notification' ;
1416
1517export class EditStackElement implements IUndoRedoElement {
1618
1719 public readonly label : string ;
1820 private _isOpen : boolean ;
21+ private _isValid : boolean ;
1922 public readonly model : ITextModel ;
2023 private readonly _beforeVersionId : number ;
2124 private readonly _beforeEOL : EndOfLineSequence ;
@@ -32,6 +35,7 @@ export class EditStackElement implements IUndoRedoElement {
3235 constructor ( model : ITextModel , beforeCursorState : Selection [ ] | null ) {
3336 this . label = nls . localize ( 'edit' , "Typing" ) ;
3437 this . _isOpen = true ;
38+ this . _isValid = true ;
3539 this . model = model ;
3640 this . _beforeVersionId = this . model . getAlternativeVersionId ( ) ;
3741 this . _beforeEOL = getModelEOL ( this . model ) ;
@@ -42,6 +46,10 @@ export class EditStackElement implements IUndoRedoElement {
4246 this . _edits = [ ] ;
4347 }
4448
49+ public isValid ( ) : boolean {
50+ return this . _isValid ;
51+ }
52+
4553 public canAppend ( model : ITextModel ) : boolean {
4654 return ( this . _isOpen && this . model === model ) ;
4755 }
@@ -59,19 +67,33 @@ export class EditStackElement implements IUndoRedoElement {
5967 this . _isOpen = false ;
6068 }
6169
62- undo ( ctx : IUndoRedoContext ) : void {
70+ public canUndo ( ) : boolean {
71+ if ( ! this . _isValid ) {
72+ return false ;
73+ }
74+ return ( this . _afterVersionId === this . model . getAlternativeVersionId ( ) ) ;
75+ }
76+
77+ public undo ( ctx : IUndoRedoContext ) : void {
6378 this . _isOpen = false ;
6479 this . _edits . reverse ( ) ;
6580 this . _edits = this . model . _applyUndoRedoEdits ( this . _edits , this . _beforeEOL , true , false , this . _beforeVersionId , this . _beforeCursorState ) ;
6681 }
6782
68- redo ( ctx : IUndoRedoContext ) : void {
83+ public canRedo ( ) : boolean {
84+ if ( ! this . _isValid ) {
85+ return false ;
86+ }
87+ return ( this . _beforeVersionId === this . model . getAlternativeVersionId ( ) ) ;
88+ }
89+
90+ public redo ( ctx : IUndoRedoContext ) : void {
6991 this . _edits . reverse ( ) ;
7092 this . _edits = this . model . _applyUndoRedoEdits ( this . _edits , this . _afterEOL , false , true , this . _afterVersionId , this . _afterCursorState ) ;
7193 }
7294
73- invalidate ( resource : URI ) : void {
74- // nothing to do
95+ public invalidate ( resource : URI ) : void {
96+ this . _isValid = false ;
7597 }
7698}
7799
@@ -82,6 +104,7 @@ export class MultiModelEditStackElement implements IUndoRedoElement {
82104
83105 private readonly _editStackElementsArr : EditStackElement [ ] ;
84106 private readonly _editStackElementsMap : Map < string , EditStackElement > ;
107+ private _isValid : boolean ;
85108
86109 public get resources ( ) : readonly URI [ ] {
87110 return this . _editStackElementsArr . map ( editStackElement => editStackElement . model . uri ) ;
@@ -90,52 +113,101 @@ export class MultiModelEditStackElement implements IUndoRedoElement {
90113 constructor (
91114 label : string ,
92115 editStackElements : EditStackElement [ ] ,
93- @IDialogService dialogService : IDialogService
116+ @IDialogService private readonly _dialogService : IDialogService
94117 ) {
95118 this . label = label ;
96119 this . _isOpen = true ;
97120 this . _editStackElementsArr = editStackElements . slice ( 0 ) ;
98121 this . _editStackElementsMap = new Map < string , EditStackElement > ( ) ;
99122 for ( const editStackElement of this . _editStackElementsArr ) {
100- this . _editStackElementsMap . set ( editStackElement . model . id , editStackElement ) ;
123+ const key = uriGetComparisonKey ( editStackElement . model . uri ) ;
124+ this . _editStackElementsMap . set ( key , editStackElement ) ;
101125 }
126+ this . _isValid = true ;
102127 }
103128
104129 public canAppend ( model : ITextModel ) : boolean {
105130 if ( ! this . _isOpen ) {
106131 return false ;
107132 }
108- if ( this . _editStackElementsMap . has ( model . id ) ) {
109- const editStackElement = this . _editStackElementsMap . get ( model . id ) ! ;
133+ const key = uriGetComparisonKey ( model . uri ) ;
134+ if ( this . _editStackElementsMap . has ( key ) ) {
135+ const editStackElement = this . _editStackElementsMap . get ( key ) ! ;
110136 return editStackElement . canAppend ( model ) ;
111137 }
112138 return false ;
113139 }
114140
115141 public append ( model : ITextModel , operations : IValidEditOperation [ ] , afterEOL : EndOfLineSequence , afterVersionId : number , afterCursorState : Selection [ ] | null ) : void {
116- const editStackElement = this . _editStackElementsMap . get ( model . id ) ! ;
142+ const key = uriGetComparisonKey ( model . uri ) ;
143+ const editStackElement = this . _editStackElementsMap . get ( key ) ! ;
117144 editStackElement . append ( model , operations , afterEOL , afterVersionId , afterCursorState ) ;
118145 }
119146
120147 public close ( ) : void {
121148 this . _isOpen = false ;
122149 }
123150
151+ private _canUndo ( ) : boolean {
152+ if ( ! this . _isValid ) {
153+ return false ;
154+ }
155+ for ( const editStackElement of this . _editStackElementsArr ) {
156+ if ( ! editStackElement . canUndo ( ) ) {
157+ return false ;
158+ }
159+ }
160+ return true ;
161+ }
162+
124163 undo ( ctx : IUndoRedoContext ) : void {
125164 this . _isOpen = false ;
165+
166+ if ( this . _canUndo ( ) ) {
167+ for ( const editStackElement of this . _editStackElementsArr ) {
168+ editStackElement . undo ( ctx ) ;
169+ }
170+ } else {
171+ // cannot apply!
172+ const validStackElements = this . _editStackElementsArr . filter ( stackElement => stackElement . isValid ( ) ) ;
173+ ctx . replaceCurrentElement ( validStackElements ) ;
174+ this . _dialogService . show ( Severity . Info , nls . localize ( 'workspace' , "Could not apply the edit in all the impacted files." ) , [ ] ) ;
175+ }
176+ }
177+
178+ private _canRedo ( ) : boolean {
179+ if ( ! this . _isValid ) {
180+ return false ;
181+ }
126182 for ( const editStackElement of this . _editStackElementsArr ) {
127- editStackElement . undo ( ctx ) ;
183+ if ( ! editStackElement . canRedo ( ) ) {
184+ return false ;
185+ }
128186 }
187+ return true ;
129188 }
130189
131190 redo ( ctx : IUndoRedoContext ) : void {
132- for ( const editStackElement of this . _editStackElementsArr ) {
133- editStackElement . redo ( ctx ) ;
191+ if ( this . _canRedo ( ) ) {
192+ for ( const editStackElement of this . _editStackElementsArr ) {
193+ editStackElement . redo ( ctx ) ;
194+ }
195+ } else {
196+ // cannot apply!
197+ const validStackElements = this . _editStackElementsArr . filter ( stackElement => stackElement . isValid ( ) ) ;
198+ ctx . replaceCurrentElement ( validStackElements ) ;
199+ this . _dialogService . show ( Severity . Info , nls . localize ( 'workspace' , "Could not apply the edit in all the impacted files." ) , [ ] ) ;
134200 }
135201 }
136202
137203 invalidate ( resource : URI ) : void {
138- console . log ( `MULTI INVALIDATE: ${ resource } ` ) ;
204+ const key = uriGetComparisonKey ( resource ) ;
205+ if ( ! this . _editStackElementsMap . has ( key ) ) {
206+ return ;
207+ }
208+ this . _isValid = false ;
209+ const stackElement = this . _editStackElementsMap . get ( key ) ! ;
210+ stackElement . invalidate ( resource ) ;
139211 }
140212}
141213
@@ -148,6 +220,13 @@ function getModelEOL(model: ITextModel): EndOfLineSequence {
148220 }
149221}
150222
223+ function isKnownStackElement ( element : IUndoRedoElement | null ) : element is EditStackElement | MultiModelEditStackElement {
224+ if ( ! element ) {
225+ return false ;
226+ }
227+ return ( ( element instanceof EditStackElement ) || ( element instanceof MultiModelEditStackElement ) ) ;
228+ }
229+
151230export class EditStack {
152231
153232 private readonly _model : TextModel ;
@@ -160,7 +239,7 @@ export class EditStack {
160239
161240 public pushStackElement ( ) : void {
162241 const lastElement = this . _undoRedoService . getLastElement ( this . _model . uri ) ;
163- if ( lastElement && lastElement instanceof EditStackElement ) {
242+ if ( isKnownStackElement ( lastElement ) ) {
164243 lastElement . close ( ) ;
165244 }
166245 }
@@ -169,9 +248,9 @@ export class EditStack {
169248 this . _undoRedoService . removeElements ( this . _model . uri ) ;
170249 }
171250
172- private _getOrCreateEditStackElement ( beforeCursorState : Selection [ ] | null ) : EditStackElement {
251+ private _getOrCreateEditStackElement ( beforeCursorState : Selection [ ] | null ) : EditStackElement | MultiModelEditStackElement {
173252 const lastElement = this . _undoRedoService . getLastElement ( this . _model . uri ) ;
174- if ( lastElement && lastElement instanceof EditStackElement && lastElement . canAppend ( this . _model ) ) {
253+ if ( isKnownStackElement ( lastElement ) && lastElement . canAppend ( this . _model ) ) {
175254 return lastElement ;
176255 }
177256 const newElement = new EditStackElement ( this . _model , beforeCursorState ) ;
0 commit comments