55'use strict' ;
66
77import { onUnexpectedError } from 'vs/base/common/errors' ;
8- import { ICursorStateComputer , IIdentifiedSingleEditOperation } from 'vs/editor/common/model' ;
8+ import { ICursorStateComputer , IIdentifiedSingleEditOperation , EndOfLineSequence } from 'vs/editor/common/model' ;
99import { Selection } from 'vs/editor/common/core/selection' ;
1010import { TextModel } from 'vs/editor/common/model/textModel' ;
1111
@@ -14,13 +14,86 @@ interface IEditOperation {
1414}
1515
1616interface IStackElement {
17- beforeVersionId : number ;
18- beforeCursorState : Selection [ ] ;
17+ readonly beforeVersionId : number ;
18+ readonly beforeCursorState : Selection [ ] ;
19+ readonly afterCursorState : Selection [ ] ;
20+ readonly afterVersionId : number ;
1921
20- editOperations : IEditOperation [ ] ;
22+ undo ( model : TextModel ) : void ;
23+ redo ( model : TextModel ) : void ;
24+ }
25+
26+ class EditStackElement implements IStackElement {
27+ public readonly beforeVersionId : number ;
28+ public readonly beforeCursorState : Selection [ ] ;
29+ public afterCursorState : Selection [ ] ;
30+ public afterVersionId : number ;
31+
32+ public editOperations : IEditOperation [ ] ;
33+
34+ constructor ( beforeVersionId : number , beforeCursorState : Selection [ ] ) {
35+ this . beforeVersionId = beforeVersionId ;
36+ this . beforeCursorState = beforeCursorState ;
37+ this . afterCursorState = null ;
38+ this . afterVersionId = - 1 ;
39+ this . editOperations = [ ] ;
40+ }
41+
42+ public undo ( model : TextModel ) : void {
43+ // Apply all operations in reverse order
44+ for ( let i = this . editOperations . length - 1 ; i >= 0 ; i -- ) {
45+ this . editOperations [ i ] = {
46+ operations : model . applyEdits ( this . editOperations [ i ] . operations )
47+ } ;
48+ }
49+ }
50+
51+ public redo ( model : TextModel ) : void {
52+ // Apply all operations
53+ for ( let i = 0 ; i < this . editOperations . length ; i ++ ) {
54+ this . editOperations [ i ] = {
55+ operations : model . applyEdits ( this . editOperations [ i ] . operations )
56+ } ;
57+ }
58+ }
59+ }
60+
61+ function getModelEOL ( model : TextModel ) : EndOfLineSequence {
62+ const eol = model . getEOL ( ) ;
63+ if ( eol === '\n' ) {
64+ return EndOfLineSequence . LF ;
65+ } else {
66+ return EndOfLineSequence . CRLF ;
67+ }
68+ }
69+
70+ class EOLStackElement implements IStackElement {
71+ public readonly beforeVersionId : number ;
72+ public readonly beforeCursorState : Selection [ ] ;
73+ public readonly afterCursorState : Selection [ ] ;
74+ public afterVersionId : number ;
75+
76+ public eol : EndOfLineSequence ;
77+
78+ constructor ( beforeVersionId : number , setEOL : EndOfLineSequence ) {
79+ this . beforeVersionId = beforeVersionId ;
80+ this . beforeCursorState = null ;
81+ this . afterCursorState = null ;
82+ this . afterVersionId = - 1 ;
83+ this . eol = setEOL ;
84+ }
85+
86+ public undo ( model : TextModel ) : void {
87+ let redoEOL = getModelEOL ( model ) ;
88+ model . setEOL ( this . eol ) ;
89+ this . eol = redoEOL ;
90+ }
2191
22- afterCursorState : Selection [ ] ;
23- afterVersionId : number ;
92+ public redo ( model : TextModel ) : void {
93+ let undoEOL = getModelEOL ( model ) ;
94+ model . setEOL ( this . eol ) ;
95+ this . eol = undoEOL ;
96+ }
2497}
2598
2699export interface IUndoRedoResult {
@@ -55,34 +128,60 @@ export class EditStack {
55128 this . future = [ ] ;
56129 }
57130
131+ public pushEOL ( eol : EndOfLineSequence ) : void {
132+ // No support for parallel universes :(
133+ this . future = [ ] ;
134+
135+ if ( this . currentOpenStackElement ) {
136+ this . pushStackElement ( ) ;
137+ }
138+
139+ const prevEOL = getModelEOL ( this . model ) ;
140+ let stackElement = new EOLStackElement ( this . model . getAlternativeVersionId ( ) , prevEOL ) ;
141+
142+ this . model . setEOL ( eol ) ;
143+
144+ stackElement . afterVersionId = this . model . getVersionId ( ) ;
145+ this . currentOpenStackElement = stackElement ;
146+ this . pushStackElement ( ) ;
147+ }
148+
58149 public pushEditOperation ( beforeCursorState : Selection [ ] , editOperations : IIdentifiedSingleEditOperation [ ] , cursorStateComputer : ICursorStateComputer ) : Selection [ ] {
59150 // No support for parallel universes :(
60151 this . future = [ ] ;
61152
153+ let stackElement : EditStackElement = null ;
154+
155+ if ( this . currentOpenStackElement ) {
156+ if ( this . currentOpenStackElement instanceof EditStackElement ) {
157+ stackElement = this . currentOpenStackElement ;
158+ } else {
159+ this . pushStackElement ( ) ;
160+ }
161+ }
162+
62163 if ( ! this . currentOpenStackElement ) {
63- this . currentOpenStackElement = {
64- beforeVersionId : this . model . getAlternativeVersionId ( ) ,
65- beforeCursorState : beforeCursorState ,
66- editOperations : [ ] ,
67- afterCursorState : null ,
68- afterVersionId : - 1
69- } ;
164+ stackElement = new EditStackElement ( this . model . getAlternativeVersionId ( ) , beforeCursorState ) ;
165+ this . currentOpenStackElement = stackElement ;
70166 }
71167
72168 const inverseEditOperation : IEditOperation = {
73169 operations : this . model . applyEdits ( editOperations )
74170 } ;
75171
76- this . currentOpenStackElement . editOperations . push ( inverseEditOperation ) ;
172+ stackElement . editOperations . push ( inverseEditOperation ) ;
173+ stackElement . afterCursorState = EditStack . _computeCursorState ( cursorStateComputer , inverseEditOperation . operations ) ;
174+ stackElement . afterVersionId = this . model . getVersionId ( ) ;
175+ return stackElement . afterCursorState ;
176+ }
177+
178+ private static _computeCursorState ( cursorStateComputer : ICursorStateComputer , inverseEditOperations : IIdentifiedSingleEditOperation [ ] ) : Selection [ ] {
77179 try {
78- this . currentOpenStackElement . afterCursorState = cursorStateComputer ? cursorStateComputer ( inverseEditOperation . operations ) : null ;
180+ return cursorStateComputer ? cursorStateComputer ( inverseEditOperations ) : null ;
79181 } catch ( e ) {
80182 onUnexpectedError ( e ) ;
81- this . currentOpenStackElement . afterCursorState = null ;
183+ return null ;
82184 }
83-
84- this . currentOpenStackElement . afterVersionId = this . model . getVersionId ( ) ;
85- return this . currentOpenStackElement . afterCursorState ;
86185 }
87186
88187 public undo ( ) : IUndoRedoResult {
@@ -93,13 +192,9 @@ export class EditStack {
93192 const pastStackElement = this . past . pop ( ) ;
94193
95194 try {
96- // Apply all operations in reverse order
97- for ( let i = pastStackElement . editOperations . length - 1 ; i >= 0 ; i -- ) {
98- pastStackElement . editOperations [ i ] = {
99- operations : this . model . applyEdits ( pastStackElement . editOperations [ i ] . operations )
100- } ;
101- }
195+ pastStackElement . undo ( this . model ) ;
102196 } catch ( e ) {
197+ onUnexpectedError ( e ) ;
103198 this . clear ( ) ;
104199 return null ;
105200 }
@@ -118,20 +213,12 @@ export class EditStack {
118213 public redo ( ) : IUndoRedoResult {
119214
120215 if ( this . future . length > 0 ) {
121- if ( this . currentOpenStackElement ) {
122- throw new Error ( 'How is this possible?' ) ;
123- }
124-
125216 const futureStackElement = this . future . pop ( ) ;
126217
127218 try {
128- // Apply all operations
129- for ( let i = 0 ; i < futureStackElement . editOperations . length ; i ++ ) {
130- futureStackElement . editOperations [ i ] = {
131- operations : this . model . applyEdits ( futureStackElement . editOperations [ i ] . operations )
132- } ;
133- }
219+ futureStackElement . redo ( this . model ) ;
134220 } catch ( e ) {
221+ onUnexpectedError ( e ) ;
135222 this . clear ( ) ;
136223 return null ;
137224 }
0 commit comments