1 unit TestWordWrap;
2
3 {$mode objfpc}{$H+}
4
5 interface
6
7 uses
8 Classes, SysUtils, math, TestBase, SynEditViewedLineMap, SynEditMiscClasses,
9 SynEditTypes, SynEditWrappedView,
10 LazSynEditText, SynEditHighlighterFoldBase, LazLoggerBase,
11 SynEditKeyCmds, SynEdit, SynEditPointClasses, testregistry;
12
13 type
14 TIntArray = Array of integer;
15
16 { TExpWraps }
17
18 TExpWraps = object
19 w: Array of Integer;
20 len: Integer;
Initnull21 function Init(const a: array of integer): TExpWraps;
22 procedure SetCapacity(l: Integer);
23 procedure InitFill(AFrom, ATo: integer; AIncrease: Integer = 1);
24 procedure FillRange(AStartIdx, ACount, AFromVal: integer; AIncrease: Integer = 1);
25 procedure Join(const a: TExpWraps; AInsertPos: Integer = -1);
26 procedure Join(const a: array of integer; AInsertPos: Integer = -1);
27 procedure SpliceArray(ADelFrom, ADelCount: integer);
28 end;
29
30 { TTestWordWrapBase }
31
32 TTestWordWrapBase = class(TTestBase)
33 protected
TheTreenull34 function TheTree: TSynLineMapAVLTree; virtual; abstract;
TreeNodeCountnull35 function TreeNodeCount: integer;
36 procedure CheckTree(AName: String); virtual;
37 procedure CheckTree(AName: String; ANode: TSynEditLineMapPage; ANodeLine: TLineIdx; AMinLine, AMaxLine: TLineIdx);
38
39 end;
40
41 { TTestWordWrap }
42
43 TTestWordWrap = class(TTestWordWrapBase)
44 private
45 FTree: TSynLineMapAVLTree;
46 procedure AssertRealToWrapOffsets(const AName: String; ALine: TSynWordWrapLineMap;
47 const ExpWrapOffsets: TExpWraps; AStartOffs: Integer = 0);
48 procedure AssertWrapToRealOffset(const AName: String; ALine: TSynWordWrapLineMap;
49 const ExpRealAndSubOffsets: TExpWraps; AStartOffs: Integer = 0);
50 procedure AssertLineForWraps(const AName: String; ALine: TSynWordWrapLineMap;
51 const ExpWrapForEachLine: TExpWraps; AnExpAllValid: Boolean = False);
52 procedure InitLine(ALine: TSynWordWrapLineMap;
53 const AWrapValues: TExpWraps);
OnPageNeedednull54 function OnPageNeeded(AMapTree: TSynLineMapAVLTree): TSynEditLineMapPage;
55 procedure ValidateWraps(ALine: TSynWordWrapLineMap;
56 const AWrapValues: TExpWraps; AStartOffs: Integer = 0; ABackward: Boolean = False);
57 procedure ValidateNeededWraps(ALine: TSynWordWrapLineMap; const AWrapValues: TExpWraps);
58
59 procedure ValidateTreeWraps(const AWrapValues: TExpWraps; AStartOffs: Integer = 0);
60 procedure AssertTreeForWraps(const AName: String; const ExpWrapForEachLine: TExpWraps; AStartOffs: Integer = 0);
61
CreateTreenull62 function CreateTree(APageJoinSize, APageSplitSize, APageJoinDistance: Integer): TSynLineMapAVLTree;
63 protected
TheTreenull64 function TheTree: TSynLineMapAVLTree; override;
65
66 procedure SetUp; override;
67 procedure TearDown; override;
68 published
69 procedure TestWordWrapLineMap;
70 procedure TestWordWrapLineMapInvalidate;
71 procedure TestWordWrapLineMapInvalidateNoneContineous;
72 procedure TestWordWrapLineMapValidate;
73 procedure TestWordWrapLineMapMerge;
74 procedure TestWordWrapLineMapMergeInvalidate;
75 procedure TestWordWrapJoinWithSibling;
76
77 procedure TestWordWrapTreeInsertThenDelete;
78 procedure TestWordWrapTreeDeleteThenInsert;
79 end;
80
81 TPointType = (ptViewed, ptAlternateViewed, ptPhys, ptLog);
82
83 TPointSpecs = record
84 XY: array [TPointType] of TPoint;
85 LogOffs: Integer;
86 end;
87
88 TCommandAndPointSpecs = record
89 Exp: TPointSpecs;
90 Cmd: Array of TSynEditorCommand;
91 RunOnlyIf: Boolean;
92 end;
93
94 TTestWrapLineInfo = record
95 TextIdx: TLineIdx;
96 ViewedIdx, ViewedTopIdx, ViewedBottomIdx: TLineIdx;
97 SubIdx: Integer;
98 //FirstLogX: Integer;
99 TextStartMatch: String;
100 NoTrim: Boolean;
101 end;
102 TTestViewedLineRangeInfo = array of TTestWrapLineInfo;
103
104 TTripleBool = (tTrue, tFalse, tKeep);
105
lnull106 function l(ATxtIdx: TLineIdx; ASubIdx: Integer; AText: String; ANoTrim: Boolean = False): TTestWrapLineInfo;
ViewedExpnull107 function ViewedExp(AFirstViewedIdx: TLineIdx; ALines:
108 array of TTestWrapLineInfo; ANoTrim: TTripleBool = tKeep): TTestViewedLineRangeInfo;
109
110 type
111
112 { TTestWordWrapPluginBase }
113
114 TTestWordWrapPluginBase = class(TTestWordWrapBase)
115 private
116 procedure ClearCaret;
GetTreeNodeHoldernull117 function GetTreeNodeHolder(AIndex: Integer): TSynEditLineMapPageHolder;
118 procedure SetCaret(SourcePt: TPointType; APos: TPoint);
119 procedure TestCaret(AName: String; SourcePt, ExpPt: TPointType; AnExp: TPoint;
120 AnExpOffs: Integer = -1);
121 protected
122 FWordWrap: TLazSynEditLineWrapPlugin;
123 class procedure AssertEquals(const AMessage: string; Expected, Actual: TPoint); overload;
124 procedure AddLines(AFirstLineIdx, ACount, ALen: Integer; AnID: String; SkipBeginUpdate: Boolean = False; AReplaceExisting: Boolean = False);
125 procedure InternalCheckLine(AName: String; dsp: TLazSynDisplayView; ALine: TLineIdx; AExpTextStart: String; NoTrim: Boolean = False);
126 procedure CheckLine(AName: String; ALine: TLineIdx; AExpTextStart: String; NoTrim: Boolean = False);
127 procedure CheckLines(AName: String; AStartLine: TLineIdx; AExpTextStart: array of String; NoTrim: Boolean = False);
128
129 procedure CheckLine(AName: String; AExpLine: TTestWrapLineInfo);
130 procedure CheckLines(AName: String; AExpLines: TTestViewedLineRangeInfo);
131
132 procedure CheckXyMap(AName: String; APhysTExtXY, AViewedXY: TPoint; OnlyViewToText: Boolean = False);
133 procedure CheckXyMap(AName: String; APhysTExtX, APhysTExtY, AViewedX, AViewedY: integer; OnlyViewToText: Boolean = False);
134
135 procedure CheckXyMap(AName: String; APoints: TPointSpecs);
136 procedure CheckXyMap(AName: String; APoints: TPointSpecs;
137 ATestCommands: array of TCommandAndPointSpecs);
138
139 procedure CheckLineIndexMapping(AName: String; ATextIdx, AViewTopIdx, AViewBottomIdx: TLineIdx);
140
TheTreenull141 function TheTree: TSynLineMapAVLTree; override;
142 property TreeNodeHolder[AIndex: Integer]: TSynEditLineMapPageHolder read GetTreeNodeHolder;
143
144 procedure ReCreateEdit(ADispWidth: Integer);
145 procedure SetUp; override;
146 procedure TearDown; override;
147 end;
148
149 TTestWordWrapPlugin = class(TTestWordWrapPluginBase)
150 published
151 procedure TestEditorWrap;
152 procedure TestWrapSplitJoin;
153 procedure TestEditorEdit;
154 end;
155
156 implementation
157
pnull158 function p(VX, VY, AVX, AVY, PX, PY, LX, LY: Integer; Offs: Integer = -1): TPointSpecs; overload;
159 begin
160 with Result do begin
161 XY[ptViewed].X := VX;
162 XY[ptViewed].Y := VY;
163 XY[ptAlternateViewed].X := AVX;
164 XY[ptAlternateViewed].Y := AVY;
165 XY[ptPhys].X := PX;
166 XY[ptPhys].Y := PY;
167 XY[ptLog].X := LX;
168 XY[ptLog].Y := LY;
169 LogOffs := Offs;
170 end;
171 end;
172
pnull173 function p(VX, VY, PX, PY, LX, LY: Integer; Offs: Integer = -1): TPointSpecs; overload;
174 begin
175 Result := p(VX, VY, -1, -1, PX, PY, LX, LY, Offs);
176 end;
177
cnull178 function c(Cmd: Array of TSynEditorCommand; VX, VY, AVX, AVY, PX, PY, LX, LY: Integer; Offs: Integer = -1; RunOnlyIf: Boolean = True): TCommandAndPointSpecs; overload;
179 begin
180 Result.Exp := p(VX, VY, AVX, AVY, PX, PY, LX, LY, Offs);
181 SetLength(Result.Cmd, Length(Cmd));
182 move(Cmd[0], Result.Cmd[0], SizeOf(cmd[0]) * Length(Cmd));
183 Result.RunOnlyIf := RunOnlyIf;
184 end;
185
cnull186 function c(Cmd: Array of TSynEditorCommand; VX, VY, PX, PY, LX, LY: Integer; Offs: Integer = -1; RunOnlyIf: Boolean = True): TCommandAndPointSpecs; overload;
187 begin
188 Result := c(Cmd, VX, VY, -1, -1, PX, PY, LX, LY, Offs, RunOnlyIf);
189 end;
190
cnull191 function c(Cmd: TSynEditorCommand; VX, VY, PX, PY, LX, LY: Integer; Offs: Integer = -1; RunOnlyIf: Boolean = True): TCommandAndPointSpecs; overload;
192 begin
193 Result := c([Cmd], VX, VY, -1, -1, PX, PY, LX, LY, Offs, RunOnlyIf);
194 end;
195
FillArraynull196 function FillArray(AFrom, ATo: integer; AIncrease: Integer = 1): TIntArray;
197 var
198 i: Integer;
199 begin
200 SetLength(Result, ATo - AFrom + 1);
201 for i := 0 to high(Result) do
202 Result[i] := AFrom + i * AIncrease;
203 end;
204
lnull205 function l(ATxtIdx: TLineIdx; ASubIdx: Integer; AText: String; ANoTrim: Boolean
206 ): TTestWrapLineInfo;
207 begin
208 Result.TextIdx := ATxtIdx;
209 Result.SubIdx := ASubIdx;
210 Result.TextStartMatch := AText;
211 Result.NoTrim := ANoTrim;
212 end;
213
ViewedExpnull214 function ViewedExp(AFirstViewedIdx: TLineIdx;
215 ALines: array of TTestWrapLineInfo; ANoTrim: TTripleBool
216 ): TTestViewedLineRangeInfo;
217 var
218 i, j: Integer;
219 begin
220 SetLength(Result, Length(ALines));
221 j := 0;
222 for i := 0 to Length(ALines) - 1 do begin
223 if (i > 0) and (ALines[i].SubIdx = 0) then begin
224 while j < i do begin
225 Result[j].ViewedBottomIdx := AFirstViewedIdx - 1;
226 inc(j);
227 end;
228 j := i;
229 end;
230 Result[i] := ALines[i];
231 Result[i].ViewedIdx := AFirstViewedIdx;
232 Result[i].ViewedTopIdx := AFirstViewedIdx - Result[i].SubIdx;
233 case ANoTrim of
234 tFalse: Result[i].NoTrim := False;
235 tTrue: Result[i].NoTrim := True;
236 end;
237 inc(AFirstViewedIdx);
238 end;
239 while j < Length(ALines) do begin
240 Result[j].ViewedBottomIdx := AFirstViewedIdx - 1;
241 inc(j);
242 end;
243 end;
244
245 { TTestWordWrapBase }
246
TTestWordWrapBase.TreeNodeCountnull247 function TTestWordWrapBase.TreeNodeCount: integer;
248 var
249 n: TSynEditLineMapPageHolder;
250 begin
251 Result := 0;
252 n := TheTree.FirstPage;
253 while n.HasPage do begin
254 inc(Result);
255 n := n.Next;
256 end;
257 end;
258
259 procedure TTestWordWrapBase.CheckTree(AName: String);
260 var
261 n: TSynEditLineMapPageHolder;
262 begin
263 if TheTree = nil then
264 AssertTrue(AName, False);
265 n := TheTree.FirstPage;
266 if n.HasPage then
267 CheckTree(AName, n.Page, n.StartLine, 0, MaxInt);
268 end;
269
270 procedure TTestWordWrapBase.CheckTree(AName: String;
271 ANode: TSynEditLineMapPage; ANodeLine: TLineIdx; AMinLine, AMaxLine: TLineIdx
272 );
273 var
274 n: TSynEditLineMapPage;
275 dummy, i, EndLine: Integer;
276 nl: TLineIdx;
277 begin
278 nl := ANodeLine;
279 n := ANode.Precessor(nl, dummy);
280 while n <> nil do begin
281 ANode := n;
282 ANodeLine := nl;
283 n := ANode.Precessor(nl, dummy);
284 end;
285
286 i := 0;
287 while ANode <> nil do begin
288 AssertTrue(Format('%s(%d): MinLine', [AName, i]), ANodeLine >= AMinLine);
289 EndLine := ANodeLine + Max(0, ANode.RealEndLine);
290 AssertTrue(Format('%s(%d): EndLine', [AName, i]), EndLine <= AMaxLine);
291
292 AMinLine := EndLine + 1;
293 ANode := ANode.Successor(ANodeLine, dummy);
294 inc(i);
295 end;
296 end;
297
298 { TExpWraps }
299
Initnull300 function TExpWraps.Init(const a: array of integer): TExpWraps;
301 begin
302 len := Length(a);
303 if len > 0 then begin
304 SetCapacity(len);
305 move(a[0], w[0], SizeOf(w[0]) * len);
306 end;
307 Result := self;
308 end;
309
310 procedure TExpWraps.SetCapacity(l: Integer);
311 begin
312 if Length(w) < l then
313 SetLength(w, l*2);
314 end;
315
316 procedure TExpWraps.InitFill(AFrom, ATo: integer; AIncrease: Integer);
317 var
318 p: PLongInt;
319 i: Integer;
320 begin
321 len := ATo - AFrom + 1;
322 SetCapacity(len);
323 p := @w[0];
324 for i := 0 to len - 1 do begin
325 p^ := AFrom;
326 inc(p);
327 inc(AFrom, AIncrease);
328 end;
329 end;
330
331 procedure TExpWraps.FillRange(AStartIdx, ACount, AFromVal: integer;
332 AIncrease: Integer);
333 var
334 p: PLongInt;
335 i: Integer;
336 begin
337 if len < AStartIdx + ACount then
338 len := AStartIdx + ACount;
339 SetCapacity(len);
340
341 p := @w[AStartIdx];
342 for i := 0 to ACount - 1 do begin
343 p^ := AFromVal;
344 inc(p);
345 inc(AFromVal, AIncrease);
346 end;
347
348 end;
349
350 procedure TExpWraps.Join(const a: TExpWraps; AInsertPos: Integer);
351 var
352 i, old: Integer;
353 begin
354 if AInsertPos < 0 then
355 AInsertPos := Len;
356
357 i := (Len-AInsertPos);
358 old := len;
359 len := len + a.len;
360 if i < 0 then
361 len := len - i;
362 SetCapacity(len);
363
364 if i > 0 then begin
365 move(w[AInsertPos], w[AInsertPos+a.len], sizeof(w[0]) * i);
366 end
367 else
368 if i < 0 then begin
369 FillDWord(w[old], -i, 1);
370 end;
371 move(a.w[0], w[AInsertPos], sizeof(w[0]) * a.len);
372 end;
373
374 procedure TExpWraps.Join(const a: array of integer; AInsertPos: Integer);
375 var
376 i, la, old: Integer;
377 begin
378 if AInsertPos < 0 then
379 AInsertPos := Len;
380
381 i := (Len-AInsertPos);
382 la := Length(a);
383 old := len;
384 len := len + la;
385 if i < 0 then
386 len := len - i;
387 SetCapacity(len);
388
389 if i > 0 then begin
390 move(w[AInsertPos], w[AInsertPos+la], sizeof(w[0]) * i);
391 end
392 else
393 if i < 0 then begin
394 FillDWord(w[old], -i, 1);
395 end;
396 move(a[0], w[AInsertPos], sizeof(w[0]) * la);
397 end;
398
399 procedure TExpWraps.SpliceArray(ADelFrom, ADelCount: integer);
400 var
401 i: Integer;
402 begin
403 len := len - ADelCount;
404
405 i := Length(w) - ADelFrom - ADelCount;
406 if i > 0 then
407 move(w[ADelFrom+ADelCount], w[ADelFrom], sizeof(w[0]) * (i));
408 end;
409
410 { TTestWordWrap }
411
412 procedure TTestWordWrap.AssertRealToWrapOffsets(const AName: String;
413 ALine: TSynWordWrapLineMap; const ExpWrapOffsets: TExpWraps;
414 AStartOffs: Integer);
415 var
416 i: Integer;
417 begin
418 for i := 0 to ExpWrapOffsets.len - 1 do
419 AssertEquals(format('%s: RealToWrap Idx %d StartOffs: %d ', [AName, i, AStartOffs]),
420 ExpWrapOffsets.w[i], ALine.WrappedOffsetFor[AStartOffs + i]);
421 end;
422
423 procedure TTestWordWrap.AssertWrapToRealOffset(const AName: String;
424 ALine: TSynWordWrapLineMap; const ExpRealAndSubOffsets: TExpWraps;
425 AStartOffs: Integer);
426 var
427 i, sub, r: Integer;
428 begin
429 for i := 0 to ExpRealAndSubOffsets.len div 2 - 1 do begin
430 r := ALine.GetOffsetForWrap(AStartOffs + i, sub);
431 AssertEquals(format('%s: WrapToReal Idx %d StartOffs: %d ', [AName, i, AStartOffs]),
432 ExpRealAndSubOffsets.w[i*2], r);
433 AssertEquals(format('%s: WrapToReal(SUB) Idx %d StartOffs: %d ', [AName, i, AStartOffs]),
434 ExpRealAndSubOffsets.w[i*2+1], sub);
435 end;
436 end;
437
438 procedure TTestWordWrap.AssertLineForWraps(const AName: String;
439 ALine: TSynWordWrapLineMap; const ExpWrapForEachLine: TExpWraps;
440 AnExpAllValid: Boolean);
441 var
442 i, j, ExpWrap, TestWrapToReal, GotReal, sub: Integer;
443 begin
444 if AnExpAllValid then
445 AssertTrue(AName + ' - all lines valid', ALine.FirstInvalidLine < 0);
446 i := 0;
447 while (i < ExpWrapForEachLine.len) and (ExpWrapForEachLine.w[i] = 1) do
448 inc(i);
449 if i = ExpWrapForEachLine.len then
450 i := 0;
451 AssertEquals(Format('%s: Offset', [AName]), i, ALine.Offset);
452
453 j := ExpWrapForEachLine.len - 1;
454 while (j >= 0) and (ExpWrapForEachLine.w[j] = 1) do
455 dec(j);
456 AssertEquals(Format('%s: RealCount', [AName]), j + 1 - i, ALine.RealCount);
457
458 ExpWrap := 0;
459 TestWrapToReal := 0;
460 for i := 0 to ExpWrapForEachLine.len - 1 do begin
461 AssertEquals(Format('%s: RealToWrap Idx %d', [AName, i]), ExpWrap, ALine.WrappedOffsetFor[i]);
462 ExpWrap := ExpWrap + ExpWrapForEachLine.w[i];
463
464 for j := 0 to ExpWrapForEachLine.w[i] - 1 do begin
465 GotReal := ALine.GetOffsetForWrap(TestWrapToReal, sub);
466 AssertEquals(Format('%s: WrapToReal Idx %d', [AName, TestWrapToReal]), i, GotReal);
467 AssertEquals(Format('%s: WrapToReal Idx %d SUB', [AName, TestWrapToReal]), j, sub);
468 inc(TestWrapToReal);
469 end;
470 end;
471
472 CheckTree(AName+'TreeCheck');
473 end;
474
475 procedure TTestWordWrap.InitLine(ALine: TSynWordWrapLineMap;
476 const AWrapValues: TExpWraps);
477 begin
478 ALine.DeleteLinesAtOffset(0, max(ALine.RealCount + ALine.Offset, ALine.LastInvalidLine+1));
479 if AWrapValues.len > 0 then begin
480 ALine.InsertLinesAtOffset(0, AWrapValues.len);
481 ValidateWraps(ALine, AWrapValues);
482 end;
483 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
484 end;
485
TTestWordWrap.OnPageNeedednull486 function TTestWordWrap.OnPageNeeded(AMapTree: TSynLineMapAVLTree
487 ): TSynEditLineMapPage;
488 begin
489 Result := TSynWordWrapIndexPage.Create(AMapTree);
490 //TSynWordWrapIndexPage(Result).FSynEditWrappedPlugin := Self;
491 end;
492
493 procedure TTestWordWrap.ValidateWraps(ALine: TSynWordWrapLineMap;
494 const AWrapValues: TExpWraps; AStartOffs: Integer; ABackward: Boolean);
495 var
496 i: Integer;
497 begin
498 if ABackward then begin
499 for i := AWrapValues.len - 1 downto 0 do
500 ALine.ValidateLine(AStartOffs + i, AWrapValues.w[i]);
501 end
502 else begin
503 for i := 0 to AWrapValues.len - 1 do
504 ALine.ValidateLine(AStartOffs + i, AWrapValues.w[i]);
505 end;
506 ALine.EndValidate;
507 end;
508
509 procedure TTestWordWrap.ValidateNeededWraps(ALine: TSynWordWrapLineMap;
510 const AWrapValues: TExpWraps);
511 var
512 i: Integer;
513 begin
514 i := ALine.FirstInvalidLine;
515 while i >= 0 do begin
516 ALine.ValidateLine(i, AWrapValues.w[i]);
517 i := ALine.FirstInvalidLine;
518 end;
519 ALine.EndValidate;
520 end;
521
522 procedure TTestWordWrap.ValidateTreeWraps(const AWrapValues: TExpWraps;
523 AStartOffs: Integer);
524 var
525 i: Integer;
526 LowLine, HighLine: TLineIdx;
527 begin
528 while FTree.NextBlockForValidation(LowLine, HighLine) do begin
529 for i := LowLine to HighLine do begin
530 AssertTrue(i-AStartOffs < AWrapValues.len);
531 FTree.ValidateLine(i, AWrapValues.w[i-AStartOffs]);
532 end;
533 end;
534 FTree.EndValidate;
535 end;
536
537 procedure TTestWordWrap.AssertTreeForWraps(const AName: String;
538 const ExpWrapForEachLine: TExpWraps; AStartOffs: Integer);
539 var
540 i, w: Integer;
541 sub: TLineIdx;
542 begin
543 w := AStartOffs;
544 for i := 0 to (ExpWrapForEachLine.len - 1) do begin
545 AssertEquals(Format('%s // l=%d getWrap', [AName, i]),
546 w,
547 FTree.GetWrapLineForForText(AStartOffs + i)
548 );
549 w := w + ExpWrapForEachLine.w[i];
550 AssertEquals(Format('%s // l=%d getLine', [AName, i]),
551 i,
552 FTree.GetLineForForWrap(w-1, sub)
553 );
554 AssertEquals(Format('%s // l=%d sub', [AName, i]),
555 ExpWrapForEachLine.w[i]-1,
556 sub
557 );
558 end;
559
560 CheckTree(AName+'TreeCheck');
561 end;
562
CreateTreenull563 function TTestWordWrap.CreateTree(APageJoinSize, APageSplitSize,
564 APageJoinDistance: Integer): TSynLineMapAVLTree;
565 begin
566 Result := TSynLineMapAVLTree.Create(APageJoinSize, APageSplitSize, APageJoinDistance);
567 Result.PageCreatorProc := @OnPageNeeded;
568 end;
569
TTestWordWrap.TheTreenull570 function TTestWordWrap.TheTree: TSynLineMapAVLTree;
571 begin
572 Result := FTree;
573 end;
574
575 procedure TTestWordWrap.SetUp;
576 begin
577 FTree := CreateTree(15, 60, 20);
578 inherited SetUp;
579 end;
580
581 procedure TTestWordWrap.TearDown;
582 begin
583 inherited TearDown;
584 FTree.Free;
585 end;
586
587 procedure TTestWordWrap.TestWordWrapLineMap;
588 var
589 ALine: TSynWordWrapLineMap;
590 ANode: TSynEditLineMapPage;
591 i: Integer;
592 ATestName: String;
593 w: TExpWraps;
594 begin
595 ANode := FTree.FindPageForLine(0, afmCreate).Page;
596 ALine := TSynWordWrapIndexPage(ANode).SynWordWrapLineMapStore;
597 ALine.InsertLinesAtOffset(0, 5);
598 ALine.InvalidateLines(2,3);
599 ValidateWraps(ALine, w.init([1, 1, 3, 3, 1]));
600 AssertLineForWraps('', ALine, w.init([1, 1, 3, 3, 1, 1,1]));
601 //AssertRealToWrapOffsets('', ALine, [0, 1, 2, 5, 8, 9, 10]);
602 //AssertWrapToRealOffset('', ALine, [0,0, 1,0, 2,0, 2,1, 2,2, 3,0, 3,1, 3,2, 4,0, 5,0]);
603 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
604
605 for i := 1 to 2 do begin
606
607 // insert into offset
608 ATestName := 'Insert at start of "Offset"';
609 ALine.InsertLinesAtOffset(0, 2);
610 ValidateWraps(ALine, w.init([2, 2]), 0, i mod 1 = 1);
611 AssertLineForWraps(ATestName, ALine, w.init([2, 2, 1, 1, 3, 3, 1, 1,1]));
612 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
613
614 ALine.DeleteLinesAtOffset(0, 2);
615 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
616 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
617
618
619 ATestName := 'Insert at middle of "Offset"';
620 ALine.InsertLinesAtOffset(1, 2);
621 ValidateWraps(ALine, w.init([2, 2]), 1, i mod 1 = 1);
622 AssertLineForWraps(ATestName, ALine, w.init([1, 2, 2, 1, 3, 3, 1, 1,1]));
623 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
624
625 ALine.DeleteLinesAtOffset(1, 2);
626 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
627 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
628
629
630 ATestName := 'Insert at end of "Offset"';
631 ALine.InsertLinesAtOffset(2, 2);
632 ValidateWraps(ALine, w.init([2, 2]), 2, i mod 1 = 1);
633 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 2, 2, 3, 3, 1, 1,1]));
634 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
635
636 ALine.DeleteLinesAtOffset(2, 2);
637 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
638 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
639
640
641
642 ATestName := 'Insert at start of "Offset" - single lines';
643 ALine.InsertLinesAtOffset(0, 2);
644 ValidateWraps(ALine, w.init([1, 1]), 0, i mod 1 = 1);
645 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 1, 1, 3, 3, 1, 1,1]));
646 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
647
648 ALine.DeleteLinesAtOffset(0, 2);
649 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
650 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
651
652
653 ATestName := 'Insert at middle of "Offset" - single lines';
654 ALine.InsertLinesAtOffset(1, 2);
655 ValidateWraps(ALine, w.init([1, 1]), 1, i mod 1 = 1);
656 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 1, 1, 3, 3, 1, 1,1]));
657 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
658
659 ALine.DeleteLinesAtOffset(1, 2);
660 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
661 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
662
663
664 ATestName := 'Insert at end of "Offset" - single lines';
665 ALine.InsertLinesAtOffset(2, 2);
666 ValidateWraps(ALine, w.init([1, 1]), 2, i mod 1 = 1);
667 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 1, 1, 3, 3, 1, 1,1]));
668 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
669
670 ALine.DeleteLinesAtOffset(2, 2);
671 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
672 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
673
674
675
676 ATestName := 'Insert at start of "Offset" - single/wrap lines';
677 ALine.InsertLinesAtOffset(0, 2);
678 ValidateWraps(ALine, w.init([1, 2]), 0, i mod 1 = 1);
679 AssertLineForWraps(ATestName, ALine, w.init([1, 2, 1, 1, 3, 3, 1, 1,1]));
680 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
681
682 ALine.DeleteLinesAtOffset(1, 1);
683 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 1, 3, 3, 1, 1,1]));
684 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
685 ALine.DeleteLinesAtOffset(0, 1);
686 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
687 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
688
689
690 ATestName := 'Delete mixed offset/data';
691 ALine.DeleteLinesAtOffset(1, 2);
692 AssertLineForWraps(ATestName, ALine, w.init([1, 3, 1, 1,1]));
693 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
694
695 ALine.InsertLinesAtOffset(1, 2);
696 ValidateWraps(ALine, w.init([1, 3]), 1, i mod 1 = 1);
697 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
698 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
699
700
701 // insert into data
702 ATestName := 'Insert at middle of Data';
703 ALine.InsertLinesAtOffset(3, 2);
704 ValidateWraps(ALine, w.init([2, 2]), 3, i mod 1 = 1);
705 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 2, 2, 3, 1, 1,1]));
706 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
707
708 ALine.DeleteLinesAtOffset(3, 2);
709 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
710 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
711
712
713
714 ATestName := 'Insert at middle of Data';
715 ALine.InsertLinesAtOffset(3, 2);
716 ValidateWraps(ALine, w.init([2, 2]), 3, i mod 1 = 1);
717 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 2, 2, 3, 1, 1,1]));
718 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
719
720 ALine.DeleteLinesAtOffset(3, 2);
721 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
722 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
723
724
725
726 // insert after data
727 ATestName := 'Insert at end of Data';
728 ALine.InsertLinesAtOffset(5, 1);
729 ValidateWraps(ALine, w.init([4]), 5, i mod 1 = 1);
730 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 4, 1,1]));
731 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
732
733 ALine.DeleteLinesAtOffset(5, 1);
734 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
735 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
736
737
738 ATestName := 'Insert at end of Data - single line';
739 ALine.InsertLinesAtOffset(5, 1);
740 ValidateWraps(ALine, w.init([1]), 5, i mod 1 = 1);
741 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1, 1,1]));
742 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
743
744 ALine.DeleteLinesAtOffset(5, 1);
745 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
746 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
747
748
749 ATestName := 'Insert behind end of Data';
750 ALine.InsertLinesAtOffset(6, 1);
751 ValidateWraps(ALine, w.init([4]), 6, i mod 1 = 1);
752 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1, 4, 1,1]));
753 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
754
755 ALine.DeleteLinesAtOffset(6, 1);
756 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
757 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
758
759
760 ATestName := 'Insert behind end of Data - single line';
761 ALine.InsertLinesAtOffset(6, 1);
762 ValidateWraps(ALine, w.init([1]), 6, i mod 1 = 1);
763 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1, 1, 1,1]));
764 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
765
766 ALine.DeleteLinesAtOffset(6, 1);
767 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
768 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
769
770
771 ATestName := 'Delete mixed data/after';
772 ALine.DeleteLinesAtOffset(3, 2);
773 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 1,1]));
774 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
775
776 ALine.InsertLinesAtOffset(3, 2);
777 ValidateWraps(ALine, w.init([3, 1]), 3, i mod 1 = 1);
778 AssertLineForWraps(ATestName, ALine, w.init([1, 1, 3, 3, 1, 1,1]));
779 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
780
781 end;
782
783 ALine.InvalidateLines(0, 4);
784 ValidateWraps(ALine, w.init([1,1,1,1,1]), 0, False);
785 AssertLineForWraps('', ALine, w.init([1, 1, 1, 1, 1, 1,1]));
786 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
787
788 ALine.InsertLinesAtOffset(0, 5);
789 ValidateWraps(ALine, w.init([1, 1, 3, 3, 1]));
790 AssertLineForWraps('', ALine, w.init([1, 1, 3, 3, 1, 1,1]));
791 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
792
793 ALine.InvalidateLines(0, 4);
794 ValidateWraps(ALine, w.init([1,1,1,1,1]), 0, True);
795 AssertLineForWraps('', ALine, w.init([1, 1, 1, 1, 1, 1,1]));
796 AssertEquals('all valid', -1, ALine.FirstInvalidLine);
797
798 end;
799
800 procedure TTestWordWrap.TestWordWrapLineMapInvalidate;
801 var
802 ANode1: TSynWordWrapIndexPage;
803 ALine1: TSynWordWrapLineMap;
804 //ATestName: String;
805 w: TExpWraps;
806 begin
807 // invalidate and insert/remove lines
808 ANode1 := TSynWordWrapIndexPage(FTree.FindPageForLine(0, afmCreate).Page);
809 ALine1 := ANode1.SynWordWrapLineMapStore;
810
811 InitLine(ALine1, w.init([1]));
812 ALine1.InvalidateLines(3,6);
813 AssertEquals('invalid', 3, ALine1.FirstInvalidLine);
814 AssertEquals('invalid', 6, ALine1.LastInvalidLine);
815
816 ALine1.DeleteLinesAtOffset(6,2);
817 AssertEquals('invalid', 3, ALine1.FirstInvalidLine);
818 AssertEquals('invalid', 5, ALine1.LastInvalidLine);
819
820 ALine1.InsertLinesAtOffset(2,1);
821 AssertEquals('invalid', 2, ALine1.FirstInvalidLine);
822 ValidateWraps(ALine1, w.init([1]), 2);
823
824 AssertEquals('invalid', 4, ALine1.FirstInvalidLine);
825 AssertEquals('invalid', 6, ALine1.LastInvalidLine);
826
827 ALine1.InsertLinesAtOffset(5,1);
828 AssertEquals('invalid', 4, ALine1.FirstInvalidLine);
829 AssertEquals('invalid', 7, ALine1.LastInvalidLine);
830
831 ALine1.DeleteLinesAtOffset(4,1);
832 AssertEquals('invalid', 4, ALine1.FirstInvalidLine);
833 AssertEquals('invalid', 6, ALine1.LastInvalidLine);
834
835 end;
836
837 procedure TTestWordWrap.TestWordWrapLineMapInvalidateNoneContineous;
838 var
839 ANode1: TSynWordWrapIndexPage;
840 ALine1: TSynWordWrapLineMap;
841 //ATestName: String;
842 w: TExpWraps;
843 begin
844 // invalidate and insert/remove lines
845 ANode1 := TSynWordWrapIndexPage(FTree.FindPageForLine(0, afmCreate).Page);
846 ALine1 := ANode1.SynWordWrapLineMapStore;
847
848 InitLine(ALine1, w.init([1]));
849 ALine1.InvalidateLines(30,31);
850 ALine1.InvalidateLines(32,35);
851 ALine1.InvalidateLines(40,41);
852 ALine1.InvalidateLines(10,11);
853 ALine1.InvalidateLines(20,21);
854 ALine1.InvalidateLines(22,23);
855
856 AssertEquals('invalid first from', 10, ALine1.FirstInvalidLine);
857 AssertEquals('invalid first to', 11, ALine1.FirstInvalidEndLine);
858 AssertEquals('invalid last', 41, ALine1.LastInvalidLine);
859
860 ALine1.ValidateLine(10, 1);
861 AssertEquals('invalid first from', 11, ALine1.FirstInvalidLine);
862 AssertEquals('invalid first to', 11, ALine1.FirstInvalidEndLine);
863 AssertEquals('invalid last', 41, ALine1.LastInvalidLine);
864
865 ALine1.ValidateLine(11, 1);
866 AssertEquals('invalid first from', 20, ALine1.FirstInvalidLine);
867 AssertEquals('invalid first to', 23, ALine1.FirstInvalidEndLine);
868 AssertEquals('invalid last', 41, ALine1.LastInvalidLine);
869
870 ALine1.ValidateLine(20, 1);
871 AssertEquals('invalid first from', 21, ALine1.FirstInvalidLine);
872 AssertEquals('invalid first to', 23, ALine1.FirstInvalidEndLine);
873 AssertEquals('invalid last', 41, ALine1.LastInvalidLine);
874
875 ALine1.ValidateLine(21, 1);
876 ALine1.ValidateLine(22, 1);
877 ALine1.ValidateLine(23, 1);
878 AssertEquals('invalid first from', 30, ALine1.FirstInvalidLine);
879 AssertEquals('invalid first to', 35, ALine1.FirstInvalidEndLine);
880 AssertEquals('invalid last', 41, ALine1.LastInvalidLine);
881
882 ALine1.ValidateLine(30, 1);
883 ALine1.ValidateLine(31, 1);
884 ALine1.ValidateLine(32, 1);
885 ALine1.ValidateLine(33, 1);
886 ALine1.ValidateLine(34, 1);
887 ALine1.ValidateLine(35, 1);
888 AssertEquals('invalid first from', 40, ALine1.FirstInvalidLine);
889 AssertEquals('invalid first to', 41, ALine1.FirstInvalidEndLine);
890 AssertEquals('invalid last', 41, ALine1.LastInvalidLine);
891
892 ALine1.ValidateLine(40, 1);
893 AssertEquals('invalid first from', 41, ALine1.FirstInvalidLine);
894 AssertEquals('invalid first to', 41, ALine1.FirstInvalidEndLine);
895 AssertEquals('invalid last', 41, ALine1.LastInvalidLine);
896
897 ALine1.ValidateLine(41, 1);
898 AssertEquals('invalid first from', -1, ALine1.FirstInvalidLine);
899 AssertEquals('invalid first to', -1, ALine1.FirstInvalidEndLine);
900 AssertEquals('invalid last', -1, ALine1.LastInvalidLine);
901
902 end;
903
904 procedure TTestWordWrap.TestWordWrapLineMapValidate;
905 var
906 ANode1: TSynWordWrapIndexPage;
907 ALine1: TSynWordWrapLineMap;
908 ATestName: String;
909 w: TExpWraps;
910 i: Integer;
911 begin
912 // invalidate/ re-validate => increase/decrease offset/tail by switching between wrap and one-line lines
913 ANode1 := TSynWordWrapIndexPage(FTree.FindPageForLine(0, afmCreate).Page);
914 ALine1 := ANode1.SynWordWrapLineMapStore;
915
916 ATestName := 'fill one-lines at start - increasing';
917 InitLine(ALine1, w.init(FillArray(10, 19)));
918 w.Join([1,1]);
919 for i := 0 to 3 do begin
920 ALine1.InvalidateLines(0, 3);
921 w.w[i] := 1;
922 ValidateNeededWraps(ALine1, w);
923 AssertLineForWraps(Format('%s %d', [ATestName, i]), ALine1, w, True);
924 end;
925
926 ATestName := 'fill one-lines at start - decreasing';
927 InitLine(ALine1, w.init(FillArray(10, 19)));
928 w.Join([1,1]);
929 for i := 3 downto 0 do begin
930 ALine1.InvalidateLines(0, 3);
931 w.w[i] := 1;
932 ValidateNeededWraps(ALine1, w);
933 AssertLineForWraps(Format('%s %d', [ATestName, i]), ALine1, w, True);
934 end;
935
936
937
938 ATestName := 'fill one-lines at end - decreasing';
939 InitLine(ALine1, w.init(FillArray(10, 19)));
940 w.Join([1,1]);
941 for i := 9 downto 7 do begin
942 ALine1.InvalidateLines(7, 9);
943 w.w[i] := 1;
944 ValidateNeededWraps(ALine1, w);
945 AssertLineForWraps(Format('%s %d', [ATestName, i]), ALine1, w, True);
946 end;
947
948 ATestName := 'fill one-lines at end - increasing';
949 InitLine(ALine1, w.init(FillArray(10, 19)));
950 w.Join([1,1]);
951 for i := 7 to 9 do begin
952 ALine1.InvalidateLines(7, 9);
953 w.w[i] := 1;
954 ValidateNeededWraps(ALine1, w);
955 AssertLineForWraps(Format('%s %d', [ATestName, i]), ALine1, w, True);
956 end;
957
958
959 ATestName := 'fill one-lines - all, incr';
960 InitLine(ALine1, w.init(FillArray(10, 19)));
961 w.Join([1,1]);
962 for i := 0 to 9 do begin
963 ALine1.InvalidateLines(0, 9);
964 w.w[i] := 1;
965 ValidateNeededWraps(ALine1, w);
966 AssertLineForWraps(Format('%s %d', [ATestName, i]), ALine1, w, True);
967 end;
968
969 ATestName := 'fill one-lines - all, decr';
970 InitLine(ALine1, w.init(FillArray(10, 19)));
971 w.Join([1,1]);
972 for i := 9 downto 0 do begin
973 ALine1.InvalidateLines(0, 9);
974 w.w[i] := 1;
975 ValidateNeededWraps(ALine1, w);
976 AssertLineForWraps(Format('%s %d', [ATestName, i]), ALine1, w, True);
977 end;
978
979
980 ATestName := 'fill one-lines - all, incr then decr';
981 InitLine(ALine1, w.init(FillArray(10, 19)));
982 w.Join([1,1]);
983 for i := 0 to 4 do begin
984 ALine1.InvalidateLines(0, 9);
985 w.w[i] := 1;
986 ValidateNeededWraps(ALine1, w);
987 AssertLineForWraps(Format('%s %d', [ATestName, i]), ALine1, w, True);
988 end;
989 for i := 9 downto 5 do begin
990 ALine1.InvalidateLines(0, 9);
991 w.w[i] := 1;
992 ValidateNeededWraps(ALine1, w);
993 AssertLineForWraps(Format('%s %d', [ATestName, i]), ALine1, w, True);
994 end;
995
996 ATestName := 'fill one-lines - all, decr then incr';
997 InitLine(ALine1, w.init(FillArray(10, 19)));
998 w.Join([1,1]);
999 for i := 9 downto 5 do begin
1000 ALine1.InvalidateLines(0, 9);
1001 w.w[i] := 1;
1002 ValidateNeededWraps(ALine1, w);
1003 AssertLineForWraps(Format('%s %d', [ATestName, i]), ALine1, w, True);
1004 end;
1005 for i := 0 to 4 do begin
1006 ALine1.InvalidateLines(0, 9);
1007 w.w[i] := 1;
1008 ValidateNeededWraps(ALine1, w);
1009 AssertLineForWraps(Format('%s %d', [ATestName, i]), ALine1, w, True);
1010 end;
1011
1012 end;
1013
1014 procedure TTestWordWrap.TestWordWrapLineMapMerge;
1015 var
1016 ANode1, ANode2: TSynWordWrapIndexPage;
1017 ALine1, ALine2: TSynWordWrapLineMap;
1018 ATestName: String;
1019 w: TExpWraps;
1020 begin
1021 ANode1 := TSynWordWrapIndexPage(FTree.FindPageForLine(0, afmCreate).Page);
1022 ANode2 := TSynWordWrapIndexPage(FTree.FindPageForLine(100, afmCreate).Page);
1023 ALine1 := ANode1.SynWordWrapLineMapStore;
1024 ALine2 := ANode2.SynWordWrapLineMapStore;
1025
1026 ATestName := 'Insert at start: no-offset => no-offset';
1027 InitLine(ALine1, w.init([2, 1, 3, 3, 1]));
1028 InitLine(ALine2, w.init([4, 5, 6]));
1029 ALine2.MoveLinesAtEndTo(ALine1, 0, 3);
1030 //ALine1.InsertLinesFromPage(ALine2, 0, 0, 3);
1031 AssertLineForWraps('', ALine1, w.init([4, 5, 6, 2, 1, 3, 3, 1, 1,1]));
1032
1033 ATestName := 'Insert at start: no-offset => offset';
1034 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1035 InitLine(ALine2, w.init([4, 5, 6]));
1036 ALine2.MoveLinesAtEndTo(ALine1, 0, 3);
1037 //ALine1.InsertLinesFromPage(ALine2, 0, 0, 3);
1038 AssertLineForWraps('', ALine1, w.init([4, 5, 6, 1, 1, 3, 3, 1, 1,1]));
1039
1040 ATestName := 'Insert at start: offset => no offset';
1041 InitLine(ALine1, w.init([2, 1, 3, 3, 1]));
1042 InitLine(ALine2, w.init([1, 5, 6]));
1043 ALine2.MoveLinesAtEndTo(ALine1, 0, 3);
1044 //ALine1.InsertLinesFromPage(ALine2, 0, 0, 3);
1045 AssertLineForWraps('', ALine1, w.init([1, 5, 6, 2, 1, 3, 3, 1, 1,1]));
1046
1047 ATestName := 'Insert at start: offset => offset';
1048 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1049 InitLine(ALine2, w.init([1, 5, 6]));
1050 ALine2.MoveLinesAtEndTo(ALine1, 0, 3);
1051 //ALine1.InsertLinesFromPage(ALine2, 0, 0, 3);
1052 AssertLineForWraps('', ALine1, w.init([1, 5, 6, 1, 1, 3, 3, 1, 1,1]));
1053
1054
1055 ATestName := 'Insert at start: no-offset 2nd => no-offset';
1056 InitLine(ALine1, w.init([2, 1, 3, 3, 1]));
1057 InitLine(ALine2, w.init([4, 5, 6]));
1058 ALine2.MoveLinesAtEndTo(ALine1, 1, 2);
1059 //ALine1.InsertLinesFromPage(ALine2, 1, 0, 2);
1060 AssertLineForWraps('', ALine1, w.init([5, 6, 2, 1, 3, 3, 1, 1,1]));
1061
1062 ATestName := 'Insert at start: no-offset 2nd => offset';
1063 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1064 InitLine(ALine2, w.init([4, 5, 6]));
1065 ALine2.MoveLinesAtEndTo(ALine1, 1, 2);
1066 //ALine1.InsertLinesFromPage(ALine2, 1, 0, 2);
1067 AssertLineForWraps('', ALine1, w.init([5, 6, 1, 1, 3, 3, 1, 1,1]));
1068
1069 ATestName := 'Insert at start: offset 2nd => no offset';
1070 InitLine(ALine1, w.init([2, 1, 3, 3, 1]));
1071 InitLine(ALine2, w.init([1, 5, 6]));
1072 ALine2.MoveLinesAtEndTo(ALine1, 1, 2);
1073 //ALine1.InsertLinesFromPage(ALine2, 1, 0, 2);
1074 AssertLineForWraps('', ALine1, w.init([5, 6, 2, 1, 3, 3, 1, 1,1]));
1075
1076 ATestName := 'Insert at start: offset 2nd => offset';
1077 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1078 InitLine(ALine2, w.init([1, 5, 6]));
1079 ALine2.MoveLinesAtEndTo(ALine1, 1, 2);
1080 //ALine1.InsertLinesFromPage(ALine2, 1, 0, 2);
1081 AssertLineForWraps('', ALine1, w.init([5, 6, 1, 1, 3, 3, 1, 1,1]));
1082
1083 ATestName := 'Insert at start: offset 3rd => no offset';
1084 InitLine(ALine1, w.init([2, 1, 3, 3, 1]));
1085 InitLine(ALine2, w.init([1, 5, 6, 7]));
1086 ALine2.MoveLinesAtEndTo(ALine1, 2, 2);
1087 //ALine1.InsertLinesFromPage(ALine2, 2, 0, 2);
1088 AssertLineForWraps('', ALine1, w.init([6, 7, 2, 1, 3, 3, 1, 1,1]));
1089
1090 ATestName := 'Insert at start: offset 3rd => offset';
1091 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1092 InitLine(ALine2, w.init([1, 5, 6, 7]));
1093 ALine2.MoveLinesAtEndTo(ALine1, 2, 2);
1094 //ALine1.InsertLinesFromPage(ALine2, 2, 0, 2);
1095 AssertLineForWraps('', ALine1, w.init([6, 7, 1, 1, 3, 3, 1, 1,1]));
1096
1097
1098 ATestName := 'Insert at start: overlen';
1099 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1100 InitLine(ALine2, w.init([4, 5, 6]));
1101 ALine2.MoveLinesAtEndTo(ALine1, 0, 4);
1102 //ALine1.InsertLinesFromPage(ALine2, 0, 0, 4);
1103 AssertLineForWraps(ATestName, ALine1, w.init([4, 5, 6, 1, 1, 1, 3, 3, 1, 1,1]));
1104
1105 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1106 InitLine(ALine2, w.init([4, 5, 6]));
1107 ALine2.MoveLinesAtEndTo(ALine1, 1, 4);
1108 //ALine1.InsertLinesFromPage(ALine2, 1, 0, 4);
1109 AssertLineForWraps(ATestName, ALine1, w.init([5, 6, 1, 1, 1, 1, 3, 3, 1, 1,1]));
1110
1111 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1112 InitLine(ALine2, w.init([1]));
1113 ALine2.MoveLinesAtEndTo(ALine1, 1, 4);
1114 //ALine1.InsertLinesFromPage(ALine2, 1, 0, 4);
1115 AssertLineForWraps(ATestName, ALine1, w.init([1, 1, 1, 1, 1, 1, 3, 3, 1, 1,1]));
1116
1117
1118
1119 ATestName := 'Insert at end';
1120 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1121 InitLine(ALine2, w.init([4, 5, 6]));
1122 ALine2.MoveLinesAtStartTo(ALine1, 2, 5);
1123 //ALine1.InsertLinesFromPage(ALine2, 0, 5, 3);
1124 AssertLineForWraps(ATestName, ALine1, w.init([1, 1, 3, 3, 1, 4, 5, 6, 1,1]));
1125
1126 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1127 InitLine(ALine2, w.init([4, 5, 6]));
1128 ALine2.MoveLinesAtStartTo(ALine1, 2, 6);
1129 //ALine1.InsertLinesFromPage(ALine2, 0, 6, 3);
1130 AssertLineForWraps(ATestName, ALine1, w.init([1, 1, 3, 3, 1, 1, 4, 5, 6, 1,1]));
1131
1132
1133 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1134 InitLine(ALine2, w.init([1, 5, 6]));
1135 ALine2.MoveLinesAtStartTo(ALine1, 2, 5);
1136 //ALine1.InsertLinesFromPage(ALine2, 0, 5, 3);
1137 AssertLineForWraps(ATestName, ALine1, w.init([1, 1, 3, 3, 1, 1, 5, 6, 1,1]));
1138
1139 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1140 InitLine(ALine2, w.init([1, 5, 6]));
1141 ALine2.MoveLinesAtStartTo(ALine1, 2, 6);
1142 //ALine1.InsertLinesFromPage(ALine2, 0, 6, 3);
1143 AssertLineForWraps(ATestName, ALine1, w.init([1, 1, 3, 3, 1, 1, 1, 5, 6, 1,1]));
1144
1145 ///////////////////////////////////////
1146 (* split node at none wrapping lines - ensure the none-wrap "WrappedExtraSums" are stripped *)
1147 ATestName := 'Insert at start: empty lines in the middle -> dest 2';
1148 InitLine(ALine1, w.init([4, 5]));
1149 InitLine(ALine2, w.init([2, 1, 1, 1, 1, 3]));
1150 ALine2.MoveLinesAtEndTo(ALine1, 3, 3);
1151 AssertLineForWraps('', ALine1, w.init([1, 1, 3, 4, 5, 1,1]));
1152
1153 ATestName := 'Insert at start: empty lines in the middle -> dest 1';
1154 InitLine(ALine1, w.init([4]));
1155 InitLine(ALine2, w.init([2, 1, 1, 1, 1, 3]));
1156 ALine2.MoveLinesAtEndTo(ALine1, 3, 3);
1157 AssertLineForWraps('', ALine1, w.init([1, 1, 3, 4, 1,1]));
1158
1159 ATestName := 'Insert at start: empty lines in the middle -> empty dest';
1160 InitLine(ALine1, w.init([]));
1161 InitLine(ALine2, w.init([2, 1, 1, 1, 1, 3]));
1162 ALine2.MoveLinesAtEndTo(ALine1, 3, 3);
1163 AssertLineForWraps('', ALine1, w.init([1, 1, 3, 1,1]));
1164
1165 ATestName := 'Insert at start: empty lines in the middle -> dest 2 - with dest offset';
1166 InitLine(ALine1, w.init([1, 1, 4, 5]));
1167 InitLine(ALine2, w.init([2, 1, 1, 1, 1, 3]));
1168 ALine2.MoveLinesAtEndTo(ALine1, 3, 3);
1169 AssertLineForWraps('', ALine1, w.init([1, 1, 3, 1, 1, 4, 5, 1,1]));
1170
1171 ATestName := 'Insert at start: empty lines in the middle -> dest 2 - with source offset';
1172 InitLine(ALine1, w.init([4, 5]));
1173 InitLine(ALine2, w.init([1, 1, 2, 1, 1, 1, 1, 3]));
1174 ALine2.MoveLinesAtEndTo(ALine1, 5, 3);
1175 AssertLineForWraps('', ALine1, w.init([1, 1, 3, 4, 5, 1,1]));
1176
1177
1178
1179 ATestName := 'Insert at end : empty lines in the middle -> dest 2';
1180 InitLine(ALine1, w.init([4, 5]));
1181 InitLine(ALine2, w.init([2, 1, 1, 1, 1, 3]));
1182 ALine2.MoveLinesAtStartTo(ALine1, 3, 2);
1183 AssertLineForWraps('', ALine1, w.init([4, 5, 2, 1, 1, 1,1]));
1184
1185 ATestName := 'Insert at end : empty lines in the middle -> dest 1';
1186 InitLine(ALine1, w.init([4]));
1187 InitLine(ALine2, w.init([2, 1, 1, 1, 1, 3]));
1188 ALine2.MoveLinesAtStartTo(ALine1, 3, 1);
1189 AssertLineForWraps('', ALine1, w.init([4, 2, 1, 1, 1,1]));
1190
1191 ATestName := 'Insert at end : empty lines in the middle -> emyty dest';
1192 InitLine(ALine1, w.init([]));
1193 InitLine(ALine2, w.init([2, 1, 1, 1, 1, 3]));
1194 ALine2.MoveLinesAtStartTo(ALine1, 3, 0);
1195 AssertLineForWraps('', ALine1, w.init([2, 1, 1, 1,1]));
1196
1197 ATestName := 'Insert at end : empty lines in the middle -> dest 2 - with dest offset';
1198 InitLine(ALine1, w.init([1, 1, 4, 5]));
1199 InitLine(ALine2, w.init([2, 1, 1, 1, 1, 3]));
1200 ALine2.MoveLinesAtStartTo(ALine1, 3, 4);
1201 AssertLineForWraps('', ALine1, w.init([1, 1, 4, 5, 2, 1, 1, 1,1]));
1202
1203 ATestName := 'Insert at end : empty lines in the middle -> dest 2 - with source offset';
1204 InitLine(ALine1, w.init([4, 5]));
1205 InitLine(ALine2, w.init([1, 1, 2, 1, 1, 1, 1, 2]));
1206 ALine2.MoveLinesAtStartTo(ALine1, 5, 2);
1207 AssertLineForWraps('', ALine1, w.init([4, 5, 1, 1, 2, 1, 1, 1,1]));
1208
1209 end;
1210
1211 procedure TTestWordWrap.TestWordWrapLineMapMergeInvalidate;
1212 var
1213 ANode1, ANode2: TSynWordWrapIndexPage;
1214 ALine1, ALine2: TSynWordWrapLineMap;
1215 ATestName: String;
1216 w: TExpWraps;
1217
1218 procedure DoMoveLinesAtEndTo(const AName: String;
1219 const AWrapValues1, AWrapValues2: array of integer; AInvalLine: Integer; const AInvalDest: Boolean;
1220 ASourceStartLine, ALineCount: Integer;
1221 Exp: array of integer; ExpInval: Integer
1222 );
1223 begin
1224 InitLine(ALine1, w.init(AWrapValues1));
1225 InitLine(ALine2, w.init(AWrapValues2));
1226 if AInvalDest then
1227 ALine1.InvalidateLines(AInvalLine, AInvalLine)
1228 else
1229 ALine2.InvalidateLines(AInvalLine, AInvalLine);
1230 ALine2.MoveLinesAtEndTo(ALine1, ASourceStartLine, ALineCount);
1231 AssertLineForWraps(AName, ALine1, w.init(Exp));
1232 AssertEquals(AName+' invalid', ExpInval, ALine1.FirstInvalidLine);
1233 AssertEquals(AName+' invalid', ExpInval, ALine1.LastInvalidLine);
1234 end;
1235
1236 procedure DoMoveLinesAtStartTo(const AName: String;
1237 const AWrapValues1, AWrapValues2: array of integer; AInvalLine: Integer; const AInvalDest: Boolean;
1238 ASourceEndLine, ATargetStartLine: Integer;
1239 Exp: array of integer; ExpInval: Integer
1240 );
1241 begin
1242 InitLine(ALine1, w.init(AWrapValues1));
1243 InitLine(ALine2, w.init(AWrapValues2));
1244 if AInvalDest then
1245 ALine1.InvalidateLines(AInvalLine, AInvalLine)
1246 else
1247 ALine2.InvalidateLines(AInvalLine, AInvalLine);
1248 ALine2.MoveLinesAtStartTo(ALine1, ASourceEndLine, ATargetStartLine);
1249 AssertLineForWraps(AName, ALine1, w.init(Exp));
1250 AssertEquals(AName+' invalid', ExpInval, ALine1.FirstInvalidLine);
1251 AssertEquals(AName+' invalid', ExpInval, ALine1.LastInvalidLine);
1252 end;
1253
1254 begin
1255 ANode1 := TSynWordWrapIndexPage(FTree.FindPageForLine(0, afmCreate).Page);
1256 ANode2 := TSynWordWrapIndexPage(FTree.FindPageForLine(100, afmCreate).Page);
1257 ALine1 := ANode1.SynWordWrapLineMapStore;
1258 ALine2 := ANode2.SynWordWrapLineMapStore;
1259
1260 ATestName := 'Insert at start: target inval';
1261 InitLine(ALine1, w.init([2, 1, 3, 3, 1]));
1262 InitLine(ALine2, w.init([4, 5, 6]));
1263 ALine1.InvalidateLines(1, 1);
1264 ALine2.MoveLinesAtEndTo(ALine1, 0, 3);
1265 //ALine1.InsertLinesFromPage(ALine2, 0, 0, 3);
1266 AssertLineForWraps('', ALine1, w.init([4, 5, 6, 2, 1, 3, 3, 1, 1,1]));
1267 AssertEquals('invalid', 4, ALine1.FirstInvalidLine);
1268 AssertEquals('invalid', 4, ALine1.LastInvalidLine);
1269
1270 ATestName := 'Insert at start: source inval';
1271 InitLine(ALine1, w.init([2, 1, 3, 3, 1]));
1272 InitLine(ALine2, w.init([4, 5, 6]));
1273 ALine2.InvalidateLines(1, 1);
1274 ALine2.MoveLinesAtEndTo(ALine1, 0, 3);
1275 //ALine1.InsertLinesFromPage(ALine2, 0, 0, 3);
1276 AssertLineForWraps('', ALine1, w.init([4, 5, 6, 2, 1, 3, 3, 1, 1,1]));
1277 AssertEquals('invalid', 1, ALine1.FirstInvalidLine);
1278 AssertEquals('invalid', 1, ALine1.LastInvalidLine);
1279
1280 ATestName := 'Insert at start: source inval';
1281 InitLine(ALine1, w.init([2, 1, 3, 3, 1]));
1282 InitLine(ALine2, w.init([4, 5, 6]));
1283 ALine2.InvalidateLines(0, 1);
1284 ALine2.MoveLinesAtEndTo(ALine1, 1, 2);
1285 //ALine1.InsertLinesFromPage(ALine2, 1, 0, 2);
1286 AssertLineForWraps('', ALine1, w.init([5, 6, 2, 1, 3, 3, 1, 1,1]));
1287 AssertEquals('invalid', 0, ALine1.FirstInvalidLine);
1288 AssertEquals('invalid', 0, ALine1.LastInvalidLine);
1289
1290 ATestName := 'Insert at start: source inval';
1291 InitLine(ALine1, w.init([2, 1, 3, 3, 1]));
1292 InitLine(ALine2, w.init([4, 5, 6]));
1293 ALine2.InvalidateLines(0, 1);
1294 ALine2.MoveLinesAtEndTo(ALine1, 2, 1);
1295 //ALine1.InsertLinesFromPage(ALine2, 2, 0, 1);
1296 AssertLineForWraps('', ALine1, w.init([6, 2, 1, 3, 3, 1, 1,1]));
1297 AssertEquals('invalid', -1, ALine1.FirstInvalidLine);
1298 AssertEquals('invalid', -1, ALine1.LastInvalidLine);
1299
1300 ATestName := 'Insert at start: both inval';
1301 InitLine(ALine1, w.init([2, 1, 3, 3, 1]));
1302 InitLine(ALine2, w.init([4, 5, 6]));
1303 ALine1.InvalidateLines(1, 1);
1304 ALine2.InvalidateLines(2, 2);
1305 ALine2.MoveLinesAtEndTo(ALine1, 0, 3);
1306 //ALine1.InsertLinesFromPage(ALine2, 0, 0, 3);
1307 AssertLineForWraps('', ALine1, w.init([4, 5, 6, 2, 1, 3, 3, 1, 1,1]));
1308 AssertEquals('invalid', 2, ALine1.FirstInvalidLine);
1309 AssertEquals('invalid', 4, ALine1.LastInvalidLine);
1310
1311
1312 ATestName := 'Insert at end: source inval';
1313 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1314 InitLine(ALine2, w.init([4, 5, 6]));
1315 ALine2.InvalidateLines(2, 2);
1316 ALine2.MoveLinesAtStartTo(ALine1, 2, 5);
1317 //ALine1.InsertLinesFromPage(ALine2, 0, 5, 3);
1318 AssertLineForWraps(ATestName, ALine1, w.init([1, 1, 3, 3, 1, 4, 5, 6, 1,1]));
1319 AssertEquals('invalid', 7, ALine1.FirstInvalidLine);
1320 AssertEquals('invalid', 7, ALine1.LastInvalidLine);
1321
1322
1323
1324 ATestName := 'Insert from end to empty';
1325 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1326 InitLine(ALine2, w.init([]));
1327 ALine1.MoveLinesAtEndTo(ALine2, 3, 3);
1328 AssertLineForWraps(ATestName, ALine2, w.init([3, 1, 1, 1,1,1]));
1329
1330 ATestName := 'Insert from end to empty';
1331 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1332 InitLine(ALine2, w.init([]));
1333 ALine1.MoveLinesAtEndTo(ALine2, 2, 3);
1334 AssertLineForWraps(ATestName, ALine2, w.init([3, 3, 1, 1,1,1]));
1335
1336 ATestName := 'Insert from end to empty';
1337 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1338 InitLine(ALine2, w.init([]));
1339 ALine1.MoveLinesAtEndTo(ALine2, 1, 3);
1340 AssertLineForWraps(ATestName, ALine2, w.init([1, 3, 3, 1,1,1]));
1341
1342 ATestName := 'Insert from end to empty';
1343 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1344 InitLine(ALine2, w.init([]));
1345 ALine1.MoveLinesAtEndTo(ALine2, 1, 4);
1346 AssertLineForWraps(ATestName, ALine2, w.init([1, 3, 3, 1, 1,1,1]));
1347
1348 ATestName := 'Insert from after end to empty';
1349 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1350 InitLine(ALine2, w.init([]));
1351 ALine1.MoveLinesAtEndTo(ALine2, 6, 3);
1352 AssertLineForWraps(ATestName, ALine2, w.init([1, 1, 1, 1,1,1]));
1353
1354
1355 ATestName := 'Insert from start to empty';
1356 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1357 InitLine(ALine2, w.init([]));
1358 ALine1.MoveLinesAtStartTo(ALine2, 2, 0);
1359 AssertLineForWraps(ATestName, ALine2, w.init([1, 1, 3, 1,1,1]));
1360
1361 ATestName := 'Insert from start to empty';
1362 InitLine(ALine1, w.init([2, 1, 3, 3, 1]));
1363 InitLine(ALine2, w.init([]));
1364 ALine1.MoveLinesAtStartTo(ALine2, 2, 0);
1365 AssertLineForWraps(ATestName, ALine2, w.init([2, 1, 3, 1,1,1,1]));
1366
1367 ATestName := 'Insert from start to empty';
1368 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1369 InitLine(ALine2, w.init([]));
1370 ALine1.MoveLinesAtStartTo(ALine2, 1, 0);
1371 AssertLineForWraps(ATestName, ALine2, w.init([1, 1, 1,1,1,1]));
1372
1373 ATestName := 'Insert from start to empty';
1374 InitLine(ALine1, w.init([1, 1, 3, 3, 1]));
1375 InitLine(ALine2, w.init([]));
1376 ALine1.MoveLinesAtStartTo(ALine2, 2, 3);
1377 AssertLineForWraps(ATestName, ALine2, w.init([1, 1, 1, 1, 1, 3, 1,1,1]));
1378
1379
1380 ///////////////////////////////////////
1381 (* split node at none wrapping lines - ensure the none-wrap "WrappedExtraSums" are stripped *)
1382 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2',
1383 [4, 5], [2, 1, 1, 1, 1, 3], 1, True, 3, 3, {=>} [1, 1, 3, 4, 5, 1,1], 4);
1384
1385 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2',
1386 [4, 5], [2, 1, 1, 1, 1, 3], 1, False, 3, 3, {=>} [1, 1, 3, 4, 5, 1,1], -1);
1387 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2',
1388 [4, 5], [2, 1, 1, 1, 1, 3], 2, False, 3, 3, {=>} [1, 1, 3, 4, 5, 1,1], -1);
1389 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2',
1390 [4, 5], [2, 1, 1, 1, 1, 3], 3, False, 3, 3, {=>} [1, 1, 3, 4, 5, 1,1], 0);
1391 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2',
1392 [4, 5], [2, 1, 1, 1, 1, 3], 4, False, 3, 3, {=>} [1, 1, 3, 4, 5, 1,1], 1);
1393
1394
1395 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2 - gap at source end',
1396 [4, 5], [2, 1, 1, 1, 1, 3], 1, True, 3, 4, {=>} [1, 1, 3, 1, 4, 5, 1,1], 5);
1397
1398 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2 - gap at source end',
1399 [4, 5], [2, 1, 1, 1, 1, 3], 1, False, 3, 4, {=>} [1, 1, 3, 1, 4, 5, 1,1], -1);
1400 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2 - gap at source end',
1401 [4, 5], [2, 1, 1, 1, 1, 3], 4, False, 3, 4, {=>} [1, 1, 3, 1, 4, 5, 1,1], 1);
1402
1403
1404 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 1',
1405 [4], [2, 1, 1, 1, 1, 3], 1, True, 3, 3, {=>} [1, 1, 3, 4, 1,1], 4);
1406
1407 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 1',
1408 [4], [2, 1, 1, 1, 1, 3], 1, False, 3, 3, {=>} [1, 1, 3, 4, 1,1], -1);
1409 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 1',
1410 [4], [2, 1, 1, 1, 1, 3], 3, False, 3, 3, {=>} [1, 1, 3, 4, 1,1], 0);
1411
1412
1413 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> empty dest',
1414 [], [2, 1, 1, 1, 1, 3], 1, True, 3, 3, {=>} [1, 1, 3, 1,1], 4);
1415
1416 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> empty dest',
1417 [], [2, 1, 1, 1, 1, 3], 1, False, 3, 3, {=>} [1, 1, 3, 1,1], -1);
1418 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> empty dest',
1419 [], [2, 1, 1, 1, 1, 3], 4, False, 3, 3, {=>} [1, 1, 3, 1,1], 1);
1420
1421
1422 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2 - with dest offset',
1423 [1,1,4, 5], [2, 1, 1, 1, 1, 3], 1, True, 3, 3, {=>} [1, 1, 3, 1,1,4, 5, 1,1], 4);
1424
1425 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2 - with dest offset',
1426 [1,1,4, 5], [2, 1, 1, 1, 1, 3], 1, False, 3, 3, {=>} [1, 1, 3, 1,1,4, 5, 1,1], -1);
1427 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2 - with dest offset',
1428 [1,1,4, 5], [2, 1, 1, 1, 1, 3], 4, False, 3, 3, {=>} [1, 1, 3, 1,1,4, 5, 1,1], 1);
1429
1430
1431 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2 - with source offset',
1432 [4, 5], [1,1,2, 1, 1, 1, 1, 3], 1, True, 5, 3, {=>} [1, 1, 3, 4, 5, 1,1], 4);
1433
1434 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2 - with source offset',
1435 [4, 5], [1,1,2, 1, 1, 1, 1, 3], 1, False, 5, 3, {=>} [1, 1, 3, 4, 5, 1,1], -1);
1436 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2 - with source offset',
1437 [4, 5], [1,1,2, 1, 1, 1, 1, 3], 3, False, 5, 3, {=>} [1, 1, 3, 4, 5, 1,1], -1);
1438 DoMoveLinesAtEndTo('Insert at start: empty lines in the middle -> dest 2 - with source offset',
1439 [4, 5], [1,1,2, 1, 1, 1, 1, 3], 6, False, 5, 3, {=>} [1, 1, 3, 4, 5, 1,1], 1);
1440
1441
1442
1443
1444
1445 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2',
1446 [4, 5], [2, 1, 1, 1, 1, 3], 1, True, 3, 2, {=>} [4, 5, 2, 1, 1, 1,1], 1);
1447
1448 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2',
1449 [4, 5], [2, 1, 1, 1, 1, 3], 1, False, 3, 2, {=>} [4, 5, 2, 1, 1, 1,1], 3);
1450 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2',
1451 [4, 5], [2, 1, 1, 1, 1, 3], 4, False, 3, 2, {=>} [4, 5, 2, 1, 1, 1,1], -1);
1452
1453
1454 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2 - gap at target end',
1455 [4, 5], [2, 1, 1, 1, 1, 3], 1, True, 3, 3, {=>} [4, 5, 1, 2, 1, 1, 1,1], 1);
1456
1457 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2 - gap at target end',
1458 [4, 5], [2, 1, 1, 1, 1, 3], 1, False, 3, 3, {=>} [4, 5, 1, 2, 1, 1, 1,1], 4);
1459 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2 - gap at target end',
1460 [4, 5], [2, 1, 1, 1, 1, 3], 4, False, 3, 3, {=>} [4, 5, 1, 2, 1, 1, 1,1], -1);
1461
1462
1463 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 1',
1464 [4], [2, 1, 1, 1, 1, 3], 1, True, 3, 1, {=>} [4, 2, 1, 1, 1,1], 1);
1465
1466 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 1',
1467 [4], [2, 1, 1, 1, 1, 3], 1, False, 3, 1, {=>} [4, 2, 1, 1, 1,1], 2);
1468 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 1',
1469 [4], [2, 1, 1, 1, 1, 3], 4, False, 3, 1, {=>} [4, 2, 1, 1, 1,1], -1);
1470
1471
1472 // empty dest can not have invalid...
1473 //DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> empty dest',
1474 // [], [2, 1, 1, 1, 1, 3], 1, True, 3, 0, {=>} [2, 1, 1, 1,1], 1);
1475
1476 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> empty dest',
1477 [], [2, 1, 1, 1, 1, 3], 1, False, 3, 0, {=>} [2, 1, 1, 1,1], 1);
1478 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> empty dest',
1479 [], [2, 1, 1, 1, 1, 3], 4, False, 3, 0, {=>} [2, 1, 1, 1,1], -1);
1480
1481
1482 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> empty dest - gap',
1483 [], [2, 1, 1, 1, 1, 3], 1, True, 3, 2, {=>} [1,1, 2, 1, 1, 1,1], 1);
1484
1485 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> empty dest - gap',
1486 [], [2, 1, 1, 1, 1, 3], 1, False, 3, 2, {=>} [1,1, 2, 1, 1, 1,1], 3);
1487 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> empty dest- gap',
1488 [], [2, 1, 1, 1, 1, 3], 4, False, 3, 2, {=>} [1,1, 2, 1, 1, 1,1], -1);
1489
1490
1491 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2 - with dest offset',
1492 [1,1, 4, 5], [2, 1, 1, 1, 1, 3], 1, True, 3, 4, {=>} [1,1, 4, 5, 2, 1, 1, 1,1], 1);
1493
1494 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2 - with dest offset',
1495 [1,1, 4, 5], [2, 1, 1, 1, 1, 3], 1, False, 3, 4, {=>} [1,1, 4, 5, 2, 1, 1, 1,1], 5);
1496 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2 - with dest offset',
1497 [1,1, 4, 5], [2, 1, 1, 1, 1, 3], 4, False, 3, 4, {=>} [1,1, 4, 5, 2, 1, 1, 1,1], -1);
1498
1499
1500 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2 - with dest offset - gap',
1501 [1,1, 4, 5], [2, 1, 1, 1, 1, 3], 1, True, 3, 5, {=>} [1,1, 4, 5, 1, 2, 1, 1, 1,1], 1);
1502
1503 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2 - with dest offset - gap',
1504 [1,1, 4, 5], [2, 1, 1, 1, 1, 3], 1, False, 3, 5, {=>} [1,1, 4, 5, 1, 2, 1, 1, 1,1], 6);
1505 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2 - with dest offset - gap',
1506 [1,1, 4, 5], [2, 1, 1, 1, 1, 3], 4, False, 3, 5, {=>} [1,1, 4, 5, 1, 2, 1, 1, 1,1], -1);
1507
1508
1509 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2 - with source offset',
1510 [4, 5], [1,1, 2, 1, 1, 1, 1, 3], 1, True, 5, 2, {=>} [4, 5, 1,1, 2, 1, 1, 1,1], 1);
1511
1512 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2 - with source offset',
1513 [4, 5], [1,1, 2, 1, 1, 1, 1, 3], 1, False, 5, 2, {=>} [4, 5, 1,1, 2, 1, 1, 1,1], 3);
1514 DoMoveLinesAtStartTo('Insert at start: empty lines in the middle -> dest 2 - with source offset',
1515 [4, 5], [1,1, 2, 1, 1, 1, 1, 3], 6, False, 5, 2, {=>} [4, 5, 1,1, 2, 1, 1, 1,1], -1);
1516
1517
1518 end;
1519
1520 procedure TTestWordWrap.TestWordWrapJoinWithSibling;
1521 var
1522 CurWraps: TExpWraps;
1523
1524 procedure DoTestAssert(AName: String; ExpNodeCnt: Integer);
1525 begin
1526 //debugln(AName); FTree.DebugDump ;
1527 AssertTreeForWraps(AName + ' Wrap', CurWraps);
1528 AssertEquals(AName + ' Cnt ', ExpNodeCnt, TreeNodeCount);
1529 end;
1530
1531 procedure DoTestInit(AName: String; AFromVal, ALineCount, AnIncrease, ExpNodeCnt: Integer);
1532 begin
1533 FTree.Clear;
1534 CurWraps.InitFill(AFromVal, AFromVal+ALineCount-1, AnIncrease); // 4 pages
1535 FTree.AdjustForLinesInserted(0, ALineCount, 0);
1536 ValidateTreeWraps(CurWraps);
1537
1538 DoTestAssert(Format('%s (Init %d, %d) Wrap', [AName, AFromVal, ALineCount]), ExpNodeCnt);
1539 end;
1540
1541 procedure DoTestChgWrp(AName: String; AStartLine, ALineCount, AFromVal, AnIncrease, ExpNodeCnt: Integer);
1542 begin
1543 CurWraps.FillRange(AStartLine, ALineCount, AFromVal, AnIncrease);
1544 FTree.InvalidateLines(AStartLine, AStartLine + ALineCount - 1);
1545 ValidateTreeWraps(CurWraps);
1546
1547 DoTestAssert(Format('%s (Changed %d, %d) Wrap', [AName, AStartLine, ALineCount]), ExpNodeCnt);
1548 end;
1549
1550 procedure DoTestDelete(AName: String; AStartLine, ALineCount, ExpNodeCnt: Integer);
1551 begin
1552 FTree.AdjustForLinesDeleted(AStartLine, ALineCount,0);
1553 CurWraps.SpliceArray(AStartLine, ALineCount);
1554
1555 DoTestAssert(Format('%s (Deleted %d, %d) Wrap', [AName, AStartLine, ALineCount]), ExpNodeCnt);
1556 end;
1557
1558 var
1559 OffsetAtStart, OffsetAtEnd, Node1Del, Node2Del, FinalNodeCount: Integer;
1560 N: String;
1561 begin
1562 (* After "DoTestInit" each node should be filled to the max.
1563 So the test knows where each node begins
1564 *)
1565 FTree := CreateTree(10, 30, 4); // APageJoinSize, APageSplitSize, APageJoinDistance
1566
1567 For OffsetAtStart := 0 to 3 do
1568 For OffsetAtEnd := 0 to 3 do // RealEnd = NextNode.Startline-1 - x
1569 For Node1Del := 18 to 25 do
1570 For Node2Del := 18 to 25 do
1571 begin
1572 N := Format('Start: %d End: %d Del1: %d Del2: %d', [OffsetAtStart, OffsetAtEnd, Node1Del, Node2Del]);
1573
1574 FinalNodeCount := 3;
1575 if (OffsetAtStart + OffsetAtEnd > 4) or // APageJoinDistance not met
1576 (Node1Del + OffsetAtEnd < 20) or (Node2Del + OffsetAtStart < 20)
1577 then
1578 FinalNodeCount := 4;
1579
1580 DoTestInit (N, 10, 4*30, 1, 4); // Create 4 full nodes
1581
1582 if OffsetAtStart > 0 then
1583 DoTestChgWrp(N, 90, OffsetAtStart, 1, 0, 4); // At Start of Last node
1584 if OffsetAtEnd > 0 then
1585 DoTestChgWrp(N, 90-OffsetAtEnd, OffsetAtEnd, 1, 0, 4); // At End of 2nd-Last node
1586
1587 DoTestDelete(N, 60, Node1Del, 4);
1588 DoTestDelete(N, 95-Node1Del, Node2Del, FinalNodeCount);
1589
1590 // Delete from 2nd node before 1st node
1591 DoTestInit (N, 10, 4*30, 1, 4); // Create 4 full nodes
1592
1593 if OffsetAtStart > 0 then
1594 DoTestChgWrp(N, 90, OffsetAtStart, 1, 0, 4); // At Start of Last node
1595 if OffsetAtEnd > 0 then
1596 DoTestChgWrp(N, 90-OffsetAtEnd, OffsetAtEnd, 1, 0, 4); // At End of 2nd-Last node
1597
1598 DoTestDelete(N, 95, Node2Del, 4);
1599 DoTestDelete(N, 60, Node1Del, FinalNodeCount);
1600
1601 end;
1602 end;
1603
1604
1605 procedure TTestWordWrap.TestWordWrapTreeInsertThenDelete;
1606 var
1607 CurWraps: TExpWraps;
1608 InsPos, InsLen, DelPos, DelCount: Integer;
1609 begin
1610 FTree.Free;
1611 FTree := CreateTree(2, 9, 4);
1612 // FTree := TSynLineMapAVLTree.Create(TSynWordWrapIndexPage, 2, 11, 4);
1613 for DelPos := 0 to 26 do
1614 for DelCount := 1 to Min(5, 27-DelPos) do
1615 for InsPos := 0 to 29 do
1616 for InsLen := 1 to 4 do begin
1617 FTree.Clear;
1618
1619 // init
1620 CurWraps.InitFill(10, 10+26);
1621 FTree.AdjustForLinesInserted(0, 27, 0);
1622 ValidateTreeWraps(CurWraps);
1623 //FTree.DebugDump;
1624 AssertTreeForWraps(Format('Before ins at pos %d Len %d', [InsPos, InsLen]), CurWraps);
1625
1626 // ins
1627 CurWraps.Join(FillArray(500, 499+InsLen), InsPos);
1628 FTree.AdjustForLinesInserted(InsPos, InsLen, 0);
1629 //FTree.DebugDump;
1630 ValidateTreeWraps(CurWraps);
1631 //FTree.DebugDump;
1632 AssertTreeForWraps(Format('After ins at pos %d Len %d ins at pos %d Len %d', [DelPos, DelCount, InsPos, InsLen]), CurWraps);
1633
1634 // del
1635 FTree.AdjustForLinesDeleted(DelPos, DelCount, 0);
1636 CurWraps.SpliceArray(DelPos, DelCount);
1637 AssertTrue(Format('valid After del at pos %d Len %d ins at pos %d Len %d', [DelPos, DelCount, InsPos, InsLen]),
1638 not FTree.NeedsValidation);
1639 AssertTreeForWraps(Format('After del at pos %d Len %d ins at pos %d Len %d', [DelPos, DelCount, InsPos, InsLen]), CurWraps);
1640 end;
1641 end;
1642
1643 procedure TTestWordWrap.TestWordWrapTreeDeleteThenInsert;
1644 var
1645 CurWraps: TExpWraps;
1646 InsPos, InsLen, DelPos, DelCount: Integer;
1647 begin
1648 FTree.Free;
1649 FTree := CreateTree(2, 9, 4);
1650 // FTree := TSynLineMapAVLTree.Create(TSynWordWrapIndexPage, 2, 11, 4);
1651 for DelPos := 0 to 26 do
1652 for DelCount := 1 to Min(5, 27-DelPos) do
1653 for InsPos := 0 to 29 do
1654 for InsLen := 1 to 4 do begin
1655 //if (InsPos<>15) or (InsLen<>1) or (DelPos<>0) or (DelCount<>1) then continue;
1656 FTree.Clear;
1657
1658 // init
1659 CurWraps.InitFill(10, 10+26);
1660 FTree.AdjustForLinesInserted(0, 27, 0);
1661 ValidateTreeWraps(CurWraps);
1662 //FTree.DebugDump;
1663 AssertTreeForWraps(Format('Before del at pos %d Len %d ins at pos %d Len %d', [DelPos, DelCount, InsPos, InsLen]), CurWraps);
1664
1665 // del
1666 FTree.AdjustForLinesDeleted(DelPos, DelCount, 0);
1667 CurWraps.SpliceArray(DelPos, DelCount);
1668 AssertTrue(Format('valid After del at pos %d Len %d ins at pos %d Len %d', [DelPos, DelCount, InsPos, InsLen]),
1669 not FTree.NeedsValidation);
1670 AssertTreeForWraps(Format('After del at pos %d Len %d ins at pos %d Len %d', [DelPos, DelCount, InsPos, InsLen]), CurWraps);
1671
1672 // ins
1673 FTree.AdjustForLinesInserted(InsPos, InsLen, 0);
1674 CurWraps.Join(FillArray(500, 499+InsLen), InsPos);
1675 //FTree.DebugDump;
1676 ValidateTreeWraps(CurWraps);
1677 //FTree.DebugDump;
1678 AssertTreeForWraps(Format('After del/ins : del at pos %d Len %d ins at pos %d Len %d', [DelPos, DelCount, InsPos, InsLen]), CurWraps);
1679 end;
1680 end;
1681
1682 { TTestWordWrapPluginBase }
1683
1684 procedure TTestWordWrapPluginBase.ClearCaret;
1685 begin
1686 SynEdit.CaretXY := Point(2,2);
1687 SynEdit.CaretXY := Point(1,1);
1688 end;
1689
TTestWordWrapPluginBase.GetTreeNodeHoldernull1690 function TTestWordWrapPluginBase.GetTreeNodeHolder(AIndex: Integer
1691 ): TSynEditLineMapPageHolder;
1692 begin
1693 Result := FWordWrap.FLineMapView.Tree.FirstPage;
1694 while (AIndex > 0) and Result.HasPage do begin
1695 dec(AIndex);
1696 Result := Result.Next;
1697 end;
1698
1699 end;
1700
1701 procedure TTestWordWrapPluginBase.SetCaret(SourcePt: TPointType; APos: TPoint);
1702 begin
1703 case SourcePt of
1704 ptViewed: SynEdit.CaretObj.ViewedLineCharPos := Apos;
1705 ptAlternateViewed: SynEdit.CaretObj.ViewedLineCharPos := Apos;
1706 ptPhys: SynEdit.CaretObj.LineCharPos := Apos;
1707 ptLog: SynEdit.CaretObj.LineBytePos := APos;
1708 end;
1709 end;
1710
1711 procedure TTestWordWrapPluginBase.TestCaret(AName: String;SourcePt, ExpPt: TPointType;
1712 AnExp: TPoint; AnExpOffs: Integer);
1713 var
1714 got: TPoint;
1715 src, dest: String;
1716 begin
1717 case ExpPt of
1718 ptViewed: got := SynEdit.CaretObj.ViewedLineCharPos;
1719 ptAlternateViewed: got := SynEdit.CaretObj.ViewedLineCharPos;
1720 ptPhys: got := SynEdit.CaretObj.LineCharPos;
1721 ptLog: got := SynEdit.CaretObj.LineBytePos;
1722 end;
1723 writestr(src, SourcePt);
1724 writestr(dest, ExpPt);
1725 AssertEquals(Format('%s (%s -> %s)', [AName, src, dest]), AnExp, got);
1726 if (ExpPt = ptLog) and (AnExpOffs >= 0) then
1727 AssertEquals(Format('%s (%s -> %s) Offs: ', [AName, src, dest]), AnExpOffs, SynEdit.CaretObj.BytePosOffset);
1728 end;
1729
1730 class procedure TTestWordWrapPluginBase.AssertEquals(const AMessage: string;
1731 Expected, Actual: TPoint);
1732 begin
1733 AssertEquals(AMessage, dbgs(Expected), dbgs(Actual));
1734 end;
1735
1736 procedure TTestWordWrapPluginBase.AddLines(AFirstLineIdx, ACount,
1737 ALen: Integer; AnID: String; SkipBeginUpdate: Boolean;
1738 AReplaceExisting: Boolean);
1739 var
1740 i, j: Integer;
1741 l: String;
1742 begin
1743 if not SkipBeginUpdate then SynEdit.BeginUpdate;
1744 for i := 0 to ACount - 1 do begin
1745 l := '';
1746 j := 0;
1747 while Length(l) < ALen do begin
1748 l := l + copy(AnID+'_'+IntToStr(i)+'_'+IntToStr(j) + ' ',1,12);
1749 inc(j);
1750 end;
1751 l := copy(l, 1, ALen);
1752 if AReplaceExisting then
1753 SynEdit.Lines[AFirstLineIdx + i] := l
1754 else
1755 SynEdit.Lines.Insert(AFirstLineIdx + i, l);
1756 end;
1757 if not SkipBeginUpdate then SynEdit.EndUpdate;
1758 end;
1759
1760 procedure TTestWordWrapPluginBase.InternalCheckLine(AName: String;
1761 dsp: TLazSynDisplayView; ALine: TLineIdx; AExpTextStart: String;
1762 NoTrim: Boolean);
1763 var
1764 gotRealLine: TLineIdx;
1765 gotStartPos, GotLineLen: Integer;
1766 gotTokenOk: Boolean;
1767 gotToken: TLazSynDisplayTokenInfo;
1768 gotText: PChar;
1769 s: String;
1770 begin
1771 dsp.SetHighlighterTokensLine(ALine, gotRealLine, gotStartPos, GotLineLen);
1772 gotTokenOk := dsp.GetNextHighlighterToken(gotToken);
1773 if gotTokenOk then
1774 gotText := gotToken.TokenStart
1775 else
1776 gotText := '';
1777
1778 if AExpTextStart = '' then begin
1779 AssertEquals(AName, '', gotText);
1780 AssertEquals(AName, 0, GotLineLen);
1781 exit;
1782 end
1783 else
1784 if gotText = '' then begin
1785 AssertTrue(AName, False);
1786 exit;
1787 end;
1788
1789 if NoTrim then
1790 s := copy(gotText, 1, Length(AExpTextStart))
1791 else
1792 s := copy(Trim(gotText), 1, Length(AExpTextStart));
1793 if not(AExpTextStart = s) then begin
1794 debugln(['Failed ', AName, ' ', ALine, ':']);
1795 DebugLn(['GOT: "', StringReplace(gotText, #9, '#9', [rfReplaceAll]), '"']);
1796 DebugLn(['EXP: "', StringReplace(AExpTextStart, #9, '#9', [rfReplaceAll]), '"']);
1797 end;
1798 AssertEquals(AName, AExpTextStart, s);
1799 end;
1800
1801 procedure TTestWordWrapPluginBase.CheckLine(AName: String; ALine: TLineIdx;
1802 AExpTextStart: String; NoTrim: Boolean);
1803 var
1804 v: TSynEditStringsLinked;
1805 dsp: TLazSynDisplayView;
1806 begin
1807 v := SynEdit.TextViewsManager.SynTextView[SynEdit.TextViewsManager.Count - 1];
1808 dsp := v.DisplayView;
1809 dsp.InitHighlighterTokens(nil);
1810 try
1811 InternalCheckLine(AName, dsp, ALine, AExpTextStart, NoTrim);
1812 finally
1813 dsp.FinishHighlighterTokens;
1814 end;
1815 end;
1816
1817 procedure TTestWordWrapPluginBase.CheckLines(AName: String;
1818 AStartLine: TLineIdx; AExpTextStart: array of String; NoTrim: Boolean);
1819 var
1820 v: TSynEditStringsLinked;
1821 dsp: TLazSynDisplayView;
1822 i, gotStartPos, GotLineLen: Integer;
1823 gotTokenOk: Boolean;
1824 gotToken: TLazSynDisplayTokenInfo;
1825 s: String;
1826 gotRealLine: TLineIdx;
1827 begin
1828 v := SynEdit.TextViewsManager.SynTextView[SynEdit.TextViewsManager.Count - 1];
1829 dsp := v.DisplayView;
1830 dsp.InitHighlighterTokens(nil);
1831 try
1832 try
1833 for i := 0 to Length(AExpTextStart)-1 do
1834 InternalCheckLine(AName, dsp, AStartLine+i, AExpTextStart[i], NoTrim);
1835 except
1836 dsp.FinishHighlighterTokens;
1837 dsp.InitHighlighterTokens(nil);
1838 for i := 0 to Length(AExpTextStart)-1 do begin
1839 dsp.SetHighlighterTokensLine(AStartLine+i, gotRealLine, gotStartPos, GotLineLen);
1840 s := '';
1841 while dsp.GetNextHighlighterToken(gotToken) and (gotToken.TokenLength > 0) do
1842 s := s + copy(gotToken.TokenStart, 1, gotToken.TokenLength);
1843 debugln('Line %d (real %d): "%s" %d/%d start %d',
1844 [AStartLine+i, gotRealLine, StringReplace(s, #9, '#9', [rfReplaceAll]), length(s), GotLineLen, gotStartPos]);
1845 end;
1846 raise;
1847 end;
1848 finally
1849 dsp.FinishHighlighterTokens;
1850 end;
1851 end;
1852
1853 procedure TTestWordWrapPluginBase.CheckLine(AName: String;
1854 AExpLine: TTestWrapLineInfo);
1855 begin
1856 CheckLine(AName, AExpLine.ViewedIdx, AExpLine.TextStartMatch, AExpLine.NoTrim);
1857 end;
1858
1859 procedure TTestWordWrapPluginBase.CheckLines(AName: String;
1860 AExpLines: TTestViewedLineRangeInfo);
1861 var
1862 i: Integer;
1863 n: TSynEditLineMapPageHolder;
1864 begin
1865 for i := 0 to length(AExpLines) - 1 do begin
1866 CheckLine(Format('%s (%d)', [AName, i]), AExpLines[i]);
1867 CheckLineIndexMapping(Format('%s (%d)', [AName, i]), AExpLines[i].TextIdx, AExpLines[i].ViewedTopIdx, AExpLines[i].ViewedBottomIdx);
1868 end;
1869 if TheTree <> nil then begin
1870 n := TheTree.FirstPage;
1871 if n.HasPage then
1872 CheckTree(AName+' (TreeCheck)', n.Page, n.StartLine, 0, SynEdit.Lines.Count-1);
1873 end;
1874 end;
1875
1876 procedure TTestWordWrapPluginBase.CheckXyMap(AName: String; APhysTExtXY,
1877 AViewedXY: TPoint; OnlyViewToText: Boolean);
1878 var
1879 v: TSynEditStringsLinked;
1880 GotTextXY, GotViewXY: TPoint;
1881 begin
1882 v := SynEdit.TextViewsManager.SynTextView[SynEdit.TextViewsManager.Count - 1];
1883 GotTextXY := v.ViewXYToTextXY(AViewedXY);
1884 GotViewXY := v.TextXYToViewXY(APhysTExtXY);
1885
1886 AssertTrue(Format('%s: Viewed %s to Text %s (exp) => got %s', [AName, dbgs(AViewedXY), dbgs(APhysTExtXY), dbgs(GotTextXY)]),
1887 (GotTextXY.x = APhysTExtXY.x) and (GotTextXY.y = APhysTExtXY.y) );
1888 if not OnlyViewToText then
1889 AssertTrue(Format('%s: Text %s to viewed %s (exp) => got %s', [AName, dbgs(APhysTExtXY), dbgs(AViewedXY), dbgs(GotViewXY)]),
1890 (GotViewXY.x = AViewedXY.x) and (GotViewXY.y = AViewedXY.y) );
1891 end;
1892
1893 procedure TTestWordWrapPluginBase.CheckXyMap(AName: String; APhysTExtX,
1894 APhysTExtY, AViewedX, AViewedY: integer; OnlyViewToText: Boolean);
1895 begin
1896 CheckXyMap(AName, Point(APhysTExtX, APhysTExtY), Point(AViewedX, AViewedY), OnlyViewToText);
1897 end;
1898
1899 procedure TTestWordWrapPluginBase.CheckXyMap(AName: String;
1900 APoints: TPointSpecs);
1901 var
1902 StartP, TestP: TPointType;
1903 begin
1904 CheckXyMap(AName + 'XyMap', APoints.xy[ptPhys], APoints.xy[ptViewed]);
1905 if APoints.xy[ptAlternateViewed].x > 0 then
1906 CheckXyMap(AName+ 'XyMap(a)', APoints.xy[ptPhys], APoints.xy[ptAlternateViewed], True);
1907
1908
1909 for TestP in TPointType do begin
1910 if TestP = ptAlternateViewed then
1911 continue;
1912
1913 ClearCaret;
1914 SynEdit.CaretXY := APoints.XY[ptPhys];
1915 TestCaret(AName, ptPhys, TestP, APoints.XY[TestP], APoints.LogOffs);
1916
1917 if APoints.LogOffs <= 0 then begin
1918 ClearCaret;
1919 SynEdit.LogicalCaretXY := APoints.XY[ptLog];
1920 TestCaret(AName, ptLog, TestP, APoints.XY[TestP], APoints.LogOffs);
1921 end;
1922
1923 AName := AName + ' [CaretObj] ';
1924 for StartP in TPointType do begin
1925 if APoints.xy[StartP].x <= 0 then
1926 Continue;
1927 if (StartP = ptAlternateViewed) then // and (TestP = ptViewed) then
1928 continue;
1929 if (StartP = ptLog) and (APoints.LogOffs > 0) then
1930 continue;
1931
1932 ClearCaret;
1933 SetCaret(StartP, APoints.XY[StartP]);
1934 TestCaret(AName, StartP, TestP, APoints.XY[TestP], APoints.LogOffs);
1935 end;
1936 end;
1937
1938 end;
1939
1940 procedure TTestWordWrapPluginBase.CheckXyMap(AName: String;
1941 APoints: TPointSpecs; ATestCommands: array of TCommandAndPointSpecs);
1942 var
1943 i: Integer;
1944 StartP, TestP: TPointType;
1945 n: string;
1946 c: TSynEditorCommand;
1947 begin
1948 CheckXyMap(AName+'(p)', APoints);
1949
1950 for i := 0 to Length(ATestCommands) - 1 do begin
1951 if not ATestCommands[i].RunOnlyIf then
1952 Continue;
1953 n := AName+'(p'+IntToStr(i)+')';
1954 CheckXyMap(n, ATestCommands[i].Exp);
1955
1956 for StartP in TPointType do begin
1957 if APoints.xy[StartP].x <= 0 then
1958 Continue;
1959 if (StartP = ptAlternateViewed) then // and (TestP = ptViewed) then
1960 continue;
1961 if (StartP = ptLog) and (APoints.LogOffs > 0) then
1962 continue;
1963
1964 for TestP in TPointType do begin
1965 if TestP = ptAlternateViewed then
1966 continue;
1967 ClearCaret;
1968 SetCaret(StartP, APoints.XY[StartP]);
1969 for c in ATestCommands[i].Cmd do
1970 SynEdit.ExecuteCommand(c, '', nil);
1971
1972 TestCaret(n, StartP, TestP, ATestCommands[i].Exp.XY[TestP], ATestCommands[i].Exp.LogOffs);
1973 end;
1974 end;
1975 end;
1976 end;
1977
1978 procedure TTestWordWrapPluginBase.CheckLineIndexMapping(AName: String;
1979 ATextIdx, AViewTopIdx, AViewBottomIdx: TLineIdx);
1980 var
1981 v: TSynEditStringsLinked;
1982 i: TLineIdx;
1983 dv: TLazSynDisplayView;
1984 r: TLineRange;
1985 begin
1986 v := SynEdit.TextViewsManager.SynTextView[SynEdit.TextViewsManager.Count - 1];
1987 dv := v.DisplayView;
1988
1989 AssertEquals(AName + ' TextToViewIndex', AViewTopIdx, v.TextToViewIndex(ATextIdx));
1990 for i := AViewTopIdx to AViewBottomIdx do
1991 AssertEquals(AName + ' ViewToTextIndex', ATextIdx, v.ViewToTextIndex(i));
1992
1993 r := dv.TextToViewIndex(ATextIdx);
1994 AssertEquals(AName + 'DispView.TextToViewIndex Top', AViewTopIdx, r.Top);
1995 AssertEquals(AName + 'DispView.TextToViewIndex Bottom', AViewBottomIdx, r.Bottom);
1996 for i := AViewTopIdx to AViewBottomIdx do begin
1997 AssertEquals(AName + ' ViewToTextIndex', ATextIdx, dv.ViewToTextIndex(i));
1998
1999 AssertEquals(AName + ' ViewToTextIndexEx', ATextIdx, dv.ViewToTextIndexEx(i, r));
2000 AssertEquals(AName + ' ViewToTextIndexEx Top', AViewTopIdx, r.Top);
2001 AssertEquals(AName + ' ViewToTextIndexEx Bottom', AViewBottomIdx, r.Bottom);
2002 end;
2003 end;
2004
TheTreenull2005 function TTestWordWrapPluginBase.TheTree: TSynLineMapAVLTree;
2006 begin
2007 Result := nil;
2008 if FWordWrap = nil then
2009 exit;
2010 Result := FWordWrap.FLineMapView.Tree;
2011 end;
2012
2013 procedure TTestWordWrapPluginBase.ReCreateEdit(ADispWidth: Integer);
2014 begin
2015 TearDown;
2016 SetUp;
2017 SetSynEditWidth(ADispWidth);
2018 end;
2019
2020 procedure TTestWordWrapPluginBase.SetUp;
2021 begin
2022 inherited SetUp;
2023 FWordWrap := TLazSynEditLineWrapPlugin.Create(SynEdit);
2024 end;
2025
2026 procedure TTestWordWrapPluginBase.TearDown;
2027 begin
2028 inherited TearDown;
2029 end;
2030
2031 { TTestWordWrapPlugin }
2032
2033 procedure TTestWordWrapPlugin.TestEditorWrap;
2034 var
2035 SkipTab, AllowPastEOL, KeepX: Boolean;
2036 begin
2037 SynEdit.Options := [];
2038 SynEdit.TabWidth := 4;
2039
2040 SetLines([
2041 'abc def ' + 'ABC DEFG ' + 'XYZ',
2042 //'A' #9'B' #9'C ' + 'DEF G'#9'H' #9 + '' #9 #9'xy',
2043 'A'#9'B'#9'C ' + 'DEF G'#9'H'#9 + #9#9'xy',
2044 'äää ööö ' + 'ÄÄÄ ÖÖÖ ' + 'ÜÜÜ',
2045 '999'
2046 ]);
2047
2048 SetSynEditWidth(10);
2049 CheckLines('', 0, [
2050 'abc def ',
2051 'ABC DEFG ',
2052 'XYZ',
2053 'A'#9'B'#9'C ',
2054 'DEF G'#9'H'#9,
2055 #9#9'xy',
2056 'äää ööö ',
2057 'ÄÄÄ ÖÖÖ ',
2058 'ÜÜÜ',
2059 '999'
2060 ], True);
2061
2062 CheckLines('', ViewedExp(0, [
2063 l(0, 0, 'abc def '),
2064 l(0, 1, 'ABC DEFG '),
2065 l(0, 2, 'XYZ'),
2066 l(1, 0, 'A'#9'B'#9'C '),
2067 l(1, 1, 'DEF G'#9'H'#9),
2068 l(1, 2, #9#9'xy'),
2069 l(2, 0, 'äää ööö '),
2070 l(2, 1, 'ÄÄÄ ÖÖÖ '),
2071 l(2, 2, 'ÜÜÜ'),
2072 l(3, 0, '999')
2073 ], tTrue));
2074
2075
2076 //CheckXyMap('', pt(4,3, 21,1, 21,1); // after "Z" EOL
2077 for AllowPastEOL in boolean do
2078 for KeepX in boolean do
2079 for SkipTab in boolean do begin
2080 if AllowPastEOL
2081 then SynEdit.Options := SynEdit.Options + [eoScrollPastEol]
2082 else SynEdit.Options := SynEdit.Options - [eoScrollPastEol];
2083 if SkipTab
2084 then SynEdit.Options2 := SynEdit.Options2 + [eoCaretSkipTab]
2085 else SynEdit.Options2 := SynEdit.Options2 - [eoCaretSkipTab];
2086 if KeepX
2087 then SynEdit.Options := SynEdit.Options + [eoKeepCaretX]
2088 else SynEdit.Options := SynEdit.Options - [eoKeepCaretX];
2089
2090 FWordWrap.CaretWrapPos := wcpEOL;
2091 CheckXyMap('wcpEOL', p( 1,1, 1,1, 1,1),
2092 [c(ecRight, 2,1, 2,1, 2,1,0),
2093 c(ecDown, 2,2, 10,1, 10,1,0),
2094 c([ecDown,ecDown], 2,3, 19,1, 19,1,0),
2095 c([ecDown,ecDown, ecRight], 3,3, 20,1, 20,1,0),
2096 c([ecDown,ecDown, ecRight,ecDown], 2,4, 2,2, 2,2,0, SkipTab),
2097 c([ecDown,ecDown, ecRight,ecDown], 3,4, 3,2, 2,2,1, not SkipTab),
2098 c([ecDown,ecDown,ecDown], 1,4, 1,2, 1,2,0, KeepX),
2099 c([ecDown,ecDown,ecDown], 2,4, 2,2, 2,2,0, not KeepX),
2100 c([ecDown,ecDown,ecDown,ecDown], 2,5, 12,2, 8,2,0)
2101 ]);
2102 CheckXyMap('wcpEOL', p( 2,1, 2,1, 2,1),
2103 [c(ecDown, 2,2, 10,1, 10,1,0)
2104 ]);
2105 CheckXyMap('wcpEOL', p( 7,1, 7,1, 7,1), // after "e"
2106 [c([ecColSelDown], 7,4, 7,2, 4,2,1, not SkipTab ), // A#9B#|9 // 1 in tab
2107 c([ecColSelDown], 6,4, 6,2, 4,2,0, SkipTab ), // A#9B|#9 // 1 in tab
2108 c([ecColSelDown,ecColSelDown], 7,7, 7,3, 12,3,0, (not SkipTab) or KeepX ),
2109 c([ecColSelDown,ecColSelDown], 6,7, 6,3, 10,3,0, SkipTab and not KeepX ),
2110 c([ecColSelDown,ecColSelDown,ecColSelDown], 4,10, 4,4, 4,4,0, not AllowPastEOL),
2111 c([ecColSelDown,ecColSelDown,ecColSelDown], 7,10, 7,4, 7,4,0, AllowPastEOL and ((not SkipTab) or KeepX) )
2112 ]);
2113 CheckXyMap('wcpEOL', p( 8,1, 8,1, 8,1));
2114 if AllowPastEOL then
2115 CheckXyMap('wcpEOL', p( 9,1, 1,2, 9,1, 9,1), // def |
2116 [c([ecDown], 9,2, 17,1, 17,1,0), // DEFG|
2117 c([ecDown,ecDown], 9,3, 26,1, 26,1,0), // XYZ |
2118 c([ecDown,ecDown,ecDown], 9,4, 9,2, 5,2,0), // A#9B#9|C
2119 c([ecDown,ecDown,ecDown,ecDown], 8,5, 18,2, 14,2,0, SkipTab), // G#9|H#9
2120 c([ecDown,ecDown,ecDown,ecDown], 9,5, 19,2, 14,2,1, not SkipTab), // G#9H#|9 //in #9
2121 c([ecDown,ecDown,ecDown,ecDown,ecDown], 9,6, 29,2, 17,2,0, (not SkipTab) or KeepX), // before x
2122 c([ecDown,ecDown,ecDown,ecDown,ecDown], 5,6, 25,2, 16,2,0, SkipTab and not KeepX), // in tab, before x
2123 c([ecDown,ecDown,ecDown,ecDown,ecDown,ecDown], 9,7, 9,3, 15,3,0, (not SkipTab) or KeepX),
2124 c([ecDown,ecDown,ecDown,ecDown,ecDown,ecDown], 5,7, 5,3, 8,3,0, SkipTab and not KeepX)
2125 ])
2126 else // not AllowPastEOL then
2127 CheckXyMap('wcpEOL', p( 9,1, 1,2, 9,1, 9,1), // after " " / still on line 1
2128 [c([ecDown], 9,2, 17,1, 17,1,0), // DEFG|
2129 c([ecDown,ecDown], 4,3, 21,1, 21,1,0), // XYZ|
2130 c([ecDown,ecDown,ecDown], 9,4, 9,2, 5,2,0, KeepX), // A#9B#9|C
2131 c([ecDown,ecDown,ecDown], 4,4, 4,2, 2,2,2, (not KeepX) and (not SkipTab) ), // A#|9B#9C //in tab
2132 c([ecDown,ecDown,ecDown], 2,4, 2,2, 2,2,0, (not KeepX) and SkipTab) // A#|9B#9C //in tab
2133 ]);
2134 CheckXyMap('wcpEOL', p( 2,2, 10,1, 10,1));
2135 CheckXyMap('wcpEOL', p( 4,2, 12,1, 12,1), // ABC| DE
2136 [c([ecColSelDown], 2,5, 12,2, 8,2,0), // D|EF G#9
2137 c([ecColSelDown,ecColSelDown], 4,8, 12,3, 21,3,0),
2138 c([ecColSelDown,ecColSelDown,ecColSelDown], 4,10, 4,4, 4,4,0, not AllowPastEOL)
2139 ]);
2140 CheckXyMap('wcpEOL', p( 9,2, 17,1, 17,1)); // after "G"
2141 CheckXyMap('wcpEOL', p(10,2, 1,3, 18,1, 18,1)); // after " "
2142 CheckXyMap('wcpEOL', p( 2,3, 19,1, 19,1)); // after "X"
2143 CheckXyMap('wcpEOL', p( 4,3, 21,1, 21,1)); // after "Z" EOL
2144 if AllowPastEOL then
2145 CheckXyMap('wcpEOL', p( 5,3, 22,1, 22,1)); // at EOL + 1
2146
2147 CheckXyMap('wcpEOL', p( 1,4, 1,2, 1,2),
2148 [c([ecDown], 2,5, 12,2, 8,2,0),
2149 c([ecDown,ecDown], 5,6, 25,2, 16,2,0, SkipTab),
2150 c([ecDown,ecDown], 2,6, 22,2, 15,2,1, not SkipTab),
2151 c([ecDown,ecDown,ecDown], 1,7, 1,3, 1,3,0, KeepX),
2152 c([ecDown,ecDown,ecDown], 5,7, 5,3, 8,3,0, (SkipTab) and not KeepX)
2153 ]);
2154 CheckXyMap('wcpEOL', p( 2,4, 2,2, 2,2, 0)); // before tab
2155 if not SkipTab then
2156 CheckXyMap('wcpEOL', p( 3,4, 3,2, 2,2, 1)); // 1 inside tab
2157 CheckXyMap('wcpEOL', p( 5,4, 5,2, 3,2, 0)); // after tab
2158 CheckXyMap('wcpEOL', p( 9,4, 9,2, 5,2)); // after tab, before "C"
2159 CheckXyMap('wcpEOL', p(10,4, 10,2, 6,2)); // after "C"
2160 CheckXyMap('wcpEOL', p(11,4, 1,5, 11,2, 7,2)); // after " "
2161 CheckXyMap('wcpEOL', p( 2,5, 12,2, 8,2)); // after "D"
2162 CheckXyMap('wcpEOL', p( 8,5, 18,2, 14,2)); // after "H"
2163 if not SkipTab then
2164 CheckXyMap('wcpEOL', p( 9,5, 19,2, 14,2, 1)); // in #9
2165 if not SkipTab then
2166 CheckXyMap('wcpEOL', p(10,5, 20,2, 14,2, 2)); // in #9
2167 CheckXyMap('wcpEOL', p(11,5, 1,6, 21,2, 15,2)); // after #9
2168 if not SkipTab then
2169 CheckXyMap('wcpEOL', p( 2,6, 22,2, 15,2, 1)); // in #9 / next line
2170 CheckXyMap('wcpEOL', p( 5,6, 25,2, 16,2)); // after 1st #9 / next line
2171 CheckXyMap('wcpEOL', p(10,6, 30,2, 18,2)); // after "x"
2172 CheckXyMap('wcpEOL', p(11,6, 31,2, 19,2)); // after "z" EOL
2173
2174 CheckXyMap('wcpEOL', p( 1,7, 1,3, 1,3));
2175 CheckXyMap('wcpEOL', p( 2,7, 2,3, 3,3));
2176 CheckXyMap('wcpEOL', p( 8,7, 8,3, 14,3)); // after "ö"
2177 CheckXyMap('wcpEOL', p( 9,7, 1,8, 9,3, 15,3)); // after " "
2178 CheckXyMap('wcpEOL', p( 2,8, 10,3, 17,3)); // after "Ä"
2179 CheckXyMap('wcpEOL', p( 9,8, 1,9, 17,3, 29,3)); // after " "
2180
2181 FWordWrap.CaretWrapPos := wcpBOL;
2182 CheckXyMap('wcpBOL', p( 1,1, 1,1, 1,1),
2183 [c(ecRight, 2,1, 2,1, 2,1,0),
2184 c(ecDown, 1,2, 9,1, 9,1,0),
2185 c([ecDown,ecDown], 1,3, 18,1, 18,1,0),
2186 c([ecDown,ecDown, ecRight,ecRight], 3,3, 20,1, 20,1,0),
2187 c([ecDown,ecDown, ecRight,ecRight,ecDown], 2,4, 2,2, 2,2,0, SkipTab),
2188 c([ecDown,ecDown, ecRight,ecRight,ecDown], 3,4, 3,2, 2,2,1, not SkipTab),
2189 c([ecDown,ecDown,ecDown], 1,4, 1,2, 1,2,0),
2190 c([ecDown,ecDown,ecDown,ecDown], 1,5, 11,2, 7,2,0)
2191 ]);
2192 CheckXyMap('wcpBOL', p( 2,1, 2,1, 2,1),
2193 [c(ecDown, 2,2, 10,1, 10,1,0)
2194 ]);
2195 CheckXyMap('wcpBOL', p( 7,1, 7,1, 7,1)); // after "e"
2196 CheckXyMap('wcpBOL', p( 8,1, 8,1, 8,1));
2197 CheckXyMap('wcpBOL', p( 1,2, 9,1, 9,1, 9,1)); // after " " / still on line 1
2198 CheckXyMap('wcpBOL', p( 2,2, 10,1, 10,1));
2199 CheckXyMap('wcpBOL', p( 9,2, 17,1, 17,1), // after "G"
2200 [c([ecUp], 8,1, 8,1, 8,1,0),
2201 c([ecUp, ecDown], 9,2, 17,1, 17,1,0, KeepX),
2202 c([ecUp, ecDown], 8,2, 16,1, 16,1,0, not KeepX)
2203 ]);
2204 CheckXyMap('wcpBOL', p( 1,3, 10,2, 18,1, 18,1)); // after " "
2205 CheckXyMap('wcpBOL', p( 2,3, 19,1, 19,1)); // after "X"
2206 CheckXyMap('wcpBOL', p( 4,3, 21,1, 21,1)); // after "Z" EOL
2207 if AllowPastEOL then
2208 CheckXyMap('wcpBOL', p( 5,3, 22,1, 22,1)); // at EOL + 1
2209
2210 CheckXyMap('wcpBOL', p( 1,4, 1,2, 1,2),
2211 [c([ecDown], 1,5, 11,2, 7,2,0),
2212 c([ecDown,ecDown], 1,6, 21,2, 15,2,0),
2213 c([ecDown,ecDown,ecDown], 1,7, 1,3, 1,3,0)
2214 ]);
2215 CheckXyMap('wcpBOL', p( 2,4, 2,2, 2,2, 0)); // before tab
2216 if not SkipTab then
2217 CheckXyMap('wcpBOL', p( 3,4, 3,2, 2,2, 1)); // 1 inside tab
2218 CheckXyMap('wcpBOL', p( 5,4, 5,2, 3,2, 0)); // after tab
2219 CheckXyMap('wcpBOL', p( 9,4, 9,2, 5,2)); // after tab, before "C"
2220 CheckXyMap('wcpBOL', p(10,4, 10,2, 6,2), // after "C"
2221 [c([ecDown], 8,5, 18,2, 14,2,0, SkipTab), // G#9H#|9
2222 c([ecDown], 10,5, 20,2, 14,2,2, not SkipTab), // G#9H#|9
2223 c([ecDown,ecDown], 10,6, 30,2, 18,2,0, KeepX), // #9#9X|Y
2224 c([ecDown,ecDown], 5,6, 25,2, 16,2,0, (not KeepX) and SkipTab) // #9#9X|Y
2225 ]);
2226 CheckXyMap('wcpBOL', p( 1,5, 11,4, 11,2, 7,2)); // after " "
2227 CheckXyMap('wcpBOL', p( 2,5, 12,2, 8,2)); // after "D"
2228 CheckXyMap('wcpBOL', p( 8,5, 18,2, 14,2)); // after "H"
2229 if not SkipTab then
2230 CheckXyMap('wcpBOL', p( 9,5, 19,2, 14,2, 1)); // in #9
2231 if not SkipTab then
2232 CheckXyMap('wcpBOL', p(10,5, 20,2, 14,2, 2)); // in #9
2233 CheckXyMap('wcpBOL', p( 1,6, 11,5, 21,2, 15,2)); // after #9
2234 if not SkipTab then
2235 CheckXyMap('wcpBOL', p( 2,6, 22,2, 15,2, 1)); // in #9 / next line
2236 CheckXyMap('wcpBOL', p( 5,6, 25,2, 16,2)); // after 1st #9 / next line
2237 CheckXyMap('wcpBOL', p(10,6, 30,2, 18,2)); // after "x"
2238 CheckXyMap('wcpBOL', p(11,6, 31,2, 19,2), // after "y" EOL
2239 [c([ecUp], 8,5, 18,2, 14,2,0, SkipTab), // G#9H#|9
2240 c([ecUp], 10,5, 20,2, 14,2,2, not SkipTab), // G#9H#|9
2241 c([ecUp,ecDown], 11,6, 31,2, 19,2,0, KeepX)
2242 ]);
2243
2244 CheckXyMap('wcpBOL', p( 1,7, 1,3, 1,3));
2245 CheckXyMap('wcpBOL', p( 2,7, 2,3, 3,3));
2246 CheckXyMap('wcpBOL', p( 8,7, 8,3, 14,3)); // after "ö"
2247 CheckXyMap('wcpBOL', p( 1,8, 9,7, 9,3, 15,3)); // after " "
2248 CheckXyMap('wcpBOL', p( 2,8, 10,3, 17,3)); // after "Ä"
2249 CheckXyMap('wcpBOL', p( 1,9, 9,8, 17,3, 29,3)); // after " "
2250
2251 end;
2252
2253
2254 CheckLineIndexMapping('LineMap 0', 0, 0, 2);
2255 CheckLineIndexMapping('LineMap 1', 1, 3, 5);
2256 CheckLineIndexMapping('LineMap 2', 2, 6, 8);
2257 CheckLineIndexMapping('LineMap 3', 3, 9, 9);
2258
2259
2260 SynEdit.ExecuteCommand(ecEditorBottom, '', nil);
2261 AssertEquals('ecEditorBottom', 4 ,SynEdit.CaretY);
2262 AssertEquals('ecEditorBottom', 10 ,SynEdit.CaretObj.ViewedLineCharPos.y);
2263
2264 SetSynEditWidth(65);
2265 AddLines(0, 6000, 60, 'A');
2266
2267 CheckLine('', 0, 'A_0_0');
2268 CheckLine('', 1, 'A_1_0');
2269 CheckLine('', 2, 'A_2_0');
2270 CheckLine('', 3, 'A_3_0');
2271
2272 SetSynEditWidth(35);
2273 CheckLine('', 0, 'A_0_0');
2274 CheckLine('', 1, 'A_0_3');
2275 CheckLine('', 2, 'A_1_0');
2276
2277 // ' '
2278
2279 end;
2280
2281 procedure TTestWordWrapPlugin.TestWrapSplitJoin;
2282 procedure AddLineTestCount(AName: String; ALineIdx, ACount, ALen: Integer; AExpCount: Integer);
2283 begin
2284 AddLines(ALineIdx, ACount, ALen, 'A');
2285 AssertEquals(Format('%s : After ins %d Line(s) at %d Len %d', [AName, ACount, ALineIdx, ALen]), AExpCount, TreeNodeCount);
2286 end;
2287 procedure AddLineTestCount(AName: String; ALineIdx, ALen: Integer; AExpCount: Integer);
2288 begin
2289 AddLineTestCount(AName, ALineIdx, 1, ALen, AExpCount);
2290 end;
2291
2292 procedure ChangeLineTestCount(AName: String; ALineIdx, ALen: Integer; AExpCount: Integer);
2293 begin
2294 AddLines(ALineIdx, 1, ALen, 'A', False, True);
2295 AssertEquals(Format('%s : After ins %d Line(s) at %d Len %d', [AName, 1, ALineIdx, ALen]), AExpCount, TreeNodeCount);
2296 end;
2297 procedure ChangeLineTestCount(AName: String; ALineIdx, ANodeIdx, ALen: Integer; AExpCount: Integer);
2298 var
2299 n: TSynEditLineMapPageHolder;
2300 begin
2301 n := TreeNodeHolder[ANodeIdx];
2302 if ALineIdx >= 0
2303 then AddLines(n.RealStartLine + ALineIdx, 1, ALen, 'A', False, True)
2304 else AddLines(n.RealEndLine + 1 + ALineIdx, 1, ALen, 'A', False, True);
2305 AssertEquals(Format('%s : After ins %d Line(s) at %d Len %d', [AName, 1, ALineIdx, ALen]), AExpCount, TreeNodeCount);
2306 end;
2307
2308 procedure DelLineTestCount(AName: String; ALineIdx: Integer; AExpCount: Integer);
2309 begin
2310 if ALineIdx < 0
2311 then SynEdit.Lines.Delete(SynEdit.Lines.Count + 1 + ALineIdx)
2312 else SynEdit.Lines.Delete(ALineIdx);
2313 AssertEquals(Format('%s : After DEL Line at %d Len %d', [AName, ALineIdx]), AExpCount, TreeNodeCount);
2314 end;
2315 procedure DelLineTestCount(AName: String; ANodeIdx, ALineIdx: Integer; AExpCount: Integer);
2316 var
2317 n: TSynEditLineMapPageHolder;
2318 begin
2319 n := TreeNodeHolder[ANodeIdx];
2320 if ALineIdx < 0
2321 then SynEdit.Lines.Delete(n.RealEndLine + 1 + ALineIdx)
2322 else SynEdit.Lines.Delete(n.RealStartLine + ALineIdx);
2323 AssertEquals(Format('%s : After DEL Line at %d Len %d', [AName, ALineIdx]), AExpCount, TreeNodeCount);
2324 end;
2325
2326 var
2327 t: TSynLineMapAVLTree;
2328 n1, n2: TSynEditLineMapPageHolder;
2329 i: Integer;
2330 begin
2331 ReCreateEdit(10);
2332 SynEdit.Options := [];
2333 t := FWordWrap.FLineMapView.Tree;
2334 debugln(' split %d join %d dist %d', [t.PageSplitSize, t.PageJoinSize, t.PageJoinDistance]);
2335
2336 AddLineTestCount('new: split - 2', 0, t.PageSplitSize - 2, 18, 1);
2337 AddLineTestCount('insert start: split - 1', 0, 18, 1);
2338 AddLineTestCount('insert start: split - 0', 0, 18, 1);
2339 AddLineTestCount('insert start: split + 1', 0, 18, 2);
2340 AddLineTestCount('insert start: split + 2', 0, 18, 2);
2341
2342
2343 ReCreateEdit(10);
2344 SynEdit.Options := [];
2345 t := FWordWrap.FLineMapView.Tree;
2346 AddLineTestCount('new: split - 2', 0, t.PageSplitSize - 2, 18, 1);
2347 AddLineTestCount('insert end: split - 1', SynEdit.Lines.Count, 18, 1);
2348 AddLineTestCount('insert end: split - 0', SynEdit.Lines.Count, 18, 1);
2349 AddLineTestCount('insert end: split + 1', SynEdit.Lines.Count, 18, 2);
2350 AddLineTestCount('insert end: split + 2', SynEdit.Lines.Count, 18, 2);
2351
2352
2353 ReCreateEdit(10);
2354 SynEdit.Options := [];
2355 t := FWordWrap.FLineMapView.Tree;
2356 AddLineTestCount('new: split - 2', 0, t.PageSplitSize - 2, 18, 1);
2357 AddLineTestCount('insert @10: split - 1', 10, 18, 1);
2358 AddLineTestCount('insert @10: split - 0', 10, 18, 1);
2359 AddLineTestCount('insert @10: split + 1', 10, 18, 2);
2360 AddLineTestCount('insert @10: split + 2', 10, 18, 2);
2361
2362
2363 ReCreateEdit(10);
2364 SynEdit.Options := [];
2365 t := FWordWrap.FLineMapView.Tree;
2366 i := t.PageSplitSize - 2;
2367 AddLineTestCount('new: split - 2', 0, i, 18, 1);
2368 AddLineTestCount('new: split - 2', i, 10, 1, 1);
2369 ChangeLineTestCount('update end: split - 1', i, 18, 1); inc(i);
2370 ChangeLineTestCount('update end: split - 0', i, 18, 1); inc(i);
2371 ChangeLineTestCount('update end: split + 1', i, 18, 2); inc(i);
2372 ChangeLineTestCount('update end: split + 2', i, 18, 2); inc(i);
2373
2374
2375 ReCreateEdit(10);
2376 SynEdit.Options := [];
2377 t := FWordWrap.FLineMapView.Tree;
2378 i := 9;
2379 AddLineTestCount('new: split - 2', 0, t.PageSplitSize - 2, 18, 1);
2380 AddLineTestCount('new: split - 2', 0, 10, 1, 1);
2381 ChangeLineTestCount('update start: split - 1', i, 18, 1); dec(i);
2382 ChangeLineTestCount('update start: split - 0', i, 18, 1); dec(i);
2383 ChangeLineTestCount('update start: split + 1', i, 18, 2); dec(i);
2384 ChangeLineTestCount('update start: split + 2', i, 18, 2); dec(i);
2385
2386
2387
2388 ///////////////////
2389 ReCreateEdit(10);
2390 SynEdit.Options := [];
2391 t := FWordWrap.FLineMapView.Tree;
2392 AddLineTestCount('new: split double', 0, t.PageSplitSize + t.PageJoinSize + 2, 18, 2);
2393
2394
2395 n1 := TreeNodeHolder[0];
2396 while n1.RealCount > t.PageJoinSize + 1 do
2397 SynEdit.Lines.Delete(n1.RealStartLine + 2);
2398 AssertEquals('insert start: split + 2', 2, TreeNodeCount);
2399
2400 n2 := TreeNodeHolder[1];
2401 while n2.RealCount > t.PageJoinSize + 1 do
2402 SynEdit.Lines.Delete(n2.RealStartLine + 2);
2403 AssertEquals('insert start: split + 2', 2, TreeNodeCount);
2404
2405 SynEdit.Lines.Delete(1);
2406 SynEdit.Lines.Delete(SynEdit.Lines.Count - 2);
2407
2408 AssertEquals('insert start: split + 2', 1, TreeNodeCount);
2409
2410
2411
2412
2413
2414 ReCreateEdit(10);
2415 SynEdit.Options := [];
2416 t := FWordWrap.FLineMapView.Tree;
2417 AddLineTestCount('new: split double', 0, t.PageSplitSize + t.PageJoinSize + 2, 18, 2);
2418
2419
2420 n1 := TreeNodeHolder[0];
2421 while n1.RealCount > t.PageJoinSize + 1 do
2422 ChangeLineTestCount('edit n1 start', 0,0, 1, 2);
2423 n2 := TreeNodeHolder[1];
2424 while n2.RealCount > t.PageJoinSize + 1 do
2425 ChangeLineTestCount('edit n2 end', -1,1, 1, 2);
2426
2427 ChangeLineTestCount('edit n1 start', 0,0, 1, 2);
2428 ChangeLineTestCount('edit n2 end ', -1,1, 1, 1);
2429
2430
2431
2432
2433 // do not join
2434 ReCreateEdit(10);
2435 SynEdit.Options := [];
2436 t := FWordWrap.FLineMapView.Tree;
2437 AddLineTestCount('new: split * 2 - 2', 0, t.PageSplitSize * 2 - 2, 18, 2);
2438
2439
2440 n1 := TreeNodeHolder[0];
2441 while n1.RealCount > t.PageJoinSize + 1 do
2442 ChangeLineTestCount('edit n1 end', -1,0, 1, 2);
2443 n2 := TreeNodeHolder[1];
2444 while n2.RealCount > t.PageJoinSize + 1 do
2445 ChangeLineTestCount('edit n2 start', 0,1, 1, 2);
2446
2447 ChangeLineTestCount('edit n1 start', -1,0, 1, 2);
2448 ChangeLineTestCount('edit n2 end ', 0,1, 1, 2);
2449 ChangeLineTestCount('edit n1 start', -1,0, 1, 2);
2450 ChangeLineTestCount('edit n2 end ', 0,1, 1, 2);
2451 ChangeLineTestCount('edit n1 start', -1,0, 1, 2);
2452 ChangeLineTestCount('edit n2 end ', 0,1, 1, 2);
2453
2454
2455
2456 end;
2457
2458 procedure TTestWordWrapPlugin.TestEditorEdit;
2459 begin
2460 SynEdit.Options := [];
2461 SynEdit.TabWidth := 4;
2462
2463 SetLines([
2464 'abc def ' + 'ABC DEFG ' + 'XYZ',
2465 '',
2466 //'A' #9'B' #9'C ' + 'DEF G'#9'H' #9 + '' #9 #9'xy',
2467 'A'#9'B'#9'C ' + 'DEF G'#9'H'#9 + #9#9'xy',
2468 '',
2469 'äää ööö ' + 'ÄÄÄ ÖÖÖ ' + 'ÜÜÜ',
2470 '',
2471 '999'
2472 ]);
2473 SetSynEditWidth(10);
2474
2475 SetSynEditWidth(10);
2476 CheckLines('', 0, [
2477 'abc def ',
2478 'ABC DEFG ',
2479 'XYZ',
2480 '',
2481 'A'#9'B'#9'C ',
2482 'DEF G'#9'H'#9,
2483 #9#9'xy',
2484 '',
2485 'äää ööö ',
2486 'ÄÄÄ ÖÖÖ ',
2487 'ÜÜÜ',
2488 '',
2489 '999'
2490 ], True);
2491
2492 SynEdit.BeginUpdate;
2493 SynEdit.TestTypeText(1,7, '4 ');
2494 SynEdit.TestTypeText(1,3, '2 ');
2495 SynEdit.TestTypeText(1,5, '3 ');
2496 SynEdit.TestTypeText(1,1, '1 ');
2497 SynEdit.EndUpdate;
2498
2499 CheckLines('', 0, [
2500 '1 abc def ',
2501 'ABC DEFG ',
2502 'XYZ',
2503 '',
2504 '2 A'#9'B'#9'C ',
2505 'DEF G'#9'H'#9,
2506 #9#9'xy',
2507 '',
2508 '3 äää ööö ',
2509 'ÄÄÄ ÖÖÖ ',
2510 'ÜÜÜ',
2511 '',
2512 '4 999'
2513 ], True);
2514 end;
2515
2516 initialization
2517
2518 RegisterTest(TTestWordWrap);
2519 RegisterTest(TTestWordWrapPlugin);
2520 end.
2521
2522