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