1 unit TestSynSharedEdits;
2 (* Test multiply edits sharing a textbuffer
3 *)
4 
5 {$mode objfpc}{$H+}
6 
7 interface
8 
9 uses
10   Classes, SysUtils, testregistry, TestBase, LCLProc,
11   SynEdit, SynEditMarks, SynEditKeyCmds;
12 
13 type
14 
15   { TTestSynSharedEdits }
16 
17   TTestSynSharedEdits = class(TTestBase)
18   private
19     FSynEdits : Array of TTestSynEdit;
20   protected
21     //procedure SetUp; override;
22     procedure TearDown; override;
23     procedure ReCreateEdit; reintroduce; overload;
24     procedure ReCreateEdit(Count: Integer); overload;
25     procedure SelectEdit(Index: Integer);
TestText1null26     function TestText1: TStringArray;
27     procedure CheckMarks(Name: String; EditIndex: Integer; MarkList: Array of Integer);
AddMarknull28     function AddMark(EditIndex: Integer; Y, X: Integer) : TSynEditMark;
29   published
30     procedure TestMarks;
31     procedure TestBookMarks;
32   end;
33 
34 implementation
35 
36 
37 { TTestSynSharedEdits }
38 
39 procedure TTestSynSharedEdits.TearDown;
40 var
41   i: Integer;
42 begin
43   FSynEdit := nil;
44   for i := 0 to length(FSynEdits) - 1 do
45     FSynEdits[i].Free;
46   SetLength(FSynEdits, 0);
47   inherited TearDown;
48 end;
49 
50 procedure TTestSynSharedEdits.ReCreateEdit;
51 begin
52   ReCreateEdit(1);
53 end;
54 
55 procedure TTestSynSharedEdits.ReCreateEdit(Count: Integer);
56 var
57   i: Integer;
58 begin
59   for i := 0 to length(FSynEdits) - 1 do
60     FSynEdits[i].Free;
61 
62   SetLength(FSynEdits, Count);
63   Form.Height := 600;
64   Form.Width :=  500;
65   for i := 0 to Count - 1 do begin
66     FSynEdits[i] := TTestSynEdit.Create(Form);
67     FSynEdits[i].Parent := Form;
68     FSynEdits[i].Top := 200 * i;
69     FSynEdits[i].Left := 0;
70     FSynEdits[i].Width:= 500;
71     FSynEdits[i].Height := 200; // FSynEdits[i].Font.Height * 20 + 2;
72   end;
73 
74   FSynEdit := FSynEdits[0];
75 end;
76 
77 procedure TTestSynSharedEdits.SelectEdit(Index: Integer);
78 begin
79   FSynEdit := FSynEdits[Index];
80 end;
81 
TestText1null82 function TTestSynSharedEdits.TestText1: TStringArray;
83 begin
84   SetLength(Result, 8);
85   Result[0] := 'abc';
86   Result[1] := 'def 123;';
87   Result[2] := 'xyz 098765';
88   Result[3] := '';
89   Result[4] := '1';
90   Result[5] := '    mno...';
91   Result[6] := '  ABCDEF';
92   Result[7] := '  321';
93 end;
94 
95 procedure TTestSynSharedEdits.CheckMarks(Name: String; EditIndex: Integer;
96   MarkList: array of Integer);
97 var
98   m: TSynEditMark;
99   i: Integer;
100 begin
101   //for i := 0 to FSynEdits[EditIndex].Marks.Count - 1 do begin
102   //  m := FSynEdits[EditIndex].Marks[i];
103   //  debugln(['Mark ',i,' Line=',m.Line,' Col=',m.Column]);
104   //end;
105   AssertEquals(Name + ' Edit: '+IntToStr(EditIndex)+' Count: ', length(MarkList) div 2, FSynEdits[EditIndex].Marks.Count);
106 
107   for i := 0 to (length(MarkList) div 2) - 1 do begin
108     m := FSynEdits[EditIndex].Marks[i];
109     AssertEquals(Name + ' Edit: '+IntToStr(EditIndex)+' Mark( '+IntToStr(i)+' ) Line: ',    MarkList[i*2+0], m.Line);
110     AssertEquals(Name + ' Edit: '+IntToStr(EditIndex)+' Mark( '+IntToStr(i)+' ) Column: ',  MarkList[i*2+1], m.Column);
111     AssertEquals(Name + ' Edit: '+IntToStr(EditIndex)+' Mark( '+IntToStr(i)+' ) IndexOf: ', i,               FSynEdits[EditIndex].Marks.IndexOf(m));
112   end;
113 end;
114 
AddMarknull115 function TTestSynSharedEdits.AddMark(EditIndex: Integer; Y, X: Integer): TSynEditMark;
116 begin
117   Result := TSynEditMark.Create(SynEdit);
118   Result.Line := Y;
119   Result.Column := X;
120   FSynEdits[EditIndex].Marks.Add(Result);
121 end;
122 
123 procedure TTestSynSharedEdits.TestMarks;
124 begin
125   {%region share with marks}
126   ReCreateEdit(2);
127   PushBaseName('share with marks');
128 
129   // default setting is shared marklist
130   FSynEdits[1].ShareOptions := [eosShareMarks];
131   FSynEdits[1].ShareTextBufferFrom(FSynEdits[0]);
132   SetLines(TestText1);
133 
134   CheckMarks('empty', 0, []);
135   CheckMarks('empty', 1, []);
136 
137   AddMark(0, 3,2);
138   CheckMarks('3/2', 0, [ 3,2 ]);
139   CheckMarks('3/2', 1, [ 3,2 ]);
140 
141   AddMark(1, 5,2);
142   CheckMarks('3/2 - 5/1', 0, [ 3,2,   5,2 ]);
143   CheckMarks('3/2 - 5/1', 1, [ 3,2,   5,2 ]);
144 
145   // move both
146   FSynEdits[0].CaretXY := Point(1, 4);
147   FSynEdits[0].CommandProcessor(ecLineBreak, ' ', nil);
148   CheckMarks('newline 3/2 - 6/1', 0, [ 3,2,   6,2 ]);
149   CheckMarks('newline 3/2 - 6/1', 1, [ 3,2,   6,2 ]);
150 
151   // unshare again
152   FSynEdits[1].UnShareTextBuffer;
153   CheckMarks('unshared 3/2 - 5/1', 0, [ 3,2,   6,2 ]);
154   CheckMarks('unshared 3/2 - 5/1', 1, []); // empty has no marks of it's own
155 
156   //move 1
157   FSynEdits[0].CaretXY := Point(1, 4); // after mark
158   FSynEdits[0].CommandProcessor(ecLineBreak, ' ', nil);
159 
160   CheckMarks('unshared newline 3/2 - 5/1', 0, [ 3,2,   7,2 ]);
161   CheckMarks('unshared newline 3/2 - 5/1', 1, []); // empty has no marks of it's own
162 
163   AddMark(0, 7,1);
164   CheckMarks('unshared add(0)', 0, [ 3,2,   7,2,   7,1 ]);
165   CheckMarks('unshared add(0)', 1, []);
166 
167 
168   AddMark(1, 3,1);
169   CheckMarks('unshared add(1)', 0, [ 3,2,   7,2,   7,1 ]);
170   CheckMarks('unshared add(1)', 1, [ 3,1]);
171 
172   // delete mark from edit 0
173   FSynEdits[0].Marks[0].Free;
174   CheckMarks('unshared del(0)', 0, [7,2,   7,1 ]);
175   CheckMarks('unshared del(0)', 1, [ 3,1 ]);
176 
177   {%endregion}
178 
179   {%region share with marks / destroy 1st}
180   ReCreateEdit(2);
181   PopPushBaseName('share with marks / destroy 2nd');
182   // default setting is shared marklist
183   FSynEdits[1].ShareOptions := [eosShareMarks];
184   FSynEdits[1].ShareTextBufferFrom(FSynEdits[0]);
185   SetLines(TestText1);
186 
187   // create setup
188   AddMark(0, 3,2);
189   AddMark(1, 5,2);
190   CheckMarks('3/2 - 5/1', 0, [ 3,2,   5,2 ]);
191   CheckMarks('3/2 - 5/1', 1, [ 3,2,   5,2 ]);
192 
193   FreeAndNil(FSynEdits[0]);
194   CheckMarks('other destroyed', 1, [ 3,2,   5,2 ]);
195 
196   //move 1
197   FSynEdits[1].CaretXY := Point(1, 4); // after mark
198   FSynEdits[1].CommandProcessor(ecLineBreak, ' ', nil);
199 
200   CheckMarks('unshared newline 3/2 - 5/1', 1, [ 3,2,   6,2 ]);
201   {%endregion}
202 
203   {%region share with marks / destroy 2nd}
204   ReCreateEdit(2);
205   PopPushBaseName('share with marks / destroy 2nd');
206   // default setting is shared marklist
207   FSynEdits[1].ShareOptions := [eosShareMarks];
208   FSynEdits[1].ShareTextBufferFrom(FSynEdits[0]);
209   SetLines(TestText1);
210 
211   // create setup
212   AddMark(0, 3,2);
213   AddMark(1, 5,2);
214   CheckMarks('3/2 - 5/1', 0, [ 3,2,   5,2 ]);
215   CheckMarks('3/2 - 5/1', 1, [ 3,2,   5,2 ]);
216 
217   FreeAndNil(FSynEdits[1]);
218   CheckMarks('other destroyed', 0, [ 3,2,   5,2 ]);
219 
220   //move 1
221   FSynEdits[0].CaretXY := Point(1, 4); // after mark
222   FSynEdits[0].CommandProcessor(ecLineBreak, ' ', nil);
223 
224   CheckMarks('unshared newline 3/2 - 5/1', 0, [ 3,2,   6,2 ]);
225   {%endregion}
226 
227   {%region share, but separate marks}
228   ReCreateEdit(2);
229   PopPushBaseName('share, but separate marks');
230 
231   // setting is not shared marklist
232   FSynEdits[1].ShareOptions := [];
233   FSynEdits[1].ShareTextBufferFrom(FSynEdits[0]);
234   SetLines(TestText1);
235 
236   CheckMarks('empty', 0, []);
237   CheckMarks('empty', 1, []);
238 
239   AddMark(0, 3,2);
240   CheckMarks('3/2', 0, [ 3,2 ]);
241   CheckMarks('3/2', 1, []);
242 
243   AddMark(1, 5,2);
244   CheckMarks('3/2 - 5/1', 0, [ 3,2 ]);
245   CheckMarks('3/2 - 5/1', 1, [ 5,2 ]);
246 
247   // unshare again
248   FSynEdits[1].UnShareTextBuffer;
249   CheckMarks('unshared', 0, [ 3,2 ]);
250   CheckMarks('unshared', 1, [ ]); // empty, even though it had own marks,. they were related to the text it has no longer
251 
252   FSynEdits[0].CaretXY := Point(1, 1);
253   FSynEdits[0].CommandProcessor(ecLineBreak, ' ', nil);
254   CheckMarks('unshared newline', 0, [ 4,2 ]);
255   CheckMarks('unshared newline', 1, [ ]); // empty, even though it had own marks,. they were related to the text it has no longer
256 
257   // delete mark from edit 0
258   FSynEdits[0].Marks[0].Free;
259   CheckMarks('unshared del', 0, [ ]);
260   CheckMarks('unshared del', 1, [ ]);
261   {%endregion}
262 
263   {%region share but separate marks / destroy 1st}
264   ReCreateEdit(2);
265   PopPushBaseName('share with marks / destroy 2nd');
266   // default setting is shared marklist
267   FSynEdits[1].ShareOptions := [];
268   FSynEdits[1].ShareTextBufferFrom(FSynEdits[0]);
269   SetLines(TestText1);
270 
271   // create setup
272   AddMark(0, 3,2);
273   AddMark(1, 5,2);
274   CheckMarks('3/2 - 5/1', 0, [ 3,2 ]);
275   CheckMarks('3/2 - 5/1', 1, [ 5,2 ]);
276 
277   FreeAndNil(FSynEdits[0]);
278   CheckMarks('other destroyed', 1, [ 5,2 ]);
279 
280   //move 1
281   FSynEdits[1].CaretXY := Point(1, 1);
282   FSynEdits[1].CommandProcessor(ecLineBreak, ' ', nil);
283 
284   CheckMarks('unshared newline 3/2 - 5/1', 1, [ 6,2 ]);
285   {%endregion}
286 
287   {%region share, but separate marks / destroy 2nd}
288   ReCreateEdit(2);
289   PopPushBaseName('share with marks / destroy 2nd');
290   // default setting is shared marklist
291   FSynEdits[1].ShareOptions := [];
292   FSynEdits[1].ShareTextBufferFrom(FSynEdits[0]);
293   SetLines(TestText1);
294 
295   // create setup
296   AddMark(0, 3,2);
297   AddMark(1, 5,2);
298   CheckMarks('3/2 - 5/1', 0, [ 3,2 ]);
299   CheckMarks('3/2 - 5/1', 1, [ 5,2 ]);
300 
301   FreeAndNil(FSynEdits[1]);
302   CheckMarks('other destroyed', 0, [ 3,2 ]);
303 
304   //move 1
305   FSynEdits[0].CaretXY := Point(1, 1);
306   FSynEdits[0].CommandProcessor(ecLineBreak, ' ', nil);
307 
308   CheckMarks('unshared newline 3/2 - 5/1', 0, [ 4,2 ]);
309   {%endregion}
310 
311 end;
312 
313 procedure TTestSynSharedEdits.TestBookMarks;
314   procedure CheckBMarks(Name: String; EditIndex: Integer; MarkList: Array of Integer);
315   var
316     m: TSynEditMark;
317     l: Array of Integer;
318     i, j: Integer;
319   begin
320     Name := BaseTestName + Name;
321     AssertEquals(Name + ' Edit: '+IntToStr(EditIndex)+' Count: ', length(MarkList) div 2, FSynEdits[EditIndex].Marks.Count);
322 
323     SetLength(l, FSynEdits[EditIndex].Lines.Count);
324     for i := 0 to length(l)-1 do
325       l[i] := 0;
326 
327     for i := 0 to (length(MarkList) div 2) - 1 do begin
328       l[MarkList[i*2+0]] := l[MarkList[i*2+0]] + 1;
329       m := FSynEdits[EditIndex].Marks[i];
330       AssertEquals(Name + ' Edit: '+IntToStr(EditIndex)+' Mark( '+IntToStr(i)+' ) Line: ',    MarkList[i*2+0], m.Line);
331       AssertEquals(Name + ' Edit: '+IntToStr(EditIndex)+' Mark( '+IntToStr(i)+' ) BookNum: ',  MarkList[i*2+1], m.BookmarkNumber);
332       AssertEquals(Name + ' Edit: '+IntToStr(EditIndex)+' Mark( '+IntToStr(i)+' ) IndexOf: ', i,               FSynEdits[EditIndex].Marks.IndexOf(m));
333     end;
334 
335     for i := 0 to length(l)-1 do begin
336       j := 0;
337       if FSynEdits[EditIndex].Marks.Line[i] <> nil then
338         j := FSynEdits[EditIndex].Marks.Line[i].Count;
339       AssertEquals(Name + ' Edit: '+IntToStr(EditIndex)+' Count in line '+IntToStr(i)+': ',    l[i], j);
340     end;
341 
342   end;
343 
344 var
345   e1, e2, e3: Integer;
346 begin
347   {%region edits already shared }
348   ReCreateEdit(2);
349   // default setting is shared marklist
350   FSynEdits[1].ShareOptions := [eosShareMarks];
351   FSynEdits[1].ShareTextBufferFrom(FSynEdits[0]);
352   SetLines(TestText1);
353   PushBaseName('2 shared');
354 
355   PushBaseName('');
356   for e1 := 0 to 1 do begin
357     for e2 := 0 to 1 do begin
358       PopPushBaseName('e1='+IntToStr(e1) + ' e2='+IntToStr(e2) );
359       // Set Mark
360       FSynEdits[e1].SetBookMark(1, 2, 4);
361       CheckBMarks('bm 1', 0, [ 4, 1  ]);
362       CheckBMarks('bm 1', 1, [ 4, 1  ]);
363 
364       // goto
365       SelectEdit(0);
366       SynEdit.GotoBookMark(1);
367       TestIsCaret('Goto1 /0', 2, 4);
368       SelectEdit(1);
369       SynEdit.GotoBookMark(1);
370       TestIsCaret('Goto1 /1', 2, 4);
371 
372       // clear
373       FSynEdits[e2].ClearBookMark(1);
374       CheckBMarks('bm clear', 0, [] );
375       CheckBMarks('bm clear', 1, [] );
376     end;
377   end;
378   PopBaseName;
379 
380   PushBaseName('');
381   for e1 := 0 to 1 do begin
382     for e2 := 0 to 1 do begin
383       for e3 := 0 to 1 do begin
384         PopPushBaseName('e1='+IntToStr(e1) + ' e2='+IntToStr(e2) + ' e3='+IntToStr(e3));
385         FSynEdits[e1].ClearBookMark(1);
386         FSynEdits[e1].ClearBookMark(2);
387         // set 1 on ed1
388         FSynEdits[e1].SetBookMark(1, 2, 4);
389         CheckBMarks('bm set 1', 0, [ 4, 1  ]);
390         CheckBMarks('bm set 1', 1, [ 4, 1  ]);
391 
392         // set 2 on ed2
393         FSynEdits[e2].SetBookMark(2, 2, 5);
394         CheckBMarks('bm set 2', 0, [ 4,1,   5,2  ]);
395         CheckBMarks('bm set 2', 1, [ 4,1,   5,2  ]);
396 
397         // move 1 on ed3
398         FSynEdits[e3].SetBookMark(1, 2, 3);
399         CheckBMarks('bm move 1', 0, [ 3,1,   5,2  ]);
400         CheckBMarks('bm move 1', 1, [ 3,1,   5,2  ]);
401 
402         // move 2 over 1 on ed3
403         FSynEdits[e3].SetBookMark(2, 3, 3);
404         CheckBMarks('bm move 2 over 1', 0, [ 3,2  ]);
405         CheckBMarks('bm move 2 over 1', 1, [ 3,2  ]);
406       end;
407     end;
408   end;
409   PopBaseName;
410   {%endregion}
411 
412 
413   {%region edits shared after bookmark set }
414   ReCreateEdit(2);
415   // default setting is shared marklist
416   FSynEdits[1].ShareOptions := [eosShareMarks];
417   SetLines(TestText1);
418   PopPushBaseName('2 shared after bm set');
419 
420   FSynEdits[0].SetBookMark(1, 2, 4);
421   CheckBMarks('bm 1', 0, [ 4, 1  ]);
422   CheckBMarks('bm 1', 1, [  ]);
423 
424   FSynEdits[1].ShareTextBufferFrom(FSynEdits[0]);
425 
426   CheckBMarks('bm shared 1', 0, [ 4, 1  ]);
427   CheckBMarks('bm shared 1', 1, [ 4, 1  ]);
428 
429   FSynEdits[1].ClearBookMark(1);
430 
431   CheckBMarks('clear', 0, []);
432   CheckBMarks('clear', 1, []);
433 
434   // unshare edits
435   FSynEdits[0].SetBookMark(1, 2, 4);
436   CheckBMarks('bm 1', 0, [ 4, 1  ]);
437   CheckBMarks('bm 1', 1, [ 4, 1  ]);
438 
439   FSynEdits[1].UnShareTextBuffer;
440   CheckBMarks('unshare 1', 0, [ 4, 1  ]);
441   CheckBMarks('unshare 1', 1, [  ]);
442 
443   FSynEdits[1].ClearBookMark(1);
444   CheckBMarks('unshare 1', 0, [ 4, 1  ]);  // not affected
445   CheckBMarks('unshare 1', 1, [  ]);
446 
447   FSynEdits[0].ClearBookMark(1);
448   CheckBMarks('unshare 1', 0, [  ]);
449   CheckBMarks('unshare 1', 1, [  ]);
450   {%endregion}
451 end;
452 
453 initialization
454 
455   RegisterTest(TTestSynSharedEdits);
456 end.
457 
458