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