2
***************************************************************************
4
* This source is free software; you can redistribute it and/or modify *
5
* it under the terms of the GNU General Public License as published by *
6
* the Free Software Foundation; either version 2 of the License, or *
7
* (at your option) any later version. *
9
* This code is distributed in the hope that it will be useful, but *
10
* WITHOUT ANY WARRANTY; without even the implied warranty of *
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12
* General Public License for more details. *
14
* A copy of the GNU General Public License is available on the World *
15
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
16
* obtain it by writing to the Free Software Foundation, *
17
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19
***************************************************************************
21
Author: Mattias Gaertner
24
Source Editor marks for (compiler) messages.
33
Classes, SysUtils, math, SynGutterLineOverview, SynEditMarkupGutterMark,
34
SynEditMarks, SynEditMiscClasses, SynEditTypes, SynEdit, LazSynEditText,
35
LazLogger, LazFileUtils, AvgLvlTree, Graphics, Controls, Forms, ImgList,
46
FSourceMarkup: TSynSelectedColor;
47
FUrgency: TMessageLineUrgency;
48
procedure SetColor(AValue: TColor);
50
constructor Create(TheUrgency: TMessageLineUrgency; TheColor: TColor);
51
destructor Destroy; override;
52
property Urgency: TMessageLineUrgency read FUrgency;
53
property Color: TColor read FColor write SetColor;
54
property ImageIndex: integer read FImageIndex write FImageIndex;
55
property SourceMarkup: TSynSelectedColor read FSourceMarkup;
62
TETMark = class(TSynEditMarkupMark)
64
FMsgLine: TMessageLine;
65
FSourceMarks: TETMarks;
66
FUrgency: TMessageLineUrgency;
68
destructor Destroy; override;
69
property Urgency: TMessageLineUrgency read FUrgency write FUrgency;
70
property MsgLine: TMessageLine read FMsgLine write FMsgLine;
71
property SourceMarks: TETMarks read FSourceMarks write FSourceMarks;
76
TLMsgViewLine = class(TMessageLine)
79
destructor Destroy; override;
84
TOnGetSynEditOfFile = procedure(Sender: TObject; aFilename: string;
85
var aSynEdit: TSynEdit) of object;
87
TETMarks = class(TComponent)
89
FImageList: TCustomImageList;
90
fMarkStyles: array[TMessageLineUrgency] of TETMarkStyle;
91
FOnGetSynEditOfFile: TOnGetSynEditOfFile;
93
function GetMarkStyles(Urgency: TMessageLineUrgency): TETMarkStyle;
95
constructor Create(AOwner: TComponent); override;
96
destructor Destroy; override;
97
function CreateMark(MsgLine: TMessageLine; aSynEdit: TSynEdit = nil): TETMark;
98
procedure RemoveMarks(aSynEdit: TSynEdit);
99
property ImageList: TCustomImageList read FImageList write FImageList; // must have same Width/Height as the TSynEdits bookmarkimages
100
property OnGetSynEditOfFile: TOnGetSynEditOfFile read FOnGetSynEditOfFile write FOnGetSynEditOfFile;
101
property MarkStyles[Urgency: TMessageLineUrgency]: TETMarkStyle read GetMarkStyles;
102
property Priority: integer read FPriority write FPriority;
105
{ TExtToolSynGutterMarkProvider }
107
TExtToolSynGutterMarkProvider = class(TSynGutterLOvProviderBookmarks)
109
procedure AdjustColorForMark(AMark: TSynEditMark; var AColor: TColor;
110
var APriority: Integer); override;
113
TETSrcChangeAction = (
122
Action: TETSrcChangeAction;
125
Prev, Next: TETSrcChange;
126
constructor Create(AnAction: TETSrcChangeAction; const aFromPos, aToPos: TPoint);
127
constructor Create(AnAction: TETSrcChangeAction; FromPosY, FromPosX, ToPosY, ToPosX: integer);
128
function AsString: string;
131
{ TETSrcChanges - edits of single file}
133
TETSrcChanges = class
136
FFirst: TETSrcChange;
138
procedure Append(Change: TETSrcChange);
139
procedure Remove(Change: TETSrcChange);
140
procedure SetFilename(AValue: string);
143
destructor Destroy; override;
145
property First: TETSrcChange read FFirst;
146
property Last: TETSrcChange read FLast;
147
property Filename: string read FFilename write SetFilename;
148
procedure GetRange(out MinY, MaxY, LineDiffBehindMaxY: integer);
149
function Add(Action: TETSrcChangeAction; const FromPos, ToPos: TPoint): TETSrcChange; inline;
150
function Add(Action: TETSrcChangeAction; FromPosY, FromPosX, ToPosY, ToPosX: integer): TETSrcChange;
151
function AdaptCaret(var Line,Col: integer;
152
LeftBound: boolean // true = position is bound to character on the left
153
): boolean; // true if changed
154
procedure ConsistencyCheck;
155
procedure WriteDebugReport(Title: string);
158
{ TETMultiSrcChanges - edits of all files }
160
TETMultiSrcChanges = class
162
fAllChanges: TAvgLvlTree; // tree of TETSrcChanges sorted for Filename
165
destructor Destroy; override;
166
function Count: integer; inline;
168
function GetChanges(const aFilename: string; CreateIfNotExists: boolean): TETSrcChanges;
169
function AdaptCaret(const aFilename: string; var Line,Col: integer;
170
LeftBound: boolean // true = position is bound to character on the left
172
property AllChanges: TAvgLvlTree read fAllChanges; // tree of TETSrcChanges sorted for Filename
175
{ TETSynPlugin - create one per file, not one per synedit }
177
TETSynPlugin = class(TLazSynEditPlugin)
179
FChanges: TETSrcChanges;
180
FOnChanged: TNotifyEvent;
181
FSyncQueued: boolean;
182
procedure SetSyncQueued(AValue: boolean);
184
procedure DoSync({%H-}Data: PtrInt); // called by Application.QueueAsyncCall
185
procedure OnLineEdit(Sender: TSynEditStrings; aLinePos, aBytePos, aCount,
186
aLineBrkCnt: Integer; {%H-}aText: String);
188
constructor Create(AOwner: TComponent); override;
189
destructor Destroy; override;
190
property Changes: TETSrcChanges read FChanges;
191
property SyncQueued: boolean read FSyncQueued write SetSyncQueued;
192
property OnChanged: TNotifyEvent read FOnChanged write FOnChanged; // called by Application.QueueAsyncCall
195
function IsCaretInFront(Line1, Col1, Line2, Col2: integer): boolean; inline; overload;
196
function IsCaretInFront(const P1: TPoint; Line2, Col2: integer): boolean; inline; overload;
197
function IsCaretInFront(Line1, Col1: integer; const P2: TPoint): boolean; inline; overload;
198
function IsCaretInFront(const P1,P2: TPoint): boolean; inline; overload;
199
function IsCaretInFrontOrSame(Line1, Col1, Line2, Col2: integer): boolean; inline; overload;
200
function IsCaretInFrontOrSame(const P1: TPoint; Line2, Col2: integer): boolean; inline; overload;
201
function IsCaretInFrontOrSame(Line1, Col1: integer; const P2: TPoint): boolean; inline; overload;
202
function IsCaretInFrontOrSame(const P1,P2: TPoint): boolean; inline; overload;
203
procedure AdaptCaret(var Line,Col: integer;
204
LeftBound: boolean; // true = position is bound to character on the left
205
Action: TETSrcChangeAction;
206
FromPosY, FromPosX, ToPosY, ToPosX: integer);
208
function CompareETSrcChangesFilenames(Changes1, Changes2: Pointer): integer;
209
function CompareFilenameAndETSrcChanges(aFilenameStr, Changes: Pointer): integer;
211
function dbgs(Action: TETSrcChangeAction): string; overload;
212
procedure Test_AdaptCaret;
213
procedure Test_MergeTETSrcChanges;
217
function IsCaretInFront(Line1, Col1, Line2, Col2: integer): boolean;
219
Result:=(Line1<Line2) or ((Line1=Line2) and (Col1<Col2));
222
function IsCaretInFront(const P1: TPoint; Line2, Col2: integer): boolean;
224
Result:=IsCaretInFront(P1.Y,P1.X,Line2,Col2);
227
function IsCaretInFront(Line1, Col1: integer; const P2: TPoint): boolean;
229
Result:=IsCaretInFront(Line1,Col1,P2.Y,P2.X);
232
function IsCaretInFront(const P1, P2: TPoint): boolean;
234
Result:=IsCaretInFront(P1.Y,P1.X,P2.Y,P2.X);
237
function IsCaretInFrontOrSame(Line1, Col1, Line2, Col2: integer): boolean;
239
Result:=(Line1<Line2) or ((Line1=Line2) and (Col1<=Col2));
242
function IsCaretInFrontOrSame(const P1: TPoint; Line2, Col2: integer): boolean;
244
Result:=IsCaretInFrontOrSame(P1.Y,P1.X,Line2,Col2);
247
function IsCaretInFrontOrSame(Line1, Col1: integer; const P2: TPoint): boolean;
249
Result:=IsCaretInFrontOrSame(Line1,Col1,P2.Y,P2.X);
252
function IsCaretInFrontOrSame(const P1, P2: TPoint): boolean;
254
Result:=IsCaretInFrontOrSame(P1.Y,P1.X,P2.Y,P2.X);
257
procedure AdaptCaret(var Line, Col: integer; LeftBound: boolean;
258
Action: TETSrcChangeAction; FromPosY, FromPosX, ToPosY, ToPosX: integer);
260
//debugln(['AdaptCaret Line=',Line,' Col=',Col,' LeftBound=',LeftBound,' Action=',dbgs(Action),' FromPos=',FromPosY,',',FromPosX,' ToPos=',ToPosY,',',ToPosX]);
261
if Line<FromPosY then exit;
262
if Action=etscaInsert then begin
264
if Line>FromPosY then begin
265
// insert in lines in front => move vertically
266
inc(Line,ToPosY-FromPosY);
268
// insert in same line
269
if LeftBound then begin
270
if Col<=FromPosX then exit;
272
if Col<FromPosX then exit;
274
if FromPosY<ToPosY then begin
276
inc(Line,ToPosY-FromPosY);
277
Col:=ToPosX+Col-FromPosX;
279
// inserting some characters
280
inc(Col,ToPosX-FromPosX);
285
if Line>ToPosY then begin
286
// delete some lines in front => move vertically
287
dec(Line,ToPosY-FromPosY);
288
end else if Line<ToPosY then begin
289
if Line>FromPosY then begin
290
// whole line of position was deleted => move to start of deletion
294
// Line=FromPosY, Line<ToPosY
295
if Col<=FromPosX then begin
296
// delete is behind position => ignore
298
// position was deleted => move to start of deletion
305
if Line>FromPosY then begin
307
if Col<=ToPosX then begin
308
// position was deleted => move to start of deletion
312
// some characters at the start of the line were deleted
317
// Line=FromPosY=ToPosY
318
if Col<=FromPosX then begin
319
// delete is behind position => ignore
320
end else if Col<=ToPosX then begin
321
// position was deleted => move to start of deletion
324
// some characters in front were deleted
325
dec(Col,ToPosX-FromPosX);
330
//debugln(['AdaptCaret ',Line,',',Col]);
333
function CompareETSrcChangesFilenames(Changes1, Changes2: Pointer): integer;
335
SrcChanges1: TETSrcChanges absolute Changes1;
336
SrcChanges2: TETSrcChanges absolute Changes2;
338
Result:=CompareFilenames(SrcChanges1.Filename,SrcChanges2.Filename);
341
function CompareFilenameAndETSrcChanges(aFilenameStr, Changes: Pointer
344
SrcChanges: TETSrcChanges absolute Changes;
346
Result:=CompareFilenames(AnsiString(aFilenameStr),SrcChanges.Filename);
349
function dbgs(Action: TETSrcChangeAction): string;
352
WriteStr(Result,Action);
355
procedure Test_AdaptCaret;
357
procedure T(Title: string; Line,Col: integer;
358
LeftBound: boolean; // true = position is bound to character on the left
359
Action: TETSrcChangeAction;
360
FromPosY, FromPosX, ToPosY, ToPosX: integer;
361
ExpectedLine, ExpectedCol: integer);
369
AdaptCaret(Y,X,LeftBound,Action,FromPosY,FromPosX,ToPosY,ToPosX);
370
if (Y=ExpectedLine) and (X=ExpectedCol) then exit;
371
s:='Test_AdaptCaret: Caret='+dbgs(Line)+','+dbgs(Col)
372
+' LeftBound='+dbgs(LeftBound)
373
+' Action='+dbgs(Action)
374
+' FromPos='+dbgs(FromPosY)+','+dbgs(FromPosX)
375
+' ToPos='+dbgs(ToPosY)+','+dbgs(ToPosX)
376
+' Expected='+dbgs(ExpectedLine)+','+dbgs(ExpectedCol)
377
+' Actual='+dbgs(Y)+','+dbgs(X);
378
raise Exception.Create(Title+': '+s);
382
T('Insert chars in front',10,10,true,etscaInsert,1,1, 1,2, 10,10);
383
T('Insert lines in front',10,10,true,etscaInsert,1,1, 2,2, 11,10);
384
T('Insert chars behind',10,10,true,etscaInsert,12,1, 12,2, 10,10);
385
T('Insert chars in front, same line',10,10,true,etscaInsert,10,1, 10,2, 10,11);
386
T('Insert chars in front, same line',10,40,true,etscaInsert,10,28, 10,29, 10,41);
387
T('Insert chars behind, same line',10,10,true,etscaInsert,10,11, 10,12, 10,10);
388
T('Insert chars behind, same line, leftbound',10,10,true,etscaInsert,10,10, 10,12, 10,10);
389
T('Insert chars behind, same line, rightbound',10,10,false,etscaInsert,10,10, 10,12, 10,12);
390
T('Insert chars and line breaks in front, same line',10,10,true,etscaInsert,10,1, 11,1, 11,10);
391
T('Insert chars and line breaks in front, same line',10,10,true,etscaInsert,10,1, 11,2, 11,11);
392
T('Insert chars and line breaks in front, same line',10,10,true,etscaInsert,10,2, 11,2, 11,10);
393
T('Insert chars and line breaks in front, same line',10,10,true,etscaInsert,10,2, 11,5, 11,13);
394
T('Insert chars and line breaks in front, same line',10,10,true,etscaInsert,10,2, 13,5, 13,13);
396
T('Delete chars in front',10,10,true,etscaDelete, 1,1, 1,2, 10,10);
397
T('Delete lines in front',10,10,true,etscaDelete, 1,1, 2,2, 9,10);
398
T('Delete chars in front, same line',10,10,true,etscaDelete, 10,1, 10,2, 10,9);
399
T('Delete lines behind',10,10,true,etscaDelete, 11,1, 12,2, 10,10);
400
T('Delete chars behind, same line',10,10,true,etscaDelete, 10,11, 10,12, 10,10);
401
T('Delete chars behind, same line',10,10,true,etscaDelete, 10,10, 10,12, 10,10);
402
T('Delete lines in front, same line',10,10,true,etscaDelete, 9,1, 10,1, 9,10);
403
T('Delete lines in front, same line',10,10,true,etscaDelete, 9,1, 10,3, 9,8);
404
T('Delete position',10,10,true,etscaDelete, 9,1, 11,1, 9,1);
405
T('Delete position',10,10,true,etscaDelete, 10,1, 11,1, 10,1);
406
T('Delete position',10,10,true,etscaDelete, 10,5, 10,11, 10,5);
409
procedure Test_MergeTETSrcChanges;
411
Changes: TETSrcChanges;
413
procedure Check(Title: string; aChanges: array of TETSrcChange);
415
procedure E(Msg: string);
420
Changes.WriteDebugReport(s);
421
raise Exception.Create(s);
426
ActualChange: TETSrcChange;
427
ExpectedChange: TETSrcChange;
429
ActualChange:=Changes.First;
431
for i:=Low(aChanges) to High(aChanges) do begin
432
ExpectedChange:=aChanges[i];
433
if ExpectedChange=nil then begin
434
if ActualChange<>nil then
435
E('too many changes');
438
if ActualChange=nil then
439
E('not enough changes (missing: '+ActualChange.AsString+')');
440
if ExpectedChange.AsString<>ActualChange.AsString then
441
E('diff: Expected=('+ExpectedChange.AsString+'), Actual=('+ActualChange.AsString+')');
442
ActualChange:=ActualChange.Next;
445
for i:=Low(aChanges) to High(aChanges) do
451
Changes:=TETSrcChanges.Create;
453
Changes.ConsistencyCheck;
457
Changes.ConsistencyCheck;
460
Changes.Add(etscaInsert,1,1,1,46);
461
Changes.ConsistencyCheck;
462
Changes.Add(etscaInsert,1,46,2,1);
463
Changes.ConsistencyCheck;
464
Check('Merge insert',[TETSrcChange.Create(etscaInsert,1,1,2,1)]);
467
// insert characters into a previous multi line insert
468
Changes.Add(etscaInsert,10,1,12,1);
469
Changes.ConsistencyCheck;
470
Changes.Add(etscaInsert,10,1,10,2);
471
Changes.ConsistencyCheck;
472
Check('Ignore small insert',[TETSrcChange.Create(etscaInsert,10,1,12,1)]);
475
// delete behind previous delete
476
Changes.Add(etscaDelete,1,2,1,4);
477
Changes.ConsistencyCheck;
478
Changes.Add(etscaDelete,1,2,1,5);
479
Changes.ConsistencyCheck;
480
Check('combine deleting characters',[TETSrcChange.Create(etscaDelete,1,2,1,7)]);
483
// delete encloses a previous delete
484
Changes.Add(etscaDelete,1,2,1,4);
485
Changes.ConsistencyCheck;
486
Changes.Add(etscaDelete,1,1,1,5);
487
Changes.ConsistencyCheck;
488
Check('combine deleting characters',[TETSrcChange.Create(etscaDelete,1,1,1,7)]);
491
// delete in front of a previous delete
492
Changes.Add(etscaDelete,2,2,2,4);
493
Changes.ConsistencyCheck;
494
Changes.Add(etscaDelete,1,1,2,2);
495
Changes.ConsistencyCheck;
496
Check('combine deleting characters',[TETSrcChange.Create(etscaDelete,1,1,2,4)]);
499
// delete encloses a previous delete of characters
500
Changes.Add(etscaDelete,2,2,2,4);
501
Changes.ConsistencyCheck;
502
Changes.Add(etscaDelete,1,1,3,1);
503
Changes.ConsistencyCheck;
504
Check('combine deleting characters',[TETSrcChange.Create(etscaDelete,1,1,3,1)]);
507
// delete encloses a previous delete of a line
508
Changes.Add(etscaDelete,2,2,3,4);
509
Changes.ConsistencyCheck;
510
Changes.Add(etscaDelete,1,1,4,1);
511
Changes.ConsistencyCheck;
512
Check('combine deleting characters',[TETSrcChange.Create(etscaDelete,1,1,5,1)]);
515
// delete encloses a previous delete at end
516
Changes.Add(etscaDelete,2,2,3,1);
517
Changes.ConsistencyCheck;
518
Changes.Add(etscaDelete,1,1,2,3);
519
Changes.ConsistencyCheck;
520
Check('combine deleting characters',[TETSrcChange.Create(etscaDelete,1,1,3,2)]);
523
// delete encloses a previous delete at end
524
Changes.Add(etscaDelete,2,2,3,2);
525
Changes.ConsistencyCheck;
526
Changes.Add(etscaDelete,1,1,2,3);
527
Changes.ConsistencyCheck;
528
Check('combine deleting characters',[TETSrcChange.Create(etscaDelete,1,1,3,3)]);
531
// delete encloses a previous delete at start
532
Changes.Add(etscaDelete,1,2,3,2);
533
Changes.ConsistencyCheck;
534
Changes.Add(etscaDelete,1,1,1,2);
535
Changes.ConsistencyCheck;
536
Check('combine deleting characters',[TETSrcChange.Create(etscaDelete,1,1,3,2)]);
543
{ TETMultiSrcChanges }
545
constructor TETMultiSrcChanges.Create;
547
fAllChanges:=TAvgLvlTree.Create(@CompareETSrcChangesFilenames);
550
destructor TETMultiSrcChanges.Destroy;
553
FreeAndNil(fAllChanges);
558
function TETMultiSrcChanges.Count: integer;
560
Result:=fAllChanges.Count;
563
procedure TETMultiSrcChanges.Clear;
565
fAllChanges.FreeAndClear;
568
function TETMultiSrcChanges.GetChanges(const aFilename: string;
569
CreateIfNotExists: boolean): TETSrcChanges;
571
Node: TAvgLvlTreeNode;
573
Node:=fAllChanges.FindKey(Pointer(aFilename),@CompareFilenameAndETSrcChanges);
575
Result:=TETSrcChanges(Node.Data)
576
else if CreateIfNotExists then begin
577
Result:=TETSrcChanges.Create;
578
Result.Filename:=aFilename;
579
fAllChanges.Add(Result);
584
function TETMultiSrcChanges.AdaptCaret(const aFilename: string; var Line,
585
Col: integer; LeftBound: boolean): boolean;
587
Changes: TETSrcChanges;
589
Changes:=GetChanges(aFilename,false);
593
Result:=Changes.AdaptCaret(Line,Col,LeftBound);
598
constructor TETSrcChange.Create(AnAction: TETSrcChangeAction; const aFromPos,
606
constructor TETSrcChange.Create(AnAction: TETSrcChangeAction; FromPosY,
607
FromPosX, ToPosY, ToPosX: integer);
616
function TETSrcChange.AsString: string;
618
if Action=etscaInsert then
622
Result+='-From='+IntToStr(FromPos.Y)+','+IntToStr(FromPos.X);
623
Result+='-To='+IntToStr(ToPos.Y)+','+IntToStr(ToPos.X);
628
procedure TETSrcChanges.Append(Change: TETSrcChange);
630
if First=nil then begin
639
procedure TETSrcChanges.Remove(Change: TETSrcChange);
645
if Change.Prev<>nil then
646
Change.Prev.Next:=Change.Next;
647
if Change.Next<>nil then
648
Change.Next.Prev:=Change.Prev;
653
procedure TETSrcChanges.SetFilename(AValue: string);
657
if FFilename=AValue then Exit;
658
HasChanged:=CompareFilenames(FFilename,AValue)<>0;
664
constructor TETSrcChanges.Create;
668
destructor TETSrcChanges.Destroy;
674
procedure TETSrcChanges.Clear;
677
CurItem: TETSrcChange;
680
while Item<>nil do begin
689
procedure TETSrcChanges.GetRange(out MinY, MaxY, LineDiffBehindMaxY: integer);
690
// true if there are changes
691
// All changes were done between lines MinY and MaxY (inclusive).
692
// Lines behind MaxY are moved by LineDiffBehindMaxY.
694
// if MinY<=Line<=MaxY then AdaptCaret(Line,Col,...)
695
// else if Line>MaxY then inc(Line,LineDiffBehindMaxY);
697
Change: TETSrcChange;
703
LineDiffBehindMaxY:=0;
705
if Change=nil then exit;
706
while Change<>nil do begin
707
MinY:=Min(MinY,Change.FromPos.Y);
708
if Change.Action=etscaInsert then
709
MaxY:=Max(MaxY,Change.FromPos.Y)
711
MaxY:=Max(MaxY,Change.ToPos.Y);
716
AdaptCaret(y,x,true);
717
LineDiffBehindMaxY:=y-(MaxY+1);
721
function TETSrcChanges.Add(Action: TETSrcChangeAction; const FromPos,
722
ToPos: TPoint): TETSrcChange;
724
Result:=Add(Action,FromPos.Y,FromPos.X,ToPos.Y,ToPos.X);
727
function TETSrcChanges.Add(Action: TETSrcChangeAction; FromPosY, FromPosX,
728
ToPosY, ToPosX: integer): TETSrcChange;
730
procedure RaiseFromBehindToPos;
732
raise Exception.CreateFmt('TETSrcChanges.Add FromPos=%s,%s behind ToPos=%s,%s',[FromPosY,FromPosX,ToPosY,ToPosX]);
735
function Merge(Prev, Cur: TETSrcChange): boolean;
737
if (Prev=nil) or (Prev.Action<>Action) then
739
// check if addition can be merged
740
if Action=etscaInsert then begin
741
if (Prev.ToPos.Y=Cur.FromPos.Y) and (Prev.ToPos.X=Cur.FromPos.X) then begin
742
// Cur is an insert exactly behind Prev insert -> append insert
743
Prev.ToPos.Y:=Cur.ToPos.Y;
744
Prev.ToPos.X:=Cur.ToPos.X;
745
{$IFDEF VerboseETSrcChange}
746
debugln(['TETSrcChanges.Add appending insert: ',Prev.AsString]);
750
if (Cur.FromPos.Y=Cur.ToPos.Y)
751
and (Prev.FromPos.Y<=Cur.FromPos.Y) and (Prev.ToPos.Y>Cur.FromPos.Y) then begin
752
// Cur inserts characters into a Prev multi line insert -> ignore
753
{$IFDEF VerboseETSrcChange}
754
debugln(['TETSrcChanges.Add inserting characters into a multi line insert -> ignore']);
758
// ToDo: insert exactly in front
760
if IsCaretInFrontOrSame(Cur.FromPos,Prev.FromPos)
761
and IsCaretInFrontOrSame(Prev.FromPos,Cur.ToPos) then begin
762
// Cur delete extends Prev delete => combine delete
763
etSrcEditMarks.AdaptCaret(Cur.ToPos.Y,Cur.ToPos.X,false,etscaInsert,
764
Prev.FromPos.Y,Prev.FromPos.X,Prev.ToPos.Y,Prev.ToPos.X);
765
Prev.ToPos:=Cur.ToPos;
766
Prev.FromPos:=Cur.FromPos;
767
{$IFDEF VerboseETSrcChange}
768
debugln(['TETSrcChanges.Add delete encloses previous delete: ',Prev.AsString]);
777
{$IFDEF VerboseETSrcChange}
778
debugln(['TETSrcChanges.Add Action=',dbgs(Action),' From=',FromPosY,',',FromPosX,' To=',ToPosY,',',ToPosX]);
781
if (FromPosY=ToPosY) and (FromPosX=ToPosX) then
782
exit; // no change => ignore
785
if IsCaretInFront(ToPosY,ToPosX,FromPosY,FromPosX) then
786
RaiseFromBehindToPos;
788
Result:=TETSrcChange.Create(Action, FromPosY, FromPosX, ToPosY, ToPosX);
790
if Merge(Last,Result) then begin
794
if not Merge(Result.Prev,Result) then exit;
802
function TETSrcChanges.AdaptCaret(var Line, Col: integer; LeftBound: boolean
805
Change: TETSrcChange;
812
while Change<>nil do begin
813
etSrcEditMarks.AdaptCaret(Line,Col,LeftBound,Change.Action,
814
Change.FromPos.Y,Change.FromPos.X,Change.ToPos.Y,Change.ToPos.X);
817
Result:=(Line<>OldLine) or (Col<>OldCol);
820
procedure TETSrcChanges.ConsistencyCheck;
822
procedure E(Msg: string);
824
raise Exception.Create('TETSrcChanges ConsistencyError: '+Msg);
828
Change: TETSrcChange;
831
if (First=nil)<>(Last=nil) then
832
E('(First=nil)<>(Last=nil)');
833
List:=TFPList.Create;
836
while Change<>nil do begin
837
if IsCaretInFront(Change.ToPos,Change.FromPos) then
838
E('FromPos>ToPos: '+Change.AsString);
839
if Change.Prev<>nil then begin
840
if Change.Prev.Next<>Change then
841
E('Change.Prev.Next<>Change '+Change.AsString);
843
if Change<>First then
844
E('Change.Prev=nil');
846
if (Change.Next=nil) and (Change<>Last) then
847
E('Change.Next=nil');
848
if List.IndexOf(Change)>=0 then
849
E('Cycle '+Change.AsString);
858
procedure TETSrcChanges.WriteDebugReport(Title: string);
860
Change: TETSrcChange;
862
debugln('TETSrcChanges.WriteDebugReport ',Title);
864
while Change<>nil do begin
865
debugln(' ',Change.AsString);
872
procedure TETSynPlugin.DoSync(Data: PtrInt);
875
if FChanges.First=nil then exit;
876
if Assigned(OnChanged) then
881
procedure TETSynPlugin.SetSyncQueued(AValue: boolean);
883
if FSyncQueued=AValue then Exit;
886
Application.QueueAsyncCall(@DoSync,0)
888
Application.RemoveAsyncCalls(Self);
891
procedure TETSynPlugin.OnLineEdit(Sender: TSynEditStrings; aLinePos, aBytePos,
892
aCount, aLineBrkCnt: Integer; aText: String);
895
aBytePos is 1-based column in line
901
Example deleting line 290..292:
902
LinePos=291 BytePos=1 Count=-45 LineBrkCnt=0 Text=""
903
LinePos=292 BytePos=1 Count=-33 LineBrkCnt=0 Text=""
904
LinePos=291 BytePos=1 Count=0 LineBrkCnt=-2 Text=""
905
LinePos=290 BytePos=70 Count=0 LineBrkCnt=-1 Text=""
906
LinePos=290 BytePos=1 Count=-69 LineBrkCnt=0 Text=""
909
{$IFDEF VerboseETSrcChange}
910
debugln(['TETSynPlugin.OnLineEdit LinePos=',aLinePos,' BytePos=',aBytePos,' Count=',aCount,' LineBrkCnt=',aLineBrkCnt,' Text="',dbgstr(aText),'"']);
912
if aCount>0 then begin
914
FChanges.Add(etscaInsert,aLinePos,aBytePos,aLinePos,aBytePos+aCount);
915
end else if aCount<0 then begin
917
FChanges.Add(etscaDelete,aLinePos,aBytePos,aLinePos,aBytePos-aCount);
918
end else if aLineBrkCnt>0 then begin
919
// insert line breaks
920
// Note: always at end of line, because Count=0
921
FChanges.Add(etscaInsert,aLinePos,aBytePos,aLinePos+aLineBrkCnt,1);
922
end else if aLineBrkCnt<0 then begin
923
// delete line breaks / empty lines
924
FChanges.Add(etscaDelete,aLinePos,aBytePos,aLinePos-aLineBrkCnt,1);
930
constructor TETSynPlugin.Create(AOwner: TComponent);
932
inherited Create(AOwner);
933
FChanges:=TETSrcChanges.Create;
934
ViewedTextBuffer.AddEditHandler(@OnLineEdit);
937
destructor TETSynPlugin.Destroy;
940
ViewedTextBuffer.RemoveEditHandler(@OnLineEdit);
942
FreeAndNil(FChanges);
947
destructor TETMark.Destroy;
949
if MsgLine is TLMsgViewLine then
950
TLMsgViewLine(MsgLine).Mark:=nil;
957
function TETMarks.GetMarkStyles(Urgency: TMessageLineUrgency): TETMarkStyle;
959
Result:=fMarkStyles[Urgency];
962
constructor TETMarks.Create(AOwner: TComponent);
964
u: TMessageLineUrgency;
966
inherited Create(AOwner);
967
for u:=low(TMessageLineUrgency) to high(TMessageLineUrgency) do
968
fMarkStyles[u]:=TETMarkStyle.Create(u,clNone);
969
fMarkStyles[mluHint].Color:=clGreen;
970
fMarkStyles[mluNote].Color:=clGreen;
971
fMarkStyles[mluWarning].Color:=clYellow;
972
fMarkStyles[mluError].Color:=clRed;
973
fMarkStyles[mluFatal].Color:=clRed;
974
fMarkStyles[mluPanic].Color:=clRed;
977
destructor TETMarks.Destroy;
979
u: TMessageLineUrgency;
981
for u:=low(TMessageLineUrgency) to high(TMessageLineUrgency) do
982
FreeAndNil(fMarkStyles[u]);
986
function TETMarks.CreateMark(MsgLine: TMessageLine; aSynEdit: TSynEdit
990
if (MsgLine.Line<1) or (MsgLine.Column<1) or (MsgLine.Filename='') then exit;
991
if aSynEdit=nil then begin
992
if OnGetSynEditOfFile=nil then exit;
993
OnGetSynEditOfFile(Self,MsgLine.Filename,aSynEdit);
994
if (aSynEdit=nil) then exit;
996
Result:=TETMark.Create(aSynEdit);
997
Result.SourceMarks:=Self;
998
Result.MsgLine:=MsgLine;
999
Result.Line:=MsgLine.Line;
1000
Result.Column:=MsgLine.Column;
1001
Result.Visible:=true;
1002
Result.Priority:=Priority;
1003
Result.Urgency:=MsgLine.Urgency;
1004
Result.ImageList:=ImageList;
1005
Result.ImageIndex:=MarkStyles[Result.Urgency].ImageIndex;
1006
Result.SourceMarkup:=MarkStyles[Result.Urgency].SourceMarkup;
1007
aSynEdit.Marks.Add(Result);
1010
procedure TETMarks.RemoveMarks(aSynEdit: TSynEdit);
1015
for i:=aSynEdit.Marks.Count-1 downto 0 do begin
1016
Mark:=aSynEdit.Marks[i];
1017
if Mark is TETMark then
1024
procedure TETMarkStyle.SetColor(AValue: TColor);
1026
if FColor=AValue then Exit;
1028
SourceMarkup.FrameColor:=Color;
1031
constructor TETMarkStyle.Create(TheUrgency: TMessageLineUrgency;
1034
FUrgency:=TheUrgency;
1036
FSourceMarkup:=TSynSelectedColor.Create;
1037
SourceMarkup.Foreground:=clNone;
1038
SourceMarkup.Background:=clNone;
1039
SourceMarkup.FrameStyle:=slsWaved;
1040
SourceMarkup.FrameEdges:=sfeBottom;
1041
SourceMarkup.FrameColor:=Color;
1044
destructor TETMarkStyle.Destroy;
1046
FreeAndNil(FSourceMarkup);
1050
{ TExtToolSynGutterMarkProvider }
1052
procedure TExtToolSynGutterMarkProvider.AdjustColorForMark(AMark: TSynEditMark;
1053
var AColor: TColor; var APriority: Integer);
1057
//DebugLn(['TExtToolSynGutterMarkProvider.AdjustColorForMark Line=',AMark.Line,' Color=',AMark.Column]);
1058
if (AMark is TETMark) then begin
1059
ETMark:=TETMark(AMark);
1060
AColor:=ETMark.SourceMarks.MarkStyles[ETMark.Urgency].Color;
1062
inherited AdjustColorForMark(AMark, AColor, APriority);
1067
destructor TLMsgViewLine.Destroy;