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 Software distributed under the License is distributed on an "AS IS" basis,
7 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
8 the specific language governing rights and limitations under the License.
9 
10 The Original Code is: SynEditHighlighter.pas, released 2000-04-07.
11 
12 The Original Code is based on mwHighlighter.pas by Martin Waldenburg, part of
13 the mwEdit component suite.
14 Portions created by Martin Waldenburg are Copyright (C) 1998 Martin Waldenburg.
15 All Rights Reserved.
16 
17 Contributors to the SynEdit and mwEdit projects are listed in the
18 Contributors.txt file.
19 
20 $Id: synedithighlighter.pp 19051 2009-03-21 00:47:33Z martin $
21 
22 You may retrieve the latest version of this file at the SynEdit home page,
23 located at http://SynEdit.SourceForge.net
24 
25 -------------------------------------------------------------------------------}
26 
27 (* Naming Conventions:
28    -  FoldBlock:
29      A continuous range of lines, that can (optional) be folded.
30      Which Foldblocks can be folded is decided by the Highlighter. It may be
31      configurable.
32      A Foldblock can contain other Foldbloccks (nested), but two Foldblocks can
33      not overlap.
34    -  FoldBlockLevel (FoldBlockNestLevel):
35      The amount of FoldBlocks in which a line (or a point of text) is.
36    -  FoldGroup:
37      An independent set of FoldBlocks. FoldBlocks in different Groups may overlap.
38      (e.g. IFDEF/REGION in the SynPasSyn allow for overlaps, rather than strict nesting)
39      Some older code use "FoldType" instead
40    -  FoldNode
41      Start or End of a FoldBlock
42 *)
43 
44 (* TODO : Workaround for bug #20850
45    Remove when FPC 2.6.2 is out
46 *)
47 {$IFDEF CPU64}
48 {$IF (FPC_FULLVERSION = 20600) or (FPC_FULLVERSION = 20501)}
49   {$DEFINE ISSUE_20850 }
50 {$ENDIF}
51 {$ENDIF}
52 
53 unit SynEditHighlighterFoldBase;
54 
55 {$I synedit.inc}
56 
57 interface
58 
59 uses
60   SysUtils, Classes, math, Laz_AVL_Tree,
61   // LCL
62   LCLProc,
63   // LazUtils
64   LazClasses, LazLoggerBase,
65   // SynEdit
66   SynEditHighlighter, SynEditTypes, LazSynEditText;
67 
68 const
69   NullRange = TSynEditRange(nil);
70 
71 type
72 
73   TSynFoldAction = ( sfaOpen,         // Any Opening node
74                      sfaClose,        // Any Closing node
75 
76                      sfaFold,         // Part of a fold- or hide-able block (FoldConf.Enabled = True)           - excludes one=liners for FoldFold, as they can not fold
77                      sfaFoldFold,     // Part of a fold-able block (FoldConf.Enabled = True / smFold in Modes)  - excludes one=liners / only opening node, except ifdef/region (todo: maybe both?)
78                      sfaFoldHide,     // Part of a hide-able block (FoldConf.Enabled = True / smHide in Modes)  - includes one=liners / only opening node, except ifdef/region (todo: maybe both?)
79 
80                      sfaMultiLine,    // The closing node is on an other line
81                      sfaSingleLine,   // The closing node is on the same line (though the keyword may be on the next)
82                      // //sfaSingleLineClosedByNext
83                      sfaCloseForNextLine,  // Fold closes this line, but keyword is on the next (e.g. "var" block)
84                      sfaLastLineClose,     // Fold is incomplete, and closed at last line of file
85                      sfaCloseAndOpen,    // This node has the same location/type as the neighbouring opposite node.
86                                          // eg an open node, matche exactly the previous node, which has to be a closing node of the same type and location (and vice versa for a closing node matching the next...)
87 
88                      sfaDefaultCollapsed,
89                      sfaMarkup,   // This node can be highlighted, by the matching Word-Pair Markup
90                      sfaOutline,  // This node will be higlighted by nested color replacing the token color
91                      sfaOutlineKeepLevel, // Direct children should not increase color dept. (But grandchild can.)  e.g. "if","then" any "procedure"
92                      sfaOutlineMergeParent,// This node want to decrease current color depth. (But Previous sibling increased) e.g. "except", "finally"
93                      sfaOutlineForceIndent, // Node will temporary ignore sfaOutlineKeep. (Next sibling can.) e.g in NESTED "procedure"
94 // TODO: review sfaOutlineNoColor / see issue 0034410
95                      sfaOutlineNoColor,     // Node will not painted by nested-coloring, but may increase color (e.g. any "procedure")
96                      sfaOutlineNoLine,      // Node doesn't want to have vertical line. (e.g. "then")
97                      sfaInvalid,  // Wrong Index
98 
99                      // TODO: deprecate
100                      sfaOpenFold,     // At this node a new Fold can start // Actually, includes all,any multiline node too.
101                      sfaCloseFold,    // At this node a fold ends
102                      sfaOneLineOpen,   // Open, but closes on same line; *only* if hide-able has [sfaOpenFold, sfaFold]; always has [sfaFoldFold, sfaFoldHide]
103                      sfaOneLineClose  // Open, but closes on same line;
104                    );
105   TSynFoldActions = set of TSynFoldAction;
106 
107   (* TSynFoldBlockFilter
108      used to specify which folds to include for:
109      - FoldOpenCount, FoldCloseCount, FoldNestCount
110      - maybe in future TLazSynFoldNodeInfoList
111        TLazSynFoldNodeInfoList has additional filters
112        TLazSynFoldNodeInfoList always uses the full set (sfbIncludeDisabled)
113 
114      A Highlighter is not required to implement this, or can choose to implement
115      a subset only. For any field/value a Highlighter may simple assume default.
116      - Highlighter that have only one "FoldGroup" do not require this.
117      - Highlighter that do not store foldblocks that are unavailable (e.g. off by
118        config) always return the same set
119 
120      Using a record, as argument is the virtual methods, allows one to add further
121      fields/values, without breaking inheritance.
122      New fields values are expected to be ignored (handled as default) by existing
123      highlighter.
124 
125      Callers of the method can:
126      - use InitFoldBlockFilter to make sure all fields are set to default
127      - use (none virtual) wrapper methods
128   *)
129   TSynFoldBlockFilterFlag = (
130     sfbIncludeDisabled // Foldable by config = off
131   );
132   TSynFoldBlockFilterFlags = set of TSynFoldBlockFilterFlag;
133   TSynFoldBlockFilter = record
134     FoldGroup: integer;
135     Flags: TSynFoldBlockFilterFlags;
136   end;
137 
138 procedure InitFoldBlockFilter(out AFilter: TSynFoldBlockFilter;
139                               AFoldGroup: Integer = 0; AFlag: TSynFoldBlockFilterFlags = []);
140 
141 type
142   TSynCustomFoldHighlighter = class;
143 
144   TSynFoldNodeInfo = record
145     LineIndex: Integer;
146     NodeIndex: Integer;          // Indicates the position within the list of info nodes (depends on search-Filter)
147     AllNodeIndex: Integer;       // Indicates the position within the unfiltered list of info nodes
148     LogXStart, LogXEnd: Integer; // -1 previous line ( 0-based)
149     FoldLvlStart, FoldLvlEnd: Integer; // FoldLvl within each FoldGroup
150     NestLvlStart, NestLvlEnd: Integer; // include disabled nodes, e.g markup (within each FoldGroup)
151     FoldAction: TSynFoldActions;
152     FoldType: Pointer;           // e.g.cfbtBeginEnd, cfbtProcedure ...
153     FoldTypeCompatible: Pointer; // map outer and inner begin, and other exchangeable types
154     FoldGroup: Integer;          // independend/overlapping folds, e.g begin/end; ifdef, region
155   end;
156   PSynFoldNodeInfo = ^TSynFoldNodeInfo;
157 
158   { TLazSynFoldNodeInfoList }
159 
160   TLazSynFoldNodeInfoList = class(TRefCountedObject)
161   private
162     FHighLighter: TSynCustomFoldHighlighter;
163     FValid: Boolean;
164     FActionFilter: TSynFoldActions;
165     FGroupFilter: Integer;
166     FLine: TLineIdx;
167     FNodeCount: Integer;
168     FFilteredCount, FFilteredProgress: Integer;
169     FNodeInfoList: Array of TSynFoldNodeInfo;
170     FFilteredList: Array of TSynFoldNodeInfo;
171     function  GetItem(Index: Integer): TSynFoldNodeInfo;
172     procedure SetActionFilter(AValue: TSynFoldActions);
173     procedure SetGroupFilter(AValue: Integer);
174     function  GetItemPointer(AnIndex: Integer): PSynFoldNodeInfo;
175     function  GetLastItemPointer: PSynFoldNodeInfo;
176   protected
177     procedure Invalidate;
178     procedure Clear;
179     procedure ClearData;
180     procedure ClearFilteredList;
181     procedure DoFilter(MinIndex: Integer = -1);
182     procedure SetLine(ALine: TLineIdx); // Does not clear anything, if line has not changed.
183     procedure SetLineClean(ALine: TLineIdx); // Does not clear anything, if line has not changed.
184     property  HighLighter: TSynCustomFoldHighlighter read FHighLighter write FHighLighter;
185   public
186     // used by HighLighters to add data
187     procedure Add(const AnInfo: TSynFoldNodeInfo);
188     procedure Delete(AnIndex: Integer = -1);
189     function  CountAll: Integer;
190     property  ItemPointer[AnIndex: Integer]: PSynFoldNodeInfo read GetItemPointer;
191     property  LastItemPointer: PSynFoldNodeInfo read GetLastItemPointer;
192   protected
193     function  DefaultGroup: Integer; virtual;
194     function  MinCapacity: Integer; virtual;
195     procedure InvalidateNode(out AnInfo: TSynFoldNodeInfo);
196     function  Match(const AnInfo: TSynFoldNodeInfo;
197                     AnActionFilter: TSynFoldActions; AGroupFilter: Integer = 0): Boolean; virtual;
198   public
199     // filtered items
200     procedure ClearFilter;
201     function Count: Integer;
202     property Item[Index: Integer]: TSynFoldNodeInfo read GetItem; default;
203     property ActionFilter: TSynFoldActions read FActionFilter write SetActionFilter;
204     property GroupFilter: Integer read FGroupFilter write SetGroupFilter;
205   public
206     // all items / filtered on the fly
207     function CountEx   (AnActionFilter: TSynFoldActions; AGroupFilter: Integer = 0): Integer;
208     function NodeInfoEx(Index: Integer; AnActionFilter: TSynFoldActions; AGroupFilter: Integer = 0): TSynFoldNodeInfo; virtual;
209   public
210     // Only allowed to be set, if highlighter has CurrentLines (and is scanned)
211     property Line: TLineIdx read FLine write SetLine;
212   end;
213 
214   (* TLazSynEditNestedFoldsList
215      Provides Info on all foldable-blocks containing a given line (0 based index).
216      That are:
217      - All foldable blocks opening on a previous line, that are still open
218        at the start of the line. (May end on this line or later)
219      - Foldable blocks opening on that line. (OpeningOnLineCount)
220 
221      The data is NOT automatically invalidated.
222   *)
223 
224   TLazSynEditNestedFoldsListEntry = record
225     FFLags: set of (nfeHasHNode, nfeMaxPrevReached);
226     FGroupMinLevels: Array of Integer;
227     //OpenCount: Integer;
228     LineIdx: TLineIdx;
229     EndLineIdx: TLineIdx;
230     HNode: TSynFoldNodeInfo;    // Highlighter Node
231     //FNode: TSynTextFoldAVLNode; // AvlFoldNode
232     PrevNodeAtSameLevel: array of TLazSynEditNestedFoldsListEntry; // Only for same NodeGroup
233   end;
234 
235 //  TSynGetHighLighter = function(): TSynCustomFoldHighlighter of object;
236 
237   TLazSynEditNestedFoldsList = class
238   { $Define DebugTLazSynEditNestedFoldsList}
239   // TODO: in all methods: get "FoldNodeInfo" from FoldProvider, instead of Highlighter
240   private
241     FLines : TSynEditStrings;
242     FHighLighter: TSynCustomFoldHighlighter;
243     FFoldGroup: Integer;
244     FLine: TLineIdx;
245     procedure SetFoldGroup(AValue: Integer);
246     procedure SetLine(AValue: TLineIdx);
247   private
248     FFoldFlags: TSynFoldBlockFilterFlags;
249     FGroupCount: Integer;
250     FGroupEndLevelsAtEval: Array of integer;
251     FCount, FOpeningOnLineCount: Integer;
252     FOpeningLineEndIndex: Integer;
253     FIncludeOpeningOnLine: Boolean;
254     FNestInfo, FOnLineNestInfo: Array of TLazSynEditNestedFoldsListEntry;
255     FEvaluationIndex: Integer;
256 
257     FPreviousNestInfo: Array of TLazSynEditNestedFoldsListEntry;
258     FPreviousLine, FPreviousEvaluationIndex, FPreviousCount: Integer;
259     FPreviousMergeLine: Integer;
260 
261     FFoldNodeInfoList: TLazSynFoldNodeInfoList;
262     FFoldNodeInfoListHoldCnt: integer;
263 
264     function GetHLNode(Index: Integer): TSynFoldNodeInfo;
265     function GetNodeEndLine(Index: Integer): Integer;
266     function GetNodeFoldGroup(Index: Integer): Integer;
267     function GetNodeLine(Index: Integer): Integer;
268     function GetNodeFoldType(Index: Integer): Pointer;
269     function GetNodeLineEx(Index, PrevCount: Integer): Integer;
270     procedure InitSubGroupEndLevels;
271     procedure InitNestInfoForIndex(AnIndex: Integer);
272     procedure InitLineInfoForIndex(AnIndex: Integer);
273     procedure InitCount;
274     procedure InitOpeningOnLine;
275     procedure SetFoldFlags(AValue: TSynFoldBlockFilterFlags);
276     procedure SetHighLighter(AValue: TSynCustomFoldHighlighter);
277     procedure SetIncludeOpeningOnLine(AValue: Boolean);
278     procedure AquireFoldNodeInfoList(const ALine: Integer = -1);
279     procedure ReleaseFoldNodeInfoList;
280     procedure SetLines(AValue: TSynEditStrings);
281     procedure SetOpeningLineEndIndex(AValue: Integer);
282     function HasCount: Boolean;
283     procedure ClearPreviousCache;
284   public
285     constructor Create(ALines : TSynEditStrings; AnHighLighter: TSynCustomFoldHighlighter = nil);
286     procedure Clear;
287     procedure ResetFilter;
288     function Count: Integer;
289     function OpeningOnLineCount: Integer;  // ignores FFoldFlags
290     procedure Debug;
291     property Line: TLineIdx read FLine write SetLine;
292     property FoldGroup: Integer read FFoldGroup write SetFoldGroup;
293     property FoldFlags: TSynFoldBlockFilterFlags read FFoldFlags write SetFoldFlags;
294     property IncludeOpeningOnLine: Boolean read FIncludeOpeningOnLine write SetIncludeOpeningOnLine;
295     // OpeningLineEnd... can only be used with sfbIncludeDisabled
296     // Highest included index (unfiltered index)
297     property OpeningLineEndIndex: Integer read FOpeningLineEndIndex write SetOpeningLineEndIndex;
298     //property OpeningLineEndLogicalPos: Integer read FOpeningLineEndLogicalPos write SetOpeningLineEndLogicalPos;
299     property Lines: TSynEditStrings read FLines write SetLines;
300     property HighLighter: TSynCustomFoldHighlighter read FHighLighter write SetHighLighter;
301   public
302     property HLNode[Index: Integer]: TSynFoldNodeInfo read GetHLNode;
303     property NodeFoldType[Index: Integer]: Pointer read GetNodeFoldType;        // e.g.cfbtBeginEnd, cfbtcfbtProcedure ...
304     property NodeFoldGroup[Index: Integer]: Integer read GetNodeFoldGroup;      // independend/overlapping folds, e.g begin/end; ifdef, region
305     property NodeLine[Index: Integer]: Integer read GetNodeLine;                // Index
306     property NodeEndLine[Index: Integer]: Integer read GetNodeEndLine;          // Index
307     property NodeLineEx[Index, PrevCount: Integer]: Integer read GetNodeLineEx; // Index
308   end;
309 
310   TSynCustomFoldConfigMode = (fmFold, fmHide, fmMarkup, fmOutline);
311   TSynCustomFoldConfigModes = set of TSynCustomFoldConfigMode;
312 
313   { TSynCustomFoldConfig }
314 
315   TSynCustomFoldConfig = class(TPersistent)
316   private
317     FEnabled: Boolean;
318     FFoldActions: TSynFoldActions;
319     FIsEssential: boolean;
320     FModes: TSynCustomFoldConfigModes;
321     FOnChange: TNotifyEvent;
322     FSupportedModes: TSynCustomFoldConfigModes;
323     procedure SetEnabled(const AValue: Boolean);
324     procedure SetModes(AValue: TSynCustomFoldConfigModes);
325     procedure SetSupportedModes(AValue: TSynCustomFoldConfigModes); deprecated 'use create';
326   protected
327     procedure DoOnChange;
328   public
329     constructor Create;
330     constructor Create(ASupportedModes: TSynCustomFoldConfigModes; AnIsEssential: Boolean = False);
331     procedure Assign(Src: TSynCustomFoldConfig); reintroduce; virtual; // TODO: do not copy supported modes
332     property OnChange: TNotifyEvent read FOnChange write FOnChange;
333     property IsEssential: boolean read FIsEssential;   // create node, even if disabled
334     property SupportedModes: TSynCustomFoldConfigModes
335              read FSupportedModes write SetSupportedModes;
336     // Actions representing the modes
337     property FoldActions: TSynFoldActions read FFoldActions;
338   published
339     property Enabled: Boolean read FEnabled write SetEnabled;
340     property Modes: TSynCustomFoldConfigModes read FModes write SetModes default [fmFold];
341   end;
342 
343   { TSynCustomCodeFoldBlock }
344 
345   TSynCustomCodeFoldBlock = class
346   private
347     FBlockType: Pointer;
348     FParent, FChildren: TSynCustomCodeFoldBlock;
349     FRight, FLeft: TSynCustomCodeFoldBlock;
350     FBalance: Integer;
351     function GetChild(ABlockType: Pointer): TSynCustomCodeFoldBlock;
352   protected
353     function GetOrCreateSibling(ABlockType: Pointer): TSynCustomCodeFoldBlock;
354     property Right: TSynCustomCodeFoldBlock read FRight;
355     property Left: TSynCustomCodeFoldBlock read FLeft;
356     property Children: TSynCustomCodeFoldBlock read FChildren;
357   public
358     destructor Destroy; override;
359     procedure WriteDebugReport;
360   public
361     procedure InitRootBlockType(AType: Pointer);
362     property BlockType: Pointer read FBlockType;
363     property Parent: TSynCustomCodeFoldBlock read FParent;
364     property Child[ABlockType: Pointer]: TSynCustomCodeFoldBlock read GetChild;
365   end;
366 
367   { TSynCustomHighlighterRange }
368 
369   TSynCustomHighlighterRange = class
370   private
371     // TODO: either reduce to one level, or create subclass for 2nd level
372     FCodeFoldStackSize: integer; // EndLevel
373     FNestFoldStackSize: integer; // EndLevel
374     FMinimumCodeFoldBlockLevel: integer;
375     FMinimumNestFoldBlockLevel: integer;
376     FRangeType: Pointer;
377     FTop: TSynCustomCodeFoldBlock;
378   public
379     constructor Create(Template: TSynCustomHighlighterRange); virtual;
380     destructor Destroy; override;
381     function Compare(Range: TSynCustomHighlighterRange): integer; virtual;
382     function Add(ABlockType: Pointer = nil; IncreaseLevel: Boolean = True):
383         TSynCustomCodeFoldBlock; virtual;
384     procedure Pop(DecreaseLevel: Boolean = True); virtual;
385     function MaxFoldLevel: Integer; virtual;
386     procedure Clear; virtual;
387     procedure Assign(Src: TSynCustomHighlighterRange); virtual;
388     procedure WriteDebugReport;
389     property FoldRoot: TSynCustomCodeFoldBlock read FTop write FTop;
390   public
391     property RangeType: Pointer read FRangeType write FRangeType;
392     property CodeFoldStackSize: integer read FCodeFoldStackSize; // excl disabled, only IncreaseLevel
393     property MinimumCodeFoldBlockLevel: integer
394       read FMinimumCodeFoldBlockLevel write FMinimumCodeFoldBlockLevel;
395     property NestFoldStackSize: integer read FNestFoldStackSize; // all, incl disabled (not IncreaseLevel)
396     property MinimumNestFoldBlockLevel: integer
397       read FMinimumNestFoldBlockLevel; // write FMinimumNestFoldBlockLevel;
398     property Top: TSynCustomCodeFoldBlock read FTop;
399   end;
400   TSynCustomHighlighterRangeClass = class of TSynCustomHighlighterRange;
401 
402   TSynCustomHighlighterRanges = class;
403 
404   { TSynCustomFoldHighlighter }
405 
406   TSynCustomFoldHighlighter = class(TSynCustomHighlighter)
407   protected
408     // Fold Config
409     FFoldConfig: Array of TSynCustomFoldConfig;
410     function GetFoldConfig(Index: Integer): TSynCustomFoldConfig; virtual;
411     procedure SetFoldConfig(Index: Integer; const AValue: TSynCustomFoldConfig); virtual;
412     function GetFoldConfigCount: Integer; virtual;
413     function GetFoldConfigInternalCount: Integer; virtual;
414     function CreateFoldConfigInstance(Index: Integer): TSynCustomFoldConfig; virtual;
415     function GetFoldConfigInstance(Index: Integer): TSynCustomFoldConfig; virtual;
416     procedure InitFoldConfig;
417     procedure DestroyFoldConfig;
418     procedure DoFoldConfigChanged(Sender: TObject); virtual;
419   private
420     FCodeFoldRange: TSynCustomHighlighterRange;
421     FIsCollectingNodeInfo: boolean;
422     fRanges: TSynCustomHighlighterRanges;
423     FRootCodeFoldBlock: TSynCustomCodeFoldBlock;
424     FFoldNodeInfoList: TLazSynFoldNodeInfoList;
425     FCollectingNodeInfoList: TLazSynFoldNodeInfoList;
426     procedure ClearFoldNodeList;
427   protected
428     // "Range"
429     function GetRangeClass: TSynCustomHighlighterRangeClass; virtual;
430     procedure CreateRootCodeFoldBlock; virtual; // set RootCodeFoldBlock
431     property CodeFoldRange: TSynCustomHighlighterRange read FCodeFoldRange;
432     function TopCodeFoldBlockType(DownIndex: Integer = 0): Pointer;
433     property RootCodeFoldBlock: TSynCustomCodeFoldBlock read FRootCodeFoldBlock
434       write FRootCodeFoldBlock;
435 
436     // Open/Close Folds
437     function StartCodeFoldBlock(ABlockType: Pointer = nil;
438               IncreaseLevel: Boolean = true; ForceDisabled: Boolean = False): TSynCustomCodeFoldBlock; virtual;
439     procedure EndCodeFoldBlock(DecreaseLevel: Boolean = True); virtual;
440     procedure CollectNodeInfo(FinishingABlock : Boolean; ABlockType: Pointer;
441               LevelChanged: Boolean); virtual;
442     procedure DoInitNode(var Node: TSynFoldNodeInfo;
443                        FinishingABlock: Boolean;
444                        ABlockType: Pointer; aActions: TSynFoldActions;
445                        AIsFold: Boolean); virtual;
446     procedure RepairSingleLineNode(var Node: TSynFoldNodeInfo); virtual;
447     procedure GetTokenBounds(out LogX1,LogX2: Integer); virtual;
448 
449     // Info about Folds
450     function CreateFoldNodeInfoList: TLazSynFoldNodeInfoList; virtual;
451     function GetFoldNodeInfo(Line: TLineIdx): TLazSynFoldNodeInfoList;
452     procedure ScanFoldNodeInfo(); virtual;
453     procedure InitFoldNodeInfo(AList: TLazSynFoldNodeInfoList; Line: TLineIdx);
454 
455     // Info about Folds, on currently set line/range (simply forwarding to range
456     function MinimumCodeFoldBlockLevel: integer; virtual;
457     function CurrentCodeFoldBlockLevel: integer; virtual;
458 
459     property IsCollectingNodeInfo : boolean read FIsCollectingNodeInfo;
460     property CollectingNodeInfoList : TLazSynFoldNodeInfoList read FCollectingNodeInfoList;
461   public
462     constructor Create(AOwner: TComponent); override;
463     destructor Destroy; override;
464     class function GetCapabilities: TSynHighlighterCapabilities; override;
465     function GetRange: Pointer; override;
466 
467     // Info about Folds
468     function FoldBlockOpeningCount(ALineIndex: TLineIdx;
469                                    const AFilter: TSynFoldBlockFilter): integer; virtual; overload;
470     function FoldBlockClosingCount(ALineIndex: TLineIdx;
471                                    const AFilter: TSynFoldBlockFilter): integer; virtual; overload;
472     function FoldBlockEndLevel(ALineIndex: TLineIdx;
473                                const AFilter: TSynFoldBlockFilter): integer; virtual; overload;
474     function FoldBlockMinLevel(ALineIndex: TLineIdx;
475                                const AFilter: TSynFoldBlockFilter): integer; virtual; overload;
476     (* All nested FoldType (cfbtBegin) if available. Similar to TopCodeFoldBlockType
477        - Index=0 is most outer / Index=FoldBlockEndLevel is most inner (TopCodeFoldBlockType 0=inner)
478        - False, if it can not be determined for the filter settings
479     *)
480     function FoldBlockNestedTypes(ALineIndex: TLineIdx; ANestIndex: Integer; out AType: Pointer;
481                                   const AFilter: TSynFoldBlockFilter): boolean; virtual; overload;
482 
483     function FoldBlockOpeningCount(ALineIndex: TLineIdx; AFoldGroup: integer = 0;
484                                    AFlags: TSynFoldBlockFilterFlags = []): integer; overload;
485     function FoldBlockClosingCount(ALineIndex: TLineIdx; AFoldGroup: integer = 0;
486                                    AFlags: TSynFoldBlockFilterFlags = []): integer; overload;
487     function FoldBlockEndLevel(ALineIndex: TLineIdx; AFoldGroup: integer = 0;
488                                AFlags: TSynFoldBlockFilterFlags = []): integer; overload;
489     function FoldBlockMinLevel(ALineIndex: TLineIdx; AFoldGroup: integer = 0;
490                                AFlags: TSynFoldBlockFilterFlags = []): integer; overload;
491     function FoldBlockNestedTypes(ALineIndex: TLineIdx; ANestIndex: Integer; out AType: Pointer;
492                                   AFoldGroup: integer = 0;
493                                   AFlags: TSynFoldBlockFilterFlags = []): boolean; virtual; overload;
494 
495     function FoldOpenCount(ALineIndex: Integer; AType: Integer = 0): integer;  deprecated;
496     function FoldCloseCount(ALineIndex: Integer; AType: Integer = 0): integer; deprecated;
497     function FoldNestCount(ALineIndex: Integer; AType: Integer = 0): integer; deprecated;
498 
499     function FoldTypeCount: integer; virtual;
500     function FoldTypeAtNodeIndex(ALineIndex, FoldIndex: Integer;
501              UseCloseNodes: boolean = false): integer; virtual; // TODO: could be deprecated ./ only child-classes
502 
503     function FindNextLineWithMinFoldLevel(ALineIndex: TLineIdx; ASearchLevel: Integer;
504                                const AFilter: TSynFoldBlockFilter): integer; virtual; overload;
505     function FindNextLineWithMinFoldLevel(ALineIndex: TLineIdx; ASearchLevel: Integer;
506                                AFoldGroup: integer = 0; AFlags: TSynFoldBlockFilterFlags = []): integer; overload;
507 
508     function FoldEndLine(ALineIndex, FoldIndex: Integer): integer; virtual; overload; // deprecate // fix inherited classes
509     function FoldEndLine(ALineIndex, FoldIndex: Integer;
510                          const AFilter: TSynFoldBlockFilter): integer; virtual; overload;
511     function FoldEndLine(ALineIndex, FoldIndex: Integer;
512                          AFoldGroup: integer; AFlags: TSynFoldBlockFilterFlags): integer; overload;
513 //                         AFoldGroup: integer = 0; AFlags: TSynFoldBlockFilterFlags = []): integer; overload;
514 
515     function FoldLineLength(ALineIndex, FoldIndex: Integer): integer; virtual;  // only for group 0 // may be one less than FoldEndLine, if end line is a mixed end-begin
516 
517     // All fold-nodes
518     // FoldNodeInfo: Returns a shared object
519     // Adding RefCount, will prevent others from getting further copies, but not from using copies they already have.
520     // If not adding refcount, the object should not be stored/re-used
521     // Not adding ref-count, should only be done for CountEx, NodeInfoEx
522     property FoldNodeInfo[Line: TLineIdx]: TLazSynFoldNodeInfoList read GetFoldNodeInfo;
523 
524     procedure SetRange(Value: Pointer); override;
525     procedure ResetRange; override;
526     procedure SetLine(const NewValue: String;
527                       LineNumber:Integer // 0 based
528                       ); override;
529     procedure DoCurrentLinesChanged; override;
530     function PerformScan(StartIndex, EndIndex: Integer; ForceEndIndex: Boolean =
531       False): Integer; override;
532   public
533     property FoldConfig[Index: Integer]: TSynCustomFoldConfig
534       read GetFoldConfig write SetFoldConfig;
535     property FoldConfigCount: Integer read GetFoldConfigCount;
536 
537   end;
538 
539   { TSynCustomHighlighterRanges }
540 
541   TSynCustomHighlighterRanges = class
542   private
543     FAllocatedCount: integer;
544     FHighlighterClass: TSynCustomHighlighterClass;
545     FItems: TAvlTree;
546   public
547     constructor Create(TheHighlighterClass: TSynCustomHighlighterClass);
548     destructor Destroy; override;
549     function GetEqual(Range: TSynCustomHighlighterRange
550                       ): TSynCustomHighlighterRange;
551     procedure Allocate;
552     procedure Release;
553     property HighlighterClass: TSynCustomHighlighterClass read FHighlighterClass;
554     property AllocatedCount: integer read FAllocatedCount;
555   end;
556 
557 function CompareSynHighlighterRanges(Data1, Data2: Pointer): integer;
558 function AllocateHighlighterRanges(
559      HighlighterClass: TSynCustomHighlighterClass): TSynCustomHighlighterRanges;
560 
561 function dbgs(AFoldActions: TSynFoldActions): String; overload;
562 function dbgs(ANode: TSynFoldNodeInfo):string; overload;
563 function dbgs(AMode: TSynCustomFoldConfigMode): String; overload;
564 function dbgs(AModes: TSynCustomFoldConfigModes): String; overload;
565 function dbgs(AFoldFlag: TSynFoldBlockFilterFlag): String; overload;
566 function dbgs(AFoldFlags: TSynFoldBlockFilterFlags): String; overload;
567 function dbgs(ANestInfo: TLazSynEditNestedFoldsListEntry): String; overload;
568 
569 implementation
570 
571 procedure InitFoldBlockFilter(out AFilter: TSynFoldBlockFilter; AFoldGroup: Integer;
572   AFlag: TSynFoldBlockFilterFlags = []);
573 begin
574   AFilter.FoldGroup := AFoldGroup;
575   AFilter.Flags     := AFlag;
576 end;
577 
578 function CompareSynHighlighterRanges(Data1, Data2: Pointer): integer;
579 var
580   Range1: TSynCustomHighlighterRange;
581   Range2: TSynCustomHighlighterRange;
582 begin
583   Range1:=TSynCustomHighlighterRange(Data1);
584   Range2:=TSynCustomHighlighterRange(Data2);
585   Result:=Range1.Compare(Range2);
586 end;
587 
588 var
589   HighlighterRanges: TFPList = nil;
590 
591 function IndexOfHighlighterRanges(
592   HighlighterClass: TSynCustomHighlighterClass): integer;
593 begin
594   if HighlighterRanges=nil then
595     Result:=-1
596   else begin
597     Result:=HighlighterRanges.Count-1;
598     while (Result>=0)
599     and (TSynCustomHighlighterRanges(HighlighterRanges[Result]).HighlighterClass
600       <>HighlighterClass)
601     do
602       dec(Result);
603   end;
604 end;
605 
606 function AllocateHighlighterRanges(
607   HighlighterClass: TSynCustomHighlighterClass): TSynCustomHighlighterRanges;
608 var
609   i: LongInt;
610 begin
611   if HighlighterRanges=nil then HighlighterRanges:=TFPList.Create;
612   i:=IndexOfHighlighterRanges(HighlighterClass);
613   if i>=0 then begin
614     Result:=TSynCustomHighlighterRanges(HighlighterRanges[i]);
615     Result.Allocate;
616   end else begin
617     Result:=TSynCustomHighlighterRanges.Create(HighlighterClass);
618     HighlighterRanges.Add(Result);
619   end;
620 end;
621 
622 function dbgs(AFoldActions: TSynFoldActions): String;
623 var
624   i: TSynFoldAction;
625   s: string;
626 begin
627   Result:='';
628   for i := low(TSynFoldAction) to high(TSynFoldAction) do
629     if i in AFoldActions then begin
630       WriteStr(s{%H-}, i);
631       Result := Result + s + ',';
632     end;
633   if Result <> '' then Result := '[' + copy(Result, 1, Length(Result)-1) + ']';
634 end;
635 
636 function dbgs(ANode: TSynFoldNodeInfo): string;
637 begin
638   with ANode do
639     if sfaInvalid in FoldAction then
640       Result := Format('L=%3d I=%d  X=%2d-%2d  Fld=%d-%d Nst=%d-%d  FT=%2d FTC=%2d  Grp=%d  A=%s',
641                        [LineIndex, NodeIndex, 0, 0, 0, 0, 0, 0, 0, 0, 0, dbgs(FoldAction)])
642     else
643       Result := Format('L=%3d I=%d  X=%2d-%2d  Fld=%d-%d Nst=%d-%d  FT=%2d FTC=%2d  Grp=%d  A=%s',
644                        [LineIndex, NodeIndex, LogXStart, LogXEnd,
645                         FoldLvlStart, FoldLvlEnd, NestLvlStart, NestLvlEnd,
646                         PtrUInt(FoldType), PtrUInt(FoldTypeCompatible), FoldGroup,
647                         dbgs(FoldAction)]);
648 end;
649 
650 function dbgs(AMode: TSynCustomFoldConfigMode): String;
651 begin
652   WriteStr(Result{%H-}, AMode);
653 end;
654 
655 function dbgs(AModes: TSynCustomFoldConfigModes): String;
656 var
657   i: TSynCustomFoldConfigMode;
658   s: string;
659 begin
660   Result:='';
661   for i := low(TSynCustomFoldConfigMode) to high(TSynCustomFoldConfigMode) do
662     if i in AModes then begin
663       WriteStr(s{%H-}, i);
664       Result := Result + s + ',';
665     end;
666   if Result <> '' then Result := '[' + copy(Result, 1, Length(Result)-1) + ']';
667 end;
668 
669 function dbgs(AFoldFlag: TSynFoldBlockFilterFlag): String;
670 begin
671   Result:='';
672   WriteStr(Result, AFoldFlag);
673 end;
674 
675 function dbgs(AFoldFlags: TSynFoldBlockFilterFlags): String;
676 var
677   i: TSynFoldBlockFilterFlag;
678 begin
679   Result := '';
680   for i := low(TSynFoldBlockFilterFlag) to high(TSynFoldBlockFilterFlag) do
681     if i in AFoldFlags then
682       if Result = ''
683       then Result := dbgs(i)
684       else Result := Result + ',' + dbgs(i);
685   if Result <> '' then
686     Result := '[' + Result + ']';
687 end;
688 
689 function dbgs(ANestInfo: TLazSynEditNestedFoldsListEntry): String;
690 var
691   i: Integer;
692 begin
693   Result := Format('LineIdx:%4d', [ANestInfo.LineIdx ])
694    +' HNode: '+dbgs(ANestInfo.HNode)
695    +' | PrevCnt: '+dbgs(length(ANestInfo.PrevNodeAtSameLevel))
696    +' MinLvl: [';
697   for i := 0  to high(ANestInfo.FGroupMinLevels) do
698     Result := Result+inttostr(ANestInfo.FGroupMinLevels[i])+',';
699   Result := Result+']';
700 end;
701 
702 { TLazSynFoldNodeInfoList }
703 
GetItemnull704 function TLazSynFoldNodeInfoList.GetItem(Index: Integer): TSynFoldNodeInfo;
705 begin
706   DoFilter(Index);
707   if (Index >= FFilteredCount) or (Index < 0) or (not FValid) then
708     InvalidateNode(Result)
709   else begin
710     Result := FFilteredList[Index];
711     Result.NodeIndex := Index; // only set copy on result
712   end;
713 end;
714 
715 procedure TLazSynFoldNodeInfoList.SetActionFilter(AValue: TSynFoldActions);
716 begin
717   if FActionFilter=AValue then Exit;
718   FActionFilter:=AValue;
719   ClearFilteredList;
720 end;
721 
722 procedure TLazSynFoldNodeInfoList.SetGroupFilter(AValue: Integer);
723 begin
724   if FGroupFilter=AValue then Exit;
725   FGroupFilter:=AValue;
726   ClearFilteredList;
727 end;
728 
729 procedure TLazSynFoldNodeInfoList.Clear;
730 begin
731   ClearFilter;
732   ClearData;
733 end;
734 
735 procedure TLazSynFoldNodeInfoList.ClearData;
736 var
737   c: Integer;
738 begin
739   FValid := True;
740   ClearFilteredList;
741   FLine := -1;
742   c := MinCapacity;
743   FNodeCount := 0;
744   if Length(FNodeInfoList) > c then
745     SetLength(FNodeInfoList, c);
746 end;
747 
748 procedure TLazSynFoldNodeInfoList.ClearFilteredList;
749 begin
750   SetLength(FFilteredList, 0);
751   FFilteredCount := 0;
752   FFilteredProgress := 0; // next to be filtered
753 end;
754 
755 procedure TLazSynFoldNodeInfoList.ClearFilter;
756 begin
757   ClearFilteredList;
758   FGroupFilter := 0;
759   FActionFilter := [];
760 end;
761 
762 procedure TLazSynFoldNodeInfoList.DoFilter(MinIndex: Integer = -1);
763 begin
764   if FFilteredProgress = FNodeCount then exit;
765   if (MinIndex >= 0) and (FFilteredCount > MinIndex) or (not FValid) then exit;
766 
767   if (FActionFilter = []) and (FGroupFilter = DefaultGroup) then begin
768     FFilteredList := FNodeInfoList;
769     FFilteredCount := FNodeCount;
770     FFilteredProgress := FNodeCount;
771     exit;
772   end;
773 
774   if Length(FFilteredList) < Length(FNodeInfoList) then
775     SetLength(FFilteredList, Length(FNodeInfoList));
776 
777   while FFilteredProgress < FNodeCount do begin
778     if Match(FNodeInfoList[FFilteredProgress], FActionFilter, FGroupFilter)
779     then begin
780       FFilteredList[FFilteredCount] := FNodeInfoList[FFilteredProgress];
781       inc(FFilteredCount);
782     end;
783     inc(FFilteredProgress);
784     if (MinIndex >= 0) and (FFilteredCount > MinIndex) then break;
785   end;
786 end;
787 
788 procedure TLazSynFoldNodeInfoList.SetLine(ALine: TLineIdx);
789 begin
790   if (FLine = ALine) or (ALine < 0) then exit;
791   ClearData;
792   FLine := ALine;
793   FHighLighter.InitFoldNodeInfo(Self, FLine);
794 end;
795 
796 procedure TLazSynFoldNodeInfoList.SetLineClean(ALine: TLineIdx);
797 begin
798   if (FLine = ALine) or (ALine < 0) then exit;
799   Clear;
800   FLine := ALine;
801   FHighLighter.InitFoldNodeInfo(Self, FLine);
802 end;
803 
MinCapacitynull804 function TLazSynFoldNodeInfoList.MinCapacity: Integer;
805 begin
806   Result := 8;
807 end;
808 
809 procedure TLazSynFoldNodeInfoList.InvalidateNode(out AnInfo: TSynFoldNodeInfo);
810 begin
811   AnInfo.FoldAction := [sfaInvalid];
812   AnInfo.LineIndex := Line;
813   AnInfo.NodeIndex := -1;
814 end;
815 
816 procedure TLazSynFoldNodeInfoList.Add(const AnInfo: TSynFoldNodeInfo);
817 var
818   c: Integer;
819 begin
820   if FNodeCount >= Length(FNodeInfoList) - 1 then begin
821     c := MinCapacity;
822     if c <= 0 then c := 8;
823     SetLength(FNodeInfoList, Max(Length(FNodeInfoList) * 2, c));
824   end;
825   FNodeInfoList[FNodeCount] := AnInfo;
826   FNodeInfoList[FNodeCount].AllNodeIndex := FNodeCount;
827   If (FNodeCount > 0) and (sfaOpen in AnInfo.FoldAction) then begin
828     c := FNodeCount-1;
829     if (sfaClose in FNodeInfoList[c].FoldAction) and
830        //(AnInfo.FoldType = FNodeInfoList[c].FoldType) and  // cfbtIfDef <> cfbtIfElse
831        (AnInfo.LogXStart = FNodeInfoList[c].LogXStart) and
832        (AnInfo.LogXEnd = FNodeInfoList[c].LogXEnd)
833     then begin
834       include(FNodeInfoList[FNodeCount].FoldAction, sfaCloseAndOpen);
835       include(FNodeInfoList[c].FoldAction, sfaCloseAndOpen);
836     end;
837   end;
838   inc(FNodeCount);
839 end;
840 
841 procedure TLazSynFoldNodeInfoList.Delete(AnIndex: Integer = -1);
842 begin
843   if AnIndex > 0 then begin
844     while (AnIndex < FNodeCount) do begin
845       FNodeInfoList[AnIndex] := FNodeInfoList[AnIndex + 1];
846       FNodeInfoList[AnIndex].AllNodeIndex := AnIndex;
847     inc(AnIndex);
848     end;
849   end;
850   if FNodeCount > 0 then
851     dec(FNodeCount);
852 end;
853 
CountAllnull854 function TLazSynFoldNodeInfoList.CountAll: Integer;
855 begin
856   if FValid then
857     Result := FNodeCount
858   else
859     Result := -1;
860 end;
861 
GetItemPointernull862 function TLazSynFoldNodeInfoList.GetItemPointer(AnIndex: Integer
863   ): PSynFoldNodeInfo;
864 begin
865   if (AnIndex >= FNodeCount) or (AnIndex < 0) then
866     Result := nil
867   else
868     Result := @FNodeInfoList[AnIndex];
869 end;
870 
GetLastItemPointernull871 function TLazSynFoldNodeInfoList.GetLastItemPointer: PSynFoldNodeInfo;
872 begin
873   if FNodeCount < 0 then
874     Result := nil
875   else
876     Result := @FNodeInfoList[FNodeCount-1];
877 end;
878 
879 procedure TLazSynFoldNodeInfoList.Invalidate;
880 begin
881   Clear;
882   FValid := False;
883 end;
884 
Matchnull885 function TLazSynFoldNodeInfoList.Match(const AnInfo: TSynFoldNodeInfo;
886   AnActionFilter: TSynFoldActions; AGroupFilter: Integer): Boolean;
887 begin
888   Result := (AnInfo.FoldAction * AnActionFilter = AnActionFilter) and
889             ( (AGroupFilter = 0) or (AnInfo.FoldGroup = AGroupFilter) );
890 end;
891 
DefaultGroupnull892 function TLazSynFoldNodeInfoList.DefaultGroup: Integer;
893 begin
894   Result := 0;
895 end;
896 
Countnull897 function TLazSynFoldNodeInfoList.Count: Integer;
898 begin
899   if not FValid then exit(-1);
900 
901   DoFilter(-1);
902   Result := FFilteredCount;
903 end;
904 
CountExnull905 function TLazSynFoldNodeInfoList.CountEx(AnActionFilter: TSynFoldActions;
906   AGroupFilter: Integer): Integer;
907 var
908   i: Integer;
909 begin
910   if not FValid then exit(-1);
911   if (AnActionFilter = []) and (AGroupFilter = DefaultGroup) then begin
912     Result := FNodeCount;
913     exit;
914   end;
915 
916   Result := 0;
917   for i := 0 to FNodeCount - 1 do
918     if Match(FNodeInfoList[i], AnActionFilter, AGroupFilter) then inc(Result);
919 end;
920 
NodeInfoExnull921 function TLazSynFoldNodeInfoList.NodeInfoEx(Index: Integer;
922   AnActionFilter: TSynFoldActions; AGroupFilter: Integer): TSynFoldNodeInfo;
923 var
924   i, j: Integer;
925 begin
926   if (Index < 0) or (not FValid) then begin
927     InvalidateNode(Result);
928     exit;
929   end;
930 
931   if (AnActionFilter = []) and (AGroupFilter = DefaultGroup) then begin
932     if (Index >= FNodeCount) then
933       InvalidateNode(Result)
934     else
935       Result := FNodeInfoList[Index];
936     Result.NodeIndex := Index; // only set copy on result
937     exit;
938   end;
939 
940   i := 0;
941   j := Index;
942   while i < FNodeCount do begin
943     if Match(FNodeInfoList[i], AnActionFilter, AGroupFilter) then dec(j);
944     if j < 0 then begin;
945       Result := FNodeInfoList[i];
946       Result.NodeIndex := Index; // only set copy on result
947       exit;
948     end;
949     inc(i);
950   end;
951 
952   InvalidateNode(Result);
953 end;
954 
955 { TLazSynEditNestedFoldsList }
956 
957 procedure TLazSynEditNestedFoldsList.SetLine(AValue: TLineIdx);
958 begin
959   if FLine = AValue then Exit;
960   {$IfDef DebugTLazSynEditNestedFoldsList}
961   debugln(['TLazSynEditNestedFoldsList.SetLine ', AValue, ' from previous ', FLine]);
962   {$EndIf}
963 
964   // might be able to re-use old data
965   FPreviousCount := FCount;
966   FPreviousEvaluationIndex := FEvaluationIndex;
967   FPreviousNestInfo := FNestInfo;
968   FPreviousLine := FLine;
969   FNestInfo := nil;
970   FOnLineNestInfo := nil;
971 
972   FLine := AValue;
973   FCount := -1;                          // will trigger InitCount
974   //FEvaluationIndex := -1;
975   FOpeningOnLineCount := -1;
976   FGroupEndLevelsAtEval := nil;   // will trigger InitSubGroupEndLevels
977   FGroupCount := -1;
978 end;
979 
980 procedure TLazSynEditNestedFoldsList.Clear;
981 begin
982   {$IfDef DebugTLazSynEditNestedFoldsList}
983   debugln(['TLazSynEditNestedFoldsList.Clear ']);
984   {$EndIf}
985 
986   FGroupCount := -1;
987   SetLength(FGroupEndLevelsAtEval, 0);
988   FCount := -1;
989   FOpeningOnLineCount := -1;
990   FEvaluationIndex := -1;
991   SetLength(FNestInfo, 0);
992   SetLength(FOnLineNestInfo, 0);
993 
994   ClearPreviousCache;
995 end;
996 
997 procedure TLazSynEditNestedFoldsList.ResetFilter;
998 begin
999   if FIncludeOpeningOnLine and (FFoldFlags = []) and (FFoldGroup = 0) and
1000      (FOpeningLineEndIndex = -1)
1001   then
1002     exit;
1003   FIncludeOpeningOnLine := True;
1004   FFoldFlags := [];
1005   FFoldGroup := 0;
1006   FOpeningLineEndIndex := -1;
1007   Clear;
1008 end;
1009 
1010 procedure TLazSynEditNestedFoldsList.InitSubGroupEndLevels;
1011 var
1012   i: integer;
1013 begin
1014   if Length(FGroupEndLevelsAtEval) > 0 then
1015     exit;
1016   if FHighLighter = nil then exit;
1017   FHighLighter.CurrentLines := FLines;
1018 
1019   if FFoldGroup = 0 then begin
1020     // special, join other groups
1021     FGroupCount := FHighlighter.FoldTypeCount;
1022     // start at 1, so FoldGroup can be used as index
1023     SetLength(FGroupEndLevelsAtEval, FGroupCount + 1);
1024     for i := 1 to FGroupCount do
1025       FGroupEndLevelsAtEval[i] := FHighlighter.FoldBlockEndLevel(FLine - 1, i, FFoldFlags);
1026   end
1027   else begin
1028     FGroupCount := 1;
1029     SetLength(FGroupEndLevelsAtEval, 1);
1030     FGroupEndLevelsAtEval[0] := Count - OpeningOnLineCount;
1031   end;
1032   // Warning: storing endlevels, not minlevels
1033   FNestInfo[FCount].FGroupMinLevels := copy(FGroupEndLevelsAtEval,0, length(FGroupEndLevelsAtEval));
1034   FNestInfo[FCount].LineIdx := Line - 1;
1035   FNestInfo[FCount].EndLineIdx := 0;
1036 end;
1037 
GetHLNodenull1038 function TLazSynEditNestedFoldsList.GetHLNode(Index: Integer): TSynFoldNodeInfo;
1039 begin
1040   if (Index < 0) or (Index >= Count) then begin
1041     Result.FoldAction := [sfaInvalid];
1042     exit;
1043   end;
1044   if Index >= FCount then
1045     Result := FOnLineNestInfo[Index - FCount].HNode
1046   else begin
1047     InitNestInfoForIndex(Index);
1048     Result := FNestInfo[Index].HNode;
1049   end;
1050 end;
1051 
GetNodeEndLinenull1052 function TLazSynEditNestedFoldsList.GetNodeEndLine(Index: Integer): Integer;
1053 var
1054   nd: TSynFoldNodeInfo;
1055   lvl, i, CurIdx, minlvl, grp, cnt: Integer;
1056 begin
1057   if (Index < 0) or (Index >= Count) then
1058     exit(-1);
1059 
1060   nd := HLNode[Index]; // make sure the list/array is initialzied
1061   grp := nd.FoldGroup;
1062   CurIdx := Index;
1063   cnt := Count;
1064   while CurIdx < cnt do begin
1065     nd := HLNode[CurIdx];
1066     if nd.FoldGroup = grp then begin
1067       if CurIdx >= FCount then
1068         Result := FOnLineNestInfo[CurIdx - FCount].EndLineIdx
1069       else
1070         Result := FNestInfo[CurIdx].EndLineIdx;
1071       if Result > 0 then
1072         break;
1073     end;
1074     inc(CurIdx);
1075   end;
1076   {$IfDef DebugTLazSynEditNestedFoldsList}
1077   debugln(['TLazSynEditNestedFoldsList.GetNodeEndLine  for ', Index, '    from curidx ',CurIdx, '   of cnt ', Count, '  / ', FCount ]);
1078   {$EndIf}
1079 
1080   if CurIdx = Index then
1081     exit;
1082 
1083   minlvl:= MaxInt;
1084   if CurIdx < cnt then begin
1085     i := Result;
1086     minlvl := HighLighter.FoldBlockMinLevel(Result, nd.FoldGroup, FoldFlags);
1087   end
1088   else if CurIdx - 1 >= FCount then
1089     i := FLine + 1
1090   else
1091     i := FLine;
1092 
1093   while CurIdx > Index do begin
1094     dec(CurIdx);
1095     nd := HLNode[CurIdx];
1096     if nd.FoldGroup <> grp then
1097       continue;
1098 
1099     if sfbIncludeDisabled in FoldFlags then
1100       lvl := nd.NestLvlStart
1101     else
1102       lvl := nd.FoldLvlStart;
1103 
1104     if minlvl > lvl then begin
1105       Result := HighLighter.FindNextLineWithMinFoldLevel(i, lvl, nd.FoldGroup, FoldFlags);
1106       minlvl := HighLighter.FoldBlockMinLevel(Result, nd.FoldGroup, FoldFlags);
1107     end;
1108 
1109     if CurIdx >= FCount then
1110       FOnLineNestInfo[CurIdx - FCount].EndLineIdx := Result
1111     else
1112       FNestInfo[CurIdx].EndLineIdx := Result;
1113 
1114     if minlvl >= lvl then begin
1115       i := Result + 1;
1116       minlvl:= MaxInt;
1117     end
1118     else if CurIdx = FCount then
1119       minlvl:= MaxInt;
1120   end;
1121 end;
1122 
GetNodeFoldGroupnull1123 function TLazSynEditNestedFoldsList.GetNodeFoldGroup(Index: Integer): Integer;
1124 begin
1125   if FoldGroup <> 0 then
1126     Result := FoldGroup
1127   else
1128     Result := HLNode[Index].FoldGroup;
1129 end;
1130 
GetNodeLinenull1131 function TLazSynEditNestedFoldsList.GetNodeLine(Index: Integer): Integer;
1132 begin
1133   InitLineInfoForIndex(Index);
1134   if Index >= FCount then
1135     Result := FLine
1136   else
1137     Result := FNestInfo[Index].LineIdx;
1138 end;
1139 
GetNodeFoldTypenull1140 function TLazSynEditNestedFoldsList.GetNodeFoldType(Index: Integer): Pointer;
1141 begin
1142   Result := nil;
1143 
1144   if HasCount and
1145      ( (Index >= Count - OpeningOnLineCount) or // OpeningOnLine
1146        ( (Index >= FEvaluationIndex) and (nfeHasHNode in FNestInfo[Index].FFLags) )
1147      )
1148   then begin
1149     Result := HLNode[Index].FoldType;
1150     exit;
1151   end;
1152 
1153   if FHighLighter = nil then exit;
1154   FHighLighter.CurrentLines := FLines;
1155 
1156   // TODO: Cache
1157   if (FFoldGroup > 0) and (FHighLighter.FoldBlockNestedTypes(Line - 1, Index, Result, FFoldGroup, FFoldFlags)) then
1158     exit;
1159 
1160   Result := HLNode[Index].FoldType;
1161 end;
1162 
GetNodeLineExnull1163 function TLazSynEditNestedFoldsList.GetNodeLineEx(Index, PrevCount: Integer): Integer;
1164 var
1165   Node: TLazSynEditNestedFoldsListEntry;
1166   MinLvl, SearchLvl, Grp, PCnt, PLineIdx: Integer;
1167 begin
1168   InitLineInfoForIndex(Index);
1169   Result := -1;
1170 
1171   Node := FNestInfo[Index];
1172   PCnt := length(Node.PrevNodeAtSameLevel);
1173 
1174   if PrevCount > PCnt then begin
1175     if (nfeMaxPrevReached in Node.FFLags) then
1176       exit;
1177     if FHighLighter = nil then exit;
1178     FHighLighter.CurrentLines := FLines;
1179 
1180     if FoldGroup = 0 then begin
1181       InitNestInfoForIndex(Index);
1182       Grp    := Node.HNode.FoldGroup;
1183       if sfbIncludeDisabled in FFoldFlags then
1184         SearchLvl := Node.HNode.NestLvlStart
1185       else
1186         SearchLvl := Node.HNode.FoldLvlStart;
1187     end else begin
1188       Grp    := FoldGroup;
1189       SearchLvl := Index;
1190     end;
1191     if PCnt = 0 then
1192       PLineIdx := Node.LineIdx - 1
1193     else
1194       PLineIdx := Node.PrevNodeAtSameLevel[PCnt-1].LineIdx - 1;
1195 
1196     while true do begin
1197 
1198       MinLvl := FHighLighter.FoldBlockMinLevel(PLineIdx, Grp, FFoldFlags);
1199       while (PLineIdx >= 0) and (SearchLvl < MinLvl) do begin
1200         dec(PLineIdx);
1201         MinLvl := FHighLighter.FoldBlockMinLevel(PLineIdx, Grp, FFoldFlags);
1202       end;
1203 
1204       if PLineIdx >= 0 then begin
1205         if length(Node.PrevNodeAtSameLevel) = PCnt then
1206           SetLength(Node.PrevNodeAtSameLevel, Max(PrevCount, PCnt+1));
1207         Node.PrevNodeAtSameLevel[PCnt].LineIdx := PLineIdx;
1208         Node.PrevNodeAtSameLevel[PCnt].FFLags  := [];
1209         inc(PCnt);
1210         if PCnt = PrevCount then begin
1211           if length(Node.PrevNodeAtSameLevel) > PCnt then
1212             SetLength(Node.PrevNodeAtSameLevel, PCnt);
1213           Result := PLineIdx;
1214           exit;
1215         end;
1216       end;
1217 
1218       If (PLineIdx < 0) or (MinLvl < SearchLvl) then begin
1219         Include(Node.FFLags, nfeMaxPrevReached);
1220         if length(Node.PrevNodeAtSameLevel) > PCnt then
1221           SetLength(Node.PrevNodeAtSameLevel, PCnt);
1222         exit;
1223       end;
1224 
1225     end;
1226   end;
1227 
1228   Result := Node.PrevNodeAtSameLevel[PrevCount-1].LineIdx;
1229 end;
1230 
1231 procedure TLazSynEditNestedFoldsList.InitNestInfoForIndex(AnIndex: Integer);
1232 var
1233   CurLine: TLineIdx;
1234   i, EvalIdx, c, t, l: Integer;
1235   NFilter: TSynFoldActions;
1236   nd: TSynFoldNodeInfo;
1237   GrpCnt: Array of integer;
1238 begin
1239   if HasCount and
1240      ( (AnIndex >= Count - OpeningOnLineCount) or
1241        ( (AnIndex >= FEvaluationIndex) and (nfeHasHNode in FNestInfo[AnIndex].FFLags) )
1242      )
1243   then exit;
1244 
1245   if FHighLighter = nil then exit;
1246   FHighLighter.CurrentLines := FLines;
1247 
1248   AquireFoldNodeInfoList;
1249   try
1250     InitLineInfoForIndex(AnIndex);
1251     if (AnIndex >= Count - OpeningOnLineCount) or
1252        ( (AnIndex >= FEvaluationIndex) and (nfeHasHNode in FNestInfo[AnIndex].FFLags) )
1253     then exit;
1254 
1255     EvalIdx := AnIndex;
1256     CurLine := FNestInfo[EvalIdx].LineIdx;
1257     while (EvalIdx < FCount-1) and (FNestInfo[EvalIdx+1].LineIdx = CurLine) do inc(EvalIdx);
1258     assert(Length(FNestInfo[EvalIdx+1].FGroupMinLevels) > 0, 'Length(FNestInfo[EvalIdx].FGroupEndLevels)');
1259 
1260     //TODO keep groupcount allocated on the same mem / instance var
1261     GrpCnt := copy(FNestInfo[EvalIdx+1].FGroupMinLevels);
1262 
1263     NFilter := [sfaOpenFold];
1264     if not(sfbIncludeDisabled in FFoldFlags) then Include(NFilter, sfaFold);
1265     FFoldNodeInfoList.Line := CurLine;
1266     FFoldNodeInfoList.ActionFilter := NFilter;
1267     FFoldNodeInfoList.GroupFilter := FFoldGroup;
1268     c := FFoldNodeInfoList.Count - 1;
1269     {$IfDef DebugTLazSynEditNestedFoldsList}
1270     debugln(['TLazSynEditNestedFoldsList.InitNestInfoForIndex CurLine=',CurLine, '  c=',c, '  EvalIdx=',EvalIdx]);
1271     {$EndIf}
1272     (* if c < 0 then it is possible that a highlighter called
1273        CodeFoldRange.Pop(false); // avoid minlevel // << still triggers min level for sfbIncludeDisabled;
1274        without generating foldnode info // maybe the HL tries to silently change the fold type
1275     *)
1276     assert(c >= 0, 'InitNestInfoForIndex: FFoldNodeInfoList.Count');
1277 
1278     for i := c downto 0 do begin
1279       nd := FFoldNodeInfoList[i];
1280 
1281       if FFoldGroup = 0
1282       then t := nd.FoldGroup
1283       else t := 0;
1284 
1285       if (sfbIncludeDisabled in FFoldFlags)
1286       then l := nd.NestLvlStart
1287       else l := nd.FoldLvlStart;
1288       if l >= GrpCnt[t] then continue;
1289 
1290       dec(GrpCnt[t]);
1291 
1292       assert(GrpCnt[t] >= 0, 'TLazSynEditNestedFoldsList.InitNestInfoForIndex GroupEndLevel < 0');
1293       assert(EvalIdx >= 0, 'TLazSynEditNestedFoldsList.InitNestInfoForIndex FEvaluationIndex < 0');
1294       assert(FNestInfo[EvalIdx].LineIdx = CurLine, 'TLazSynEditNestedFoldsList.InitNestInfoForIndex FNestInfo[EvalIdx].LineIdx = CurLine');
1295 
1296       //FNestInfo[EvalIdx].LineIdx := CurLine;
1297       include(FNestInfo[EvalIdx].FFLags, nfeHasHNode);
1298       FNestInfo[EvalIdx].HNode := nd;
1299 
1300       dec(EvalIdx);
1301     end;
1302 
1303   finally
1304     ReleaseFoldNodeInfoList;
1305   end;
1306   //for i := FCount-1 downto 0 do  DbgOut([', ',dbgs(nfeHasHNode in FNestInfo[i].FFLags)]); DebugLn();
1307   assert(nfeHasHNode in FNestInfo[AnIndex].FFLags, 'nfeHasHNode in FNestInfo[AnIndex].FFLags');
1308   assert(AnIndex >= FEvaluationIndex, 'TLazSynEditNestedFoldsList.InitNestInfoForIndex Index not found');
1309 end;
1310 
1311 procedure TLazSynEditNestedFoldsList.InitLineInfoForIndex(AnIndex: Integer);
1312 var
1313   CurLine: TLineIdx;
1314   i, c, c1, l: Integer;
1315 
1316   procedure DoMergePrevious;   // TODO: copy nodeinfo if avail
1317   var
1318     pcnt, c, l, c1: integer;
1319   begin
1320     pcnt := FPreviousCount - 1;
1321     {$IfDef DebugTLazSynEditNestedFoldsList}
1322     debugln(['TLazSynEditNestedFoldsList.InitLineInfoForIndex() DoMergePrev (',pcnt, ' ',FPreviousEvaluationIndex ,') ', FPreviousNestInfo[pcnt].LineIdx,' to ', FPreviousNestInfo[FPreviousEvaluationIndex].LineIdx, ' FEvaluationIndex:',FEvaluationIndex, ' CurLine=',CurLine ]);
1323     {$EndIf}
1324     assert(FPreviousNestInfo[pcnt].LineIdx = CurLine, 'TLazSynEditNestedFoldsList.InitLineInfoForIndex.DoMergePrevious LineIdx = CurLine');
1325     while  pcnt >= FPreviousEvaluationIndex do begin
1326       while (pcnt > 0) and (pcnt > FPreviousEvaluationIndex) and
1327             (FPreviousNestInfo[pcnt].LineIdx = FPreviousNestInfo[pcnt-1].LineIdx)
1328       do
1329         dec(pcnt);
1330       assert(length(FPreviousNestInfo[pcnt].FGroupMinLevels) > 0, 'TLazSynEditNestedFoldsList.InitLineInfoForIndex.DoMergePrevious FGroupEndLevels > 0');
1331 
1332       c := 0;
1333       if FFoldGroup = 0 then begin
1334         i := FGroupCount;
1335         while (i > 0) do begin
1336           l := FPreviousNestInfo[pcnt].FGroupMinLevels[i];
1337           if (l < FGroupEndLevelsAtEval[i]) then begin
1338             c1 := FGroupEndLevelsAtEval[i] - l;
1339             FGroupEndLevelsAtEval[i] := l;
1340             c := c + c1;
1341           end
1342           else begin
1343             FPreviousNestInfo[pcnt].FGroupMinLevels[i] := FGroupEndLevelsAtEval[i];
1344           end;
1345           dec(i);
1346         end;
1347       end
1348       else begin
1349         l := FPreviousNestInfo[pcnt].FGroupMinLevels[0];
1350         if (l < FGroupEndLevelsAtEval[0]) then begin
1351           c := FGroupEndLevelsAtEval[0] - l;
1352           FGroupEndLevelsAtEval[0] := l;
1353         end
1354         else begin
1355           FPreviousNestInfo[pcnt].FGroupMinLevels[0] := FGroupEndLevelsAtEval[0];
1356         end;
1357       end;
1358 
1359       while c > 0 do begin
1360         dec(FEvaluationIndex);
1361         FNestInfo[FEvaluationIndex].LineIdx := FPreviousNestInfo[pcnt].LineIdx;
1362         FNestInfo[FEvaluationIndex].EndLineIdx := FPreviousNestInfo[pcnt].EndLineIdx;
1363         FNestInfo[FEvaluationIndex].FFLags:= [];
1364         FNestInfo[FEvaluationIndex].FGroupMinLevels := FPreviousNestInfo[pcnt].FGroupMinLevels;
1365         dec(c);
1366       end;
1367 
1368       dec(pcnt);
1369     end;
1370     ClearPreviousCache;
1371   end;
1372 
1373 begin
1374   if HasCount and ((AnIndex >= Count - OpeningOnLineCount) or (AnIndex >= FEvaluationIndex)) then exit;
1375   assert(FEvaluationIndex > 0, 'TLazSynEditNestedFoldsList.InitLineInfoForIndex already finilhed');
1376 
1377   if FHighLighter = nil then exit;
1378   FHighLighter.CurrentLines := FLines;
1379 
1380   // prepare previous info.
1381   // TODO FLine = FPreviousNestInfo[i].LineIdx or FLine = FPreviousLine;
1382   if (FEvaluationIndex = FCount) then begin
1383     FPreviousMergeLine := -1;
1384     i := FPreviousCount; // + 1 - 1
1385     if (i > 0) and (FPreviousEvaluationIndex < i) then begin
1386       if i >= Length(FPreviousNestInfo) then
1387         exit;
1388       if (i > FPreviousEvaluationIndex) and
1389          (FPreviousNestInfo[i].LineIdx = FPreviousNestInfo[i-1].LineIdx)
1390       then
1391         dec(i);
1392       while (i >= 0) and (i >= FPreviousEvaluationIndex) do
1393         if FPreviousNestInfo[i].LineIdx >= Line then
1394           dec(i)
1395         else
1396           break;
1397       FPreviousCount := i + 1;
1398       if (i >= 0) and (i >= FPreviousEvaluationIndex) then
1399         FPreviousMergeLine := FPreviousNestInfo[i].LineIdx;
1400     end;
1401   end;
1402 
1403   AquireFoldNodeInfoList;
1404   try
1405     if (AnIndex >= Count - OpeningOnLineCount) or (AnIndex >= FEvaluationIndex) then exit;
1406 
1407     InitSubGroupEndLevels;
1408 
1409 //    FNestInfo[FEvaluationIndex].FGroupMinLevels := copy(FGroupEndLevelsAtEval,0, length(FGroupEndLevelsAtEval));
1410 
1411     if (FEvaluationIndex = FCount) then
1412       CurLine := Line - 1
1413     else
1414       CurLine := FNestInfo[FEvaluationIndex].LineIdx - 1;
1415 
1416     inc(CurLine);
1417     while CurLine > 0 do begin
1418       dec(CurLine);
1419       if CurLine = FPreviousMergeLine then begin
1420         FPreviousMergeLine := -1;
1421         DoMergePrevious;
1422         InitLineInfoForIndex(AnIndex);
1423         exit;
1424       end;
1425 
1426       c := 0;
1427       if FFoldGroup = 0 then begin
1428         i := FGroupCount;
1429         while (i > 0) do begin
1430           l := FHighLighter.FoldBlockMinLevel(CurLine, i, FFoldFlags);
1431           if (l < FGroupEndLevelsAtEval[i]) then begin
1432             c1 := FGroupEndLevelsAtEval[i] - l;
1433             FGroupEndLevelsAtEval[i] := FGroupEndLevelsAtEval[i] - c1;
1434             c := c + c1;
1435           end;
1436           dec(i);
1437         end;
1438       end
1439       else begin
1440         l := FHighLighter.FoldBlockMinLevel(CurLine, FFoldGroup, FFoldFlags);
1441         if l < FGroupEndLevelsAtEval[0] then begin
1442           c := FGroupEndLevelsAtEval[0] - l;
1443           FGroupEndLevelsAtEval[0] := FGroupEndLevelsAtEval[0] - c;
1444         end;
1445       end;
1446       if c = 0 then continue;
1447 
1448       while c > 0 do begin
1449         dec(FEvaluationIndex);
1450         FNestInfo[FEvaluationIndex].LineIdx := CurLine;
1451         FNestInfo[FEvaluationIndex].FFLags:= [];
1452         dec(c);
1453       end;
1454       FNestInfo[FEvaluationIndex].FGroupMinLevels := copy(FGroupEndLevelsAtEval,0, length(FGroupEndLevelsAtEval));
1455 
1456       if (AnIndex >= FEvaluationIndex) then Break;
1457     end;
1458 
1459   finally
1460     ReleaseFoldNodeInfoList;
1461   end;
1462   {$IfDef DebugTLazSynEditNestedFoldsList}
1463   debugln(['TLazSynEditNestedFoldsList.InitLineInfoForIndex FEvaluationIndex=', FEvaluationIndex, '  AnIndex=',AnIndex]);
1464   for i := FCount-1 downto 0 do begin DbgOut([', ',FNestInfo[i].LineIdx]); if length(FNestInfo[i].FGroupMinLevels) > 0 then begin DbgOut(' ('); for c := 0 to length(FNestInfo[i].FGroupMinLevels)-1 do DbgOut([',',FNestInfo[i].FGroupMinLevels[c]]);  DbgOut(') '); end; end; DebugLn();
1465   {$EndIf}
1466   assert(CurLine >= 0, 'TLazSynEditNestedFoldsList.InitLineInfoForIndex Curline < 0');
1467   assert(AnIndex >= FEvaluationIndex, 'TLazSynEditNestedFoldsList.InitLineInfoForIndex Index not found');
1468 end;
1469 
1470 procedure TLazSynEditNestedFoldsList.InitCount;
1471 begin
1472   if FHighLighter = nil then exit;
1473   FHighLighter.CurrentLines := FLines;
1474 
1475   FCount := FHighlighter.FoldBlockEndLevel(FLine - 1, FFoldGroup, FFoldFlags);
1476   FEvaluationIndex := FCount;
1477   SetLength(FNestInfo, FCount+1);
1478 end;
1479 
1480 procedure TLazSynEditNestedFoldsList.InitOpeningOnLine;
1481 var
1482   nd: TSynFoldNodeInfo;
1483   OpenIdx: Array of Array of Integer; // List of open-node-index, for each FoldCroup
1484   OpenCnt: Array of Integer; // List of open-node-index, for each FoldCroup
1485   Grp, c, i, j, GrpLow, GrpHigh, ListCnt: Integer;
1486   oc: LongInt;
1487 begin
1488   Assert((FOpeningLineEndIndex < 0) or (sfbIncludeDisabled in FoldFlags), 'OpeningLineEndIndex only implemented for sfbIncludeDisabled');
1489 
1490   FOpeningOnLineCount := 0;
1491   if FCount < 0 then
1492     InitCount;
1493 
1494   if not FIncludeOpeningOnLine then
1495     exit;
1496   // FOnLineNestInfo
1497 
1498   if FHighLighter = nil then exit;
1499   FHighLighter.CurrentLines := FLines;
1500 
1501   AquireFoldNodeInfoList(FLine);
1502   try
1503     if (sfbIncludeDisabled in FFoldFlags) then
1504       FFoldNodeInfoList.ActionFilter := []
1505     else
1506       FFoldNodeInfoList.ActionFilter := [sfaFold];
1507     FFoldNodeInfoList.GroupFilter := 0;
1508 
1509     if FFoldGroup = 0 then begin
1510       FGroupCount := FHighlighter.FoldTypeCount;
1511       GrpLow := 1;
1512       GrpHigh := FGroupCount;
1513     end
1514     else begin
1515       FGroupCount := 1;
1516       GrpLow := FFoldGroup;
1517       GrpHigh := FFoldGroup;
1518     end;
1519     SetLength(OpenCnt, FGroupCount);
1520     for Grp := 0 to FGroupCount - 1 do
1521       OpenCnt[Grp] := 0;
1522     ListCnt := FFoldNodeInfoList.Count;
1523     if ListCnt < 0 then
1524       exit;
1525     SetLength(OpenIdx, FGroupCount, ListCnt);
1526 
1527     for Grp := GrpLow to GrpHigh do begin
1528       (* Filtering group in the loop instead of the list only works, if 0 is the only special group
1529          See use of NodeIndex below, if changing this *)
1530       //FFoldNodeInfoList.GroupFilter := Grp;
1531       for i := 0 to ListCnt - 1 do begin
1532         nd := FFoldNodeInfoList[i];
1533         if (sfaInvalid in nd.FoldAction) or (nd.FoldGroup <> Grp) then
1534           Continue;
1535         if (FOpeningLineEndIndex >= 0) and (nd.AllNodeIndex > FOpeningLineEndIndex) then
1536           break;
1537 
1538         if sfaOpen in nd.FoldAction then begin
1539           inc(FOpeningOnLineCount);
1540           OpenIdx[Grp - GrpLow, OpenCnt[Grp - GrpLow]] := nd.NodeIndex; // Using NodeIndex only works, because we do NOT change the filter
1541           inc(OpenCnt[Grp - GrpLow]);
1542         end
1543         else
1544         if (nd.FoldAction * [sfaClose, sfaFold, sfaSingleLine] = [sfaClose, sfaSingleLine]) then begin
1545           dec(FOpeningOnLineCount);
1546           dec(OpenCnt[Grp - GrpLow]);
1547         end;
1548       end;
1549     end;
1550 
1551     SetLength(FOnLineNestInfo, FOpeningOnLineCount);
1552 
1553     //FFoldNodeInfoList.ActionFilter := [];
1554     //FFoldNodeInfoList.GroupFilter := 0;
1555     c := ListCnt - 1;
1556     if (FOpeningLineEndIndex >= 0) and (c > FOpeningLineEndIndex) then
1557       c := FOpeningLineEndIndex;
1558     j := FOpeningOnLineCount;
1559 
1560     For i := c downto 0 do begin
1561       if j = 0 then break;
1562       nd := FFoldNodeInfoList[i];
1563       Grp := nd.FoldGroup;
1564       if (Grp < GrpLow) or (Grp > GrpHigh) then Continue;
1565       oc := OpenCnt[Grp - GrpLow];
1566       Assert(oc >= 0, 'TLazSynEditNestedFoldsList.InitOpeningOnLine bad count for '+IntToStr(Grp));
1567       Assert((oc=0) or (OpenIdx[Grp - GrpLow, oc-1] <= i), 'TLazSynEditNestedFoldsList.InitOpeningOnLine bad index for '+IntToStr(i)+' G='+IntToStr(Grp));
1568       if (oc > 0) and (OpenIdx[Grp - GrpLow, oc-1] = i) then begin
1569         dec(OpenCnt[Grp - GrpLow]);
1570         dec(j);
1571         FOnLineNestInfo[j].LineIdx := FLine;
1572         FOnLineNestInfo[j].HNode := nd;
1573         FOnLineNestInfo[j].HNode.NodeIndex := j;
1574         FOnLineNestInfo[j].EndLineIdx := 0;
1575       end;
1576     end;
1577 
1578     Assert(j=0, 'TLazSynEditNestedFoldsList.InitOpeningOnLine did not fill all nodes '+IntToStr(j));
1579   finally
1580     ReleaseFoldNodeInfoList;
1581   end;
1582 end;
1583 
1584 procedure TLazSynEditNestedFoldsList.SetFoldFlags(AValue: TSynFoldBlockFilterFlags);
1585 begin
1586   if FFoldFlags = AValue then Exit;
1587   FFoldFlags := AValue;
1588   Clear;
1589 end;
1590 
1591 procedure TLazSynEditNestedFoldsList.SetHighLighter(
1592   AValue: TSynCustomFoldHighlighter);
1593 begin
1594   if FHighLighter = AValue then Exit;
1595   FHighLighter := AValue;
1596   Clear;
1597 end;
1598 
1599 procedure TLazSynEditNestedFoldsList.SetIncludeOpeningOnLine(AValue: Boolean);
1600 begin
1601   if FIncludeOpeningOnLine = AValue then Exit;
1602   FIncludeOpeningOnLine := AValue;
1603   //Clear; // Do not Clear, keep the data, can be re-enabled
1604 end;
1605 
1606 procedure TLazSynEditNestedFoldsList.AquireFoldNodeInfoList(const ALine: Integer
1607   );
1608 begin
1609   if FHighLighter = nil then exit;
1610   FHighLighter.CurrentLines := FLines;
1611 
1612   if FFoldNodeInfoListHoldCnt = 0 then begin
1613     FFoldNodeInfoList := FHighlighter.FoldNodeInfo[ALine];
1614     FFoldNodeInfoList.AddReference;
1615   end else
1616     FFoldNodeInfoList.Line := ALine;
1617   inc(FFoldNodeInfoListHoldCnt);
1618 end;
1619 
1620 procedure TLazSynEditNestedFoldsList.ReleaseFoldNodeInfoList;
1621 begin
1622   dec(FFoldNodeInfoListHoldCnt);
1623   if FFoldNodeInfoListHoldCnt = 0 then
1624     ReleaseRefAndNil(FFoldNodeInfoList);
1625 end;
1626 
1627 procedure TLazSynEditNestedFoldsList.SetLines(AValue: TSynEditStrings);
1628 begin
1629   if FLines = AValue then Exit;
1630   FLines := AValue;
1631   Clear;
1632 end;
1633 
1634 procedure TLazSynEditNestedFoldsList.SetOpeningLineEndIndex(AValue: Integer);
1635 begin
1636   if FOpeningLineEndIndex = AValue then Exit;
1637   FOpeningLineEndIndex := AValue;
1638   Clear; // TODO only clear current line, the rest will still be valid
1639 end;
1640 
HasCountnull1641 function TLazSynEditNestedFoldsList.HasCount: Boolean;
1642 begin
1643   Result := (FCount >= 0) and ( (not FIncludeOpeningOnLine) or (FOpeningOnLineCount >= 0) );
1644 end;
1645 
1646 procedure TLazSynEditNestedFoldsList.ClearPreviousCache;
1647 begin
1648   FPreviousCount := -1;
1649   FPreviousEvaluationIndex := -1;
1650   SetLength(FPreviousNestInfo, 0);
1651 end;
1652 
1653 procedure TLazSynEditNestedFoldsList.SetFoldGroup(AValue: Integer);
1654 begin
1655   if FFoldGroup = AValue then Exit;
1656   FFoldGroup := AValue;
1657   Clear;
1658 end;
1659 
1660 constructor TLazSynEditNestedFoldsList.Create(ALines: TSynEditStrings;
1661   AnHighLighter: TSynCustomFoldHighlighter);
1662 begin
1663   FLines := ALines;
1664   FHighLighter := AnHighLighter;
1665   FIncludeOpeningOnLine := True;
1666   FFoldFlags := [];
1667   FFoldGroup := 0;
1668   FFoldNodeInfoListHoldCnt := 0;
1669 end;
1670 
Countnull1671 function TLazSynEditNestedFoldsList.Count: Integer;
1672 begin
1673   if (FCount < 0) then begin
1674     InitCount;
1675   end;
1676   if FIncludeOpeningOnLine and (FOpeningOnLineCount < 0) then begin
1677     InitOpeningOnLine;
1678   end;
1679 
1680   Result := FCount + OpeningOnLineCount;
1681 end;
1682 
OpeningOnLineCountnull1683 function TLazSynEditNestedFoldsList.OpeningOnLineCount: Integer;
1684 begin
1685   if (not FIncludeOpeningOnLine) or (FLine < 0) then
1686     exit(0);
1687 
1688   if (FOpeningOnLineCount < 0) then begin
1689     InitOpeningOnLine;
1690   end;
1691 
1692   Result := FOpeningOnLineCount;
1693 end;
1694 
1695 procedure TLazSynEditNestedFoldsList.Debug;
1696 var
1697   i: Integer;
1698 begin
1699   Debugln(['TLazSynEditNestedFoldsList for FFoldGroup=', FFoldGroup, ' FLine=', FLine,
1700            ' FFoldFlags=', dbgs(FFoldFlags), ' FGroupCount=', FGroupCount,
1701            ' FIncludeOpeningOnLine=', dbgs(FIncludeOpeningOnLine), ' FEvaluationIndex=', FEvaluationIndex,
1702            ' FCount=', FCount, ' FOpeningOnLineCount=', FOpeningOnLineCount]);
1703   Debugln(['FGroupEndLevelsAtEval=', length(FGroupEndLevelsAtEval), ': ']); for i := 0 to length(FGroupEndLevelsAtEval)-1 do DbgOut([FGroupEndLevelsAtEval[i]]); Debugln;
1704   for i := 0 to length(FNestInfo)-1 do
1705     Debugln(['N-Info ', i,': ',dbgs(FNestInfo[i])]);
1706 end;
1707 
1708 { TSynCustomFoldHighlighter }
1709 
1710 constructor TSynCustomFoldHighlighter.Create(AOwner: TComponent);
1711 begin
1712   SetLength(FFoldConfig, GetFoldConfigInternalCount);
1713   InitFoldConfig;
1714   fRanges:=AllocateHighlighterRanges(TSynCustomHighlighterClass(ClassType));
1715   CreateRootCodeFoldBlock;
1716   inherited Create(AOwner);
1717   FCodeFoldRange:=GetRangeClass.Create(nil);
1718   FCodeFoldRange.FoldRoot := FRootCodeFoldBlock;
1719   FFoldNodeInfoList := nil;;
1720 end;
1721 
1722 destructor TSynCustomFoldHighlighter.Destroy;
1723 begin
1724   inherited Destroy;
1725   DestroyFoldConfig;
1726   FreeAndNil(FCodeFoldRange);
1727   FreeAndNil(FRootCodeFoldBlock);
1728   ReleaseRefAndNil(FFoldNodeInfoList);
1729   fRanges.Release;
1730   FFoldConfig := nil;
1731 end;
1732 
1733 class function TSynCustomFoldHighlighter.GetCapabilities: TSynHighlighterCapabilities;
1734 begin
1735   Result := inherited GetCapabilities + [hcCodeFolding];
1736 end;
1737 
GetRangenull1738 function TSynCustomFoldHighlighter.GetRange: Pointer;
1739 begin
1740   // FCodeFoldRange is the working range and changed steadily
1741   // => return a fixed copy of the current CodeFoldRange instance,
1742   //    that can be stored by other classes (e.g. TSynEdit)
1743   Result:=fRanges.GetEqual(FCodeFoldRange);
1744 end;
1745 
FoldBlockOpeningCountnull1746 function TSynCustomFoldHighlighter.FoldBlockOpeningCount(ALineIndex: TLineIdx;
1747   const AFilter: TSynFoldBlockFilter): integer;
1748 {$IFDEF ISSUE_20850}
1749 var x : integer;
1750 {$ENDIF}
1751 begin
1752   if (ALineIndex < 0) or (ALineIndex >= CurrentLines.Count) then
1753     exit(0);
1754   {$IFDEF ISSUE_20850}
1755   x      := FoldBlockEndLevel(ALineIndex, AFilter);
1756   Result := FoldBlockMinLevel(ALineIndex, AFilter);
1757   Result := x - Result;
1758   {$ELSE}
1759   Result := FoldBlockEndLevel(ALineIndex, AFilter) - FoldBlockMinLevel(ALineIndex, AFilter);
1760   {$ENDIF}
1761 end;
1762 
FoldBlockClosingCountnull1763 function TSynCustomFoldHighlighter.FoldBlockClosingCount(ALineIndex: TLineIdx;
1764   const AFilter: TSynFoldBlockFilter): integer;
1765 {$IFDEF ISSUE_20850}
1766 var x : integer;
1767 {$ENDIF}
1768 begin
1769   if (ALineIndex < 0) or (ALineIndex >= CurrentLines.Count) then
1770     exit(0);
1771   {$IFDEF ISSUE_20850}
1772   x      := FoldBlockEndLevel(ALineIndex - 1, AFilter);
1773   Result := FoldBlockMinLevel(ALineIndex, AFilter);
1774   Result := x - Result;
1775   {$ELSE}
1776   Result := FoldBlockEndLevel(ALineIndex - 1, AFilter) - FoldBlockMinLevel(ALineIndex, AFilter);
1777   {$ENDIF}
1778 end;
1779 
FoldBlockEndLevelnull1780 function TSynCustomFoldHighlighter.FoldBlockEndLevel(ALineIndex: TLineIdx;
1781   const AFilter: TSynFoldBlockFilter): integer;
1782 var
1783   r: Pointer;
1784 begin
1785   Assert(CurrentRanges <> nil, 'TSynCustomFoldHighlighter.FoldBlockEndLevel requires CurrentRanges');
1786   if (ALineIndex < 0) or (ALineIndex >= CurrentLines.Count - 1) then
1787     exit(0);
1788   r := CurrentRanges[ALineIndex];
1789   if (r <> nil) and (r <> NullRange) then
1790     Result := TSynCustomHighlighterRange(r).CodeFoldStackSize
1791   else
1792     Result:=0;
1793 end;
1794 
FoldBlockMinLevelnull1795 function TSynCustomFoldHighlighter.FoldBlockMinLevel(ALineIndex: TLineIdx;
1796   const AFilter: TSynFoldBlockFilter): integer;
1797 var
1798   r: Pointer;
1799 begin
1800   Assert(CurrentRanges <> nil, 'TSynCustomFoldHighlighter.FoldBlockMinLevelrequires CurrentRanges');
1801   if (ALineIndex < 0) or (ALineIndex >= CurrentLines.Count - 1) then
1802     exit(0);
1803   r := CurrentRanges[ALineIndex];
1804   if (r <> nil) and (r <> NullRange) then
1805     Result := TSynCustomHighlighterRange(r).MinimumCodeFoldBlockLevel
1806   else
1807     Result:=0;
1808 end;
1809 
FoldBlockNestedTypesnull1810 function TSynCustomFoldHighlighter.FoldBlockNestedTypes(ALineIndex: TLineIdx;
1811   ANestIndex: Integer; out AType: Pointer; const AFilter: TSynFoldBlockFilter): boolean;
1812 begin
1813   Result := False;
1814 end;
1815 
FoldBlockOpeningCountnull1816 function TSynCustomFoldHighlighter.FoldBlockOpeningCount(ALineIndex: TLineIdx;
1817   AFoldGroup: integer; AFlags: TSynFoldBlockFilterFlags): integer;
1818 var
1819   Filter: TSynFoldBlockFilter;
1820 begin
1821   Filter.FoldGroup := AFoldGroup;
1822   Filter.Flags := AFlags;
1823   Result := FoldBlockOpeningCount(ALineIndex, Filter);
1824 end;
1825 
FoldBlockClosingCountnull1826 function TSynCustomFoldHighlighter.FoldBlockClosingCount(ALineIndex: TLineIdx;
1827   AFoldGroup: integer; AFlags: TSynFoldBlockFilterFlags): integer;
1828 var
1829   Filter: TSynFoldBlockFilter;
1830 begin
1831   Filter.FoldGroup := AFoldGroup;
1832   Filter.Flags := AFlags;
1833   Result := FoldBlockClosingCount(ALineIndex, Filter);
1834 end;
1835 
FoldBlockEndLevelnull1836 function TSynCustomFoldHighlighter.FoldBlockEndLevel(ALineIndex: TLineIdx;
1837   AFoldGroup: integer; AFlags: TSynFoldBlockFilterFlags): integer;
1838 var
1839   Filter: TSynFoldBlockFilter;
1840 begin
1841   Filter.FoldGroup := AFoldGroup;
1842   Filter.Flags := AFlags;
1843   Result := FoldBlockEndLevel(ALineIndex, Filter);
1844 end;
1845 
FoldBlockMinLevelnull1846 function TSynCustomFoldHighlighter.FoldBlockMinLevel(ALineIndex: TLineIdx;
1847   AFoldGroup: integer; AFlags: TSynFoldBlockFilterFlags): integer;
1848 var
1849   Filter: TSynFoldBlockFilter;
1850 begin
1851   Filter.FoldGroup := AFoldGroup;
1852   Filter.Flags := AFlags;
1853   Result := FoldBlockMinLevel(ALineIndex, Filter);
1854 end;
1855 
FoldBlockNestedTypesnull1856 function TSynCustomFoldHighlighter.FoldBlockNestedTypes(ALineIndex: TLineIdx;
1857   ANestIndex: Integer; out AType: Pointer; AFoldGroup: integer;
1858   AFlags: TSynFoldBlockFilterFlags): boolean;
1859 var
1860   Filter: TSynFoldBlockFilter;
1861 begin
1862   Filter.FoldGroup := AFoldGroup;
1863   Filter.Flags := AFlags;
1864   Result := FoldBlockNestedTypes(ALineIndex, ANestIndex, AType, Filter);
1865 end;
1866 
1867 procedure TSynCustomFoldHighlighter.ResetRange;
1868 begin
1869   FCodeFoldRange.Clear;
1870   FCodeFoldRange.FoldRoot := FRootCodeFoldBlock;
1871 end;
1872 
MinimumCodeFoldBlockLevelnull1873 function TSynCustomFoldHighlighter.MinimumCodeFoldBlockLevel: integer;
1874 begin
1875   assert(FCodeFoldRange <> nil, 'MinimumCodeFoldBlockLevel requires FCodeFoldRange');
1876   Result := FCodeFoldRange.MinimumCodeFoldBlockLevel;
1877 end;
1878 
1879 procedure TSynCustomFoldHighlighter.SetRange(Value: Pointer);
1880 begin
1881   FCodeFoldRange.Assign(TSynCustomHighlighterRange(Value));
1882   // in case we asigned a null range
1883   if not assigned(FCodeFoldRange.FoldRoot) then
1884     FCodeFoldRange.FoldRoot := FRootCodeFoldBlock;
1885 end;
1886 
1887 procedure TSynCustomFoldHighlighter.SetLine(const NewValue: String;
1888   LineNumber: Integer);
1889 begin
1890   inherited;
1891   FCodeFoldRange.MinimumCodeFoldBlockLevel := FCodeFoldRange.FCodeFoldStackSize;
1892   FCodeFoldRange.FMinimumNestFoldBlockLevel := FCodeFoldRange.NestFoldStackSize;
1893 end;
1894 
1895 procedure TSynCustomFoldHighlighter.DoCurrentLinesChanged;
1896 begin
1897   inherited DoCurrentLinesChanged;
1898   ClearFoldNodeList;
1899 end;
1900 
PerformScannull1901 function TSynCustomFoldHighlighter.PerformScan(StartIndex, EndIndex: Integer;
1902   ForceEndIndex: Boolean): Integer;
1903 begin
1904   ClearFoldNodeList;
1905   Result := inherited PerformScan(StartIndex, EndIndex, ForceEndIndex);
1906 end;
1907 
CurrentCodeFoldBlockLevelnull1908 function TSynCustomFoldHighlighter.CurrentCodeFoldBlockLevel: integer;
1909 begin
1910   assert(FCodeFoldRange <> nil, 'MinimumCodeFoldBlockLevel requires FCodeFoldRange');
1911   Result := FCodeFoldRange.CodeFoldStackSize;
1912 end;
1913 
FoldOpenCountnull1914 function TSynCustomFoldHighlighter.FoldOpenCount(ALineIndex: Integer; AType: Integer = 0): integer;
1915 begin
1916   result := FoldBlockOpeningCount(ALineIndex, AType);
1917 end;
1918 
FoldCloseCountnull1919 function TSynCustomFoldHighlighter.FoldCloseCount(ALineIndex: Integer; AType: Integer = 0): integer;
1920 begin
1921   result := FoldBlockClosingCount(ALineIndex, AType);
1922 end;
1923 
FoldNestCountnull1924 function TSynCustomFoldHighlighter.FoldNestCount(ALineIndex: Integer; AType: Integer = 0): integer;
1925 begin
1926   Result := FoldBlockEndLevel(ALineIndex, AType);
1927 end;
1928 
FoldTypeCountnull1929 function TSynCustomFoldHighlighter.FoldTypeCount: integer;
1930 begin
1931   Result := 1;
1932 end;
1933 
FoldTypeAtNodeIndexnull1934 function TSynCustomFoldHighlighter.FoldTypeAtNodeIndex(ALineIndex, FoldIndex: Integer;
1935   UseCloseNodes: boolean): integer;
1936 begin
1937   Result := 0;
1938 end;
1939 
FindNextLineWithMinFoldLevelnull1940 function TSynCustomFoldHighlighter.FindNextLineWithMinFoldLevel(
1941   ALineIndex: TLineIdx; ASearchLevel: Integer;
1942   const AFilter: TSynFoldBlockFilter): integer;
1943 var
1944   cnt: Integer;
1945 begin
1946   cnt := CurrentLines.Count;
1947   Result := ALineIndex; // Can return the original line
1948   while (Result < cnt) and (FoldBlockMinLevel(Result, AFilter) > ASearchLevel) do inc(Result);
1949   if (Result = cnt) then
1950     dec(Result);
1951 end;
1952 
FindNextLineWithMinFoldLevelnull1953 function TSynCustomFoldHighlighter.FindNextLineWithMinFoldLevel(
1954   ALineIndex: TLineIdx; ASearchLevel: Integer; AFoldGroup: integer;
1955   AFlags: TSynFoldBlockFilterFlags): integer;
1956 var
1957   Filter: TSynFoldBlockFilter;
1958 begin
1959   Filter.FoldGroup := AFoldGroup;
1960   Filter.Flags := AFlags;
1961   Result := FindNextLineWithMinFoldLevel(ALineIndex, ASearchLevel, Filter);
1962 end;
1963 
FoldLineLengthnull1964 function TSynCustomFoldHighlighter.FoldLineLength(ALineIndex, FoldIndex: Integer): integer;
1965 begin
1966   Result := FoldEndLine(ALineIndex, FoldIndex);
1967   // check if fold last line of block (not mixed "end begin")
1968   if (FoldBlockEndLevel(Result) > FoldBlockMinLevel(Result)) then
1969     dec(Result);
1970   // Amount of lines, that will become invisible (excludes the cfCollapsed line)
1971   Result := Result - ALineIndex;
1972 end;
1973 
FoldEndLinenull1974 function TSynCustomFoldHighlighter.FoldEndLine(ALineIndex, FoldIndex: Integer): integer;
1975 begin
1976   Result := FoldEndLine(ALineIndex, FoldIndex, 0, []);
1977 end;
1978 
FoldEndLinenull1979 function TSynCustomFoldHighlighter.FoldEndLine(ALineIndex, FoldIndex: Integer;
1980   const AFilter: TSynFoldBlockFilter): integer;
1981 var
1982   lvl: Integer;
1983   e, m: Integer;
1984 begin
1985   e := FoldBlockEndLevel(ALineIndex, AFilter);
1986   m := FoldBlockMinLevel(ALineIndex, AFilter);
1987   lvl := Min(m+1+FoldIndex, e);
1988   Result := FindNextLineWithMinFoldLevel(ALineIndex+1, lvl-1, AFilter);
1989 end;
1990 
FoldEndLinenull1991 function TSynCustomFoldHighlighter.FoldEndLine(ALineIndex, FoldIndex: Integer;
1992   AFoldGroup: integer; AFlags: TSynFoldBlockFilterFlags): integer;
1993 var
1994   Filter: TSynFoldBlockFilter;
1995 begin
1996   Filter.FoldGroup := AFoldGroup;
1997   Filter.Flags := AFlags;
1998   Result := FoldEndLine(ALineIndex, FoldIndex, Filter);
1999 end;
2000 
GetFoldConfignull2001 function TSynCustomFoldHighlighter.GetFoldConfig(Index: Integer): TSynCustomFoldConfig;
2002 begin
2003   Result := FFoldConfig[Index];
2004 end;
2005 
2006 procedure TSynCustomFoldHighlighter.SetFoldConfig(Index: Integer; const AValue: TSynCustomFoldConfig);
2007 begin
2008   BeginUpdate;
2009   FFoldConfig[Index].Assign(AValue);
2010   EndUpdate;
2011 end;
2012 
GetFoldConfigCountnull2013 function TSynCustomFoldHighlighter.GetFoldConfigCount: Integer;
2014 begin
2015   Result := 0;
2016 end;
2017 
GetFoldConfigInternalCountnull2018 function TSynCustomFoldHighlighter.GetFoldConfigInternalCount: Integer;
2019 begin
2020   Result := 0;
2021 end;
2022 
CreateFoldConfigInstancenull2023 function TSynCustomFoldHighlighter.CreateFoldConfigInstance(Index: Integer
2024   ): TSynCustomFoldConfig;
2025 begin
2026   Result := TSynCustomFoldConfig.Create;
2027 end;
2028 
GetFoldConfigInstancenull2029 function TSynCustomFoldHighlighter.GetFoldConfigInstance(Index: Integer): TSynCustomFoldConfig;
2030 begin
2031   Result := CreateFoldConfigInstance(Index);
2032   Result.OnChange := @DoFoldConfigChanged;
2033   Result.Enabled := False;
2034 end;
2035 
2036 procedure TSynCustomFoldHighlighter.InitFoldConfig;
2037 var
2038   i: Integer;
2039 begin
2040   for i := 0 to high(FFoldConfig) do
2041     FFoldConfig[i] := GetFoldConfigInstance(i);
2042 end;
2043 
2044 procedure TSynCustomFoldHighlighter.DestroyFoldConfig;
2045 var
2046   i: Integer;
2047 begin
2048   for i := 0 to high(FFoldConfig) do
2049     FFoldConfig[i].Free;
2050 end;
2051 
2052 procedure TSynCustomFoldHighlighter.DoFoldConfigChanged(Sender: TObject);
2053 begin
2054   FAttributeChangeNeedScan := True;
2055   DefHighlightChange(self);
2056 end;
2057 
2058 procedure TSynCustomFoldHighlighter.ClearFoldNodeList;
2059 begin
2060   if FFoldNodeInfoList <> nil then begin
2061     if (FFoldNodeInfoList.RefCount > 1) then
2062       ReleaseRefAndNil(FFoldNodeInfoList)
2063     else
2064       FFoldNodeInfoList.Clear;
2065   end;
2066 end;
2067 
GetFoldNodeInfonull2068 function TSynCustomFoldHighlighter.GetFoldNodeInfo(Line: TLineIdx
2069   ): TLazSynFoldNodeInfoList;
2070 begin
2071   if (FFoldNodeInfoList <> nil) and (FFoldNodeInfoList.RefCount > 1) then
2072     ReleaseRefAndNil(FFoldNodeInfoList);
2073 
2074   if FFoldNodeInfoList = nil then begin
2075     FFoldNodeInfoList := CreateFoldNodeInfoList;
2076     FFoldNodeInfoList.AddReference;
2077     FFoldNodeInfoList.HighLighter := Self;
2078   end
2079   else
2080   if (CurrentRanges <> nil) and (CurrentRanges.NeedsReScanStartIndex >= 0) then
2081     ClearFoldNodeList;
2082 
2083 
2084   Result := FFoldNodeInfoList;
2085   Result.SetLineClean(Line);
2086 end;
2087 
2088 procedure TSynCustomFoldHighlighter.ScanFoldNodeInfo;
2089 begin
2090   NextToEol;
2091 end;
2092 
2093 procedure TSynCustomFoldHighlighter.InitFoldNodeInfo(AList: TLazSynFoldNodeInfoList; Line: TLineIdx);
2094 begin
2095   FIsCollectingNodeInfo := True;
2096   try
2097     FCollectingNodeInfoList := TLazSynFoldNodeInfoList(AList);
2098     StartAtLineIndex(Line);
2099     ScanFoldNodeInfo();
2100   finally
2101     FIsCollectingNodeInfo := False;
2102   end;
2103 end;
2104 
CreateFoldNodeInfoListnull2105 function TSynCustomFoldHighlighter.CreateFoldNodeInfoList: TLazSynFoldNodeInfoList;
2106 begin
2107   Result := TLazSynFoldNodeInfoList.Create;
2108 end;
2109 
GetRangeClassnull2110 function TSynCustomFoldHighlighter.GetRangeClass: TSynCustomHighlighterRangeClass;
2111 begin
2112   Result:=TSynCustomHighlighterRange;
2113 end;
2114 
TopCodeFoldBlockTypenull2115 function TSynCustomFoldHighlighter.TopCodeFoldBlockType(DownIndex: Integer = 0): Pointer;
2116 var
2117   Fold: TSynCustomCodeFoldBlock;
2118 begin
2119   Result:=nil;
2120   if (CodeFoldRange<>nil) then begin
2121     Fold := CodeFoldRange.Top;
2122     while (Fold <> nil) and (DownIndex > 0) do begin
2123       Fold := Fold.Parent;
2124       dec(DownIndex);
2125     end;
2126     if Fold <> nil then
2127       Result := Fold.BlockType
2128   end;
2129 end;
2130 
2131 procedure TSynCustomFoldHighlighter.GetTokenBounds(out LogX1, LogX2: Integer);
2132 var p : pchar; L : integer;
2133 begin
2134   GetTokenEx(p,L);
2135   LogX1 := GetTokenPos;
2136   LogX2 := LogX1 + L ;
2137 end;
2138 
StartCodeFoldBlocknull2139 function TSynCustomFoldHighlighter.StartCodeFoldBlock(ABlockType: Pointer;
2140   IncreaseLevel: Boolean; ForceDisabled: Boolean): TSynCustomCodeFoldBlock;
2141 begin
2142   if (PtrUInt(ABlockType) < FoldConfigCount) and (not ForceDisabled) and
2143      (not FoldConfig[PtrUInt(ABlockType)].Enabled) and
2144      (not FoldConfig[PtrUInt(ABlockType)].IsEssential)
2145   then
2146     exit(nil);
2147 
2148   if FIsCollectingNodeInfo then
2149     CollectNodeInfo(False, ABlockType, IncreaseLevel);
2150 
2151   Result:=CodeFoldRange.Add(ABlockType, IncreaseLevel);
2152 end;
2153 
2154 procedure TSynCustomFoldHighlighter.EndCodeFoldBlock(DecreaseLevel: Boolean = True);
2155 var
2156   BlockType: Pointer;
2157 begin
2158   //ABlockType required for detect whether singleline /multiline is being paired
2159   BlockType := TopCodeFoldBlockType;
2160   if FIsCollectingNodeInfo then
2161     CollectNodeInfo(True, BlockType, DecreaseLevel);
2162 
2163   CodeFoldRange.Pop(DecreaseLevel);
2164 end;
2165 
2166 procedure TSynCustomFoldHighlighter.CollectNodeInfo(FinishingABlock: Boolean;
2167   ABlockType: Pointer; LevelChanged: Boolean);
2168 var
2169   //DecreaseLevel,
2170   BlockTypeEnabled, BlockConfExists: Boolean;
2171   act: TSynFoldActions;
2172   nd: TSynFoldNodeInfo;
2173 begin
2174   if not IsCollectingNodeInfo then exit;
2175 
2176   BlockConfExists := (PtrUInt(ABlockType) < FoldConfigCount);  // how about pascal that has blocktype > foldconfigcount?
2177   //BlockConfExists := HasFoldConfig(PtrUInt(ABlockType));
2178   BlockTypeEnabled := False;
2179   if BlockConfExists then
2180     BlockTypeEnabled := FoldConfig[PtrUInt(ABlockType)].Enabled;
2181 
2182   //Start
2183   if not FinishingABlock then
2184   begin
2185     act := [sfaOpen, sfaOpenFold]; // todo deprecate sfaOpenFold
2186     if BlockTypeEnabled then
2187       act := act + FoldConfig[PtrUInt(ABlockType)].FoldActions
2188     else
2189     if not BlockConfExists then
2190       act := act + [sfaFold,sfaFoldFold, sfaMarkup, sfaOutline];
2191   end
2192   else
2193   //Finish
2194   begin
2195     act := [sfaClose, sfaCloseFold]; // todo deprecate sfaCloseFold
2196     if BlockTypeEnabled then
2197       act := act + FoldConfig[PtrUInt(ABlockType)].FoldActions
2198     else
2199     if not BlockConfExists then
2200       act := act + [sfaFold, sfaFoldFold, sfaMarkup, sfaOutline];
2201     act := act - [sfaFoldFold, sfaFoldHide]; // it is closing tag
2202   end;
2203 
2204   DoInitNode(nd{%H-}, FinishingABlock, ABlockType, act, LevelChanged);
2205   FCollectingNodeInfoList.Add(nd);
2206 end;
2207 
2208 procedure TSynCustomFoldHighlighter.DoInitNode(var Node: TSynFoldNodeInfo;
2209   FinishingABlock: Boolean; ABlockType: Pointer;
2210   aActions: TSynFoldActions; AIsFold: Boolean);
2211 var
2212   OneLine: Boolean;
2213   EndOffs: Integer;
2214   LogX1, LogX2: Integer;
2215 
2216 begin
2217   GetTokenBounds(LogX1, LogX2);
2218 
2219   aActions := aActions + [sfaMultiLine];
2220   if FinishingABlock then
2221     EndOffs := -1
2222   else
2223     EndOffs := +1;
2224   Node.LineIndex := LineIndex;
2225   Node.LogXStart := LogX1;
2226   Node.LogXEnd := LogX2;
2227   Node.FoldType := ABlockType;
2228   Node.FoldTypeCompatible := ABlockType;
2229   Node.FoldAction := aActions;
2230   node.FoldGroup := 1;//FOLDGROUP_PASCAL;
2231   Node.FoldLvlStart := CodeFoldRange.CodeFoldStackSize; // If "not AIsFold" then the node has no foldlevel of its own
2232   Node.NestLvlStart := CodeFoldRange.NestFoldStackSize;
2233   OneLine := FinishingABlock and (Node.FoldLvlStart > CodeFoldRange.MinimumCodeFoldBlockLevel);
2234   Node.NestLvlEnd := Node.NestLvlStart + EndOffs;
2235   if not (sfaFold in aActions) then
2236     EndOffs := 0;
2237   Node.FoldLvlEnd := Node.FoldLvlStart + EndOffs;
2238   if OneLine then  // find opening node
2239     RepairSingleLineNode(Node);
2240 end;
2241 
2242 procedure TSynCustomFoldHighlighter.RepairSingleLineNode(var Node: TSynFoldNodeInfo);
2243 var
2244   nd: PSynFoldNodeInfo;
2245   i : integer;
2246 begin
2247     i := FCollectingNodeInfoList.CountAll - 1;
2248     nd := FCollectingNodeInfoList.ItemPointer[i];
2249     while (i >= 0) and
2250           ( (nd^.FoldType <> node.FoldType) or
2251             (nd^.FoldGroup <> node.FoldGroup) or
2252             (not (sfaOpenFold in nd^.FoldAction))
2253             or (nd^.FoldLvlEnd <> Node.FoldLvlStart)
2254           )
2255     do begin
2256       dec(i);
2257       nd := FCollectingNodeInfoList.ItemPointer[i];
2258     end;
2259     if i >= 0 then begin
2260       nd^.FoldAction  := nd^.FoldAction + [sfaOneLineOpen, sfaSingleLine] - [sfaMultiLine];
2261       Node.FoldAction := Node.FoldAction + [sfaOneLineClose, sfaSingleLine] - [sfaMultiLine];
2262       if (sfaFoldHide in nd^.FoldAction) then begin
2263         assert(sfaFold in nd^.FoldAction, 'sfaFoldHide without sfaFold');
2264         // one liner: hide-able / not fold-able
2265         nd^.FoldAction  := nd^.FoldAction - [sfaFoldFold];
2266         Node.FoldAction := Node.FoldAction - [sfaFoldFold];
2267       end else begin
2268         // one liner: nether hide-able nore fold-able
2269         nd^.FoldAction  := nd^.FoldAction - [sfaOpenFold, sfaFold, sfaFoldFold];
2270         Node.FoldAction := Node.FoldAction - [sfaCloseFold, sfaFold, sfaFoldFold];
2271       end;
2272     end;
2273 end;
2274 
2275 procedure TSynCustomFoldHighlighter.CreateRootCodeFoldBlock;
2276 begin
2277   FRootCodeFoldBlock := TSynCustomCodeFoldBlock.Create;
2278 end;
2279 
2280 { TSynCustomCodeFoldBlock }
2281 
GetChildnull2282 function TSynCustomCodeFoldBlock.GetChild(ABlockType: Pointer): TSynCustomCodeFoldBlock;
2283 begin
2284   if assigned(FChildren) then
2285     Result := FChildren.GetOrCreateSibling(ABlockType)
2286   else begin
2287     Result := TSynCustomCodeFoldBlock(self.ClassType.Create);
2288     Result.FBlockType := ABlockType;
2289     Result.FParent := self;
2290     FChildren := Result;
2291   end;
2292 end;
2293 
2294 var
2295   CreateSiblingBalanceList: Array of TSynCustomCodeFoldBlock;
2296 
GetOrCreateSiblingnull2297 function TSynCustomCodeFoldBlock.GetOrCreateSibling(ABlockType: Pointer): TSynCustomCodeFoldBlock;
2298   procedure BalanceNode(TheNode: TSynCustomCodeFoldBlock);
2299   var
2300     i, l: Integer;
2301     t: Pointer;
2302     N, P, C: TSynCustomCodeFoldBlock;
2303   begin
2304     l := length(CreateSiblingBalanceList);
2305     i := 0;
2306     t := TheNode.FBlockType;
2307     N := self;
2308     while N.FBlockType <> t do begin
2309       if i >= l then begin
2310         inc(l, 20);
2311         SetLength(CreateSiblingBalanceList, l);
2312       end;
2313       CreateSiblingBalanceList[i] := N; // Record all parents
2314       inc(i);
2315       if t < N.FBlockType
2316       then N := N.FLeft
2317       else N := N.FRight;
2318     end;
2319     if i >= l then begin
2320       inc(l, 20);
2321       SetLength(CreateSiblingBalanceList, l);
2322     end;
2323     CreateSiblingBalanceList[i] := TheNode;
2324     while i >= 0 do begin
2325       if CreateSiblingBalanceList[i].FBalance = 0
2326         then exit;
2327       if (CreateSiblingBalanceList[i].FBalance = -1) or
2328          (CreateSiblingBalanceList[i].FBalance = 1) then begin
2329         if i = 0 then
2330           exit;
2331         dec(i);
2332         if CreateSiblingBalanceList[i+1] = CreateSiblingBalanceList[i].FLeft
2333         then dec(CreateSiblingBalanceList[i].FBalance)
2334         else inc(CreateSiblingBalanceList[i].FBalance);
2335         continue;
2336       end;
2337       // rotate
2338       P := CreateSiblingBalanceList[i];
2339       if P.FBalance = -2 then begin
2340         N := P.FLeft;
2341         if N.FBalance < 0 then begin
2342           (* ** single rotate ** *)
2343           (*  []\[]_     _C                []_      C_    _[]
2344                     N(-1)_     _[]    =>      []_    _P(0)
2345                           P(-2)                  N(0)           *)
2346           C := N.FRight;
2347           N.FRight := P;
2348           P.FLeft := C;
2349           N.FBalance := 0;
2350           P.FBalance := 0;
2351         end else begin
2352           (* ** double rotate ** *)
2353           (*          x1 x2
2354                []_     _C                  x1    x2
2355                   N(+1)_     _[]    =>    N _    _ P
2356                         P(-2)                 C           *)
2357           C := N.FRight;
2358           N.FRight := C.FLeft;
2359           P.FLeft  := C.FRight;
2360           C.FLeft  := N;
2361           C.FRight := P;
2362           // balance
2363           if (C.FBalance <= 0)
2364           then N.FBalance := 0
2365           else N.FBalance := -1;
2366           if (C.FBalance = -1)
2367           then P.FBalance := 1
2368           else P.FBalance := 0;
2369           C.FBalance := 0;
2370           N := C;
2371         end;
2372       end else begin // *******************
2373         N := P.FRight;
2374         if N.FBalance > 0 then begin
2375           (* ** single rotate ** *)
2376           C := N.FLeft;
2377           N.FLeft := P;
2378           P.FRight := C;
2379           N.FBalance := 0;
2380           P.FBalance := 0;
2381         end else begin
2382           (* ** double rotate ** *)
2383           C := N.FLeft;
2384           N.FLeft := C.FRight;
2385           P.FRight  := C.FLeft;
2386           C.FRight  := N;
2387           C.FLeft := P;
2388           // balance
2389           if (C.FBalance >= 0)
2390           then N.FBalance := 0
2391           else N.FBalance := +1;
2392           if (C.FBalance = +1)
2393           then P.FBalance := -1
2394           else P.FBalance := 0;
2395           C.FBalance := 0;
2396           N := C;
2397         end;
2398       end;
2399       // update parent
2400       dec(i);
2401       if i < 0 then begin
2402         if assigned(self.FParent) then
2403           self.FParent.FChildren := N
2404       end else
2405         if CreateSiblingBalanceList[i].FLeft = P
2406         then CreateSiblingBalanceList[i].FLeft := N
2407         else CreateSiblingBalanceList[i].FRight := N;
2408       break;
2409     end
2410   end;
2411 var
2412   P: TSynCustomCodeFoldBlock;
2413 begin
2414   Result := self;
2415   while (assigned(Result)) do begin
2416     if Result.FBlockType = ABlockType then
2417       exit;
2418     P := Result;
2419     if ABlockType < Result.FBlockType
2420     then Result := Result.FLeft
2421     else Result := Result.FRight;
2422   end;
2423   // Not Found
2424   Result := TSynCustomCodeFoldBlock(self.ClassType.Create);
2425   Result.FBlockType := ABlockType;
2426   Result.FParent := self.FParent;
2427 
2428   if ABlockType < P.FBlockType then begin
2429     P.FLeft := Result;
2430     dec(P.FBalance);
2431   end else begin
2432     P.FRight := Result;
2433     inc(P.FBalance);
2434   end;
2435 
2436   // Balance
2437   if P.FBalance <> 0 then
2438     BalanceNode(P);
2439 
2440 end;
2441 
2442 destructor TSynCustomCodeFoldBlock.Destroy;
2443 begin
2444   FreeAndNil(FRight);
2445   FreeAndNil(FLeft);
2446   FreeAndNil(FChildren);
2447   inherited Destroy;
2448 end;
2449 
2450 procedure TSynCustomCodeFoldBlock.WriteDebugReport;
2451   procedure debugout(n: TSynCustomCodeFoldBlock; s1, s: String; p: TSynCustomCodeFoldBlock);
2452   begin
2453     if n = nil then exit;
2454     if n.FParent <> p then
2455       DebugLn([s1, 'Wrong Parent for', ' (', PtrInt(n), ')']);
2456     DebugLn([s1, PtrUInt(n.BlockType), ' (', PtrInt(n), ')']);
2457     debugout(n.FLeft, s+'L: ', s+'   ', p);
2458     debugout(n.FRight, s+'R: ', s+'   ', p);
2459     debugout(n.FChildren, s+'C: ', s+'   ', n);
2460   end;
2461 begin
2462   debugout(self, '', '', nil);
2463 end;
2464 
2465 procedure TSynCustomCodeFoldBlock.InitRootBlockType(AType: Pointer);
2466 begin
2467   if assigned(FParent) then
2468     raise Exception.Create('Attempt to modify a FoldBlock');
2469   FBlockType := AType;
2470 end;
2471 
2472 { TSynCustomHighlighterRange }
2473 
2474 constructor TSynCustomHighlighterRange.Create(
2475   Template: TSynCustomHighlighterRange);
2476 begin
2477   if (Template<>nil) and (ClassType<>Template.ClassType) then
2478     RaiseGDBException('');
2479   if Template<>nil then
2480     Assign(Template);
2481 end;
2482 
2483 destructor TSynCustomHighlighterRange.Destroy;
2484 begin
2485   Clear;
2486   inherited Destroy;
2487 end;
2488 
Comparenull2489 function TSynCustomHighlighterRange.Compare(Range: TSynCustomHighlighterRange
2490   ): integer;
2491 begin
2492   if RangeType < Range.RangeType then
2493     Result:=1
2494   else if RangeType > Range.RangeType then
2495     Result:=-1
2496   else if Pointer(FTop) < Pointer(Range.FTop) then
2497     Result:= -1
2498   else if Pointer(FTop) > Pointer(Range.FTop) then
2499     Result:= 1
2500   else
2501     Result := FMinimumNestFoldBlockLevel - Range.FMinimumNestFoldBlockLevel;
2502   if Result <> 0 then
2503     exit;
2504   Result := FNestFoldStackSize - Range.FNestFoldStackSize;
2505   if Result <> 0 then
2506     exit;
2507 
2508     Result := FMinimumCodeFoldBlockLevel - Range.FMinimumCodeFoldBlockLevel;
2509   if Result <> 0 then
2510     exit;
2511   Result := FCodeFoldStackSize - Range.FCodeFoldStackSize;
2512 end;
2513 
Addnull2514 function TSynCustomHighlighterRange.Add(ABlockType: Pointer;
2515   IncreaseLevel: Boolean = True): TSynCustomCodeFoldBlock;
2516 var
2517   i: LongInt;
2518 begin
2519   i := MaxFoldLevel;
2520   if (i > 0) and (FCodeFoldStackSize >= i) then begin
2521     //debugln('Reached MaxFoldLevel, ignoring folds');
2522     exit(nil);
2523   end;
2524   Result := FTop.Child[ABlockType];
2525   inc(FNestFoldStackSize);
2526   if IncreaseLevel then
2527     inc(FCodeFoldStackSize);
2528   FTop:=Result;
2529 end;
2530 
2531 procedure TSynCustomHighlighterRange.Pop(DecreaseLevel: Boolean = True);
2532 // can be called, even if there is no stack
2533 // because it's normal that sources under development have unclosed blocks
2534 begin
2535   //debugln('TSynCustomHighlighterRange.Pop');
2536   if assigned(FTop.Parent) then begin
2537     FTop := FTop.Parent;
2538     dec(FNestFoldStackSize);
2539     if FMinimumNestFoldBlockLevel > FNestFoldStackSize then
2540       FMinimumNestFoldBlockLevel := FNestFoldStackSize;
2541     if DecreaseLevel then begin
2542       dec(FCodeFoldStackSize);
2543       if FMinimumCodeFoldBlockLevel > FCodeFoldStackSize then
2544         FMinimumCodeFoldBlockLevel := FCodeFoldStackSize;
2545     end;
2546   end;
2547 end;
2548 
TSynCustomHighlighterRange.MaxFoldLevelnull2549 function TSynCustomHighlighterRange.MaxFoldLevel: Integer;
2550 begin
2551   Result := -1;
2552 end;
2553 
2554 procedure TSynCustomHighlighterRange.Clear;
2555 begin
2556   FRangeType:=nil;
2557   FCodeFoldStackSize := 0;
2558   FNestFoldStackSize := 0;
2559   FMinimumCodeFoldBlockLevel := 0;
2560   FMinimumNestFoldBlockLevel:= 0;
2561   FTop:=nil;
2562 end;
2563 
2564 procedure TSynCustomHighlighterRange.Assign(Src: TSynCustomHighlighterRange);
2565 begin
2566   if (Src<>nil) and (Src<>TSynCustomHighlighterRange(NullRange)) then begin
2567     FTop := Src.FTop;
2568     FCodeFoldStackSize := Src.FCodeFoldStackSize;
2569     FMinimumCodeFoldBlockLevel := Src.FMinimumCodeFoldBlockLevel;
2570     FNestFoldStackSize := Src.FNestFoldStackSize;
2571     FMinimumNestFoldBlockLevel := Src.FMinimumNestFoldBlockLevel;
2572     FRangeType := Src.FRangeType;
2573   end
2574   else begin
2575     FTop := nil;
2576     FCodeFoldStackSize := 0;
2577     FNestFoldStackSize := 0;
2578     FMinimumCodeFoldBlockLevel := 0;
2579     FMinimumNestFoldBlockLevel := 0;
2580     FRangeType := nil;
2581   end;
2582 end;
2583 
2584 procedure TSynCustomHighlighterRange.WriteDebugReport;
2585 begin
2586   debugln('TSynCustomHighlighterRange.WriteDebugReport ',DbgSName(Self),
2587     ' RangeType=',dbgs(RangeType),' StackSize=',dbgs(CodeFoldStackSize));
2588   debugln(' Block=',dbgs(PtrInt(FTop)));
2589   FTop.WriteDebugReport;
2590 end;
2591 
2592 { TSynCustomHighlighterRanges }
2593 
2594 constructor TSynCustomHighlighterRanges.Create(
2595   TheHighlighterClass: TSynCustomHighlighterClass);
2596 begin
2597   Allocate;
2598   FItems:=TAvlTree.Create(@CompareSynHighlighterRanges);
2599 end;
2600 
2601 destructor TSynCustomHighlighterRanges.Destroy;
2602 begin
2603   if HighlighterRanges<>nil then begin
2604     HighlighterRanges.Remove(Self);
2605     if HighlighterRanges.Count=0 then
2606       FreeAndNil(HighlighterRanges);
2607   end;
2608   FItems.FreeAndClear;
2609   FreeAndNil(FItems);
2610   inherited Destroy;
2611 end;
2612 
GetEqualnull2613 function TSynCustomHighlighterRanges.GetEqual(Range: TSynCustomHighlighterRange
2614   ): TSynCustomHighlighterRange;
2615 var
2616   Node: TAvlTreeNode;
2617 begin
2618   if Range=nil then exit(nil);
2619   Node:=FItems.Find(Range);
2620   if Node<>nil then begin
2621     Result:=TSynCustomHighlighterRange(Node.Data);
2622   end else begin
2623     // add a copy
2624     Result:=TSynCustomHighlighterRangeClass(Range.ClassType).Create(Range);
2625     FItems.Add(Result);
2626     //if FItems.Count mod 32 = 0 then debugln(['FOLDRANGE Count=', FItems.Count]);
2627   end;
2628   //debugln('TSynCustomHighlighterRanges.GetEqual A ',dbgs(Node),' ',dbgs(Result.Compare(Range)),' ',dbgs(Result.CodeFoldStackSize));
2629 end;
2630 
2631 procedure TSynCustomHighlighterRanges.Allocate;
2632 begin
2633   inc(FAllocatedCount);
2634 end;
2635 
2636 procedure TSynCustomHighlighterRanges.Release;
2637 begin
2638   dec(FAllocatedCount);
2639   if FAllocatedCount=0 then Free;
2640 end;
2641 
2642 { TSynCustomFoldConfig }
2643 
2644 procedure TSynCustomFoldConfig.SetEnabled(const AValue: Boolean);
2645 begin
2646   if FEnabled = AValue then exit;
2647   FEnabled := AValue;
2648   DoOnChange;
2649 end;
2650 
2651 procedure TSynCustomFoldConfig.SetModes(AValue: TSynCustomFoldConfigModes);
2652 begin
2653   AValue := AValue * FSupportedModes;
2654   if FModes = AValue then exit;
2655   FModes := AValue;
2656   FFoldActions := [];
2657   if fmFold   in AValue then FFoldActions := FFoldActions + [sfaFold, sfaFoldFold];
2658   if fmHide   in AValue then FFoldActions := FFoldActions + [sfaFold, sfaFoldHide];
2659   if fmMarkup in AValue then FFoldActions := FFoldActions + [sfaMarkup];
2660   if fmOutline in AValue then FFoldActions := FFoldActions + [sfaOutline];
2661   DoOnChange;
2662 end;
2663 
2664 procedure TSynCustomFoldConfig.SetSupportedModes(AValue: TSynCustomFoldConfigModes);
2665 begin
2666   if FSupportedModes = AValue then Exit;
2667   FSupportedModes := AValue;
2668   Modes := Modes * FSupportedModes;
2669 end;
2670 
2671 procedure TSynCustomFoldConfig.DoOnChange;
2672 begin
2673   if assigned(FOnChange) then
2674     FOnChange(self);
2675 end;
2676 
2677 constructor TSynCustomFoldConfig.Create;
2678 begin
2679   Inherited;
2680   FIsEssential := True;
2681   FSupportedModes := [fmFold];
2682   Modes := [fmFold];
2683 end;
2684 
2685 constructor TSynCustomFoldConfig.Create(
2686   ASupportedModes: TSynCustomFoldConfigModes; AnIsEssential: Boolean);
2687 begin
2688   Create;
2689   FSupportedModes := ASupportedModes;
2690   FIsEssential := AnIsEssential;
2691 end;
2692 
2693 procedure TSynCustomFoldConfig.Assign(Src: TSynCustomFoldConfig);
2694 begin
2695   Enabled := Src.Enabled;
2696   SupportedModes := Src.SupportedModes;
2697   Modes := Src.Modes;
2698 end;
2699 
2700 end.
2701 
2702