1 unit TestBasicSynEdit;
2 
3 (* TODO:
4    - TestEditEmpty:
5      Test with different sets of VirtualViews (with/without trimming (enabled/module present at all)
6 *)
7 
8 {$mode objfpc}{$H+}
9 
10 interface
11 
12 uses
13   Classes, SysUtils, testregistry, LCLProc, LCLType, Forms, TestBase, SynEdit,
14   SynEditTextTrimmer, SynEditKeyCmds, LazSynEditText, SynEditPointClasses,
15   SynEditMiscClasses, SynEditTypes, SynEditMiscProcs;
16 
17 type
18 
19   { TTestBasicSynEdit }
20 
21   TTestBasicSynEdit = class(TTestBase)
22   private
23     InsertFlag: Boolean;
24     TrimType: TSynEditStringTrimmingType;
25     TrimEnabled: Boolean;
26   private
27     AllowPastEOL, FPersistSel: Boolean;
28     FDoInit: procedure of object;
TStringArraynull29     FTestLines: function: TStringArray of object;
30     Procedure DoInit1;
TestLines1null31     function TestLines1: TStringArray;
TestLines2null32     function TestLines2: TStringArray;
33   private
34     FTestCommand:TSynEditorCommand;
35     procedure TestCommand(Name:String; X, Y: Integer;
36                            ExpX1, ExpY1: Integer; Repl: Array of const);
37     procedure TestCommand(Name:String; X, Y, X2, Y2: Integer;
38                            ExpX1, ExpY1, ExpX2, ExpY2: Integer;
39                            Repl: Array of const);
40     procedure TestCommand(Name:String; X, Y: Integer;
41                            ExpX1, ExpY1: Integer; Repl: Array of const;
42                            ExpX2, ExpY2: Integer; Repl2: Array of const);
43   protected
TestMaxLeftProcnull44     function TestMaxLeftProc: Integer;
45     procedure ReCreateEdit; reintroduce;
46   published
47     procedure TestEditEmpty;
48     procedure TestEditTabs;
49     procedure TestEditEcChar;
50     procedure TestPhysicalLogical; // TODO adjust to char tests
51     procedure TestPhysicalLogicalCaretMove;
52     procedure TestLogicalAdjust;
53     procedure TestCaretObject;
54     procedure TestCaretAutoMove;
55     procedure TestCaretEcDeleteWord;
56     procedure TestCaretEcDeleteLastWord;
57     procedure TestCaretEcDeleteEOL;
58     procedure TestWordBreaker;
59     procedure TestSearchReplace;
60     procedure TestMoveLineUpDown;
61   end;
62 
63 implementation
64 
65 procedure TTestBasicSynEdit.DoInit1;
66 begin
67   InsertFlag := False;
68   TrimEnabled := False;;
69   ReCreateEdit;
70   if AllowPastEOL
71   then SynEdit.Options := SynEdit.Options + [eoScrollPastEol]
72   else SynEdit.Options := SynEdit.Options - [eoScrollPastEol];
73   if FPersistSel
74   then SynEdit.Options2 := SynEdit.Options2 + [eoPersistentBlock]
75   else SynEdit.Options2 := SynEdit.Options2 - [eoPersistentBlock];
76   SynEdit.TabWidth := 7;
77   SetLines(FTestLines());
78 end;
79 
TestLines1null80 function TTestBasicSynEdit.TestLines1: TStringArray;
81 begin
82   SetLength(Result, 16);
83               // 1    6    11 14
84   Result[0]  := 'Some text to test';                 //1
85               // 1   5   9
86   Result[1]  := 'Foo bar abc';                       // 2
87               //   8     14   19   24
88   Result[2]  := #9'Other line with tab';             // 3
89               // 1      8  11  15
90   Result[3]  := 'tab'#9'in the middle';              // 4
91               // 1       9  12  16     23   28
92   Result[4]  := 'tab'#9' in the middle with space';  // 5
93               // 1       9   13 16
94   Result[5]  := 'umlaute äää in text';               // 6
95   Result[6]  := 'normal line';
96               //    4         14     21     28  32
97   Result[7]  := '   untrimmed spaces around line   '; // 8
98   Result[8]  := 'normal line';
99               //   8      15      22  26
100   Result[9]  := #9'tab'#9'only'#9'line'#9;            // 10
101               // 1      8
102   Result[10] := 'normal line';
103   Result[11] := '';                                    // 12 (empty)
104   Result[12] := 'normal line';
105   Result[13] := '     '; // space only empty line      // 14
106   Result[14] := 'normal line';
107   Result[15] := '';
108 end;
109 
TestLines2null110 function TTestBasicSynEdit.TestLines2: TStringArray;
111 begin
112   SetLength(Result, 7);
113               // 1    6    11 14
114   Result[0]  := 'abc def ghi'; // 1
115   Result[1]  := 'ABC DEF GHI'; // 2
116   Result[2]  := '123 456 789'; // 3
117   Result[3]  := 'mno pqr stu'; // 4
118   Result[4]  := 'MNO PQR STU';  //5
119   Result[5]  := 'xyz XYZ vw';
120   Result[6] := '';
121 end;
122 
123 procedure TTestBasicSynEdit.TestCommand(Name: String; X, Y: Integer; ExpX1,
124   ExpY1: Integer; Repl: array of const);
125 begin
126   FDoInit();
127   SetCaretPhys(X,Y);
128   SynEdit.CommandProcessor(FTestCommand, '', nil);
129   TestIsCaretPhys(Name + '(1st)', ExpX1, ExpY1);
130   TestIsFullText(Name +  '(1st)', FTestLines(), Repl);
131 end;
132 
133 procedure TTestBasicSynEdit.TestCommand(Name: String; X, Y, X2, Y2: Integer;
134   ExpX1, ExpY1, ExpX2, ExpY2: Integer; Repl: array of const);
135 begin
136   FDoInit();
137   SetCaretAndSel(X,Y, x2,y2);
138   SynEdit.CommandProcessor(FTestCommand, '', nil);
139   TestIsCaretAndSel(Name + '(1st)', ExpX1, ExpY1, ExpX2, ExpY2);
140   TestIsFullText(Name +  '(1st)', FTestLines(), Repl);
141 end;
142 
143 procedure TTestBasicSynEdit.TestCommand(Name: String; X, Y: Integer; ExpX1,
144   ExpY1: Integer; Repl: array of const; ExpX2, ExpY2: Integer;
145   Repl2: array of const);
146 begin
147     TestCommand(Name, X, Y, ExpX1, ExpY1, Repl);
148     SynEdit.CommandProcessor(FTestCommand, '', nil);
149     TestIsCaretPhys(Name + '(2nd)', ExpX2, ExpY2);
150     TestIsFullText(Name +  '(2nd)', FTestLines(), Repl2);
151 end;
152 
TestMaxLeftProcnull153 function TTestBasicSynEdit.TestMaxLeftProc: Integer;
154 begin
155   Result := 6000;
156 end;
157 
158 procedure TTestBasicSynEdit.ReCreateEdit;
159 begin
160   inherited ReCreateEdit;
161   SynEdit.InsertMode := InsertFlag;
162   SynEdit.TrimSpaceType := TrimType;
163   if TrimEnabled then
164     SynEdit.Options := SynEdit.Options + [eoTrimTrailingSpaces]
165   else
166     SynEdit.Options := SynEdit.Options - [eoTrimTrailingSpaces];
167 
168 end;
169 
170 procedure TTestBasicSynEdit.TestEditEmpty;
171   procedure CheckText(aName: String; ExpText: String; ExpLines: Integer);
172   var
173     s: String;
174   begin
175     AssertEquals(BaseTestName + aName+' Count', ExpLines, SynEdit.Lines.Count);
176     // TestIsText (without Views => just real text)
177     // TestIsFullText (with Views => eg trimmed spaces)
178     s:='';
179     if ExpLines > 0 then
180       s := LineEnding;
181     TestIsText(aName+' Text', ExpText+s);
182   end;
183   procedure DoChecks;
184   begin
185     ReCreateEdit;
186     CheckText('Empty', '', 0);
187     SynEdit.CommandProcessor(ecChar, 'x', nil);
188     CheckText('After Insert "x"', 'x', 1);
189 
190     ReCreateEdit;
191     SynEdit.CommandProcessor(ecChar, ' ', nil);
192     if TrimEnabled then begin
193       CheckText('After Insert <space>', '', 1);
194       if TrimType = settIgnoreAll then begin
195         TestIsFullText('After Insert <space> (FullText)', ''+LineEnding);
196         TestIsCaret('After Insert <space>', 2,1);
197       end else begin
198         TestIsFullText('After Insert <space> (FullText)', ' '+LineEnding);
199         TestIsCaret('After Insert <space>', 2,1);
200       end;
201     end else begin
202       CheckText('After Insert <space>', ' ', 1);
203       TestIsFullText('After Insert <space> (FullText)', ' '+LineEnding);
204       TestIsCaret('After Insert <space>', 2,1);
205     end;
206 
207     ReCreateEdit;
208     CheckText('Empty', '', 0);
209     SynEdit.CommandProcessor(ecDeleteChar, '', nil);
210     CheckText('After ecDeleteChar', '', 0);
211 
212     ReCreateEdit;
213     SynEdit.CommandProcessor(ecDeleteLastChar, '', nil);
214     CheckText('After ecDeleteLastChar', '', 0);
215 
216     ReCreateEdit;
217     SynEdit.CommandProcessor(ecDeleteWord, '', nil);
218     CheckText('After ecDeleteWord', '', 0);
219 
220     ReCreateEdit;
221     SynEdit.CommandProcessor(ecDeleteLastWord, '', nil);
222     CheckText('After ecDeleteLastWord', '', 0);
223 
224     ReCreateEdit;
225     SynEdit.CommandProcessor(ecInsertLine, '', nil);
226     CheckText('After ecInsertLine', LineEnding, 2);
227 
228     ReCreateEdit;
229     SynEdit.CommandProcessor(ecLineBreak, '', nil);
230     CheckText('After ecLineBreak', LineEnding, 2);
231 
232   end;
233 begin
234   FPersistSel := False;
235   TrimEnabled := True;
236   TrimType := settEditLine;
237   PushBaseName('Trim=EditLine');
238     PushBaseName('InsertMode');
239       InsertFlag := True;
240       DoChecks;
241     PopPushBaseName('OverwriteMode');
242       InsertFlag := False;
243       DoChecks;
244     PopBaseName;
245 
246   TrimType := settIgnoreAll;
247   PopPushBaseName('Trim=IgnoreAll');
248     PushBaseName('InsertMode');
249       InsertFlag := True;
250       DoChecks;
251     PopPushBaseName('OverwriteMode');
252       InsertFlag := False;
253       DoChecks;
254     PopBaseName;
255 
256   TrimEnabled := False;
257   PopPushBaseName('Trim=Disabled');
258     PushBaseName('InsertMode');
259       InsertFlag := True;
260       DoChecks;
261     PopPushBaseName('OverwriteMode');
262       InsertFlag := False;
263       DoChecks;
264     PopBaseName;
265 
266 end;
267 
268 procedure TTestBasicSynEdit.TestEditTabs;
269 begin
270   FPersistSel := False;
271   ReCreateEdit;
272   // witout eoAutoIndent
273   SynEdit.Options  := SynEdit.Options
274                     - [eoTabIndent, eoTabsToSpaces, eoSpacesToTabs, eoAutoIndent, eoSmartTabs, eoSmartTabDelete];
275   SynEdit.TabWidth := 4;
276   SetLines(['  abc', #9'abcde', '']);
277   SetCaret(2, 2); // after tab
278   TestIsCaretPhys('Before delete tab', 5, 2);
279   SynEdit.CommandProcessor(ecDeleteLastChar, '', nil);
280 
281   TestIsCaret('After delete tab', 1, 2);
282   TestIsCaretPhys('After delete tab', 1, 2);
283   TestIsText('After delete tab', ['  abc', 'abcde', '']);
284 
285   ReCreateEdit;
286   // with eoAutoIndent
287   SynEdit.Options  := SynEdit.Options + [eoSmartTabs, eoSmartTabDelete, eoAutoIndent]
288                     - [eoTabIndent, eoTabsToSpaces, eoSpacesToTabs];
289   SynEdit.TabWidth := 4;
290   SetLines(['  abc', #9'abcde', '']);
291   SetCaret(2, 2); // after tab
292   TestIsCaretPhys('Before delete tab', 5, 2);
293   SynEdit.CommandProcessor(ecDeleteLastChar, '', nil);
294 
295   // reuqired indent is filled up with spaces
296   TestIsCaret('After delete tab (smart)', 3, 2);
297   TestIsCaretPhys('After delete tab (smart)', 3, 2);
298   TestIsText('After delete tab (smart)', ['  abc', '  abcde', '']);
299 
300 end;
301 
302 procedure TTestBasicSynEdit.TestEditEcChar;
303 var
304   CaretPEol: Boolean;
305 
TestText1null306   function TestText1: TStringArray;
307   begin
308     SetLength(Result, 4);
309     Result[0] := 'abc';
310     Result[1] := 'öbc';
311     Result[2] := #9'abc';
312     Result[3] := '';
313   end;
314   procedure InitEdit;
315   begin
316     ReCreateEdit;
317     SynEdit.Options := [];
318     if CaretPEol then SynEdit.Options := SynEdit.Options + [eoScrollPastEol];
319     SynEdit.TabWidth := 6;
320     SetLines(TestText1);
321   end;
322 
323   // All Logical pos
324   Procedure DoTest(ATestName: String; StartX, StartY: Integer;
325                    Char1: String; ExpX1, ExpY1: Integer; Repl1: Array of const;
326                    Char2: String; ExpX2, ExpY2: Integer; Repl2: Array of const;
327                    AStartIsPhys: Boolean = False
328                   );
329   begin
330     BaseTestName := Format('%s -Ins=%s - PastEol=%s - StartXY=(%d,%d) - ',
331                            [ATestName, dbgs(InsertFlag), dbgs(CaretPEol), StartX, StartY]);
332     InitEdit;
333 
334     if AStartIsPhys
335     then SetCaretPhys(StartX, StartY)
336     else SetCaret(StartX, StartY);
337 
338     SynEdit.TestTypeText(Char1);
339     TestIsCaret('After Char 1', ExpX1, ExpY1);
340     TestIsText ('After Char 1', TestText1, Repl1);
341 
342     SynEdit.TestTypeText(Char2);
343     TestIsCaret('After Char 2', ExpX2, ExpY2);
344     TestIsText ('After Char 2', TestText1, Repl2);
345 
346     SynEdit.Undo;
347     TestIsCaret('Undo 1', ExpX1, ExpY1);
348     TestIsText ('Undo 1', TestText1, Repl1);
349 
350     SynEdit.Redo;
351     TestIsCaret('Redo 1', ExpX2, ExpY2);
352     TestIsText ('Redo 1', TestText1, Repl2);
353 
354     SynEdit.Undo;
355     TestIsCaret('Undo 2a', ExpX1, ExpY1);
356     TestIsText ('Undo 2a', TestText1, Repl1);
357 
358     SynEdit.Undo;
359     if AStartIsPhys
360     then TestIsCaretPhys('Undo 2b', StartX, StartY)
361     else TestIsCaret('Undo 2b', StartX, StartY);
362     TestIsText ('Undo 2b', TestText1);
363 
364     SynEdit.Redo;
365     TestIsCaret('Redo 2a', ExpX1, ExpY1);
366     TestIsText ('Redo 2a', TestText1, Repl1);
367 
368     SynEdit.Redo;
369     TestIsCaret('Redo 2b', ExpX2, ExpY2);
370     TestIsText ('Redo 2b', TestText1, Repl2);
371 
372     InitEdit;
373 
374     if AStartIsPhys
375     then SetCaretPhys(StartX, StartY)
376     else SetCaret(StartX, StartY);
377     SynEdit.TestTypeText(Char1);
378     SynEdit.TestTypeText(Char2);
379     TestIsCaret('After Char 1+2', ExpX2, ExpY2);
380     TestIsText ('After Char 1+2', TestText1, Repl2);
381 
382   end;
383 begin
384   FPersistSel := False;
385   TrimEnabled := False; // Trim has its own test
386 
387   // Testing ecChar. Tab is ecTab, so not included
388 
389   {%region  Normal Line}
390     {%region  Normal Line -- Normal Char}
391     InsertFlag := True;
392     CaretPEol := False;
393     DoTest('normal line - BOL',      1, 1,     'X', 2, 1, [1,'Xabc'],    'Y', 3,1, [1,'XYabc']);
394     DoTest('normal line - mid',      2, 1,     'X', 3, 1, [1,'aXbc'],    'Y', 4,1, [1,'aXYbc']);
395     DoTest('normal line - EOL',      4, 1,     'X', 5, 1, [1,'abcX'],    'Y', 6,1, [1,'abcXY']);
396     CaretPEol := True;
397     DoTest('normal line - EOL',      4, 1,     'X', 5, 1, [1,'abcX'],    'Y', 6,1, [1,'abcXY']);
398     DoTest('normal line - Past',     6, 1,     'X', 7, 1, [1,'abc  X'],  'Y', 8,1, [1,'abc  XY']);
399 
400     InsertFlag := False;
401     CaretPEol := False;
402     DoTest('normal line - BOL',      1, 1,     'X', 2, 1, [1,'Xbc'],     'Y', 3,1, [1,'XYc']);
403     DoTest('normal line - mid',      2, 1,     'X', 3, 1, [1,'aXc'],     'Y', 4,1, [1,'aXY']);
404     DoTest('normal line - mid',      3, 1,     'X', 4, 1, [1,'abX'],     'Y', 5,1, [1,'abXY']);
405     DoTest('normal line - EOL',      4, 1,     'X', 5, 1, [1,'abcX'],    'Y', 6,1, [1,'abcXY']);
406     CaretPEol := True;
407     DoTest('normal line - EOL',      4, 1,     'X', 5, 1, [1,'abcX'],    'Y', 6,1, [1,'abcXY']);
408     DoTest('normal line - Past',     6, 1,     'X', 7, 1, [1,'abc  X'],  'Y', 8,1, [1,'abc  XY']);
409     {%endregion}
410 
411     {%region  Normal Line -- Space (and char)}
412     InsertFlag := True;
413     CaretPEol := False;
414     DoTest('normal line - space,char - BOL',      1, 1,     ' ', 2, 1, [1,' abc'],    'Y', 3,1, [1,' Yabc']);
415     DoTest('normal line - space,char - mid',      2, 1,     ' ', 3, 1, [1,'a bc'],    'Y', 4,1, [1,'a Ybc']);
416     DoTest('normal line - space,char - EOL',      4, 1,     ' ', 5, 1, [1,'abc '],    'Y', 6,1, [1,'abc Y']);
417     CaretPEol := True;
418     DoTest('normal line - space,char - EOL',      4, 1,     ' ', 5, 1, [1,'abc '],    'Y', 6,1, [1,'abc Y']);
419     DoTest('normal line - space,char - Past',     6, 1,     ' ', 7, 1, [1,'abc   '],  'Y', 8,1, [1,'abc   Y']);
420 
421     InsertFlag := False;
422     CaretPEol := False;
423     DoTest('normal line - space,char - BOL',      1, 1,     ' ', 2, 1, [1,' bc'],     'Y', 3,1, [1,' Yc']);
424     DoTest('normal line - space,char - mid',      2, 1,     ' ', 3, 1, [1,'a c'],     'Y', 4,1, [1,'a Y']);
425     DoTest('normal line - space,char - mid',      3, 1,     ' ', 4, 1, [1,'ab '],     'Y', 5,1, [1,'ab Y']);
426     DoTest('normal line - space,char - EOL',      4, 1,     ' ', 5, 1, [1,'abc '],    'Y', 6,1, [1,'abc Y']);
427     CaretPEol := True;
428     DoTest('normal line - space,char - EOL',      4, 1,     ' ', 5, 1, [1,'abc '],    'Y', 6,1, [1,'abc Y']);
429     DoTest('normal line - space,char - Past',     6, 1,     ' ', 7, 1, [1,'abc   '],  'Y', 8,1, [1,'abc   Y']);
430     {%endregion}
431 
432     {%region  Normal Line -- Space}
433     InsertFlag := True;
434     CaretPEol := False;
435     DoTest('normal line - space - BOL',      1, 1,     ' ', 2, 1, [1,' abc'],    ' ', 3,1, [1,'  abc']);
436     DoTest('normal line - space - mid',      2, 1,     ' ', 3, 1, [1,'a bc'],    ' ', 4,1, [1,'a  bc']);
437     DoTest('normal line - space - EOL',      4, 1,     ' ', 5, 1, [1,'abc '],    ' ', 6,1, [1,'abc  ']);
438     CaretPEol := True;
439     DoTest('normal line - space - EOL',      4, 1,     ' ', 5, 1, [1,'abc '],    ' ', 6,1, [1,'abc  ']);
440     DoTest('normal line - space - Past',     6, 1,     ' ', 7, 1, [1,'abc   '],  ' ', 8,1, [1,'abc    ']);
441 
442     InsertFlag := False;
443     CaretPEol := False;
444     DoTest('normal line - space - BOL',      1, 1,     ' ', 2, 1, [1,' bc'],     ' ', 3,1, [1,'  c']);
445     DoTest('normal line - space - mid',      2, 1,     ' ', 3, 1, [1,'a c'],     ' ', 4,1, [1,'a  ']);
446     DoTest('normal line - space - mid',      3, 1,     ' ', 4, 1, [1,'ab '],     ' ', 5,1, [1,'ab  ']);
447     DoTest('normal line - space - EOL',      4, 1,     ' ', 5, 1, [1,'abc '],    ' ', 6,1, [1,'abc  ']);
448     CaretPEol := True;
449     DoTest('normal line - space - EOL',      4, 1,     ' ', 5, 1, [1,'abc '],    ' ', 6,1, [1,'abc  ']);
450     DoTest('normal line - space - Past',     6, 1,     ' ', 7, 1, [1,'abc   '],  ' ', 8,1, [1,'abc    ']);
451     {%endregion}
452 
453     {%region  Normal Line -- utf8 2 byte Char}
454     InsertFlag := True;
455     CaretPEol := False;
456     DoTest('normal line - 2byte utf8 - BOL',      1, 1,     'Ä', 3, 1, [1,'Äabc'],    'Ü', 5,1, [1,'ÄÜabc']);
457     DoTest('normal line - 2byte utf8 - mid',      2, 1,     'Ä', 4, 1, [1,'aÄbc'],    'Ü', 6,1, [1,'aÄÜbc']);
458     DoTest('normal line - 2byte utf8 - EOL',      4, 1,     'Ä', 6, 1, [1,'abcÄ'],    'Ü', 8,1, [1,'abcÄÜ']);
459     CaretPEol := True;
460     DoTest('normal line - 2byte utf8 - EOL',      4, 1,     'Ä', 6, 1, [1,'abcÄ'],    'Ü', 8,1, [1,'abcÄÜ']);
461     DoTest('normal line - 2byte utf8 - Past',     6, 1,     'Ä', 8, 1, [1,'abc  Ä'],  'Ü',10,1, [1,'abc  ÄÜ']);
462 
463     InsertFlag := False;
464     CaretPEol := False;
465     DoTest('normal line - 2byte utf8 - BOL',      1, 1,     'Ä', 3, 1, [1,'Äbc'],     'Ü', 5,1, [1,'ÄÜc']);
466     DoTest('normal line - 2byte utf8 - mid',      2, 1,     'Ä', 4, 1, [1,'aÄc'],     'Ü', 6,1, [1,'aÄÜ']);
467     DoTest('normal line - 2byte utf8 - mid',      3, 1,     'Ä', 5, 1, [1,'abÄ'],     'Ü', 7,1, [1,'abÄÜ']);
468     DoTest('normal line - 2byte utf8 - EOL',      4, 1,     'Ä', 6, 1, [1,'abcÄ'],    'Ü', 8,1, [1,'abcÄÜ']);
469     CaretPEol := True;
470     DoTest('normal line - 2byte utf8 - EOL',      4, 1,     'Ä', 6, 1, [1,'abcÄ'],    'Ü', 8,1, [1,'abcÄÜ']);
471     DoTest('normal line - 2byte utf8 - Past',     6, 1,     'Ä', 8, 1, [1,'abc  Ä'],  'Ü',10,1, [1,'abc  ÄÜ']);
472     {%endregion}
473 
474     {%region  Normal Line -- full width Char - 3 bytes}
475     // May change in future. overwrite only one char
476     InsertFlag := True;
477     CaretPEol := False;
478     DoTest('normal line - 3 byte full witdh - BOL',      1, 1,     'あ', 4, 1, [1,'あabc'],    '吾', 7,1, [1,'あ吾abc']);
479     DoTest('normal line - 3 byte full witdh - mid',      2, 1,     'あ', 5, 1, [1,'aあbc'],    '吾', 8,1, [1,'aあ吾bc']);
480     DoTest('normal line - 3 byte full witdh - EOL',      4, 1,     'あ', 7, 1, [1,'abcあ'],    '吾',10,1, [1,'abcあ吾']);
481     CaretPEol := True;
482     DoTest('normal line - 3 byte full witdh - EOL',      4, 1,     'あ', 7, 1, [1,'abcあ'],    '吾',10,1, [1,'abcあ吾']);
483     DoTest('normal line - 3 byte full witdh - Past',     6, 1,     'あ', 9, 1, [1,'abc  あ'],  '吾',12,1, [1,'abc  あ吾']);
484 
485     InsertFlag := False;
486     CaretPEol := False;
487     DoTest('normal line - 3 byte full witdh - BOL',      1, 1,     'あ', 4, 1, [1,'あbc'],     '吾', 7,1, [1,'あ吾c']);
488     DoTest('normal line - 3 byte full witdh - mid',      2, 1,     'あ', 5, 1, [1,'aあc'],     '吾', 8,1, [1,'aあ吾']);
489     DoTest('normal line - 3 byte full witdh - mid',      3, 1,     'あ', 6, 1, [1,'abあ'],     '吾', 9,1, [1,'abあ吾']);
490     DoTest('normal line - 3 byte full witdh - EOL',      4, 1,     'あ', 7, 1, [1,'abcあ'],    '吾',10,1, [1,'abcあ吾']);
491     CaretPEol := True;
492     DoTest('normal line - 3 byte full witdh - EOL',      4, 1,     'あ', 7, 1, [1,'abcあ'],    '吾',10,1, [1,'abcあ吾']);
493     DoTest('normal line - 3 byte full witdh - Past',     6, 1,     'あ', 9, 1, [1,'abc  あ'],  '吾',12,1, [1,'abc  あ吾']);
494     {%endregion}
495   {%endregion}
496 
497   {%region  Line with utf8 at start}
498     {%region   Normal Char}
499     InsertFlag := True;
500     CaretPEol := False;
501     DoTest('utf8 at start - BOL',      1, 2,     'X', 2, 2, [2,'Xöbc'],    'Y', 3,2, [2,'XYöbc']);
502     DoTest('utf8 at start - mid',      3, 2,     'X', 4, 2, [2,'öXbc'],    'Y', 5,2, [2,'öXYbc']);
503     DoTest('utf8 at start - EOL',      5, 2,     'X', 6, 2, [2,'öbcX'],    'Y', 7,2, [2,'öbcXY']);
504     CaretPEol := True;
505     DoTest('utf8 at start - EOL',      5, 2,     'X', 6, 2, [2,'öbcX'],    'Y', 7,2, [2,'öbcXY']);
506     DoTest('utf8 at start - Past',     7, 2,     'X', 8, 2, [2,'öbc  X'],  'Y', 9,2, [2,'öbc  XY']);
507 
508     InsertFlag := False;
509     CaretPEol := False;
510     DoTest('utf8 at start - BOL',      1, 2,     'X', 2, 2, [2,'Xbc'],     'Y', 3,2, [2,'XYc']);
511     DoTest('utf8 at start - mid',      3, 2,     'X', 4, 2, [2,'öXc'],     'Y', 5,2, [2,'öXY']);
512     DoTest('utf8 at start - mid',      4, 2,     'X', 5, 2, [2,'öbX'],     'Y', 6,2, [2,'öbXY']);
513     DoTest('utf8 at start - EOL',      5, 2,     'X', 6, 2, [2,'öbcX'],    'Y', 7,2, [2,'öbcXY']);
514     CaretPEol := True;
515     DoTest('utf8 at start - EOL',      5, 2,     'X', 6, 2, [2,'öbcX'],    'Y', 7,2, [2,'öbcXY']);
516     DoTest('utf8 at start - Past',     7, 2,     'X', 8, 2, [2,'öbc  X'],  'Y', 9,2, [2,'öbc  XY']);
517     {%endregion}
518 
519     {%region   utf8 2 byte Char}
520     InsertFlag := True;
521     CaretPEol := False;
522     DoTest('utf8 at start - 2byte utf8 - BOL',      1, 2,     'Ä', 3, 2, [2,'Äöbc'],    'Ü', 5,2, [2,'ÄÜöbc']);
523     DoTest('utf8 at start - 2byte utf8 - mid',      3, 2,     'Ä', 5, 2, [2,'öÄbc'],    'Ü', 7,2, [2,'öÄÜbc']);
524     DoTest('utf8 at start - 2byte utf8 - EOL',      5, 2,     'Ä', 7, 2, [2,'öbcÄ'],    'Ü', 9,2, [2,'öbcÄÜ']);
525     CaretPEol := True;
526     DoTest('utf8 at start - 2byte utf8 - EOL',      5, 2,     'Ä', 7, 2, [2,'öbcÄ'],    'Ü', 9,2, [2,'öbcÄÜ']);
527     DoTest('utf8 at start - 2byte utf8 - Past',     7, 2,     'Ä', 9, 2, [2,'öbc  Ä'],  'Ü',11,2, [2,'öbc  ÄÜ']);
528 
529     InsertFlag := False;
530     CaretPEol := False;
531     DoTest('utf8 at start - 2byte utf8 - BOL',      1, 2,     'Ä', 3, 2, [2,'Äbc'],     'Ü', 5,2, [2,'ÄÜc']);
532     DoTest('utf8 at start - 2byte utf8 - mid',      3, 2,     'Ä', 5, 2, [2,'öÄc'],     'Ü', 7,2, [2,'öÄÜ']);
533     DoTest('utf8 at start - 2byte utf8 - mid',      4, 2,     'Ä', 6, 2, [2,'öbÄ'],     'Ü', 8,2, [2,'öbÄÜ']);
534     DoTest('utf8 at start - 2byte utf8 - EOL',      5, 2,     'Ä', 7, 2, [2,'öbcÄ'],    'Ü', 9,2, [2,'öbcÄÜ']);
535     CaretPEol := True;
536     DoTest('utf8 at start - 2byte utf8 - EOL',      5, 2,     'Ä', 7, 2, [2,'öbcÄ'],    'Ü', 9,2, [2,'öbcÄÜ']);
537     DoTest('utf8 at start - 2byte utf8 - Past',     7, 2,     'Ä', 9, 2, [2,'öbc  Ä'],  'Ü',11,2, [2,'öbc  ÄÜ']);
538     {%endregion}
539 
540   {%endregion}
541 
542   {%region  Line with tab at start}
543     {%region   Normal Char}
544     InsertFlag := True;
545     CaretPEol := False;
546     // Phys start pos
547     DoTest('tab at start - BOL',       1, 3,     'X', 2, 3, [3,'X'#9'abc'],    'Y', 3,3, [3,'XY'#9'abc'], True);
548     DoTest('tab at start - after tab', 7, 3,     'X', 3, 3, [3,#9'Xabc'],      'Y', 4,3, [3,#9'XYabc'],   True);
549     DoTest('tab at start - in tab',    3, 3,     'X', 2, 3, [3,'X'#9'abc'],    'Y', 3,3, [3,'XY'#9'abc'], True);
550 
551     InsertFlag := False;
552     CaretPEol := False;
553     DoTest('tab at start - BOL',       1, 3,     'X', 2, 3, [3,'Xabc'],        'Y', 3,3, [3,'XYbc'],    True);
554     DoTest('tab at start - after tab', 7, 3,     'X', 3, 3, [3,#9'Xbc'],       'Y', 4,3, [3,#9'XYc'],   True);
555     DoTest('tab at start - in tab',    3, 3,     'X', 2, 3, [3,'Xabc'],        'Y', 3,3, [3,'XYbc'],    True);
556     {%endregion}
557   {%endregion}
558 
559   // TODO:  2 byte at EOL
560   // TODO Overwrite selection
561 end;
562 
563 procedure TTestBasicSynEdit.TestPhysicalLogical;
564 
LogPhysConvnull565   function LogPhysConv: TSynLogicalPhysicalConvertor;
566   begin
567     Result := SynEdit.ViewedTextBuffer.LogPhysConvertor;
568   end;
569 
570   procedure TestPhysLog(name: string; y, x: integer;
571     expX: integer; expCol: integer = -1;
572     expXcsRight: integer = -1; expColCsRight: integer = -1;
573     expXcsLtr: integer = -1; expColCsLtr: integer = -1;
574     expXcsRtl: integer = -1; expColCsRtl: integer = -1);
575   var
576     gotX, gotCol: Integer;
577     expDef: Integer;
578   begin
579     name := name + ' y='+inttostr(y)+' x='+inttostr(x);
580 
581     expDef := expX;
582     if expXcsLtr >= 0 then expDef := expXcsLtr;
583 
584     gotX := SynEdit.PhysicalToLogicalPos(Point(x, y)).x;
585     AssertEquals(name+'  PhysicalToLogicalPos', expDef, gotX);
586 
587     gotX := SynEdit.PhysicalToLogicalCol(SynEdit.Lines[y-1], y-1, x);
588     AssertEquals(name+'  PhysicalToLogicalCol', expDef, gotX);
589 
590 
591 
592     gotX := LogPhysConv.PhysicalToLogical(y-1, x, gotCol, cspLeft);
593     AssertEquals(name+'  c.PhysicalToLogical', expX, gotX);
594     if expCol >= 0 then
595       AssertEquals(name+'  c.PhysicalToLogical  COL', expCol, gotCol);
596 
597 
598     if expXcsRight >= 0 then begin
599       gotX := LogPhysConv.PhysicalToLogical(y-1, x, gotCol, cspRight);
600       AssertEquals(name+'  c.PhysicalToLogical csRight', expXcsRight, gotX);
601       if expColCsRight >= 0 then
602         AssertEquals(name+'  c.PhysicalToLogical csRight COL', expColCsRight, gotCol);
603     end;
604 
605 
606     if expXcsLtr >= 0 then begin
607       gotX := LogPhysConv.PhysicalToLogical(y-1, x, gotCol, cspFollowLtr);
608       AssertEquals(name+'  c.PhysicalToLogical csLtr', expXcsLtr, gotX);
609       if expColCsLtr >= 0 then
610         AssertEquals(name+'  c.PhysicalToLogical csLtr COL', expColCsLtr, gotCol);
611     end;
612 
613 
614     if expXcsRtl >= 0 then begin
615       gotX := LogPhysConv.PhysicalToLogical(y-1, x, gotCol, cspFollowRtl);
616       AssertEquals(name+'  c.PhysicalToLogical csRtl', expXcsRtl, gotX);
617       if expColCsRtl >= 0 then
618         AssertEquals(name+'  c.PhysicalToLogical csRtl COL', expColCsRtl, gotCol);
619     end;
620   end;
621 
622   procedure TestLogPhys(name: string; y, x, aCol: integer;
623     expX: integer; expXcsAfter: integer = -1; expXcsLtr: integer = -1; expXcsRtl: integer = -1);
624   var gotX: Integer;
625     expDef: Integer;
626   begin
627     name := name + ' y='+inttostr(y)+' x='+inttostr(x)+' c='+IntToStr(aCol);
628 
629     if aCol = 0 then begin
630       expDef := expX;
631       if expXcsLtr >= 0 then expDef := expXcsLtr; // default is now cslFollowLtr
632 
633       gotX := SynEdit.LogicalToPhysicalPos(Point(x, y)).x;
634       AssertEquals(name+'  LogicalToPhysicalPos', expDef, gotX);
635 
636       gotX := SynEdit.LogicalToPhysicalCol(SynEdit.Lines[y-1], y-1, x);
637       AssertEquals(name+'  LogicalToPhysicalCol', expDef, gotX);
638     end;
639 
640     gotX := LogPhysConv.LogicalToPhysical(y-1, x, aCol, cslBefore);
641     AssertEquals(name+'  c.LogicalToPhysical', expX, gotX);
642 
643     if expXcsAfter >= 0 then begin
644       gotX := LogPhysConv.LogicalToPhysical(y-1, x, aCol, cslAfter);
645       AssertEquals(name+'  c.LogicalToPhysical cslAfter', expXcsAfter, gotX);
646     end;
647 
648     if expXcsLtr >= 0 then begin
649       gotX := LogPhysConv.LogicalToPhysical(y-1, x, aCol, cslFollowLtr);
650       AssertEquals(name+'  c.LogicalToPhysical cslFollowLtr', expXcsLtr, gotX);
651     end;
652 
653     if expXcsRtl >= 0 then begin
654       gotX := LogPhysConv.LogicalToPhysical(y-1, x, aCol, cslFollowRtl);
655       AssertEquals(name+'  c.LogicalToPhysical cslFollowRtl', expXcsRtl, gotX);
656     end;
657   end;
658 
659 begin
660   FPersistSel := False;
661   ReCreateEdit;
662   SynEdit.TabWidth := 6;
663 
664   // Todo Log2Phys: test column is cut off
665 
666   SetLines(['abc',  ' ääX',  #9'mn',  'abc'#9'de',  #9'Xää.',  'ab'#9,  'あ吾' ]);
667 
668   TestLogPhys('simple line (abc)',           1,  1, 0,    1);
669   TestLogPhys('simple line (abc)',           1,  2, 0,    2);
670   TestLogPhys('simple line (abc)',           1,  4, 0,    4);
671   TestLogPhys('simple line (abc)',           1,  5, 0,    5);
672   TestLogPhys('simple line (abc)',           1,  6, 0,    6);
673   TestLogPhys('line with 2byte-char',        2,  1, 0,    1);
674   TestLogPhys('line with 2byte-char',        2,  2, 0,    2);
675   TestLogPhys('line with 2byte-char',        2,  4, 0,    3); // after ae
676   TestLogPhys('line with 2byte-char',        2,  6, 0,    4);
677   TestLogPhys('line with 2byte-char',        2,  7, 0,    5);
678   TestLogPhys('line with 2byte-char',        2,  8, 0,    6);
679   TestLogPhys('line with 2byte-char',        2, 11, 0,    9);
680   TestLogPhys('line with tab (start)',       3,  1, 0,    1);
681   TestLogPhys('line with tab (start)',       3,  2, 0,    7);
682   TestLogPhys('line with tab (middle)',      4,  3, 0,    3);
683   TestLogPhys('line with tab (middle)',      4,  4, 0,    4); // before tab
684   TestLogPhys('line with tab (middle)',      4,  4, 1,    5); // inside tab
685   TestLogPhys('line with tab (middle)',      4,  4, 2,    6); // inside tab
686   TestLogPhys('line with tab (middle)',      4,  5, 0,    7); // after tab
687   TestLogPhys('line with tab (middle)',      4,  6, 0,    8);
688   TestLogPhys('line with tab (middle)',      4,  9, 0,   11);
689   TestLogPhys('line with tab (start) + 2bc', 5,  1, 0,    1);
690   TestLogPhys('line with tab (start) + 2bc', 5,  2, 0,    7);
691   TestLogPhys('line with tab (start) + 2bc', 5,  3, 0,    8);
692   TestLogPhys('line with tab (start) + 2bc', 5,  5, 0,    9);
693   TestLogPhys('line with tab (end)',         6,  3, 0,    3);
694   TestLogPhys('line with tab (end)',         6,  4, 0,    7);
695   TestLogPhys('line with tab (end)',         6,  5, 0,    8);
696   TestLogPhys('line with tab (end)',         6,  3, 1,    4);
697   TestLogPhys('line with tab (end)',         6,  3, 2,    5);
698   TestLogPhys('line with tab (end)',         6,  3, 3,    6);
699   TestLogPhys('line with double-width/3byte',7,  1, 0,    1);
700   TestLogPhys('line with double-width/3byte',7,  1, 1,    2);
701   TestLogPhys('line with double-width/3byte',7,  4, 0,    3);
702   TestLogPhys('line with double-width/3byte',7,  4, 1,    4);
703   TestLogPhys('line with double-width/3byte',7,  7, 0,    5);
704 
705   TestPhysLog('simple line (abc)',     1,  1,   1);
706   TestPhysLog('simple line (abc)',     1,  2,   2);
707   TestPhysLog('simple line (abc)',     1,  4,   4);
708   TestPhysLog('simple line (abc)',     1,  5,   5);
709   TestPhysLog('simple line (abc)',     1,  6,   6);
710   TestPhysLog('line with 3byte-char',  2,  1,   1);
711   TestPhysLog('line with 3byte-char',  2,  2,   2);
712   TestPhysLog('line with 3byte-char',  2,  3,   4);
713   TestPhysLog('line with 3byte-char',  2,  4,   6);
714   TestPhysLog('line with 3byte-char',  2,  5,   7);
715   TestPhysLog('line with 3byte-char',  2,  6,   8);
716   TestPhysLog('line with 3byte-char',  2,  7,   9);
717   TestPhysLog('line with tab (start)', 3,  1,   1);
718   TestPhysLog('line with tab (start)', 3,  2,   1);
719   TestPhysLog('line with tab (start)', 3,  5,   1);
720   TestPhysLog('line with tab (start)', 3,  6,   1);
721   TestPhysLog('line with tab (start)', 3,  7,   2);
722   TestPhysLog('line with tab (start)', 3,  8,   3);
723   TestPhysLog('line with tab (start)', 3,  9,   4);
724   TestPhysLog('line with tab (start)', 3, 11,   6);
725   TestPhysLog('line with double-width/3byte', 7,  1,   1, 0);
726   TestPhysLog('line with double-width/3byte', 7,  2,   1, 1);
727   TestPhysLog('line with double-width/3byte', 7,  3,   4, 0);
728   TestPhysLog('line with double-width/3byte', 7,  4,   4, 1);
729   TestPhysLog('line with double-width/3byte', 7,  5,   7, 0);
730 
731   //abc def ghi // 2bytes per char
732 
733   (*  Order in String "123" / Order on Screen "321"
734     LogicalToPhys
735       Log = 1
736       <|321    L2p (csLeft)  => 1 // Log 1 is after  it's LEFT  neighbour (BOL)
737         321<|  L2p (csRight) => 4 // Log 1 is before it's RIGHT neighbour (char "1") // logical right, byte order in string
738       Log = 4
739       |>321    L2p (csLeft)  => 1 // Log 4 is after  it's LEFT  neighbour (char "3") // logical left, byte order in string
740         321|>  L2p (csRight) => 4 // Log 4 is before it's RIGHT neighbour (EOL)
741 
742     PhysToLog:
743       Phys = 1
744       <|321    L2p (csLeft)  => 1
745       |>321    L2p (csRight) => 4
746       Phys = 4
747         321<|  L2p (csLeft)  => 1
748         321|>  L2p (csRight) => 4
749 
750   *)
751 
752   SetLines(['شىه ايغ عتل', 'ABCشىه ايغ عتلDEF', 'abcشىه ايغ عتل', 'شىه ايغ عتلdef', '']);
753 
754   //                                                           B,  A,  L,  R
755   TestLogPhys('empty line',                   5,   1, 0,       1,  1,  1,  1);
756   TestLogPhys('empty line',                   5,   2, 0,       2,  2,  2,  2);
757 
758   TestLogPhys('bidi line (arab only)',        1,   1, 0,       1, 12,  1, 12);
759   TestLogPhys('bidi line (arab only)',        1,   3, 0,      11, 11, 11, 11);
760   TestLogPhys('bidi line (arab only)',        1,   5, 0,      10, 10, 10, 10);
761   TestLogPhys('bidi line (arab only)',        1,   7, 0,       9,  9,  9,  9);
762   TestLogPhys('bidi line (arab only)',        1,   8, 0,       8,  8,  8,  8); // after space
763   TestLogPhys('bidi line (arab only)',        1,  15, 0,       4,  4,  4,  4); // after space
764   TestLogPhys('bidi line (arab only)',        1,  19, 0,       2,  2,  2,  2);
765   TestLogPhys('bidi line (arab only)',        1,  21, 0,       1, 12, 12,  1); // at EOL
766   TestLogPhys('bidi line (arab only)',        1,  22, 0,      13, 13, 13, 13); // past eol
767   TestLogPhys('bidi line (arab only)',        1,  23, 0,      14, 14, 14, 14);
768 
769   TestLogPhys('bidi line (mixed arab/latin)', 2,   1, 0,       1,  1,  1,  1);
770   TestLogPhys('bidi line (mixed arab/latin)', 2,   4, 0,       4, 15,  4, 15); // after C
771   TestLogPhys('bidi line (mixed arab/latin)', 2,   6, 0,      14, 14, 14, 14); // 1 into arabic
772   TestLogPhys('bidi line (mixed arab/latin)', 2,  22, 0,       5,  5,  5,  5); // 1 before end arabic
773   TestLogPhys('bidi line (mixed arab/latin)', 2,  24, 0,       4, 15, 15,  4); // at end arabic
774   TestLogPhys('bidi line (mixed arab/latin)', 2,  25, 0,      16, 16, 16, 16); // after D
775   TestLogPhys('bidi line (mixed arab/latin)', 2,  27, 0,      18, 18, 18, 18); // at eol
776   TestLogPhys('bidi line (mixed arab/latin)', 2,  28, 0,      19, 19, 19, 19); // after eol
777 
778   //                                                     Lft      Rght     LTR      RTL
779   TestPhysLog('empty line',                 5,   1,      1, 0,    1, 0,    1, 0,    1, 0);
780   TestPhysLog('empty line',                 5,   2,      2, 0,    2, 0,    2, 0,    2, 0);
781 
782   TestPhysLog('bidi line (arab only)',      1,   1,      1, 0,   21, 0,    1, 0,   21, 0);
783   TestPhysLog('bidi line (arab only)',      1,   2,     19, 0,   19, 0,   19, 0,   19, 0);
784   TestPhysLog('bidi line (arab only)',      1,   3,     17, 0,   17, 0,   17, 0,   17, 0);
785   TestPhysLog('bidi line (arab only)',      1,   4,     15, 0,   15, 0,   15, 0,   15, 0); // before space
786   TestPhysLog('bidi line (arab only)',      1,   5,     14, 0,   14, 0,   14, 0,   14, 0); // after space
787   TestPhysLog('bidi line (arab only)',      1,  10,      5, 0,    5, 0,    5, 0,    5, 0);
788   TestPhysLog('bidi line (arab only)',      1,  11,      3, 0,    3, 0,    3, 0,    3, 0);
789   TestPhysLog('bidi line (arab only)',      1,  12,      1, 0,   21, 0,   21, 0,    1, 0); // at eol
790   TestPhysLog('bidi line (arab only)',      1,  13,     22, 0,   22, 0,   22, 0,   22, 0);
791   TestPhysLog('bidi line (arab only)',      1,  14,     23, 0,   23, 0,   23, 0,   23, 0);
792 
793   TestPhysLog('bidi line (mixed arab/latin)',2,  1,      1, 0,    1, 0,    1, 0,    1, 0);
794   TestPhysLog('bidi line (mixed arab/latin)',2,  4,      4, 0,   24, 0,    4, 0,   24, 0);
795   TestPhysLog('bidi line (mixed arab/latin)',2, 15,      4, 0,   24, 0,   24, 0,    4, 0);
796 end;
797 
798 procedure TTestBasicSynEdit.TestPhysicalLogicalCaretMove;
799 begin
800   SetLines([
801 '//',
802 '// X あsf//',
803 '//��X あsf//',
804 '//',
805 ''
806   ]);
807 
808   SynEdit.Options := SynEdit.Options + [eoKeepCaretX] - [eoScrollPastEol];
809 
810   SetCaretPhys(8, 2);
811   TestIsCaretPhys('(1st)', 8, 2);
812   TestIsCaret('(1st)', 9, 2);
813 
814   SynEdit.CommandProcessor(ecDown, '', nil);
815   TestIsCaretPhys('(1st)', 7, 3);
816   TestIsCaret('(1st)', 9, 3);
817   SynEdit.CommandProcessor(ecDown, '', nil);
818   TestIsCaretPhys('(1st)', 3, 4);
819 
820   SynEdit.CommandProcessor(ecUp, '', nil);
821   TestIsCaretPhys('(1st)', 7, 3);
822   TestIsCaret('(1st)', 9, 3);
823   SynEdit.CommandProcessor(ecUp, '', nil);
824   TestIsCaretPhys('(1st)', 8, 2);
825   TestIsCaret('(1st)', 9, 2);
826 
827 
828 end;
829 
830 procedure TTestBasicSynEdit.TestLogicalAdjust;
831 var
832   tb: TSynEditStrings;
833 begin
834   FPersistSel := False;
835   tb := SynEdit.TextBuffer;
836   // #$CC#$81 Combining
837   AssertEquals('LogicPosIsAtChar 1 ', True, tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 1)); // a
838   AssertEquals('LogicPosIsAtChar 2 ', True, tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 2)); // ü
839   AssertEquals('LogicPosIsAtChar 3 ', False,tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 3)); // mid ü
840   AssertEquals('LogicPosIsAtChar 4 ', True, tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 4)); // b
841   AssertEquals('LogicPosIsAtChar 5 ', False,tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 5)); // AT Combining
842   AssertEquals('LogicPosIsAtChar 6 ', False,tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 6)); // mid Combining
843   AssertEquals('LogicPosIsAtChar 7 ', True, tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 7)); // c
844 
845   AssertEquals('LogicPosIsAtChar 1 C', True, tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 1, [lpStopAtCodePoint])); // a
846   AssertEquals('LogicPosIsAtChar 2 C', True, tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 2, [lpStopAtCodePoint])); // ü
847   AssertEquals('LogicPosIsAtChar 3 C', False,tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 3, [lpStopAtCodePoint])); // mid ü
848   AssertEquals('LogicPosIsAtChar 4 C', True, tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 4, [lpStopAtCodePoint])); // b
849   AssertEquals('LogicPosIsAtChar 5 C', True ,tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 5, [lpStopAtCodePoint])); // AT Combining
850   AssertEquals('LogicPosIsAtChar 6 C', False,tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 6, [lpStopAtCodePoint])); // mid Combining
851   AssertEquals('LogicPosIsAtChar 7 C', True, tb.LogicPosIsAtChar('aüb'#$CC#$81'c', 7, [lpStopAtCodePoint])); // c
852 
853   // broken text
854   AssertEquals('LogicPosIsAtChar 1 Comb', True, tb.LogicPosIsAtChar(#$CC#$81'c', 1, []));
855   AssertEquals('LogicPosIsAtChar 1 Comb C', True, tb.LogicPosIsAtChar(#$CC#$81'c', 1, [lpStopAtCodePoint]));
856 
857 
858   AssertEquals('LogicPosAdjustToChar 1 ', 1, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 1)); // a
859   AssertEquals('LogicPosAdjustToChar 2 ', 2, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 2)); // ü
860   AssertEquals('LogicPosAdjustToChar 3 ', 2, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 3)); // mid ü
861   AssertEquals('LogicPosAdjustToChar 4 ', 4, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 4)); // b
862   AssertEquals('LogicPosAdjustToChar 5 ', 4, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 5)); // AT Combining
863   AssertEquals('LogicPosAdjustToChar 6 ', 4, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 6)); // mid Combining
864   AssertEquals('LogicPosAdjustToChar 7 ', 7, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 7)); // c
865 
866   AssertEquals('LogicPosAdjustToChar 1N ', 1, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 1, [lpAdjustToNext])); // a
867   AssertEquals('LogicPosAdjustToChar 2N ', 2, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 2, [lpAdjustToNext])); // ü
868   AssertEquals('LogicPosAdjustToChar 3N ', 4, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 3, [lpAdjustToNext])); // mid ü
869   AssertEquals('LogicPosAdjustToChar 4N ', 4, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 4, [lpAdjustToNext])); // b
870   AssertEquals('LogicPosAdjustToChar 5N ', 7, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 5, [lpAdjustToNext])); // AT Combining
871   AssertEquals('LogicPosAdjustToChar 6N ', 7, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 6, [lpAdjustToNext])); // mid Combining
872   AssertEquals('LogicPosAdjustToChar 7N ', 7, tb.LogicPosAdjustToChar('aüb'#$CC#$81'ü', 7, [lpAdjustToNext])); // ü
873   AssertEquals('LogicPosAdjustToChar 8N ', 7, tb.LogicPosAdjustToChar('aüb'#$CC#$81'ü', 8, [lpAdjustToNext])); // mid ü
874   AssertEquals('LogicPosAdjustToChar 8NE', 9, tb.LogicPosAdjustToChar('aüb'#$CC#$81'ü', 8, [lpAdjustToNext, lpAllowPastEol])); // mid ü
875 
876   AssertEquals('LogicPosAdjustToChar 1 C', 1, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 1, [lpStopAtCodePoint])); // a
877   AssertEquals('LogicPosAdjustToChar 2 C', 2, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 2, [lpStopAtCodePoint])); // ü
878   AssertEquals('LogicPosAdjustToChar 3 C', 2, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 3, [lpStopAtCodePoint])); // mid ü
879   AssertEquals('LogicPosAdjustToChar 4 C', 4, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 4, [lpStopAtCodePoint])); // b
880   AssertEquals('LogicPosAdjustToChar 5 C', 5, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 5, [lpStopAtCodePoint])); // AT Combining
881   AssertEquals('LogicPosAdjustToChar 6 C', 5, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 6, [lpStopAtCodePoint])); // mid Combining
882   AssertEquals('LogicPosAdjustToChar 7 C', 7, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 7, [lpStopAtCodePoint])); // c
883 
884   AssertEquals('LogicPosAdjustToChar 1N C', 1, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 1, [lpStopAtCodePoint, lpAdjustToNext])); // a
885   AssertEquals('LogicPosAdjustToChar 2N C', 2, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 2, [lpStopAtCodePoint, lpAdjustToNext])); // ü
886   AssertEquals('LogicPosAdjustToChar 3N C', 4, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 3, [lpStopAtCodePoint, lpAdjustToNext])); // mid ü
887   AssertEquals('LogicPosAdjustToChar 4N C', 4, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 4, [lpStopAtCodePoint, lpAdjustToNext])); // b
888   AssertEquals('LogicPosAdjustToChar 5N C', 5, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 5, [lpStopAtCodePoint, lpAdjustToNext])); // AT Combining
889   AssertEquals('LogicPosAdjustToChar 6N C', 7, tb.LogicPosAdjustToChar('aüb'#$CC#$81'c', 6, [lpStopAtCodePoint, lpAdjustToNext])); // mid Combining
890   AssertEquals('LogicPosAdjustToChar 7N C', 7, tb.LogicPosAdjustToChar('aüb'#$CC#$81'ü', 7, [lpStopAtCodePoint, lpAdjustToNext])); // ü
891   AssertEquals('LogicPosAdjustToChar 8N C', 7, tb.LogicPosAdjustToChar('aüb'#$CC#$81'ü', 8, [lpStopAtCodePoint, lpAdjustToNext])); // mid ü
892   AssertEquals('LogicPosAdjustToChar 8NE C', 9, tb.LogicPosAdjustToChar('aüb'#$CC#$81'ü', 8, [lpStopAtCodePoint, lpAdjustToNext, lpAllowPastEol])); // mid ü
893 
894   // broken text
895   AssertEquals('LogicPosAdjustToChar 1 Comb', 1, tb.LogicPosAdjustToChar(#$CC#$81'c', 1, []));
896   AssertEquals('LogicPosAdjustToChar 2 Comb', 1, tb.LogicPosAdjustToChar(#$CC#$81'c', 2, []));
897   AssertEquals('LogicPosAdjustToChar 1 Comb N', 1, tb.LogicPosAdjustToChar(#$CC#$81'c', 1, [lpAdjustToNext]));
898   AssertEquals('LogicPosAdjustToChar 2 Comb N', 3, tb.LogicPosAdjustToChar(#$CC#$81'c', 2, [lpAdjustToNext]));
899   AssertEquals('LogicPosAdjustToChar 1 Comb', 1, tb.LogicPosAdjustToChar(#$CC#$81'c', 1, [lpStopAtCodePoint]));
900   AssertEquals('LogicPosAdjustToChar 2 Comb', 1, tb.LogicPosAdjustToChar(#$CC#$81'c', 2, [lpStopAtCodePoint]));
901   AssertEquals('LogicPosAdjustToChar 1 Comb N', 1, tb.LogicPosAdjustToChar(#$CC#$81'c', 1, [lpStopAtCodePoint, lpAdjustToNext]));
902   AssertEquals('LogicPosAdjustToChar 2 Comb N', 3, tb.LogicPosAdjustToChar(#$CC#$81'c', 2, [lpStopAtCodePoint, lpAdjustToNext]));
903 
904 
905   AssertEquals('LogicPosAddChars  1, 1  ', 2, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 1, 1)); // a
906   AssertEquals('LogicPosAddChars  2, 1  ', 4, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 2, 1)); // ü
907   AssertEquals('LogicPosAddChars  3, 1  ', 4, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 3, 1)); // mid ü
908   AssertEquals('LogicPosAddChars  4, 1  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 4, 1)); // b
909   AssertEquals('LogicPosAddChars  5, 1  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 5, 1)); // comb
910   AssertEquals('LogicPosAddChars  6, 1  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 6, 1)); // mid
911   AssertEquals('LogicPosAddChars  7, 1  ', 8, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 7, 1)); // c
912   AssertEquals('LogicPosAddChars  8, 1  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 8, 1)); // ü
913   AssertEquals('LogicPosAddChars  9, 1  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 9, 1)); // mid ü
914   AssertEquals('LogicPosAddChars 10, 1  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',10, 1)); // ü
915   AssertEquals('LogicPosAddChars 11, 1  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',11, 1)); // mid ü
916   AssertEquals('LogicPosAddChars 10, 1 E',12, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',10, 1, [lpAllowPastEol])); // ü
917   AssertEquals('LogicPosAddChars 11, 1 E',12, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',11, 1, [lpAllowPastEol])); // mid ü
918 
919   AssertEquals('LogicPosAddChars  1, 2  ', 4, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 1, 2)); // a
920   AssertEquals('LogicPosAddChars  2, 2  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 2, 2)); // ü
921   AssertEquals('LogicPosAddChars  3, 2  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 3, 2)); // mid ü
922   AssertEquals('LogicPosAddChars  4, 2  ', 8, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 4, 2)); // b
923   AssertEquals('LogicPosAddChars  5, 2  ', 8, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 5, 2)); // comb
924   AssertEquals('LogicPosAddChars  6, 2  ', 8, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 6, 2)); // mid
925   AssertEquals('LogicPosAddChars  7, 2  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 7, 2)); // c
926   AssertEquals('LogicPosAddChars  8, 2  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 8, 2)); // ü
927   AssertEquals('LogicPosAddChars  9, 2  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 9, 2)); // mid ü
928   AssertEquals('LogicPosAddChars 10, 2  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',10, 2)); // ü
929   AssertEquals('LogicPosAddChars 11, 2  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',11, 2)); // mid ü
930   AssertEquals('LogicPosAddChars  8, 2 E',12, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 8, 2, [lpAllowPastEol])); // ü
931   AssertEquals('LogicPosAddChars  9, 2 E',12, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 9, 2, [lpAllowPastEol])); // mid ü
932   AssertEquals('LogicPosAddChars 10, 2 E',13, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',10, 2, [lpAllowPastEol])); // ü
933   AssertEquals('LogicPosAddChars 11, 2 E',13, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',11, 2, [lpAllowPastEol])); // mid ü
934 
935   AssertEquals('LogicPosAddChars  1, -1  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 1, -1)); // a
936   AssertEquals('LogicPosAddChars  2, -1  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 2, -1)); // ü
937 //AssertEquals('LogicPosAddChars  3, -1  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 3, -1)); // mid ü
938   AssertEquals('LogicPosAddChars  4, -1  ', 2, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 4, -1)); // b
939 //AssertEquals('LogicPosAddChars  5, -1  ', 2, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 5, -1)); // comb
940 //AssertEquals('LogicPosAddChars  6, -1  ', 2, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 6, -1)); // mid
941   AssertEquals('LogicPosAddChars  7, -1  ', 4, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 7, -1)); // c
942   AssertEquals('LogicPosAddChars  8, -1  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 8, -1)); // ü
943 //AssertEquals('LogicPosAddChars  9, -1  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 9, -1)); // mid ü
944   AssertEquals('LogicPosAddChars 10, -1  ', 8, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',10, -1)); // ü
945 //AssertEquals('LogicPosAddChars 11, -1  ', 8, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',11, -1)); // mid ü
946 
947   AssertEquals('LogicPosAddChars  1, -2  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 1, -2)); // a
948   AssertEquals('LogicPosAddChars  2, -2  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 2, -2)); // ü
949 //AssertEquals('LogicPosAddChars  3, -2  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 3, -2)); // mid ü
950   AssertEquals('LogicPosAddChars  4, -2  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 4, -2)); // b
951 //AssertEquals('LogicPosAddChars  5, -2  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 5, -2)); // comb
952 //AssertEquals('LogicPosAddChars  6, -2  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 6, -2)); // mid
953   AssertEquals('LogicPosAddChars  7, -2  ', 2, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 7, -2)); // c
954   AssertEquals('LogicPosAddChars  8, -2  ', 4, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 8, -2)); // ü
955 //AssertEquals('LogicPosAddChars  9, -2  ', 4, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 9, -2)); // mid ü
956   AssertEquals('LogicPosAddChars 10, -2  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',10, -2)); // ü
957 //AssertEquals('LogicPosAddChars 11, -2  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',11, -2)); // mid ü
958 
959 
960   AssertEquals('LogicPosAddChars  1, 1C  ', 2, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 1, 1, [lpStopAtCodePoint])); // a
961   AssertEquals('LogicPosAddChars  2, 1C  ', 4, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 2, 1, [lpStopAtCodePoint])); // ü
962   AssertEquals('LogicPosAddChars  3, 1C  ', 4, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 3, 1, [lpStopAtCodePoint])); // mid ü
963   AssertEquals('LogicPosAddChars  4, 1C  ', 5, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 4, 1, [lpStopAtCodePoint])); // b
964   AssertEquals('LogicPosAddChars  5, 1C  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 5, 1, [lpStopAtCodePoint])); // comb
965   AssertEquals('LogicPosAddChars  6, 1C  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 6, 1, [lpStopAtCodePoint])); // mid
966   AssertEquals('LogicPosAddChars  7, 1C  ', 8, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 7, 1, [lpStopAtCodePoint])); // c
967   AssertEquals('LogicPosAddChars  8, 1C  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 8, 1, [lpStopAtCodePoint])); // ü
968   AssertEquals('LogicPosAddChars  9, 1C  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 9, 1, [lpStopAtCodePoint])); // mid ü
969   AssertEquals('LogicPosAddChars 10, 1C  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',10, 1, [lpStopAtCodePoint])); // ü
970   AssertEquals('LogicPosAddChars 11, 1C  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',11, 1, [lpStopAtCodePoint])); // mid ü
971   AssertEquals('LogicPosAddChars 10, 1C E',12, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',10, 1, [lpAllowPastEol, lpStopAtCodePoint])); // ü
972   AssertEquals('LogicPosAddChars 11, 1C E',12, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',11, 1, [lpAllowPastEol, lpStopAtCodePoint])); // mid ü
973 
974   AssertEquals('LogicPosAddChars  1, 2C  ', 4, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 1, 2, [lpStopAtCodePoint])); // a
975   AssertEquals('LogicPosAddChars  2, 2C  ', 5, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 2, 2, [lpStopAtCodePoint])); // ü
976   AssertEquals('LogicPosAddChars  3, 2C  ', 5, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 3, 2, [lpStopAtCodePoint])); // mid ü
977   AssertEquals('LogicPosAddChars  4, 2C  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 4, 2, [lpStopAtCodePoint])); // b
978   AssertEquals('LogicPosAddChars  5, 2C  ', 8, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 5, 2, [lpStopAtCodePoint])); // comb
979   AssertEquals('LogicPosAddChars  6, 2C  ', 8, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 6, 2, [lpStopAtCodePoint])); // mid
980   AssertEquals('LogicPosAddChars  7, 2C  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 7, 2, [lpStopAtCodePoint])); // c
981   AssertEquals('LogicPosAddChars  8, 2C  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 8, 2, [lpStopAtCodePoint])); // ü
982   AssertEquals('LogicPosAddChars  9, 2C  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 9, 2, [lpStopAtCodePoint])); // mid ü
983   AssertEquals('LogicPosAddChars 10, 2C  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',10, 2, [lpStopAtCodePoint])); // ü
984   AssertEquals('LogicPosAddChars 11, 2C  ',10, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',11, 2, [lpStopAtCodePoint])); // mid ü
985   AssertEquals('LogicPosAddChars  8, 2C E',12, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 8, 2, [lpAllowPastEol, lpStopAtCodePoint])); // ü
986   AssertEquals('LogicPosAddChars  9, 2C E',12, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 9, 2, [lpAllowPastEol, lpStopAtCodePoint])); // mid ü
987   AssertEquals('LogicPosAddChars 10, 2C E',13, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',10, 2, [lpAllowPastEol, lpStopAtCodePoint])); // ü
988   AssertEquals('LogicPosAddChars 11, 2C E',13, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',11, 2, [lpAllowPastEol, lpStopAtCodePoint])); // mid ü
989 
990   AssertEquals('LogicPosAddChars  1, -1C  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 1, -1, [lpStopAtCodePoint])); // a
991   AssertEquals('LogicPosAddChars  2, -1C  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 2, -1, [lpStopAtCodePoint])); // ü
992 //AssertEquals('LogicPosAddChars  3, -1C  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 3, -1, [lpStopAtCodePoint])); // mid ü
993   AssertEquals('LogicPosAddChars  4, -1C  ', 2, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 4, -1, [lpStopAtCodePoint])); // b
994   AssertEquals('LogicPosAddChars  5, -1C  ', 4, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 5, -1, [lpStopAtCodePoint])); // comb
995 //AssertEquals('LogicPosAddChars  6, -1C  ', 4, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 6, -1, [lpStopAtCodePoint])); // mid
996   AssertEquals('LogicPosAddChars  7, -1C  ', 5, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 7, -1, [lpStopAtCodePoint])); // c
997   AssertEquals('LogicPosAddChars  8, -1C  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 8, -1, [lpStopAtCodePoint])); // ü
998 //AssertEquals('LogicPosAddChars  9, -1C  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 9, -1, [lpStopAtCodePoint])); // mid ü
999   AssertEquals('LogicPosAddChars 10, -1C  ', 8, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',10, -1, [lpStopAtCodePoint])); // ü
1000 //AssertEquals('LogicPosAddChars 11, -1C  ', 8, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',11, -1, [lpStopAtCodePoint])); // mid ü
1001 
1002   AssertEquals('LogicPosAddChars  1, -2C  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 1, -2, [lpStopAtCodePoint])); // a
1003   AssertEquals('LogicPosAddChars  2, -2C  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 2, -2, [lpStopAtCodePoint])); // ü
1004 //AssertEquals('LogicPosAddChars  3, -2C  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 3, -2, [lpStopAtCodePoint])); // mid ü
1005   AssertEquals('LogicPosAddChars  4, -2C  ', 1, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 4, -2, [lpStopAtCodePoint])); // b
1006   AssertEquals('LogicPosAddChars  5, -2C  ', 2, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 5, -2, [lpStopAtCodePoint])); // comb
1007 //AssertEquals('LogicPosAddChars  6, -2C  ', 2, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 6, -2, [lpStopAtCodePoint])); // mid
1008   AssertEquals('LogicPosAddChars  7, -2C  ', 4, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 7, -2, [lpStopAtCodePoint])); // c
1009   AssertEquals('LogicPosAddChars  8, -2C  ', 5, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 8, -2, [lpStopAtCodePoint])); // ü
1010 //AssertEquals('LogicPosAddChars  9, -2C  ', 5, tb.LogicPosAddChars('aüb'#$CC#$81'cüü', 9, -2, [lpStopAtCodePoint])); // mid ü
1011   AssertEquals('LogicPosAddChars 10, -2C  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',10, -2, [lpStopAtCodePoint])); // ü
1012 //AssertEquals('LogicPosAddChars 11, -2C  ', 7, tb.LogicPosAddChars('aüb'#$CC#$81'cüü',11, -2, [lpStopAtCodePoint])); // mid ü
1013 
1014 
1015 
1016 end;
1017 
1018 procedure TTestBasicSynEdit.TestCaretObject;
1019 var
1020   TestCaret, TestCaret2: TSynEditCaret;
1021   TestOrder: Integer;
1022   UseAdjustToNextChar, UseIncAdjustToNextChar: Boolean;
1023   UseAllowPastEOL, UseIncAllowPastEOL: Boolean;
1024   UseKeepCaretX: Boolean;
1025   UseLock: Boolean;
1026   UseChangeOnTouch: Boolean;
1027   UseIncAutoMoveOnEdit: Boolean;
1028   UseSkipTabs: Boolean;
1029   UseMaxLeft: Boolean;
1030 
1031 
1032   procedure CheckPhys(AName: String; ExpY, ExpX: Integer);
1033     procedure CheckEach;
1034     begin
1035       AssertEquals(AName + 'Phys.Y', ExpY, TestCaret.LinePos);
1036       AssertEquals(AName + 'Phys.X', ExpX, TestCaret.CharPos);
1037     end;
1038     procedure CheckPoint;
1039     begin
1040       AssertEquals(AName + 'Phys.XY.Y', ExpY, TestCaret.LineCharPos.Y);
1041       AssertEquals(AName + 'Phys.XY.X', ExpX, TestCaret.LineCharPos.x);
1042     end;
1043   begin
1044     if ExpX <= 0 then exit;
1045     AName := BaseTestName + ' ' + AName;
1046     if (TestOrder and 1) = 0 then begin
1047       CheckEach;
1048       CheckPoint;
1049     end else begin
1050       CheckPoint;
1051       CheckEach;
1052     end;
1053   end;
1054   procedure CheckIsAtChar(AName: String; ExpY, ExpX: Integer);
1055   begin
1056     if ExpX <= 0 then exit;
1057     AName := BaseTestName + ' ' + AName;
1058     if (TestOrder and 1) = 1 then exit; // Only one order
1059     AssertEquals(AName + 'IsAtLineChar',     True,  TestCaret.IsAtLineChar(point(ExpX, ExpY)));
1060     AssertEquals(AName + 'NOT IsAtLineChar', False, TestCaret.IsAtLineChar(point(ExpX+1, ExpY)));
1061   end;
1062 
1063   procedure CheckLog(AName: String; ExpY, ExpX, ExpOffs: Integer);
1064     procedure CheckEach;
1065     begin
1066       AssertEquals(AName + 'Log.Y', ExpY, TestCaret.LinePos);
1067       AssertEquals(AName + 'Log.X', ExpX, TestCaret.BytePos);
1068       AssertEquals(AName + 'Log.Offs', ExpOffs, TestCaret.BytePosOffset);
1069     end;
1070     procedure CheckPoint;
1071     begin
1072       AssertEquals(AName + 'Log.XY.Y', ExpY, TestCaret.LineBytePos.y);
1073       AssertEquals(AName + 'Log.XY.X', ExpX, TestCaret.LineBytePos.x);
1074     end;
1075   begin
1076     if ExpX <= 0 then exit;
1077     AName := BaseTestName + ' ' + AName;
1078     AName := BaseTestName + ' ' + AName;
1079     if (TestOrder and 1) = 0 then begin
1080       CheckEach;
1081       CheckPoint;
1082     end else begin
1083       CheckPoint;
1084       CheckEach;
1085     end;
1086   end;
1087   procedure CheckIsAtByte(AName: String; ExpY, ExpX, ExpOffs: Integer);
1088   begin
1089     if ExpX <= 0 then exit;
1090     AName := BaseTestName + ' ' + AName;
1091     if (TestOrder and 1) = 1 then exit; // Only one order
1092     AssertEquals(AName + 'IsAtLineChar',     True,  TestCaret.IsAtLineByte(point(ExpX, ExpY), ExpOffs));
1093     AssertEquals(AName + 'NOT IsAtLineByte', False, TestCaret.IsAtLineByte(point(ExpX+1, ExpY), ExpOffs));
1094     if ExpOffs = 0 then begin
1095       AssertEquals(AName + 'IsAtLineChar',     True,  TestCaret.IsAtLineByte(point(ExpX, ExpY)));
1096       AssertEquals(AName + 'NOT IsAtLineByte', False, TestCaret.IsAtLineByte(point(ExpX+1, ExpY)));
1097       AssertEquals(AName + 'NOT IsAtLineByte', False, TestCaret.IsAtLineByte(point(ExpX, ExpY), 1));
1098     end;
1099   end;
1100 
1101 
1102   procedure CheckLogPhys(AName: String; ExpY, ExpX, ExpLogX, ExpOffs:  Integer);
1103     procedure CheckAtPos;
1104       procedure CheckAtPosChar;
1105       begin
1106         TestCaret2.LineBytePos := point(1, 1);
1107         TestCaret2.LineCharPos := point(ExpX, ExpY);
1108         AssertTrue(AName + 'IsAtPos(Char)', TestCaret.IsAtPos(TestCaret2));
1109         TestCaret2.LineCharPos := point(1, ExpY-1);
1110         AssertFalse(AName + 'not IsAtPos(Char)', TestCaret.IsAtPos(TestCaret2));
1111       end;
1112       procedure CheckAtPosByte;
1113       begin
1114         TestCaret2.LineCharPos := point(1, 1);
1115         TestCaret2.LineBytePos := point(ExpLogX, ExpY);
1116         //TestCaret2.BytePosOffset := ExpOffs;
1117         if ExpOffs = 0 then // TODO
1118         AssertTrue(AName + 'IsAtPos(Byte)', TestCaret.IsAtPos(TestCaret2));
1119         TestCaret2.LineBytePos := point(1, ExpY-1);
1120         AssertFalse(AName + 'not IsAtPos(Byte)', TestCaret.IsAtPos(TestCaret2));
1121       end;
1122     begin
1123       if ((TestOrder and 1) = 0) and (ExpX > 0)    then CheckAtPosChar;
1124       if                             (ExpLogX > 0) then CheckAtPosByte;
1125       if ((TestOrder and 1) = 1) and (ExpX > 0)    then CheckAtPosChar;
1126     end;
1127     procedure CheckPos;
1128     begin
1129       if (TestOrder and 2) = 0 then begin
1130         if ExpX > 0    then CheckPhys(AName, ExpY, ExpX);
1131         if ExpLogX > 0 then CheckLog (AName, ExpY, ExpLogX, ExpOffs);
1132       end else begin
1133         if ExpLogX > 0 then CheckLog (AName, ExpY, ExpLogX, ExpOffs);
1134         if ExpX > 0    then CheckPhys(AName, ExpY, ExpX);
1135       end;
1136     end;
1137   begin
1138     if (TestOrder and 8) = 8 then
1139       CheckAtPos;
1140 
1141     if (TestOrder and 4) = 4 then
1142       CheckPos;
1143 
1144     if (TestOrder and 2) = 2 then begin
1145       CheckIsAtChar(AName, ExpY, ExpX);
1146       CheckIsAtByte(AName, ExpY, ExpLogX, ExpOffs);
1147     end else begin
1148       CheckIsAtByte(AName, ExpY, ExpLogX, ExpOffs);
1149       CheckIsAtChar(AName, ExpY, ExpX);
1150     end;
1151 
1152     if (TestOrder and 4) = 0 then
1153       CheckPos;
1154 
1155     if (TestOrder and 8) = 0 then
1156       CheckAtPos;
1157   end;
1158 
1159 
1160   Procedure DoOneTest(AName: String; AY, AX, ALogX, ALogOffs: Integer;
1161     ExpY, ExpX, ExpLogX, ExpLogOffs: Integer;
1162     // X,LogX,LogOffs, [X,LogX,LogOffs]
1163     AMoveHorizNext: array of integer; AMoveHorizPrev: array of integer;
1164     ExpMoveHorizFalse: Boolean = False;
1165     ALineForKeepX: Integer = -1; ExpNotKeptX: Integer = -1; ExpNotKeptLogX: Integer = -1
1166     );
1167     procedure DoOneSetLine(Y: Integer);
1168     begin
1169       if UseLock then TestCaret.Lock;
1170       if UseChangeOnTouch then TestCaret.ChangeOnTouch;
1171       TestCaret.LinePos := Y;
1172       if UseLock then TestCaret.Unlock;
1173     end;
1174     procedure DoOneSetChar(Y, X: Integer; ChangeToLine: Integer = -1);
1175     begin
1176       if UseLock then TestCaret.Lock;
1177       if UseChangeOnTouch then TestCaret.ChangeOnTouch;
1178       TestCaret.LineCharPos := point(X, Y);
1179       if ChangeToLine > 0 then TestCaret.LinePos := ChangeToLine;
1180       if UseLock then TestCaret.Unlock;
1181     end;
1182     procedure DoOneSetByte(Y, X, {%H-}O: Integer; ChangeToLine: Integer = -1);
1183     begin
1184       if UseLock then TestCaret.Lock;
1185       if UseChangeOnTouch then TestCaret.ChangeOnTouch;
1186       TestCaret.LineBytePos := point(X, Y);
1187       //TestCaret.BytePosOffset := O;
1188       if ChangeToLine > 0 then TestCaret.LinePos := ChangeToLine;
1189       if UseLock then TestCaret.Unlock;
1190     end;
1191     procedure DoOneMoveHoriz(C: Integer);
1192     begin
1193       if UseLock then TestCaret.Lock;
1194       if UseChangeOnTouch then TestCaret.ChangeOnTouch;
1195       AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(C));
1196       if UseLock then TestCaret.Unlock;
1197     end;
1198     procedure DoOneMoveHorizFromChar(C, Y, X: Integer);
1199     begin
1200       if UseLock then TestCaret.Lock;
1201       if UseChangeOnTouch then TestCaret.ChangeOnTouch;
1202       TestCaret.LineCharPos := point(X, Y);
1203       AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(C));
1204       if UseLock then TestCaret.Unlock;
1205     end;
1206     procedure DoOneMoveHorizFromByte(C, Y, X, {%H-}O: Integer);
1207     begin
1208       if UseLock then TestCaret.Lock;
1209       if UseChangeOnTouch then TestCaret.ChangeOnTouch;
1210       TestCaret.LineBytePos := point(X, Y);
1211       //TestCaret.BytePosOffset := O;
1212       AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(C));
1213       if UseLock then TestCaret.Unlock;
1214     end;
1215   begin
1216     DoOneSetChar(AY, AX);
1217     CheckLogPhys(AName + ' from CharPos',  ExpY,  ExpX,  ExpLogX, ExpLogOffs);
1218 
1219     // KeepX
1220     if (ALineForKeepX > 0) and UseKeepCaretX then begin
1221       if (TestOrder and 4) = 4 then begin
1222         TestCaret.LineCharPos := point(1, 1);
1223         if (TestOrder and 6) = 4 then
1224           TestCaret.LineCharPos := point(AX, AY);
1225       end;
1226 
1227       if (TestOrder and 6) = 6
1228       then DoOneSetChar(AY, AX, ALineForKeepX)
1229       else DoOneSetLine(ALineForKeepX);
1230       CheckLogPhys(AName + ' from CharPos',  ALineForKeepX,  ExpNotKeptX,  ExpNotKeptLogX, 0);
1231       if ExpNotKeptX < 0    then AssertFalse(AName + '(char) keepx moved (c)', ExpX    = TestCaret.CharPos);
1232       if ExpNotKeptLogX < 0 then AssertFalse(AName + '(char) keepx moved (b)', ExpLogX = TestCaret.BytePos);
1233 
1234       DoOneSetLine(AY);
1235       CheckLogPhys(AName + ' from CharPos',  ExpY,  ExpX,  ExpLogX, ExpLogOffs);
1236     end;
1237 
1238     if length(AMoveHorizNext) >= 3 then begin
1239       DoOneMoveHorizFromChar(1, AY, AX);
1240       CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizNext[0],  AMoveHorizNext[1], AMoveHorizNext[2]);
1241 
1242       if not ExpMoveHorizFalse then begin
1243         DoOneMoveHoriz(-1);
1244         CheckLogPhys(AName + ' from CharPos',  ExpY,  ExpX,  ExpLogX, ExpLogOffs);
1245       end;
1246     end;
1247     if length(AMoveHorizNext) >= 6 then begin
1248       TestCaret.LineCharPos := point(AX, AY);
1249       if UseLock then TestCaret.Lock;
1250       if UseChangeOnTouch then TestCaret.ChangeOnTouch;
1251       AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(1));
1252       AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(1));
1253       if UseLock then TestCaret.Unlock;
1254       CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizNext[3],  AMoveHorizNext[4], AMoveHorizNext[5]);
1255 
1256       if not ExpMoveHorizFalse then begin
1257         DoOneMoveHoriz(-1);
1258         CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizNext[0],  AMoveHorizNext[1], AMoveHorizNext[2]);
1259         DoOneMoveHoriz(-1);
1260         CheckLogPhys(AName + ' from CharPos',  ExpY,  ExpX,  ExpLogX, ExpLogOffs);
1261 
1262         TestCaret.LineCharPos := point(AX, AY);
1263         DoOneMoveHoriz(2);
1264         CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizNext[3],  AMoveHorizNext[4], AMoveHorizNext[5]);
1265       end;
1266     end;
1267 
1268     if length(AMoveHorizPrev) >= 3 then begin
1269       DoOneMoveHorizFromChar(-1, AY, AX);
1270       CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizPrev[0],  AMoveHorizPrev[1], AMoveHorizPrev[2]);
1271 
1272       if not ExpMoveHorizFalse then begin
1273         DoOneMoveHoriz(1);
1274         CheckLogPhys(AName + ' from CharPos',  ExpY,  ExpX,  ExpLogX, ExpLogOffs);
1275       end;
1276     end;
1277     if length(AMoveHorizPrev) >= 6 then begin
1278       TestCaret.LineCharPos := point(AX, AY);
1279       if UseLock then TestCaret.Lock;
1280       if UseChangeOnTouch then TestCaret.ChangeOnTouch;
1281       AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(-1));
1282       AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(-1));
1283       if UseLock then TestCaret.Unlock;
1284       CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizPrev[3],  AMoveHorizPrev[4], AMoveHorizPrev[5]);
1285 
1286       if not ExpMoveHorizFalse then begin
1287         DoOneMoveHoriz(1);
1288         CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizPrev[0],  AMoveHorizPrev[1], AMoveHorizPrev[2]);
1289         DoOneMoveHoriz(1);
1290         CheckLogPhys(AName + ' from CharPos',  ExpY,  ExpX,  ExpLogX, ExpLogOffs);
1291 
1292         TestCaret.LineCharPos := point(AX, AY);
1293         DoOneMoveHoriz(-2);
1294         CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizPrev[3],  AMoveHorizPrev[4], AMoveHorizPrev[5]);
1295       end;
1296     end;
1297 
1298 
1299 
1300     // Logical
1301     if ALogOffs <> 0 then exit; // TODO;
1302     if ALogX > 0 then begin
1303       DoOneSetByte(AY, ALogX, ALogOffs);
1304       CheckLogPhys(AName + ' from BytePos',  ExpY,  ExpX,  ExpLogX, ExpLogOffs);
1305 
1306       // KeepX
1307       if (ALineForKeepX > 0) and UseKeepCaretX then begin
1308         if (TestOrder and 4) = 4 then begin
1309           TestCaret.LineBytePos := point(1, 1);
1310           if (TestOrder and 6) = 4 then
1311             TestCaret.LineBytePos := point(ALogX, AY);
1312         end;
1313 
1314         if (TestOrder and 6) = 6
1315         then DoOneSetByte(AY, ALogX, ALogOffs, ALineForKeepX)
1316         else DoOneSetLine(ALineForKeepX);
1317         CheckLogPhys(AName + ' from CharPos',  ALineForKeepX,  ExpNotKeptX,  ExpNotKeptLogX, 0);
1318         if ExpNotKeptX < 0    then AssertFalse(AName + '(char) keepx moved (c)', ExpX    = TestCaret.CharPos);
1319         if ExpNotKeptLogX < 0 then AssertFalse(AName + '(char) keepx moved (b)', ExpLogX = TestCaret.BytePos);
1320 
1321         DoOneSetLine(AY);
1322         CheckLogPhys(AName + ' from CharPos',  ExpY,  ExpX,  ExpLogX, ExpLogOffs);
1323       end;
1324 
1325       if length(AMoveHorizNext) >= 3 then begin
1326         DoOneMoveHorizFromByte(1, AY, ALogX, ALogOffs);
1327         CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizNext[0],  AMoveHorizNext[1], AMoveHorizNext[2]);
1328 
1329         if not ExpMoveHorizFalse then begin
1330           DoOneMoveHoriz(-1);
1331           CheckLogPhys(AName + ' from CharPos',  ExpY,  ExpX,  ExpLogX, ExpLogOffs);
1332         end;
1333       end;
1334       if length(AMoveHorizNext) >= 6 then begin
1335         TestCaret.LineBytePos := point(ALogX, AY);
1336         if UseLock then TestCaret.Lock;
1337         if UseChangeOnTouch then TestCaret.ChangeOnTouch;
1338         TestCaret.MoveHoriz(1);
1339         TestCaret.MoveHoriz(1);
1340         if UseLock then TestCaret.Unlock;
1341         CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizNext[3],  AMoveHorizNext[4], AMoveHorizNext[5]);
1342 
1343         if not ExpMoveHorizFalse then begin
1344           DoOneMoveHoriz(-1);
1345           CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizNext[0],  AMoveHorizNext[1], AMoveHorizNext[2]);
1346           DoOneMoveHoriz(-1);
1347           CheckLogPhys(AName + ' from CharPos',  ExpY,  ExpX,  ExpLogX, ExpLogOffs);
1348 
1349           TestCaret.LineBytePos := point(ALogX, AY);
1350           DoOneMoveHoriz(2);
1351           CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizNext[3],  AMoveHorizNext[4], AMoveHorizNext[5]);
1352         end;
1353       end;
1354 
1355       if length(AMoveHorizPrev) >= 3 then begin
1356         DoOneMoveHorizFromByte(-1, AY, ALogX, ALogOffs);
1357         CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizPrev[0],  AMoveHorizPrev[1], AMoveHorizPrev[2]);
1358 
1359         if not ExpMoveHorizFalse then begin
1360           DoOneMoveHoriz(1);
1361           CheckLogPhys(AName + ' from CharPos',  ExpY,  ExpX,  ExpLogX, ExpLogOffs);
1362         end;
1363       end;
1364       if length(AMoveHorizPrev) >= 6 then begin
1365         TestCaret.LineBytePos := point(ALogX, AY);
1366         if UseLock then TestCaret.Lock;
1367         if UseChangeOnTouch then TestCaret.ChangeOnTouch;
1368         TestCaret.MoveHoriz(-1);
1369         TestCaret.MoveHoriz(-1);
1370         if UseLock then TestCaret.Unlock;
1371         CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizPrev[3],  AMoveHorizPrev[4], AMoveHorizPrev[5]);
1372 
1373         if not ExpMoveHorizFalse then begin
1374           DoOneMoveHoriz(1);
1375           CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizPrev[0],  AMoveHorizPrev[1], AMoveHorizPrev[2]);
1376           DoOneMoveHoriz(1);
1377           CheckLogPhys(AName + ' from CharPos',  ExpY,  ExpX,  ExpLogX, ExpLogOffs);
1378 
1379           TestCaret.LineBytePos := point(ALogX, AY);
1380           DoOneMoveHoriz(-2);
1381           CheckLogPhys(AName + ' from CharPos',  ExpY,  AMoveHorizPrev[3],  AMoveHorizPrev[4], AMoveHorizPrev[5]);
1382         end;
1383       end;
1384 
1385     end;
1386 
1387   end;
1388 
1389   procedure DoTests;
1390   begin
1391     if (TestOrder >= 4) and (UseIncAllowPastEOL or UseIncAdjustToNextChar) then
1392       exit;
1393 
1394     ReCreateEdit;
1395     SynEdit.TabWidth := 6;
1396     SetLines(['x',
1397               StringOfChar('x', 40),
1398               ' äääbc', // EOL = 7, 10
1399               'X嗚呼あ嗚呼嗚呼あ嗚呼嗚呼あ嗚呼嗚呼あ',
1400               '嗚呼嗚呼あ嗚呼嗚呼あ嗚呼嗚呼あ嗚呼嗚呼あ',
1401               ' '#9#9#9'mn',
1402               '',
1403               'X ab',
1404               '']);
1405     try
1406       PushBaseName(Format('UseLock=%s, AdjustToNextChar=%s, IncAdjustToNextChar=%s,'+
1407                           ' AllowPastEOL=%s, IncAllowPastEOL=%s, KeepCaretX=%s,' +
1408                           ' ChangeOnTouch=%s, IncAutoMoveOnEdit=%s, SkipTabs=%s' +
1409                           ' MaxLeft=%s',
1410                           [dbgs(UseLock), dbgs(UseAdjustToNextChar), dbgs(UseIncAdjustToNextChar),
1411                            dbgs(UseAllowPastEOL), dbgs(UseIncAllowPastEOL),
1412                            dbgs(UseKeepCaretX), dbgs(UseChangeOnTouch),
1413                            dbgs(UseIncAutoMoveOnEdit), dbgs(UseSkipTabs), dbgs(UseMaxLeft)
1414                           ]));
1415 
1416 //debugln(BaseTestName);
1417 
1418       TestCaret := TSynEditCaret.Create;
1419       TestCaret.Lines := SynEdit.ViewedTextBuffer;
1420 
1421       TestCaret.AdjustToNextChar := UseAdjustToNextChar;
1422       if UseIncAdjustToNextChar then TestCaret.IncForceAdjustToNextChar;
1423 
1424       TestCaret.AllowPastEOL := UseAllowPastEOL;
1425       if UseIncAllowPastEOL then TestCaret.IncForcePastEOL;
1426 
1427       TestCaret.KeepCaretX := UseKeepCaretX;
1428       if UseIncAutoMoveOnEdit then TestCaret.IncAutoMoveOnEdit;
1429       TestCaret.SkipTabs := UseSkipTabs;
1430       if UseMaxLeft then
1431         TestCaret.MaxLeftChar := @TestMaxLeftProc; // 6000
1432 
1433       TestCaret2 := TSynEditCaret.Create;
1434       TestCaret2.Lines := SynEdit.ViewedTextBuffer;
1435 
1436 
1437 
1438       if UseAdjustToNextChar or UseIncAdjustToNextChar
1439       then DoOneTest('Basic',     2, 3, 3, 0,    2, 3, 3, 0,
1440                      [4,4,0,  5,5,0], [2,2,0,  1,1,0], False,   // MoveHoriz
1441                      4, 4, 5  // KeepX
1442                     )
1443       else DoOneTest('Basic',     2, 3, 3, 0,    2, 3, 3, 0,
1444                      [4,4,0,  5,5,0], [2,2,0,  1,1,0], False,    // MoveHoriz
1445                      4, 2, 2  // KeepX
1446                     );
1447 
1448       // past EOL
1449       if UseAllowPastEOL or UseIncAllowPastEOL
1450       then DoOneTest('past EOL',     8, 9, 9, 0,    8, 9, 9, 0,
1451                      [10,10,0], [8,8,0])
1452       else DoOneTest('past EOL',     8, 9, 9, 0,    8, 5, 5, 0,
1453                      [5,5,0,  5,5,0],[], True);
1454       // BOL
1455       DoOneTest('at BOL',     8, 1, 1, 0,    8, 1, 1, 0,
1456                 [],[1,1,0,  1,1,0], True);
1457 
1458       // one past EOL
1459       if UseAllowPastEOL or UseIncAllowPastEOL
1460       then DoOneTest('one past EOL',     3, 8,11, 0,    3, 8,11, 0,
1461                      [ 9,12,0,  10,13,0],  [7,10,0,  6,9,0])
1462       else DoOneTest('one past EOL',     3, 8,11, 0,    3, 7,10, 0,
1463                      [7,10,0,  7,10,0], [], True);
1464 
1465       // MaxLeftChar 6000 (5999 char / EOL = 6000)
1466       if UseAllowPastEOL or UseIncAllowPastEOL
1467       then if UseMaxLeft
1468            then DoOneTest('past EOL',     3, 6001, 6004, 0,    3, 6000, 6003, 0,
1469                           [6000, 6003, 0], [], True)
1470            else DoOneTest('past EOL',     3, 6001, 6004, 0,    3, 6001, 6004, 0,
1471                           [], [])
1472       else DoOneTest('past EOL',     3, 6001, 6004, 0,    3, 7,10, 0,
1473                      [],[]
1474                     );
1475 
1476 
1477       // ' äääbc'
1478       if UseAdjustToNextChar or UseIncAdjustToNextChar
1479       then DoOneTest('LogPhys',   3, 4, 6, 0,    3, 4, 6, 0,
1480                      [5,8,0,  6,9,0], [3,4,0,  2,2,0], False,
1481                      5, 5, 7
1482                     )
1483       else DoOneTest('LogPhys',   3, 4, 6, 0,    3, 4, 6, 0,
1484                      [5,8,0,  6,9,0], [3,4,0,  2,2,0], False,
1485                      5, 3, 4
1486                     );
1487 
1488       // 'X嗚呼あ'  // skip "from byte"
1489       if UseAdjustToNextChar or UseIncAdjustToNextChar
1490       then DoOneTest('Mid Dbl-Width',   4, 3, -3, 0,    4, 4, 5, 0,
1491                      [6,8,0], [2,2,0], False,
1492                      5, 5, 7
1493                     )
1494       else DoOneTest('Mid Dbl-Width',   4, 3, -3, 0,    4, 2, 2, 0,
1495                      [4,5,0], [1,1,0], False,
1496                      5, 1, 1
1497                     );
1498 
1499       // ' '#9#9#9'mn'   // skip "from byte" TODO
1500       if UseSkipTabs
1501       then if UseAdjustToNextChar or UseIncAdjustToNextChar
1502            then DoOneTest('Mid Tab',   6, 8, -3, 1,    6,13, 4, 0,
1503                           [19,5,0,  20,6,0],  [7,3,0,  2,2,0], False
1504                           //7,1,1
1505                          )
1506            else if UseAllowPastEOL or UseIncAllowPastEOL
1507                 then DoOneTest('Mid Tab',   6, 8, -3, 1,    6, 7, 3, 0,
1508                                [13,4,0,  19,5,0],  [2,2,0,  1,1,0], False
1509                               )
1510                 else DoOneTest('Mid Tab',   6, 8, -3, 1,    6, 7, 3, 0,
1511                                [13,4,0,  19,5,0],  [2,2,0,  1,1,0], False,
1512                                7,1,1
1513                               )
1514       else if UseAllowPastEOL or UseIncAllowPastEOL
1515            then DoOneTest('Mid Tab',   6, 8, -3, 1,    6, 8, 3, 1,
1516                           [9,3,2,  10,3,3],  [7,3,0,  6,2,4],  False
1517                          )
1518            else DoOneTest('Mid Tab',   6, 8, -3, 1,    6, 8, 3, 1,
1519                           [9,3,2,  10,3,3],  [7,3,0,  6,2,4], False,
1520                           7,1,1
1521                          );
1522 
1523 
1524 
1525     finally
1526       PopBaseName;
1527       FreeAndNil(TestCaret);
1528       FreeAndNil(TestCaret2);
1529     end;
1530   end;
1531 
1532 begin
1533   FPersistSel := False;
1534   //(*
1535   TestOrder := 6;
1536   UseLock := True;
1537   UseAdjustToNextChar := False;
1538   UseIncAdjustToNextChar := False;
1539   UseAllowPastEOL := False;
1540   UseIncAllowPastEOL := False;
1541   UseKeepCaretX := True;
1542   UseChangeOnTouch := False;
1543   UseIncAutoMoveOnEdit := False;
1544   UseSkipTabs := False;
1545   UseMaxLeft := False;
1546   DoTests;
1547   exit;
1548   //*)
1549 
1550   for TestOrder := 0 to 11 do // CheckAtPos (8) only runs first 4
1551     for UseLock := low(Boolean) to high(Boolean) do
1552       for UseAdjustToNextChar := low(Boolean) to high(Boolean) do
1553       for UseIncAdjustToNextChar := low(Boolean) to high(Boolean) do
1554         for UseAllowPastEOL := low(Boolean) to high(Boolean) do
1555         for UseIncAllowPastEOL := low(Boolean) to high(Boolean) do
1556           for UseKeepCaretX := low(Boolean) to high(Boolean) do
1557             for UseChangeOnTouch := low(Boolean) to high(Boolean) do
1558               for UseIncAutoMoveOnEdit := low(Boolean) to high(Boolean) do
1559                 for UseSkipTabs := low(Boolean) to high(Boolean) do
1560                   for UseMaxLeft := low(Boolean) to high(Boolean) do
1561 // OldPos, MoveHoriz
1562 // IsAtPos /
1563     DoTests;
1564 
1565 end;
1566 
1567 procedure TTestBasicSynEdit.TestCaretAutoMove;
1568 
1569   procedure DoTest(name: string; y, x, insertY, insertX, InsertY2, InsertX2: integer;
1570                    txt: string; expY, expX: Integer);
1571   begin
1572     name := name + ' y='+inttostr(y)+' x='+inttostr(x);
1573     if y > 0 then begin
1574       ReCreateEdit;
1575       SynEdit.TabWidth := 6;
1576       SetLines(['x', 'abc', ' ääX', #9'mn', 'abc'#9'de', #9'Xää.']);
1577       SynEdit.CaretXY := Point(x, y);
1578     end;
1579 
1580     SynEdit.TextBetweenPointsEx[Point(insertX, insertY), point(insertX2, InsertY2), scamAdjust]
1581       := txt;
1582 debugln(dbgstr(SynEdit.Text));
1583     TestIsCaretPhys(name, expX, expY);
1584   end;
1585 
1586 const
1587   cr = LineEnding;
1588 begin
1589   FPersistSel := False;
1590 
1591   DoTest('simple insert',              2,2,   2,1,  2,1, 'X',      2,3);
1592   DoTest('simple insert CR',           2,2,   2,1,  2,1, 'X'+cr,   3,2);
1593   DoTest('simple insert CR+',          2,2,   2,1,  2,1, cr+'X',   3,3);
1594   DoTest('simple delete',              2,2,   2,1,  2,2, '',       2,1);
1595   DoTest('simple delete CR',           2,2,   1,2,  2,1, '',       1,3);
1596   DoTest('+simple delete CR',          2,2,   1,1,  2,1, '',       1,2);
1597 
1598   DoTest('simple insert (eol)',        2,4,   2,1,  2,1, 'X',   2,5);
1599   DoTest('simple insert (past eol)',   2,7,   2,1,  2,1, 'X',   2,8);
1600 
1601   DoTest('insert with tab',            4,8,   4,1,  4,1, 'X',      4,8);
1602   DoTest('insert with tab (cont)',    -4,8,   4,2,  4,2, 'Y',      4,8);
1603   DoTest('insert with tab (cont)',    -4,8,   4,3,  4,3, 'abc',    4,8);
1604   DoTest('insert with tab (cont)',    -4,8,   4,6,  4,6, 'Z',      4,14);
1605   DoTest('insert with tab (cont)',    -4,8,   4,7,  4,7, '.',      4,14);
1606   DoTest('delete with tab (cont)',    -4,8,   4,1,  4,2, '',       4,14);
1607   DoTest('delete with tab (cont)',    -4,8,   4,1,  4,2, '',       4,8);
1608   DoTest('delete with tab (cont)',    -4,8,   4,1,  4,2, '',       4,8);
1609   DoTest('delete with tab (cont)',    -4,8,   4,1,  4,2, '',       4,8);
1610 
1611 
1612   SynEdit.CaretObj.IncAutoMoveOnEdit;
1613   DoTest('insert with tab (am-block)',            4,8,   4,1,  4,1, 'X',      4,8);
1614   DoTest('insert with tab (am-block) (cont)',    -4,8,   4,2,  4,2, 'Y',      4,8);
1615   DoTest('insert with tab (am-block) (cont)',    -4,8,   4,3,  4,3, 'abc',    4,8);
1616   DoTest('insert with tab (am-block) (cont)',    -4,8,   4,6,  4,6, 'Z',      4,14);
1617   DoTest('insert with tab (am-block) (cont)',    -4,8,   4,7,  4,7, '.',      4,14);
1618   DoTest('delete with tab (cont)',    -4,8,   4,1,  4,2, '',       4,14);
1619   DoTest('delete with tab (cont)',    -4,8,   4,1,  4,2, '',       4,8);
1620   DoTest('delete with tab (cont)',    -4,8,   4,1,  4,2, '',       4,8);
1621   DoTest('delete with tab (cont)',    -4,8,   4,1,  4,2, '',       4,8);
1622   SynEdit.CaretObj.DecAutoMoveOnEdit;
1623 
1624 end;
1625 
1626 procedure TTestBasicSynEdit.TestCaretEcDeleteWord;
1627 begin
1628   FPersistSel := False;
1629   FDoInit := @DoInit1;
1630   FTestLines := @TestLines1;
1631   FTestCommand := ecDeleteWord;
1632   AllowPastEOL := True;
1633   PushBaseName('ecDeleteWord');
1634 
1635   // if in middle of word, keep spaces after
1636   TestCommand('simple "te|xt"',                    8, 1,    8, 1, [1,'Some te to test'],
1637                                                               8, 1, [1,'Some teto test']);
1638   // if at end of word, just del spaces after
1639   TestCommand('simple EOW "text|"',               10, 1,   10, 1, [1,'Some textto test'],
1640                                                              10, 1, [1,'Some text test']);
1641   // if at start of word, do NOT keep spaces after
1642   TestCommand('simple BOW "|text"',                6, 1,    6, 1, [1,'Some to test'],
1643                                                               6, 1, [1,'Some test']);
1644   TestCommand('simple EOT "li|ne"',               10,15,   10,15, [15,'normal li'],
1645                                                              10,15, [15,'normal li']);
1646   TestCommand('simple > EOL, next line "te|st"',  16, 1,   16, 1, [1,'Some text to te'],
1647                                                              16, 1, [1, 1,'Some text to teFoo bar abc']);
1648 
1649   TestCommand('tab "li|ne"',                      16, 3,   16, 3, [3, #9'Other li with tab'],
1650                                                              16, 3, [3, #9'Other liwith tab']);
1651   TestCommand('tab EOW "line|"',                  18, 3,   18, 3, [3, #9'Other linewith tab'],
1652                                                              18, 3, [3, #9'Other line tab']);
1653   TestCommand('tab BOW "|line"',                  14, 3,   14, 3, [3, #9'Other with tab'],
1654                                                              14, 3, [3, #9'Other tab']);
1655   TestCommand('tab > EOL, next-line',             25, 3,   25, 3, [3, #9'Other line with t'],
1656                                                              25, 3, [3, 3, #9'Other line with ttab'#9'in the middle']);
1657 
1658   TestCommand('M-tab "t|ab"',                      2, 4,    2, 4, [4, 't'#9'in the middle'],
1659                                                               2, 4, [4, 'tin the middle']);
1660   TestCommand('M-tab "tab|"',                      4, 4,    4, 4, [4, 'tabin the middle'],
1661                                                               4, 4, [4, 'tab the middle']);
1662   TestCommand('M-tab > EOL, next-line-tab',       17, 4,   17, 4, [4, 'tab'#9'in the mi'],
1663                                                              17, 4, [4, 4, 'tab'#9'in the mitab'#9' in the middle with space']);
1664 
1665   //TestCommand('M-S-tab BOW "t|ab"',                2, 5,    9, 5,
1666   //                                                           12, 5);
1667   //TestCommand('M-S-tab EOW "tab|"',                4, 5,    9, 5,
1668   //                                                           12, 5);
1669   //TestCommand('M-S-tab BOW "|tab"',                1, 5,    9, 5,
1670   //                                                           12, 5);
1671   //TestCommand('M-S-tab "tab#9| "',                 5, 5,    9, 5,
1672   //                                                           12, 5);
1673   //
1674   TestCommand('Umlaut "ää|ä"',                    11, 6,   11, 6, [6, 'umlaute ää in text'],
1675                                                              11, 6, [6, 'umlaute ääin text']);
1676   TestCommand('Umlaut EOW "äää|"',                12, 6,   12, 6, [6, 'umlaute äääin text'],
1677                                                              12, 6, [6, 'umlaute äää text']);
1678   TestCommand('Umlaut BOW "|äää"',                 9, 6,    9, 6, [6, 'umlaute in text'],
1679                                                               9, 6, [6, 'umlaute text']);
1680   TestCommand('Umlaut "umlaute|"',                 8, 6,    8, 6, [6, 'umlauteäää in text'],
1681                                                               8, 6, [6, 'umlaute in text']);
1682   TestCommand('After Umlaut > EOL, next line',    18, 6,   18, 6, [6, 'umlaute äää in te'],
1683                                                              18, 6, [6, 6, 'umlaute äää in te'+FTestLines()[6]]);
1684   // past eol
1685   TestCommand('Umlaut EOW "äää in text |"',                21, 6,   21, 6, [6, 6, 'umlaute äää in text normal line']);
1686 
1687   //TestCommand('Before untrimmed > next',           12, 7,   4, 8,
1688   //                                                           14, 8);
1689   //TestCommand('untrimmed > EOL, next',             30, 8,  35, 8,
1690   //                                                            1, 9);
1691   //TestCommand('Before untrimmed tab > next',       12, 9,   8,10,
1692   //                                                           15,10);
1693   //TestCommand('untrimmed tab > EOL, next',         24,10,  29,10,
1694   //                                                            1,11);
1695   //
1696   //TestCommand('Before empty > next',                12,11,  1,12);
1697   //TestCommand('Before space empty > next',          12,13,  1,14,
1698   //                                                            6, 14);
1699 end;
1700 
1701 procedure TTestBasicSynEdit.TestCaretEcDeleteLastWord;
1702 begin
1703   FPersistSel := False;
1704   FDoInit := @DoInit1;
1705   FTestLines := @TestLines1;
1706   FTestCommand := ecDeleteLastWord;
1707   AllowPastEOL := True;
1708   PushBaseName('ecDeleteLastWord');
1709 
1710   TestCommand('simple "te|st"',                   16, 1,   14, 1, [1,'Some text to st'],
1711                                                             11, 1, [1,'Some text st']);
1712   TestCommand('simple EOW "test|"',               18, 1,   14, 1, [1,'Some text to '],
1713                                                             11, 1, [1,'Some text ']);
1714   TestCommand('simple BOW "|test"',               14, 1,   11, 1, [1,'Some text test'],
1715                                                              6, 1, [1,'Some test']);
1716   TestCommand('simple > BOT "So|me"',              3, 1,    1, 1, [1,'me text to test'],
1717                                                              1, 1, [1,'me text to test']);
1718   TestCommand('simple > prev-line "F|oo"',         2, 2,    1, 2, [2,'oo bar abc'],
1719                                                             18, 1, [1, 1, 'Some text to testoo bar abc']);
1720   TestCommand('simple > prev-line "|Foo"',         1, 2,   18, 1, [1, 1, 'Some text to testFoo bar abc'],
1721                                                             14, 1, [1, 1, 'Some text to Foo bar abc']);
1722 
1723   TestCommand('tab "wi|th"',                      21, 3,   19, 3, [3, #9'Other line th tab'],
1724                                                             14, 3, [3, #9'Other th tab']);
1725   TestCommand('tab EOW "with|"',                  23, 3,   19, 3, [3, #9'Other line  tab'],
1726                                                             14, 3, [3, #9'Other  tab']);
1727   TestCommand('tab BOW "|with"',                  19, 3,   14, 3, [3, #9'Other with tab'],
1728                                                              8, 3, [3, #9'with tab']);
1729   TestCommand('tab > prev-line "O|ther"',          9, 3,    8, 3, [3, #9'ther line with tab'],
1730                                                             12, 2, [2, 2, 'Foo bar abcther line with tab']);
1731 
1732   TestCommand('M-tab "i|n"',                       9, 4,    8, 4, [4, 'tab'#9'n the middle'],
1733                                                              1, 4, [4, 'n the middle']);
1734   TestCommand('M-tab > prev-line-tab "ta|b"',      3, 4,    1, 4, [4, 'b'#9'in the middle'],
1735                                                             27, 3, [3, 3, #9'Other line with tabb'#9'in the middle']);
1736 
1737   //TestCommand('M-S-tab "i|n"',                    10, 5,    9, 5,
1738   //                                                           1, 5);
1739   //TestCommand('M-S-EOW tab "in|"',                11, 5,    9, 5,
1740   //                                                           1, 5);
1741   //TestCommand('M-S-BOW tab "|in"',                 9, 5,    1, 5,
1742   //                                                          21, 4);
1743   //TestCommand('M-S-tab "#9| in"',                  8, 5,    1, 5);
1744   //
1745   TestCommand('Umlaut "ää|ä"',                    11, 6,    9, 6, [6, 'umlaute ä in text'],
1746                                                              1, 6, [6, 'ä in text']);
1747   TestCommand('Umlaut EOW "äää|"',                12, 6,    9, 6, [6, 'umlaute  in text'],
1748                                                              1, 6, [6, ' in text']);
1749   TestCommand('Umlaut BOW "|äää"',                 9, 6,    1, 6, [6, 'äää in text'],
1750                                                             33, 5, [5, 5, FTestLines()[4]+'äää in text']);
1751   TestCommand('Umlaut "i|n"',                     14, 6,   13, 6, [6, 'umlaute äää n text'],
1752                                                              9, 6, [6, 'umlaute n text']);
1753   TestCommand('After Umlaut > prev line',          1, 7,   20, 6, [6, 6, 'umlaute äää in text'+FTestLines()[6]],
1754                                                             16, 6, [6, 6, 'umlaute äää in '+FTestLines()[6]]);
1755 
1756   //TestCommand('untrimmed "un|trimmed"',            6, 8,    4, 8,
1757   //                                                          12, 7);
1758   //TestCommand('After untrimmed > prev',            1, 9,   35, 8,
1759   //                                                          28, 8);
1760   //TestCommand('untrimmed tab "t|ab"',              9,10,    8,10,
1761   //                                                          12, 9);
1762   //TestCommand('After untrimmed tab > prev',        1,11,   29,10,
1763   //                                                          22,10);
1764   //
1765   //TestCommand('After empty > prev',                1,13,    1,12);
1766   //TestCommand('After space empty > prev',          1,15,    6,14);
1767 end;
1768 
1769 procedure TTestBasicSynEdit.TestCaretEcDeleteEOL;
1770 begin
1771   FPersistSel := False;
1772   FDoInit := @DoInit1;
1773   FTestLines := @TestLines1;
1774   FTestCommand := ecDeleteEOL;
1775   AllowPastEOL := True;
1776   PushBaseName('ecDeleteEOL');
1777 
1778   TestCommand('simple "te|xt"',                    8, 1,    8, 1, [1,'Some te']);
1779   // if at end of line
1780   TestCommand('simple EOL "text|"',               18, 1,   18, 1, []);
1781   // if at start of line
1782   TestCommand('simple BOL "|text"',                1, 1,    1, 1, [1,'']);
1783 
1784   // utf8
1785   TestCommand('utf8 "te|xt"',                   10, 6,   10, 6, [6,'umlaute ä']);
1786   // if at end of line
1787   TestCommand('utf8 EOL "text|"',               20, 6,   20, 6, []);
1788   // if at start of line
1789   TestCommand('utf8 BOL "|text"',                1, 6,    1, 6, [6,'']);
1790 
1791   //tab
1792   TestCommand('tab "te|#9xt"',                 11, 10,   11, 10, [10,#9'tab']);
1793   TestCommand('tab "te#9|xt"',                 15, 10,   15, 10, [10,#9'tab'#9]);
1794   // if at end of line
1795   TestCommand('tab EOL "text|"',               29, 10,   29, 10, []);
1796   // if at start of line
1797   TestCommand('tab BOL "|text"',                1, 10,    1, 10, [10,'']);
1798 
1799 end;
1800 
1801 procedure TTestBasicSynEdit.TestWordBreaker;
1802 var
1803   WBrker: TSynWordBreaker;
1804 begin
1805   FPersistSel := False;
1806   WBrker := TSynWordBreaker.Create;
1807   WBrker.IdentChars := ['a'..'z', 'A'..'Z', '0'..'9']; // not used
1808   WBrker.WhiteChars := [' ', #9];
1809   WBrker.WordBreakChars := [',', '.', '-', ';' ,':'];
1810 
1811   AssertEquals('', True,  WBrker.IsInWord('abc  123  ... aa', 1));
1812   AssertEquals('', True,  WBrker.IsInWord('abc  123  ... aa', 2));
1813   AssertEquals('', True,  WBrker.IsInWord('abc  123  ... aa', 3));
1814   AssertEquals('', True,  WBrker.IsInWord('abc  123  ... aa', 4));
1815   AssertEquals('', False, WBrker.IsInWord('abc  123  ... aa', 5));
1816   AssertEquals('', True,  WBrker.IsInWord('abc  123  ... aa', 6));
1817   AssertEquals('', True,  WBrker.IsInWord('abc  123  ... aa', 7));
1818   AssertEquals('', True,  WBrker.IsInWord('abc  123  ... aa', 9));
1819   AssertEquals('', False, WBrker.IsInWord('abc  123  ... aa', 10));
1820   AssertEquals('', False, WBrker.IsInWord('abc  123  ... aa', 11)); // before ...
1821   AssertEquals('', True,  WBrker.IsInWord('abc  123  ... aa', 15)); // |aa
1822   AssertEquals('', True,  WBrker.IsInWord('abc  123  ... aa', 17)); // at eol
1823   AssertEquals('', False, WBrker.IsInWord('abc  123  ... aa', 18));
1824   AssertEquals('', False, WBrker.IsInWord('abc  123  ... aa', 19));
1825   AssertEquals('', False, WBrker.IsInWord('abc  123  ... aa', 0));
1826   AssertEquals('', False, WBrker.IsInWord('abc  123  ... aa', -1));
1827   AssertEquals('', False, WBrker.IsInWord(' ', 1));
1828   AssertEquals('', False, WBrker.IsInWord(' ', 2));
1829   AssertEquals('', False, WBrker.IsInWord('', 1));
1830   AssertEquals('', False, WBrker.IsInWord('...', 1));
1831   AssertEquals('', False, WBrker.IsInWord('...', 2));
1832   AssertEquals('', False, WBrker.IsInWord('...', 4));
1833   AssertEquals('', False, WBrker.IsInWord('..aa', 2));
1834   AssertEquals('', True,  WBrker.IsInWord('..aa', 3));
1835   AssertEquals('', True,  WBrker.IsInWord('..aa', 4));
1836   AssertEquals('', True,  WBrker.IsInWord('..aa', 5));
1837 
1838 
1839   AssertEquals('', True,  WBrker.IsAtWordStart('abc  123  ... aa', 1));
1840   AssertEquals('', False, WBrker.IsAtWordStart('abc  123  ... aa', 2));
1841   AssertEquals('', False, WBrker.IsAtWordStart('abc  123  ... aa', 3));
1842   AssertEquals('', False, WBrker.IsAtWordStart('abc  123  ... aa', 4));
1843   AssertEquals('', False, WBrker.IsAtWordStart('abc  123  ... aa', 5));
1844   AssertEquals('', True,  WBrker.IsAtWordStart('abc  123  ... aa', 6));
1845   AssertEquals('', False, WBrker.IsAtWordStart('abc  123  ... aa', 7));
1846   AssertEquals('', False, WBrker.IsAtWordStart('abc  123  ... aa', 9));
1847   AssertEquals('', False, WBrker.IsAtWordStart('abc  123  ... aa', 10));
1848   AssertEquals('', False, WBrker.IsAtWordStart('abc  123  ... aa', 11)); // before ...
1849   AssertEquals('', True,  WBrker.IsAtWordStart('abc  123  ... aa', 15)); // |aa
1850   AssertEquals('', False, WBrker.IsAtWordStart('abc  123  ... aa', 17)); // at eol
1851   AssertEquals('', False, WBrker.IsAtWordStart('abc  123  ... aa', 18));
1852   AssertEquals('', False, WBrker.IsAtWordStart('abc  123  ... aa', 19));
1853   AssertEquals('', False, WBrker.IsAtWordStart('abc  123  ... aa', 0));
1854   AssertEquals('', False, WBrker.IsAtWordStart('abc  123  ... aa', -1));
1855   AssertEquals('', False, WBrker.IsAtWordStart(' ', 1));
1856   AssertEquals('', False, WBrker.IsAtWordStart('', 1));
1857   AssertEquals('', False, WBrker.IsAtWordStart(' ', 2));
1858   AssertEquals('', False, WBrker.IsAtWordStart('...', 1));
1859   AssertEquals('', True,  WBrker.IsAtWordStart('..aa', 3));
1860 
1861   AssertEquals('', False, WBrker.IsAtWordEnd('abc  123  ... aa', 1));
1862   AssertEquals('', False, WBrker.IsAtWordEnd('abc  123  ... aa', 2));
1863   AssertEquals('', False, WBrker.IsAtWordEnd('abc  123  ... aa', 3));
1864   AssertEquals('', True,  WBrker.IsAtWordEnd('abc  123  ... aa', 4));
1865   AssertEquals('', False, WBrker.IsAtWordEnd('abc  123  ... aa', 5));
1866   AssertEquals('', False, WBrker.IsAtWordEnd('abc  123  ... aa', 6));
1867   AssertEquals('', False, WBrker.IsAtWordEnd('abc  123  ... aa', 7));
1868   AssertEquals('', True,  WBrker.IsAtWordEnd('abc  123  ... aa', 9));
1869   AssertEquals('', False, WBrker.IsAtWordEnd('abc  123  ... aa', 10));
1870   AssertEquals('', False, WBrker.IsAtWordEnd('abc  123  ... aa', 11)); // before ...
1871   AssertEquals('', False, WBrker.IsAtWordEnd('abc  123  ... aa', 15)); // |aa
1872   AssertEquals('', True,  WBrker.IsAtWordEnd('abc  123  ... aa', 17)); // at eol
1873   AssertEquals('', False, WBrker.IsAtWordEnd('abc  123  ... aa', 18));
1874   AssertEquals('', False, WBrker.IsAtWordEnd('abc  123  ... aa', 19));
1875   AssertEquals('', False, WBrker.IsAtWordEnd('abc  123  ... aa', 0));
1876   AssertEquals('', False, WBrker.IsAtWordEnd('abc  123  ... aa', -1));
1877   AssertEquals('', False, WBrker.IsAtWordEnd(' ', 1));
1878   AssertEquals('', False, WBrker.IsAtWordEnd(' ', 2));
1879   AssertEquals('', False, WBrker.IsAtWordEnd('', 1));
1880   AssertEquals('', False, WBrker.IsAtWordEnd('...', 1));
1881   AssertEquals('', False, WBrker.IsAtWordEnd('..aa', 3));
1882 
1883   AssertEquals('', 6, WBrker.NextWordStart('abc  123  ... aa', 1, False));
1884   AssertEquals('', 6, WBrker.NextWordStart('abc  123  ... aa', 2, False));
1885   AssertEquals('', 6, WBrker.NextWordStart('abc  123  ... aa', 3, False));
1886   AssertEquals('', 6, WBrker.NextWordStart('abc  123  ... aa', 4, False)); // abc|
1887   AssertEquals('', 6, WBrker.NextWordStart('abc  123  ... aa', 5, False));
1888   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 6, False)); // |123
1889   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 7, False)); // 1|23
1890   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 8, False));
1891   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 9, False)); // 123|
1892   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 10, False));
1893   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 11, False)); // |...
1894   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 12, False));
1895   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 13, False));
1896   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 14, False)); // ...|
1897   AssertEquals('',-1, WBrker.NextWordStart('abc  123  ... aa', 15, False));
1898   AssertEquals('',-1, WBrker.NextWordStart('abc  123  ... aa', 16, False));
1899   AssertEquals('',-1, WBrker.NextWordStart('abc  123  ... aa', 17, False)); // at eol
1900   AssertEquals('',-1, WBrker.NextWordStart('abc  123  ... aa', 18, False));
1901   AssertEquals('',-1, WBrker.NextWordStart('abc  123  ... aa', 19, False));
1902   AssertEquals('',-1, WBrker.NextWordStart('abc  123  ... aa', 0, False));
1903   AssertEquals('',-1, WBrker.NextWordStart('abc  123  ... aa', -1, False));
1904   AssertEquals('',-1, WBrker.NextWordStart('', 1, False));
1905   AssertEquals('',-1, WBrker.NextWordStart(' ', 1, False));
1906   AssertEquals('',-1, WBrker.NextWordStart(' ', 2, False));
1907   AssertEquals('',3,  WBrker.NextWordStart('..aa', 1, False));
1908   AssertEquals('',3,  WBrker.NextWordStart('..aa', 2, False));
1909   AssertEquals('',-1, WBrker.NextWordStart('..aa', 3, False));
1910   AssertEquals('',-1, WBrker.NextWordStart('..aa', 4, False));
1911   AssertEquals('', 3, WBrker.NextWordStart('a a 1', 1, False));
1912   AssertEquals('', 3, WBrker.NextWordStart('a a 1', 2, False));
1913   AssertEquals('', 5, WBrker.NextWordStart('a a 1', 3, False));
1914   AssertEquals('', 5, WBrker.NextWordStart('a a 1', 4, False));
1915   AssertEquals('',-1, WBrker.NextWordStart('a a 1', 5, False));
1916   AssertEquals('',-1, WBrker.NextWordStart('a a 1', 6, False));
1917   AssertEquals('', 2, WBrker.NextWordStart(' a ', 1, False));
1918   AssertEquals('',-1, WBrker.NextWordStart(' a ', 2, False));
1919   AssertEquals('',-1, WBrker.NextWordStart(' a ', 3, False));
1920   AssertEquals('',-1, WBrker.NextWordStart(' a ', 4, False));
1921 
1922   AssertEquals('', 1, WBrker.NextWordStart('abc  123  ... aa', 1, True));
1923   AssertEquals('', 6, WBrker.NextWordStart('abc  123  ... aa', 2, True));
1924   AssertEquals('', 6, WBrker.NextWordStart('abc  123  ... aa', 3, True));
1925   AssertEquals('', 6, WBrker.NextWordStart('abc  123  ... aa', 4, True)); // abc|
1926   AssertEquals('', 6, WBrker.NextWordStart('abc  123  ... aa', 5, True));
1927   AssertEquals('', 6, WBrker.NextWordStart('abc  123  ... aa', 6, True)); // |123
1928   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 7, True)); // 1|23
1929   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 8, True));
1930   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 9, True)); // 123|
1931   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 10, True));
1932   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 11, True)); // |...
1933   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 12, True));
1934   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 13, True));
1935   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 14, True)); // ...|
1936   AssertEquals('',15, WBrker.NextWordStart('abc  123  ... aa', 15, True));
1937   AssertEquals('',-1, WBrker.NextWordStart('abc  123  ... aa', 16, True));
1938   AssertEquals('',-1, WBrker.NextWordStart('abc  123  ... aa', 17, True)); // at eol
1939   AssertEquals('',-1, WBrker.NextWordStart('abc  123  ... aa', 18, True));
1940   AssertEquals('',-1, WBrker.NextWordStart('abc  123  ... aa', 19, True));
1941   AssertEquals('',-1, WBrker.NextWordStart('abc  123  ... aa', 0, True));
1942   AssertEquals('',-1, WBrker.NextWordStart('abc  123  ... aa', -1, True));
1943   AssertEquals('',-1, WBrker.NextWordStart('', 1, True));
1944   AssertEquals('',-1, WBrker.NextWordStart(' ', 1, True));
1945   AssertEquals('',-1, WBrker.NextWordStart(' ', 2, True));
1946   AssertEquals('',3,  WBrker.NextWordStart('..aa', 1, True));
1947   AssertEquals('',3,  WBrker.NextWordStart('..aa', 2, True));
1948   AssertEquals('',3,  WBrker.NextWordStart('..aa', 3, True));
1949   AssertEquals('',-1, WBrker.NextWordStart('..aa', 4, True));
1950   AssertEquals('', 1, WBrker.NextWordStart('a a 1', 1, True));
1951   AssertEquals('', 3, WBrker.NextWordStart('a a 1', 2, True));
1952   AssertEquals('', 3, WBrker.NextWordStart('a a 1', 3, True));
1953   AssertEquals('', 5, WBrker.NextWordStart('a a 1', 4, True));
1954   AssertEquals('', 5, WBrker.NextWordStart('a a 1', 5, True));
1955   AssertEquals('',-1, WBrker.NextWordStart('a a 1', 6, True));
1956   AssertEquals('', 2, WBrker.NextWordStart(' a ', 1, True));
1957   AssertEquals('', 2, WBrker.NextWordStart(' a ', 2, True));
1958   AssertEquals('',-1, WBrker.NextWordStart(' a ', 3, True));
1959   AssertEquals('',-1, WBrker.NextWordStart(' a ', 4, True));
1960 
1961   AssertEquals('', 4, WBrker.NextWordEnd('abc  123  ... aa', 1, False));
1962   AssertEquals('', 4, WBrker.NextWordEnd('abc  123  ... aa', 2, False));
1963   AssertEquals('', 4, WBrker.NextWordEnd('abc  123  ... aa', 3, False));
1964   AssertEquals('', 9, WBrker.NextWordEnd('abc  123  ... aa', 4, False)); // abc|
1965   AssertEquals('', 9, WBrker.NextWordEnd('abc  123  ... aa', 5, False));
1966   AssertEquals('', 9, WBrker.NextWordEnd('abc  123  ... aa', 6, False)); // |123
1967   AssertEquals('', 9, WBrker.NextWordEnd('abc  123  ... aa', 7, False)); // 1|23
1968   AssertEquals('', 9, WBrker.NextWordEnd('abc  123  ... aa', 8, False));
1969   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 9, False)); // 123|
1970   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 10, False));
1971   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 11, False)); // |...
1972   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 12, False));
1973   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 13, False));
1974   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 14, False)); // ...|
1975   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 15, False));
1976   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 16, False));
1977   AssertEquals('',-1, WBrker.NextWordEnd('abc  123  ... aa', 17, False)); // at eol
1978   AssertEquals('',-1, WBrker.NextWordEnd('abc  123  ... aa', 18, False));
1979   AssertEquals('',-1, WBrker.NextWordEnd('abc  123  ... aa', 19, False));
1980   AssertEquals('',-1, WBrker.NextWordEnd('abc  123  ... aa', 0, False));
1981   AssertEquals('',-1, WBrker.NextWordEnd('abc  123  ... aa', -1, False));
1982   AssertEquals('',-1, WBrker.NextWordEnd('', 1, False));
1983   AssertEquals('',-1, WBrker.NextWordEnd(' ', 1, False));
1984   AssertEquals('',-1, WBrker.NextWordEnd(' ', 2, False));
1985   AssertEquals('',5,  WBrker.NextWordEnd('..aa', 1, False));
1986   AssertEquals('',5,  WBrker.NextWordEnd('..aa', 2, False));
1987   AssertEquals('',5,  WBrker.NextWordEnd('..aa', 3, False));
1988   AssertEquals('',5,  WBrker.NextWordEnd('..aa', 4, False));
1989   AssertEquals('',-1, WBrker.NextWordEnd('..aa', 5, False));
1990   AssertEquals('', 2, WBrker.NextWordEnd('a a 1', 1, False));
1991   AssertEquals('', 4, WBrker.NextWordEnd('a a 1', 2, False));
1992   AssertEquals('', 4, WBrker.NextWordEnd('a a 1', 3, False));
1993   AssertEquals('', 6, WBrker.NextWordEnd('a a 1', 4, False));
1994   AssertEquals('', 6, WBrker.NextWordEnd('a a 1', 5, False));
1995   AssertEquals('',-1, WBrker.NextWordEnd('a a 1', 6, False));
1996   AssertEquals('', 3, WBrker.NextWordEnd(' a ', 1, False));
1997   AssertEquals('', 3, WBrker.NextWordEnd(' a ', 2, False));
1998   AssertEquals('',-1, WBrker.NextWordEnd(' a ', 3, False));
1999   AssertEquals('',-1, WBrker.NextWordEnd(' a ', 4, False));
2000 
2001   AssertEquals('', 4, WBrker.NextWordEnd('abc  123  ... aa', 1, True));
2002   AssertEquals('', 4, WBrker.NextWordEnd('abc  123  ... aa', 2, True));
2003   AssertEquals('', 4, WBrker.NextWordEnd('abc  123  ... aa', 3, True));
2004   AssertEquals('', 4, WBrker.NextWordEnd('abc  123  ... aa', 4, True)); // abc|
2005   AssertEquals('', 9, WBrker.NextWordEnd('abc  123  ... aa', 5, True));
2006   AssertEquals('', 9, WBrker.NextWordEnd('abc  123  ... aa', 6, True)); // |123
2007   AssertEquals('', 9, WBrker.NextWordEnd('abc  123  ... aa', 7, True)); // 1|23
2008   AssertEquals('', 9, WBrker.NextWordEnd('abc  123  ... aa', 8, True));
2009   AssertEquals('', 9, WBrker.NextWordEnd('abc  123  ... aa', 9, True)); // 123|
2010   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 10, True));
2011   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 11, True)); // |...
2012   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 12, True));
2013   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 13, True));
2014   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 14, True)); // ...|
2015   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 15, True));
2016   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 16, True));
2017   AssertEquals('',17, WBrker.NextWordEnd('abc  123  ... aa', 17, True)); // at eol
2018   AssertEquals('',-1, WBrker.NextWordEnd('abc  123  ... aa', 18, True));
2019   AssertEquals('',-1, WBrker.NextWordEnd('abc  123  ... aa', 19, True));
2020   AssertEquals('',-1, WBrker.NextWordEnd('abc  123  ... aa', 0, True));
2021   AssertEquals('',-1, WBrker.NextWordEnd('abc  123  ... aa', -1, True));
2022   AssertEquals('',-1, WBrker.NextWordEnd('', 1, True));
2023   AssertEquals('',-1, WBrker.NextWordEnd(' ', 1, True));
2024   AssertEquals('',-1, WBrker.NextWordEnd(' ', 2, True));
2025   AssertEquals('',5,  WBrker.NextWordEnd('..aa', 1, True));
2026   AssertEquals('',5,  WBrker.NextWordEnd('..aa', 2, True));
2027   AssertEquals('',5,  WBrker.NextWordEnd('..aa', 3, True));
2028   AssertEquals('',5,  WBrker.NextWordEnd('..aa', 4, True));
2029   AssertEquals('',5, WBrker.NextWordEnd('..aa', 5, True));
2030   AssertEquals('', 2, WBrker.NextWordEnd('a a 1', 1, True));
2031   AssertEquals('', 2, WBrker.NextWordEnd('a a 1', 2, True));
2032   AssertEquals('', 4, WBrker.NextWordEnd('a a 1', 3, True));
2033   AssertEquals('', 4, WBrker.NextWordEnd('a a 1', 4, True));
2034   AssertEquals('', 6, WBrker.NextWordEnd('a a 1', 5, True));
2035   AssertEquals('', 6, WBrker.NextWordEnd('a a 1', 6, True));
2036   AssertEquals('', 3, WBrker.NextWordEnd(' a ', 1, True));
2037   AssertEquals('', 3, WBrker.NextWordEnd(' a ', 2, True));
2038   AssertEquals('', 3, WBrker.NextWordEnd(' a ', 3, True));
2039   AssertEquals('',-1, WBrker.NextWordEnd(' a ', 4, True));
2040 
2041   AssertEquals('',-1, WBrker.PrevWordStart('abc  123  ... aa', 1, False));
2042   AssertEquals('', 1, WBrker.PrevWordStart('abc  123  ... aa', 2, False));
2043   AssertEquals('', 1, WBrker.PrevWordStart('abc  123  ... aa', 3, False));
2044   AssertEquals('', 1, WBrker.PrevWordStart('abc  123  ... aa', 4, False)); // abc|
2045   AssertEquals('', 1, WBrker.PrevWordStart('abc  123  ... aa', 5, False));
2046   AssertEquals('', 1, WBrker.PrevWordStart('abc  123  ... aa', 6, False)); // |123
2047   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 7, False)); // 1|23
2048   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 8, False));
2049   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 9, False)); // 123|
2050   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 10, False));
2051   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 11, False)); // |...
2052   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 12, False));
2053   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 13, False));
2054   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 14, False)); // ...|
2055   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 15, False));
2056   AssertEquals('',15, WBrker.PrevWordStart('abc  123  ... aa', 16, False));
2057   AssertEquals('',15, WBrker.PrevWordStart('abc  123  ... aa', 17, False)); // at eol
2058   AssertEquals('',-1, WBrker.PrevWordStart('abc  123  ... aa', 18, False));
2059   AssertEquals('',-1, WBrker.PrevWordStart('abc  123  ... aa', 19, False));
2060   AssertEquals('',-1, WBrker.PrevWordStart('abc  123  ... aa', 0, False));
2061   AssertEquals('',-1, WBrker.PrevWordStart('abc  123  ... aa', -1, False));
2062   AssertEquals('',-1, WBrker.PrevWordStart('', 1, False));
2063   AssertEquals('',-1, WBrker.PrevWordStart(' ', 1, False));
2064   AssertEquals('',-1, WBrker.PrevWordStart(' ', 2, False));
2065   AssertEquals('',-1, WBrker.PrevWordStart('..aa', 1, False));
2066   AssertEquals('',-1, WBrker.PrevWordStart('..aa', 2, False));
2067   AssertEquals('',-1, WBrker.PrevWordStart('..aa', 3, False));
2068   AssertEquals('', 3, WBrker.PrevWordStart('..aa', 4, False));
2069   AssertEquals('', 3, WBrker.PrevWordStart('..aa', 5, False));
2070   AssertEquals('',-1, WBrker.PrevWordStart('a a 1', 1, False));
2071   AssertEquals('', 1, WBrker.PrevWordStart('a a 1', 2, False));
2072   AssertEquals('', 1, WBrker.PrevWordStart('a a 1', 3, False));
2073   AssertEquals('', 3, WBrker.PrevWordStart('a a 1', 4, False));
2074   AssertEquals('', 3, WBrker.PrevWordStart('a a 1', 5, False));
2075   AssertEquals('', 5, WBrker.PrevWordStart('a a 1', 6, False));
2076   AssertEquals('',-1, WBrker.PrevWordStart(' a ', 1, False));
2077   AssertEquals('',-1, WBrker.PrevWordStart(' a ', 2, False));
2078   AssertEquals('', 2, WBrker.PrevWordStart(' a ', 3, False));
2079   AssertEquals('', 2, WBrker.PrevWordStart(' a ', 4, False));
2080 
2081   AssertEquals('', 1, WBrker.PrevWordStart('abc  123  ... aa', 1, True));
2082   AssertEquals('', 1, WBrker.PrevWordStart('abc  123  ... aa', 2, True));
2083   AssertEquals('', 1, WBrker.PrevWordStart('abc  123  ... aa', 3, True));
2084   AssertEquals('', 1, WBrker.PrevWordStart('abc  123  ... aa', 4, True)); // abc|
2085   AssertEquals('', 1, WBrker.PrevWordStart('abc  123  ... aa', 5, True));
2086   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 6, True)); // |123
2087   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 7, True)); // 1|23
2088   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 8, True));
2089   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 9, True)); // 123|
2090   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 10, True));
2091   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 11, True)); // |...
2092   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 12, True));
2093   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 13, True));
2094   AssertEquals('', 6, WBrker.PrevWordStart('abc  123  ... aa', 14, True)); // ...|
2095   AssertEquals('',15, WBrker.PrevWordStart('abc  123  ... aa', 15, True));
2096   AssertEquals('',15, WBrker.PrevWordStart('abc  123  ... aa', 16, True));
2097   AssertEquals('',15, WBrker.PrevWordStart('abc  123  ... aa', 17, True)); // at eol
2098   AssertEquals('',-1, WBrker.PrevWordStart('abc  123  ... aa', 18, True));
2099   AssertEquals('',-1, WBrker.PrevWordStart('abc  123  ... aa', 19, True));
2100   AssertEquals('',-1, WBrker.PrevWordStart('abc  123  ... aa', 0, True));
2101   AssertEquals('',-1, WBrker.PrevWordStart('abc  123  ... aa', -1, True));
2102   AssertEquals('',-1, WBrker.PrevWordStart('', 1, True));
2103   AssertEquals('',-1, WBrker.PrevWordStart(' ', 1, True));
2104   AssertEquals('',-1, WBrker.PrevWordStart(' ', 2, True));
2105   AssertEquals('',-1, WBrker.PrevWordStart('..aa', 1, True));
2106   AssertEquals('',-1, WBrker.PrevWordStart('..aa', 2, True));
2107   AssertEquals('', 3, WBrker.PrevWordStart('..aa', 3, True));
2108   AssertEquals('', 3, WBrker.PrevWordStart('..aa', 4, True));
2109   AssertEquals('', 3, WBrker.PrevWordStart('..aa', 5, True));
2110   AssertEquals('', 1, WBrker.PrevWordStart('a a 1', 1, True));
2111   AssertEquals('', 1, WBrker.PrevWordStart('a a 1', 2, True));
2112   AssertEquals('', 3, WBrker.PrevWordStart('a a 1', 3, True));
2113   AssertEquals('', 3, WBrker.PrevWordStart('a a 1', 4, True));
2114   AssertEquals('', 5, WBrker.PrevWordStart('a a 1', 5, True));
2115   AssertEquals('', 5, WBrker.PrevWordStart('a a 1', 6, True));
2116   AssertEquals('',-1, WBrker.PrevWordStart(' a ', 1, True));
2117   AssertEquals('', 2, WBrker.PrevWordStart(' a ', 2, True));
2118   AssertEquals('', 2, WBrker.PrevWordStart(' a ', 3, True));
2119   AssertEquals('', 2, WBrker.PrevWordStart(' a ', 4, True));
2120 
2121   AssertEquals('', -1, WBrker.PrevWordEnd('abc  123  ... aa', 1, False));
2122   AssertEquals('', -1, WBrker.PrevWordEnd('abc  123  ... aa', 2, False));
2123   AssertEquals('', -1, WBrker.PrevWordEnd('abc  123  ... aa', 3, False));
2124   AssertEquals('', -1, WBrker.PrevWordEnd('abc  123  ... aa', 4, False)); // abc|
2125   AssertEquals('', 4, WBrker.PrevWordEnd('abc  123  ... aa', 5, False));
2126   AssertEquals('',4, WBrker.PrevWordEnd('abc  123  ... aa', 6, False)); // |123
2127   AssertEquals('',4, WBrker.PrevWordEnd('abc  123  ... aa', 7, False)); // 1|23
2128   AssertEquals('',4, WBrker.PrevWordEnd('abc  123  ... aa', 8, False));
2129   AssertEquals('',4, WBrker.PrevWordEnd('abc  123  ... aa', 9, False)); // 123|
2130   AssertEquals('',9, WBrker.PrevWordEnd('abc  123  ... aa', 10, False));
2131   AssertEquals('',9, WBrker.PrevWordEnd('abc  123  ... aa', 11, False)); // |...
2132   AssertEquals('',9, WBrker.PrevWordEnd('abc  123  ... aa', 12, False));
2133   AssertEquals('',9, WBrker.PrevWordEnd('abc  123  ... aa', 13, False));
2134   AssertEquals('',9, WBrker.PrevWordEnd('abc  123  ... aa', 14, False)); // ...|
2135   AssertEquals('',9, WBrker.PrevWordEnd('abc  123  ... aa', 15, False));
2136   AssertEquals('',9, WBrker.PrevWordEnd('abc  123  ... aa', 16, False));
2137   AssertEquals('',9, WBrker.PrevWordEnd('abc  123  ... aa', 17, False)); // at eol
2138   AssertEquals('',-1, WBrker.PrevWordEnd('abc  123  ... aa', 18, False));
2139   AssertEquals('',-1, WBrker.PrevWordEnd('abc  123  ... aa', 19, False));
2140   AssertEquals('',-1, WBrker.PrevWordEnd('abc  123  ... aa', 0, False));
2141   AssertEquals('',-1, WBrker.PrevWordEnd('abc  123  ... aa', -1, False));
2142   AssertEquals('',-1, WBrker.PrevWordEnd('', 1, False));
2143   AssertEquals('',-1, WBrker.PrevWordEnd(' ', 1, False));
2144   AssertEquals('',-1, WBrker.PrevWordEnd(' ', 2, False));
2145   AssertEquals('',-1,  WBrker.PrevWordEnd('..aa', 1, False));
2146   AssertEquals('',-1,  WBrker.PrevWordEnd('..aa', 2, False));
2147   AssertEquals('',-1, WBrker.PrevWordEnd('..aa', 3, False));
2148   AssertEquals('',-1, WBrker.PrevWordEnd('..aa', 4, False));
2149   AssertEquals('',-1, WBrker.PrevWordEnd('..aa', 5, False));
2150   AssertEquals('',-1, WBrker.PrevWordEnd('a a 1', 1, False));
2151   AssertEquals('',-1, WBrker.PrevWordEnd('a a 1', 2, False));
2152   AssertEquals('', 2, WBrker.PrevWordEnd('a a 1', 3, False));
2153   AssertEquals('', 2, WBrker.PrevWordEnd('a a 1', 4, False));
2154   AssertEquals('', 4, WBrker.PrevWordEnd('a a 1', 5, False));
2155   AssertEquals('', 4, WBrker.PrevWordEnd('a a 1', 6, False));
2156   AssertEquals('',-1, WBrker.PrevWordEnd(' a ', 1, False));
2157   AssertEquals('',-1, WBrker.PrevWordEnd(' a ', 2, False));
2158   AssertEquals('',-1, WBrker.PrevWordEnd(' a ', 3, False));
2159   AssertEquals('', 3, WBrker.PrevWordEnd(' a ', 4, False));
2160 
2161   AssertEquals('', -1, WBrker.PrevWordEnd('abc  123  ... aa', 1, True));
2162   AssertEquals('', -1, WBrker.PrevWordEnd('abc  123  ... aa', 2, True));
2163   AssertEquals('', -1, WBrker.PrevWordEnd('abc  123  ... aa', 3, True));
2164   AssertEquals('', 4, WBrker.PrevWordEnd('abc  123  ... aa', 4, True)); // abc|
2165   AssertEquals('', 4, WBrker.PrevWordEnd('abc  123  ... aa', 5, True));
2166   AssertEquals('', 4, WBrker.PrevWordEnd('abc  123  ... aa', 6, True)); // |123
2167   AssertEquals('', 4, WBrker.PrevWordEnd('abc  123  ... aa', 7, True)); // 1|23
2168   AssertEquals('', 4, WBrker.PrevWordEnd('abc  123  ... aa', 8, True));
2169   AssertEquals('', 9, WBrker.PrevWordEnd('abc  123  ... aa', 9, True)); // 123|
2170   AssertEquals('', 9, WBrker.PrevWordEnd('abc  123  ... aa', 10, True));
2171   AssertEquals('', 9, WBrker.PrevWordEnd('abc  123  ... aa', 11, True)); // |...
2172   AssertEquals('', 9, WBrker.PrevWordEnd('abc  123  ... aa', 12, True));
2173   AssertEquals('', 9, WBrker.PrevWordEnd('abc  123  ... aa', 13, True));
2174   AssertEquals('', 9, WBrker.PrevWordEnd('abc  123  ... aa', 14, True)); // ...|
2175   AssertEquals('', 9, WBrker.PrevWordEnd('abc  123  ... aa', 15, True));
2176   AssertEquals('', 9, WBrker.PrevWordEnd('abc  123  ... aa', 16, True));
2177   AssertEquals('',17, WBrker.PrevWordEnd('abc  123  ... aa', 17, True)); // at eol
2178   AssertEquals('',-1, WBrker.PrevWordEnd('abc  123  ... aa', 18, True));
2179   AssertEquals('',-1, WBrker.PrevWordEnd('abc  123  ... aa', 19, True));
2180   AssertEquals('',-1, WBrker.PrevWordEnd('abc  123  ... aa', 0, True));
2181   AssertEquals('',-1, WBrker.PrevWordEnd('abc  123  ... aa', -1, True));
2182   AssertEquals('',-1, WBrker.PrevWordEnd('', 1, True));
2183   AssertEquals('',-1, WBrker.PrevWordEnd(' ', 1, True));
2184   AssertEquals('',-1, WBrker.PrevWordEnd(' ', 2, True));
2185   AssertEquals('',-1,  WBrker.PrevWordEnd('..aa', 1, True));
2186   AssertEquals('',-1,  WBrker.PrevWordEnd('..aa', 2, True));
2187   AssertEquals('',-1,  WBrker.PrevWordEnd('..aa', 3, True));
2188   AssertEquals('',-1, WBrker.PrevWordEnd('..aa', 4, True));
2189   AssertEquals('', 5, WBrker.PrevWordEnd('..aa', 5, True));
2190   AssertEquals('',-1, WBrker.PrevWordEnd('a a 1', 1, True));
2191   AssertEquals('', 2, WBrker.PrevWordEnd('a a 1', 2, True));
2192   AssertEquals('', 2, WBrker.PrevWordEnd('a a 1', 3, True));
2193   AssertEquals('', 4, WBrker.PrevWordEnd('a a 1', 4, True));
2194   AssertEquals('', 4, WBrker.PrevWordEnd('a a 1', 5, True));
2195   AssertEquals('', 6, WBrker.PrevWordEnd('a a 1', 6, True));
2196   AssertEquals('',-1, WBrker.PrevWordEnd(' a ', 1, True));
2197   AssertEquals('',-1, WBrker.PrevWordEnd(' a ', 2, True));
2198   AssertEquals('', 3, WBrker.PrevWordEnd(' a ', 3, True));
2199   AssertEquals('', 3, WBrker.PrevWordEnd(' a ', 4, True));
2200 
2201 
2202   AssertEquals('', 4, WBrker.NextBoundary('abc  123  ... aa', 1, False));
2203   AssertEquals('', 4, WBrker.NextBoundary('abc  123  ... aa', 2, False));
2204   AssertEquals('', 4, WBrker.NextBoundary('abc  123  ... aa', 3, False));
2205   AssertEquals('', 6, WBrker.NextBoundary('abc  123  ... aa', 4, False)); // abc|
2206   AssertEquals('', 6, WBrker.NextBoundary('abc  123  ... aa', 5, False));
2207   AssertEquals('', 9, WBrker.NextBoundary('abc  123  ... aa', 6, False)); // |123
2208   AssertEquals('', 9, WBrker.NextBoundary('abc  123  ... aa', 7, False)); // 1|23
2209   AssertEquals('', 9, WBrker.NextBoundary('abc  123  ... aa', 8, False));
2210   AssertEquals('',11, WBrker.NextBoundary('abc  123  ... aa', 9, False)); // 123|
2211   AssertEquals('',11, WBrker.NextBoundary('abc  123  ... aa', 10, False));
2212   AssertEquals('',14, WBrker.NextBoundary('abc  123  ... aa', 11, False)); // |...
2213   AssertEquals('',14, WBrker.NextBoundary('abc  123  ... aa', 12, False));
2214   AssertEquals('',14, WBrker.NextBoundary('abc  123  ... aa', 13, False));
2215   AssertEquals('',15, WBrker.NextBoundary('abc  123  ... aa', 14, False)); // ...|
2216   AssertEquals('',17, WBrker.NextBoundary('abc  123  ... aa', 15, False));
2217   AssertEquals('',17, WBrker.NextBoundary('abc  123  ... aa', 16, False));
2218   AssertEquals('',-1, WBrker.NextBoundary('abc  123  ... aa', 17, False)); // at eol
2219   AssertEquals('',-1, WBrker.NextBoundary('abc  123  ... aa', 18, False));
2220   AssertEquals('',-1, WBrker.NextBoundary('abc  123  ... aa', 19, False));
2221   AssertEquals('',-1, WBrker.NextBoundary('abc  123  ... aa', 0, False));
2222   AssertEquals('',-1, WBrker.NextBoundary('abc  123  ... aa', -1, False));
2223   AssertEquals('',-1, WBrker.NextBoundary('', 1, False));
2224   AssertEquals('',-1, WBrker.NextBoundary(' ', 1, False));
2225   AssertEquals('',-1, WBrker.NextBoundary(' ', 2, False));
2226   AssertEquals('',3,  WBrker.NextBoundary('..aa', 1, False));
2227   AssertEquals('',3,  WBrker.NextBoundary('..aa', 2, False));
2228   AssertEquals('',5, WBrker.NextBoundary('..aa', 3, False));
2229   AssertEquals('',5, WBrker.NextBoundary('..aa', 4, False));
2230   AssertEquals('',-1, WBrker.NextBoundary('..aa', 5, False));
2231   AssertEquals('', 2, WBrker.NextBoundary('a a 1', 1, False));
2232   AssertEquals('', 3, WBrker.NextBoundary('a a 1', 2, False));
2233   AssertEquals('', 4, WBrker.NextBoundary('a a 1', 3, False));
2234   AssertEquals('', 5, WBrker.NextBoundary('a a 1', 4, False));
2235   AssertEquals('', 6, WBrker.NextBoundary('a a 1', 5, False));
2236   AssertEquals('',-1, WBrker.NextBoundary('a a 1', 6, False));
2237   AssertEquals('', 2, WBrker.NextBoundary(' a ', 1, False));
2238   AssertEquals('', 3, WBrker.NextBoundary(' a ', 2, False));
2239   AssertEquals('',-1, WBrker.NextBoundary(' a ', 3, False));
2240   AssertEquals('',-1, WBrker.NextBoundary(' a ', 4, False));
2241 
2242   AssertEquals('', 1, WBrker.NextBoundary('abc  123  ... aa', 1, True));
2243   AssertEquals('', 4, WBrker.NextBoundary('abc  123  ... aa', 2, True));
2244   AssertEquals('', 4, WBrker.NextBoundary('abc  123  ... aa', 3, True));
2245   AssertEquals('', 4, WBrker.NextBoundary('abc  123  ... aa', 4, True)); // abc|
2246   AssertEquals('', 6, WBrker.NextBoundary('abc  123  ... aa', 5, True));
2247   AssertEquals('', 6, WBrker.NextBoundary('abc  123  ... aa', 6, True)); // |123
2248   AssertEquals('', 9, WBrker.NextBoundary('abc  123  ... aa', 7, True)); // 1|23
2249   AssertEquals('', 9, WBrker.NextBoundary('abc  123  ... aa', 8, True));
2250   AssertEquals('', 9, WBrker.NextBoundary('abc  123  ... aa', 9, True)); // 123|
2251   AssertEquals('',11, WBrker.NextBoundary('abc  123  ... aa', 10, True));
2252   AssertEquals('',11, WBrker.NextBoundary('abc  123  ... aa', 11, True)); // |...
2253   AssertEquals('',14, WBrker.NextBoundary('abc  123  ... aa', 12, True));
2254   AssertEquals('',14, WBrker.NextBoundary('abc  123  ... aa', 13, True));
2255   AssertEquals('',14, WBrker.NextBoundary('abc  123  ... aa', 14, True)); // ...|
2256   AssertEquals('',15, WBrker.NextBoundary('abc  123  ... aa', 15, True));
2257   AssertEquals('',17, WBrker.NextBoundary('abc  123  ... aa', 16, True));
2258   AssertEquals('',17, WBrker.NextBoundary('abc  123  ... aa', 17, True)); // at eol
2259   AssertEquals('',-1, WBrker.NextBoundary('abc  123  ... aa', 18, True));
2260   AssertEquals('',-1, WBrker.NextBoundary('abc  123  ... aa', 19, True));
2261   AssertEquals('',-1, WBrker.NextBoundary('abc  123  ... aa', 0, True));
2262   AssertEquals('',-1, WBrker.NextBoundary('abc  123  ... aa', -1, True));
2263   AssertEquals('',-1, WBrker.NextBoundary('', 1, True));
2264   AssertEquals('',-1, WBrker.NextBoundary(' ', 1, True));
2265   AssertEquals('',-1, WBrker.NextBoundary(' ', 2, True));
2266   AssertEquals('',1,  WBrker.NextBoundary('..aa', 1, True));
2267   AssertEquals('',3,  WBrker.NextBoundary('..aa', 2, True));
2268   AssertEquals('',3, WBrker.NextBoundary('..aa', 3, True));
2269   AssertEquals('',5, WBrker.NextBoundary('..aa', 4, True));
2270   AssertEquals('',5, WBrker.NextBoundary('..aa', 5, True));
2271   AssertEquals('', 1, WBrker.NextBoundary('a a 1', 1, True));
2272   AssertEquals('', 2, WBrker.NextBoundary('a a 1', 2, True));
2273   AssertEquals('', 3, WBrker.NextBoundary('a a 1', 3, True));
2274   AssertEquals('', 4, WBrker.NextBoundary('a a 1', 4, True));
2275   AssertEquals('', 5, WBrker.NextBoundary('a a 1', 5, True));
2276   AssertEquals('', 6, WBrker.NextBoundary('a a 1', 6, True));
2277   AssertEquals('', 2, WBrker.NextBoundary(' a ', 1, True));
2278   AssertEquals('', 2, WBrker.NextBoundary(' a ', 2, True));
2279   AssertEquals('', 3, WBrker.NextBoundary(' a ', 3, True));
2280   AssertEquals('',-1, WBrker.NextBoundary(' a ', 4, True));
2281 
2282   AssertEquals('',-1, WBrker.PrevBoundary('abc  123  ... aa', 1, False));
2283   AssertEquals('', 1, WBrker.PrevBoundary('abc  123  ... aa', 2, False));
2284   AssertEquals('', 1, WBrker.PrevBoundary('abc  123  ... aa', 3, False));
2285   AssertEquals('', 1, WBrker.PrevBoundary('abc  123  ... aa', 4, False)); // abc|
2286   AssertEquals('', 4, WBrker.PrevBoundary('abc  123  ... aa', 5, False));
2287   AssertEquals('', 4, WBrker.PrevBoundary('abc  123  ... aa', 6, False)); // |123
2288   AssertEquals('', 6, WBrker.PrevBoundary('abc  123  ... aa', 7, False)); // 1|23
2289   AssertEquals('', 6, WBrker.PrevBoundary('abc  123  ... aa', 8, False));
2290   AssertEquals('', 6, WBrker.PrevBoundary('abc  123  ... aa', 9, False)); // 123|
2291   AssertEquals('', 9, WBrker.PrevBoundary('abc  123  ... aa', 10, False));
2292   AssertEquals('', 9, WBrker.PrevBoundary('abc  123  ... aa', 11, False)); // |...
2293   AssertEquals('',11, WBrker.PrevBoundary('abc  123  ... aa', 12, False));
2294   AssertEquals('',11, WBrker.PrevBoundary('abc  123  ... aa', 13, False));
2295   AssertEquals('',11, WBrker.PrevBoundary('abc  123  ... aa', 14, False)); // ...|
2296   AssertEquals('',14, WBrker.PrevBoundary('abc  123  ... aa', 15, False));
2297   AssertEquals('',15, WBrker.PrevBoundary('abc  123  ... aa', 16, False));
2298   AssertEquals('',15, WBrker.PrevBoundary('abc  123  ... aa', 17, False)); // at eol
2299   AssertEquals('',-1, WBrker.PrevBoundary('abc  123  ... aa', 18, False));
2300   AssertEquals('',-1, WBrker.PrevBoundary('abc  123  ... aa', 19, False));
2301   AssertEquals('',-1, WBrker.PrevBoundary('abc  123  ... aa', 0, False));
2302   AssertEquals('',-1, WBrker.PrevBoundary('abc  123  ... aa', -1, False));
2303   AssertEquals('',-1, WBrker.PrevBoundary('', 1, False));
2304   AssertEquals('',-1, WBrker.PrevBoundary(' ', 1, False));
2305   AssertEquals('',-1, WBrker.PrevBoundary(' ', 2, False));
2306   AssertEquals('',-1,  WBrker.PrevBoundary('..aa', 1, False));
2307   AssertEquals('',1,  WBrker.PrevBoundary('..aa', 2, False));
2308   AssertEquals('',1, WBrker.PrevBoundary('..aa', 3, False));
2309   AssertEquals('',3, WBrker.PrevBoundary('..aa', 4, False));
2310   AssertEquals('',3, WBrker.PrevBoundary('..aa', 5, False));
2311   AssertEquals('',-1, WBrker.PrevBoundary('a a 1', 1, False));
2312   AssertEquals('', 1, WBrker.PrevBoundary('a a 1', 2, False));
2313   AssertEquals('', 2, WBrker.PrevBoundary('a a 1', 3, False));
2314   AssertEquals('', 3, WBrker.PrevBoundary('a a 1', 4, False));
2315   AssertEquals('', 4, WBrker.PrevBoundary('a a 1', 5, False));
2316   AssertEquals('', 5, WBrker.PrevBoundary('a a 1', 6, False));
2317   AssertEquals('',-1, WBrker.PrevBoundary(' a ', 1, False));
2318   AssertEquals('',-1, WBrker.PrevBoundary(' a ', 2, False));
2319   AssertEquals('', 2, WBrker.PrevBoundary(' a ', 3, False));
2320   AssertEquals('', 3, WBrker.PrevBoundary(' a ', 4, False));
2321 
2322   AssertEquals('', 1, WBrker.PrevBoundary('abc  123  ... aa', 1, True));
2323   AssertEquals('', 1, WBrker.PrevBoundary('abc  123  ... aa', 2, True));
2324   AssertEquals('', 1, WBrker.PrevBoundary('abc  123  ... aa', 3, True));
2325   AssertEquals('', 4, WBrker.PrevBoundary('abc  123  ... aa', 4, True)); // abc|
2326   AssertEquals('', 4, WBrker.PrevBoundary('abc  123  ... aa', 5, True));
2327   AssertEquals('', 6, WBrker.PrevBoundary('abc  123  ... aa', 6, True)); // |123
2328   AssertEquals('', 6, WBrker.PrevBoundary('abc  123  ... aa', 7, True)); // 1|23
2329   AssertEquals('', 6, WBrker.PrevBoundary('abc  123  ... aa', 8, True));
2330   AssertEquals('', 9, WBrker.PrevBoundary('abc  123  ... aa', 9, True)); // 123|
2331   AssertEquals('', 9, WBrker.PrevBoundary('abc  123  ... aa', 10, True));
2332   AssertEquals('',11, WBrker.PrevBoundary('abc  123  ... aa', 11, True)); // |...
2333   AssertEquals('',11, WBrker.PrevBoundary('abc  123  ... aa', 12, True));
2334   AssertEquals('',11, WBrker.PrevBoundary('abc  123  ... aa', 13, True));
2335   AssertEquals('',14, WBrker.PrevBoundary('abc  123  ... aa', 14, True)); // ...|
2336   AssertEquals('',15, WBrker.PrevBoundary('abc  123  ... aa', 15, True));
2337   AssertEquals('',15, WBrker.PrevBoundary('abc  123  ... aa', 16, True));
2338   AssertEquals('',17, WBrker.PrevBoundary('abc  123  ... aa', 17, True)); // at eol
2339   AssertEquals('',-1, WBrker.PrevBoundary('abc  123  ... aa', 18, True));
2340   AssertEquals('',-1, WBrker.PrevBoundary('abc  123  ... aa', 19, True));
2341   AssertEquals('',-1, WBrker.PrevBoundary('abc  123  ... aa', 0, True));
2342   AssertEquals('',-1, WBrker.PrevBoundary('abc  123  ... aa', -1, True));
2343   AssertEquals('',-1, WBrker.PrevBoundary('', 1, True));
2344   AssertEquals('',-1, WBrker.PrevBoundary(' ', 1, True));
2345   AssertEquals('',-1, WBrker.PrevBoundary(' ', 2, True));
2346   AssertEquals('',1,  WBrker.PrevBoundary('..aa', 1, True));
2347   AssertEquals('',1,  WBrker.PrevBoundary('..aa', 2, True));
2348   AssertEquals('',3, WBrker.PrevBoundary('..aa', 3, True));
2349   AssertEquals('',3, WBrker.PrevBoundary('..aa', 4, True));
2350   AssertEquals('',5, WBrker.PrevBoundary('..aa', 5, True));
2351   AssertEquals('', 1, WBrker.PrevBoundary('a a 1', 1, True));
2352   AssertEquals('', 2, WBrker.PrevBoundary('a a 1', 2, True));
2353   AssertEquals('', 3, WBrker.PrevBoundary('a a 1', 3, True));
2354   AssertEquals('', 4, WBrker.PrevBoundary('a a 1', 4, True));
2355   AssertEquals('', 5, WBrker.PrevBoundary('a a 1', 5, True));
2356   AssertEquals('', 6, WBrker.PrevBoundary('a a 1', 6, True));
2357   AssertEquals('',-1, WBrker.PrevBoundary(' a ', 1, True));
2358   AssertEquals('', 2, WBrker.PrevBoundary(' a ', 2, True));
2359   AssertEquals('', 3, WBrker.PrevBoundary(' a ', 3, True));
2360   AssertEquals('', 3, WBrker.PrevBoundary(' a ', 4, True));
2361 
2362 
2363   FreeAndNil(WBrker);
2364 end;
2365 
2366 procedure TTestBasicSynEdit.TestSearchReplace;
TestText1null2367   function TestText1: TStringArray;
2368   begin
2369     SetLength(Result, 9);
2370     Result[0] := 'aaaa';
2371     Result[1] := 'xx11';
2372     Result[2] := 'cccc';
2373     Result[3] := '1 1 x';
2374     Result[4] := '9';
2375     Result[5] := '1 1 x';
2376     Result[6] := '9';
2377     Result[7] := '';
2378     Result[8] := '';
2379   end;
2380 
2381   type
2382     TCaretBlockCoordinates = record x,y, BBx, BBy, BEx, BEy: Integer end;
2383 
expCnull2384   function expC(ExpCaretX, ExpCaretY, ExpBBX, ExpBBY, ExpBEX, ExpBEY : Integer): TCaretBlockCoordinates;
2385   begin
2386     with result do begin
2387       x   := ExpCaretX;      y   := ExpCaretY;
2388       BBx := ExpBBX;         BBy := ExpBBY;
2389       BEx := ExpBEX;         BEy := ExpBEY;
2390     end;
2391   end;
expCnull2392   function expC(ExpCaretX, ExpCaretY: Integer; ExpBBX : Integer = -1): TCaretBlockCoordinates;
2393   begin
2394     Result := ExpC(ExpCaretX, ExpCaretY, ExpBBX, -1, -1, -1);
2395   end;
expCnull2396   function expC(ExpBBX, ExpBBY, ExpBEX, ExpBEY: Integer): TCaretBlockCoordinates;
2397   begin
2398     with result do begin
2399       x   := -1;          y   := -1;
2400       BBx := ExpBBX;      BBy := ExpBBY;
2401       BEx := ExpBEX;      BEy := ExpBEY;
2402     end;
2403   end;
expCNonull2404   function expCNo(ExpCaretX, ExpCaretY: Integer): TCaretBlockCoordinates; // no solection
2405   begin
2406     Result := expC(ExpCaretX, ExpCaretY, -2);
2407   end;
expCFwnull2408   function expCFw(ExpBBX, ExpBBY, ExpBEX, ExpBEY: Integer): TCaretBlockCoordinates;
2409   begin // Forward selection
2410     with result do begin
2411       x   := ExpBEX;      y   := ExpBEY;
2412       BBx := ExpBBX;      BBy := ExpBBY;
2413       BEx := ExpBEX;      BEy := ExpBEY;
2414     end;
2415   end;
expCBwnull2416   function expCBw(ExpBBX, ExpBBY, ExpBEX, ExpBEY: Integer): TCaretBlockCoordinates;
2417   begin // Backward selection
2418     with result do begin
2419       x   := ExpBBX;      y   := ExpBBY;
2420       BBx := ExpBBX;      BBy := ExpBBY;
2421       BEx := ExpBEX;      BEy := ExpBEY;
2422     end;
2423   end;
2424 
nextSelnull2425   function nextSel(ExpBBX, ExpBBY, ExpBEX, ExpBEY: Integer): TCaretBlockCoordinates;
2426   begin
2427     Result := expC(ExpBBX, ExpBBY, ExpBEX, ExpBEY);
2428   end;
2429 
2430   var
2431     TheTestText: TStringArray;
2432     NextTestSetSelection: TCaretBlockCoordinates;
2433 
2434   procedure TestCoord(Name: String; ExpCoord: TCaretBlockCoordinates);
2435   begin
2436     if ExpCoord.x > 0 then
2437       TestIsCaret(Name+' Caret ', ExpCoord.x, ExpCoord.y);
2438     if ExpCoord.BBx > 0 then
2439       TestIsSelection(Name+' Selection ', ExpCoord.BBx, ExpCoord.BBy, ExpCoord.BEx, ExpCoord.BEy);
2440     if ExpCoord.BBx = -2 then
2441       TestIsSelection(Name+' NO Selection ', SynEdit.LogicalCaretXY.x, SynEdit.CaretY, SynEdit.LogicalCaretXY.x, SynEdit.CaretY);
2442   end;
2443 
2444   procedure TestSearch(Name, Find, Repl: String; SrcOpts: TSynSearchOptions;
2445     CaretX, CaretY: Integer;
2446     ExpCnt: Integer; ExpTxt: Array of const;
2447     ExpSearchCoord, ExpReplCoord: TCaretBlockCoordinates
2448   );
2449   var
2450     got: Integer;
2451   begin
2452     ReCreateEdit;
2453     SynEdit.Options := SynEdit.Options + [eoScrollPastEol];
2454     Name := ' - ' + Name;
2455 
2456     PushBaseName('Search');
2457       SetLines(TheTestText);
2458       if NextTestSetSelection.BBx > 0 then
2459         with NextTestSetSelection do SetCaretAndSel(BBx, BBy, BEx, BEy)
2460       else
2461         SetCaret(CaretX, CaretY);
2462       got := SynEdit.SearchReplace(Find, '', SrcOpts - [ssoReplace, ssoReplaceAll]);
2463       AssertEquals(BaseTestName + Name + 'Result Count', Min(ExpCnt,1), got);
2464       TestCoord(Name, ExpSearchCoord);
2465     PopBaseName;
2466 
2467     PushBaseName('Search ssoRegExprMultiLine');
2468       SetLines(TheTestText);
2469       if NextTestSetSelection.BBx > 0 then
2470         with NextTestSetSelection do SetCaretAndSel(BBx, BBy, BEx, BEy)
2471       else
2472         SetCaret(CaretX, CaretY);
2473       got := SynEdit.SearchReplace(Find, '', SrcOpts - [ssoReplace, ssoReplaceAll]+[ssoRegExprMultiLine]);
2474       AssertEquals(BaseTestName + Name + 'Result Count', Min(ExpCnt,1), got);
2475       TestCoord(Name, ExpSearchCoord);
2476     PopBaseName;
2477 
2478     if (SrcOpts * [ssoReplace, ssoReplaceAll]) = [] then exit;
2479 
2480     PushBaseName('Replace');
2481       SetLines(TheTestText);
2482       if NextTestSetSelection.BBx > 0 then
2483         with NextTestSetSelection do SetCaretAndSel(BBx, BBy, BEx, BEy)
2484       else
2485         SetCaret(CaretX, CaretY);
2486       got := SynEdit.SearchReplace(Find, Repl, SrcOpts);
2487       AssertEquals(BaseTestName + Name + 'Result Count', ExpCnt, got);
2488       TestIsText(Name + 'Result Text', TheTestText, ExpTxt);
2489       TestCoord(Name, ExpReplCoord);
2490     PopBaseName;
2491 
2492     PushBaseName('Replace ssoRegExprMultiLine');
2493       SetLines(TheTestText);
2494       if NextTestSetSelection.BBx > 0 then
2495         with NextTestSetSelection do SetCaretAndSel(BBx, BBy, BEx, BEy)
2496       else
2497         SetCaret(CaretX, CaretY);
2498       got := SynEdit.SearchReplace(Find, Repl, SrcOpts+[ssoRegExprMultiLine]);
2499       AssertEquals(BaseTestName + Name + 'Result Count', ExpCnt, got);
2500       TestIsText(Name + 'Result Text', TheTestText, ExpTxt);
2501       TestCoord(Name, ExpReplCoord);
2502     PopBaseName;
2503 
2504     NextTestSetSelection := expCNo(-1,-1);
2505   end;
2506 
2507   procedure TestSearch(Name, Find, Repl: String; SrcOpts: TSynSearchOptions;
2508     CaretX, CaretY: Integer; ExpCnt: Integer; ExpTxt: Array of const);
2509   begin
2510     TestSearch(Name, Find, Repl, SrcOpts, CaretX, CaretY, ExpCnt, ExpTxt, ExpC(-1,-1), ExpC(-1,-1));
2511   end;
2512   procedure TestSearch(Name, Find, Repl: String; Opts: TSynSearchOptions;
2513     ExpCnt: Integer; ExpTxt: Array of const);
2514   begin
2515     if ssoBackwards in Opts
2516     then TestSearch(Name, Find, Repl, Opts, 1,length(TheTestText)-1, ExpCnt, ExpTxt)
2517     else TestSearch(Name, Find, Repl, Opts, 1,1, ExpCnt, ExpTxt);
2518   end;
2519   procedure TestSearch(Name, Find, Repl: String; Opts: TSynSearchOptions;
2520     ExpCnt: Integer; ExpTxt: Array of const; ExpSearchCoord, ExpReplCoord: TCaretBlockCoordinates);
2521   begin
2522     if ssoBackwards in Opts
2523     then TestSearch(Name, Find, Repl, Opts, 1,length(TheTestText)-1, ExpCnt, ExpTxt, ExpSearchCoord, ExpReplCoord)
2524     else TestSearch(Name, Find, Repl, Opts, 1,1, ExpCnt, ExpTxt, ExpSearchCoord, ExpReplCoord);
2525   end;
2526 
2527 const
2528   LE = LineEnding;
2529   optAllAll: TSynSearchOptions = [ssoEntireScope, ssoReplace, ssoReplaceAll];
2530   optAllAllB: TSynSearchOptions = [ssoEntireScope, ssoReplace, ssoReplaceAll, ssoBackwards];
2531   optSelAll: TSynSearchOptions = [ssoSelectedOnly, ssoReplace, ssoReplaceAll];
2532   optSelAllB: TSynSearchOptions = [ssoSelectedOnly, ssoReplace, ssoReplaceAll, ssoBackwards];
2533   var f, r: String;
2534   var txl: Integer;
2535 begin
2536   FPersistSel := False;
2537   TheTestText := TestText1;
2538   txl := length(TheTestText)-1;
2539   NextTestSetSelection := nextSel(0,0, 0,0);
2540 
2541   PushBaseName('Find single line term ');
2542     PushBaseName('no match ');
2543       f := '11xx';
2544       r := '2222';
2545       TestSearch('',         f, r, optAllAll,  0, [], expCNo(1,1),   expCNo(1,1) );
2546       TestSearch('backward', f, r, optAllAllB, 0, [], expCNo(1,txl), expCNo(1,txl) );
2547 
2548     PopPushBaseName('match - full line');
2549       f := 'xx11';
2550       r := '2222';
2551       TestSearch('',         f, r, optAllAll,  1, [2, '2222'], expCFw(1,2, 5,2), expCFw(1,2, 5,2) );
2552       TestSearch('backward', f, r, optAllAllB, 1, [2, '2222'], expCBw(1,2, 5,2), expCBw(1,2, 5,2) );
2553 
2554       r := '';
2555       TestSearch('repl-empty ',         f, r, optAllAll,  1, [2, ''], expCFw(1,2, 5,2), expCNo(1,2) );
2556       TestSearch('repl-empty backward', f, r, optAllAllB, 1, [2, ''], expCBw(1,2, 5,2), expCNo(1,2) );
2557 
2558       r := LE;
2559       TestSearch('repl-CR ',         f, r, optAllAll,  1, [2, '', ''], expCFw(1,2, 5,2), expCFw(1,2, 1,3) );
2560       TestSearch('repl-CR backward', f, r, optAllAllB, 1, [2, '', ''], expCBw(1,2, 5,2), expCBw(1,2, 1,3) );
2561 
2562     PopPushBaseName('match - part line (end)');
2563       f := 'x11';
2564       r := '22';
2565       TestSearch('repl-shorter ',         f, r, optAllAll,  1, [2, 'x22'], expCFw(2,2, 5,2), expCFw(2,2, 4,2) );
2566       TestSearch('repl-shorter backward', f, r, optAllAllB, 1, [2, 'x22'], expCBw(2,2, 5,2), expCBw(2,2, 4,2) );
2567 
2568       r := '2222';
2569       TestSearch('repl-longer ',         f, r, optAllAll,  1, [2, 'x2222'], expCFw(2,2, 5,2), expCFw(2,2, 6,2) );
2570       TestSearch('repl-longer backward', f, r, optAllAllB, 1, [2, 'x2222'], expCBw(2,2, 5,2), expCBw(2,2, 6,2) );
2571 
2572       r := LE;
2573       TestSearch('repl-CR ',         f, r, optAllAll,  1, [2, 'x', ''], expCFw(2,2, 5,2), expCFw(2,2, 1,3) );
2574       TestSearch('repl-CR backward', f, r, optAllAllB, 1, [2, 'x', ''], expCBw(2,2, 5,2), expCBw(2,2, 1,3) );
2575 
2576     PopPushBaseName('match in selection');
2577       f := '1';
2578       r := 'N';
2579       NextTestSetSelection := nextSel(1,4, 2,6);
2580       TestSearch('repl-at-end ',         f, r, optSelAll,  3, [4,'N N x', 6,'N 1 x'], expCFw(1,4, 2,4), expCFw(1,6, 2,6) );
2581       NextTestSetSelection := nextSel(1,4, 2,6);
2582       TestSearch('repl-at-end backward', f, r, optSelAllB, 3, [4,'N N x', 6,'N 1 x'], expCBw(1,6, 2,6), expCBw(1,4, 2,4) );
2583 
2584       NextTestSetSelection := nextSel(1,4, 3,6);
2585       TestSearch('no-repl-after-end ',         f, r, optSelAll,  3, [4,'N N x', 6,'N 1 x'], expCFw(1,4, 2,4), expCFw(1,6, 2,6) );
2586       NextTestSetSelection := nextSel(1,4, 3,6);
2587       TestSearch('no-repl-after-end backward', f, r, optSelAllB, 3, [4,'N N x', 6,'N 1 x'], expCBw(1,6, 2,6), expCBw(1,4, 2,4) );
2588 
2589       NextTestSetSelection := nextSel(1,4, 1,6);
2590       TestSearch('no-repl-after-end(BOL) ',         f, r, optSelAll,  2, [4,'N N x'], expCFw(1,4, 2,4), expCFw(3,4, 4,4) );
2591       NextTestSetSelection := nextSel(1,4, 1,6);
2592       TestSearch('no-repl-after-end(BOL) backward', f, r, optSelAllB, 2, [4,'N N x'], expCBw(3,4, 4,4), expCBw(1,4, 2,4) );
2593 
2594     PopPushBaseName('match in selection, repl-SHORTER - to empty');
2595       r := '';
2596       NextTestSetSelection := nextSel(1,4, 4,6);
2597       TestSearch('repl-at-end ',         f, r, optSelAll,  4, [4,'  x', 6,'  x'], expCFw(1,4, 2,4), expCNo(2,6) );
2598       NextTestSetSelection := nextSel(1,4, 4,6);
2599       TestSearch('repl-at-end backward', f, r, optSelAllB, 4, [4,'  x', 6,'  x'], expCBw(3,6, 4,6), expCNo(1,4) );
2600 
2601       NextTestSetSelection := nextSel(1,4, 3,6);
2602       TestSearch('no-repl-after-end ',         f, r, optSelAll,  3, [4,'  x', 6,' 1 x'], expCFw(1,4, 2,4), expCNo(1,6) );
2603       NextTestSetSelection := nextSel(1,4, 3,6);
2604       TestSearch('no-repl-after-end backward', f, r, optSelAllB, 3, [4,'  x', 6,' 1 x'], expCBw(1,6, 2,6), expCNo(1,4) );
2605 
2606     PopPushBaseName('match in selection, repl-LONGER');
2607       r := 'NN';
2608       NextTestSetSelection := nextSel(1,4, 4,6);
2609       TestSearch('repl-at-end ',         f, r, optSelAll,  4, [4,'NN NN x', 6,'NN NN x'], expCFw(1,4, 2,4), expCFw(4,6, 6,6) );
2610       NextTestSetSelection := nextSel(1,4, 4,6);
2611       TestSearch('repl-at-end backward', f, r, optSelAllB, 4, [4,'NN NN x', 6,'NN NN x'], expCBw(3,6, 4,6), expCBw(1,4, 3,4) );
2612 
2613       NextTestSetSelection := nextSel(1,4, 3,6);
2614       TestSearch('no-repl-after-end ',         f, r, optSelAll,  3, [4,'NN NN x', 6,'NN 1 x'], expCFw(1,4, 2,4), expCFw(1,6, 3,6) );
2615       NextTestSetSelection := nextSel(1,4, 3,6);
2616       TestSearch('no-repl-after-end backward', f, r, optSelAllB, 3, [4,'NN NN x', 6,'NN 1 x'], expCBw(1,6, 2,6), expCBw(1,4, 3,4) );
2617 
2618     PopPushBaseName('match in selection, repl-CR');
2619       r := LE;
2620       NextTestSetSelection := nextSel(1,4, 4,6);
2621       TestSearch('repl-at-end ',         f, r, optSelAll,  4, [4,'',' ',' x', 8,'',' ',' x'], expCFw(1,4, 2,4), expCFw(2,9, 1,10) );
2622       NextTestSetSelection := nextSel(1,4, 4,6);
2623       TestSearch('repl-at-end backward', f, r, optSelAllB, 4, [4,'',' ',' x', 8,'',' ',' x'], expCBw(3,6, 4,6), expCBw(1,4, 1,5) );
2624 
2625       NextTestSetSelection := nextSel(1,4, 3,6);
2626       TestSearch('no-repl-after-end ',         f, r, optSelAll,  3, [4,'',' ',' x', 8,'',' 1 x'], expCFw(1,4, 2,4), expCFw(1,8, 1,9) );
2627       NextTestSetSelection := nextSel(1,4, 3,6);
2628       TestSearch('no-repl-after-end backward', f, r, optSelAllB, 3, [4,'',' ',' x', 8,'',' 1 x'], expCBw(1,6, 2,6), expCBw(1,4, 1,5) );
2629 
2630     PopBaseName;
2631 
2632 
2633   PopPushBaseName('Find with trailing CR ');
2634     PushBaseName('no match ');
2635       f := 'xx'+LE;
2636       r := '11'+LE;
2637       TestSearch('',         f, r, optSelAll,  0, [], expCNo(1,1),   expCNo(1,1) );
2638       TestSearch('backward', f, r, optSelAllB, 0, [], expCNo(1,txl), expCNo(1,txl) );
2639 
2640     PopPushBaseName('match ');
2641       f := '11'+LE;
2642       r := 'bb'+LE;
2643       TestSearch('',         f, r, optSelAll,  1, [2, 'xxbb'], expCFw(3,2, 1,3), expCFw(3,2, 1,3) );
2644       TestSearch('backward', f, r, optSelAllB, 1, [2, 'xxbb'], expCBw(3,2, 1,3), expCBw(3,2, 1,3) );
2645     PopBaseName;
2646 
2647   PopPushBaseName('Find with leading CR ');
2648     PushBaseName('no match ');
2649       f := LE+'11';
2650       r := LE+'xx';
2651       TestSearch('',         f, r, optSelAll,  0, [], expCNo(1,1),   expCNo(1,1) );
2652       TestSearch('backward', f, r, optSelAllB, 0, [], expCNo(1,txl), expCNo(1,txl) );
2653 
2654     PopPushBaseName('match ');
2655       f := LE+'xx';
2656       r := LE+'bb';
2657       TestSearch('',         f, r, optSelAll,  1, [2, 'bb11'], expCFw(5,1, 3,2), expCFw(5,1, 3,2) );
2658       TestSearch('backward', f, r, optSelAllB, 1, [2, 'bb11'], expCBw(5,1, 3,2), expCBw(5,1, 3,2) );
2659     PopBaseName;
2660 
2661   PopBaseName;
2662 end;
2663 
2664 procedure TTestBasicSynEdit.TestMoveLineUpDown;
2665 begin
2666   FDoInit := @DoInit1;
2667   FPersistSel := False;
2668   FTestLines := @TestLines2;
2669 
2670   FTestCommand := ecMoveLineUp;
2671   TestCommand('ecMoveLineUp - no sel',           1,3,       1,2,       [2, '123 456 789',  3, 'ABC DEF GHI']);
2672   TestCommand('ecMoveLineUp - no sel',           4,3,       4,2,       [2, '123 456 789',  3, 'ABC DEF GHI']);
2673   TestCommand('ecMoveLineUp - 1L part sel',      2,3, 4,3,  2,2, 4,2,  [2, '123 456 789',  3, 'ABC DEF GHI']);
2674   TestCommand('ecMoveLineUp - 1L part sel',      2,3, 1,4,  2,2, 1,3,  [2, '123 456 789',  3, 'ABC DEF GHI']);
2675   TestCommand('ecMoveLineUp - 1L full sel',      1,3, 1,4,  1,2, 1,3,  [2, '123 456 789',  3, 'ABC DEF GHI']);
2676 
2677   TestCommand('ecMoveLineUp - 2L part sel',      2,3, 3,4,  2,2, 3,3,  [2, '123 456 789',  3, 'mno pqr stu', 4, 'ABC DEF GHI']);
2678   TestCommand('ecMoveLineUp - 2L part sel',      2,3, 1,5,  2,2, 1,4,  [2, '123 456 789',  3, 'mno pqr stu', 4, 'ABC DEF GHI']);
2679   TestCommand('ecMoveLineUp - 2L full sel',      1,3, 1,5,  1,2, 1,4,  [2, '123 456 789',  3, 'mno pqr stu', 4, 'ABC DEF GHI']);
2680 
2681 
2682   FTestCommand := ecMoveLineDown;
2683   TestCommand('ecMoveLineDown - no sel',           1,2,       1,3,       [2, '123 456 789',  3, 'ABC DEF GHI']);
2684   TestCommand('ecMoveLineDown - no sel',           4,2,       4,3,       [2, '123 456 789',  3, 'ABC DEF GHI']);
2685   TestCommand('ecMoveLineDown - 1L part sel',      2,2, 4,2,  2,3, 4,3,  [2, '123 456 789',  3, 'ABC DEF GHI']);
2686   TestCommand('ecMoveLineDown - 1L part sel',      2,2, 1,3,  2,3, 1,4,  [2, '123 456 789',  3, 'ABC DEF GHI']);
2687   TestCommand('ecMoveLineDown - 1L full sel',      1,2, 1,3,  1,3, 1,4,  [2, '123 456 789',  3, 'ABC DEF GHI']);
2688 
2689   TestCommand('ecMoveLineDown - 2L part sel',      2,2, 3,3,  2,3, 3,4,  [2, 'mno pqr stu', 3, 'ABC DEF GHI', 4, '123 456 789']);
2690   TestCommand('ecMoveLineDown - 2L part sel',      2,2, 1,4,  2,3, 1,5,  [2, 'mno pqr stu', 3, 'ABC DEF GHI', 4, '123 456 789']);
2691   TestCommand('ecMoveLineDown - 2L full sel',      1,2, 1,4,  1,3, 1,5,  [2, 'mno pqr stu', 3, 'ABC DEF GHI', 4, '123 456 789']);
2692 
2693 
2694   FPersistSel := true;
2695 
2696   FTestCommand := ecMoveLineUp;
2697   TestCommand('ecMoveLineUp - no sel',           1,3,       1,2,       [2, '123 456 789',  3, 'ABC DEF GHI']);
2698   TestCommand('ecMoveLineUp - no sel',           4,3,       4,2,       [2, '123 456 789',  3, 'ABC DEF GHI']);
2699   TestCommand('ecMoveLineUp - 1L part sel',      2,3, 4,3,  2,2, 4,2,  [2, '123 456 789',  3, 'ABC DEF GHI']);
2700   TestCommand('ecMoveLineUp - 1L part sel',      2,3, 1,4,  2,2, 1,3,  [2, '123 456 789',  3, 'ABC DEF GHI']);
2701   TestCommand('ecMoveLineUp - 1L full sel',      1,3, 1,4,  1,2, 1,3,  [2, '123 456 789',  3, 'ABC DEF GHI']);
2702 
2703   TestCommand('ecMoveLineUp - 2L part sel',      2,3, 3,4,  2,2, 3,3,  [2, '123 456 789',  3, 'mno pqr stu', 4, 'ABC DEF GHI']);
2704   TestCommand('ecMoveLineUp - 2L part sel',      2,3, 1,5,  2,2, 1,4,  [2, '123 456 789',  3, 'mno pqr stu', 4, 'ABC DEF GHI']);
2705   TestCommand('ecMoveLineUp - 2L full sel',      1,3, 1,5,  1,2, 1,4,  [2, '123 456 789',  3, 'mno pqr stu', 4, 'ABC DEF GHI']);
2706 
2707 
2708   FTestCommand := ecMoveLineDown;
2709   TestCommand('ecMoveLineDown - no sel',           1,2,       1,3,       [2, '123 456 789',  3, 'ABC DEF GHI']);
2710   TestCommand('ecMoveLineDown - no sel',           4,2,       4,3,       [2, '123 456 789',  3, 'ABC DEF GHI']);
2711   TestCommand('ecMoveLineDown - 1L part sel',      2,2, 4,2,  2,3, 4,3,  [2, '123 456 789',  3, 'ABC DEF GHI']);
2712   TestCommand('ecMoveLineDown - 1L part sel',      2,2, 1,3,  2,3, 1,4,  [2, '123 456 789',  3, 'ABC DEF GHI']);
2713   TestCommand('ecMoveLineDown - 1L full sel',      1,2, 1,3,  1,3, 1,4,  [2, '123 456 789',  3, 'ABC DEF GHI']);
2714 
2715   TestCommand('ecMoveLineDown - 2L part sel',      2,2, 3,3,  2,3, 3,4,  [2, 'mno pqr stu', 3, 'ABC DEF GHI', 4, '123 456 789']);
2716   TestCommand('ecMoveLineDown - 2L part sel',      2,2, 1,4,  2,3, 1,5,  [2, 'mno pqr stu', 3, 'ABC DEF GHI', 4, '123 456 789']);
2717   TestCommand('ecMoveLineDown - 2L full sel',      1,2, 1,4,  1,3, 1,5,  [2, 'mno pqr stu', 3, 'ABC DEF GHI', 4, '123 456 789']);
2718 
2719 end;
2720 
2721 
2722 
2723 initialization
2724 
2725   RegisterTest(TTestBasicSynEdit);
2726 end.
2727 
2728