1 unit TestTrimSpace;
2 
3 {$mode objfpc}{$H+}
4 
5 interface
6 
7 (* TODO
8 
9 trim if the caret changes between identical lines
10 __abc|___
11 __abc
12 
13 or between two empty lines
14 |___
15 
16 *)
17 
18 uses
19   Classes, SysUtils, Forms, testregistry, TestBase, LCLProc, LCLType,
20   SynEdit, SynEditKeyCmds, SynEditTextTrimmer;
21 
22 type
23 
24   TTestTrimUndoEntry = record
25     Command:  TSynEditorCommand;
26     CaretWas: TPoint;
27     VTextWas: String;
28   end;
29 
30   { TTestTrimSpace }
31 
32   TTestTrimSpace = class(TTestBase)
33   private
34     FTName: String;
35     FUseEc: Boolean;
36     FUndoList: Array of TTestTrimUndoEntry;
37     FRedoList: Array of TTestTrimUndoEntry;
38     FUndoIndex: Integer;
39   protected
40     procedure test_assert(ASubName: String; AExp: String = ''; AExpX: Integer = -1; AExpY: Integer = -1; AppendCr: Boolean = True);
41     procedure test_assert(ASubName: String; AExp: Array of String; AExpX: Integer = -1; AExpY: Integer = -1; AppendCr: Boolean = True);
42     procedure test_add_undo(Cmd: TSynEditorCommand; IsRedo: Boolean = False);
43 
44     procedure test_start(AName, Txt: String);
45     procedure test_start(AName: String; Txt: Array of String);
46     procedure test_start(AName: String; Txt: Array of String; X, Y: Integer);
47 
48     Procedure test_cmd(ASubName: String; X, Y: Integer; Cmd: TSynEditorCommand; AChar: TUTF8Char;
49                        AExp: String = ''; AExpX: Integer = -1; AExpY: Integer = -1);
50     Procedure test_cmd(ASubName: String; Cmd: TSynEditorCommand;
51                        AExp: Array of String; AExpX: Integer = -1; AExpY: Integer = -1;
52                        SkipAddUndo: Boolean = False);
53     Procedure test_caret(ASubName: String; X, Y: Integer;
54                        AExp: String = ''; AExpX: Integer = -1; AExpY: Integer = -1);
55 
56     Procedure test_type  (ASubName: String; AText: String;
57                        AExp: Array of String; AExpX: Integer = -1; AExpY: Integer = -1);
58     Procedure test_type  (ASubName: String; AText: String; X, Y: Integer;
59                        AExp: Array of String; AExpX: Integer = -1; AExpY: Integer = -1);
60     Procedure test_bspace(ASubName: String;
61                        AExp: Array of String; AExpX: Integer = -1; AExpY: Integer = -1);
62     Procedure test_bspace(ASubName: String; X, Y: Integer;
63                        AExp: Array of String; AExpX: Integer = -1; AExpY: Integer = -1);
64     Procedure test_del   (ASubName: String;
65                        AExp: Array of String; AExpX: Integer = -1; AExpY: Integer = -1);
66     Procedure test_del   (ASubName: String; X, Y: Integer;
67                        AExp: Array of String; AExpX: Integer = -1; AExpY: Integer = -1);
68 
69     Procedure test_undo(ASubName: String; SkipCaretTest: Boolean = False);
70     Procedure test_redo(ASubName: String; SkipCaretTest: Boolean = False);
71     Procedure test_undo_exp(ASubName: String;
72                        AExp: String = ''; AExpX: Integer = -1; AExpY: Integer = -1);
73     Procedure test_undo_exp(ASubName: String;
74                        AExp: Array of String; AExpX: Integer = -1; AExpY: Integer = -1);
75     Procedure test_redo_exp(ASubName: String;
76                        AExp: String = ''; AExpX: Integer = -1; AExpY: Integer = -1);
77     Procedure test_redo_exp(ASubName: String;
78                        AExp: Array of String; AExpX: Integer = -1; AExpY: Integer = -1);
79     procedure SetUp; override;
80     procedure TearDown; override;
81   published
82     procedure TrimUndoRedo;
83     procedure TrimUndoRedoEc;
84     procedure NoTrimUndoRedo;
85     procedure TestInUndoBlock;
86     procedure TimmEdit;
87   end;
88 
89 implementation
90 const
91   cr: String = LineEnding;
92 
93 procedure TTestTrimSpace.SetUp;
94 begin
95   inherited;
96   FUseEc := False;
97 end;
98 
99 procedure TTestTrimSpace.TearDown;
100 begin
101   SetLength(FUndoList, 0);
102   inherited TearDown;
103 end;
104 
105 procedure TTestTrimSpace.test_assert(ASubName: String; AExp: String = '';
106   AExpX: Integer = -1; AExpY: Integer = -1; AppendCr: Boolean = True);
107 begin
108   if AppendCr then AExp := AExp + cr;
109   if AExp <> '' then
110     TestCompareString(FTName+' ('+ASubName+') Text', AExp, SynEdit.ViewedTextBuffer.Text);
111   if AExpX > 0 then begin
112     AssertEquals(FTName+' ('+ASubName+') Caret X', AExpX, SynEdit.CaretX);
113     AssertEquals(FTName+' ('+ASubName+') Caret Y', AExpY, SynEdit.CaretY);
114   end;
115 //debugln(['done ',ASubName]);
116 end;
117 
118 procedure TTestTrimSpace.test_assert(ASubName: String; AExp: array of String; AExpX: Integer;
119   AExpY: Integer; AppendCr: Boolean = True);
120 begin
121   test_assert(ASubName, LinesToText(AExp), AExpX, AExpY, AppendCr);
122 end;
123 
124 procedure TTestTrimSpace.test_add_undo(Cmd: TSynEditorCommand; IsRedo: Boolean);
125 begin
126   if not IsRedo then begin
127     inc(FUndoIndex);
128     SetLength(FUndoList, FUndoIndex);
129     FUndoList[FUndoIndex-1].Command := Cmd;
130     FUndoList[FUndoIndex-1].CaretWas := SynEdit.CaretXY;
131     FUndoList[FUndoIndex-1].VTextWas := SynEdit.ViewedTextBuffer.Text;
132   end
133   else begin
134     SetLength(FRedoList, FUndoIndex);
135     FRedoList[FUndoIndex-1].Command := Cmd;
136     FRedoList[FUndoIndex-1].CaretWas := SynEdit.CaretXY;
137     FRedoList[FUndoIndex-1].VTextWas := SynEdit.ViewedTextBuffer.Text;
138   end;
139 end;
140 
141 procedure TTestTrimSpace.test_start(AName, Txt: String);
142 begin
143   FTName := AName;
144   SynEdit.Text := Txt;
145   SetLength(FUndoList, 0);
146   FUndoIndex := 0;
147 //debugln(['----- START ',AName]);
148 end;
149 
150 procedure TTestTrimSpace.test_start(AName: String; Txt: array of String);
151 begin
152   test_start(AName, LinesToText(Txt));
153 end;
154 
155 procedure TTestTrimSpace.test_start(AName: String; Txt: array of String; X, Y: Integer);
156 begin
157   test_start(AName, LinesToText(Txt));
158   SynEdit.CaretXY := Point(x, y);
159 end;
160 
161 procedure TTestTrimSpace.test_cmd(ASubName: String; X, Y: Integer; Cmd: TSynEditorCommand;
162   AChar: TUTF8Char; AExp: String; AExpX: Integer; AExpY: Integer);
163 begin
164   test_add_undo(Cmd);
165 
166   SynEdit.CaretXY := Point(x, y);
167   SynEdit.CommandProcessor(Cmd, AChar, nil);
168 
169   test_add_undo(Cmd, True);
170   test_assert('cmd: '+ASubName, AExp, AExpX, AExpY);
171 end;
172 
173 procedure TTestTrimSpace.test_cmd(ASubName: String; Cmd: TSynEditorCommand;
174   AExp: array of String; AExpX: Integer; AExpY: Integer; SkipAddUndo: Boolean);
175 begin
176   if not SkipAddUndo then
177     test_add_undo(Cmd);
178 
179   SynEdit.CommandProcessor(Cmd, '', nil);
180 
181   if not SkipAddUndo then
182     test_add_undo(Cmd, True);
183   test_assert('cmd: '+ASubName, AExp, AExpX, AExpY);
184 end;
185 
186 procedure TTestTrimSpace.test_caret(ASubName: String; X, Y: Integer; AExp: String;
187   AExpX: Integer; AExpY: Integer);
188 begin
189   SynEdit.CaretXY := Point(x, y);
190   test_assert('caret: '+ASubName, AExp, AExpX, AExpY);
191 end;
192 
193 procedure TTestTrimSpace.test_type(ASubName: String; AText: String; AExp: array of String;
194   AExpX: Integer; AExpY: Integer);
195 begin
196   test_add_undo(ecChar);
197   SynEdit.TestTypeText(AText);
198   test_add_undo(ecNone, True);
199   test_assert('Typed: '+ASubName, AExp, AExpX, AExpY);
200 end;
201 
202 procedure TTestTrimSpace.test_type(ASubName: String; AText: String; X, Y: Integer;
203   AExp: array of String; AExpX: Integer; AExpY: Integer);
204 begin
205   test_add_undo(ecChar);
206   SynEdit.CaretXY := Point(x, y);
207   SynEdit.TestTypeText(AText);
208   test_add_undo(ecNone, True);
209   test_assert('Typed: '+ASubName, AExp, AExpX, AExpY);
210 end;
211 
212 procedure TTestTrimSpace.test_bspace(ASubName: String; AExp: array of String; AExpX: Integer;
213   AExpY: Integer);
214 begin
215   test_add_undo(ecDeleteLastChar);
216   SynEdit.CommandProcessor(ecDeleteLastChar, '', nil);
217   test_add_undo(ecNone, True);
218   test_assert('BSpace: '+ASubName, AExp, AExpX, AExpY);
219 end;
220 
221 procedure TTestTrimSpace.test_bspace(ASubName: String; X, Y: Integer; AExp: array of String;
222   AExpX: Integer; AExpY: Integer);
223 begin
224   test_add_undo(ecDeleteLastChar);
225   SynEdit.CaretXY := Point(x, y);
226   SynEdit.CommandProcessor(ecDeleteLastChar, '', nil);
227   test_add_undo(ecNone, True);
228   test_assert('BSpace: '+ASubName, AExp, AExpX, AExpY);
229 end;
230 
231 procedure TTestTrimSpace.test_del(ASubName: String; AExp: array of String; AExpX: Integer;
232   AExpY: Integer);
233 begin
234   test_add_undo(ecDeleteChar);
235   SynEdit.CommandProcessor(ecDeleteChar, '', nil);
236   test_add_undo(ecNone, True);
237   test_assert('Del: '+ASubName, AExp, AExpX, AExpY);
238 end;
239 
240 procedure TTestTrimSpace.test_del(ASubName: String; X, Y: Integer; AExp: array of String;
241   AExpX: Integer; AExpY: Integer);
242 begin
243   test_add_undo(ecDeleteChar);
244   SynEdit.CaretXY := Point(x, y);
245   SynEdit.CommandProcessor(ecDeleteChar, '', nil);
246   test_add_undo(ecNone, True);
247   test_assert('Del: '+ASubName, AExp, AExpX, AExpY);
248 end;
249 
250 procedure TTestTrimSpace.test_undo(ASubName: String; SkipCaretTest: Boolean);
251 begin
252   if FUseEc then
253     SynEdit.CommandProcessor(ecUndo, ' ', nil)
254   else
255     SynEdit.Undo;
256   AssertTrue('Undo selftest', FUndoIndex > 0);
257   dec(FUndoIndex);
258   if SkipCaretTest then
259     test_assert('undo: '+ASubName, FUndoList[FUndoIndex].VTextWas,
260                 -1, -1,
261                 False)
262   else
263     test_assert('undo: '+ASubName, FUndoList[FUndoIndex].VTextWas,
264                 FUndoList[FUndoIndex].CaretWas.X, FUndoList[FUndoIndex].CaretWas.Y,
265                 False);
266 end;
267 
268 procedure TTestTrimSpace.test_redo(ASubName: String; SkipCaretTest: Boolean);
269 begin
270   if FUseEc then
271     SynEdit.CommandProcessor(ecRedo, ' ', nil)
272   else
273     SynEdit.Redo;
274   assert(FUndoIndex < length(FRedoList));
275   if SkipCaretTest then
276     test_assert('undo: '+ASubName, FRedoList[FUndoIndex].VTextWas,
277                 -1, -1,
278                 False)
279   else
280     test_assert('undo: '+ASubName, FRedoList[FUndoIndex].VTextWas,
281                 FRedoList[FUndoIndex].CaretWas.X, FRedoList[FUndoIndex].CaretWas.Y,
282                 False);
283   inc(FUndoIndex);
284 end;
285 
286 procedure TTestTrimSpace.test_undo_exp(ASubName: String; AExp: String; AExpX: Integer;
287   AExpY: Integer);
288 begin
289   if FUseEc then
290     SynEdit.CommandProcessor(ecUndo, ' ', nil)
291   else
292     SynEdit.Undo;
293   test_assert('undo: '+ASubName, AExp, AExpX, AExpY);
294 end;
295 
296 procedure TTestTrimSpace.test_undo_exp(ASubName: String; AExp: array of String;
297   AExpX: Integer; AExpY: Integer);
298 begin
299   test_undo_exp(ASubName, LinesToText(AExp), AExpX, AExpY);
300 end;
301 
302 procedure TTestTrimSpace.test_redo_exp(ASubName: String; AExp: String; AExpX: Integer;
303   AExpY: Integer);
304 begin
305   if FUseEc then
306     SynEdit.CommandProcessor(ecRedo, ' ', nil)
307   else
308     SynEdit.Redo;
309   test_assert('redo: '+ASubName, AExp, AExpX, AExpY);
310 end;
311 
312 procedure TTestTrimSpace.test_redo_exp(ASubName: String; AExp: array of String;
313   AExpX: Integer; AExpY: Integer);
314 begin
315   test_redo_exp(ASubName, LinesToText(AExp), AExpX, AExpY);
316 end;
317 
318 procedure TTestTrimSpace.TrimUndoRedo;
319   procedure UndoRedo(AMax: Integer; AName: String = ''; SkipCaretTest: Boolean = False);
320   var
321     i, j: Integer;
322   begin
323     case AMax of
324       1: begin
325           test_undo(AName, SkipCaretTest);
326           test_redo(AName, SkipCaretTest);
327         end;
328       2: begin
329         j := FUndoIndex;
330           for i := 1 to j do test_undo(Format('%s %d/%d', [AName, i, j]), SkipCaretTest);
331           for i := 1 to j do test_redo(Format('%s %d/%d', [AName, i, j]), SkipCaretTest);
332         end;
333     end;
334   end;
335 var
336   MaxUndo: Integer;
337 begin
338 // must have eoScrollPastEol
339 // because ForcePastEOL does not work on longest line
340   SynEdit.Options := [eoTrimTrailingSpaces, eoAutoIndent, eoScrollPastEol]; // eoScrollPastEol, eoGroupUndo
341   SynEdit.TabWidth := 6;
342   for MaxUndo := 0 to 2 do begin
343     {%region    settLeaveLine }
344       SynEdit.TrimSpaceType := settLeaveLine;
345 
346       // no trim
347       test_start ('Append Space and text',    ['', 'abc'],        4,2);
348       test_type  ('Space 1',   ' ',           ['', 'abc '],       5,2); UndoRedo(MaxUndo);
349       test_type  ('Space 2',   ' ',           ['', 'abc  '],      6,2); UndoRedo(MaxUndo);
350       test_type  ('Char',      'A',           ['', 'abc  A'],     7,2); UndoRedo(MaxUndo);
351       test_bspace('BackSpace',                ['', 'abc  '],      6,2); UndoRedo(MaxUndo);
352       test_bspace('BackSpace',                ['', 'abc '],       5,2); UndoRedo(MaxUndo);
353       test_bspace('BackSpace',                ['', 'abc'],        4,2); UndoRedo(MaxUndo);
354 
355       // no trim
356       test_start ('Append tab + Space and text', ['', 'abc'],        4,2);
357       test_type  ('tab 1',      #9,              ['', 'abc'#9],         7,2); UndoRedo(MaxUndo);
358       test_type  ('Space 2',   ' ',              ['', 'abc'#9' '],      8,2); UndoRedo(MaxUndo);
359       test_type  ('Char',      'A',              ['', 'abc'#9' A'],     9,2); UndoRedo(MaxUndo);
360       test_bspace('BackSpace',                   ['', 'abc'#9' '],      8,2); UndoRedo(MaxUndo);
361       test_bspace('BackSpace',                   ['', 'abc'#9],         7,2); UndoRedo(MaxUndo);
362       test_bspace('BackSpace',                   ['', 'abc'],           4,2); UndoRedo(MaxUndo);
363 
364       // trim on leave line
365       test_start ('Append Space, move left, text, trim', ['', 'abc'],        4,2);
366       test_type  ('Space 1',   ' ',                   ['', 'abc '],       5,2); UndoRedo(MaxUndo);
367       test_type  ('Space 2',   ' ',                   ['', 'abc  '],      6,2); UndoRedo(MaxUndo);
368       test_cmd   ('caret left',ecLeft,                ['', 'abc  '],      5,2,  True);
369       test_type  ('Char',      'A',                   ['', 'abc A '],     6,2); UndoRedo(MaxUndo);
370       test_cmd   ('caret up',  ecUp,                  ['', 'abc A'],      6,1,  True);
371       test_undo  ('');
372 
373       test_start ('Insert CR, and trim space before CR ', ['', 'abc def'],        5,2);
374       test_type  ('CR',        #13,                       ['', 'abc', 'def'],     1,3);
375       UndoRedo(MaxUndo);
376 
377       test_start ('Replace select by CR, and trim before sel', ['', 'abc def,123'],     5,2);
378       SetCaretAndSel(5,2, 8,2); // 'def'
379       test_type  ('CR',        #13,                            ['', 'abc', ',123'],     1,3);
380       UndoRedo(MaxUndo);
381 
382     {%endregion settLeaveLine }
383 
384     {%region    settEditLine }
385       SynEdit.TrimSpaceType := settEditLine;
386 
387       // no trim
388       test_start ('Append Space and text',    ['', 'abc'],        4,2);
389       test_type  ('Space 1',   ' ',           ['', 'abc '],       5,2); UndoRedo(MaxUndo);
390       test_type  ('Space 2',   ' ',           ['', 'abc  '],      6,2); UndoRedo(MaxUndo);
391       test_type  ('Char',      'A',           ['', 'abc  A'],     7,2); UndoRedo(MaxUndo);
392       test_bspace('BackSpace',                ['', 'abc  '],      6,2); UndoRedo(MaxUndo);
393       test_bspace('BackSpace',                ['', 'abc '],       5,2); UndoRedo(MaxUndo);
394       test_bspace('BackSpace',                ['', 'abc'],        4,2); UndoRedo(MaxUndo);
395 
396       // no trim
397       test_start ('Append tab + Space and text', ['', 'abc'],        4,2);
398       test_type  ('tab 1',      #9,              ['', 'abc'#9],         7,2); UndoRedo(MaxUndo);
399       test_type  ('Space 2',   ' ',              ['', 'abc'#9' '],      8,2); UndoRedo(MaxUndo);
400       test_type  ('Char',      'A',              ['', 'abc'#9' A'],     9,2); UndoRedo(MaxUndo);
401       test_bspace('BackSpace',                   ['', 'abc'#9' '],      8,2); UndoRedo(MaxUndo);
402       test_bspace('BackSpace',                   ['', 'abc'#9],         7,2); UndoRedo(MaxUndo);
403       test_bspace('BackSpace',                   ['', 'abc'],           4,2); UndoRedo(MaxUndo);
404 
405       // trim on edit
406       test_start ('Append Space, move left, text',    ['', 'abc'],        4,2);
407       test_type  ('Space 1',   ' ',                   ['', 'abc '],       5,2); UndoRedo(MaxUndo);
408       test_type  ('Space 2',   ' ',                   ['', 'abc  '],      6,2); UndoRedo(MaxUndo);
409       test_cmd   ('caret left',ecLeft,                ['', 'abc  '],      5,2,  True);
410       test_type  ('Char',      'A',                   ['', 'abc A'],      6,2); UndoRedo(MaxUndo); // Trimmed on Edit
411       test_cmd   ('caret up',  ecUp,                  ['', 'abc A'],      6,1,  True);
412       test_undo  ('');
413 
414       test_start ('Insert CR, and trim space before CR ', ['', 'abc def'],        5,2);
415       test_type  ('CR',        #13,                       ['', 'abc', 'def'],     1,3);
416       UndoRedo(MaxUndo);
417 
418       test_start ('Replace select by CR, and trim before sel', ['', 'abc def,123'],     5,2);
419       SetCaretAndSel(5,2, 8,2); // 'def'
420       test_type  ('CR',        #13,                            ['', 'abc', ',123'],     1,3);
421       UndoRedo(MaxUndo);
422 
423     {%endregion settEditLine }
424 
425     {%region    settMoveCaret }
426       SynEdit.TrimSpaceType := settMoveCaret;
427 
428       // no trim
429       test_start ('Append Space and text',    ['', 'abc'],        4,2);
430       test_type  ('Space 1',   ' ',           ['', 'abc '],       5,2); UndoRedo(MaxUndo);
431       test_type  ('Space 2',   ' ',           ['', 'abc  '],      6,2); UndoRedo(MaxUndo);
432       test_type  ('Char',      'A',           ['', 'abc  A'],     7,2); UndoRedo(MaxUndo);
433       test_bspace('BackSpace',                ['', 'abc  '],      6,2); UndoRedo(MaxUndo);
434       test_bspace('BackSpace',                ['', 'abc '],       5,2); UndoRedo(MaxUndo);
435       test_bspace('BackSpace',                ['', 'abc'],        4,2); UndoRedo(MaxUndo);
436 
437       // no trim
438       test_start ('Append tab + Space and text', ['', 'abc'],        4,2);
439       test_type  ('tab 1',      #9,              ['', 'abc'#9],         7,2); UndoRedo(MaxUndo);
440       test_type  ('Space 2',   ' ',              ['', 'abc'#9' '],      8,2); UndoRedo(MaxUndo);
441       test_type  ('Char',      'A',              ['', 'abc'#9' A'],     9,2); UndoRedo(MaxUndo);
442       test_bspace('BackSpace',                   ['', 'abc'#9' '],      8,2); UndoRedo(MaxUndo);
443       test_bspace('BackSpace',                   ['', 'abc'#9],         7,2); UndoRedo(MaxUndo);
444       test_bspace('BackSpace',                   ['', 'abc'],           4,2); UndoRedo(MaxUndo);
445 
446       // trim on move caret
447       test_start ('Append Space, move left, text',    ['', 'abc'],        4,2);
448       test_type  ('Space 1',   ' ',                   ['', 'abc '],       5,2); UndoRedo(MaxUndo);
449       test_type  ('Space 2',   ' ',                   ['', 'abc  '],      6,2); UndoRedo(MaxUndo);
450       test_cmd   ('caret left',ecLeft,                ['', 'abc '],       5,2,  True);
451 // TODO: Review: The spacethat was just trimmed, can be undone (leading to an undo that changes nothing)
452       test_type  ('Char',      'A',                   ['', 'abc A'],      6,2); UndoRedo(MaxUndo); // Trimmed on Edit
453       test_cmd   ('caret up',  ecUp,                  ['', 'abc A'],      6,1,  True);
454 //TODO: FAIL: This fails with MaxUndo = 2
455 //      test_undo  ('');
456 
457       test_start ('Insert CR, and trim space before CR ', ['', 'abc def'],        5,2);
458       test_type  ('CR',        #13,                       ['', 'abc', 'def'],     1,3);
459       UndoRedo(MaxUndo);
460 
461       test_start ('Replace select by CR, and trim before sel', ['', 'abc def,123'],     5,2);
462       SetCaretAndSel(5,2, 8,2); // 'def'
463       test_type  ('CR',        #13,                            ['', 'abc', ',123'],     1,3);
464       UndoRedo(MaxUndo);
465 
466     {%endregion settMoveCaret }
467 
468     {%region    settIgnoreAll }
469       SynEdit.TrimSpaceType := settIgnoreAll;
470 
471       // no trim
472       test_start ('Append Space and text',    ['', 'abc'],        4,2);
473       test_type  ('Space 1',   ' ',           ['', 'abc'],        5,2);
474       test_type  ('Space 2',   ' ',           ['', 'abc'],        6,2);
475       test_type  ('Char',      'A',           ['', 'abc  A'],     7,2);
476       if MaxUndo > 0 then begin
477         test_undo_exp('undo',                   ['', 'abc'],        6,2); // only undo the "A"
478         test_redo_exp('redo',                   ['', 'abc  A'],     7,2);
479       end;
480       test_bspace('BackSpace',                ['', 'abc'],        6,2);
481       test_bspace('BackSpace',                ['', 'abc'],        5,2);
482       test_bspace('BackSpace',                ['', 'abc'],        4,2);
483 
484       // no trim
485       test_start ('Append tab + Space and text', ['', 'abc'],           4,2);
486 // TODO: currently tab acts as space, because Log/Phys can not work, if the tab is not present
487       test_type  ('tab 1',      #9,              ['', 'abc'],           7,2);
488       test_type  ('Space 2',   ' ',              ['', 'abc'],           8,2);
489       test_type  ('Char',      'A',              ['', 'abc    A'],     9,2);
490       if MaxUndo > 0 then begin
491         test_undo_exp('undo',                   ['', 'abc'],        8,2); // only undo the "A"
492         test_redo_exp('redo',                   ['', 'abc    A'],     9,2);
493       end;
494       test_bspace('BackSpace',                   ['', 'abc'],           8,2);
495       test_bspace('BackSpace',                   ['', 'abc'],           7,2);
496       test_bspace('BackSpace',                   ['', 'abc'],           6,2);
497 
498       //
499       test_start ('Append Space, move left, text',    ['', 'abc'],        4,2);
500       test_type  ('Space 1',   ' ',                   ['', 'abc'],        5,2);
501       test_type  ('Space 2',   ' ',                   ['', 'abc'],        6,2);
502       test_cmd   ('caret left',ecLeft,                ['', 'abc'],        5,2,  True);
503       test_type  ('Char',      'A',                   ['', 'abc A'],      6,2);
504       test_cmd   ('caret up',  ecUp,                  ['', 'abc A'],      6,1,  True);
505 
506       test_start ('Insert CR, and trim space before CR ', ['', 'abc def'],        5,2);
507       test_type  ('CR',        #13,                       ['', 'abc', 'def'],     1,3);
508       UndoRedo(MaxUndo);
509 
510       test_start ('Replace select by CR, and trim before sel', ['', 'abc def,123'],     5,2);
511       SetCaretAndSel(5,2, 8,2); // 'def'
512       test_type  ('CR',        #13,                            ['', 'abc', ',123'],     1,3);
513       UndoRedo(MaxUndo);
514 
515     {%endregion settIgnoreAll }
516   end;
517 
518   SynEdit.Options := [eoTrimTrailingSpaces, eoAutoIndent, eoScrollPastEol]; // eoGroupUndo
519   SynEdit.TrimSpaceType := settLeaveLine;
520 
521   { Edit Lines }
522   (* *)
523   test_start('Delete leaves trimable space', cr+'abc d');
524   test_cmd  ('Backspace 6,2', 6,2, ecDeleteLastChar, '', cr+'abc ', 5,2);
525   test_caret('trim', 1,1, cr+'abc', 1,1);
526   // undo
527 //#  test_undo_exp('undo trim', cr+'abc ', 5,2);
528   test_undo_exp('undo bspace', cr+'abc d', 6,2);
529   test_redo_exp('redo bspace', cr+'abc ', 5,2);
530   test_caret('trim', 1,1, cr+'abc', 1,1);
531   // undo again
532 //#  test_undo_exp('undo trim(2)', cr+'abc ', 5,2);
533   test_undo_exp('undo bspace(2)', cr+'abc d', 6,2);
534   test_redo_exp('redo bspace(2)', cr+'abc ', 5,2);
535   test_redo_exp('no redo trim(2)', cr+'abc ', 5,2);
536   // undo again
537   test_caret('trim(3)', 1,1, cr+'abc', 1,1);
538 //#  test_undo_exp('undo trim(3)', cr+'abc ', 5,2);
539 //##  test_caret('trim(3)', 1,1, cr+'abc', 1,1);
540 
541   (* *)
542   test_start('insert past real space', cr+'abc ');
543   test_cmd  ('insert 5,2', 5,2, ecChar, 'x', cr+'abc x', 6,2);
544   // undo
545   test_undo_exp('undo insert', cr+'abc ', 5,2);
546   test_caret('no trim', 1,1, cr+'abc ', 1,1);
547   // rdeo/undo again
548   test_redo_exp('redo insert', cr+'abc x', 6,2);
549   test_undo_exp('undo insert(2)', cr+'abc ', 5,2);
550   test_caret('no trim(2)', 1,1, cr+'abc ', 1,1);
551 
552   (* *)
553   test_start('insert spaces past real space', cr+'abc ');
554   test_cmd  ('insert 5,2', 5,2, ecChar, ' ', cr+'abc  ', 6,2);
555   test_cmd  ('insert 6,2', 6,2, ecChar, ' ', cr+'abc   ', 7,2);
556   // undo
557   test_undo_exp('undo insert2', cr+'abc  ', 6,2);
558   test_undo_exp('undo insert1', cr+'abc ',  5,2);
559   test_caret('no trim', 1,1, cr+'abc ', 1,1);
560   // rdeo/undo again
561   test_redo_exp('redo insert1', cr+'abc  ',  6,2);
562   test_redo_exp('redo insert2', cr+'abc   ', 7,2);
563   test_undo_exp('undo inserts2(2)', cr+'abc  ', 6,2);
564   test_undo_exp('undo inserts1(2)', cr+'abc ',  5,2);
565   test_caret('no trim(2)', 1,1, cr+'abc ', 1,1);
566 
567 
568   (* *)
569   test_start('insert spaces,char', cr+'abc');
570   test_cmd  ('insert 4,2', 4,2, ecChar, ' ', cr+'abc ',  5,2);
571   test_cmd  ('insert 5,2', 5,2, ecChar, 'x', cr+'abc x', 6,2);
572   // undo
573   test_undo_exp('undo ins char', cr+'abc ', 5,2);
574   test_caret('trim', 1,1, cr+'abc', 1,1); /// xxx this undo, remose the ability to redo....
575 
576   (* *)
577   test_start('insert spaces,char after space', cr+'ab ');
578   test_cmd  ('insert 4,2', 4,2, ecChar, ' ', cr+'ab  ',  5,2);
579   test_cmd  ('insert 5,2', 5,2, ecChar, 'x', cr+'ab  x', 6,2);
580   // undo
581   test_undo_exp('undo ins char', cr+'ab  ', 5,2);
582   test_caret('trim', 1,1, cr+'ab', 1,1); /// xxx this undo, remose the ability to redo.... xxxxxxxxxxxxxxxx
583   // We can no longer undo back to the real space
584 
585   (* *)
586   test_start('insert spaces,char after space (2)', cr+'ab ');
587   test_cmd  ('insert 4,2', 4,2, ecChar, ' ', cr+'ab  ',  5,2);
588   test_cmd  ('insert 5,2', 5,2, ecChar, 'x', cr+'ab  x', 6,2);
589   // undo
590   test_undo_exp('undo ins char', cr+'ab  ', 5,2);
591   test_undo_exp('undo ins space', cr+'ab ', 4,2);
592   test_caret('no trim', 1,1, cr+'ab ', 1,1); /// xxx this undo, remose the ability to redo....
593 
594   (* *)
595   test_start('insert past eol', cr+'abc ');
596   test_cmd  ('insert 8,2', 8,2, ecChar, 'x', cr+'abc    x', 9,2);
597   test_undo_exp('undo insert', cr+'abc ', 8,2);
598   test_caret('trim', 1,1, cr+'abc ', 1,1);
599   // TODO: redo
600 
601   { Insert LineBreaks }
602   (* *)
603   test_start('insert newline', cr+'ab 1234 ');
604   test_cmd  ('insert 4,2', 4,2, ecLineBreak, ' ', cr+'ab'+cr+'1234 ',  1,3);
605   test_caret('trim', 1,1, cr+'ab'+cr+'1234', 1,1);
606 //#  test_undo_exp('-trim', cr+'ab'+cr+'1234 ', 6,3);  // TODO caret at:   1,3);
607   test_undo_exp('-newline', cr+'ab 1234 ', 4,2);
608   test_caret('no trim', 1,1, cr+'ab 1234 ', 1,1);
609 
610   (* *)
611   test_start('insert newline+indent', cr+'  ab ');
612   test_cmd  ('insert 6,2', 6,2, ecLineBreak, ' ', cr+'  ab'+cr+'  ',              3,3);
613   test_cmd  ('insert 3,3', 3,3, ecLineBreak, ' ', cr+'  ab'+cr+''+cr+'  ',        3,4);
614   test_cmd  ('insert 3,4', 3,4, ecLineBreak, ' ', cr+'  ab'+cr+''+cr+''+cr+'  ',  3,5);
615   test_caret('trim', 1,1, cr+'  ab'+cr+''+cr+''+cr+'', 1,1);
616 //#  test_undo_exp('-trim',      cr+'  ab'+cr+''+cr+''+cr+'  ', 3,5);
617   test_undo_exp('-newlin 1', cr+'  ab'+cr+''+cr+'  ',       3,4);
618   test_undo_exp('-newlin 2', cr+'  ab'+cr+'  ',             3,3);
619   test_undo_exp('-newlin 3', cr+'  ab ',                    6,2);
620   test_caret('no trim', 1,1, cr+'  ab ', 1,1);
621   test_redo_exp('redo 3',    cr+'  ab'+cr+'  ', 3,3);
622   test_redo_exp('redo 2',    cr+'  ab'+cr+''+cr+'  ', 3,4);
623   test_redo_exp('redo 1',    cr+'  ab'+cr+''+cr+''+cr+'  ', 3,5);
624   test_undo_exp('-newlin 1(r)',  cr+'  ab'+cr+''+cr+'  ',       3,4);
625   test_undo_exp('-newlin 2(r)',  cr+'  ab'+cr+'  ',             3,3);
626   test_undo_exp('-newlin 3(r)',  cr+'  ab ',                    6,2);
627   test_caret('no trim', 1,1, cr+'  ab ', 1,1);
628 
629   (* *)
630   test_start('insert newline+indent before space', cr+'  ab ');
631   test_cmd  ('insert 5,2', 5,2, ecLineBreak, ' ', cr+'  ab'+cr+'   ',              3,3);
632   test_cmd  ('insert 3,3', 3,3, ecLineBreak, ' ', cr+'  ab'+cr+''+cr+'   ',        3,4);
633   test_cmd  ('insert 3,4', 3,4, ecLineBreak, ' ', cr+'  ab'+cr+''+cr+''+cr+'   ',  3,5);
634   test_caret('trim', 1,1, cr+'  ab'+cr+''+cr+''+cr+'', 1,1);
635 //#  test_undo_exp('-trim',      cr+'  ab'+cr+''+cr+''+cr+'   ', 4,5); // TODO caret at:   3,5);
636   test_undo_exp('-newlin 1', cr+'  ab'+cr+''+cr+'   ',        3,4);
637   test_undo_exp('-newlin 2', cr+'  ab'+cr+'   ',              3,3);
638   test_undo_exp('-newlin 3', cr+'  ab ',                      5,2);
639   test_caret('no trim', 1,1, cr+'  ab ',           1,1);
640   test_redo_exp('redo 3',    cr+'  ab'+cr+'   ',       3,3);
641   test_redo_exp('redo 2',    cr+'  ab'+cr+''+cr+'   ', 3,4);
642   test_redo_exp('redo 1',    cr+'  ab'+cr+''+cr+''+cr+'   ', 3,5);
643   test_undo_exp('-newlin 1(r)',  cr+'  ab'+cr+''+cr+'   ',       3,4);
644   test_undo_exp('-newlin 2(r)',  cr+'  ab'+cr+'   ',             3,3);
645   test_undo_exp('-newlin 3(r)',  cr+'  ab ',                     5,2);
646   test_caret('no trim', 1,1, cr+'  ab ', 1,1);
647 
648   (* *)
649   test_start('insert linebreak', cr+'ab 1234 ');
650   test_cmd  ('insert 4,2', 4,2, ecInsertLine, ' ', cr+'ab '+cr+'1234',  4,2);
651   test_caret('trim', 1,1, cr+'ab'+cr+'1234', 1,1);
652 //#  test_undo_exp('-trim', cr+'ab '+cr+'1234', 4,2);
653   test_undo_exp('-linebreak', cr+'ab 1234 ', 4,2);
654   test_caret('no trim', 1,1, cr+'ab 1234 ', 1,1);
655 
656   (* *)
657   test_start('insert linebreak+indent', cr+'  ab ');
658   test_cmd  ('insert1 6,2', 6,2, ecInsertLine, ' ', cr+'  ab '+cr+'',  6,2);
659   test_cmd  ('insert2 6,2', 6,2, ecInsertLine, ' ', cr+'  ab '+cr+''+cr+'',  6,2);
660   test_caret('trim', 1,1, cr+'  ab'+cr+''+cr+'', 1,1);
661 //#  test_undo_exp('-trim',      cr+'  ab '+cr+''+cr+'', 6,2);
662   test_undo_exp('-linebreak 1', cr+'  ab '+cr+'',     6,2);
663   test_undo_exp('-linebreak 2', cr+'  ab ',           6,2);
664   test_caret('no trim', 1,1, cr+'  ab ',          1,1);
665 (*
666   test_redo_exp('redo 2',    cr+'  ab '+cr+'',        6,2);
667   test_redo_exp('redo 1',    cr+'  ab '+cr+''+cr+'',  6,2);
668   test_undo_exp('-linebreak 1(r)',  cr+'  ab '+cr+'', 6,2);
669   test_undo_exp('-linebreak 2(r)',  cr+'  ab ',        6,2);
670   test_caret('no trim', 1,1, cr+'  ab ', 1,1);
671 *)
672 
673   (* *)
674   test_start('insert linebreak+indent before space', cr+'  ab ');
675   test_cmd  ('insert1 5,2', 5,2, ecInsertLine, ' ', cr+'  ab'+cr+'',  5,2);
676   test_cmd  ('insert2 5,2', 5,2, ecInsertLine, ' ', cr+'  ab'+cr+''+cr+'',  5,2);
677   test_caret('no trim (0 spaces)', 1,1, cr+'  ab'+cr+''+cr+'', 1,1);
678   test_undo_exp('-linebreak 1', cr+'  ab'+cr+'',     5,2);
679   test_undo_exp('-linebreak 2', cr+'  ab ',          5,2);
680   test_caret('no trim', 1,1, cr+'  ab ',         1,1);
681 (*
682   test_redo_exp('redo 2',    cr+'  ab'+cr+'',       5,2);
683   test_redo_exp('redo 1',    cr+'  ab'+cr+''+cr+'', 5,2);
684   test_undo_exp('-linebreak 1(r)',  cr+'  ab'+cr+'', 5,2);
685   test_undo_exp('-linebreak 2(r)',  cr+'  ab ',      5,2);
686   test_caret('no trim', 1,1, cr+'  ab ', 1,1);
687 *)
688 
689   { Join Lines }
690   (* *)
691   test_start('del',       cr+'ab '+cr+'12');
692   test_cmd  ('del 4,2', 4,2, ecDeleteChar, ' ', cr+'ab 12',  4,2);
693   test_undo_exp('-del',       cr+'ab '+cr+'12',     4,2);
694   test_caret('trim', 1,1, cr+'ab '+cr+'12', 1,1);
695   test_redo_exp('+del',       cr+'ab 12',     4,2);
696   test_undo_exp('-del(2)',    cr+'ab '+cr+'12',     4,2);
697   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12', 1,1);
698 
699   (* *)
700   test_start('del (both sp)',       cr+'ab '+cr+'12 ');
701   test_cmd  ('del 4,2', 4,2, ecDeleteChar, ' ', cr+'ab 12 ',  4,2);
702   test_undo_exp('-del',       cr+'ab '+cr+'12 ',     4,2);
703   test_caret('trim', 1,1, cr+'ab '+cr+'12 ', 1,1);
704   test_redo_exp('+del',       cr+'ab 12 ',     4,2);
705   test_undo_exp('-del(2)',    cr+'ab '+cr+'12 ',     4,2);
706   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12 ', 1,1);
707 
708   (* *)
709   test_start('del past eol',       cr+'ab '+cr+'12');
710   test_cmd  ('del 6,2', 6,2, ecDeleteChar, ' ', cr+'ab   12',  6,2);
711   test_undo_exp('-del',        cr+'ab '+cr+'12',  6,2);
712   test_caret('no trim', 1,1,  cr+'ab '+cr+'12',  1,1);
713   test_caret('-no trim', 6,2, cr+'ab '+cr+'12',  6,2);
714   test_redo_exp('+del(2)',       cr+'ab   12',     6,2);
715   test_undo_exp('-del(2)',    cr+'ab '+cr+'12', 6,2);
716   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12', 1,1);
717   test_redo_exp('+del(3)',       cr+'ab   12',     6,2);  // redo with mis-place caret
718 
719   (* *)
720   test_start('backspace', cr+'ab '+cr+'12');
721   test_cmd  ('bs 1,3', 1,3, ecDeleteLastChar, ' ', cr+'ab 12',  4,2);
722   test_undo_exp('-bs',    cr+'ab '+cr+'12',     1,3);
723   test_caret('trim', 1,1, cr+'ab '+cr+'12', 1,1);
724   test_redo_exp('+bs',    cr+'ab 12',     4,2);
725   test_undo_exp('-bs(2)', cr+'ab '+cr+'12',     1,3);
726   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12', 1,1);
727 
728   (* *)
729   test_start('del-word',       cr+'ab '+cr+'12');
730   test_cmd  ('del 4,2', 4,2, ecDeleteWord, ' ', cr+'ab 12',  4,2);
731   test_undo_exp('-del',       cr+'ab '+cr+'12',     4,2);
732   test_caret('no trim', 1,1, cr+'ab '+cr+'12', 1,1);
733   test_redo_exp('+del',       cr+'ab 12',     4,2);
734   test_undo_exp('-del(2)',    cr+'ab '+cr+'12',     4,2);
735   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12', 1,1);
736 
737   (* *)
738   test_start('del-word (both sp)',       cr+'ab '+cr+'12 ');
739   test_cmd  ('del 4,2', 4,2, ecDeleteWord, ' ', cr+'ab 12 ',  4,2);
740 (* xxxxxxxxxxxxxxx
741   test_undo_exp('-del',       cr+'ab '+cr+'12 ',     4,2);
742   test_caret('trim', 1,1, cr+'ab '+cr+'12 ', 1,1);
743   test_redo_exp('+del',       cr+'ab 12 ',     4,2);
744   test_undo_exp('-del(2)',    cr+'ab '+cr+'12 ',     4,2);
745   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12 ', 1,1);
746 *)
747 
748   (* *)
749   test_start('del-word past eol',       cr+'ab '+cr+'12');
750   test_cmd  ('del 6,2', 6,2, ecDeleteWord, ' ', cr+'ab   12',  6,2);
751   test_undo_exp('-del',        cr+'ab '+cr+'12',  6,2);
752   test_caret('trim', 1,1,  cr+'ab '+cr+'12',  1,1);
753   test_caret('-trim', 6,2, cr+'ab '+cr+'12',  6,2);
754   test_redo_exp('+del(2)',       cr+'ab   12',     6,2);
755   test_undo_exp('-del(2)',    cr+'ab '+cr+'12', 6,2);
756   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12', 1,1);
757   test_redo_exp('+del(3)',       cr+'ab   12',     6,2);  // redo with mis-place caret
758 
759 end;
760 
761 procedure TTestTrimSpace.TrimUndoRedoEc;
762 begin
763   FUseEc := True;
764   TrimUndoRedo;
765 end;
766 
767 procedure TTestTrimSpace.NoTrimUndoRedo;
768 begin
769   SynEdit.Options := [eoAutoIndent, eoScrollPastEol]; // eoGroupUndo
770 
771 
772   { Insert LineBreaks }
773   (* *)
774   test_start('insert newline', cr+'ab 1234 ');
775   test_cmd  ('insert 4,2', 4,2, ecLineBreak, ' ', cr+'ab '+cr+'1234 ',  1,3);
776   test_caret('trim', 1,1, cr+'ab '+cr+'1234 ', 1,1);
777   test_undo_exp('-newline', cr+'ab 1234 ', 4,2);
778   test_caret('no trim', 1,1, cr+'ab 1234 ', 1,1);
779 
780   (* *)
781   test_start('insert newline+indent', cr+'  ab ');
782   test_cmd  ('insert 6,2', 6,2, ecLineBreak, ' ', cr+'  ab '+cr+'  ',                  3,3);
783   test_cmd  ('insert 3,3', 3,3, ecLineBreak, ' ', cr+'  ab '+cr+'  '+cr+'  ',          3,4);
784   test_cmd  ('insert 3,4', 3,4, ecLineBreak, ' ', cr+'  ab '+cr+'  '+cr+'  '+cr+'  ',  3,5);
785   test_caret('trim', 1,1, cr+'  ab '+cr+'  '+cr+'  '+cr+'  ', 1,1);
786   test_undo_exp('-newlin 1', cr+'  ab '+cr+'  '+cr+'  ',       3,4);
787   test_undo_exp('-newlin 2', cr+'  ab '+cr+'  ',               3,3);
788   test_undo_exp('-newlin 3', cr+'  ab ',                      6,2);
789   test_caret('no trim', 1,1, cr+'  ab ', 1,1);
790   test_redo_exp('redo 3',    cr+'  ab '+cr+'  ',                 3,3);
791   test_redo_exp('redo 2',    cr+'  ab '+cr+'  '+cr+'  ',         3,4);
792   test_redo_exp('redo 1',    cr+'  ab '+cr+'  '+cr+'  '+cr+'  ', 3,5);
793   test_undo_exp('-newlin 1(r)',  cr+'  ab '+cr+'  '+cr+'  ',     3,4);
794   test_undo_exp('-newlin 2(r)',  cr+'  ab '+cr+'  ',             3,3);
795   test_undo_exp('-newlin 3(r)',  cr+'  ab ',                    6,2);
796   test_caret('no trim', 1,1, cr+'  ab ', 1,1);
797 
798 {
799   (* *)
800   test_start('insert newline+indent before space', cr+'  ab ');
801   test_cmd  ('insert 5,2', 5,2, ecLineBreak, ' ', cr+'  ab'+cr+'   ',              3,3);
802   test_cmd  ('insert 3,3', 3,3, ecLineBreak, ' ', cr+'  ab'+cr+''+cr+'   ',        3,4);
803   test_cmd  ('insert 3,4', 3,4, ecLineBreak, ' ', cr+'  ab'+cr+''+cr+''+cr+'   ',  3,5);
804   test_caret('trim', 1,1, cr+'  ab'+cr+''+cr+''+cr+'', 1,1);
805   test_undo_exp('-trim',      cr+'  ab'+cr+''+cr+''+cr+'   ', 4,5); // TODO caret at:   3,5);
806   test_undo_exp('-newlin 1', cr+'  ab'+cr+''+cr+'   ',        3,4);
807   test_undo_exp('-newlin 2', cr+'  ab'+cr+'   ',              3,3);
808   test_undo_exp('-newlin 3', cr+'  ab ',                      5,2);
809   test_caret('no trim', 1,1, cr+'  ab ',           1,1);
810   test_redo_exp('redo 3',    cr+'  ab'+cr+'   ',       3,3);
811   test_redo_exp('redo 2',    cr+'  ab'+cr+''+cr+'   ', 3,4);
812   test_redo_exp('redo 1',    cr+'  ab'+cr+''+cr+''+cr+'   ', 3,5);
813   test_undo_exp('-newlin 1(r)',  cr+'  ab'+cr+''+cr+'   ',       3,4);
814   test_undo_exp('-newlin 2(r)',  cr+'  ab'+cr+'   ',             3,3);
815   test_undo_exp('-newlin 3(r)',  cr+'  ab ',                     5,2);
816   test_caret('no trim', 1,1, cr+'  ab ', 1,1);
817 
818 
819   (* *)
820   test_start('insert linebreak', cr+'ab 1234 ');
821   test_cmd  ('insert 4,2', 4,2, ecInsertLine, ' ', cr+'ab '+cr+'1234',  4,2);
822   test_caret('trim', 1,1, cr+'ab'+cr+'1234', 1,1);
823   test_undo_exp('-trim', cr+'ab '+cr+'1234', 4,2);
824   test_undo_exp('-linebreak', cr+'ab 1234 ', 4,2);
825   test_caret('no trim', 1,1, cr+'ab 1234 ', 1,1);
826 
827   (* *)
828   test_start('insert linebreak+indent', cr+'  ab ');
829   test_cmd  ('insert1 6,2', 6,2, ecInsertLine, ' ', cr+'  ab '+cr+'',  6,2);
830   test_cmd  ('insert2 6,2', 6,2, ecInsertLine, ' ', cr+'  ab '+cr+''+cr+'',  6,2);
831   test_caret('trim', 1,1, cr+'  ab'+cr+''+cr+'', 1,1);
832   test_undo_exp('-trim',      cr+'  ab '+cr+''+cr+'', 6,2);
833   test_undo_exp('-linebreak 1', cr+'  ab '+cr+'',     6,2);
834   test_undo_exp('-linebreak 2', cr+'  ab ',           6,2);
835   test_caret('no trim', 1,1, cr+'  ab ',          1,1);
836 (*
837   test_redo_exp('redo 2',    cr+'  ab '+cr+'',        6,2);
838   test_redo_exp('redo 1',    cr+'  ab '+cr+''+cr+'',  6,2);
839   test_undo_exp('-linebreak 1(r)',  cr+'  ab '+cr+'', 6,2);
840   test_undo_exp('-linebreak 2(r)',  cr+'  ab ',        6,2);
841   test_caret('no trim', 1,1, cr+'  ab ', 1,1);
842 *)
843 
844   (* *)
845   test_start('insert linebreak+indent before space', cr+'  ab ');
846   test_cmd  ('insert1 5,2', 5,2, ecInsertLine, ' ', cr+'  ab'+cr+'',  5,2);
847   test_cmd  ('insert2 5,2', 5,2, ecInsertLine, ' ', cr+'  ab'+cr+''+cr+'',  5,2);
848   test_caret('no trim (0 spaces)', 1,1, cr+'  ab'+cr+''+cr+'', 1,1);
849   test_undo_exp('-linebreak 1', cr+'  ab'+cr+'',     5,2);
850   test_undo_exp('-linebreak 2', cr+'  ab ',          5,2);
851   test_caret('no trim', 1,1, cr+'  ab ',         1,1);
852 (*
853   test_redo_exp('redo 2',    cr+'  ab'+cr+'',       5,2);
854   test_redo_exp('redo 1',    cr+'  ab'+cr+''+cr+'', 5,2);
855   test_undo_exp('-linebreak 1(r)',  cr+'  ab'+cr+'', 5,2);
856   test_undo_exp('-linebreak 2(r)',  cr+'  ab ',      5,2);
857   test_caret('no trim', 1,1, cr+'  ab ', 1,1);
858 *)
859 }
860 
861   { Join Lines }
862   (* *)
863   test_start('del',       cr+'ab '+cr+'12');
864   test_cmd  ('del 4,2', 4,2, ecDeleteChar, ' ', cr+'ab 12',  4,2);
865   test_undo_exp('-del',       cr+'ab '+cr+'12',     4,2);
866   test_caret('trim', 1,1, cr+'ab '+cr+'12', 1,1);
867   test_redo_exp('+del',       cr+'ab 12',     4,2);
868   test_undo_exp('-del(2)',    cr+'ab '+cr+'12',     4,2);
869   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12', 1,1);
870 
871   (* *)
872   test_start('del (both sp)',       cr+'ab '+cr+'12 ');
873   test_cmd  ('del 4,2', 4,2, ecDeleteChar, ' ', cr+'ab 12 ',  4,2);
874   test_undo_exp('-del',       cr+'ab '+cr+'12 ',     4,2);
875   test_caret('trim', 1,1, cr+'ab '+cr+'12 ', 1,1);
876   test_redo_exp('+del',       cr+'ab 12 ',     4,2);
877   test_undo_exp('-del(2)',    cr+'ab '+cr+'12 ',     4,2);
878   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12 ', 1,1);
879 
880   (* *)
881   test_start('del past eol',       cr+'ab '+cr+'12');
882   test_cmd  ('del 6,2', 6,2, ecDeleteChar, ' ', cr+'ab   12',  6,2);
883   test_undo_exp('-del',        cr+'ab '+cr+'12',  6,2);
884   test_caret('trim', 1,1,  cr+'ab '+cr+'12',  1,1);
885   test_caret('-trim', 6,2, cr+'ab '+cr+'12',  6,2);
886 (*
887   test_redo_exp('+del(2)',       cr+'ab   12',     6,2);
888   test_undo_exp('-del(2)',    cr+'ab '+cr+'12', 6,2);
889   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12', 1,1);
890   test_redo_exp('+del(3)',       cr+'ab   12',     6,2);  // redo with mis-place caret
891 *)
892 
893   (* *)
894   test_start('backspace', cr+'ab '+cr+'12');
895   test_cmd  ('bs 1,3', 1,3, ecDeleteLastChar, ' ', cr+'ab 12',  4,2);
896   test_undo_exp('-bs',    cr+'ab '+cr+'12',     1,3);
897   test_caret('trim', 1,1, cr+'ab '+cr+'12', 1,1);
898   test_redo_exp('+bs',    cr+'ab 12',     4,2);
899   test_undo_exp('-bs(2)', cr+'ab '+cr+'12',     1,3);
900   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12', 1,1);
901 
902   (* *)
903   test_start('del-word',       cr+'ab '+cr+'12');
904   test_cmd  ('del 4,2', 4,2, ecDeleteWord, ' ', cr+'ab 12',  4,2);
905   test_undo_exp('-del',       cr+'ab '+cr+'12',     4,2);
906   test_caret('trim', 1,1, cr+'ab '+cr+'12', 1,1);
907   test_redo_exp('+del',       cr+'ab 12',     4,2);
908   test_undo_exp('-del(2)',    cr+'ab '+cr+'12',     4,2);
909   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12', 1,1);
910 
911   (* *)
912   test_start('del-word (both sp)',       cr+'ab '+cr+'12 ');
913   test_cmd  ('del 4,2', 4,2, ecDeleteWord, ' ', cr+'ab 12 ',  4,2);
914   test_undo_exp('-del',       cr+'ab '+cr+'12 ',     4,2);
915   test_caret('trim', 1,1, cr+'ab '+cr+'12 ', 1,1);
916   test_redo_exp('+del',       cr+'ab 12 ',     4,2);
917   test_undo_exp('-del(2)',    cr+'ab '+cr+'12 ',     4,2);
918   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12 ', 1,1);
919 
920   (* *)
921   test_start('del-word past eol',       cr+'ab '+cr+'12');
922   test_cmd  ('del 6,2', 6,2, ecDeleteWord, ' ', cr+'ab   12',  6,2);
923   test_undo_exp('-del',        cr+'ab '+cr+'12',  6,2);
924   test_caret('trim', 1,1,  cr+'ab '+cr+'12',  1,1);
925   test_caret('-trim', 6,2, cr+'ab '+cr+'12',  6,2);
926   test_redo_exp('+del(2)',       cr+'ab   12',     6,2);
927   test_undo_exp('-del(2)',    cr+'ab '+cr+'12', 6,2);
928   test_caret('trim(2)', 1,1, cr+'ab '+cr+'12', 1,1);
929   test_redo_exp('+del(3)',       cr+'ab   12',     6,2);  // redo with mis-place caret
930 
931 end;
932 
933 procedure TTestTrimSpace.TestInUndoBlock;
934 type TUpdateMode = (umNone, umOuter, umInner);
935 var UpdateMode: TUpdateMode;
936 
937   procedure BeginUndoBlock;
938   begin
939     if UpdateMode = umOuter then SynEdit.BeginUpdate;
940     SynEdit.BeginUndoBlock{$IFDEF SynUndoDebugBeginEnd}('test'){$ENDIF};
941     if UpdateMode = umInner then SynEdit.BeginUpdate;
942   end;
943 
944   procedure EndUndoBlock;
945   begin
946     if UpdateMode = umInner then SynEdit.EndUpdate;
947     SynEdit.EndUndoBlock{$IFDEF SynUndoDebugBeginEnd}('test'){$ENDIF};
948     if UpdateMode = umOuter then SynEdit.EndUpdate;
949   end;
950 
951   procedure DoTestInUndoBlock;
952   begin
953     ReCreateEdit;
954     SynEdit.Options := [eoTrimTrailingSpaces, eoAutoIndent, eoScrollPastEol]; // eoGroupUndo
955     SynEdit.TrimSpaceType := settLeaveLine;
956     SetLines(['abc d', 'mno', 'xyz', '']);
957     SetCaret(1,1);
958     // need to add space later, so it is regocnized as trailing
959 
960     BeginUndoBlock;
961       SynEdit.TextBetweenPointsEx[point(5,1), point(6,1), scamEnd] := ''; // delete d
962       SynEdit.TextBetweenPointsEx[point(4,2), point(4,2), scamEnd] := ' '; // add space
963     EndUndoBlock;
964     TestIsFullText ('modified after block', ['abc', 'mno ', 'xyz', '']);
965     TestIsCaret('modified after block', 5,2);
966 
967     SynEdit.Undo;
968     TestIsFullText ('Undone', ['abc d', 'mno', 'xyz', '']);
969     TestIsCaret('UnDone', 1,1);
970 
971     SynEdit.Redo;
972     TestIsFullText ('Redone', ['abc', 'mno ', 'xyz', '']);
973     TestIsCaret('Redone', 5,2);
974 
975     SynEdit.Undo;
976     TestIsFullText ('Undone 2', ['abc d', 'mno', 'xyz', '']);
977     TestIsCaret('UnDone 2', 1,1);
978 
979     SynEdit.Redo;
980     TestIsFullText ('Redone 2', ['abc', 'mno ', 'xyz', '']);
981     TestIsCaret('Redone 2', 5,2);
982 
983   end;
984 
985 begin
986   UpdateMode := umNone;
987   PushBaseName('Without BeginUpdate');
988   DoTestInUndoBlock;
989 
990   UpdateMode := umInner;
991   PushBaseName('With BeginUpdate inside');
992   DoTestInUndoBlock;
993 
994   UpdateMode := umOuter;
995   PushBaseName('With BeginUpdate outside');
996   DoTestInUndoBlock;
997 
998   PopBaseName;
999 end;
1000 
1001 procedure TTestTrimSpace.TimmEdit;
1002 var
1003   i: Integer;
1004 begin
1005   ReCreateEdit;
1006   SynEdit.Options := [eoTrimTrailingSpaces, eoAutoIndent, eoScrollPastEol]; // eoGroupUndo
1007   SynEdit.TrimSpaceType := settLeaveLine;
1008   SetLines(['', '', '', 'xyz', '']);
1009   SetCaret(1,2);
1010   for i := 1 to 7 do
1011     SynEdit.CommandProcessor(ecChar, ' ', nil);
1012   SetCaret(3,2);
1013   SynEdit.CommandProcessor(ecChar, '/', nil);
1014   TestIsFullText ('spaces after edit', ['', '  /     ', '', 'xyz', '']);
1015 
1016 end;
1017 
1018 
1019 
1020 initialization
1021 
1022   RegisterTest(TTestTrimSpace);
1023 end.
1024 
1025