1 {-------------------------------------------------------------------------------
2 The contents of this file are subject to the Mozilla Public License
3 Version 1.1 (the "License"); you may not use this file except in compliance
4 with the License. You may obtain a copy of the License at
5 http://www.mozilla.org/MPL/
6
7 Software distributed under the License is distributed on an "AS IS" basis,
8 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
9 the specific language governing rights and limitations under the License.
10
11 The Original Code is: SynEditMarkupFoldColoring.pas, released 2015-12-07.
12 Copyleft (c) 2015-2016 x2nie - Fathony Luth.
13
14 The Original SynEdit Project is based on mwCustomEdit.pas by Martin Waldenburg,
15 part of the mwEdit component suite.
16 Portions created by Martin Waldenburg are Copyright (C) 1998 Martin Waldenburg.
17 All Rights Reserved.
18
19 Contributors to the SynEdit and mwEdit projects are listed in the
20 Contributors.txt file.
21
22 Alternatively, the contents of this file may be used under the terms of the
23 GNU General Public License Version 2 or later (the "GPL"), in which case
24 the provisions of the GPL are applicable instead of those above.
25 If you wish to allow use of your version of this file only under the terms
26 of the GPL and not to allow others to use your version of this file
27 under the MPL, indicate your decision by deleting the provisions above and
28 replace them with the notice and other provisions required by the GPL.
29 If you do not delete the provisions above, a recipient may use your version
30 of this file under either the MPL or the GPL.
31
32
33
34 Features:
35 - paint keywords in multiple colors, depends on fold block level or by config
36 - paint vertical line between paired open~close fold
37 - vertical line and/or keyword can be disabled
38 - independent, can be used for any SynHighlighter
39 - many features are well tested for PasSynPas.pas
40 - only active when SynEdit.Highlighter is TSynCustomFoldHighlighter
41
42 -------------------------------------------------------------------------------}
43 unit SynEditMarkupFoldColoring;
44
45 {$mode objfpc}{$H+}
46 { $define SynEditMarkupFoldColoringDebug}
47 { $define WithSynMarkupFoldColorDebugGutter}
48
49 interface
50
51 uses
52 Classes, SysUtils, Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
53 LCLProc, LCLType, SynEditHighlighter,
54 SynEditHighlighterFoldBase, LazSynEditText, SynEditTextBase, SynEditTypes
55 {$IFDEF WithSynMarkupFoldColorDebugGutter}, SynGutterBase, SynTextDrawer{$ENDIF}
56 ;
57
58 type
59
60 {$IFDEF WithSynMarkupFoldColorDebugGutter}
61 TSynEditMarkupFoldColors = class;
62
63 { TIDESynMarkupFoldColorDebugGutter }
64
65 TIDESynMarkupFoldColorDebugGutter = class(TSynGutterPartBase)
66 protected
67 FOwner: TSynEditMarkupFoldColors;
PreferedWidthnull68 function PreferedWidth: Integer; override;
69 public
70 procedure Paint(Canvas: TCanvas; AClip: TRect; FirstLine, LastLine: integer);
71 override;
72 end;
73 {$ENDIF}
74
75 PMarkupFoldColorInfo = ^TMarkupFoldColorInfo;
76 TMarkupFoldColorInfo = record
77 PhysX, PhysX2, PhysCol: Integer;
78 ColorIdx: Integer;
79 Border : Boolean;
80 Ignore : Boolean; //no color no line
81 SrcNode : TSynFoldNodeInfo;
82 Level, LevelAfter : integer; //needed by non nest nodes
83 end;
84
85 TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
86 TSynFoldNodeInfos = array of TSynFoldNodeInfo; //for quick compare detection
87
88 TColumnCacheEntry = Integer;
89 PColumnCacheEntry = ^TColumnCacheEntry;
90
91 { TMarkupFoldColorsLineColor }
92
93 TMarkupFoldColorsLineColor = class
94 private
95 FAlpha: Byte;
96 FColor: TColor;
97 FOnChange: TNotifyEvent;
98 FOnChanged: TNotifyEvent;
99 FPriority: Integer;
100 FStyle: TSynLineStyle;
101 procedure SetAlpha(AValue: Byte);
102 procedure SetColor(AValue: TColor);
103 procedure SetPriority(AValue: Integer);
104 procedure SetStyle(AValue: TSynLineStyle);
105 procedure Changed;
106 public
107 constructor Create;
108 property Color: TColor read FColor write SetColor; // clDefault will take Color[].Frame or Color[].Foreground
109 property Style: TSynLineStyle read FStyle write SetStyle;
110 property Alpha: Byte read FAlpha write SetAlpha;
111 property Priority: Integer read FPriority write SetPriority;
112 property OnChange: TNotifyEvent read FOnChanged write FOnChanged;
113 end;
114
115 { TSynEditMarkupFoldColorsColumnCache }
116
117 TSynEditMarkupFoldColorsColumnCache = class(TSynManagedStorageMem)
118 private
GetColumnDatanull119 function GetColumnData(Index: Integer): TColumnCacheEntry;
GetIsValidForLinenull120 function GetIsValidForLine(Index: Integer): Boolean;
121 procedure SetColumnData(Index: Integer; AValue: TColumnCacheEntry);
122 protected
123 procedure LineTextChanged(AIndex: Integer; ACount: Integer = 1); override;
124 procedure InsertedLines(AIndex, ACount: Integer); override;
125 //procedure DeletedLines(AIndex, ACount: Integer); override;
126 procedure Invalidate;
127 public
128 constructor Create;
129 property ColumnData[Index: Integer]: TColumnCacheEntry read GetColumnData write SetColumnData; default;
130 property IsValidForLine[Index: Integer]: Boolean read GetIsValidForLine;
131 end;
132
133 { TSynEditMarkupFoldColors }
134
135 TSynEditMarkupFoldColors = class(TSynEditMarkup)
136 private
137 {$IFDEF WithSynMarkupFoldColorDebugGutter}
138 FDebugGutter: TIDESynMarkupFoldColorDebugGutter;
139 {$ENDIF}
GetFirstCharacterColumnnull140 function GetFirstCharacterColumn(pIndex: Integer): TColumnCacheEntry;
141 procedure TextBufferChanged(pSender: TObject);
142 private
143 FColorCount: Integer;
144 fHighlighter: TSynCustomFoldHighlighter;
145 fMarkupColors: array of TSynSelectedColor;
146 fLineColors : array of TMarkupFoldColorsLineColor;
147 fNestList, fNestList2: TLazSynEditNestedFoldsList;
148
149 // cache
150 FColumnCache: TSynEditMarkupFoldColorsColumnCache;
151 fFoldColorInfosCount,
152 fFoldColorInfosCapacity: Integer;
153
154 fDefaultGroup: integer;
155 fFoldColorInfos: TMarkupFoldColorInfos;
156
157 fPreparedRow: integer;
158 fLastOpenNode: TSynFoldNodeInfo;
159 fLastIndex,
160 fLastOpenIndex: Integer;
161 fLastEnabled: Boolean;
162
163 procedure DoMarkupParentFoldAtRow(pRow: Integer);
164 procedure DoMarkupParentCloseFoldAtRow(pRow: Integer);
GetColornull165 function GetColor(pIndex: Integer): TSynSelectedColor;
GetLineColornull166 function GetLineColor(pIndex: Integer): TMarkupFoldColorsLineColor;
167 procedure SetColorCount(AValue: Integer);
168 procedure SetDefaultGroup(pValue: integer);
169 procedure SetFoldColorInfosCount(pNewCount: Integer);
170 procedure InitNestList;
171 property FirstCharacterColumn[pIindex: Integer]: TColumnCacheEntry read GetFirstCharacterColumn;
172 protected
173 // Notifications about Changes to the text
174 procedure DoTextChanged({%H-}pStartLine, pEndLine, {%H-}pCountDiff: Integer); override; // 1 based
175 procedure SetLines(const pValue: TSynEditStrings); override;
176 procedure HighlightChanged(pSender: TSynEditStrings; pIndex, pCount: Integer);
177 procedure DoEnabledChanged(pSender: TObject); override;
178 procedure ColorChanged(pMarkup: TObject);
179 public
180 constructor Create(pSynEdit : TSynEditBase);
181 destructor Destroy; override;
RealEnablednull182 function RealEnabled: Boolean; override;
183 procedure BeginMarkup; override;
GetMarkupAttributeAtRowColnull184 function GetMarkupAttributeAtRowCol(const pRow: Integer;
185 const pStartCol: TLazSynDisplayTokenBound;
186 const {%H-}pRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor; override;
187 procedure GetNextMarkupColAfterRowCol(const pRow: Integer;
188 const pStartCol: TLazSynDisplayTokenBound;
189 const {%H-}pRtlInfo: TLazSynDisplayRtlInfo;
190 out pNextPhys, pNextLog: Integer); override;
191
192 procedure PrepareMarkupForRow(pRow : Integer); override;
193 property DefaultGroup : integer read fDefaultGroup write SetDefaultGroup;
194 property ColorCount: Integer read FColorCount write SetColorCount;
195 property Color[pIndex: Integer]: TSynSelectedColor read GetColor;
196 property LineColor[pIndex: Integer]: TMarkupFoldColorsLineColor read GetLineColor;
197 end;
198
199 implementation
200 uses
201 SynEdit,
202 SynEditMiscProcs,
203 {$IFDEF SynEditMarkupFoldColoringDebug}
204 SynHighlighterPas,
205 strutils,
206 {$endif}
207 Dialogs;
208
209 { TMarkupFoldColorsLineColor }
210
211 procedure TMarkupFoldColorsLineColor.SetColor(AValue: TColor);
212 begin
213 if FColor = AValue then Exit;
214 FColor := AValue;
215 Changed;
216 end;
217
218 procedure TMarkupFoldColorsLineColor.SetAlpha(AValue: Byte);
219 begin
220 if FAlpha = AValue then Exit;
221 FAlpha := AValue;
222 Changed;
223 end;
224
225 procedure TMarkupFoldColorsLineColor.SetPriority(AValue: Integer);
226 begin
227 if FPriority = AValue then Exit;
228 FPriority := AValue;
229 Changed;
230 end;
231
232 procedure TMarkupFoldColorsLineColor.SetStyle(AValue: TSynLineStyle);
233 begin
234 if FStyle = AValue then Exit;
235 FStyle := AValue;
236 Changed;
237 end;
238
239 procedure TMarkupFoldColorsLineColor.Changed;
240 begin
241 if FOnChange <> nil then
242 FOnChange(Self);
243 end;
244
245 constructor TMarkupFoldColorsLineColor.Create;
246 begin
247 FStyle := slsSolid;
248 FColor := clDefault;
249 FPriority := 0;
250 inherited;
251 end;
252
253 { TSynEditMarkupFoldColorsColumnCache }
254
TSynEditMarkupFoldColorsColumnCache.GetColumnDatanull255 function TSynEditMarkupFoldColorsColumnCache.GetColumnData(Index: Integer
256 ): TColumnCacheEntry;
257 begin
258 Result := PColumnCacheEntry(ItemPointer[Index])^;
259 end;
260
GetIsValidForLinenull261 function TSynEditMarkupFoldColorsColumnCache.GetIsValidForLine(Index: Integer
262 ): Boolean;
263 begin
264 Result := PColumnCacheEntry(ItemPointer[Index])^ > 0;
265 end;
266
267 procedure TSynEditMarkupFoldColorsColumnCache.SetColumnData(Index: Integer;
268 AValue: TColumnCacheEntry);
269 begin
270 PColumnCacheEntry(ItemPointer[Index])^ := AValue;
271 end;
272
273 procedure TSynEditMarkupFoldColorsColumnCache.LineTextChanged(AIndex: Integer;
274 ACount: Integer);
275 var
276 i: Integer;
277 begin
278 for i := AIndex to AIndex + ACount - 1 do
279 ColumnData[i] := 0;
280 end;
281
282 procedure TSynEditMarkupFoldColorsColumnCache.InsertedLines(AIndex,
283 ACount: Integer);
284 var
285 i: Integer;
286 begin
287 for i := AIndex to AIndex + ACount - 1 do
288 ColumnData[i] := 0;
289 end;
290
291 procedure TSynEditMarkupFoldColorsColumnCache.Invalidate;
292 var
293 i: Integer;
294 begin
295 for i := 0 to Count - 1 do
296 ColumnData[i] := 0;
297 end;
298
299 constructor TSynEditMarkupFoldColorsColumnCache.Create;
300 begin
301 inherited;
302 ItemSize := SizeOf(TColumnCacheEntry);
303 end;
304
305 {$IFDEF WithSynMarkupFoldColorDebugGutter}
306 { TIDESynMarkupFoldColorDebugGutter }
307
PreferedWidthnull308 function TIDESynMarkupFoldColorDebugGutter.PreferedWidth: Integer;
309 begin
310 Result := 600;
311 end;
312
313 procedure TIDESynMarkupFoldColorDebugGutter.Paint(Canvas: TCanvas;
314 AClip: TRect; FirstLine, LastLine: integer);
315 var
316 TextDrawer: TheTextDrawer;
317 dc: HDC;
318 rcLine: TRect;
319 LineHeight, c, i, j: Integer;
320 iLine: LongInt;
321 s, fc: string;
322 begin
323 TextDrawer := Gutter.TextDrawer;
324 dc := Canvas.Handle;
325 TextDrawer.BeginDrawing(dc);
326 try
327 TextDrawer.SetBackColor(Gutter.Color);
328 TextDrawer.SetForeColor(TCustomSynEdit(SynEdit).Font.Color);
329 TextDrawer.SetFrameColor(clNone);
330 with AClip do
331 TextDrawer.ExtTextOut(Left, Top, ETO_OPAQUE, AClip, nil, 0);
332
333 rcLine := AClip;
334 rcLine.Bottom := AClip.Top;
335 LineHeight := TCustomSynEdit(SynEdit).LineHeight;
336 c := TCustomSynEdit(SynEdit).Lines.Count;
337 for i := FirstLine to LastLine do
338 begin
339 iLine := FoldView.DisplayNumber[i];
340 if (iLine <= 0) or (iLine > c) then break;
341 // next line rect
342 rcLine.Top := rcLine.Bottom;
343 rcLine.Bottom := rcLine.Bottom + LineHeight;
344
345 FOwner.PrepareMarkupForRow(iLine);
346 s := '';
347 for j := 0 to FOwner.fFoldColorInfosCount - 1 do begin
348 with FOwner.fFoldColorInfos[j] do
349 s := s + '('
350 + IntToStr(PhysX) + ',' + IntToStr(PhysX2) + ',' + IntToStr(PhysCol) + '/'
351 + IntToStr(ColorIdx) + '/'
352 + BoolToStr(Border, True)[1] + BoolToStr(Ignore, True)[1] + '/'
353 + IntToStr(Level) + ',' + IntToStr(LevelAfter)
354 + ') ';
355 while length(s) < 21 * (j+1) do s := s + ' ';
356 end;
357 s := IntToStr(FOwner.fFoldColorInfosCount) + s;
358 if iLine < FOwner.FColumnCache.Count then
359 s := s + ', '+IntToStr(FOwner.FColumnCache[ToIdx(iLine)]);
360
361 TextDrawer.ExtTextOut(rcLine.Left, rcLine.Top, ETO_OPAQUE or ETO_CLIPPED, rcLine,
362 PChar(Pointer(S)),Length(S));
363 end;
364
365 finally
366 TextDrawer.EndDrawing;
367 end;
368 end;
369 {$ENDIF}
370
371
372 {$IFDEF SynEditMarkupFoldColoringDebug}
FoldTypeToStrnull373 function FoldTypeToStr(p_FoldType: Pointer): String;
374 begin
375 WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
376 while length(Result) < 17 do Result := Result + ' ';
377 end;
378 {$ENDIF}
379
380
381 { TSynEditMarkupFoldColors }
382
383 constructor TSynEditMarkupFoldColors.Create(pSynEdit: TSynEditBase);
384 begin
385 inherited Create(pSynEdit);
386
387 {$IFDEF WithSynMarkupFoldColorDebugGutter}
388 FDebugGutter := TIDESynMarkupFoldColorDebugGutter.Create(TSynEdit(pSynEdit).RightGutter.Parts);
389 FDebugGutter.FOwner := Self;
390 {$ENDIF}
391
392 FColumnCache := TSynEditMarkupFoldColorsColumnCache.Create;
393
394 fHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(SynEdit).Highlighter);
395 if Assigned(fHighlighter)
396 and not (fHighlighter is TSynCustomFoldHighlighter) then
397 fHighlighter := nil;
398
399 fDefaultGroup := 0;
400 fFoldColorInfosCount := 0;
401 SetLength(fFoldColorInfos, 50);
402 fFoldColorInfosCapacity := 50;
403
404 fNestList := TLazSynEditNestedFoldsList.Create(Lines, fHighlighter);
405 fNestList.ResetFilter;
406 fNestList.FoldGroup := fDefaultGroup;
407 fNestList.FoldFlags := [sfbIncludeDisabled];
408 fNestList.IncludeOpeningOnLine := True;
409
410 // for scanning the "if" of a "then" to find the indent
411 fNestList2 := TLazSynEditNestedFoldsList.Create(Lines, fHighlighter);
412 fNestList2.ResetFilter;
413 fNestList2.FoldGroup := fDefaultGroup;
414 fNestList2.FoldFlags := [sfbIncludeDisabled];
415 fNestList2.IncludeOpeningOnLine := False;
416
417 MarkupInfo.Foreground := clGreen;
418 MarkupInfo.Background := clNone;
419 MarkupInfo.Style := [];
420 MarkupInfo.StyleMask := [];
421 MarkupInfo.FrameEdges:= sfeLeft;
422
423 SetColorCount(6);
424 fMarkupColors[0].Foreground := clRed;
425 fMarkupColors[1].Foreground := $000098F7; //orange
426 fMarkupColors[2].Foreground := $0022CC40; //green
427 fMarkupColors[3].Foreground := $00CCCC00; //cyan
428 fMarkupColors[4].Foreground := $00FF682A; //blue
429 fMarkupColors[5].Foreground := $00CF00C4; //purple
430 end;
431
432 destructor TSynEditMarkupFoldColors.Destroy;
433 begin
434 if Lines <> nil then
435 Lines.Ranges[Self] := nil;
436 FColumnCache.Free;
437
438 ColorCount := 0;
439 if Assigned(Lines) then begin
440 Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
441 Lines.RemoveNotifyHandler(senrTextBufferChanged, @TextBufferChanged);
442 end;
443 FreeAndNil(fNestList);
444 FreeAndNil(fNestList2);
445 inherited Destroy;
446 end;
447
TSynEditMarkupFoldColors.RealEnablednull448 function TSynEditMarkupFoldColors.RealEnabled: Boolean;
449 begin
450 Result := (not IsTempDisabled) and Enabled and (FColorCount > 0);
451 end;
452
453 procedure TSynEditMarkupFoldColors.BeginMarkup;
454 begin
455 {$IFDEF SynEditMarkupFoldColoringDebug}
456 //DebugLn('BeginMarkup');
457 {$ENDIF}
458 inherited BeginMarkup;
459 if not Assigned(fHighlighter) then
460 exit;
461 fNestList.Clear; // for next markup start
462 fNestList2.Clear;
463 end;
464
TSynEditMarkupFoldColors.GetMarkupAttributeAtRowColnull465 function TSynEditMarkupFoldColors.GetMarkupAttributeAtRowCol(
466 const pRow: Integer; const pStartCol: TLazSynDisplayTokenBound;
467 const pRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
468 var
469 i, x2both: integer;
470 begin
471 Result := nil;
472 if not Assigned(fHighlighter) then exit;
473 if (fPreparedRow = pRow) then begin
474 {$IFDEF SynEditMarkupFoldColoringDebug}
475 //DebugLn(' GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
476 {$ENDIF}
477
478 x2both := 0;
479 for i := 0 to fFoldColorInfosCount - 1 do
480 with fFoldColorInfos[i] do
481 if not Ignore
482 and (PhysX < PhysX2)
483 and (ColorIdx >= 0)
484 and (pStartCol.Physical >= PhysX)
485 and (pStartCol.Physical < PhysX2) then begin
486 {$IFDEF SynEditMarkupFoldColoringDebug}
487 //DebugLn(' X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
488 {$ENDIF}
489
490 Result := MarkupInfo;
491 x2both := max(x2both, PhysX2);
492 if Border then begin
493 MarkupInfo.Clear;
494 MarkupInfo.SetFrameBoundsPhys(PhysX, x2both);
495 MarkupInfo.FrameAlpha := fLineColors[ColorIdx].Alpha;
496 MarkupInfo.FramePriority := fLineColors[ColorIdx].Priority;
497 MarkupInfo.FrameColor := fLineColors[ColorIdx].Color;
498 if MarkupInfo.FrameColor = clDefault then begin
499 if (fMarkupColors[ColorIdx].FrameColor <> clNone) and
500 (fMarkupColors[ColorIdx].FrameColor <> clDefault)
501 then
502 MarkupInfo.FrameColor := fMarkupColors[ColorIdx].FrameColor
503 else
504 MarkupInfo.FrameColor := fMarkupColors[ColorIdx].Foreground;
505 end;
506 MarkupInfo.FrameStyle := fLineColors[ColorIdx].Style;
507 MarkupInfo.FrameEdges := sfeLeft;
508 end else begin
509 MarkupInfo.Assign(fMarkupColors[ColorIdx]);
510 MarkupInfo.SetFrameBoundsPhys(PhysX, PhysX2);
511 end;
512 end;
513 end;
514 end;
515
516 procedure TSynEditMarkupFoldColors.GetNextMarkupColAfterRowCol(
517 const pRow: Integer; const pStartCol: TLazSynDisplayTokenBound;
518 const pRtlInfo: TLazSynDisplayRtlInfo; out pNextPhys, pNextLog: Integer);
519 var i : integer;
520 begin
521 {$IFDEF SynEditMarkupFoldColoringDebug}
522 //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
523 {$ENDIF}
524 if not Assigned(fHighlighter)
525 or (fPreparedRow <> pRow) then
526 exit;
527
528 pNextLog := -1;
529 pNextPhys := -1;
530 for i := 0 to fFoldColorInfosCount - 1 do
531 with fFoldColorInfos[i] do begin
532 if not Ignore and (ColorIdx >= 0) and
533 ( ((not Border) and fMarkupColors[ColorIdx].IsEnabled) or
534 (Border and (fLineColors[ColorIdx].Color <> clNone))
535 ) and
536 (PhysX < PhysX2) and (pStartCol.Physical < PhysX) and (pStartCol.Physical <= PhysX2)
537 then begin
538 pNextPhys := fFoldColorInfos[i].PhysX;
539 break;
540 end;
541 end;
542 end;
543
TSynEditMarkupFoldColors.GetFirstCharacterColumnnull544 function TSynEditMarkupFoldColors.GetFirstCharacterColumn(pIndex: Integer
545 ): TColumnCacheEntry;
546 var
547 l: String;
548 s, p: Integer;
549 begin
550 l := SynEdit.Lines[pIndex];
551 s := length(l);
552 p := 1;
553 while (p <= s)
554 //and (l[p] in [#9, #32, '/']) do inc(p);
555 and (l[p] in [#9, #32]) do inc(p);
556 if p > s then
557 exit(high(Result));
558 Result := TCustomSynEdit(SynEdit).LogicalToPhysicalPos(Point(p, toPos(pIndex))).x;
559 end;
560
561 procedure TSynEditMarkupFoldColors.TextBufferChanged(pSender: TObject);
562 begin
563 if pSender <> nil then
564 TSynEditStrings(pSender).Ranges[Self] := nil;
565
566 if not Enabled then
567 exit;
568 InitNestList;
569
570 FColumnCache.Capacity := SynEdit.Lines.Capacity;
571 FColumnCache.Count := SynEdit.Lines.Count;
572 Lines.Ranges[Self] := FColumnCache;
573
574 FColumnCache.Invalidate;
575 end;
576
577 procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(pRow: Integer);
578 var
579 lNodeCol: TColumnCacheEntry;
580 i, lLvl, lLineIdx, lCurIndex: Integer;
581 lCurNode: TSynFoldNodeInfo;
582
583 procedure AddVerticalLine;
584 begin
585 with fFoldColorInfos[lCurIndex] do begin
586 SrcNode:= lCurNode; //needed by close node
587 PhysCol := lNodeCol;
588 PhysX := lNodeCol;
589 PhysX2 := PhysX + 1;
590 Border := PhysX < GetFirstCharacterColumn(lLineIdx); // use real one here not cache
591 Ignore :=
592 (Border and (sfaOutlineNoLine in lCurNode.FoldAction))
593 or (not Border);
594 Level := lLvl;
595 ColorIdx := Max(0, lLvl) mod FColorCount;
596 {$IFDEF SynEditMarkupFoldColoringDebug}
597 //DebugLn(' %.5d %.2d %.2d-%.2d: %d - %s %.5d:%s', [Row, PhysCol, PhysX, PhysX2, Level, IfThen(sfaClose in SrcNode.FoldAction, 'C ', IfThen(sfaOpen in SrcNode.FoldAction, 'O ', '??')),ToPos(SrcNode.LineIndex),FoldTypeToStr(SrcNode.FoldType)]);
598 {$ENDIF}
599 end;
600 end;
601
602 procedure InitColumnForKeepLvl(LineIdx, FoldGroup: Integer);
603 var
604 LevelOpenNode: TSynFoldNodeInfo;
605 i, ONodeFirstCol: integer;
606 begin
607 ONodeFirstCol := FirstCharacterColumn[LineIdx];
608 FColumnCache[LineIdx] := ONodeFirstCol;
609
610 fNestList2.Line := LineIdx;
611 fNestList2.FoldGroup := FoldGroup;
612 if fNestList2.Count = 0 then
613 exit;
614 LevelOpenNode := fNestList2.HLNode[fNestList2.Count-1];
615 if sfaInvalid in LevelOpenNode.FoldAction then
616 exit; // try node before ??
617
618 if not FColumnCache.IsValidForLine[LevelOpenNode.LineIndex] then begin
619 if (LevelOpenNode.NestLvlStart < fHighlighter.FoldBlockEndLevel(LevelOpenNode.LineIndex-1, lCurNode.FoldGroup, [sfbIncludeDisabled])) and
620 (sfaCloseAndOpen in LevelOpenNode.FoldAction)
621 then
622 InitColumnForKeepLvl(LevelOpenNode.LineIndex, FoldGroup)
623 else
624 FColumnCache[LevelOpenNode.LineIndex] := FirstCharacterColumn[LevelOpenNode.LineIndex];
625 if not FColumnCache.IsValidForLine[LevelOpenNode.LineIndex] then
626 exit;
627 end;
628 i := FColumnCache[LevelOpenNode.LineIndex];
629 FColumnCache[LineIdx] := min(i, ONodeFirstCol);
630 end;
631
632 var
633 lKeepLevel: Boolean;
634 LastNode: TSynFoldNodeInfo;
635 cnf: TSynCustomFoldConfig;
636 cnfCnt, fType: Integer;
637 begin
638 lLineIdx := ToIdx(pRow);
639 fNestList.Line := lLineIdx;
640 fHighlighter.CurrentLines := Lines;
641 LastNode.LineIndex := -1;
642
643 // get first character for current line
644 if not FColumnCache.IsValidForLine[lLineIdx] then
645 FColumnCache[lLineIdx] := FirstCharacterColumn[lLineIdx];
646
647 lLvl := 0;
648 cnfCnt := TSynCustomFoldHighlighter(Highlighter).FoldConfigCount;
649 i := 0; // starting at the node with the lowest line number
650 while i < fNestList.Count do begin
651 fType := PtrInt(fNestList.NodeFoldType[i]);
652 if fType >= cnfCnt then begin
653 inc(i);
654 continue;
655 end;
656 cnf := TSynCustomFoldHighlighter(Highlighter).FoldConfig[fType];
657 if (not cnf.Enabled) or not(fmOutline in cnf.Modes) then begin
658 inc(i);
659 continue;
660 end;
661 lCurNode := fNestList.HLNode[i];
662 // sanity check
663 Assert(sfaOpen in lCurNode.FoldAction, 'no sfaOpen in lCurNode.FoldAction');
664 {$IFDEF SynEditMarkupFoldColoringDebug}
665 //DebugLn(' O: %s %s %s', [IfThen(sfaOutline in lCurNode.FoldAction, 'X', '-'), IfThen(sfaClose in lCurNode.FoldAction, 'C ', IfThen(sfaOpen in lCurNode.FoldAction, 'O ', '??')),FoldTypeToStr(lCurNode.FoldType)]);
666 {$ENDIF}
667 if (sfaOutline in lCurNode.FoldAction)
668 and not (sfaInvalid in lCurNode.FoldAction)
669 and (lCurNode.LineIndex <> lLineIdx) then begin
670
671 if ( sfaOutlineForceIndent in lCurNode.FoldAction) then
672 inc(lLvl)
673 else if ( sfaOutlineMergeParent in lCurNode.FoldAction) then
674 dec(lLvl);
675
676 // new FoldColorInfo
677 SetFoldColorInfosCount(fFoldColorInfosCount + 1);
678 lCurIndex := fFoldColorInfosCount - 1;
679
680 {$IFDEF SynEditMarkupFoldColoringDebug}
681 //if (fLastOpenNode.LineIndex >= 0) then
682 // DebugLn(' %s %s - %s %s', [FoldTypeToStr(fLastOpenNode.FoldType), IfThen(sfaOutlineKeepLevel in fLastOpenNode.FoldAction, '(Keep)', ''), FoldTypeToStr(lCurNode.FoldType), IfThen(sfaOutlineKeepLevel in lCurNode.FoldAction, '(Keep)', '')]);
683 {$ENDIF}
684
685
686 // find lastnode // first opening node on this line, that is = hl.line[-1].endnestlevel (-1)
687 if (lCurNode.NestLvlStart < fHighlighter.FoldBlockEndLevel(lCurNode.LineIndex-1, lCurNode.FoldGroup, [sfbIncludeDisabled])) and
688 (sfaCloseAndOpen in lCurNode.FoldAction)
689 // // TODO: check that this is the FIRST sfaCloseAndOpen on this line
690 then
691 InitColumnForKeepLvl(lCurNode.LineIndex, lCurNode.FoldGroup);
692
693 if not FColumnCache.IsValidForLine[lCurNode.LineIndex] then
694 FColumnCache[lCurNode.LineIndex] := FirstCharacterColumn[lCurNode.LineIndex];
695 lNodeCol := FColumnCache[lCurNode.LineIndex];
696
697 { do not keep level if two consecutive sfaOutlineKeepLevel nodes are
698 on different lines and start on different columns }
699 if (LastNode.LineIndex >= 0)
700 and (sfaOutlineKeepLevel in LastNode.FoldAction) then begin
701 {$IFDEF SynEditMarkupFoldColoringDebug}
702 //DebugLn(' %.5d/%.5d %.2d/%.2d', [LastNode.LineIndex+1,lCurNode.LineIndex+1, FFoldColorInfos[fLastIndex].PhysCol, lNodeCol]);
703 //DbgOut(' keep');
704 {$ENDIF}
705 lKeepLevel := True;
706 if (sfaOutlineKeepLevel in lCurNode.FoldAction)
707 and not (LastNode.LineIndex = lCurNode.LineIndex)
708 and not (fFoldColorInfos[fLastIndex].PhysCol = lNodeCol) then begin
709 {$IFDEF SynEditMarkupFoldColoringDebug}
710 //DbgOut(' not');
711 {$ENDIF}
712 inc(lLvl);
713 lKeepLevel := False;
714 end;
715 {$IFDEF SynEditMarkupFoldColoringDebug}
716 //DebugLn('');
717 {$ENDIF}
718 end else
719 lKeepLevel := False;
720
721 AddVerticalLine;
722
723 if lKeepLevel then begin
724 // keep level for none sfaOutlineKeepLevel after sfaOutlineKeepLevel
725 lLvl := fFoldColorInfos[fLastIndex].Level;
726 if fFoldColorInfos[fLastIndex].PhysX < fFoldColorInfos[lCurIndex].PhysX then
727 fFoldColorInfos[lCurIndex].PhysX := fFoldColorInfos[fLastIndex].PhysX;
728 fFoldColorInfos[lCurIndex].Level := lLvl;
729 fFoldColorInfos[lCurIndex].ColorIdx := Max(0, lLvl) mod FColorCount;
730 // overwrite first character column with new value
731 if fFoldColorInfos[fLastIndex].PhysX < FColumnCache[fFoldColorInfos[lCurIndex].SrcNode.LineIndex] then
732 FColumnCache[fFoldColorInfos[lCurIndex].SrcNode.LineIndex] := fFoldColorInfos[fLastIndex].PhysX;
733 {$IFDEF SynEditMarkupFoldColoringDebug}
734 //with FFoldColorInfos[lCurIndex] do
735 // DebugLn(' > > > %.2d %.2d-%.2d: %d - %s %.5d:%s', [PhysCol, PhysX, PhysX2, Level, IfThen(sfaClose in SrcNode.FoldAction, 'C ', IfThen(sfaOpen in SrcNode.FoldAction, 'O ', '??')),ToPos(SrcNode.LineIndex),FoldTypeToStr(SrcNode.FoldType)]);
736 {$ENDIF}
737 end;
738
739 if not (sfaOutlineKeepLevel in lCurNode.FoldAction) then
740 inc(lLvl);
741
742 LastNode := lCurNode;
743 fLastIndex := lCurIndex;
744 fLastOpenNode := lCurNode;
745 fLastOpenIndex := lCurIndex;
746
747 with fFoldColorInfos[fFoldColorInfosCount - 1] do begin
748 LevelAfter := lLvl; // used in DoMarkupParentCloseFoldAtRow
749 end;
750 end;
751 inc(i);
752 end;
753 end;
754
755 procedure TSynEditMarkupFoldColors.DoMarkupParentCloseFoldAtRow(pRow: Integer);
756 var
757 lMaxLevel, lvl, lCurIndex: Integer;
758 lCurNode: TSynFoldNodeInfo;
759 lKeepLevel: Boolean;
760 lNodeCol: TColumnCacheEntry;
761
AddHighlightnull762 function AddHighlight: Boolean;
763 var
764 lPhysX, lPhysX2, j: Integer;
765 begin
766 Result := False;
767 // ignore implicit close nodes at end of line, especially if line is empty
768 // or at least has less characters as vertical line is on
769 if not(sfaCloseForNextLine in lCurNode.FoldAction) then begin
770 Result := True;
771 lPhysX := TCustomSynEdit(SynEdit).LogicalToPhysicalPos(Point(ToPos(lCurNode.LogXStart), ToPos(lCurNode.LineIndex))).x;
772 lPhysX2 := TCustomSynEdit(SynEdit).LogicalToPhysicalPos(Point(ToPos(lCurNode.LogXEnd), ToPos(lCurNode.LineIndex))).x;
773 if lCurNode.LogXStart < lCurNode.LogXEnd then begin
774 {$IFDEF SynEditMarkupFoldColoringDebug}
775 //DebugLn(' %d < %d', [lCurNode.LogXStart, lCurNode.LogXEnd]);
776 {$ENDIF}
777 for j := 0 to fFoldColorInfosCount - 1 do
778 if (fFoldColorInfos[j].PhysX = lPhysX)
779 and (fFoldColorInfos[j].Border)
780 and (fFoldColorInfos[j].SrcNode.FoldType = lCurNode.FoldType )
781 and (fFoldColorInfos[j].SrcNode.FoldLvlEnd = lCurNode.FoldLvlStart ) then begin
782 {$IFDEF SynEditMarkupFoldColoringDebug}
783 //DebugLn(' X2: %d->%d', [FFoldColorInfos[j].X2, lCurNode.LogXEnd + 1]);
784 {$ENDIF}
785 fFoldColorInfos[j].PhysX2 := lPhysX2;
786 fFoldColorInfos[j].Border := False;
787 end;
788 end;
789
790 SetFoldColorInfosCount(fFoldColorInfosCount + 1);
791 lCurIndex := fFoldColorInfosCount - 1;
792 with fFoldColorInfos[lCurIndex] do begin
793 Ignore := False;
794 Border := False;
795 SrcNode:= lCurNode; //needed by close node
796 PhysX := lPhysX;
797 //if not FColumnCache.IsValidForLine[lCurNode.LineIndex] then
798 // FColumnCache[lCurNode.LineIndex] := FirstCharacterColumn[lCurNode.LineIndex];
799 PhysCol := lNodeCol; //FColumnCache[lCurNode.LineIndex];
800 PhysX2 := lPhysX2;
801 Level := lvl;
802 lMaxLevel := Max(lMaxLevel, lvl);
803 if not (sfaOutlineNoColor in lCurNode.FoldAction) then
804 ColorIdx := Max(0, lvl) mod FColorCount
805 else
806 ColorIdx := -1;
807
808 {$IFDEF SynEditMarkupFoldColoringDebug}
809 //DebugLn(' %.5d %.2d %.2d-%.2d: %d - %s %.5d:%s - %s', [Row, PhysCol, PhysX, PhysX2, Level, IfThen(sfaClose in SrcNode.FoldAction, 'C ', IfThen(sfaOpen in SrcNode.FoldAction, 'O ', '??')),ToPos(SrcNode.LineIndex),FoldTypeToStr(SrcNode.FoldType), IfThen(lKeepLevel, 'Keep', '')]);
810 {$ENDIF}
811 end;
812 end;
813 end;
814
815 var
816 lLineIdx,i,j,lvlA , k: integer;
817 lNodeList: TLazSynFoldNodeInfoList;
818
819 begin
820 lLineIdx := ToIdx(pRow);
821
822 // as all nodes will be on pRow we can set lNodeCol here already
823 if not FColumnCache.IsValidForLine[lLineIdx] then
824 FColumnCache[lLineIdx] := FirstCharacterColumn[lLineIdx];
825 lNodeCol := FColumnCache[lLineIdx];
826
827 fHighlighter.CurrentLines := Lines;
828
829 lNodeList := fHighlighter.FoldNodeInfo[lLineIdx];
830 lNodeList.ClearFilter; // only needed once, in case the line was already used
831 lNodeList.AddReference;
832 try
833 lNodeList.ActionFilter := [sfaOutline];
834 lvl := 0;
835 J := fFoldColorInfosCount - 1;
836 if J >=0 then
837 lvl := max(0,fFoldColorInfos[J].LevelAfter);
838 lMaxLevel := lvl;
839 i := 0;
840 repeat
841 lCurNode := lNodeList[i];
842 lCurIndex := fFoldColorInfosCount - 1;
843 // sanity check
844 Assert(lCurNode.LineIndex = lLineIdx, 'Node not on aRow');
845
846 {$IFDEF SynEditMarkupFoldColoringDebug}
847 //if not (sfaInvalid in lCurNode.FoldAction) then
848 // DebugLn(' C: %s %s %s', [IfThen(sfaOutline in lCurNode.FoldAction, 'X', '-'), IfThen(sfaClose in lCurNode.FoldAction, 'C ', IfThen(sfaOpen in lCurNode.FoldAction, 'O ', '??')),FoldTypeToStr(lCurNode.FoldType)]);
849 {$ENDIF}
850
851 if not (sfaInvalid in lCurNode.FoldAction)
852 and (sfaOutline in lCurNode.FoldAction) then begin
853 if sfaOpen in lCurNode.FoldAction then begin
854
855 if ( sfaOutlineForceIndent in lCurNode.FoldAction) then
856 inc(lvl)
857 else if ( sfaOutlineMergeParent in lCurNode.FoldAction) then
858 dec(lvl);
859
860 {$IFDEF SynEditMarkupFoldColoringDebug}
861 //if (fLastOpenNode.LineIndex >= 0) then
862 // DebugLn(' %s %s - %s %s', [FoldTypeToStr(fLastOpenNode.FoldType), IfThen(sfaOutlineKeepLevel in fLastOpenNode.FoldAction, '(Keep)', ''), FoldTypeToStr(lCurNode.FoldType), IfThen(sfaOutlineKeepLevel in lCurNode.FoldAction, '(Keep)', '')]);
863 {$ENDIF}
864
865 { do not keep level if two consecutive sfaOutlineKeepLevel nodes are
866 on different lines and start on different columns }
867 if (fLastOpenNode.LineIndex >= 0)
868 and (sfaOutlineKeepLevel in fLastOpenNode.FoldAction) then begin
869 {$IFDEF SynEditMarkupFoldColoringDebug}
870 //DebugLn(' keep');
871 {$ENDIF}
872 lKeepLevel := True;
873 if (sfaOutlineKeepLevel in lCurNode.FoldAction)
874 and not (fLastOpenNode.LineIndex = lLineIdx)
875 and not (fFoldColorInfos[fLastIndex].PhysCol = lNodeCol) then begin
876 inc(lvl);
877 lKeepLevel := False;
878 end;
879
880 end else
881 lKeepLevel := False;
882
883 if AddHighlight then begin
884 if lKeepLevel then begin
885 // overwrite first character column with new value
886 if fFoldColorInfos[fLastOpenIndex].PhysX < FColumnCache[fFoldColorInfos[lCurIndex].SrcNode.LineIndex] then begin
887 FColumnCache[fFoldColorInfos[lCurIndex].SrcNode.LineIndex] := fFoldColorInfos[fLastOpenIndex].PhysX;
888 Assert(fFoldColorInfos[lCurIndex].SrcNode.LineIndex = lLineIdx, 'fFoldColorInfos[lCurIndex].SrcNode.LineIndex <> lLineIdx');
889 lNodeCol := FColumnCache[lLineIdx]
890 end;
891 {$IFDEF SynEditMarkupFoldColoringDebug}
892 //with FFoldColorInfos[lCurIndex] do
893 // DebugLn(' > > > %.2d %.2d-%.2d: %d - %s %.5d:%s', [PhysCol, PhysX, PhysX2, Level, IfThen(sfaClose in SrcNode.FoldAction, 'C ', IfThen(sfaOpen in SrcNode.FoldAction, 'O ', '??')),ToPos(SrcNode.LineIndex),FoldTypeToStr(SrcNode.FoldType)]);
894 {$ENDIF}
895 end;
896
897 if not (sfaOutlineKeepLevel in lCurNode.FoldAction) then
898 inc(lvl);
899
900 lvlA := lvl;
901 fLastIndex := lCurIndex;
902 fLastOpenNode := lCurNode;
903 fLastOpenIndex := lCurIndex;
904
905 with fFoldColorInfos[fFoldColorInfosCount - 1] do begin
906 LevelAfter := lvlA;
907 end;
908 end;
909 end else if sfaClose in lCurNode.FoldAction then begin
910 lKeepLevel := False;
911 for j := fFoldColorInfosCount - 1 downto 0 do begin
912 with fFoldColorInfos[j].SrcNode do begin
913 if (FoldType = lCurNode.FoldType)
914 and (FoldGroup = lCurNode.FoldGroup)
915 and (sfaOpen in FoldAction)
916 and (NestLvlEnd = lCurNode.NestLvlStart) then begin
917 lvl := fFoldColorInfos[j].Level;
918 lvlA := fFoldColorInfos[j].LevelAfter;
919 if AddHighlight then begin
920 fLastIndex := lCurIndex;
921
922 with fFoldColorInfos[fFoldColorInfosCount - 1] do begin
923 LevelAfter := lvlA;
924 end;
925 // if found opening position is behind closing position:
926 // delete this as it does not have to be drawn
927 if fFoldColorInfos[j].PhysX > fFoldColorInfos[fFoldColorInfosCount - 1].PhysX then begin
928 for k := j to fFoldColorInfosCount - 1 - 1 do begin
929 fFoldColorInfos[k] := fFoldColorInfos[k+1];
930 end;
931 dec(fFoldColorInfosCount);
932 end;
933 end;
934 break;
935 end;
936 end;
937 end;
938 end;
939 end;
940 inc(i);
941 until i >= lNodeList.Count;
942 finally
943 lNodeList.ReleaseReference;
944 end;
945 end;
946
GetColornull947 function TSynEditMarkupFoldColors.GetColor(pIndex: Integer): TSynSelectedColor;
948 begin
949 Assert((pIndex >= 0) and (pIndex < FColorCount), 'Index out of range');
950 Result := fMarkupColors[pIndex];
951 end;
952
GetLineColornull953 function TSynEditMarkupFoldColors.GetLineColor(pIndex: Integer
954 ): TMarkupFoldColorsLineColor;
955 begin
956 Assert((pIndex >= 0) and (pIndex < FColorCount), 'Index out of range');
957 Result := fLineColors[pIndex];
958 end;
959
960 procedure TSynEditMarkupFoldColors.SetColorCount(AValue: Integer);
961 var
962 i: Integer;
963 begin
964 if FColorCount = AValue then Exit;
965
966 for i := AValue to FColorCount - 1 do begin
967 fMarkupColors[i].Free;
968 fLineColors[i].Free;
969 end;
970
971 SetLength(fMarkupColors, AValue);
972 SetLength(fLineColors, AValue);
973
974 for i := FColorCount to AValue - 1 do begin
975 fMarkupColors[i] := TSynSelectedColor.Create;
976 fMarkupColors[i].Clear;
977 fMarkupColors[i].OnChange := @ColorChanged;
978 fLineColors[i] := TMarkupFoldColorsLineColor.Create;
979 fLineColors[i].OnChange := @ColorChanged;
980 end;
981
982 FColorCount := AValue;
983 end;
984
985 procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(pRow: Integer);
986 var
987 i, lLastX, j: Integer;
988
989 begin
990 if not Assigned(fHighlighter)
991 and not (TCustomSynEdit(Self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
992 exit;
993
994 {$IFDEF SynEditMarkupFoldColoringDebug}
995 //DebugLn(#10'PrepareMarkupForRow %d', [aRow]);
996 {$ENDIF}
997
998 fPreparedRow := pRow;
999 fFoldColorInfosCount := 0; //reset needed to prevent using of invalid area
1000
1001 // invalidate LastNode
1002 fLastIndex := -1;
1003 fLastOpenNode.LineIndex := -1;
1004 fLastOpenIndex := -1;
1005
1006 {$IFDEF SynEditMarkupFoldColoringDebug}
1007 //DebugLn(' ----- DoMarkupParentFoldAtRow ------');
1008 {$ENDIF}
1009 DoMarkupParentFoldAtRow(pRow);
1010
1011 {$IFDEF SynEditMarkupFoldColoringDebug}
1012 //DebugLn(' --- DoMarkupParentCloseFoldAtRow ---');
1013 {$ENDIF}
1014 DoMarkupParentCloseFoldAtRow(pRow);
1015
1016 // delete parents with bigger x
1017 // to keep out mis indented blocks
1018 lLastX := MaxInt;
1019 for i := fFoldColorInfosCount - 1 downto 0 do begin
1020 if fFoldColorInfos[i].PhysX > lLastX then begin
1021 for j := i to length(fFoldColorInfos) - 2 do begin
1022 fFoldColorInfos[j] := fFoldColorInfos[j + 1];
1023 end;
1024 dec(fFoldColorInfosCount);
1025 end;
1026 lLastX := fFoldColorInfos[i].PhysX;
1027 end;
1028 {$IFDEF SynEditMarkupFoldColoringDebug}
1029 //DebugLn(' -------------- Final ---------------');
1030 //for i := 0 to FFoldColorInfosCount - 1 do with FFoldColorInfos[i] do begin
1031 // DebugLn(' %.5d %.2d %.2d-%.2d: %d - %s %.5d:%s - %d %s', [Row, PhysCol, PhysX, PhysX2, Level, IfThen(sfaClose in SrcNode.FoldAction, 'C ', IfThen(sfaOpen in SrcNode.FoldAction, 'O ', '??')),ToPos(SrcNode.LineIndex),FoldTypeToStr(SrcNode.FoldType), ColorIdx, IfThen(Ignore, 'Ignore', '')]);
1032 //end;
1033 {$ENDIF}
1034 end;
1035
1036 procedure TSynEditMarkupFoldColors.SetDefaultGroup(pValue: integer);
1037 begin
1038 if fDefaultGroup = pValue then Exit;
1039 fDefaultGroup := pValue;
1040 fNestList.FoldGroup := fDefaultGroup;
1041 end;
1042
1043 procedure TSynEditMarkupFoldColors.SetFoldColorInfosCount(pNewCount: Integer);
1044 begin
1045 if pNewCount > fFoldColorInfosCapacity then begin
1046 // expand array
1047 fFoldColorInfosCapacity := pNewCount + 49;
1048 SetLength(fFoldColorInfos, fFoldColorInfosCapacity);
1049 end;
1050 fFoldColorInfosCount := pNewCount;
1051 end;
1052
1053 procedure TSynEditMarkupFoldColors.InitNestList;
1054 begin
1055 if Assigned(fNestList) then
1056 fNestList.Lines := Lines;
1057 if Assigned(fNestList2) then
1058 fNestList2.Lines := Lines;
1059 end;
1060
1061 procedure TSynEditMarkupFoldColors.DoTextChanged(pStartLine, pEndLine, pCountDiff: Integer);
1062 var
1063 lNode: TSynFoldNodeInfo;
1064 lNodeIdx, lEndLine, lLineIdx, lBottomLine, lDecreaseCount, lOuterNodeIdx: Integer;
1065 nl: LongInt;
1066 x: TColumnCacheEntry;
1067 {$IFDEF SynEditMarkupFoldColoringDebug}
1068 t: QWord;
1069 {$ENDIF}
1070 begin
1071 if not Enabled then
1072 exit;
1073
1074 {$IFDEF SynEditMarkupFoldColoringDebug}
1075 //DebugLn(' DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
1076 {$ENDIF}
1077
1078 // lines available?
1079 if Lines.Count = 0 then
1080 exit;
1081
1082 // called by accident
1083 if pStartLine = 0 then
1084 exit;
1085
1086 // no TSynCustomFoldHighlighter
1087 if not Assigned(fHighlighter) then
1088 exit;
1089
1090 fHighlighter.CurrentLines := Lines;
1091 // highlighter still scanning
1092 if fHighlighter.NeedScan then
1093 exit;
1094
1095 {$IFDEF SynEditMarkupFoldColoringDebug}
1096 t := GetTickCount64;
1097 {$ENDIF}
1098
1099 if pEndLine < 0 then
1100 pEndLine := pStartLine
1101 else
1102 // pEndLine seems to be the first line after the change
1103 pEndLine := pEndLine - 1;
1104 lEndLine := pEndLine;
1105 FColumnCache[ToIdx(lEndLine)] := FirstCharacterColumn[ToIdx(lEndLine)];
1106 x := FColumnCache[ToIdx(lEndLine)];
1107 lBottomLine := TCustomSynEdit(SynEdit).TopLine + TCustomSynEdit(SynEdit).LinesInWindow;
1108
1109 fNestList.Clear;
1110 fNestList2.Clear;
1111 lLineIdx := ToIdx(pStartLine);
1112 fNestList.Line := lLineIdx;
1113 lNodeIdx := fNestList.Count - 1;
1114 lOuterNodeIdx := -1;
1115 if lNodeIdx >= 0 then begin
1116 lDecreaseCount := 2;
1117 while (lNodeIdx >= 0)
1118 and (lDecreaseCount > 0) do begin
1119 dec(lDecreaseCount);
1120 while lNodeIdx >= 0 do begin
1121 dec(lNodeIdx);
1122 lNode := fNestList.HLNode[lNodeIdx];
1123 if not (sfaInvalid in lNode.FoldAction)
1124 and (sfaOutline in lNode.FoldAction)
1125 and not (sfaOutlineKeepLevel in lNode.FoldAction)
1126 and not (
1127 (sfaOpen in lNode.FoldAction)
1128 and (lNode.LineIndex = lLineIdx)
1129 ) then begin
1130 if lNodeIdx >= 0 then
1131 lOuterNodeIdx := lNodeIdx;
1132 break;
1133 end;
1134 end;
1135 end;
1136 end;
1137 if (lOuterNodeIdx >= 0) then begin
1138 lEndLine := ToPos(fNestList.NodeEndLine[lOuterNodeIdx]);
1139 end else begin
1140 // if there is no outer Outline:
1141 // expand lEndline if x is left to FirstCharacterColumn;
1142 lLineIdx := ToIdx(lEndLine);
1143 while x <= FirstCharacterColumn[lLineIdx] do begin
1144 // if x (FirstCharacterColoum of pStartLine) is left or equal to
1145 // the FirstCharacterColumn of line lLineIdx
1146 // then the real pEndLine is at the pEndLine of the lines last node
1147 fNestList.Line := lLineIdx;
1148 if fNestList.Count = 0 then
1149 break;
1150 nl := fNestList.NodeEndLine[fNestList.Count - 1];
1151 if nl = lLineIdx then
1152 break;
1153 {$IFDEF SynEditMarkupFoldColoringDebug}
1154 DebugLn(' %d -> %d [%d/%d]', [lLineIdx, nl, x, FirstCharacterColumn[nl]]);
1155 {$ENDIF}
1156 lLineIdx := nl;
1157 end;
1158 lLineIdx := ToPos(lLineIdx);
1159 lEndLine := Max(lEndLine, lLineIdx);
1160 end;
1161
1162 // invalidate cache
1163 FColumnCache.LineTextChanged(ToIdx(pStartLine), Max(pEndLine, lEndLine) - pStartLine);
1164
1165 if lEndLine > pEndLine then begin
1166 {$IFDEF SynEditMarkupFoldColoringDebug}
1167 //DebugLn(' InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
1168 {$ENDIF}
1169 InvalidateSynLines(pEndLine + 1 , Min(lEndLine, lBottomLine));
1170 end;
1171
1172 {$IFDEF SynEditMarkupFoldColoringDebug}
1173 DebugLn('*** DoTextChanged %d-%d-%d-%d duration=%d', [pStartLine, pEndLine, lEndLine, lBottomLine, GetTickCount64 - t]);
1174 {$ENDIF}
1175
1176 end;
1177
1178 procedure TSynEditMarkupFoldColors.SetLines(const pValue: TSynEditStrings);
1179 var
1180 old: TSynEditStrings;
1181 begin
1182 if Lines <> nil then
1183 Lines.Ranges[Self] := nil;
1184 if Enabled then begin
1185 old := Lines;
1186 if Assigned(old)
1187 and (pValue <> old) then begin
1188 // change:
1189 // remove Changehandler
1190 old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
1191 old.RemoveNotifyHandler(senrTextBufferChanged, @TextBufferChanged);
1192 FColumnCache.Invalidate;
1193 end;
1194 end;
1195 inherited SetLines(pValue);
1196 if Enabled then begin
1197 if (pValue <> old) then begin
1198 // change:
1199 if Assigned(pValue) then begin
1200 // add Changehandler
1201 pValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
1202 pValue.AddNotifyHandler(senrTextBufferChanged, @TextBufferChanged);
1203 InitNestList;
1204 end else begin
1205 if Assigned(fNestList) then
1206 fNestList.Lines := nil;
1207 if Assigned(fNestList2) then
1208 fNestList2.Lines := nil;
1209 //DebugLn('*** SetLines');
1210 end;
1211 end;
1212 end;
1213 if (Lines <> nil) and Enabled then begin
1214 FColumnCache.Capacity := Lines.Capacity;
1215 FColumnCache.Count := Lines.Count;
1216 Lines.Ranges[Self] := FColumnCache;
1217 end;
1218 FColumnCache.Invalidate;
1219 end;
1220
1221 procedure TSynEditMarkupFoldColors.HighlightChanged(pSender: TSynEditStrings;
1222 pIndex, pCount: Integer);
1223 var
1224 newHighlighter: TSynCustomHighlighter;
1225 begin
1226 {$IFDEF SynEditMarkupFoldColoringDebug}
1227 //DebugLn(' HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
1228 {$ENDIF}
1229
1230 if (pIndex <> -1)
1231 or (pCount <> -1) then
1232 exit;
1233
1234 newHighlighter := TCustomSynEdit(self.SynEdit).Highlighter;
1235 if Assigned(newHighlighter)
1236 and not (newHighlighter is TSynCustomFoldHighlighter) then
1237 newHighlighter := nil;
1238
1239 if (newHighlighter = fHighlighter) then
1240 exit;
1241
1242 fHighlighter := TSynCustomFoldHighlighter(newHighlighter);
1243
1244 fNestList.HighLighter := fHighlighter;
1245 fNestList2.HighLighter := fHighlighter;
1246
1247 if not Enabled then
1248 exit;
1249
1250 FColumnCache.Invalidate;
1251 end;
1252
1253 procedure TSynEditMarkupFoldColors.DoEnabledChanged(pSender: TObject);
1254 begin
1255 if Enabled = fLastEnabled then
1256 exit;
1257 fLastEnabled := Enabled;
1258 if fLastEnabled then begin
1259 {$IFDEF SynEditMarkupFoldColoringDebug}
1260 //DebugLn(' *** TSynEditMarkupFoldColors Enabled');
1261 {$ENDIF}
1262 if Assigned(Lines) then begin
1263 // add Changehandler
1264 Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
1265 Lines.AddNotifyHandler(senrTextBufferChanged, @TextBufferChanged);
1266 InitNestList;
1267 end;
1268 end else begin
1269 {$IFDEF SynEditMarkupFoldColoringDebug}
1270 //DebugLn(' *** TSynEditMarkupFoldColors Disabled');
1271 {$ENDIF}
1272 if Assigned(Lines) then begin
1273 // remove Changehandler
1274 Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
1275 Lines.RemoveNotifyHandler(senrTextBufferChanged, @TextBufferChanged);
1276 end;
1277 end;
1278
1279 if Assigned(Lines) then begin
1280 if Enabled then begin
1281 FColumnCache.Capacity := Lines.Capacity;
1282 FColumnCache.Count := Lines.Count;
1283 Lines.Ranges[Self] := FColumnCache;
1284 FColumnCache.Invalidate;
1285 end
1286 else
1287 Lines.Ranges[Self] := nil;
1288
1289 InvalidateSynLines(1, Lines.Count);
1290 end;
1291 end;
1292
1293 procedure TSynEditMarkupFoldColors.ColorChanged(pMarkup: TObject);
1294 begin
1295 if Assigned(Lines) then
1296 InvalidateSynLines(1, Lines.Count);
1297 end;
1298
1299 end.
1300
1301
1302