1{------------------------------------------------------------------------------- 2The contents of this file are subject to the Mozilla Public License 3Version 1.1 (the "License"); you may not use this file except in compliance 4with the License. You may obtain a copy of the License at 5http://www.mozilla.org/MPL/ 6 7Software distributed under the License is distributed on an "AS IS" basis, 8WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for 9the specific language governing rights and limitations under the License. 10 11Alternatively, the contents of this file may be used under the terms of the 12GNU General Public License Version 2 or later (the "GPL"), in which case 13the provisions of the GPL are applicable instead of those above. 14If you wish to allow use of your version of this file only under the terms 15of the GPL and not to allow others to use your version of this file 16under the MPL, indicate your decision by deleting the provisions above and 17replace them with the notice and other provisions required by the GPL. 18If you do not delete the provisions above, a recipient may use your version 19of this file under either the MPL or the GPL. 20 21-------------------------------------------------------------------------------} 22(* some parts (AdjustBalance...) of this unit are based on the AVLTree unit *) 23(* TODO: Implement node.eof / node.bof *) 24unit SynEditFoldedView; 25 26{$mode objfpc}{$H+} 27{$IFDEF CPUPOWERPC} {$INLINE OFF} {$ENDIF} (* Workaround for bug 12576 (fpc) see bugs.freepascal.org/view.php?id=12576 *) 28 29{$IFOPT C+} 30 {$DEFINE SynAssertFold} 31{$ENDIF} 32{$IFDEF SynAssert} 33 {$DEFINE SynAssertFold} 34{$ENDIF} 35 36{$IFDEF SynFoldDebug} 37 {$DEFINE SynDebug} 38 {$DEFINE SynFoldSaveDebug} 39{$ENDIF} 40{$IFDEF SynFoldSaveDebug} 41 {$DEFINE SynDebug} 42{$ENDIF} 43 44interface 45 46uses 47 Classes, SysUtils, 48 // LCL 49 LCLProc, Graphics, LCLType, 50 // LazUtils 51 LazLoggerBase, LazMethodList, 52 // SynEdit 53 LazSynEditText, SynEditTypes, SynEditMiscClasses, SynEditMiscProcs, 54 SynEditPointClasses, SynEditHighlighter, SynEditHighlighterFoldBase, 55 SynEditKeyCmds; 56 57type 58 59 TFoldNodeClassification = ( 60 fncInvalid, 61 fncHighlighter, // Fold provided by HL 62 fncHighlighterEx, // Fold originally provided by HL, but no longer exists in HL (text edited) 63 fncBlockSelection 64 ); 65 TFoldNodeClassifications = set of TFoldNodeClassification; 66 67 { TSynTextFoldAVLNodeData } 68 69 TSynTextFoldAVLNodeData = class(TSynSizedDifferentialAVLNode) 70 protected 71 function Left: TSynTextFoldAVLNodeData; 72 function Parent: TSynTextFoldAVLNodeData; 73 function Right: TSynTextFoldAVLNodeData; 74 procedure FreeAllChildrenAndNested; 75 public (* Position / Size *) 76 (* FullCount: Amount of lines in source for this fold only 77 (excluding overlaps) *) 78 FullCount : Integer; 79 (* LineOffset: Line-Number Offset to parent node 80 All line numbers are stored as offsets, 81 for faster updates if lines are inserted/deleted *) 82 property LineOffset: Integer read FPositionOffset write FPositionOffset; 83 (* LeftCount: Lines folded in left tree. 84 Used to calculate how many lines are folded up to a specified line *) 85 property LeftCount: Integer read FLeftSizeSum write FLeftSizeSum; 86 (* MergedLineCount: Amount of lines folded away by this fold, 87 FullCount + Lines covered by overlaps *) 88 property MergedLineCount: Integer read FSize write FSize; 89 public 90 (* Sub-Tree *) 91 Nested : TSynTextFoldAVLNodeData; (* Nested folds (folds within this fold) do not need to be part of the searchable tree 92 They will be restored, if the outer fold (this fold) is unfolded 93 Nested points to a standalone tree, the root node in the nested tree, does *not* point back to this node *) 94 95 96 (* Source Info *) 97 FoldIndex: Integer; (* Index of fold in line; if a line has more than one fold starting *) 98 FoldColumn, FoldColumnLen: Integer; (* The column (1-based) and len of the keywordm which starts this fold *) 99 FoldTypeCompatible: Pointer; (* help identifying in FixFolding *) 100 Classification: TFoldNodeClassification; 101 VisibleLines: Integer; (* Visible Source lines, containing the "fold keyword" 102 0: Hiden block (the fold-keyword is inside the fold) 103 1: Normal fold (There is *1* visible line with the fold-keyword) 104 *) 105 106 107 function RecursiveFoldCount : Integer; (* Amount of lines covered by this and all child nodes *) 108 function Precessor : TSynTextFoldAVLNodeData; reintroduce; 109 function Successor : TSynTextFoldAVLNodeData; reintroduce; 110 function Precessor(var aStartPosition, aSizesBeforeSum : Integer) : TSynTextFoldAVLNodeData; reintroduce; 111 function Successor(var aStartPosition, aSizesBeforeSum : Integer) : TSynTextFoldAVLNodeData; reintroduce; 112 end; 113 114 { TSynTextFoldAVLNode } 115 116 TSynTextFoldAVLNode = object 117 private 118 function GetClassification: TFoldNodeClassification; 119 function GetFoldColumn: Integer; 120 function GetFoldColumnLen: Integer; 121 function GetFoldIndex: Integer; 122 function GetMergedLineCount : Integer; 123 function GetFullCount : Integer; 124 function GetSourceLine: integer; 125 function GetSourceLineOffset: integer; 126 procedure SetFoldColumn(const AValue: Integer); 127 protected 128 fData : TSynTextFoldAVLNodeData; // nil if unfolded 129 fStartLine : Integer; // start of folded 130 fFoldedBefore : Integer; 131 public 132 procedure Init(aData : TSynTextFoldAVLNodeData; aStartLine, aFoldedBefore: Integer); 133 function IsInFold : Boolean; 134 function Next : TSynTextFoldAVLNode; 135 function Prev : TSynTextFoldAVLNode; 136 137 property MergedLineCount: Integer read GetMergedLineCount; // Zero, if Not in a fold 138 property FullCount: Integer read GetFullCount; // Zero, if Not in a fold 139 property StartLine: Integer read fStartLine; // 1st Line of Current Fold 140 property FoldedBefore: Integer read fFoldedBefore; // Count of Lines folded before Startline 141 142 function IsHide: Boolean; 143 property FoldIndex: Integer read GetFoldIndex; 144 property FoldColumn: Integer read GetFoldColumn write SetFoldColumn; 145 property FoldColumnLen: Integer read GetFoldColumnLen; 146 property SourceLine: integer read GetSourceLine; // The SourceLine with the fold-keyword 147 property SourceLineOffset: integer read GetSourceLineOffset; // The SourceLine with the fold-keyword 148 property Classification: TFoldNodeClassification read GetClassification; 149 end; 150 151 { TSynTextFoldAVLNodeNestedIterator: 152 Iterates included nested nodes 153 FoldedBefore is not valid in nested nodes 154 } 155 156 TSynTextFoldAVLNodeNestedIterator = class 157 private 158 FCurrentNode: TSynTextFoldAVLNode; 159 FOuterNodes: Array of TSynTextFoldAVLNode; 160 public 161 constructor Create(ANode: TSynTextFoldAVLNode); 162 destructor Destroy; override; 163 function Next: TSynTextFoldAVLNode; 164 function Prev: TSynTextFoldAVLNode; 165 function EOF: Boolean; 166 function BOF: Boolean; 167 function IsInFold: Boolean; 168 property Node: TSynTextFoldAVLNode read FCurrentNode; 169 end; 170 171 { TSynTextFoldAVLTree 172 - Nodes in the tree cover the folded lines only. 173 The (visible) cfCollapsed line at the start of a fold, is *not* part of a node. 174 - In the public methods "ALine" indicates the first invisible/hidden line 175 - TSynEditFoldedView uses this with 1-based lines (ToDo: make 0-based) 176 } 177 178 TSynTextFoldAVLTree = class(TSynSizedDifferentialAVLTree) 179 protected 180 fNestParent: TSynTextFoldAVLNodeData; 181 fNestedNodesTree: TSynTextFoldAVLTree; // FlyWeight Tree used for any nested subtree. 182 183 function NewNode : TSynTextFoldAVLNodeData; inline; 184 Function RemoveFoldForNodeAtLine(ANode: TSynTextFoldAVLNode; 185 ALine : Integer) : Integer; overload; // Line is for Nested Nodes 186 187 // SetRoot, does not obbey fRootOffset => use SetRoot(node, -fRootOffset) 188 procedure SetRoot(ANode : TSynSizedDifferentialAVLNode); overload; override; 189 procedure SetRoot(ANode : TSynSizedDifferentialAVLNode; anAdjustChildLineOffset : Integer); overload; override; 190 191 Function InsertNode(ANode : TSynTextFoldAVLNodeData) : Integer; reintroduce; // returns FoldedBefore // ANode may not have children 192 function TreeForNestedNode(ANode: TSynTextFoldAVLNodeData; aOffset : Integer) : TSynTextFoldAVLTree; 193 public 194 constructor Create; 195 destructor Destroy; override; 196 procedure Clear; override; 197 198 (* Find Fold by Line in Real Text *) 199 Function FindFoldForLine(ALine : Integer; FindNextNode : Boolean = False) : TSynTextFoldAVLNode; 200 (* Find Fold by Line in Folded Text // always returns unfolded, unless next=true *) 201 Function FindFoldForFoldedLine(ALine : Integer; FindNextNode: Boolean = False) : TSynTextFoldAVLNode; 202 Function InsertNewFold(ALine, AFoldIndex, AColumn, AColumnLen, ACount, AVisibleLines: Integer; 203 AClassification: TFoldNodeClassification; 204 AFoldTypeCompatible: Pointer 205 ) : TSynTextFoldAVLNode; 206 (* This will unfold the block which either contains tALine, or has Aline as its cgColapsed line 207 If IgnoreFirst, the cfCollapsed will *not* unfold => Hint: IgnoreFirst = Make folded visible 208 Returns the pos(1-based) of the cfCollapsed Line that was expanded; or ALine, if nothing was done 209 *) 210 Function RemoveFoldForLine(ALine : Integer; OnlyCol: Integer = -1) : Integer; overload; 211 Procedure AdjustForLinesInserted(AStartLine, ALineCount, ABytePos: Integer); 212 Procedure AdjustForLinesDeleted(AStartLine, ALineCount, ABytePos: Integer); 213 procedure AdjustColumn(ALine, ABytePos, ACount: Integer; InLineBreak: boolean = False); 214 Function FindLastFold : TSynTextFoldAVLNode; 215 Function FindFirstFold : TSynTextFoldAVLNode; 216 Function LastFoldedLine : integer; // The actual line; LastNode.StartLine + LastNode.LineCount - 1 217 {$IFDEF SynDebug} 218 procedure Debug; reintroduce; 219 {$ENDIF} 220 end; 221 222 { TSynFoldNodeInfoHelper } 223 224 TSynFoldNodeInfoHelper = class 225 FCurInfo: TSynFoldNodeInfo; 226 FActions: TSynFoldActions; 227 FHighlighter: TSynCustomFoldHighlighter; 228 protected 229 procedure Invalidate; 230 public 231 constructor Create(AHighlighter: TSynCustomFoldHighlighter); 232 233 function FirstOpen: TSynFoldNodeInfo; 234 function Next: TSynFoldNodeInfo; 235 function Prev: TSynFoldNodeInfo; 236 function FindClose: TSynFoldNodeInfo; 237 function GotoOpenPos(aLineIdx, aNodeIdx: integer): TSynFoldNodeInfo; 238 function GotoOpenAtChar(aLineIdx, aXPos: integer): TSynFoldNodeInfo; 239 function GotoNodeOpenPos(ANode : TSynTextFoldAVLNode): TSynFoldNodeInfo; 240 function GotoNodeClosePos(ANode : TSynTextFoldAVLNode): TSynFoldNodeInfo; 241 function IsAtNodeOpenPos(ANode : TSynTextFoldAVLNode): Boolean; 242 function IsValid: Boolean; 243 function Equals(AnInfo: TSynFoldNodeInfo): Boolean; 244 function Equals(AHelper: TSynFoldNodeInfoHelper): Boolean; 245 246 property Info: TSynFoldNodeInfo read FCurInfo write FCurInfo; 247 property Actions: TSynFoldActions read FActions write FActions; 248 end; 249 250 TFoldChangedEvent = procedure(aLine: Integer) of object; 251 TInvalidateLineProc = procedure(FirstLine, LastLine: integer) of object; 252 253 TFoldViewNodeInfo = record 254 HNode: TSynFoldNodeInfo; // Highlighter Node 255 IsFold, IsHide: Boolean; 256 Text, Keyword: String; 257 LineNum, ColIndex: Integer; 258 OpenCount: Integer; // Highlighter-Nodes opening on this line (limited to the FoldGroup requested) 259 end; 260 261 TSynEditFoldLineCapability = ( 262 // Capabilities of Line 263 cfFoldStart, cfHideStart, 264 cfFoldBody, 265 cfFoldEnd, 266 // State indicators 267 cfCollapsedFold, 268 cfCollapsedHide, // lines hidden, after this line 269 // Special flags 270 cfSingleLineHide, 271 cfNone 272 ); 273 TSynEditFoldLineCapabilities = set of TSynEditFoldLineCapability; 274 TSynEditFoldType = (scftOpen, scftFold, scftHide, scftAll, scftInvalid); 275 276 TSynEditFoldLineMapInfo = record 277 Capability: TSynEditFoldLineCapabilities; 278 Classifications :TFoldNodeClassifications; 279 end; 280 281 {$IFDEF SynFoldSaveDebug} 282const 283 SynEditFoldTypeNames: Array [TSynEditFoldType] of string = 284 ('scftOpen', 'scftFold', 'scftHide', 'scftAll', 'scftInvalid'); 285type 286 {$ENDIF} 287 288 { TSynEditFoldProvider } 289 TSynEditFoldProviderNodeInfo = record 290 LineCount: Integer; 291 Column, ColumnLen: Integer; 292 DefaultCollapsed: Boolean; 293 FoldTypeCompatible: Pointer; // eg begin, var, procedure 294 FoldGroup: Integer; // eg.: pas, region, ifdef 295 Classification: TFoldNodeClassification; 296 end; 297 298 TSynEditFoldProviderNodeInfoList = array of TSynEditFoldProviderNodeInfo; 299 TSynEditFoldProvider = class; 300 301 TSynEditFoldProvider = class 302 private 303 FEnabled: boolean; 304 FHighlighter: TSynCustomFoldHighlighter; 305 FLines : TSynEditStrings; 306 FEdit: TSynEditBase; 307 FNestedFoldsList: TLazSynEditNestedFoldsList; 308 function GetFoldsAvailable: Boolean; 309 function GetHighLighterWithLines: TSynCustomFoldHighlighter; 310 function GetLineCapabilities(ALineIdx: Integer): TSynEditFoldLineCapabilities; 311 function GetLineClassification(ALineIdx: Integer): TFoldNodeClassifications; 312 function GetNestedFoldsList: TLazSynEditNestedFoldsList; 313 procedure SetHighLighter(const AValue: TSynCustomFoldHighlighter); 314 procedure SetLines(AValue: TSynEditStrings); 315 protected 316 property HighLighterWithLines: TSynCustomFoldHighlighter read GetHighLighterWithLines; 317 public 318 constructor Create; 319 destructor Destroy; override; 320 321 // Info about Folds opening on ALineIdx 322 function FoldOpenCount(ALineIdx: Integer; AType: Integer = 0): Integer; 323 function FoldOpenInfo(ALineIdx, AFoldIdx: Integer; AType: Integer = 0): TSynFoldNodeInfo; 324 //property FoldOpenInfo[ALineIdx, AColumnIdx: Integer]: Integer read GetFoldOpenInfo; 325 326 function FoldLineLength(ALine, AFoldIndex: Integer): integer; 327 function InfoForFoldAtTextIndex(ALine, AFoldIndex : Integer; 328 HideLen: Boolean = False; 329 NeedLen: Boolean = True): TSynEditFoldProviderNodeInfo; 330 function InfoListForFoldsAtTextIndex(ALine: Integer; NeedLen: Boolean = False): TSynEditFoldProviderNodeInfoList; 331 332 property LineCapabilities[ALineIdx: Integer]: TSynEditFoldLineCapabilities 333 read GetLineCapabilities; 334 property LineClassification[ALineIdx: Integer]: TFoldNodeClassifications 335 read GetLineClassification; 336 property Lines: TSynEditStrings read FLines write SetLines; 337 property HighLighter: TSynCustomFoldHighlighter read FHighlighter write SetHighLighter; 338 property FoldsAvailable: Boolean read GetFoldsAvailable; 339 property NestedFoldsList: TLazSynEditNestedFoldsList read GetNestedFoldsList; 340 property Enabled: boolean read FEnabled write FEnabled; 341 end; 342 343 { TFoldChangedHandlerList } 344 345 TFoldChangedHandlerList = class(TMethodList) 346 public 347 procedure CallFoldChangedEvents(AnIndex: Integer); 348 end; 349 350 TSynEditFoldedView = class; 351 352 { TLazSynDisplayFold } 353 354 TLazSynDisplayFold = class(TLazSynDisplayViewEx) 355 private 356 FFoldView: TSynEditFoldedView; 357 FLineState: integer; 358 FTokenAttr: TSynHighlighterAttributesModifier; 359 FMarkupLine: TSynSelectedColorMergeResult; 360 FLineFlags, FLineFlags2: TSynEditFoldLineCapabilities; 361 public 362 constructor Create(AFoldView: TSynEditFoldedView); 363 destructor Destroy; override; 364 procedure SetHighlighterTokensLine(ALine: TLineIdx; out ARealLine: TLineIdx; out AStartBytePos, ALineByteLen: Integer); override; 365 function GetNextHighlighterToken(out ATokenInfo: TLazSynDisplayTokenInfo): Boolean; override; 366 function GetLinesCount: Integer; override; 367 368 function TextToViewIndex(ATextIndex: TLineIdx): TLineRange; override; 369 function ViewToTextIndex(AViewIndex: TLineIdx): TLineIdx; override; 370 function ViewToTextIndexEx(AViewIndex: TLineIdx; out AViewRange: TLineRange): TLineIdx; override; 371 end; 372 373 { TSynTextFoldedView 374 *Line = Line (0-based) on Screen (except TopLine which should be TopViewPos) 375 *ViewPos = Line (1-based) in the array of viewable/visible lines 376 *TextIndex = Line (0-based) in the complete text(folded and unfolded) 377 } 378 379 TSynEditFoldedViewFlag = (fvfNeedCaretCheck, fvfNeedCalcMaps); 380 TSynEditFoldedViewFlags = set of TSynEditFoldedViewFlag; 381 382 { TSynEditFoldedView } 383 384 TSynEditFoldedView = class(TSynEditStringsLinked) 385 private 386 FOwner: TSynEditBase; 387 fCaret: TSynEditCaret; 388 FFoldProvider: TSynEditFoldProvider; 389 fFoldTree : TSynTextFoldAVLTree; // Folds are stored 1-based (the 1st line is 1) 390 FMarkupInfoFoldedCode: TSynSelectedColor; 391 FMarkupInfoFoldedCodeLine: TSynSelectedColor; 392 FMarkupInfoHiddenCodeLine: TSynSelectedColor; 393 fTopLine : Integer; 394 fLinesInWindow : Integer; // there may be an additional part visible line 395 fTextIndexList : Array of integer; (* Map each Screen line into a line in textbuffer *) 396 fFoldTypeList : Array of TSynEditFoldLineMapInfo; 397 fLockCount : Integer; 398 fNeedFixFrom, fNeedFixMinEnd : Integer; 399 FFlags: TSynEditFoldedViewFlags; 400 FInTopLineChanged: Boolean; 401 FDisplayView: TLazSynDisplayFold; 402 403 function GetFoldClasifications(index : Integer): TFoldNodeClassifications; 404 function GetHighLighter: TSynCustomHighlighter; 405 function GetDisplayNumber(index : Integer) : Integer; 406 function GetTextIndex(index : Integer) : Integer; 407 function GetFoldType(index : Integer) : TSynEditFoldLineCapabilities; 408 function IsFolded(index : integer) : Boolean; // TextIndex 409 procedure ProcessMySynCommand(Sender: TObject; AfterProcessing: boolean; 410 var Handled: boolean; var Command: TSynEditorCommand; 411 var AChar: TUTF8Char; Data: pointer; HandlerData: pointer); 412 procedure SetHighLighter(AValue: TSynCustomHighlighter); 413 procedure SetTopLine(const ALine : integer); 414 function GetTopTextIndex : integer; 415 procedure SetTopTextIndex(const AIndex : integer); 416 procedure SetLinesInWindow(const AValue : integer); 417 procedure DoFoldChanged(AnIndex: Integer); 418 function TextIndexAddLines(aTextIndex, LineOffset : Integer) : Integer; (* Add/Sub to/from TextIndex (0-based) skipping folded *) 419 protected 420 procedure SetManager(AManager: TSynTextViewsManager); override; 421 procedure SetSynStrings(AValue: TSynEditStrings); override; 422 function GetViewedLines(index : Integer) : String; override; 423 function GetViewedCount: integer; override; 424 function GetDisplayView: TLazSynDisplayView; override; 425 procedure InternalGetInfoForViewedXY(AViewedXY: TPhysPoint; 426 AFlags: TViewedXYInfoFlags; out AViewedXYInfo: TViewedXYInfo; 427 ALogPhysConvertor: TSynLogicalPhysicalConvertor); override; 428 procedure DoBlockSelChanged(Sender: TObject; Changes: TSynStatusChanges); 429 Procedure CalculateMaps; 430 function FoldNodeAtTextIndex(AStartIndex, ColIndex: Integer): TSynTextFoldAVLNode; (* Returns xth Fold at nth TextIndex (all lines in buffer) / 1-based *) 431 function FixFolding(AStart : Integer; AMinEnd : Integer; aFoldTree : TSynTextFoldAVLTree) : Boolean; 432 433 procedure DoCaretChanged(Sender : TObject); 434 Procedure LineCountChanged(Sender: TSynEditStrings; AIndex, ACount : Integer); 435 Procedure LinesCleared(Sender: TObject); 436 Procedure LineEdited(Sender: TSynEditStrings; aLinePos, aBytePos, aCount, 437 aLineBrkCnt: Integer; aText: String); 438 Procedure LinesInsertedAtTextIndex(AStartIndex, ALineCount, ABytePos: Integer; 439 SkipFixFolding : Boolean = False); 440 //Procedure LinesInsertedAtViewPos(AStartPos, ALineCount : Integer; 441 // SkipFixFolding : Boolean = False); 442 Procedure LinesDeletedAtTextIndex(AStartIndex, ALineCount, ABytePos: Integer; 443 SkipFixFolding : Boolean = False); 444 //Procedure LinesDeletedAtViewPos(AStartPos, ALineCount : Integer; 445 // SkipFixFolding : Boolean = False); 446 property FoldTree: TSynTextFoldAVLTree read fFoldTree; 447 public 448 constructor Create(AOwner: TSynEditBase; ACaret: TSynEditCaret); 449 destructor Destroy; override; 450 451 // Converting between Folded and Unfolded Lines/Indexes 452 function TextToViewIndex(aTextIndex : TLineIdx) : TLineIdx; override; (* Convert TextIndex (0-based) to ViewPos (1-based) *) 453 function ViewToTextIndex(aViewIndex : TLineIdx) : TLineIdx; override; (* Convert ViewPos (1-based) to TextIndex (0-based) *) 454 function TextXYToViewXY(APhysTextXY: TPhysPoint): TPhysPoint; override; 455 function ViewXYToTextXY(APhysViewXY: TPhysPoint): TPhysPoint; override; 456 457 function InternTextToViewIndex(aTextIndex : TLineIdx) : TLineIdx; (* Convert TextIndex (0-based) to ViewPos (1-based) *) 458 function InternViewToTextIndex(aViewIndex : TLineIdx) : TLineIdx; (* Convert ViewPos (1-based) to TextIndex (0-based) *) 459 460 function TextIndexToScreenLine(aTextIndex : Integer) : Integer; (* Convert TextIndex (0-based) to Screen (0-based) *) 461 function ScreenLineToTextIndex(aLine : Integer) : Integer; (* Convert Screen (0-based) to TextIndex (0-based) *) 462 463 function AddVisibleOffsetToTextIndex(aTextIndex: TLineIdx; LineOffset: Integer): TLineIdx; override; 464 function IsTextIdxVisible(aTextIndex: TLineIdx): Boolean; override; 465 466 // Attributes for Visible-Lines-On-screen 467 property DisplayNumber[index : Integer] : Integer (* LineNumber for display in Gutter / result is 1-based *) 468 read GetDisplayNumber; 469 property FoldType[index : Integer] : TSynEditFoldLineCapabilities (* FoldIcon / State *) 470 read GetFoldType; 471 property FoldClasifications[index : Integer] : TFoldNodeClassifications (* FoldIcon / State *) 472 read GetFoldClasifications; 473 property TextIndex[index : Integer] : Integer (* Position in SynTextBuffer / result is 0-based *) 474 read GetTextIndex; // maybe writable 475 476 // Define Visible Area 477 property TopLine : integer (* refers to visible (unfolded) lines / 1-based *) 478 read fTopLine write SetTopLine; 479 property TopTextIndex : integer (* refers to TextIndex (folded + unfolded lines) / 1-based *) 480 read GetTopTextIndex write SetTopTextIndex; 481 property LinesInWindow : integer (* Fully Visible lines in Window; There may be one half visible line *) 482 read fLinesInWindow write SetLinesInWindow; 483 484 property MarkupInfoFoldedCode: TSynSelectedColor read FMarkupInfoFoldedCode; 485 property MarkupInfoFoldedCodeLine: TSynSelectedColor read FMarkupInfoFoldedCodeLine; 486 property MarkupInfoHiddenCodeLine: TSynSelectedColor read FMarkupInfoHiddenCodeLine; 487 public 488 procedure Lock; 489 procedure UnLock; 490 {$IFDEF SynDebug} 491 procedure debug; 492 {$ENDIF} 493 (* Arguments for (Un)FoldAt* (Line, ViewPos, TextIndex): 494 - ColumnIndex (0-based) 495 Can be negative, to access the highest(-1) available, 2nd highest(-2) ... 496 If negative, count points downward 497 - ColCount = 0 => all 498 - Skip => Do not count nodes that are already in the desired state 499 (or can not archive the desired state: e.g. can not hide) 500 - AVisibleLines: 0 = Hide / 1 = Fold 501 *) 502 procedure FoldAtLine(AStartLine: Integer; ColIndex : Integer = -1; (* Folds at ScreenLine / 0-based *) 503 ColCount : Integer = 1; Skip: Boolean = False; 504 AVisibleLines: Integer = 1); 505 procedure FoldAtViewPos(AStartPos: Integer; ColIndex : Integer = -1; (* Folds at nth visible/unfolded Line / 1-based *) 506 ColCount : Integer = 1; Skip: Boolean = False; 507 AVisibleLines: Integer = 1); 508 procedure FoldAtTextIndex(AStartIndex: Integer; ColIndex : Integer = -1; (* Folds at nth TextIndex (all lines in buffer) / 1-based *) 509 ColCount : Integer = 1; Skip: Boolean = False; 510 AVisibleLines: Integer = 1); 511 procedure UnFoldAtLine(AStartLine: Integer; ColIndex : Integer = -1; (* UnFolds at ScreenLine / 0-based *) 512 ColCount : Integer = 0; Skip: Boolean = False; 513 AVisibleLines: Integer = 1); 514 procedure UnFoldAtViewPos(AStartPos: Integer; ColIndex : Integer = -1; (* UnFolds at nth visible/unfolded Line / 1-based *) 515 ColCount : Integer = 0; Skip: Boolean = False; 516 AVisibleLines: Integer = 1); 517 procedure UnFoldAtTextIndex(AStartIndex: Integer; ColIndex : Integer = -1; (* UnFolds at nth TextIndex (all lines in buffer) / 1-based *) 518 ColCount : Integer = 0; Skip: Boolean = False; 519 AVisibleLines: Integer = 1); 520 procedure UnFoldAtTextIndexCollapsed(AStartIndex: Integer); (* UnFolds only if Index is in the fold, ignores cfcollapsed line, if unfolded / 1-based *) 521 522 function LogicalPosToNodeIndex(AStartIndex: Integer; LogX: Integer; (* Returns the index of the node, at the logical char pos *) 523 Previous: Boolean = False): Integer; 524 525 procedure CollapseDefaultFolds; 526 // Load/Save folds to string 527 // AStartIndex, AEndIndex: (0 based) First/last line (EndIndex = -1 = open end) 528 // AStartCol, AEndCol: (1 based) Logical text pos in Line. (AEndCol = -1 = full line) 529 function GetFoldDescription(AStartIndex, AStartCol, AEndIndex, 530 AEndCol: Integer; AsText: Boolean = False; 531 Extended: Boolean = False) :String; 532 procedure ApplyFoldDescription(AStartIndex, AStartCol, AEndIndex, 533 AEndCol: Integer; FoldDesc: PChar; 534 FoldDescLen: Integer; IsText: Boolean = False); 535 536 procedure UnfoldAll; 537 procedure FoldAll(StartLevel : Integer = 0; IgnoreNested : Boolean = False); 538 procedure FixFoldingAtTextIndex(AStartIndex: Integer; AMinEndLine: Integer = 0); // Real/All lines 539 public 540 function OpenFoldCount(aStartIndex: Integer; AType: Integer = 0): Integer; 541 function OpenFoldInfo(aStartIndex, ColIndex: Integer; AType: Integer = 0): TFoldViewNodeInfo; 542 543 public 544 // Find the visible first line of the fold at ALine. Returns -1 if Aline is not folded 545 function CollapsedLineForFoldAtLine(ALine : Integer) : Integer; 546 function ExpandedLineForBlockAtLine(ALine : Integer; HalfExpanded: Boolean = True) : Integer; 547 548 function GetPhysicalCharWidths(Index: Integer): TPhysicalCharWidths; 549 550 function IsFoldedAtTextIndex(AStartIndex, ColIndex: Integer): Boolean; (* Checks xth Fold at nth TextIndex (all lines in buffer) / 1-based *) 551 property FoldedAtTextIndex [index : integer] : Boolean read IsFolded; 552 553 property HighLighter: TSynCustomHighlighter read GetHighLighter 554 write SetHighLighter; 555 property FoldProvider: TSynEditFoldProvider read FFoldProvider; 556 end; 557 558function dbgs(AClassification: TFoldNodeClassification): String; overload; 559 560implementation 561 562//var 563// SYN_FOLD_DEBUG: PLazLoggerLogGroup; 564 565type 566 TFoldExportEntry = Record 567 // Lines and Pos (o 1st line) are relative to Scan-Start 568 Line, LogX, LogX2: Integer; // StartLine and Pos 569 ELine, ELogX, ELogX2: Integer; // EndLine and pos 570 FType: Integer; // e.g ord(cfbtBeginEnd) 571 LinesFolded: Integer; // Lines Folded according to AVL-Node 572 end; 573 574 { TSynEditFoldExportStream } 575 576 TSynEditFoldExportStream = class 577 private 578 FData: String; 579 FLen, FPos: Integer; 580 FMem: PChar; 581 function GetLen: Integer; 582 procedure SetLen(const AValue: Integer); 583 function GetMem: PChar; 584 procedure SetMem(const AValue: PChar); 585 function GetText: String; 586 procedure SetText(const AValue: String); 587 protected 588 function GrowData(AppendSize: Integer): PChar; 589 function EncodeIntEx(Anum: Integer): String; // base 43, with leading continue bit 590 function EncodeIntEx2(Anum: Integer): String; // for numbers expected below 467; specially 0..80 591 function InternalReadNum(var APos: Integer): Integer; 592 function InternalReadNumEx(var APos: Integer): Integer; 593 public 594 constructor Create; 595 procedure Compress; 596 procedure Decompress; 597 598 procedure AddChecksum; 599 function VerifyChecksum: Boolean; 600 601 // see notes for Compression 602 Procedure AppendMem(AMem: Pointer; ALen: Integer); 603 Procedure AppendString(ATxt: String); 604 Procedure AppendNum(ANum: Integer); 605 Procedure AppendNumEx(ANum: Integer); 606 607 Procedure Reset; 608 Procedure Clear; 609 function ReadMem(AMem: Pointer; ALen: Integer): Boolean; 610 function PeakString(ALen: Integer): String; 611 function FindChar(AChar: Char): Integer; // 0 based 612 function ReadString(ALen: Integer): String; 613 function ReadNum: Integer; 614 function ReadNumEx: Integer; 615 function EOF: Boolean; 616 617 property Text: String read GetText write SetText; 618 property Mem: PChar read GetMem write SetMem; 619 property Len: Integer read GetLen write SetLen; 620 property Pos: Integer read FPos; 621 end; 622 623 TSynEditFoldExportCoderEntry = record 624 aX, aY, aLen: Integer; 625 aFoldType: TSynEditFoldType; 626 end; 627 TSynEditFoldExportCoderStates = 628 (sfecAtBegin, sfecAtPoint, sfecInRepeatCount, sfecInvalid, sfecAtEOF); 629 {$IFDEF SynFoldSaveDebug} 630const 631 SynEditFoldExportCoderStates: Array [TSynEditFoldExportCoderStates] of String = 632 ('sfecAtBegin', 'sfecAtPoint', 'sfecInRepeatCount', 'sfecInvalid', 'sfecAtEOF'); 633type 634 {$ENDIF} 635 636 { TSynEditFoldExportCoder } 637 638 TSynEditFoldExportCoder = class 639 private 640 FExportStream: TSynEditFoldExportStream; 641 FFoldType: Pointer; 642 643 FReadY, FReadLastY, FReadX, FReadSumLen, FReadCount: Integer; 644 FReadType: TSynEditFoldType; 645 FReadDefaultType: TSynEditFoldType; 646 FReadState: TSynEditFoldExportCoderStates; 647 648 FWriteCache: Array of TSynEditFoldExportCoderEntry; 649 FWriteCacheLen: Integer; 650 FWriteCacheTypes: set of TSynEditFoldType; 651 function GetReadIsValid: Boolean; 652 public 653 constructor Create(AFoldType: Pointer); 654 constructor Create(AStream: TSynEditFoldExportStream); 655 destructor Destroy; override; 656 657 procedure AddNode(aX, aY, aLen: Integer; aFoldType: TSynEditFoldType); 658 procedure Finish; 659 660 function ReadNode(aX, aY: Integer; aLen: Integer): TSynEditFoldType; 661 function EOF: Boolean; 662 procedure Reset; 663 property ReadIsValid: Boolean read GetReadIsValid; 664 665 property FoldType: Pointer read FFoldType; 666 property Stream: TSynEditFoldExportStream read FExportStream; 667 end; 668 669const 670 // use only xml encode-able ascii 671 // do not use [ or ], they are reserved for compression 672 // space can be used a special indicator 673 NumEncode86Chars: string[86] = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-+;:,.@=*/\!?$%()''^{}~_#'; 674 675 NumEncodeAsOneMax = 80; // Maximum Value to encode as 1 char 676 NumEncodeAsTwoMax = 81 + 4*86 + 43; // = 467; Maximum Value to encode as 2 char 677 NumEncodeAsThreeMax = 81 + 4*86 + 43 * 43 - 1; // = 2273 Maximum Value to encode as 3 char 678 679 680 SEQMaxNodeCount = 75; // New Full entry at least every 75 folds 681 SEQMaxLineDistEach = 500; // New Full entry, if folds startlines are more than 500 appart 682 SEQMaxLineDistTotal = 2500; // New Full entry at least every 2500; check position 683 684var 685 NumEncode86Values: Array [Char] of integer; 686 687procedure InitNumEncodeValues; 688var 689 i: integer; 690 c : Char; 691begin 692 for c := low(Char) to high(Char) do begin 693 NumEncode86Values[c] := -1; 694 end; 695 for i := 1 to length(NumEncode86Chars) do 696 NumEncode86Values[NumEncode86Chars[i]] := i - 1; 697end; 698 699{ TFoldChangedHandlerList } 700 701procedure TFoldChangedHandlerList.CallFoldChangedEvents(AnIndex: Integer); 702var 703 i: LongInt; 704begin 705 i:=Count; 706 while NextDownIndex(i) do 707 TFoldChangedEvent(Items[i])(AnIndex); 708end; 709 710{ TLazSynDisplayFold } 711 712constructor TLazSynDisplayFold.Create(AFoldView: TSynEditFoldedView); 713begin 714 inherited Create; 715 FFoldView := AFoldView; 716 FTokenAttr := TSynHighlighterAttributesModifier.Create(nil); 717 FMarkupLine := TSynSelectedColorMergeResult.Create(nil); 718end; 719 720destructor TLazSynDisplayFold.Destroy; 721begin 722 FreeAndNil(FTokenAttr); 723 FreeAndNil(FMarkupLine); 724 inherited Destroy; 725end; 726 727procedure TLazSynDisplayFold.SetHighlighterTokensLine(ALine: TLineIdx; out 728 ARealLine: TLineIdx; out AStartBytePos, ALineByteLen: Integer); 729begin 730 FLineState := 0; 731 CurrentTokenLine := ALine; 732 FLineFlags := FFoldView.FoldType[CurrentTokenLine + 1 - FFoldView.TopLine] * [cfCollapsedFold, cfCollapsedHide]; 733 FLineFlags2 := FLineFlags; 734 735 if not FFoldView.MarkupInfoFoldedCodeLine.IsEnabled then 736 Exclude(FLineFlags2, cfCollapsedFold); 737 if not FFoldView.MarkupInfoHiddenCodeLine.IsEnabled then 738 Exclude(FLineFlags2, cfCollapsedHide); 739 740 if (FLineFlags2 <> []) then begin 741 FFoldView.MarkupInfoFoldedCodeLine.SetFrameBoundsLog(1, MaxInt, 0); 742 FFoldView.MarkupInfoHiddenCodeLine.SetFrameBoundsLog(1, MaxInt, 0); 743 end; 744 745 inherited SetHighlighterTokensLine(FFoldView.InternViewToTextIndex(ALine), ARealLine, AStartBytePos, ALineByteLen); 746end; 747 748function TLazSynDisplayFold.GetNextHighlighterToken(out ATokenInfo: TLazSynDisplayTokenInfo): Boolean; 749const 750 MarkSpaces: string = ' '; 751 MarkDots: string = '...'; 752 LSTATE_BOL = 0; // at BOL 753 LSTATE_TEXT = 1; // in text 754 LSTATE_BOL_GAP = 2; // BOL and in Gap (empty line) // must be LSTATE_BOL + 2 755 LSTATE_GAP = 3; // In Gap betwen txt and dots // must be LSTATE_TEXT + 2 756 LSTATE_DOTS = 4; // In Dots 757 LSTATE_EOL = 5; // at start of EOL 758var 759 EolAttr: TSynHighlighterAttributes; 760 MergeStartX, MergeEndX: TLazSynDisplayTokenBound; 761begin 762 case FLineState of 763 LSTATE_BOL, LSTATE_TEXT: begin 764 Result := inherited GetNextHighlighterToken(ATokenInfo); 765 if ( (not Result) or (ATokenInfo.TokenStart = nil)) and (FLineFlags <> []) 766 then begin 767 inc(FLineState, 2); // LSTATE_BOL_GAP(2), if was at bol // LSTATE_GAP(3) otherwise 768 ATokenInfo.TokenStart := PChar(MarkSpaces); 769 ATokenInfo.TokenLength := 3; 770 if Assigned(CurrentTokenHighlighter) 771 then EolAttr := CurrentTokenHighlighter.GetEndOfLineAttribute 772 else EolAttr := nil; 773 if EolAttr <> nil then begin 774 FTokenAttr.Assign(EolAttr); 775 ATokenInfo.TokenAttr := FTokenAttr; 776 end 777 else begin 778 ATokenInfo.TokenAttr := nil; 779 end; 780 Result := True; 781 end; 782 end; 783 LSTATE_GAP: begin 784 FLineState := LSTATE_DOTS; 785 FTokenAttr.Assign(FFoldView.MarkupInfoFoldedCode); 786 FTokenAttr.SetAllPriorities(MaxInt); 787 ATokenInfo.TokenStart := PChar(MarkDots); 788 ATokenInfo.TokenLength := 3; 789 ATokenInfo.TokenAttr := FTokenAttr; 790 Result := True; 791 end; 792 else begin 793 Result := inherited GetNextHighlighterToken(ATokenInfo); 794 end; 795 end; 796 797 if (FLineFlags2 <> []) then begin 798 FMarkupLine.Clear; 799 if ATokenInfo.TokenAttr = nil then begin 800 // Text Area does not expect StartX/Endx 801 // So we must merge, to eliminate unwanted borders 802 // if (cfCollapsedFold in FLineFlags2) 803 // then ATokenInfo.TokenAttr := FFoldView.MarkupInfoFoldedCodeLine 804 // else ATokenInfo.TokenAttr := FFoldView.MarkupInfoHiddenCodeLine; 805 // exit; 806 FMarkupLine.Clear; 807 end //; 808 else 809 FMarkupLine.Assign(ATokenInfo.TokenAttr); 810 811 MergeStartX.Physical := -1; 812 MergeStartX.Logical := -1; 813 MergeEndX.Physical := -1; 814 MergeEndX.Logical := -1; 815 if FLineState in [LSTATE_BOL, LSTATE_BOL_GAP] then 816 MergeStartX := FFoldView.MarkupInfoFoldedCodeLine.StartX; 817 if FLineState = LSTATE_EOL then // LSTATE_GAP; // or result := true 818 MergeEndX := FFoldView.MarkupInfoFoldedCodeLine.EndX; 819 820 // fully expand all frames 821 //FMarkupLine.SetFrameBoundsLog(0,0,0); 822 //FMarkupLine.CurrentStartX := FMarkupLine.StartX; 823 //FMarkupLine.CurrentEndX := FMarkupLine.EndX; 824 825 if (cfCollapsedFold in FLineFlags2) then 826 FMarkupLine.Merge(FFoldView.MarkupInfoFoldedCodeLine, MergeStartX, MergeEndX) 827 else 828 FMarkupLine.Merge(FFoldView.MarkupInfoHiddenCodeLine, MergeStartX, MergeEndX); 829 830 ATokenInfo.TokenAttr := FMarkupLine; 831 end; 832 833 if FLineState in [LSTATE_BOL, LSTATE_BOL_GAP, LSTATE_DOTS, LSTATE_EOL] then 834 inc(FLineState); 835end; 836 837function TLazSynDisplayFold.GetLinesCount: Integer; 838begin 839 Result := FFoldView.ViewedCount; 840end; 841 842function TLazSynDisplayFold.TextToViewIndex(ATextIndex: TLineIdx): TLineRange; 843begin 844// TODO: inherited AFTER fold mapping? 845 Result := inherited TextToViewIndex(ATextIndex); 846 if Result.Top = Result.Bottom then begin 847 Result.Top := FFoldView.InternTextToViewIndex(Result.Top); 848 Result.Bottom := Result.Top; 849 end 850 else begin 851 Result.Top := FFoldView.InternTextToViewIndex(Result.Top); 852 Result.Bottom := FFoldView.InternTextToViewIndex(Result.Bottom); 853 end; 854end; 855 856function TLazSynDisplayFold.ViewToTextIndex(AViewIndex: TLineIdx): TLineIdx; 857begin 858 Result := FFoldView.InternViewToTextIndex(inherited ViewToTextIndex(AViewIndex)); 859end; 860 861function TLazSynDisplayFold.ViewToTextIndexEx(AViewIndex: TLineIdx; out 862 AViewRange: TLineRange): TLineIdx; 863begin 864 Result := FFoldView.InternViewToTextIndex(inherited ViewToTextIndexEx(AViewIndex, AViewRange)); 865end; 866 867{ TSynEditFoldExportStream } 868 869constructor TSynEditFoldExportStream.Create; 870begin 871 inherited; 872 FPos := 0; 873 FLen := 0; 874 FMem := nil; 875end; 876 877 878function TSynEditFoldExportStream.GetLen: Integer; 879begin 880 Result := FLen; 881end; 882 883procedure TSynEditFoldExportStream.SetLen(const AValue: Integer); 884begin 885 FPos := 0; 886 FLen:= AValue; 887end; 888 889function TSynEditFoldExportStream.GetMem: PChar; 890begin 891 if FData <> '' then 892 Result := @FData[1] 893 else 894 Result := FMem; 895end; 896 897procedure TSynEditFoldExportStream.SetMem(const AValue: PChar); 898begin 899 FData := ''; 900 FMem := AValue; 901 FPos := 0; 902end; 903 904function TSynEditFoldExportStream.GetText: String; 905begin 906 // only valid for FData 907 SetLength(FData, FLen); 908 Result := FData; 909end; 910 911procedure TSynEditFoldExportStream.SetText(const AValue: String); 912begin 913 FData := AValue; 914 FMem := nil; 915 FPos := 0; 916end; 917 918function TSynEditFoldExportStream.GrowData(AppendSize: Integer): PChar; 919var 920 l: integer; 921begin 922 l := length(FData); 923 if l < FLen + AppendSize then 924 SetLength(FData, l + AppendSize + Max((l+AppendSize) div 4, 1024)); 925 Result := @FData[FLen + 1]; 926 inc(FLen, AppendSize); 927end; 928 929function TSynEditFoldExportStream.EncodeIntEx(Anum: Integer): String; 930var 931 n: integer; 932begin 933 // 0 - 42 => 1 byte 934 // 43 - 1848 => 2 byte 935 // 1849 - .... => 3 and more 936 Result := ''; 937 if ANum = 0 then Result := NumEncode86Chars[1]; 938 n := 0; 939 while ANum > 0 do begin 940 Result := NumEncode86Chars[1 + (Anum mod 43) + n] + Result; 941 ANum := ANum div 43; 942 n := 43; 943 end; 944end; 945 946function TSynEditFoldExportStream.EncodeIntEx2(Anum: Integer): String; 947var 948 n: Integer; 949begin 950 // 0 - 80 => 1 char 951 // 81 - 424 => 2 char (80 + 4 * 86) 952 // 425 - 467 => 2 char (len(EncodeIntEx) = 1) 953 // 468 - 2272 => 3 and more char 954 //2273 - .... => 4 and more char 955 Result := ''; 956 if Anum <= 80 then 957 Result := NumEncode86Chars[1 + Anum] 958 else 959 begin 960 n := (Anum-81) div 86; 961 if n <= 3 then 962 Result := NumEncode86Chars[1 + 81 + n] + NumEncode86Chars[1 + (Anum - 81) mod 86] 963 else 964 Result := NumEncode86Chars[1 + 85] + EncodeIntEx(Anum - 81 - 4*86); 965 end; 966end; 967 968function TSynEditFoldExportStream.InternalReadNum(var APos: Integer): Integer; 969var 970 n: Integer; 971begin 972 Result := 0; 973 while True do begin 974 if FPos >= FLen then exit(-1); 975 n := NumEncode86Values[(FMem + APos)^]; 976 if n < 43 then break; 977 dec(n, 43); 978 Result := Result * 43 + n; 979 inc(APos); 980 end; 981 Result := Result * 43 + n; 982 inc(APos); 983end; 984 985function TSynEditFoldExportStream.InternalReadNumEx(var APos: Integer): Integer; 986begin 987 if FPos >= FLen then exit(-1); 988 Result := NumEncode86Values[(FMem + APos)^]; 989 inc(APos); 990 if Result <= 80 then 991 exit; 992 if FPos >= FLen then exit(-1); 993 if Result < 85 then begin 994 Result := 81 + (Result-81)*86 + NumEncode86Values[(FMem + APos)^]; 995 inc(APos); 996 exit; 997 end; 998 Result := 81 + 4*86 + InternalReadNum(APos); 999end; 1000 1001procedure TSynEditFoldExportStream.Compress; 1002(* Known Sequences: XX = Enc64Num (copy sequence from XX chars before) 1003 NN = ENc22 Num / n = enc22digit (copy n bytes) 1004 [XXn (up to 21 bytes, from up to 64*64 back) 1005 [NNXX[ (more then 21 bytes, from up to 64*64 back) 1006 ]X (3 bytes from max 64 back) 1007 ]nx ( reocurring space,x times, ever n pos) 1008const 1009 max_single_len = 22 - 1; 1010 *) 1011var 1012 CurPos, EndPos, SearchPos: Integer; 1013 FndLen, FndPos, FndPos2: Integer; 1014 BestLen, BestPos, BestPos2: Integer; 1015 s: string; 1016begin 1017 AppendString(#0); 1018 dec(FLen); 1019 1020 EndPos := FLen; 1021 CurPos := FLen - 3; 1022 while CurPos >= 4 do begin 1023 SearchPos := CurPos - 3; 1024 BestLen := 0; 1025 while (SearchPos >= 1) do begin 1026 if CompareMem(@FData[CurPos], @FData[SearchPos], 3) then begin 1027 FndLen := 3; 1028 FndPos := SearchPos; 1029 FndPos2 := CurPos; 1030 while (SearchPos + FndLen < FndPos2) and 1031 (FndPos2 + FndLen < EndPos - 1) and 1032 (FData[SearchPos + FndLen] = FData[CurPos + FndLen]) 1033 do 1034 inc(FndLen); 1035 while (FndPos > 1) and (FndPos + FndLen < FndPos2) and 1036 (FData[FndPos - 1] = FData[FndPos2 - 1]) do 1037 begin 1038 dec(FndPos); 1039 dec(FndPos2); 1040 inc(FndLen); 1041 end; 1042 1043 if (FndLen > BestLen) and 1044 ((FndPos2 - FndPos <= NumEncodeAsOneMax) or (FndLen >= 4)) and 1045 ((FndPos2 - FndPos <= NumEncodeAsTwoMax) or (FndLen >= 5)) and 1046 ((FndPos2 - FndPos <= NumEncodeAsThreeMax) or (FndLen >= 6)) 1047 then begin 1048 BestLen := FndLen; 1049 BestPos := FndPos; 1050 BestPos2 := FndPos2; 1051 end; 1052 end; 1053 dec(SearchPos); 1054 end; 1055 1056 s := ''; 1057 if (BestLen >= 4) then 1058 s := '[' + EncodeIntEx2(BestPos2 - BestPos) + EncodeIntEx2(BestLen) 1059 else 1060 if (BestLen = 3) and (BestPos2 - BestPos <= NumEncodeAsOneMax) then 1061 s := ']' + EncodeIntEx2(BestPos2 - BestPos); 1062 if (s<>'') and (length(s) < BestLen) then begin 1063 System.Move(s[1], FData[BestPos2], length(s)); 1064 System.Move(FData[BestPos2 + BestLen], FData[BestPos2 + length(s)], FLen + 1 - (BestPos2 + BestLen)); 1065 dec(FLen, BestLen - length(s)); 1066 EndPos := BestPos; 1067 CurPos := BestPos2 - 3; 1068 end 1069 else 1070 dec(CurPos); 1071 end; 1072end; 1073 1074procedure TSynEditFoldExportStream.Decompress; 1075var 1076 i, j, n: Integer; 1077 p, p2: PChar; 1078 NewLen: Integer; 1079begin 1080 // curently assumes that FMem points NOT at FData 1081 if FLen = 0 then 1082 exit; 1083 NewLen := 0; 1084 i := 0; 1085 while i < Flen do begin 1086 case (FMem+i)^ of 1087 '[' : 1088 begin 1089 inc(i); 1090 j := InternalReadNumEx(i); 1091 n := InternalReadNumEx(i); 1092 if (j < n) or (j > NewLen) then raise ESynEditError.Create('fold format error'); 1093 inc(NewLen, n); 1094 end; 1095 ']' : 1096 begin 1097 inc(i, 1); 1098 j := InternalReadNumEx(i); 1099 if (j < 3) or (j > NewLen) then raise ESynEditError.Create('fold format error'); 1100 inc(NewLen, 3); 1101 end; 1102 else 1103 begin 1104 inc(NewLen); 1105 inc(i); 1106 end; 1107 end; 1108 end; 1109 SetLength(FData, NewLen); 1110 1111 i := 0; 1112 p := PChar(FData); 1113 while i < Flen do begin 1114 case (FMem+i)^ of 1115 '[' : 1116 begin 1117 inc(i); 1118 j := InternalReadNumEx(i); 1119 n := InternalReadNumEx(i); 1120 p2 := p; 1121 while n > 0 do begin 1122 p^ := (p2 - j)^; 1123 inc(p); 1124 dec(j); 1125 dec(n); 1126 end; 1127 end; 1128 ']' : 1129 begin 1130 inc(i); 1131 j := InternalReadNumEx(i); 1132 p2 := p; 1133 for n := 0 to 2 do begin 1134 p^ := (p2 - j)^; 1135 inc(p); 1136 dec(j); 1137 end; 1138 end; 1139 else 1140 begin 1141 p^ := (FMem + i)^; 1142 inc(p); 1143 inc(i); 1144 end; 1145 end; 1146 end; 1147 1148 FLen := NewLen; 1149 FMem := PChar(FData); 1150 FPos := 0; 1151end; 1152 1153procedure TSynEditFoldExportStream.AddChecksum; 1154var 1155 i, c: Integer; 1156begin 1157 if FLen = 0 then 1158 exit; 1159 if FMem = nil then 1160 FMem := @FData[1]; 1161 c := 0; 1162 for i := 0 to FLen - 1 do 1163 c := c xor (ord((FMem + i)^) * (i+1)); 1164 c := (c mod 256) xor ((c div 256) mod 256) xor ((c div 65536) mod 256); 1165 AppendString(NumEncode86Chars[1 + (c mod 86)]); 1166end; 1167 1168function TSynEditFoldExportStream.VerifyChecksum: Boolean; 1169var 1170 i, c: Integer; 1171begin 1172 if FLen = 0 then 1173 exit(True); 1174 if FMem = nil then 1175 FMem := @FData[1]; 1176 dec(Flen); 1177 c := 0; 1178 for i := 0 to FLen - 1 do 1179 c := c xor (ord((FMem + i)^) * (i+1)); 1180 c := (c mod 256) xor ((c div 256) mod 256) xor ((c div 65536) mod 256); 1181 Result := (FMem + FLen)^ = NumEncode86Chars[1 + (c mod 86)]; 1182end; 1183 1184procedure TSynEditFoldExportStream.AppendMem(AMem: Pointer; ALen: Integer); 1185begin 1186 {$IFDEF SynFoldSaveDebug} 1187 DebugLn(['TSynEditFoldExportStream.AppendMem len=', ALen]); 1188 {$ENDIF} 1189 FMem := nil; 1190 if ALen > 0 then 1191 System.Move(AMem^, GrowData(ALen)^, ALen); 1192end; 1193 1194procedure TSynEditFoldExportStream.AppendString(ATxt: String); 1195var 1196 l: Integer; 1197begin 1198 {$IFDEF SynFoldSaveDebug} 1199 DebugLn(['TSynEditFoldExportStream.AppendString ', ATxt]); 1200 {$ENDIF} 1201 FMem := nil; 1202 l := length(ATxt); 1203 if l > 0 then 1204 System.Move(ATxt[1], GrowData(l)^, l); 1205end; 1206 1207procedure TSynEditFoldExportStream.AppendNum(ANum: Integer); 1208begin 1209 {$IFDEF SynFoldSaveDebug} 1210 DebugLn(['TSynEditFoldExportStream.AppendNum ', ANum]); 1211 {$ENDIF} 1212 FMem := nil; 1213 AppendString(EncodeIntEx(ANum)); 1214end; 1215 1216procedure TSynEditFoldExportStream.AppendNumEx(ANum: Integer); 1217begin 1218 {$IFDEF SynFoldSaveDebug} 1219 DebugLn(['TSynEditFoldExportStream.AppendNumEx ', ANum]); 1220 {$ENDIF} 1221 FMem := nil; 1222 AppendString(EncodeIntEx2(ANum)); 1223end; 1224 1225procedure TSynEditFoldExportStream.Reset; 1226begin 1227 FPos := 0; 1228 if (FMem = nil) and (FData <> '') then 1229 FMem := @FData[1]; 1230end; 1231 1232procedure TSynEditFoldExportStream.Clear; 1233begin 1234 FLen := 0; 1235 FMem := nil; 1236 FPos := 0; 1237 SetLength(FData, 0); 1238end; 1239 1240function TSynEditFoldExportStream.ReadMem(AMem: Pointer; ALen: Integer): Boolean; 1241begin 1242 Result := FPos+ ALen <= FLen; 1243 If not Result then 1244 exit; 1245 System.Move((FMem + FPos)^, AMem^, ALen); 1246 inc(FPos, ALen); 1247end; 1248 1249function TSynEditFoldExportStream.PeakString(ALen: Integer): String; 1250begin 1251 If not(FPos+ ALen <= FLen) then 1252 exit(''); 1253 SetLength(Result, ALen); 1254 if ALen > 0 then 1255 System.Move((FMem + FPos)^, Result[1], ALen); 1256end; 1257 1258function TSynEditFoldExportStream.FindChar(AChar: Char): Integer; 1259begin 1260 Result := 0; 1261 While (FPos + Result < FLen) and ((FMem + FPos + Result)^ <> AChar) do 1262 inc(Result); 1263 if FPos + Result = FLen then 1264 Result := -1; 1265end; 1266 1267function TSynEditFoldExportStream.ReadString(ALen: Integer): String; 1268begin 1269 If not(FPos+ ALen <= FLen) then 1270 exit(''); 1271 SetLength(Result, ALen); 1272 if ALen > 0 then 1273 System.Move((FMem + FPos)^, Result[1], ALen); 1274 inc(FPos, ALen); 1275end; 1276 1277function TSynEditFoldExportStream.ReadNum: Integer; 1278begin 1279 Result := InternalReadNum(FPos); 1280 {$IFDEF SynFoldSaveDebug} 1281 DebugLn(['TSynEditFoldExportStream.ReadNum ', Result]); 1282 {$ENDIF} 1283end; 1284 1285function TSynEditFoldExportStream.ReadNumEx: Integer; 1286begin 1287 Result := InternalReadNumEx(FPos); 1288 {$IFDEF SynFoldSaveDebug} 1289 DebugLn(['TSynEditFoldExportStream.ReadNumEx ', Result]); 1290 {$ENDIF} 1291end; 1292 1293function TSynEditFoldExportStream.EOF: Boolean; 1294begin 1295 Result := FPos >= FLen; 1296end; 1297 1298{ TSynEditFoldExportCoder } 1299 1300function TSynEditFoldExportCoder.GetReadIsValid: Boolean; 1301begin 1302 Result := FReadState <> sfecInvalid; 1303end; 1304 1305constructor TSynEditFoldExportCoder.Create(AFoldType: Pointer); 1306begin 1307 inherited Create; 1308 FExportStream := TSynEditFoldExportStream.Create; 1309 FExportStream.AppendString(' T'); // Type Marker 1310 FExportStream.AppendNum(PtrUInt(AFoldType)); 1311 FFoldType := AFoldType; 1312 FWriteCacheLen := 0; 1313 FWriteCache := nil; 1314 FWriteCacheTypes := []; 1315end; 1316 1317constructor TSynEditFoldExportCoder.Create(AStream: TSynEditFoldExportStream); 1318var 1319 i: Integer; 1320begin 1321 inherited Create; 1322 FExportStream := TSynEditFoldExportStream.Create; 1323 FReadState := sfecInvalid; 1324 if AStream.PeakString(2) <> ' T' then exit; 1325 1326 AStream.ReadString(2); 1327 1328 FFoldType := Pointer(PtrUInt(AStream.ReadNum)); 1329 while(true) do begin 1330 i := AStream.FindChar(' '); 1331 if i < 0 then i := AStream.Len - AStream.Pos; 1332 FExportStream.AppendString(AStream.ReadString(i)); 1333 if AStream.EOF or (AStream.PeakString(2) = ' T') then 1334 break; 1335 FExportStream.AppendString(AStream.ReadString(2)); 1336 end; 1337 {$IFDEF SynFoldSaveDebug} 1338 DebugLn(['TSynEditFoldExportCoder.Create(<from input-stream> FType=', dbgs(FFoldType), ' txtLen=', FExportStream.Len, ' Txt="', FExportStream.Text, '"']); 1339 {$ENDIF} 1340 Reset; 1341end; 1342 1343destructor TSynEditFoldExportCoder.Destroy; 1344begin 1345 FreeAndNil(FExportStream); 1346 Inherited; 1347end; 1348 1349procedure TSynEditFoldExportCoder.AddNode(aX, aY, aLen: Integer; aFoldType: TSynEditFoldType); 1350(* Format: [Num] <NumEX> 1351 ' T' [type] [yo] <X> <len> ( <c>* ' p' [sum] [yo] <X> <len> )* <c>* (' P' [sum] [yo] <X> <len>)? 1352 1353 ////////////////////////// 1354 // Version info 1355 V1 - no entries 1356 V2 July 2010 0.9.29 1357 - added fold-hide <HideInfo> 1358 1359 ////////////////////////// 1360 1361 <Stream> = { <TypeStream> }; 1362 1363 <TypeStream> = " T" <TypeId> <TypeData>; [* Stores all folds for the given type (eg cfbtBeginEnd) *] 1364 1365 <TypeId> = ord(cfbtBeginEnd) or similar 1366 <TypeData> = [<HideInfo>], 1367 <NodePos>, 1368 [ [<FoldList>,] [{ <FoldListEndCont>, <NodePos>, [<FoldList>] }] ], 1369 [ <FoldListEnd> ]; 1370 1371 1372 <FoldList> = [{ <ConsecutiveFoldedCount>, <ConsecutiveUnFoldedCount> }], 1373 <ConsecutiveFoldedCount>, 1374 ; 1375 [* NodePos: is the position of a folded node (of the type matching the current stream) 1376 ConsecutiveFoldedCount: more folded nodes of the same type, without any 1377 unfolded node (of this type) inbetween. 1378 ConsecutiveUnFoldedCount: amount of unfolded nodes (of this type) before the next folded node. 1379 *] 1380 1381 <NodePos> = <YOffset> <XPos> <len>; 1382 <YOffset> = <Number> 1383 <XPos> = <ExNumber> 1384 <len> = <ExNumber> 1385 <ConsecutiveFoldedCount> = <ExNumber> 1386 <ConsecutiveUnFoldedCount> = <ExNumber> 1387 1388 <FoldListEndCont> = ' p', <SumFoldedLines>; 1389 [* FoldListEndCont is mandotory, if another block of <NodePos>, <FoldList> is coming *] 1390 <FoldListEnd> = ' P' <SumFoldedLines>, <EndY>, <EndX>; 1391 [* FoldListEnd is optional. It is expected if the previous <FoldList> has more than 10 folded lines*] 1392 1393 <SumFoldedLines> = <Number> 1394 [* The sum of all lines folded by folds in <ConsecutiveFoldedCount>. 1395 Not including the fold in <NodePos>, which has it's own len. 1396 *] 1397 1398 <Number> = bigger numbers 1399 <ExNumber> = for numbers expected below 467; specially 0..80 1400 1401 <HideInfo> = ' h' | ' H' 1402 not present: all folds, no hides (default) 1403 ' H': all hides, no folds 1404 ' h': mixed hides and folds 1405 For mixed lists the following applies: 1406 - XPos is doubled; bit 0 (odd <number>) indicates the first node is a hide 1407 - ConsecutiveFoldedCount, ConsecutiveUnFoldedCount are doubled; 1408 bit 0 indicates: 1409 If last was fold: 1-odd = hide / 0-even = open 1410 If last was hide: 1-odd = fold / 0-even = open 1411 If last was open: 1-odd = hide / 0-even = fold 1412 In the first <ConsecutiveFoldedCount> after <NodePos> the bit is unused, since nodepos is continued. 1413*) 1414begin 1415 {$IFDEF SynFoldSaveDebug} 1416 debugln(['TSynEditFoldExportCoder.AddNode FType=', dbgs(FFoldType),' X=', aX, ' Y=', aY, 'Len=', aLen, 'FType=', SynEditFoldTypeNames[aFoldType], ' WCacheLen=', FWriteCacheLen]); 1417 {$ENDIF} 1418 if (FWriteCacheLen = 0) and (aFoldType = scftOpen) then 1419 exit; 1420 if FWriteCacheLen >= length(FWriteCache) then 1421 SetLength(FWriteCache, Max(1000, FWriteCacheLen*2)); 1422 FWriteCache[FWriteCacheLen].aY := aY; 1423 FWriteCache[FWriteCacheLen].aX := aX; 1424 FWriteCache[FWriteCacheLen].aLen := aLen; 1425 FWriteCache[FWriteCacheLen].aFoldType := aFoldType; 1426 inc(FWriteCacheLen); 1427 include(FWriteCacheTypes, aFoldType); 1428end; 1429 1430procedure TSynEditFoldExportCoder.Finish; 1431var 1432 FirstLine, HideFactor, HideBit: Integer; 1433 CntSum, LinesSum: Integer; 1434 LastFoldType: TSynEditFoldType; 1435 1436 procedure WriteCachedNode(AIndex: Integer); 1437 begin 1438 HideBit := 0; 1439 LastFoldType := FWriteCache[AIndex].aFoldType; 1440 if (HideFactor = 2) and (LastFoldType = scftHide) then 1441 HideBit := 1; 1442 FExportStream.AppendNum (FWriteCache[AIndex].aY - FirstLine); 1443 FExportStream.AppendNumEx(FWriteCache[AIndex].aX * HideFactor + HideBit); 1444 FExportStream.AppendNumEx(FWriteCache[AIndex].aLen); 1445 FirstLine := FWriteCache[AIndex].aY; 1446 end; 1447 1448 function CountConsecutiveNodes(var AStartIndex: Integer; out ACount, ALines: Integer; 1449 ASkipFirst: Boolean = True): Boolean; 1450 var l1, l2: Integer; 1451 t: TSynEditFoldType; 1452 begin 1453 // reset counters for following <FoldList> 1454 CntSum := 0; 1455 LinesSum := 0; 1456 1457 HideBit := 0;; 1458 case LastFoldType of 1459 scftOpen: if scftHide = FWriteCache[AStartIndex].aFoldType then HideBit := 1; 1460 scftFold: if scftHide = FWriteCache[AStartIndex].aFoldType then HideBit := 1; 1461 scftHide: if scftFold = FWriteCache[AStartIndex].aFoldType then HideBit := 1; 1462 end; 1463 LastFoldType := FWriteCache[AStartIndex].aFoldType; 1464 1465 Result := False; 1466 ACount := 0; 1467 ALines := 0; 1468 1469 l2 := FirstLine; 1470 t := FWriteCache[AStartIndex].aFoldType; 1471 Repeat 1472 if (AStartIndex >= FWriteCacheLen) then 1473 exit; 1474 l1 := FWriteCache[AStartIndex].aY; 1475 if (ACount > SEQMaxNodeCount) or 1476 (ALines > SEQMaxNodeCount) or 1477 (l1 - l2 > SEQMaxLineDistEach) or 1478 (l1 - FirstLine > SEQMaxLineDistTotal) 1479 then 1480 exit; 1481 1482 if not ASkipFirst then begin 1483 ALines := ALines + FWriteCache[AStartIndex].aLen; 1484 inc(ACount); 1485 end; 1486 inc(AStartIndex); 1487 l2 := l1; 1488 ASkipFirst := False; 1489 until FWriteCache[AStartIndex].aFoldType <> t; 1490 Result := True; 1491 end; 1492 1493 var DeferredZero: Boolean; 1494 procedure WriteNodeCount(ACount, ALines: Integer; AState: TSynEditFoldType); 1495 begin 1496 inc(CntSum, ACount); 1497 inc(LinesSum, ALines); // non folds are always 0 1498 if ACount = 0 then begin 1499 DeferredZero := True; 1500 exit; 1501 end; 1502 if DeferredZero then 1503 FExportStream.AppendNumEx(0); 1504 DeferredZero := False; 1505 FExportStream.AppendNumEx(ACount * HideFactor + HideBit); 1506 end; 1507 1508 function ScanForFold(var AIndex: Integer): Boolean; 1509 begin 1510 Result := True; 1511 while AIndex < FWriteCacheLen do begin 1512 if FWriteCache[AIndex].aFoldType in [scftFold, scftHide] then exit; 1513 inc(AIndex); 1514 end; 1515 Result := False; 1516 end; 1517var 1518 i, i2, CntF, CntNF, LinesF, LinesNF: Integer; 1519 r: boolean; 1520begin 1521 if (FWriteCacheLen = 0) or (FWriteCacheTypes * [scftFold, scftHide] = []) then begin 1522 FExportStream.Clear; 1523 exit; 1524 end; 1525 {$IFDEF SynFoldSaveDebug} 1526 DebugLnEnter(['TSynEditFoldExportCoder.Finish FType=', dbgs(FFoldType)]); 1527 {$ENDIF} 1528 1529 FirstLine := 0; 1530 if (FWriteCacheTypes * [scftFold, scftHide] = [scftFold, scftHide]) then begin 1531 HideFactor := 2; 1532 FExportStream.AppendString(' h'); 1533 end 1534 else begin 1535 HideFactor := 1; // no bit for hide/fold differentation needed 1536 if scftHide in FWriteCacheTypes then 1537 FExportStream.AppendString(' H'); 1538 end; 1539 i := 0; 1540 while i < FWriteCacheLen do begin 1541 WriteCachedNode(i); 1542 1543 DeferredZero := False; // special case at start, there may be 0 more folded nodes 1544 r := CountConsecutiveNodes(i, cntF, linesF, True); 1545 WriteNodeCount(CntF, LinesF, scftFold); // or hide, no matter here 1546 while r do begin 1547 r := CountConsecutiveNodes(i, cntNF, linesNF, False); 1548 if not r then break; 1549 r := CountConsecutiveNodes(i, cntF, linesF, False); 1550 WriteNodeCount(CntNF, LinesNF, scftOpen); 1551 WriteNodeCount(CntF, LinesF, scftFold); // or hide, no matter here 1552 end; 1553 1554 i2 := i; 1555 ScanForFold(i); 1556 if (i < FWriteCacheLen) then begin 1557 // another node will follow, must insert ' p' marker 1558 FExportStream.AppendString(' p'); // point marker (no marker needed for first entry) 1559 FExportStream.AppendNum(LinesSum); // Start with sum from last sequence 1560 end; 1561 end; 1562 1563 if LinesSum > 10 then begin 1564 // end of data; write ' P' marker if needed 1565 FExportStream.AppendString(' P'); // point marker (no marker needed for first entry) 1566 FExportStream.AppendNum (LinesSum); // Start with sum from last sequence 1567 FExportStream.AppendNum (FWriteCache[i2-1].aY - FirstLine); // Last folded Coords 1568 FExportStream.AppendNumEx(FWriteCache[i2-1].aX); 1569 end; 1570 {$IFDEF SynFoldSaveDebug} 1571 DebugLnExit(['TSynEditFoldExportCoder.Finish FType=', dbgs(FFoldType), ' txtLen=', FExportStream.Len, ' Txt="', FExportStream.Text, '"']); 1572 {$ENDIF} 1573end; 1574 1575function TSynEditFoldExportCoder.ReadNode(aX, aY: Integer; aLen: Integer): TSynEditFoldType; 1576(* Format: [Num] <NumEX> 1577 ' T' [type] 1578 [yo] <X> <len> ( <c>* ' p' [sum] [yo] <X> <len> )* <c>* (' P' [sum] [yo] <X>)? 1579*) 1580 function GetCommand: Char; 1581 begin 1582 Result := #0; 1583 if (FExportStream.PeakString(1) = ' ') and (FExportStream.Len > FExportStream.Pos+1) then 1584 Result := FExportStream.ReadString(2)[2]; 1585 end; 1586 1587 function Invalidate: TSynEditFoldType; 1588 begin 1589 {$IFDEF SynFoldSaveDebug} 1590 DebugLn(['Invalidate']); 1591 {$ENDIF} 1592 FReadState := sfecInvalid; 1593 Result := scftInvalid; 1594 end; 1595 1596var 1597 i: Integer; 1598begin 1599 {$IFDEF SynFoldSaveDebug} 1600 DebugLnEnter(['TSynEditFoldExportCoder.Readnode X=', aX, ' Y=', aY, ' Len=',aLen, 1601 ' ReadState=',SynEditFoldExportCoderStates[FReadState], 1602 ' FReadCount=', FReadCount, ' FReadY=', FReadY, ' FReadX=', FReadX, 1603 ' FReadSumLen=', FReadSumLen, ' FReadType=', SynEditFoldTypeNames[FReadType] 1604 ]); 1605 try 1606 {$ENDIF} 1607 Result := scftInvalid; 1608 case FReadState of 1609 sfecAtBegin, sfecAtPoint: 1610 begin 1611 if (FReadState = sfecAtBegin) then begin 1612 case GetCommand of 1613 'H': begin 1614 FReadDefaultType := scftHide; 1615 FReadType := scftHide; 1616 end; 1617 'h': begin 1618 FReadDefaultType := scftAll; 1619 end; 1620 end; 1621 FReadState := sfecAtPoint; 1622 end; 1623 1624 if FReadCount = 0 then begin 1625 FReadCount := 1; 1626 FReadY := FExportStream.ReadNum + FReadLastY; 1627 FReadX := FExportStream.ReadNumEx; 1628 FReadSumLen := FExportStream.ReadNumEx; 1629 if FReadSumLen < 0 then exit(Invalidate); 1630 1631 if FReadDefaultType = scftAll then begin 1632 if (FReadX and 1) = 1 then 1633 FReadType := scftHide 1634 else 1635 FReadType := scftFold; 1636 FReadX := FReadX div 2; 1637 end 1638 else 1639 FReadType := FReadDefaultType; 1640 end; 1641 1642 // ax may be off by one, since pas highlighter changed to include $ in $IFDEF 1643 if ((aY < FReadY) or ((aY = FReadY) and (aX+1 < FReadX))) then 1644 exit(scftOpen); // actually, read before point 1645 1646 i := 0; 1647 if FReadType = scftHide then i := 1; // fold one more than len 1648 if (aY <> FReadY) or (abs(aX - FReadX) > 1) or (aLen + i <> FReadSumLen) then 1649 exit(Invalidate); 1650 1651 FReadLastY := FReadY; 1652 FReadSumLen := 0; // was len of current fold, no len remaining => prepare for counting consecutive folds 1653 Result := FReadType; 1654 1655 if FExportStream.EOF then 1656 FReadState := sfecAtEOF 1657 else case GetCommand of 1658 'p': 1659 begin 1660 FExportStream.ReadNum; // skip len (must be 0) since there was no <ConsecutiveFoldedCount> 1661 FReadCount := 0; 1662 FReadState := sfecAtPoint; 1663 end; 1664 'P': 1665 begin 1666 // end marker isnt expected? there were no <ConsecutiveFoldedCount> 1667 FReadState := sfecAtEOF; 1668 end; 1669 else 1670 begin 1671 FReadState := sfecInRepeatCount; 1672 FReadCount := FExportStream.ReadNumEx; // count up and check at end 1673 end; 1674 end; 1675 end; 1676 1677 sfecInRepeatCount: 1678 begin 1679 if FReadCount = 0 then begin 1680 if FExportStream.EOF then begin 1681 FReadState := sfecAtEOF; 1682 exit(scftOpen); 1683 end 1684 else case GetCommand of 1685 'p': 1686 begin 1687 if FReadSumLen <> FExportStream.ReadNum then 1688 exit(Invalidate); 1689 FReadCount := 0; 1690 FReadState := sfecAtPoint; 1691 exit(ReadNode(aX, aY, aLen)); 1692 end; 1693 'P': 1694 begin 1695 if (FReadSumLen <> FExportStream.ReadNum) or 1696 (FReadY <> FExportStream.ReadNum + FReadLastY) or 1697 (FReadX <> FExportStream.ReadNumEx) 1698 then 1699 exit(Invalidate); 1700 FReadState := sfecAtEOF; 1701 exit(scftOpen); 1702 end; 1703 else 1704 begin 1705 FReadCount := FExportStream.ReadNumEx; // count up and check at end 1706 if FReadDefaultType = scftAll then begin 1707 if (FReadCount and 1) = 1 then begin 1708 case FReadType of 1709 scftOpen: FReadType := scftHide; 1710 scftFold: FReadType := scftHide; 1711 scftHide: FReadType := scftFold; 1712 end; 1713 end else begin 1714 case FReadType of 1715 scftOpen: FReadType := scftFold; 1716 scftFold: FReadType := scftOpen; 1717 scftHide: FReadType := scftOpen; 1718 end; 1719 end; 1720 FReadCount := FReadCount div 2; 1721 end 1722 else begin 1723 if FReadType = scftOpen then 1724 FReadType := FReadDefaultType 1725 else 1726 FReadType := scftOpen; 1727 end; 1728 end; 1729 end; 1730 end; 1731 dec(FReadCount); 1732 inc(FReadSumLen, aLen); 1733 Result := FReadType; 1734 end; 1735 1736 sfecAtEOF: 1737 begin 1738 exit(scftOpen); 1739 end; 1740 sfecInvalid: 1741 begin 1742 exit(scftInvalid); 1743 end; 1744 end; 1745 {$IFDEF SynFoldSaveDebug} 1746 finally 1747 DebugLnExit(['TSynEditFoldExportCoder.Readnode << ']); 1748 end; 1749 {$ENDIF} 1750end; 1751 1752function TSynEditFoldExportCoder.EOF: Boolean; 1753begin 1754 Result := FExportStream.EOF; 1755end; 1756 1757procedure TSynEditFoldExportCoder.Reset; 1758begin 1759 FExportStream.Reset; 1760 FReadY := -1; 1761 FReadX := -1; 1762 FReadLastY := 0; 1763 FReadCount := 0; 1764 FReadSumLen := 0; 1765 FReadState := sfecAtBegin; 1766 if FExportStream.Len = 0 then 1767 FReadState := sfecInvalid; 1768 FReadDefaultType := scftFold; 1769 FReadType := scftFold; 1770end; 1771 1772{ TSynTextFoldAVLNodeData } 1773 1774function TSynTextFoldAVLNodeData.Left: TSynTextFoldAVLNodeData; 1775begin 1776 Result := TSynTextFoldAVLNodeData(FLeft); 1777end; 1778 1779function TSynTextFoldAVLNodeData.Parent: TSynTextFoldAVLNodeData; 1780begin 1781 Result := TSynTextFoldAVLNodeData(FParent); 1782end; 1783 1784function TSynTextFoldAVLNodeData.Right: TSynTextFoldAVLNodeData; 1785begin 1786 Result := TSynTextFoldAVLNodeData(FRight); 1787end; 1788 1789procedure TSynTextFoldAVLNodeData.FreeAllChildrenAndNested; 1790begin 1791 if FLeft <> nil then begin 1792 Left.FreeAllChildrenAndNested; 1793 FreeAndNil(FLeft); 1794 end; 1795 1796 if FRight <> nil then begin 1797 Right.FreeAllChildrenAndNested; 1798 FreeAndNil(FRight); 1799 end; 1800 1801 if Nested <> nil then begin 1802 Nested.FreeAllChildrenAndNested; 1803 FreeAndNil(Nested); 1804 end; 1805end; 1806 1807function TSynTextFoldAVLNodeData.RecursiveFoldCount : Integer; 1808var 1809 ANode: TSynTextFoldAVLNodeData; 1810begin 1811 Result := 0; 1812 ANode := self; 1813 while ANode <> nil do begin 1814 Result := Result + ANode.MergedLineCount + ANode.LeftCount; 1815 ANode := ANode.Right; 1816 end; 1817end; 1818 1819function TSynTextFoldAVLNodeData.Precessor: TSynTextFoldAVLNodeData; 1820begin 1821 Result := TSynTextFoldAVLNodeData(inherited Precessor); 1822end; 1823 1824function TSynTextFoldAVLNodeData.Successor: TSynTextFoldAVLNodeData; 1825begin 1826 Result := TSynTextFoldAVLNodeData(inherited Successor); 1827end; 1828 1829function TSynTextFoldAVLNodeData.Precessor(var aStartPosition, 1830 aSizesBeforeSum: Integer): TSynTextFoldAVLNodeData; 1831begin 1832 Result := TSynTextFoldAVLNodeData(inherited Precessor(aStartPosition, aSizesBeforeSum)); 1833end; 1834 1835function TSynTextFoldAVLNodeData.Successor(var aStartPosition, 1836 aSizesBeforeSum: Integer): TSynTextFoldAVLNodeData; 1837begin 1838 Result := TSynTextFoldAVLNodeData(inherited Successor(aStartPosition, aSizesBeforeSum)); 1839end; 1840 1841{ TSynTextFoldAVLNode } 1842 1843function TSynTextFoldAVLNode.GetClassification: TFoldNodeClassification; 1844begin 1845 if fData = nil 1846 then Result := fncInvalid 1847 else Result := fData.Classification; 1848end; 1849 1850function TSynTextFoldAVLNode.GetFoldColumn: Integer; 1851begin 1852 if fData = nil 1853 then Result := -1 1854 else Result := fData.FoldColumn; 1855end; 1856 1857function TSynTextFoldAVLNode.GetFoldColumnLen: Integer; 1858begin 1859 if fData = nil 1860 then Result := -1 1861 else Result := fData.FoldColumnLen; 1862end; 1863 1864function TSynTextFoldAVLNode.GetFoldIndex: Integer; 1865begin 1866 if fData = nil 1867 then Result := -1 1868 else Result := fData.FoldIndex; 1869end; 1870 1871function TSynTextFoldAVLNode.GetMergedLineCount : Integer; 1872begin 1873 if fData = nil 1874 then Result := 0 1875 else Result := fData.MergedLineCount; 1876end; 1877 1878function TSynTextFoldAVLNode.GetFullCount: Integer; 1879begin 1880 if fData = nil 1881 then Result := -1 1882 else Result := fData.FullCount; 1883end; 1884 1885function TSynTextFoldAVLNode.GetSourceLine: integer; 1886begin 1887 if fData = nil then 1888 Result := -1 1889 else 1890 Result := StartLine - fData.VisibleLines; 1891end; 1892 1893function TSynTextFoldAVLNode.GetSourceLineOffset: integer; 1894begin 1895 if fData = nil then 1896 Result := 0 1897 else 1898 Result := fData.VisibleLines; 1899end; 1900 1901procedure TSynTextFoldAVLNode.SetFoldColumn(const AValue: Integer); 1902begin 1903 if fData <> nil then 1904 fData.FoldColumn := AValue; 1905end; 1906 1907procedure TSynTextFoldAVLNode.Init(aData: TSynTextFoldAVLNodeData; aStartLine, 1908 aFoldedBefore: Integer); 1909begin 1910 fData := aData; 1911 fStartLine := aStartLine; 1912 fFoldedBefore := aFoldedBefore; 1913end; 1914 1915function TSynTextFoldAVLNode.IsInFold : Boolean; 1916begin 1917 Result := fData <> nil; 1918end; 1919 1920function TSynTextFoldAVLNode.Next : TSynTextFoldAVLNode; 1921var aStart, aBefore : Integer; 1922begin 1923 if fData <> nil then begin 1924 aStart := StartLine; 1925 aBefore := FoldedBefore; 1926 Result.fData := fData.Successor(aStart, aBefore); 1927 Result.fStartLine := aStart; 1928 Result.fFoldedBefore := aBefore; 1929 end 1930 else Result.fData := nil; 1931end; 1932 1933function TSynTextFoldAVLNode.Prev : TSynTextFoldAVLNode; 1934var aStart, aBefore : Integer; 1935begin 1936 if fData <> nil then begin 1937 aStart := StartLine; 1938 aBefore := FoldedBefore; 1939 Result.fData := fData.Precessor(aStart, aBefore); 1940 Result.fStartLine := aStart; 1941 Result.fFoldedBefore := aBefore; 1942 end 1943 else Result.fData := nil; 1944end; 1945 1946function TSynTextFoldAVLNode.IsHide: Boolean; 1947begin 1948 Result := (fData <> nil) and (fData.VisibleLines = 0); 1949end; 1950 1951{ TSynTextFoldAVLNodeNestedIterator } 1952 1953constructor TSynTextFoldAVLNodeNestedIterator.Create(ANode: TSynTextFoldAVLNode); 1954begin 1955 SetLength(FOuterNodes, 0); 1956 FCurrentNode := ANode; 1957end; 1958 1959destructor TSynTextFoldAVLNodeNestedIterator.Destroy; 1960begin 1961 SetLength(FOuterNodes, 0); 1962 inherited Destroy; 1963end; 1964 1965function TSynTextFoldAVLNodeNestedIterator.Next: TSynTextFoldAVLNode; 1966var 1967 NewData: TSynTextFoldAVLNodeData; 1968 i: Integer; 1969 PNode: TSynTextFoldAVLNode; 1970begin 1971 i := length(FOuterNodes); 1972 if FCurrentNode.fData.Nested = nil then begin 1973 FCurrentNode := FCurrentNode.Next; 1974 while (not FCurrentNode.IsInFold) and (i > 0) do begin 1975 dec(i); 1976 FCurrentNode := FOuterNodes[i]; 1977 SetLength(FOuterNodes, i); 1978 FCurrentNode := FCurrentNode.Next; 1979 end; 1980 end else begin 1981 SetLength(FOuterNodes, i + 1); 1982 FOuterNodes[i] := FCurrentNode; 1983 NewData := FCurrentNode.fData.Nested; 1984 FCurrentNode.fData := NewData; 1985 FCurrentNode.FStartLine := FCurrentNode.FStartLine + NewData.LineOffset; 1986 1987 PNode := FCurrentNode.Prev; 1988 while PNode.IsInFold do begin 1989 FCurrentNode := PNode; 1990 PNode := FCurrentNode.Prev; 1991 end; 1992 end; 1993 Result := FCurrentNode; 1994end; 1995 1996function TSynTextFoldAVLNodeNestedIterator.Prev: TSynTextFoldAVLNode; 1997var 1998 i: Integer; 1999 NewData: TSynTextFoldAVLNodeData; 2000 PNode: TSynTextFoldAVLNode; 2001begin 2002 FCurrentNode := FCurrentNode.Prev; 2003 i := length(FOuterNodes); 2004 if FCurrentNode.IsInFold then begin 2005 while (FCurrentNode.fData.Nested <> nil) do begin 2006 SetLength(FOuterNodes, i + 1); 2007 FOuterNodes[i] := FCurrentNode; 2008 NewData := FCurrentNode.fData.Nested; 2009 FCurrentNode.fData := NewData; 2010 FCurrentNode.FStartLine := FCurrentNode.FStartLine + NewData.LineOffset; 2011 2012 PNode := FCurrentNode.Next; 2013 while PNode.IsInFold do begin 2014 FCurrentNode := PNode; 2015 PNode := FCurrentNode.Next; 2016 end; 2017 end; 2018 end 2019 else // not IsInFold 2020 if (i > 0) then begin 2021 dec(i); 2022 FCurrentNode := FOuterNodes[i]; 2023 SetLength(FOuterNodes, i); 2024 end; 2025 Result := FCurrentNode; 2026end; 2027 2028function TSynTextFoldAVLNodeNestedIterator.EOF: Boolean; 2029begin 2030 Result := not FCurrentNode.Next.IsInFold; 2031end; 2032 2033function TSynTextFoldAVLNodeNestedIterator.BOF: Boolean; 2034begin 2035 Result := not FCurrentNode.Prev.IsInFold; 2036end; 2037 2038function TSynTextFoldAVLNodeNestedIterator.IsInFold: Boolean; 2039begin 2040 Result := FCurrentNode.IsInFold; 2041end; 2042 2043{ TSynFoldNodeInfoHelper } 2044 2045constructor TSynFoldNodeInfoHelper.Create(AHighlighter: TSynCustomFoldHighlighter); 2046begin 2047 inherited Create; 2048 FHighlighter := AHighlighter; 2049 Invalidate; 2050end; 2051 2052function TSynFoldNodeInfoHelper.FirstOpen: TSynFoldNodeInfo; 2053begin 2054 FActions := [sfaOpen, sfaFold]; 2055 FCurInfo.NodeIndex := -1; 2056 FCurInfo.LineIndex := 0; 2057 Result := Next; 2058end; 2059 2060procedure TSynFoldNodeInfoHelper.Invalidate; 2061begin 2062 FCurInfo.FoldAction := [sfaInvalid]; 2063end; 2064 2065function TSynFoldNodeInfoHelper.Next: TSynFoldNodeInfo; 2066var 2067 Cnt, Line, Idx: LongInt; 2068begin 2069 Idx := FCurInfo.NodeIndex + 1; 2070 Line := FCurInfo.LineIndex; 2071 Cnt := FHighlighter.FoldNodeInfo[Line].CountEx(FActions); 2072 if Idx >= Cnt then begin 2073 Idx := 0; 2074 inc(Line); 2075 while (Line < FHighlighter.CurrentLines.Count) and 2076 (FHighlighter.FoldNodeInfo[Line].CountEx(FActions) = 0) 2077 do 2078 inc(Line); 2079 end; 2080 if (Line < FHighlighter.CurrentLines.Count) then 2081 FCurInfo := FHighlighter.FoldNodeInfo[Line].NodeInfoEx(Idx, FActions) 2082 else 2083 Invalidate; 2084 Result := FCurInfo; 2085end; 2086 2087function TSynFoldNodeInfoHelper.Prev: TSynFoldNodeInfo; 2088var 2089 Line, Idx: LongInt; 2090begin 2091 Idx := FCurInfo.NodeIndex - 1; 2092 Line := FCurInfo.LineIndex; 2093 if Idx < 0 then begin 2094 dec(Line); 2095 while (Line >= 0) and 2096 (FHighlighter.FoldNodeInfo[Line].CountEx(FActions) = 0) 2097 do 2098 dec(Line); 2099 Idx := FHighlighter.FoldNodeInfo[Line].CountEx(FActions) - 1; 2100 end; 2101 if (Line >= 0) then 2102 FCurInfo := FHighlighter.FoldNodeInfo[Line].NodeInfoEx(Idx, FActions) 2103 else 2104 Invalidate; 2105 Result := FCurInfo; 2106end; 2107 2108function TSynFoldNodeInfoHelper.FindClose: TSynFoldNodeInfo; 2109var 2110 Line, EndLine, Cnt: Integer; 2111 NdInfo: TSynFoldNodeInfo; 2112begin 2113 Line := FCurInfo.LineIndex; 2114 EndLine := FHighlighter.FoldEndLine(Line, FCurInfo.NodeIndex); 2115 FActions := [sfaClose, sfaFold]; 2116 Cnt := FHighlighter.FoldNodeInfo[EndLine].CountEx(FActions) - 1; 2117 while Cnt >= 0 do begin 2118 NdInfo := FHighlighter.FoldNodeInfo[EndLine].NodeInfoEx(Cnt, FActions); 2119 if (NdInfo.FoldLvlStart = FCurInfo.FoldLvlEnd) and 2120 (NdInfo.FoldType = FCurInfo.FoldType) 2121 then 2122 break; 2123 dec(Cnt); 2124 end; 2125 if Cnt < 0 then 2126 Invalidate 2127 else 2128 FCurInfo := NdInfo; 2129 Result := FCurInfo; 2130end; 2131 2132function TSynFoldNodeInfoHelper.GotoOpenPos(aLineIdx, aNodeIdx: integer): TSynFoldNodeInfo; 2133begin 2134 FActions := [sfaOpen, sfaFold]; 2135 FCurInfo := FHighlighter.FoldNodeInfo[aLineIdx].NodeInfoEx(aNodeIdx, FActions); 2136 Result := FCurInfo; 2137end; 2138 2139function TSynFoldNodeInfoHelper.GotoOpenAtChar(aLineIdx, aXPos: integer): TSynFoldNodeInfo; 2140var 2141 Cnt: Integer; 2142begin 2143 FActions := [sfaOpen, sfaFold]; 2144 Cnt := FHighlighter.FoldNodeInfo[aLineIdx].CountEx(FActions) - 1; 2145 while Cnt >= 0 do begin 2146 FCurInfo := FHighlighter.FoldNodeInfo[aLineIdx].NodeInfoEx(Cnt, FActions); 2147 if FCurInfo.LogXStart = aXPos then break; 2148 dec(Cnt); 2149 end; 2150 if Cnt < 0 then 2151 Invalidate; 2152 Result := FCurInfo; 2153end; 2154 2155function TSynFoldNodeInfoHelper.GotoNodeOpenPos(ANode: TSynTextFoldAVLNode): TSynFoldNodeInfo; 2156begin 2157 FActions := [sfaOpen, sfaFold]; 2158 FCurInfo := FHighlighter.FoldNodeInfo[ANode.StartLine - ANode.SourceLineOffset - 1] 2159 .NodeInfoEx(ANode.FoldIndex, FActions); 2160 Result := FCurInfo; 2161end; 2162 2163function TSynFoldNodeInfoHelper.GotoNodeClosePos(ANode: TSynTextFoldAVLNode): TSynFoldNodeInfo; 2164var 2165 NdInfo, NdInfo2: TSynFoldNodeInfo; 2166 Cnt, EndCol, EndLineIdx: Integer; 2167begin 2168 FActions := [sfaClose, sfaFold]; 2169 NdInfo := FHighlighter.FoldNodeInfo[ANode.StartLine - ANode.SourceLineOffset - 1] 2170 .NodeInfoEx(ANode.FoldIndex, [sfaOpen, sfaFold]); 2171 if sfaInvalid in NdInfo.FoldAction then exit(NdInfo); 2172 2173 EndLineIdx := FHighlighter.FoldEndLine(ANode.StartLine - ANode.SourceLineOffset - 1, 2174 ANode.FoldIndex); 2175 {$IFDEF SynAssertFold} 2176 SynAssert(EndLineIdx >= 0, 'TSynFoldNodeInfoHelper.GotoNodeClosePos: Bad EndLineIdx=%d # Anode: StartLine=%d SrcLOffs=%d ColIdx=%d FoldCol=%d', [EndLineIdx, ANode.StartLine, ANode.SourceLineOffset, ANode.FoldIndex, ANode.FoldColumn]); 2177 {$ENDIF} 2178 Cnt := FHighlighter.FoldNodeInfo[EndLineIdx].CountEx([sfaClose, sfaFold]); 2179 EndCol := 0; 2180 while EndCol < Cnt do begin 2181 NdInfo2 := FHighlighter.FoldNodeInfo[EndLineIdx].NodeInfoEx(EndCol, [sfaClose, sfaFold]); 2182 if (NdInfo2.FoldLvlStart = NdInfo.FoldLvlEnd) and 2183 (NdInfo2.FoldType = NdInfo.FoldType) then break; 2184 inc(EndCol); 2185 end; 2186 if (EndCol = Cnt) or (sfaInvalid in NdInfo2.FoldAction) then 2187 Invalidate 2188 else 2189 FCurInfo := NdInfo2; 2190 Result := FCurInfo; 2191end; 2192 2193function TSynFoldNodeInfoHelper.IsAtNodeOpenPos(ANode: TSynTextFoldAVLNode): Boolean; 2194begin 2195 Result := (not (sfaInvalid in FCurInfo.FoldAction)) and 2196 (ANode.IsInFold) and 2197 (FCurInfo.LineIndex = ANode.StartLine - ANode.SourceLineOffset - 1) and 2198 (FCurInfo.NodeIndex = ANode.FoldIndex); 2199end; 2200 2201function TSynFoldNodeInfoHelper.IsValid: Boolean; 2202begin 2203 Result := (not (sfaInvalid in FCurInfo.FoldAction)); 2204end; 2205 2206function TSynFoldNodeInfoHelper.Equals(AnInfo: TSynFoldNodeInfo): Boolean; 2207begin 2208 Result := (FCurInfo.LineIndex = AnInfo.LineIndex) and 2209 (FCurInfo.NodeIndex = AnInfo.NodeIndex) and 2210 (FCurInfo.LogXStart = AnInfo.LogXStart) and 2211 (FCurInfo.LogXEnd = AnInfo.LogXEnd) and 2212 (FCurInfo.FoldLvlStart = AnInfo.FoldLvlStart) and 2213 (FCurInfo.FoldLvlEnd = AnInfo.FoldLvlEnd) and 2214 (FCurInfo.FoldAction = AnInfo.FoldAction) and 2215 (FCurInfo.FoldType = AnInfo.FoldType) and 2216 (FCurInfo.FoldGroup = AnInfo.FoldGroup); 2217end; 2218 2219function TSynFoldNodeInfoHelper.Equals(AHelper: TSynFoldNodeInfoHelper): Boolean; 2220begin 2221 Result := Equals(AHelper.Info); 2222end; 2223 2224{ TSynTextFoldAVLTree } 2225 2226function TSynTextFoldAVLTree.NewNode : TSynTextFoldAVLNodeData; 2227begin 2228 Result := TSynTextFoldAVLNodeData.Create; 2229end; 2230 2231destructor TSynTextFoldAVLTree.Destroy; 2232begin 2233 Clear; 2234 if fNestedNodesTree <> nil then begin 2235 fNestedNodesTree.fRoot := nil; //was freed in self.Clear 2236 fNestedNodesTree.fNestParent := nil; // Or Destroy will access invalid memory 2237 fNestedNodesTree.Free; 2238 end; 2239 inherited Destroy; 2240end; 2241 2242procedure TSynTextFoldAVLTree.Clear; 2243 procedure DeleteNode({var} ANode: TSynTextFoldAVLNodeData); 2244 begin 2245 if ANode.Left <>nil then DeleteNode(ANode.Left); 2246 if ANode.Right <>nil then DeleteNode(ANode.Right); 2247 if ANode.Nested <>nil then DeleteNode(ANode.Nested); 2248 DisposeNode(TSynSizedDifferentialAVLNode(ANode)); 2249 end; 2250begin 2251 if fRoot <> nil then DeleteNode(TSynTextFoldAVLNodeData(fRoot)); 2252 SetRoot(nil); 2253end; 2254 2255procedure TSynTextFoldAVLTree.SetRoot(ANode : TSynSizedDifferentialAVLNode); 2256begin 2257 inherited;; 2258 if fNestParent <> nil then fNestParent.Nested := TSynTextFoldAVLNodeData(ANode); 2259end; 2260 2261procedure TSynTextFoldAVLTree.SetRoot(ANode : TSynSizedDifferentialAVLNode; anAdjustChildLineOffset : Integer); 2262begin 2263 inherited;; 2264 if fNestParent <> nil then fNestParent.Nested := TSynTextFoldAVLNodeData(ANode); 2265end; 2266 2267(* Find Fold by Line in Real Text *) 2268function TSynTextFoldAVLTree.FindFoldForLine(ALine : Integer; 2269 FindNextNode : Boolean = False) : TSynTextFoldAVLNode; 2270var 2271 r : TSynTextFoldAVLNodeData; 2272 rStartLine : Integer; 2273 rFoldedBefore : Integer; 2274begin 2275 r := TSynTextFoldAVLNodeData(fRoot); 2276 rStartLine := fRootOffset; 2277 rFoldedBefore := 0; 2278 while (r <> nil) do begin 2279 rStartLine := rStartLine + r.LineOffset; 2280 2281 if ALine < rStartLine then begin 2282 if FindNextNode and (r.Left = nil) then break; 2283 r := r.Left; // rStartLine points to r, so if r.Left is nil then it is pointing to the next fold; 2284 continue; 2285 end; 2286 2287 rFoldedBefore := rFoldedBefore + r.LeftCount; 2288 if ALine < rStartLine + r.MergedLineCount 2289 then break; 2290 2291 if FindNextNode and (r.Right = nil) then begin 2292 r := r.Successor(rStartLine, rFoldedBefore); 2293 break; 2294 end; 2295 2296 rFoldedBefore := rFoldedBefore + r.MergedLineCount; 2297 r := r.Right; // rStartLine points to r, which now is the start of the previous fold; 2298 end; 2299 2300 Result{%H-}.Init(r, rStartLine, rFoldedBefore); 2301end; 2302 2303(* Find Fold by Line in Folded Text // always returns unfolded, unless next=true *) 2304function TSynTextFoldAVLTree.FindFoldForFoldedLine(ALine : Integer; 2305 FindNextNode : Boolean) : TSynTextFoldAVLNode; 2306var 2307 r : TSynTextFoldAVLNodeData; 2308 rStartLine : Integer; 2309 rFoldedBefore : Integer; 2310begin 2311 r := TSynTextFoldAVLNodeData(fRoot); 2312 rStartLine := fRootOffset; 2313 rFoldedBefore := 0; 2314 while (r <> nil) do begin 2315 rStartLine := rStartLine + r.LineOffset; 2316 2317 // r.LeftCount => "FoldedBefore" 2318 if ALine + r.LeftCount < rStartLine then begin 2319 if FindNextNode and (r.Left = nil) then break; 2320 r := r.Left; // rStartLine points to r, so if r.Left is nil then it is pointing to the next fold; 2321 continue; 2322 end; 2323 2324 ALine := ALine + r.LeftCount + r.MergedLineCount; 2325 rFoldedBefore := rFoldedBefore + r.LeftCount; 2326 2327 if FindNextNode and (r.Right = nil) then begin 2328 r := r.Successor(rStartLine, rFoldedBefore); 2329 break; 2330 end; 2331 2332 rFoldedBefore := rFoldedBefore + r.MergedLineCount; 2333 r := r.Right; // rStartLine points to r, which now is the start of the previous fold; 2334 end; 2335 2336 Result{%H-}.Init(r, rStartLine, rFoldedBefore); 2337end; 2338 2339procedure TSynTextFoldAVLTree.AdjustForLinesInserted(AStartLine, ALineCount, ABytePos: Integer); 2340 Procedure DoAdjustForLinesInserted(Current : TSynTextFoldAVLNodeData; 2341 CurrentLine : Integer); 2342 var 2343 t: LongInt; 2344 begin 2345 while (Current <> nil) do begin 2346 CurrentLine := CurrentLine + Current.LineOffset; 2347 2348 if (AStartLine <= CurrentLine - Current.VisibleLines) or 2349 ( (AStartLine - 1 = CurrentLine - Current.VisibleLines) and 2350 (ABytePos <= Current.FoldColumn) ) 2351 then begin 2352 // move current node 2353 Current.LineOffset := Current.LineOffset + ALineCount; 2354 CurrentLine := CurrentLine + ALineCount; 2355 if Current.Left <> nil then 2356 Current.Left.LineOffset := Current.Left.LineOffset - ALineCount; 2357 Current := Current.Left; 2358 end 2359 else if AStartLine > CurrentLine + Current.MergedLineCount- 1 then begin 2360 // The new lines are entirly behind the current node 2361 Current := Current.Right; 2362 end 2363 else begin 2364 // grow current node (there is only one node one the line, the others are nested) 2365 // CurrentLine <= AStartLine <= CurrentLine + Current.FullCount - 1 2366 t := Current.FullCount; 2367 if AStartLine <= CurrentLine + t - 1 then 2368 Current.FullCount := t + ALineCount; 2369 Current.MergedLineCount:= Current.MergedLineCount+ ALineCount; 2370 Current.AdjustParentLeftCount(ALineCount); 2371 TreeForNestedNode(Current, CurrentLine).AdjustForLinesInserted(AStartLine, ALineCount, ABytePos); 2372 2373 if Current.Right <> nil then // and move entire right 2374 Current.Right.LineOffset := Current.Right.LineOffset + ALineCount; 2375 break; 2376 end; 2377 end; 2378 end; 2379 2380begin 2381 {$IFDEF SynFoldDebug}debugln(['FOLD-- AdjustForLinesInsertedAStartLine:=', AStartLine, ' ALineCount=',ALineCount, ' ABytePos=',ABytePos ]); {$ENDIF} 2382 DoAdjustForLinesInserted(TSynTextFoldAVLNodeData(fRoot), fRootOffset); 2383 AdjustColumn(AStartLine+ALineCount-1, ABytePos, -ABytePos+1, True); 2384end; 2385 2386procedure TSynTextFoldAVLTree.AdjustForLinesDeleted(AStartLine, 2387 ALineCount, ABytePos: Integer); 2388 Procedure AdjustNodeForLinesDeleted(Current : TSynTextFoldAVLNodeData; 2389 CurrentLine, FirstLineToDelete, CountLinesToDelete : Integer); 2390 var 2391 LastLineToDelete, LinesBefore, LinesInside, LinesAfter, t : Integer; 2392 begin 2393 LastLineToDelete := FirstLineToDelete + CountLinesToDelete - 1; // only valid for delete; CountLinesToDelete < 0 2394 2395 while (Current <> nil) do begin 2396 CurrentLine := CurrentLine + Current.LineOffset; 2397 2398 if FirstLineToDelete <= CurrentLine - Current.VisibleLines then begin 2399 // move current node 2400 if LastLineToDelete > CurrentLine - Current.VisibleLines then begin 2401 // overlap => shrink 2402 LinesBefore := CurrentLine - FirstLineToDelete; 2403 LinesInside := CountLinesToDelete - LinesBefore; 2404 // shrink 2405 t := Current.MergedLineCount; 2406 Current.FullCount := Max(Current.FullCount - LinesInside, -1); 2407 Current.MergedLineCount := Max(Current.MergedLineCount - LinesInside, 0); 2408 Current.AdjustParentLeftCount(Current.MergedLineCount - t); // If LineCount = -1; LeftCount will be correctd on delete node 2409 TreeForNestedNode(Current, CurrentLine).AdjustForLinesDeleted(CurrentLine, LinesInside, ABytePos); 2410 2411 if (Current.Right <> nil) then begin 2412 // move right // Calculate from the new curent.LineOffset, as below 2413 AdjustNodeForLinesDeleted(Current.Right, CurrentLine - LinesBefore, 2414 FirstLineToDelete, LinesInside); 2415 end; 2416 end 2417 else LinesBefore := CountLinesToDelete; 2418 2419 // move current node (includes right subtree / left subtree needs eval) 2420 Current.LineOffset := Current.LineOffset - LinesBefore; 2421 CurrentLine := CurrentLine - LinesBefore; 2422 //if AStartLine = CurrentLine then begin 2423 // Current.FoldColumn := Current.FoldColumn + ABytePos-1; 2424 // TreeForNestedNode(Current, CurrentLine).AdjustColumn(CurrentLine, 1, ABytePos-1); 2425 //end; 2426 if Current.Left <> nil then 2427 Current.Left.LineOffset := Current.Left.LineOffset + LinesBefore; 2428 Current := Current.Left; 2429 end 2430 else if FirstLineToDelete > CurrentLine + Current.MergedLineCount - 1 then begin 2431 // The deleted lines are entirly behind the current node 2432 Current := Current.Right; 2433 end 2434 else begin 2435 // (FirstLineToDelete >= CurrentLine) AND (FirstLineToDelete < CurrentLine + Current.LineCount); 2436 LinesAfter := LastLineToDelete - (CurrentLine + Current.MergedLineCount - 1); 2437 if LinesAfter < 0 then LinesAfter := 0; 2438 LinesInside := CountLinesToDelete - LinesAfter; 2439 2440 // shrink current node 2441 t := Current.MergedLineCount; 2442 Current.MergedLineCount := Current.MergedLineCount- LinesInside; 2443 if Current.FullCount > Current.MergedLineCount then 2444 Current.FullCount := Current.MergedLineCount; 2445 Current.AdjustParentLeftCount(Current.MergedLineCount - t); // If MergedLineCount = -1; LeftCount will be correctd on delete node 2446 2447 TreeForNestedNode(Current, CurrentLine).AdjustForLinesDeleted(FirstLineToDelete, LinesInside, ABytePos); 2448 Current := Current.Right; 2449 end; 2450 2451 end; 2452 end; 2453 2454begin 2455 {$IFDEF SynFoldDebug}debugln(['FOLD-- AdjustForLinesDeleted AStartLine:=', AStartLine, ' ALineCount=',ALineCount, ' ABytePos=',ABytePos ]); {$ENDIF} 2456 if ABytePos > 1 then 2457 AdjustColumn(AStartLine+ALineCount-1, 1, ABytePos-1); 2458 AdjustNodeForLinesDeleted(TSynTextFoldAVLNodeData(fRoot), fRootOffset, AStartLine, ALineCount); 2459end; 2460 2461procedure TSynTextFoldAVLTree.AdjustColumn(ALine, ABytePos, ACount: Integer; 2462 InLineBreak: boolean = False); 2463var 2464 Node: TSynTextFoldAVLNode; 2465begin 2466 Node := FindFoldForLine(ALine, True); 2467 {$IFDEF SynFoldDebug}debugln(['FOLD-- AdjustColumn ALine:=', ALine, ' ABytePos=',ABytePos, ' ACount=',ACount, ' // node.srcline=',Node.SourceLine, ' StartLine=', node.StartLine, 'column=',Node.FoldColumn ]); {$ENDIF} 2468 if (not Node.IsInFold) or (Node.SourceLine > ALine) then exit; 2469 if (Node.SourceLine = ALine) and (node.FoldColumn >= ABytePos) then begin 2470 node.FoldColumn := Node.FoldColumn + ACount; 2471 if (not InLineBreak) and (node.FoldColumn < ABytePos) then node.FoldColumn := ABytePos; 2472 end; 2473 TreeForNestedNode(Node.fData, node.StartLine).AdjustColumn(ALine, ABytePos, ACount); 2474end; 2475 2476function TSynTextFoldAVLTree.FindLastFold : TSynTextFoldAVLNode; 2477var 2478 r : TSynTextFoldAVLNodeData; 2479 rStartLine : Integer; 2480 rFoldedBefore : Integer; 2481begin 2482 r := TSynTextFoldAVLNodeData(fRoot); 2483 rStartLine := fRootOffset; 2484 rFoldedBefore := 0; 2485 while (r <> nil) do begin 2486 rStartLine := rStartLine + r.LineOffset; 2487 rFoldedBefore := rFoldedBefore + r.LeftCount + r.MergedLineCount; 2488 if r.Right = nil then break; 2489 r := r.Right; // rStartLine points to r, which now is the start of the previous fold; 2490 end; 2491 2492 Result{%H-}.Init(r, rStartLine, rFoldedBefore); 2493end; 2494 2495function TSynTextFoldAVLTree.FindFirstFold : TSynTextFoldAVLNode; 2496var 2497 r : TSynTextFoldAVLNodeData; 2498 rStartLine : Integer; 2499begin 2500 r := TSynTextFoldAVLNodeData(fRoot); 2501 rStartLine := fRootOffset; 2502 while (r <> nil) do begin 2503 rStartLine := rStartLine + r.LineOffset; 2504 if r.Left = nil then break; 2505 r := r.Left; 2506 end; 2507 2508 Result{%H-}.Init(r, rStartLine, 0); 2509end; 2510 2511function TSynTextFoldAVLTree.LastFoldedLine: integer; 2512var 2513 n: TSynTextFoldAVLNode; 2514begin 2515 n := FindFirstFold; 2516 if not n.IsInFold then exit(0); 2517 Result := n.StartLine + n.MergedLineCount - 1; 2518end; 2519 2520{$IFDEF SynDebug} 2521procedure TSynTextFoldAVLTree.debug; 2522 function debug2(ind, typ : String; ANode, AParent : TSynTextFoldAVLNodeData; offset : integer) :integer; 2523 begin 2524 result := 0; 2525 if ANode = nil then exit; 2526 with ANode do 2527 DebugLn([Format('Lines=%3d-%3d (e=%3d / idx=%d) %2d:%d; Lcnt=%2d / Fcnt=%2d | ', 2528 [offset + ANode.LineOffset, offset + ANode.LineOffset + ANode.FullCount -1, 2529 offset + ANode.LineOffset + ANode.MergedLineCount-1, ANode.FoldIndex, 2530 ANode.FoldColumn, ANode.FoldColumnLen, 2531 MergedLineCount, FullCount]), 2532 ind, typ, ' (',LineOffset, ') LeftCount: ', LeftCount, 2533 ' Balance: ',FBalance]); 2534 if ANode.Parent <> AParent then DebugLn([ind,'* Bad parent']); 2535 Result := debug2(ind+' ', 'L', ANode.Left, ANode, offset+ANode.LineOffset); 2536 If Result <> ANode.LeftCount then debugln([ind,' ***** Leftcount was ',Result, ' but should be ', ANode.LeftCount]); 2537 Result := Result + debug2(ind+' ', 'R', ANode.Right, ANode, offset+ANode.LineOffset); 2538 debug2(ind+' #', 'N', ANode.Nested, nil, offset+ANode.LineOffset); 2539 Result := Result + ANode.MergedLineCount; 2540 end; 2541begin 2542 debugln('StartLine, EndLine (MergedEnd, FoldIndex) - Column:Len; MergedLineCnt / FullCCnt | .. (LineOffset) ...'); 2543 debug2('', ' -', TSynTextFoldAVLNodeData(fRoot), nil, 0); 2544end; 2545{$ENDIF} 2546 2547function TSynTextFoldAVLTree.InsertNewFold(ALine, AFoldIndex, AColumn, AColumnLen, 2548 ACount, AVisibleLines: Integer; AClassification: TFoldNodeClassification; 2549 AFoldTypeCompatible: Pointer) : TSynTextFoldAVLNode; 2550var 2551 r : TSynTextFoldAVLNodeData; 2552begin 2553 {$IFDEF SynFoldDebug}debugln(['FOLD-- InsertNewFold ALine:=', ALine, ' AFoldIndex=', AFoldIndex]);{$ENDIF} 2554 r := NewNode; 2555 r.LineOffset := ALine; // 1-based 2556 r.FoldIndex := AFoldIndex; 2557 r.FoldColumn := AColumn; 2558 r.FoldColumnLen := AColumnLen; 2559 r.MergedLineCount := ACount; 2560 r.FullCount := ACount; 2561 r.LeftCount := 0; 2562 r.VisibleLines := AVisibleLines; 2563 r.Classification := AClassification; 2564 r.FoldTypeCompatible := AFoldTypeCompatible; 2565 2566 Result{%H-}.Init(r, ALine, 0); 2567 Result.fFoldedBefore := InsertNode(r); 2568end; 2569 2570function TSynTextFoldAVLTree.RemoveFoldForLine(ALine : Integer; 2571 OnlyCol: Integer = -1) : Integer; 2572var 2573 OldFold : TSynTextFoldAVLNode; 2574 lcount: Integer; 2575begin 2576 {$IFDEF SynFoldDebug}debugln(['FOLD-- RemoveFoldForLine ALine:=', ALine, ' OnlyCol=',OnlyCol]);{$ENDIF} 2577 Result := ALine; // - 1; // Return index 2578 OldFold := FindFoldForLine(ALine, False); 2579 if OldFold.StartLine < Result then 2580 Result := OldFold.StartLine; 2581 2582 if (not OldFold.IsInFold) then exit; 2583 2584 if OnlyCol < 0 then 2585 RemoveFoldForNodeAtLine(OldFold, ALine) 2586 else 2587 if OldFold.FoldIndex = OnlyCol then 2588 RemoveFoldForNodeAtLine(OldFold, -1) 2589 else 2590 if OldFold.fData.Nested <> nil then begin 2591 TreeForNestedNode(OldFold.fData, OldFold.StartLine).RemoveFoldForLine 2592 (ALine, OnlyCol); 2593 lcount := max(OldFold.FullCount, 2594 TreeForNestedNode(OldFold.fData, 0).LastFoldedLine + 1); 2595 if lcount <> OldFold.MergedLineCount then begin 2596 OldFold.fData.MergedLineCount := lcount; 2597 OldFold.fData.AdjustParentLeftCount(OldFold.MergedLineCount - lcount); 2598 end; 2599 end; 2600end; 2601 2602function TSynTextFoldAVLTree.RemoveFoldForNodeAtLine(ANode : TSynTextFoldAVLNode; 2603 ALine : Integer) : Integer; 2604var 2605 NestedNode, MergeNode : TSynTextFoldAVLNodeData; 2606 NestedLine, offs, lcount : Integer; 2607 OnlyNested: Boolean; 2608 Nested: TSynTextFoldAVLNode; 2609begin 2610 {$IFDEF SynFoldDebug}debugln(['FOLD-- RemoveFoldForNodeAtLine: ALine:=', ALine, ' ANode.StartLine=', ANode.StartLine]);{$ENDIF} 2611 OnlyNested := ALine >= ANode.StartLine + ANode.FullCount; 2612 // The cfCollapsed line is one line before the fold 2613 Result := ANode.StartLine-1; // Return the cfcollapsed that was unfolded 2614 if not OnlyNested then 2615 RemoveNode(ANode.fData); 2616 2617 NestedLine := 0; 2618 If ANode.fData.Nested <> nil then 2619 begin 2620 (*Todo: should we mark the tree as NO balancing needed ???*) 2621 TreeForNestedNode(ANode.fData, ANode.StartLine).RemoveFoldForLine(ALine); 2622 2623 if OnlyNested then begin 2624 NestedLine := ANode.StartLine + ANode.FullCount; 2625 Nested := TreeForNestedNode(ANode.fData, ANode.StartLine).FindLastFold; 2626 while Nested.IsInFold and (Nested.StartLine >= NestedLine) do begin 2627 NestedNode := Nested.fData; 2628 offs := Nested.StartLine; 2629 Nested := Nested.Prev; 2630 2631 lcount := ANode.fData.MergedLineCount; 2632 ANode.fData.MergedLineCount := max(ANode.FullCount, 2633 Nested.StartLine + Nested.MergedLineCount - ANode.StartLine); 2634 ANode.fData.AdjustParentLeftCount(ANode.MergedLineCount - lcount); 2635 2636 TreeForNestedNode(ANode.fData, ANode.StartLine).RemoveNode(NestedNode); 2637 NestedNode.LineOffset := offs; 2638 InsertNode(NestedNode); 2639 end; 2640 lcount := max(ANode.FullCount, 2641 TreeForNestedNode(ANode.fData, ANode.StartLine).LastFoldedLine 2642 - ANode.StartLine + 1); 2643 if lcount <> ANode.MergedLineCount then begin 2644 ANode.fData.MergedLineCount := lcount; 2645 ANode.fData.AdjustParentLeftCount(ANode.MergedLineCount - lcount); 2646 end; 2647 end 2648 else begin 2649 // merge the remaining nested into current 2650 NestedNode := ANode.fData.Nested; 2651 if NestedNode <> nil 2652 then NestedLine := ANode.fStartLine + NestedNode.LineOffset; 2653 2654 while NestedNode <> nil do begin 2655 while NestedNode.Left <> nil do begin 2656 NestedNode := NestedNode.Left; 2657 NestedLine := NestedLine + NestedNode.LineOffset; 2658 end; 2659 2660 if NestedNode.Right <> nil then begin 2661 NestedNode := NestedNode.Right; 2662 NestedLine := NestedLine + NestedNode.LineOffset; 2663 continue; 2664 end; 2665 2666 // leaf node 2667 // Anything that is still nested (MergeNode.Nested), will stay nested 2668 MergeNode := NestedNode; 2669 2670 NestedLine := NestedLine - NestedNode.LineOffset; 2671 NestedNode := NestedNode.Parent; 2672 2673 MergeNode.LineOffset := MergeNode.LineOffset + NestedLine; 2674 if NestedNode <> nil then begin 2675 NestedNode.ReplaceChild(MergeNode, nil); 2676 MergeNode.FParent := nil; 2677 end; 2678 MergeNode.LeftCount := 0; 2679 MergeNode.FBalance := 0; 2680 if MergeNode.FullCount <= 0 then begin 2681 MergeNode.FreeAllChildrenAndNested; 2682 MergeNode.Free; 2683 end 2684 else 2685 InsertNode(MergeNode); 2686 end; 2687 end; 2688 2689 end; 2690 2691 if not OnlyNested then 2692 DisposeNode(TSynSizedDifferentialAVLNode(ANode.fData)); 2693end; 2694 2695function TSynTextFoldAVLTree.InsertNode(ANode : TSynTextFoldAVLNodeData) : Integer; 2696var 2697 rStartLine, NestStartLine : Integer; 2698 rFoldedBefore, NestFoldedBefore : Integer; 2699 2700 current, Nest : TSynTextFoldAVLNodeData; 2701 ALine, AEnd, ACount : Integer; 2702 2703 (* ANode.StartLine < Current.StartLine // ANode goes into tree *) 2704 procedure NestCurrentIntoNewBlock; inline; 2705 var 2706 diff, start2, before2 : Integer; 2707 p : TSynTextFoldAVLNodeData; 2708 begin 2709 current.AdjustParentLeftCount(ACount-current.MergedLineCount); // -RecursiveFoldCount(current)); 2710 rStartLine := rStartLine - current.LineOffset; // rStarteLine is now current.Parent 2711 p := current.Parent; 2712 if p <> nil 2713 then p.ReplaceChild(current, ANode, -rStartLine) 2714 else SetRoot(ANode, -rStartLine); 2715 2716 diff := current.LineOffset - ANode.LineOffset; 2717 ANode.Nested := current; 2718 ANode.FBalance := current.FBalance; 2719 current.LineOffset := diff; // offset to ANode (via Nested) 2720 current.FParent := nil; 2721 current.FBalance := 0; 2722 2723 ANode.SetLeftChild(current.Left, diff, current.LeftCount); 2724 current.FLeft := nil; 2725 current.LeftCount := 0; 2726 ANode.SetRightChild(current.Right, diff); 2727 current.FRight := nil; 2728 2729 start2 := ALine; before2 := rFoldedBefore; 2730 p := ANode.Successor(start2, before2); 2731 while (p <> nil) and (start2 <= AEnd) do begin 2732 RemoveNode(p); 2733 p.LineOffset := start2- ALine; 2734 TreeForNestedNode(Anode, 0).InsertNode(p); 2735 2736 start2 := ALine; before2 := rFoldedBefore; 2737 p := ANode.Successor(start2, before2); 2738 end; 2739 // check only after loop, if we gre, we did so by existing nodes, so no new overlaps 2740 start2 := TreeForNestedNode(Anode, 0).LastFoldedLine; 2741 if start2 > ANode.FullCount - 1 then begin 2742 ANode.AdjustParentLeftCount(start2 + 1 - ANode.MergedLineCount); 2743 ANode.MergedLineCount := start2 + 1; 2744 end; 2745 end; 2746 2747 (* ANode.StartLine > Current.StartLine // Current remains in tree *) 2748 procedure NestNewBlockIntoCurrent; //inline; 2749 var 2750 end2, start2, before2: Integer; 2751 p: TSynTextFoldAVLNodeData; 2752 begin 2753 // Check if current.LineCount needs extension 2754 ANode.LineOffset := ALine - rStartLine; 2755 if current.Nested <> nil 2756 then TreeForNestedNode(current, 0).InsertNode(ANode) 2757 else current.Nested := ANode; 2758 2759 end2 := TreeForNestedNode(current, 0).LastFoldedLine; 2760 if end2 > current.FullCount -1 then begin 2761 end2 := rStartLine + end2; 2762 2763 start2 := rStartLine; before2 := rFoldedBefore; 2764 p := current.Successor(start2, before2); 2765 while (p <> nil) and (start2 <= end2) do begin 2766 RemoveNode(p); 2767 p.LineOffset := start2 - rStartLine; 2768 TreeForNestedNode(current, 0).InsertNode(p); 2769 2770 start2 := rStartLine; before2 := rFoldedBefore; 2771 p := current.Successor(start2, before2); 2772 end; 2773 end2 := TreeForNestedNode(current, 0).LastFoldedLine; 2774 if end2 > current.FullCount -1 then begin 2775 current.AdjustParentLeftCount(end2 + 1 - current.MergedLineCount); 2776 current.MergedLineCount := end2 + 1; 2777 end; 2778 end; 2779 end; 2780 2781begin 2782 Result := 0; 2783 if fRoot = nil then begin 2784 SetRoot(ANode, -fRootOffset); 2785 exit; 2786 end; 2787 ALine := ANode.LineOffset; 2788 ACount := ANode.MergedLineCount; 2789 AEnd := ALine + ACount - 1; 2790 current := TSynTextFoldAVLNodeData(fRoot); 2791 rStartLine := fRootOffset; 2792 rFoldedBefore := 0; 2793 Nest := nil; 2794 NestFoldedBefore := 0; 2795 NestStartLine := 0; 2796 2797 while (current <> nil) do begin 2798 rStartLine := rStartLine + current.LineOffset; 2799 2800 if ALine < rStartLine then begin 2801 (* *** New block goes to the left *** *) 2802 // remember possible nesting, continue scan for nesting with precessor 2803 if (AEnd >= rStartLine) then begin 2804 Nest := current; 2805 NestFoldedBefore := rFoldedBefore; 2806 NestStartLine := rStartLine; 2807 end; 2808 2809 if current.Left <> nil Then begin 2810 current := current.Left; 2811 continue; 2812 end 2813 else if Nest = nil then begin // insert as Left - no nesting 2814 current.AdjustParentLeftCount(ACount); 2815 current.SetLeftChild(ANode, -rStartLine, ANode.MergedLineCount); 2816 BalanceAfterInsert(ANode); 2817 end 2818 else begin // nest 2819 current := Nest; 2820 rStartLine := NestStartLine; 2821 rFoldedBefore := NestFoldedBefore; 2822 NestCurrentIntoNewBlock; 2823 end; 2824 break; 2825 end; 2826 2827 rFoldedBefore := rFoldedBefore + current.LeftCount; 2828 if ALine = rStartLine then begin 2829 if ANode.FoldIndex > current.FoldIndex then 2830 (* *** New Block will be nested in current *** *) 2831 NestNewBlockIntoCurrent 2832 else 2833 if ANode.FoldIndex < current.FoldIndex then 2834 (* *** current will be nested in New Block *** *) 2835 NestCurrentIntoNewBlock 2836 else begin 2837 debugln(['Droping Foldnode / Already exists. Startline=', rStartLine,' LineCount=',ACount]); 2838 FreeAndNil(ANode); 2839 end; 2840 end 2841 else begin 2842 If ALine <= rStartLine + current.MergedLineCount - 1 2843 (* *** New Block will be nested in current *** *) 2844 then NestNewBlockIntoCurrent 2845 (* *** New block goes to the right *** *) 2846 else begin 2847 rFoldedBefore := rFoldedBefore + current.MergedLineCount; 2848 if current.Right <> nil then begin 2849 current := current.Right; 2850 continue; 2851 end 2852 else if Nest=nil then Begin // insert to the right - no nesting 2853 current.AdjustParentLeftCount(ACount); 2854 current.SetRightChild(ANode, -rStartLine); 2855 BalanceAfterInsert(ANode); 2856 end 2857 else begin // nest 2858 current := Nest; 2859 rStartLine := NestStartLine; 2860 rFoldedBefore := NestFoldedBefore; 2861 NestCurrentIntoNewBlock; 2862 end; 2863 2864 end; 2865 end; 2866 2867 break; 2868 end; // while 2869 2870 Result := rFoldedBefore; 2871end; 2872 2873function TSynTextFoldAVLTree.TreeForNestedNode(ANode: TSynTextFoldAVLNodeData; aOffset : Integer) : TSynTextFoldAVLTree; 2874begin 2875 if fNestedNodesTree = nil then fNestedNodesTree := TSynTextFoldAVLTree.Create; 2876 Result := fNestedNodesTree; 2877 Result.fRoot := ANode.Nested; 2878 Result.fNestParent := ANode; // TODO: this is dangerous, this is never cleaned up, even if ANode is Destroyed 2879 Result.fRootOffset := aOffset; 2880end; 2881 2882constructor TSynTextFoldAVLTree.Create; 2883begin 2884 inherited; 2885 fNestParent := nil; 2886 fNestedNodesTree := nil; 2887end; 2888 2889{ TSynEditFoldProvider } 2890 2891function TSynEditFoldProvider.GetLineCapabilities(ALineIdx: Integer): TSynEditFoldLineCapabilities; 2892var 2893 c: Integer; 2894begin 2895 Result := []; 2896 if not FEnabled then 2897 exit; 2898 if (FEdit <> nil) and (FEdit.SelAvail) then begin 2899 if (FEdit.BlockBegin.Y < ALineIdx+1) and 2900 (FEdit.BlockEnd.Y > ALineIdx+1) 2901 then Result := [cfFoldBody]; 2902 if (FEdit.BlockEnd.Y = ALineIdx+1) then Result := [cfFoldEnd]; 2903 if (FEdit.BlockBegin.Y = ALineIdx+1) then Result := [cfHideStart]; 2904 if (FEdit.BlockBegin.Y = ALineIdx+1) and 2905 (FEdit.BlockEnd.Y = ALineIdx+1) then Result := [cfHideStart, cfSingleLineHide]; 2906 end; 2907 if (FHighlighter = nil) or (ALineIdx < 0) then 2908 exit; 2909 2910 FHighlighter.CurrentLines := FLines; 2911 if FHighlighter.FoldBlockEndLevel(ALineIdx - 1) > 0 then Result := Result + [cfFoldBody]; 2912 if FHighlighter.FoldBlockClosingCount(ALineIdx) > 0 then Result := Result + [cfFoldEnd, cfFoldBody]; 2913 2914 c := FHighlighter.FoldNodeInfo[ALineIdx].CountEx([]); 2915 if c > 0 then begin 2916 c := FHighlighter.FoldNodeInfo[ALineIdx].CountEx([sfaOpenFold, sfaFoldFold]); 2917 if c > 0 then 2918 include(Result, cfFoldStart); 2919 c := FHighlighter.FoldNodeInfo[ALineIdx].CountEx([sfaOpenFold, sfaFoldHide]); 2920 if c > 0 then 2921 include(Result, cfHideStart); 2922 c := FHighlighter.FoldNodeInfo[ALineIdx].CountEx([sfaOneLineOpen, sfaFoldHide]); // TODO: Include scftFoldEnd ? 2923 // Todo: cfSingleLineHide only, if there is no other hide 2924 if c > 0 then 2925 Result := Result + [cfHideStart, cfSingleLineHide]; 2926 end 2927 else 2928 if FHighlighter.FoldBlockOpeningCount(ALineIdx) > 0 then include(Result, cfFoldStart); 2929end; 2930 2931function TSynEditFoldProvider.GetLineClassification(ALineIdx: Integer): TFoldNodeClassifications; 2932begin 2933 Result := []; 2934 if (FEdit <> nil) and FEdit.SelAvail and (FEdit.BlockBegin.Y = ALineIdx+1) then 2935 Result := [fncBlockSelection]; 2936end; 2937 2938function TSynEditFoldProvider.GetNestedFoldsList: TLazSynEditNestedFoldsList; 2939begin 2940 if FNestedFoldsList = nil then 2941 FNestedFoldsList := TLazSynEditNestedFoldsList.Create(FLines, FHighlighter); 2942 Result := FNestedFoldsList; 2943end; 2944 2945function TSynEditFoldProvider.GetFoldsAvailable: Boolean; 2946begin 2947 Result := FEnabled and ( 2948 (FHighlighter <> nil) or 2949 ((FEdit <> nil) and FEdit.SelAvail) 2950 ); 2951end; 2952 2953function TSynEditFoldProvider.GetHighLighterWithLines: TSynCustomFoldHighlighter; 2954begin 2955 Result := FHighlighter; 2956 if (Result = nil) then 2957 exit; 2958 Result.CurrentLines := FLines; 2959end; 2960 2961procedure TSynEditFoldProvider.SetHighLighter(const AValue: TSynCustomFoldHighlighter); 2962begin 2963 if FHighlighter = AValue then exit; 2964 FHighlighter := AValue; 2965 if FNestedFoldsList <> nil then 2966 FNestedFoldsList.HighLighter := FHighlighter; 2967end; 2968 2969procedure TSynEditFoldProvider.SetLines(AValue: TSynEditStrings); 2970begin 2971 if FLines = AValue then Exit; 2972 FLines := AValue; 2973 if FNestedFoldsList <> nil then 2974 FNestedFoldsList.Lines := FLines; 2975end; 2976 2977constructor TSynEditFoldProvider.Create; 2978begin 2979 FEnabled := True; 2980 inherited Create; 2981end; 2982 2983destructor TSynEditFoldProvider.Destroy; 2984begin 2985 inherited Destroy; 2986 FreeAndNil(FNestedFoldsList); 2987end; 2988 2989function TSynEditFoldProvider.FoldOpenCount(ALineIdx: Integer; AType: Integer = 0): Integer; 2990begin 2991 if not FEnabled then 2992 exit(0); 2993 2994 if (FHighlighter = nil) or (ALineIdx < 0) then begin 2995 if (AType=0) and (FEdit <> nil) and FEdit.SelAvail and (FEdit.BlockBegin.Y=ALineIdx+1) then exit(1); 2996 exit(0); 2997 end; 2998 // Need to check alll nodes with FoldNodeInfoCount 2999 // Hide-able nodes can open and close on the same line "(* comment *)" 3000 FHighlighter.CurrentLines := FLines; 3001 Result := FHighlighter.FoldNodeInfo[ALineIdx].CountEx([sfaOpenFold, sfaFold], AType); 3002 // fallback for HL without GetFoldNodeInfoCountEx 3003 if Result < 0 then 3004 Result := FHighlighter.FoldBlockOpeningCount(ALineIdx, AType); 3005 if (AType=0) and (FEdit <> nil) and FEdit.SelAvail and (FEdit.BlockBegin.Y=ALineIdx+1) then 3006 inc(Result); 3007end; 3008 3009function TSynEditFoldProvider.FoldOpenInfo(ALineIdx, AFoldIdx: Integer; 3010 AType: Integer = 0): TSynFoldNodeInfo; 3011 3012 function BlockSelInfo(NIdx: Integer): TSynFoldNodeInfo; 3013 begin 3014 Result.LineIndex := ALineIdx; 3015 Result.NodeIndex := NIdx; 3016 Result.LogXStart := FEdit.BlockBegin.x; 3017 Result.LogXEnd := FEdit.BlockBegin.x; 3018 Result.FoldLvlStart := 0; 3019 Result.NestLvlStart := 0; 3020 Result.NestLvlEnd := 1; 3021 Result.FoldLvlEnd := 1; 3022 Result.FoldAction := [sfaOpen, sfaOpenFold, sfaFold, sfaFoldHide]; 3023 Result.FoldType := nil; 3024 Result.FoldTypeCompatible := nil; 3025 Result.FoldGroup := -1; 3026 end; 3027 3028begin 3029 Result.FoldAction := [sfaInvalid]; 3030 if (FHighlighter = nil) or (ALineIdx < 0) then begin 3031 if (AType=0) and (FEdit <> nil) and FEdit.SelAvail and (FEdit.BlockBegin.Y=ALineIdx+1) then 3032 exit(BlockSelInfo(0)); 3033 exit; 3034 end; 3035 3036 FHighlighter.CurrentLines := FLines; 3037 if (AType = 0) and (FEdit <> nil) and FEdit.SelAvail and 3038 (FEdit.BlockBegin.Y=ALineIdx+1) and 3039 (AFoldIdx = FoldOpenCount(ALineIdx, AType)-1) 3040 then 3041 Result := BlockSelInfo(AFoldIdx) 3042 else 3043 Result := FHighlighter.FoldNodeInfo[ALineIdx].NodeInfoEx(AFoldIdx, [sfaOpen, sfaFold], AType); 3044end; 3045 3046function TSynEditFoldProvider.FoldLineLength(ALine, AFoldIndex: Integer): integer; 3047begin 3048 if (FEdit <> nil) and FEdit.SelAvail and (FEdit.BlockBegin.Y=ALine+1) and 3049 (AFoldIndex = FoldOpenCount(ALine, 0)-1) 3050 then 3051 exit(FEdit.BlockEnd.y - FEdit.BlockBegin.y); 3052 3053 FHighlighter.CurrentLines := FLines; 3054 Result := FHighlighter.FoldLineLength(ALine, AFoldIndex); 3055end; 3056 3057function TSynEditFoldProvider.InfoForFoldAtTextIndex(ALine, AFoldIndex: Integer; 3058 HideLen: Boolean; NeedLen: Boolean = True): TSynEditFoldProviderNodeInfo; 3059var 3060 nd: TSynFoldNodeInfo; 3061begin 3062 Result.LineCount := 0; 3063 Result.Column := 0; 3064 Result.ColumnLen := 0; 3065 Result.DefaultCollapsed := False; 3066 Result.Classification := fncInvalid; 3067 if not FoldsAvailable then 3068 exit; 3069 3070 if NeedLen then begin 3071 Result.LineCount := FoldLineLength(ALine, AFoldIndex); 3072 if HideLen then 3073 inc(Result.LineCount); 3074 end 3075 else 3076 Result.LineCount := -1; 3077 nd := FoldOpenInfo(ALine, AFoldIndex, 0); 3078 Result.Column := nd.LogXStart+1; 3079 Result.ColumnLen := nd.LogXEnd - nd.LogXStart; 3080 Result.DefaultCollapsed := (sfaDefaultCollapsed in nd.FoldAction); 3081 Result.FoldTypeCompatible := nd.FoldTypeCompatible; 3082 Result.FoldGroup := nd.FoldGroup; 3083 if Result.FoldGroup = -1 then 3084 Result.Classification := fncBlockSelection 3085 else 3086 Result.Classification := fncHighlighter; 3087end; 3088 3089function TSynEditFoldProvider.InfoListForFoldsAtTextIndex(ALine: Integer; 3090 NeedLen: Boolean): TSynEditFoldProviderNodeInfoList; 3091var 3092 i: Integer; 3093begin 3094 i := FoldOpenCount(ALine); 3095 SetLength(Result, i); 3096 while i > 0 do begin 3097 dec(i); 3098 Result[i] := InfoForFoldAtTextIndex(ALine, i, False, NeedLen); 3099 end; 3100end; 3101 3102{ TSynEditFoldedView } 3103 3104constructor TSynEditFoldedView.Create(AOwner: TSynEditBase; 3105 ACaret: TSynEditCaret); 3106begin 3107 FOwner := AOwner; 3108 inherited Create; 3109 fTopLine := 0; 3110 fLinesInWindow := -1; 3111 fCaret := ACaret; 3112 fCaret.AddChangeHandler(@DoCaretChanged); 3113 fFoldTree := TSynTextFoldAVLTree.Create; 3114 FFoldProvider := TSynEditFoldProvider.Create; 3115 FFoldProvider.FEdit := FOwner; 3116 // TODO: if NextLineChanges, update FFoldProvider // DoSynStringsChanged 3117 FDisplayView := TLazSynDisplayFold.Create(Self); 3118 3119 FMarkupInfoFoldedCode := TSynSelectedColor.Create; 3120 FMarkupInfoFoldedCode.Background := clNone; 3121 FMarkupInfoFoldedCode.Foreground := clDkGray; 3122 FMarkupInfoFoldedCode.FrameColor := clDkGray; 3123 3124 FMarkupInfoFoldedCodeLine := TSynSelectedColor.Create; 3125 FMarkupInfoFoldedCodeLine.Background := clNone; 3126 FMarkupInfoFoldedCodeLine.Foreground := clNone; 3127 FMarkupInfoFoldedCodeLine.FrameColor := clNone; 3128 3129 FMarkupInfoHiddenCodeLine := TSynSelectedColor.Create; 3130 FMarkupInfoHiddenCodeLine.Background := clNone; 3131 FMarkupInfoHiddenCodeLine.Foreground := clNone; 3132 FMarkupInfoHiddenCodeLine.FrameColor := clNone; 3133 3134 FOwner.RegisterStatusChangedHandler(@DoBlockSelChanged, [scSelection]); 3135 FOwner.RegisterCommandHandler(@ProcessMySynCommand, nil, [hcfPreExec]); 3136 FOwner.TextViewsManager.AddTextView(Self); 3137end; 3138 3139destructor TSynEditFoldedView.Destroy; 3140begin 3141 FOwner.UnregisterCommandHandler(@ProcessMySynCommand); 3142 NextLines := nil; 3143 fCaret.RemoveChangeHandler(@DoCaretChanged); 3144 FreeAndNil(FDisplayView); 3145 fFoldTree.Free; 3146 fTextIndexList := nil; 3147 fFoldTypeList := nil; 3148 FMarkupInfoFoldedCode.Free; 3149 FMarkupInfoFoldedCodeLine.Free; 3150 FMarkupInfoHiddenCodeLine.Free; 3151 FreeAndNil(FFoldProvider); 3152 inherited Destroy; 3153end; 3154 3155function TSynEditFoldedView.TextToViewIndex(aTextIndex: TLineIdx): TLineIdx; 3156begin 3157 aTextIndex := InternTextToViewIndex(aTextIndex); 3158 Result := inherited TextToViewIndex(aTextIndex); 3159end; 3160 3161function TSynEditFoldedView.ViewToTextIndex(aViewIndex: TLineIdx): TLineIdx; 3162begin 3163 aViewIndex := inherited ViewToTextIndex(aViewIndex); 3164 Result := InternViewToTextIndex(aViewIndex); 3165end; 3166 3167function TSynEditFoldedView.TextXYToViewXY(APhysTextXY: TPhysPoint): TPhysPoint; 3168begin 3169 Result := inherited TextXYToViewXY(APhysTextXY); 3170 Result.y := ToPos(InternTextToViewIndex(ToIdx(Result.y))); 3171end; 3172 3173function TSynEditFoldedView.ViewXYToTextXY(APhysViewXY: TPhysPoint): TPhysPoint; 3174begin 3175 APhysViewXY.y := ToPos(InternViewToTextIndex(ToIdx(APhysViewXY.y))); 3176 Result := inherited ViewXYToTextXY(APhysViewXY); 3177end; 3178 3179procedure TSynEditFoldedView.LinesInsertedAtTextIndex(AStartIndex, ALineCount, ABytePos: Integer; SkipFixFolding : Boolean); 3180var top : Integer; 3181begin 3182 if ALineCount = 0 then exit; 3183 top := TopTextIndex; 3184 fFoldTree.AdjustForLinesInserted(AStartIndex+1, ALineCount, ABytePos); 3185 if AStartIndex < top then 3186 TopTextIndex := top + ALineCount; 3187 if not(SkipFixFolding) then FixFoldingAtTextIndex(AStartIndex, AStartIndex+ALineCount+1) 3188 else 3189 if AStartIndex < top + ALineCount then CalculateMaps; 3190end; 3191 3192//procedure TSynEditFoldedView.LinesInsertedAtViewPos(AStartPos, ALineCount : Integer; SkipFixFolding : Boolean); 3193//begin 3194// LinesInsertedAtTextIndex(InternViewToTextIndex(ToIdx(AStartPos)), ALineCount, SkipFixFolding); 3195//end; 3196 3197procedure TSynEditFoldedView.LinesDeletedAtTextIndex(AStartIndex, ALineCount, ABytePos: Integer; SkipFixFolding : Boolean); 3198var top : Integer; 3199begin 3200 top := TopTextIndex; 3201 // topline may get out of sync => synedit is always going to change it back 3202 fFoldTree.AdjustForLinesDeleted(AStartIndex+1, ALineCount, ABytePos); 3203 if not(SkipFixFolding) then 3204 FixFoldingAtTextIndex(AStartIndex, AStartIndex+ALineCount+1) 3205 else 3206 if AStartIndex < top - ALineCount then CalculateMaps; 3207end; 3208 3209//procedure TSynEditFoldedView.LinesDeletedAtViewPos(AStartPos, ALineCount : Integer; SkipFixFolding : Boolean); 3210//begin 3211// LinesDeletedAtTextIndex(InternViewToTextIndex(ToIdx(AStartPos)), ALineCount, SkipFixFolding); 3212//end; 3213 3214function TSynEditFoldedView.InternTextToViewIndex(aTextIndex: TLineIdx): TLineIdx; 3215var 3216 n: TSynTextFoldAVLNode; 3217begin 3218 n := fFoldTree.FindFoldForLine(aTextIndex + 1); 3219 if n.IsInFold then 3220 Result := ToIdx(n.StartLine) - 1 - n.FoldedBefore 3221 else 3222 Result := aTextIndex - n.FoldedBefore; 3223end; 3224 3225function TSynEditFoldedView.TextIndexToScreenLine(aTextIndex : Integer) : Integer; 3226begin 3227 Result := InternTextToViewIndex(aTextIndex) - TopLine + 1; 3228end; 3229 3230function TSynEditFoldedView.InternViewToTextIndex(aViewIndex: TLineIdx): TLineIdx; 3231begin 3232 result := aViewIndex + fFoldTree.FindFoldForFoldedLine(ToPos(aViewIndex)).FoldedBefore; 3233end; 3234 3235function TSynEditFoldedView.ScreenLineToTextIndex(aLine : Integer) : Integer; 3236begin 3237 Result := InternViewToTextIndex(aLine + TopLine - 1); 3238end; 3239 3240function TSynEditFoldedView.TextIndexAddLines(aTextIndex, LineOffset : Integer) : Integer; 3241var 3242 node : TSynTextFoldAVLNode; 3243 boundary : integer; 3244begin 3245 node := fFoldTree.FindFoldForLine(aTextIndex+1, True); 3246 result := aTextIndex; 3247 if LineOffset < 0 then begin 3248 boundary := Max(0, InternViewToTextIndex(0)); 3249 if node.IsInFold 3250 then node := node.Prev 3251 else node := fFoldTree.FindLastFold; 3252 while LineOffset < 0 do begin 3253 dec(Result); 3254 if Result <= boundary then exit(boundary); 3255 while node.IsInFold and (Result+1 < node.StartLine + node.MergedLineCount) do begin 3256 Result := Result - node.MergedLineCount; 3257 if Result <= boundary then exit(boundary); 3258 node := node.Prev; 3259 end; 3260 inc(LineOffset); 3261 end; 3262 end else begin 3263 boundary := NextLines.Count; 3264 while LineOffset > 0 do begin 3265 if Result >= boundary then exit(boundary); 3266 inc(Result); 3267 while node.IsInFold and (Result+1 >= node.StartLine) do begin 3268 Result := Result + node.MergedLineCount; 3269 if Result >= boundary then exit(boundary); 3270 if Result >= boundary then exit(boundary-node.MergedLineCount-1); 3271 node := node.Next; 3272 end; 3273 dec(LineOffset); 3274 end; 3275 end; 3276end; 3277 3278function TSynEditFoldedView.AddVisibleOffsetToTextIndex(aTextIndex: TLineIdx; 3279 LineOffset: Integer): TLineIdx; 3280begin 3281 //TODO: Modify LineOffset then call inherited; 3282 Result := TextIndexAddLines(aTextIndex, LineOffset); 3283// Result := inherited AddVisibleOffsetToTextIndex(aTextIndex, LineOffset); 3284end; 3285 3286function TSynEditFoldedView.IsTextIdxVisible(aTextIndex: TLineIdx): Boolean; 3287begin 3288 Result := not FoldedAtTextIndex[aTextIndex]; 3289 if Result then 3290 Result := inherited IsTextIdxVisible(aTextIndex); 3291end; 3292 3293procedure TSynEditFoldedView.Lock; 3294begin 3295 if fLockCount=0 then begin 3296 fNeedFixFrom := -1; 3297 fNeedFixMinEnd := -1; 3298 end; 3299 inc(fLockCount); 3300end; 3301 3302procedure TSynEditFoldedView.UnLock; 3303begin 3304 dec(fLockCount); 3305 if (fLockCount=0) then begin 3306 if (fNeedFixFrom >= 0) then 3307 FixFolding(fNeedFixFrom, fNeedFixMinEnd, fFoldTree); 3308 if fvfNeedCaretCheck in FFlags then 3309 DoCaretChanged(fCaret); 3310 if fvfNeedCalcMaps in FFlags then 3311 CalculateMaps; 3312 end; 3313end; 3314 3315(* Count *) 3316function TSynEditFoldedView.GetViewedCount : integer; 3317begin 3318 Result := NextLines.ViewedCount - fFoldTree.FindLastFold.FoldedBefore; 3319end; 3320 3321function TSynEditFoldedView.GetDisplayView: TLazSynDisplayView; 3322begin 3323 Result := FDisplayView; 3324end; 3325 3326procedure TSynEditFoldedView.InternalGetInfoForViewedXY(AViewedXY: TPhysPoint; 3327 AFlags: TViewedXYInfoFlags; out AViewedXYInfo: TViewedXYInfo; 3328 ALogPhysConvertor: TSynLogicalPhysicalConvertor); 3329var 3330 OldY: LongInt; 3331begin 3332 OldY := AViewedXY.y; 3333 AViewedXY.y := ToPos(InternViewToTextIndex(ToIdx(AViewedXY.y))); 3334 OldY := OldY - AViewedXY.y; 3335 inherited InternalGetInfoForViewedXY(AViewedXY, AFlags, AViewedXYInfo, 3336 ALogPhysConvertor); 3337 AViewedXYInfo.CorrectedViewedXY.y := AViewedXYInfo.CorrectedViewedXY.y + OldY; 3338end; 3339 3340function TSynEditFoldedView.GetFoldClasifications(index : Integer): TFoldNodeClassifications; 3341begin 3342 if (index < -1) or (index > fLinesInWindow + 1) then exit([]); 3343 Result := fFoldTypeList[index+1].Classifications; 3344end; 3345 3346function TSynEditFoldedView.GetHighLighter: TSynCustomHighlighter; 3347begin 3348 Result := FFoldProvider.HighLighter; 3349 if assigned(Result) then 3350 Result.CurrentLines := NextLines; 3351end; 3352 3353(* Topline *) 3354procedure TSynEditFoldedView.SetTopLine(const ALine : integer); 3355begin 3356 if fTopLine = ALine then exit; 3357 FInTopLineChanged := True; 3358 fTopLine := ALine; 3359 CalculateMaps; 3360 FInTopLineChanged := False; 3361end; 3362 3363function TSynEditFoldedView.GetTopTextIndex : integer; 3364begin 3365 Result := fTopLine + fFoldTree.FindFoldForFoldedLine(fTopLine).FoldedBefore - 1; 3366end; 3367 3368procedure TSynEditFoldedView.SetTopTextIndex(const AIndex : integer); 3369begin 3370 TopLine := AIndex + 1 - fFoldTree.FindFoldForLine(AIndex+1).FoldedBefore; 3371end; 3372 3373(* LinesInWindow*) 3374procedure TSynEditFoldedView.SetLinesInWindow(const AValue : integer); 3375begin 3376 if fLinesInWindow = AValue then exit; 3377 fLinesInWindow := AValue; 3378 SetLength(fTextIndexList, AValue + 3); 3379 SetLength(fFoldTypeList, AValue + 3); // start 1 before topline 3380 CalculateMaps; 3381end; 3382 3383procedure TSynEditFoldedView.DoFoldChanged(AnIndex: Integer); 3384begin 3385 SendNotification(senrLineMappingChanged, Self, AnIndex, 0); 3386end; 3387 3388procedure TSynEditFoldedView.SetManager(AManager: TSynTextViewsManager); 3389begin 3390 if Manager <> nil then begin 3391 RemoveChangeHandler(senrLineCount, @LineCountChanged); 3392 RemoveNotifyHandler(senrCleared, @LinesCleared); 3393 RemoveEditHandler(@LineEdited); 3394 end; 3395 inherited SetManager(AManager); 3396 if Manager <> nil then begin 3397 AddChangeHandler(senrLineCount, @LineCountChanged); 3398 AddNotifyHandler(senrCleared, @LinesCleared); 3399 AddEditHandler(@LineEdited); 3400 end; 3401end; 3402 3403procedure TSynEditFoldedView.SetSynStrings(AValue: TSynEditStrings); 3404begin 3405 inherited SetSynStrings(AValue); 3406 FFoldProvider.FLines := AValue; 3407end; 3408 3409procedure TSynEditFoldedView.DoBlockSelChanged(Sender: TObject; 3410 Changes: TSynStatusChanges); 3411begin 3412 CalculateMaps; 3413end; 3414 3415procedure TSynEditFoldedView.CalculateMaps; 3416var 3417 i, tpos, cnt : Integer; 3418 node, tmpnode: TSynTextFoldAVLNode; 3419 FirstChanged, LastChanged: Integer; 3420 NewCapability: TSynEditFoldLineCapabilities; 3421 NewClassifications :TFoldNodeClassifications; 3422begin 3423 if fLinesInWindow < 0 then exit; 3424 if (fLockCount > 0) and 3425 ((not FInTopLineChanged) or (fvfNeedCalcMaps in FFlags)) // TODO: Scan now, to avoid invalidate later 3426 then begin 3427 Include(FFlags, fvfNeedCalcMaps); 3428 exit; 3429 end; 3430 Exclude(FFlags, fvfNeedCalcMaps); 3431 3432 node := fFoldTree.FindFoldForFoldedLine(fTopLine, true); 3433 // ftopline is not a folded line 3434 // so node.FoldedBefore(next node after ftopl) does apply 3435 tpos := fTopLine + node.FoldedBefore - 1; 3436 if node.IsInFold then 3437 tmpnode := node.Prev 3438 else 3439 tmpnode := fFoldTree.FindLastFold; 3440 if tmpnode.IsInFold and (tmpnode.StartLine + tmpnode.MergedLineCount = tpos + 1) then begin 3441 node := tmpnode; 3442 tpos := tpos - node.MergedLineCount; 3443 end; 3444 {$IFDEF SynFoldDebug}debugln(['FOLD-- CalculateMaps fTopLine:=', fTopLine, ' tpos=',tpos]);{$ENDIF} 3445 cnt := NextLines.Count; 3446 FirstChanged := -1; 3447 LastChanged := -1; 3448 for i := 0 to fLinesInWindow + 2 do begin 3449 if (tpos > cnt) or (tpos < 0) then begin 3450 // Past end of Text 3451 fTextIndexList[i] := -1; 3452 NewCapability := []; 3453 NewClassifications := []; 3454 end else begin 3455 fTextIndexList[i] := tpos - 1; // TextIndex is 0-based 3456 NewCapability := FFoldProvider.LineCapabilities[tpos - 1]; 3457 NewClassifications := FFoldProvider.LineClassification[tpos - 1]; 3458 if (node.IsInFold) then begin 3459 if (tpos = node.SourceLine) then begin 3460 include(NewCapability, cfCollapsedFold); 3461 include(NewClassifications, node.fData.Classification); 3462 end 3463 else if node.IsHide and (tpos + 1 = node.SourceLine) then begin 3464 include(NewCapability, cfCollapsedHide); 3465 include(NewClassifications, node.fData.Classification); 3466 end; 3467 end; 3468 3469 inc(tpos); 3470 while (node.IsInFold) and (tpos >= node.StartLine) do begin 3471 tpos := tpos + node.MergedLineCount; 3472 node := node.Next; 3473 end; 3474 end; 3475 3476 if (fFoldTypeList[i].Capability <> NewCapability) or 3477 (fFoldTypeList[i].Classifications <> NewClassifications) 3478 then begin 3479 if FirstChanged < 0 then FirstChanged := tpos - 1; 3480 LastChanged := tpos; 3481 end; 3482 fFoldTypeList[i].Capability := NewCapability; 3483 fFoldTypeList[i].Classifications := NewClassifications; 3484 end; 3485 if (not FInTopLineChanged) and (FirstChanged > 0) then 3486 FOwner.InvalidateGutterLines(FirstChanged, LastChanged + 1); 3487end; 3488 3489(* Lines *) 3490function TSynEditFoldedView.GetViewedLines(index : Integer) : String; 3491begin 3492 if (index < -1) or (index > fLinesInWindow + 1) then 3493 exit(NextLines.ViewedLines[ScreenLineToTextIndex(Index)]); 3494 Result := NextLines.ViewedLines[fTextIndexList[index+1]]; 3495end; 3496 3497function TSynEditFoldedView.GetDisplayNumber(index : Integer) : Integer; 3498begin 3499 if (index < -1) or (index > fLinesInWindow + 1) 3500 or (fTextIndexList[index+1] < 0) then exit(-1); 3501 Result := fTextIndexList[index+1]+1; 3502end; 3503 3504function TSynEditFoldedView.GetTextIndex(index : Integer) : Integer; 3505begin 3506 if (index < -1) or (index > fLinesInWindow + 1) then 3507 exit(ScreenLineToTextIndex(Index)); 3508 Result := fTextIndexList[index+1]; 3509end; 3510 3511function TSynEditFoldedView.GetFoldType(index : Integer) : TSynEditFoldLineCapabilities; 3512begin 3513 if (index < -1) or (index > fLinesInWindow + 1) then exit([]); 3514 Result := fFoldTypeList[index+1].Capability; 3515end; 3516 3517function TSynEditFoldedView.IsFolded(index : integer) : Boolean; 3518begin 3519 Result := fFoldTree.FindFoldForLine(index+1).IsInFold; 3520end; 3521 3522procedure TSynEditFoldedView.ProcessMySynCommand(Sender: TObject; 3523 AfterProcessing: boolean; var Handled: boolean; 3524 var Command: TSynEditorCommand; var AChar: TUTF8Char; Data: pointer; 3525 HandlerData: pointer); 3526var 3527 CY: Integer; 3528begin 3529 if Handled then 3530 exit; 3531 3532 case Command of 3533 EcFoldLevel1..EcFoldLevel9: 3534 begin 3535 FoldAll(Command - EcFoldLevel1); 3536 FCaret.Touch; 3537 Handled := True; 3538 end; 3539 EcFoldLevel0: 3540 begin 3541 UnfoldAll; 3542 FCaret.Touch; 3543 Handled := True; 3544 end; 3545 EcFoldCurrent: 3546 begin 3547 CY := ExpandedLineForBlockAtLine(FCaret.LinePos); 3548 if CY > 0 then begin 3549 FoldAtTextIndex(CY-1); 3550 FCaret.ChangeOnTouch; // setting the caret always clears selection (even setting to current pos / no change) 3551 FCaret.LineCharPos:= Point(1, CY); 3552 end; 3553 Handled := True; 3554 end; 3555 EcUnFoldCurrent: 3556 begin 3557 UnFoldAtTextIndex(FCaret.LinePos-1); 3558 FCaret.Touch; 3559 Handled := True; 3560 end; 3561 end; 3562end; 3563 3564procedure TSynEditFoldedView.SetHighLighter(AValue: TSynCustomHighlighter); 3565begin 3566 if not(AValue is TSynCustomFoldHighlighter) then 3567 AValue := nil; 3568 FFoldProvider.HighLighter := TSynCustomFoldHighlighter(AValue); 3569 UnfoldAll; 3570end; 3571 3572(* Folding *) 3573 3574procedure TSynEditFoldedView.FoldAtLine(AStartLine : Integer; 3575 ColIndex : Integer = -1; ColCount : Integer = 1; Skip: Boolean = False; 3576 AVisibleLines: Integer = 1); 3577begin 3578 FoldAtViewPos(AStartLine + fTopLine, ColIndex, ColCount, Skip, AVisibleLines); 3579end; 3580 3581procedure TSynEditFoldedView.FoldAtViewPos(AStartPos : Integer; 3582 ColIndex : Integer = -1; ColCount : Integer = 1; Skip: Boolean = False; 3583 AVisibleLines: Integer = 1); 3584begin 3585 FoldAtTextIndex(AStartPos - 1 + fFoldTree.FindFoldForFoldedLine(AStartPos).FoldedBefore, 3586 ColIndex, ColCount, Skip, AVisibleLines); 3587end; 3588 3589function TSynEditFoldedView.FoldNodeAtTextIndex(AStartIndex, 3590 ColIndex: Integer): TSynTextFoldAVLNode; 3591var 3592 tree: TSynTextFoldAVLTree; 3593begin 3594 Result := fFoldTree.FindFoldForLine(AStartIndex + 1, True); 3595 3596 tree := fFoldTree; 3597 while (not Result.IsInFold) or (Result.SourceLine <> AStartIndex + 1) do begin 3598 if (not Result.IsInFold) then 3599 Result := tree.FindLastFold; 3600 while Result.IsInFold and (Result.SourceLine > AStartIndex + 1) do 3601 Result := Result.Prev; 3602 if not Result.IsInFold then break; 3603 3604 if Result.IsInFold and (Result.SourceLine < AStartIndex + 1) then begin 3605 if Result.fData.Nested = nil then break; 3606 tree := fFoldTree.TreeForNestedNode(Result.fData, Result.StartLine); 3607 Result := tree.FindFirstFold; 3608 while Result.IsInFold and (Result.SourceLine < AStartIndex + 1) do 3609 Result := Result.Next; 3610 end 3611 else 3612 break; 3613 end; 3614 3615 while Result.IsInFold and (Result.SourceLine = AStartIndex + 1) do begin 3616 if Result.FoldIndex = ColIndex then 3617 exit; 3618 if Result.fData.Nested = nil then break; 3619 Result := fFoldTree.TreeForNestedNode(Result.fData, Result.StartLine).FindFirstFold; 3620 end; 3621 Result.fData := nil; 3622end; 3623 3624function TSynEditFoldedView.IsFoldedAtTextIndex(AStartIndex, ColIndex: Integer): Boolean; 3625begin 3626 Result := FoldNodeAtTextIndex(AStartIndex, ColIndex).IsInFold; 3627end; 3628 3629function TSynEditFoldedView.LogicalPosToNodeIndex(AStartIndex: Integer; LogX: Integer; 3630 Previous: Boolean): Integer; 3631var 3632 hl: TSynCustomFoldHighlighter; 3633 c, i: Integer; 3634 nd: TSynFoldNodeInfo; 3635begin 3636 hl := TSynCustomFoldHighlighter(HighLighter); 3637 if not assigned(hl) then 3638 exit(0); 3639 // AStartIndex is 0-based 3640 // FoldTree is 1-based AND first line remains visble 3641 c := hl.FoldNodeInfo[AStartIndex].CountEx([sfaOpen, sfaFold]); 3642 if c = 0 then 3643 exit(-1); 3644 i := 0; 3645 while i < c do begin 3646 nd := hl.FoldNodeInfo[aStartIndex].NodeInfoEx(i, [sfaOpen, sfaFold]); 3647 if (nd.LogXStart >= LogX) then begin 3648 dec(i); 3649 if not Previous then 3650 i := -1; 3651 break; 3652 end; 3653 if (nd.LogXEnd >= LogX) then 3654 break; 3655 inc(i); 3656 end; 3657 Result := i; 3658end; 3659 3660procedure TSynEditFoldedView.CollapseDefaultFolds; 3661var 3662 i, j, c: Integer; 3663 hl: TSynCustomFoldHighlighter; 3664 fldinf: TSynEditFoldProviderNodeInfo; 3665begin 3666 hl := TSynCustomFoldHighlighter(HighLighter); 3667 if not assigned(hl) then 3668 exit; 3669 3670 i := 0; 3671 while i < NextLines.Count do begin 3672 // Todo: Highlighter should return a list of types that can return default folded 3673 // Currently PascalHl Type 2 = Region 3674 c := hl.FoldBlockOpeningCount(i, 2); 3675 if c > 0 then begin 3676 c := hl.FoldNodeInfo[i].CountEx([sfaOpen, sfaFold]); 3677 j := 0; 3678 while j < c do begin 3679 fldinf := FoldProvider.InfoForFoldAtTextIndex(i, j); 3680 if (fldinf.DefaultCollapsed) and (not IsFoldedAtTextIndex(i, j)) 3681 then begin 3682 // TODO: detect default hide too 3683 // currently always VisibleLines=1 => since region only folds 3684 fFoldTree.InsertNewFold(i+2, j, fldinf.Column, fldinf.ColumnLen, fldinf.LineCount, 1, 3685 fldinf.Classification, fldinf.FoldTypeCompatible); 3686 DoFoldChanged(i); 3687 end; 3688 inc(j); 3689 end; 3690 end; 3691 inc(i); 3692 end; 3693 CalculateMaps; 3694end; 3695 3696function TSynEditFoldedView.GetFoldDescription(AStartIndex, AStartCol, AEndIndex, 3697 AEndCol: Integer; AsText: Boolean = False; Extended: Boolean = False): String; 3698var 3699 FoldCoders: Array of TSynEditFoldExportCoder; 3700 3701 function FoldCoderForType(AType: Pointer): TSynEditFoldExportCoder; 3702 var 3703 i, j: Integer; 3704 begin 3705 i := 0; 3706 j := length(FoldCoders); 3707 while (i < j) and (FoldCoders[i].FoldType <> AType) do 3708 inc(i); 3709 if (i = j) then begin 3710 SetLength(FoldCoders, i + 1); 3711 FoldCoders[i] := TSynEditFoldExportCoder.Create(AType); 3712 end; 3713 Result := FoldCoders[i]; 3714 end; 3715 3716var 3717 hl: TSynCustomFoldHighlighter; 3718 FoldHelper: TSynEditFoldExportStream; 3719 NodeIterator: TSynTextFoldAVLNodeNestedIterator; 3720 NdiHelper1: TSynFoldNodeInfoHelper; 3721 Node: TSynTextFoldAVLNode; 3722 NdInfo, NdInfo2: TSynFoldNodeInfo; 3723 entry: TFoldExportEntry; 3724 i: Integer; 3725 NodeFoldType: TSynEditFoldType; 3726begin 3727 Result := ''; 3728 hl := TSynCustomFoldHighlighter(HighLighter); 3729 if not assigned(hl) then exit; 3730 3731 if AEndIndex < 0 then AEndIndex := MaxInt; 3732 if AEndCol < 0 then AEndCol := MaxInt; 3733 3734 Node := fFoldTree.FindFoldForLine(AStartIndex + 1, True); 3735 NodeIterator := TSynTextFoldAVLNodeNestedIterator.Create(Node); 3736 FoldHelper := TSynEditFoldExportStream.Create; 3737 NdiHelper1 := TSynFoldNodeInfoHelper.Create(hl); 3738 try 3739 if (AStartCol > 1) then 3740 while Node.IsInFold and (Node.StartLine = AStartIndex + 2) do begin 3741 NdInfo := NdiHelper1.GotoNodeOpenPos(Node); 3742 if (sfaInvalid in NdInfo.FoldAction) or (ndinfo.LogXStart >= AStartCol) then 3743 break; 3744 Node := NodeIterator.Next; 3745 end; 3746 dec(AStartCol); 3747 if not node.IsInFold then 3748 exit; 3749 3750 (* Text stores fold length according to AVLNode 3751 Binary stores line-diff between highlighter open and close line 3752 *) 3753 if AsText then 3754 begin (* *** Encode as Text for XML *** *) 3755 {$IFDEF SynFoldSaveDebug} 3756 DebugLnEnter(['TSynEditFoldedView.GetFoldDescription as Text']); 3757 {$ENDIF} 3758 while Node.IsInFold and (Node.fData.Classification <> fncHighlighter) do 3759 Node := NodeIterator.Next; 3760 if not node.IsInFold then 3761 exit; 3762 3763 NdInfo := NdiHelper1.GotoNodeOpenPos(Node); 3764 while Node.IsInFold and (Node.StartLine-2 <= AEndIndex) do 3765 begin 3766 if (node.StartLine > AStartIndex + 2) then AStartCol := 0; 3767 3768 NodeFoldType := scftFold; 3769 if Node.SourceLineOffset = 0 then 3770 NodeFoldType := scftHide; 3771 if (NdInfo.FoldAction * [sfaInvalid, sfaDefaultCollapsed] = []) then // Currently skip default nodes 3772 FoldCoderForType(NdInfo.FoldType).AddNode 3773 (NdInfo.LogXStart, NdInfo.LineIndex, Node.FullCount, NodeFoldType); 3774 3775 Node := NodeIterator.Next; 3776 while Node.IsInFold and (Node.fData.Classification <> fncHighlighter) do 3777 Node := NodeIterator.Next; 3778 if not Node.IsInFold then 3779 break; 3780 3781 NdInfo := NdiHelper1.Next; 3782 while NdiHelper1.IsValid and (not NdiHelper1.IsAtNodeOpenPos(Node)) do begin 3783 // Add unfolded nodes 3784 if (NdInfo.FoldAction * [sfaInvalid, sfaDefaultCollapsed] = []) then // Currently skip default nodes 3785 FoldCoderForType(NdInfo.FoldType).AddNode 3786 (NdInfo.LogXStart, NdInfo.LineIndex, 0, scftOpen); 3787 NdInfo := NdiHelper1.Next; 3788 end; 3789 end; 3790 3791 for i := 0 to length(FoldCoders) - 1 do begin 3792 FoldCoders[i].Finish; 3793 FoldHelper.AppendMem(FoldCoders[i].Stream.Mem, FoldCoders[i].Stream.Len); 3794 end; 3795 FoldHelper.AddChecksum; 3796 FoldHelper.Compress; 3797 {$IFDEF SynFoldSaveDebug} 3798 DebugLnExit(['TSynEditFoldedView.GetFoldDescription as Text']); 3799 {$ENDIF} 3800 end (* *** END: Encode as Text for XML *** *) 3801 else 3802 begin (* *** Encode as Binary *** *) 3803 while Node.IsInFold and (Node.StartLine-2 <= AEndIndex) do 3804 begin 3805 if (node.StartLine > AStartIndex + 2) then 3806 AStartCol := 0; 3807 3808 NdInfo2 := NdiHelper1.GotoNodeClosePos(Node); 3809 if (sfaInvalid in NdInfo2.FoldAction) or 3810 (NdInfo2.LineIndex > AEndIndex) or 3811 ((NdInfo2.LineIndex = AEndIndex) and (ndinfo2.LogXEnd > AEndCol)) 3812 then begin 3813 node := NodeIterator.Next; 3814 continue; 3815 end; 3816 3817 NdInfo := NdiHelper1.GotoNodeOpenPos(Node); 3818 3819 with entry do begin 3820 LogX := NdInfo.LogXStart - AStartCol; 3821 LogX2 := NdInfo.LogXEnd - ndinfo.LogXStart + (ndinfo.LogXStart - AStartCol); 3822 Line := NdInfo.LineIndex - AStartIndex; 3823 ELogX := NdInfo2.LogXStart; 3824 ELogX2 := NdInfo2.LogXEnd; 3825 ELine := NdInfo2.LineIndex - AStartIndex; 3826 //if sfaLastLineClose in NdInfo2.FoldAction then 3827 // ELine := -1; // unfinished fold 3828 FType := PtrUInt(NdInfo.FoldType); 3829 LinesFolded := node.FullCount; 3830 end; 3831 FoldHelper.AppendMem(@entry, SizeOf(TFoldExportEntry)); 3832 3833 Node := NodeIterator.Next; 3834 end; 3835 end; (* *** END: Encode as Binary *** *) 3836 3837 Result := FoldHelper.Text; 3838 finally 3839 FoldHelper.Free; 3840 for i := 0 to length(FoldCoders) - 1 do 3841 FoldCoders[i].Free; 3842 NodeIterator.Free; 3843 NdiHelper1.Free; 3844 end; 3845end; 3846 3847procedure TSynEditFoldedView.ApplyFoldDescription(AStartIndex, AStartCol, AEndIndex, 3848 AEndCol: Integer; FoldDesc: PChar; FoldDescLen: Integer; IsText: Boolean = False); 3849var 3850 FoldCoders: Array of TSynEditFoldExportCoder; 3851 3852 function FoldCoderForType(AType: Pointer): TSynEditFoldExportCoder; 3853 var 3854 j: Integer; 3855 begin 3856 j := length(FoldCoders) - 1; 3857 while (j >= 0) and (FoldCoders[j] <> nil) and (FoldCoders[j].FoldType <> AType) do 3858 dec(j); 3859 if (j < 0) then 3860 Result := nil 3861 else 3862 Result := FoldCoders[j]; 3863 end; 3864 3865 procedure RemoveCoderForType(AType: Pointer); 3866 var 3867 j: Integer; 3868 begin 3869 j := length(FoldCoders) - 1; 3870 while (j >= 0) and (FoldCoders[j] <> nil) and (FoldCoders[j].FoldType <> AType) do 3871 dec(j); 3872 if (j >= 0) then begin 3873 debugln(['FoldState loading removed data for foldtype: ', PtrUInt(AType)]); 3874 FreeAndNil(FoldCoders[j]); 3875 end; 3876 end; 3877 3878 3879var 3880 hl: TSynCustomFoldHighlighter; 3881 FoldHelper: TSynEditFoldExportStream; 3882 NdiHelper1: TSynFoldNodeInfoHelper; 3883 NdInfo, ndinfo2: TSynFoldNodeInfo; 3884 i: Integer; 3885 Line, FL: Integer; 3886 entry: TFoldExportEntry; 3887 Coder: TSynEditFoldExportCoder; 3888 IsFold, IsHide: Boolean; 3889begin 3890 hl := TSynCustomFoldHighlighter(HighLighter); 3891 if not assigned(hl) then 3892 exit; 3893 if (FoldDesc = nil) or (FoldDescLen = 0) then exit; 3894 3895 NdiHelper1 := TSynFoldNodeInfoHelper.Create(hl); 3896 FoldHelper := TSynEditFoldExportStream.Create; 3897 try 3898 FoldHelper.Mem := FoldDesc; 3899 FoldHelper.Len := FoldDescLen; 3900 3901 if IsText then 3902 begin (* *** Decode from Text for XML *** *) 3903 try 3904 FoldHelper.Decompress; 3905 except 3906 exit; 3907 end; 3908 if not FoldHelper.VerifyChecksum then 3909 exit; //raise ESynEditError.Create('fold checksum error'); 3910 3911 i := 0; 3912 while not FoldHelper.EOF do begin 3913 SetLength(FoldCoders, i + 1); 3914 FoldCoders[i] := TSynEditFoldExportCoder.Create(FoldHelper); 3915 if not FoldCoders[i].ReadIsValid then 3916 break; 3917 inc(i); 3918 end; 3919 3920 NdInfo := NdiHelper1.FirstOpen; 3921 3922 while NdiHelper1.IsValid do begin 3923 if (sfaDefaultCollapsed in NdInfo.FoldAction) then begin // Currently skip default nodes 3924 NdInfo := NdiHelper1.Next; 3925 continue; 3926 end; 3927 Coder := FoldCoderForType(NdInfo.FoldType); 3928 if coder <> nil then begin 3929 i := FoldProvider.InfoForFoldAtTextIndex(NdInfo.LineIndex, NdInfo.NodeIndex).LineCount; 3930 case coder.ReadNode(NdInfo.LogXStart, NdInfo.LineIndex, i) of 3931 scftFold: FoldAtTextIndex(NdInfo.LineIndex, NdInfo.NodeIndex); 3932 scftHide: FoldAtTextIndex(NdInfo.LineIndex, NdInfo.NodeIndex, 1, False, 0); 3933 scftInvalid: RemoveCoderForType(NdInfo.FoldType); 3934 end; 3935 end; 3936 NdInfo := NdiHelper1.Next; 3937 end; 3938 end (* *** END: Encode as Text for XML *** *) 3939 else 3940 begin (* *** Decode from Binary *** *) 3941 entry.Line := 0; 3942 if AStartCol > 0 then 3943 dec(AStartCol); 3944 while not FoldHelper.EOF do begin 3945 if not FoldHelper.ReadMem(@entry, sizeof(TFoldExportEntry)) then 3946 break; 3947 if entry.Line > 0 then AStartCol := 0; 3948 3949 Line := AStartIndex + entry.Line; 3950 if Line >= NextLines.Count then 3951 continue; 3952 3953 ndinfo :=NdiHelper1.GotoOpenAtChar(Line, entry.LogX); 3954 Fl := FoldProvider.InfoForFoldAtTextIndex(Line, ndinfo.NodeIndex).LineCount; 3955 IsFold := (sfaFoldFold in NdInfo.FoldAction) and (entry.LinesFolded = FL); 3956 IsHide := (sfaFoldHide in NdInfo.FoldAction) and (entry.LinesFolded = FL + 1); 3957 if (sfaInvalid in ndinfo.FoldAction) or 3958 (ndinfo.LogXStart <> entry.LogX + AStartCol) or 3959 (ndinfo.LogXEnd <> entry.LogX2 + AStartCol) or 3960 //(ndinfo.FoldType <> entry.FType) or 3961 (not (IsHide or IsFold)) 3962 then 3963 continue; 3964 3965 ndinfo2 := NdiHelper1.FindClose; 3966 if (sfaInvalid in ndinfo2.FoldAction) or 3967 (ndinfo2.LogXStart <> entry.ELogX) or 3968 (ndinfo2.LogXEnd <> entry.ELogX2) 3969 then 3970 continue; 3971 3972 i := 1; 3973 if IsHide then i := 0;; 3974 FoldAtTextIndex(Line, NdInfo.NodeIndex, 1, False, i); 3975 end; 3976 end; (* *** END: Encode as Binary *** *) 3977 finally 3978 for i := 0 to length(FoldCoders) - 1 do 3979 FoldCoders[i].Free; 3980 FreeAndNil(FoldHelper); 3981 FreeAndNil(NdiHelper1); 3982 end; 3983end; 3984 3985procedure TSynEditFoldedView.FoldAtTextIndex(AStartIndex : Integer; 3986 ColIndex : Integer = -1; ColCount : Integer = 1; Skip: Boolean = False; 3987 AVisibleLines: Integer = 1); 3988var 3989 NodeCount, top: Integer; 3990 down: Boolean; 3991 NFolded: TSynTextFoldAVLNode; 3992 IsHide: Boolean; 3993 fldinf: TSynEditFoldProviderNodeInfo; 3994begin 3995 if not FoldProvider.FoldsAvailable then exit; 3996 top := TopTextIndex; 3997 3998 // AStartIndex is 0-based 3999 // FoldTree is 1-based AND first line remains visble 4000 NodeCount := FoldProvider.FoldOpenCount(AStartIndex); 4001 if ColCount = 0 then 4002 ColCount := NodeCount; 4003 4004 down := ColIndex < 0; 4005 if down then 4006 ColIndex := NodeCount + ColIndex; 4007 4008 IsHide := AVisibleLines = 0; 4009 4010 while ColCount > 0 do begin 4011 if (ColIndex < 0) or (ColIndex >= NodeCount) then break; 4012 NFolded := FoldNodeAtTextIndex(AStartIndex, ColIndex); 4013 // TODO: Check if position can Hide or fold 4014 if skip and ( ( (AVisibleLines=0) and NFolded.IsHide ) or 4015 ( (AVisibleLines>0) and NFolded.IsInFold ) ) 4016 then begin 4017 if down 4018 then dec(ColIndex) 4019 else inc(ColIndex); 4020 continue; 4021 end; 4022 4023 // TODO: missing check, that hl-node is hideable 4024 fldinf := FoldProvider.InfoForFoldAtTextIndex(AStartIndex, ColIndex, IsHide); 4025 if not NFolded.IsInFold then begin 4026 if fldinf.LineCount > 0 then 4027 fFoldTree.InsertNewFold(AStartIndex+1+AVisibleLines, ColIndex, 4028 fldinf.Column, fldinf.ColumnLen, fldinf.LineCount, 4029 AVisibleLines, 4030 fldinf.Classification, fldinf.FoldTypeCompatible) 4031 end 4032 else begin 4033 if (AVisibleLines=0) and (not NFolded.IsHide) and (fldinf.LineCount > 0) then begin 4034 // upgrade to hide 4035 fFoldTree.RemoveFoldForNodeAtLine(NFolded, -1); 4036 fFoldTree.InsertNewFold(AStartIndex+1, ColIndex, 4037 fldinf.Column, fldinf.ColumnLen, fldinf.LineCount, 4038 AVisibleLines, 4039 fldinf.Classification, fldinf.FoldTypeCompatible); 4040 end; 4041 end; 4042 if down 4043 then dec(ColIndex) 4044 else inc(ColIndex); 4045 dec(ColCount); 4046 end; 4047 4048 fTopLine := -1; // make sure seting TopLineTextIndex, will do CalculateMaps; 4049 TopTextIndex := top; 4050 DoFoldChanged(AStartIndex); 4051end; 4052 4053procedure TSynEditFoldedView.UnFoldAtLine(AStartLine : Integer; 4054 ColIndex : Integer = -1; ColCount : Integer = 0; Skip: Boolean = False; 4055 AVisibleLines: Integer = 1); 4056begin 4057 UnFoldAtViewPos(AStartLine + fTopLine, ColIndex, ColCount, Skip, AVisibleLines); 4058end; 4059 4060procedure TSynEditFoldedView.UnFoldAtViewPos(AStartPos : Integer; 4061 ColIndex : Integer = -1; ColCount : Integer = 0; Skip: Boolean = False; 4062 AVisibleLines: Integer = 1); 4063begin 4064 UnFoldAtTextIndex(AStartPos - 1 + fFoldTree.FindFoldForFoldedLine(AStartPos).FoldedBefore, 4065 ColIndex, ColCount, Skip, AVisibleLines); 4066end; 4067 4068procedure TSynEditFoldedView.UnFoldAtTextIndex(AStartIndex : Integer; 4069 ColIndex : Integer = -1; ColCount : Integer = 0; Skip: Boolean = False; 4070 AVisibleLines: Integer = 1); 4071var 4072 top, c, r, r2 : Integer; 4073 down: Boolean; 4074 NFolded: TSynTextFoldAVLNode; 4075begin 4076 top := TopTextIndex; 4077 c := FoldProvider.FoldOpenCount(AStartIndex); 4078 4079 //TODO move to FoldProvider 4080 NFolded := fFoldTree.FindFoldForLine(AStartIndex+1, True); 4081 while NFolded.IsInFold and (NFolded.StartLine = AStartIndex+1) do begin 4082 if NFolded.FoldIndex + 1 > c then c := NFolded.FoldIndex + 1; 4083 NFolded := fFoldTree.TreeForNestedNode(NFolded.fData, NFolded.StartLine).FindFoldForLine(AStartIndex, True); 4084 end; 4085 4086 if c < 1 then begin 4087 // TODO: foldprovider to return all folded nodes, for hte line 4088 ColCount := 0; 4089 end; 4090 4091 r := -1; 4092 if ColCount = 0 then begin 4093 r := fFoldTree.RemoveFoldForLine(AStartIndex+AVisibleLines+1); // r is 1-based num of first (ex-)hidden line 4094 end 4095 else begin 4096 down := ColIndex < 0; 4097 if down then 4098 ColIndex := c + ColIndex ; 4099 while ColCount > 0 do begin 4100 if (ColIndex < 0) or (ColIndex >= c) then break; 4101 NFolded := FoldNodeAtTextIndex(AStartIndex, ColIndex); 4102 if skip and ( ( (AVisibleLines=0) and not NFolded.IsHide ) or 4103 ( (AVisibleLines>0) and not NFolded.IsInFold ) ) 4104 then begin 4105 if down 4106 then dec(ColIndex) 4107 else inc(ColIndex); 4108 continue; 4109 end; 4110 r2 := fFoldTree.RemoveFoldForLine(AStartIndex+1+AVisibleLines, ColIndex); 4111 if r2 > 0 then dec(r2); 4112 if (r < 0) or (r2 < r) then r := r2; 4113 if down 4114 then dec(ColIndex) 4115 else inc(ColIndex); 4116 dec(ColCount); 4117 end; 4118 end; 4119 4120 fTopLine := -1; // make sure seting TopLineTextIndex, will do CalculateMaps; 4121 TopTextIndex := top; 4122 if (r >= 0) then 4123 DoFoldChanged(Max(0, r - 2)); 4124end; 4125 4126procedure TSynEditFoldedView.UnFoldAtTextIndexCollapsed(AStartIndex: Integer); 4127var 4128 top, r: Integer; 4129begin 4130 top := TopTextIndex; 4131 r := fFoldTree.RemoveFoldForLine(AStartIndex+1) - 1; 4132 fTopLine := -1; // make sure seting TopLineTextIndex, will do CalculateMaps; 4133 TopTextIndex := top; 4134 DoFoldChanged(r); 4135end; 4136 4137procedure TSynEditFoldedView.UnfoldAll; 4138var 4139 top : Integer; 4140begin 4141 top := TopTextIndex; 4142 fFoldTree.Clear; 4143 fTopLine := -1; // make sure seting TopLineTextIndex, will do CalculateMaps; 4144 TopTextIndex := top; 4145 DoFoldChanged(0); 4146end; 4147 4148procedure TSynEditFoldedView.FoldAll(StartLevel : Integer = 0; IgnoreNested : Boolean = False); 4149var 4150 c, i, top, t: Integer; 4151 hl: TSynCustomFoldHighlighter; 4152 fldinf: TSynEditFoldProviderNodeInfo; 4153begin 4154 hl := TSynCustomFoldHighlighter(HighLighter); 4155 if not assigned(hl) then 4156 exit; 4157 4158 t := 1; // TODO: Highlighter default type; or iterate through all types 4159 top := TopTextIndex; 4160 fFoldTree.Clear; 4161 i := 0; 4162 while i < NextLines.Count do begin 4163 if (hl.FoldBlockOpeningCount(i, t) > 0) 4164 and (hl.FoldBlockEndLevel(i, t) > StartLevel) then begin 4165 c := hl.FoldBlockOpeningCount(i) -1; 4166 fldinf := FoldProvider.InfoForFoldAtTextIndex(i, c); 4167 // i is 0-based 4168 // FoldTree is 1-based AND first line remains visble 4169 fFoldTree.InsertNewFold(i+2, c, fldinf.Column, fldinf.ColumnLen, fldinf.LineCount, 1, 4170 fldinf.Classification, fldinf.FoldTypeCompatible); // TODO: hide too? currently VisibleLines=1 4171 if IgnoreNested then 4172 i := i + fldinf.LineCount; 4173 end; 4174 inc(i); 4175 end; 4176 fTopLine := -1; 4177 TopTextIndex := top; 4178 DoFoldChanged(0); 4179end; 4180 4181function TSynEditFoldedView.FixFolding(AStart: Integer; AMinEnd: Integer; 4182 aFoldTree: TSynTextFoldAVLTree): Boolean; 4183var 4184 FirstchangedLine, MaxCol: Integer; 4185 SrcLineForFldInfos: Integer; 4186 FldInfos: TSynEditFoldProviderNodeInfoList; 4187 4188 function DoFixFolding(doStart: Integer; doMinEnd, AtColumn: Integer; 4189 doFoldTree: TSynTextFoldAVLTree; node: TSynTextFoldAVLNode) : Boolean; 4190 4191 Procedure DoRemoveNode(var theNode: TSynTextFoldAVLNode); 4192 var 4193 tmpnode: TSynTextFoldAVLNode; 4194 l: Integer; 4195 begin 4196 Result := True; 4197 tmpnode := theNode.Prev; 4198 l := theNode.SourceLine; 4199 doFoldTree.RemoveFoldForNodeAtLine(theNode, -1); // Don't touch any nested node 4200 if tmpnode.IsInFold then theNode := tmpnode.Next 4201 else theNode := doFoldTree.FindFirstFold; 4202 if (FirstchangedLine < 0) or (l < FirstchangedLine) then 4203 FirstchangedLine := l; 4204 end; 4205 4206 var 4207 FldSrcLine, FldSrcIndex, FLdNodeLine, FldLen, FndLen: Integer; 4208 i, j, CurLen: Integer; 4209 SubTree: TSynTextFoldAVLTree; 4210 begin 4211 {$IFDEF SynFoldDebug}try DebugLnEnter(['>>FOLD-- DoFixFolding: doStart=', doStart, ' AMinEnd=',AMinEnd]);{$ENDIF} 4212 {$IFDEF SynFoldDebug}aFoldTree.Debug;{$ENDIF} 4213 Result := False; 4214 FldSrcLine := doStart; 4215 while node.IsInFold do begin 4216 {$IFDEF SynFoldDebug}debugln(['>>FOLD-- Node StartLine=', node.StartLine, ' FoldColumn=', node.FoldColumn, ' FoldIndex=', node.FoldIndex, ' FullCount=', node.FullCount, ' Classification=', dbgs(node.Classification)]);{$ENDIF} 4217 FldSrcLine := node.SourceLine; // the 1-based cfCollapsed (last visible) Line (or 1st hidden) 4218 FLdNodeLine := node.StartLine; // the 1 based, first hidden line 4219 FldSrcIndex := FldSrcLine - 1; 4220 FldLen := node.FullCount; 4221 if (FldLen <= 0) then begin 4222 {$IFDEF SynFoldDebug}debugln(['>>FOLD-- FixFolding: Remove node with len<0 FldSrcLine=', FldSrcLine]);{$ENDIF} 4223 DoRemoveNode(node); 4224 continue; 4225 end; 4226 4227 //{$IFDEF SynAssertFold} 4228 //With mixed fold/hide => line goes up/down 4229 //SynAssert(FldSrcLine >= SrcLineForFldInfos, 'TSynEditFoldedView.FixFolding: FoldLine went backwards now %d was %d', [FldSrcLine, SrcLineForFldInfos]); 4230 //{$ENDIF} 4231 if (FldSrcLine <> SrcLineForFldInfos) then begin 4232 // Next Line 4233 SrcLineForFldInfos := FldSrcLine; 4234 AtColumn := 0; 4235 // AtColumn is used for nodes, behing the HLs index-range (fncHighlighterEx, fncBlockSelection) 4236 // TODO: At Colum may be wrong for mixed fold/hide 4237 FldInfos := FoldProvider.InfoListForFoldsAtTextIndex(FldSrcIndex, False); 4238 MaxCol := length(FldInfos)-1; 4239 {$IFDEF SynFoldDebug}debugln(['>>FOLD-- Got FldInfos for FldSrcIndex=', FldSrcIndex, ' MaxCol=', MaxCol]);{$ENDIF} 4240 end; 4241 4242 if node.fData.Classification in [fncHighlighter, fncHighlighterEx] then begin 4243 // find node in list 4244 i := -1; 4245 while (i < MaxCol) do begin 4246 inc(i); 4247 if (FldInfos[i].Classification <> fncHighlighter) or 4248 (FldInfos[i].FoldTypeCompatible <> node.fData.FoldTypeCompatible) 4249 then 4250 continue; 4251 FndLen := -1; 4252 j := abs(FldInfos[i].Column - node.FoldColumn); 4253 if (j > 0) and (j < node.FoldColumnLen) then begin 4254 //maybe 4255 FndLen := FoldProvider.FoldLineLength(FldSrcIndex, i); 4256 if node.IsHide then inc(FndLen); 4257 if FndLen <> node.FullCount then Continue; 4258 {$IFDEF SynFoldDebug}debugln('******** FixFolding: Adjusting x pos');{$ENDIF} 4259 //FldInfos[i].Column := node.FoldColumn; 4260 end; 4261 if (FndLen > 0) or (FldInfos[i].Column = node.FoldColumn) then begin 4262 if FndLen < 0 then begin 4263 FndLen := FoldProvider.FoldLineLength(FldSrcIndex, i); 4264 if node.IsHide then inc(FndLen); 4265 end; 4266 if abs(FndLen - node.FullCount) > 1 then continue; 4267 if (node.fData.Classification <> fncHighlighter) or 4268 (node.FoldColumn <> FldInfos[i].Column) or 4269 (node.FoldIndex <> i) 4270 then 4271 Result := true; 4272 {$IFDEF SynFoldDebug}if (node.fData.Classification <> fncHighlighter) then debugln(['>>FOLD-- FixFolding: set Node to fncHighlighter (FOUND) FldSrcLine=', FldSrcLine]);{$ENDIF} 4273 node.fData.Classification := fncHighlighter; 4274 node.FoldColumn := FldInfos[i].Column; 4275 node.fData.FoldIndex := i; 4276 i := -1; 4277 break; 4278 end; 4279 end; 4280 if i = MaxCol then begin 4281 {$IFDEF SynFoldDebug}debugln(['>>FOLD-- FixFolding: set Node to fncHighlighterEx (NOT FOUND) FldSrcLine=', FldSrcLine]);{$ENDIF} 4282 node.fData.Classification := fncHighlighterEx; 4283 node.fData.FoldIndex := MaxCol + AtColumn; 4284 inc(AtColumn); 4285 Result := True; 4286 end; 4287 end 4288 else begin 4289 if node.fData.FoldIndex <> MaxCol + AtColumn then 4290 Result := True; 4291 node.fData.FoldIndex := MaxCol + AtColumn; 4292 inc(AtColumn); 4293 end; 4294 4295 if (node.fData.Nested <> nil) then begin 4296 SubTree := doFoldTree.TreeForNestedNode(node.fData, FLdNodeLine); 4297 CurLen := node.MergedLineCount; 4298 if DoFixFolding(FldSrcLine, FLdNodeLine + CurLen, AtColumn, SubTree, SubTree.FindFirstFold) 4299 then begin 4300 if CurLen > FldLen then begin 4301 node.fData.MergedLineCount:= max(node.FullCount, 4302 doFoldTree.TreeForNestedNode(node.fData, 0).LastFoldedLine + 1); 4303 if CurLen <> node.MergedLineCount then 4304 node.fData.AdjustParentLeftCount(node.MergedLineCount - CurLen); 4305 end; 4306 end; 4307 end; 4308 4309 // the node was ok 4310 if node.StartLine >= doMinEnd then break; 4311 node := node.Next; 4312 end; 4313 {$IFDEF SynFoldDebug}finally DebugLnExit(['<<FOLD-- DoFixFolding: DONE=', Result]); end{$ENDIF} 4314 end; 4315 4316var 4317 node, tmpnode: TSynTextFoldAVLNode; 4318begin 4319 {$IFDEF SynFoldDebug}try DebugLnEnter(['>>FOLD-- FixFolding: Start=', AStart, ' AMinEnd=',AMinEnd]);{$ENDIF} 4320 Result := false; 4321 if fLockCount > 0 then begin 4322 Include(FFlags, fvfNeedCaretCheck); 4323 if fNeedFixFrom < 0 then fNeedFixFrom := AStart 4324 else fNeedFixFrom := Min(fNeedFixFrom, AStart); 4325 fNeedFixMinEnd := Max(fNeedFixMinEnd, AMinEnd); 4326 exit; 4327 end; 4328 4329 node := aFoldTree.FindFoldForLine(aStart, true); 4330 if not node.IsInFold then node:= aFoldTree.FindLastFold; 4331 if not node.IsInFold then begin 4332 CalculateMaps; 4333 exit; 4334 end; 4335 If aMinEnd < node.StartLine then aMinEnd := node.StartLine; // XXX SourceLine 4336 4337 // FullCount is allowed to be -1 4338 while node.IsInFold and (node.StartLine + node.FullCount + 1 >= aStart) do begin 4339 tmpnode := node.Prev; 4340 if tmpnode.IsInFold 4341 then node := tmpnode 4342 else break; // first node 4343 end; 4344 4345 FirstchangedLine := -1; 4346 FldInfos := nil; 4347 MaxCol := -1; 4348 SrcLineForFldInfos := -1; 4349 Result := DoFixFolding(-1, AMinEnd, 0, aFoldTree, node); 4350 CalculateMaps; 4351 if (FirstchangedLine >= 0) then 4352 DoFoldChanged(FirstchangedLine); 4353 {$IFDEF SynFoldDebug}finally DebugLnExit(['<<FOLD-- FixFolding: DONE=', Result]); end{$ENDIF} 4354end; 4355 4356procedure TSynEditFoldedView.DoCaretChanged(Sender : TObject); 4357var 4358 i: Integer; 4359begin 4360 if fLockCount > 0 then begin 4361 Include(FFlags, fvfNeedCaretCheck); 4362 exit; 4363 end; 4364 Exclude(FFlags, fvfNeedCaretCheck); 4365 i := TSynEditCaret(Sender).LinePos-1; 4366 {$IFDEF SynFoldDebug}if FoldedAtTextIndex[i] then debugln(['FOLD-- DoCaretChanged about to unfold at Index=', i]);{$ENDIF} 4367 if FoldedAtTextIndex[i] then 4368 UnFoldAtTextIndexCollapsed(i); 4369end; 4370 4371procedure TSynEditFoldedView.LineCountChanged(Sender: TSynEditStrings; AIndex, ACount : Integer); 4372begin 4373 {$IFDEF SynFoldDebug}try DebugLnEnter(['>> FOLD-- LineCountChanged AIndex=', AIndex, ' Acount=',ACount]);{$ENDIF} 4374 // no need for fix folding => synedit will be called, and scanlines will call fixfolding 4375 {TODO: a "need fix folding" flag => to ensure it will be called if synedit doesnt 4376 SynEdit.ScanRanges, calls Fixfolding as workaroound => review 4377 } 4378 if (fLockCount > 0) and (AIndex < max(fNeedFixFrom, fNeedFixMinEnd)) then begin 4379 // adapt the fixfold range. Could be done smarter, but it doesn't matter if the range gets bigger than needed. 4380 if (ACount < 0) and (AIndex < fNeedFixFrom) then inc(fNeedFixFrom, ACount); 4381 if (ACount > 0) and (AIndex < fNeedFixMinEnd) then inc(fNeedFixMinEnd, ACount); 4382 end; 4383 if NextLines.IsInEditAction then exit; 4384 if ACount<0 4385 then LinesDeletedAtTextIndex(AIndex+1, -ACount, 1, true) 4386 else LinesInsertedAtTextIndex(AIndex+1, ACount, 1, true); 4387 {$IFDEF SynFoldDebug}finally DebugLnExit(['<< FOLD-- LineCountChanged']); end;{$ENDIF} 4388end; 4389 4390procedure TSynEditFoldedView.LinesCleared(Sender: TObject); 4391begin 4392 UnfoldAll; 4393end; 4394 4395procedure TSynEditFoldedView.LineEdited(Sender: TSynEditStrings; aLinePos, aBytePos, aCount, 4396 aLineBrkCnt: Integer; aText: String); 4397begin 4398 {$IFDEF SynFoldDebug}try DebugLnEnter(['>> FOLD-- LineEditied aLinePos=', aLinePos, ' aBytePos=', aBytePos, ' Acount=',ACount, ' aLineBrkCnt=',aLineBrkCnt]);{$ENDIF} 4399 if aLineBrkCnt<0 4400 then LinesDeletedAtTextIndex(aLinePos, -aLineBrkCnt, ABytePos, true) 4401 else if aLineBrkCnt > 0 4402 then LinesInsertedAtTextIndex(aLinePos, aLineBrkCnt, ABytePos, true) 4403 else begin 4404 fFoldTree.AdjustColumn(aLinePos, aBytePos, aCount); 4405 //if not(SkipFixFolding) then FixFoldingAtTextIndex(AStartIndex, AStartIndex+ALineCount+1) 4406 //else 4407 //if aLinePos < top + ALineCount then CalculateMaps; 4408 end; 4409 {$IFDEF SynFoldDebug}finally DebugLnExit(['<< FOLD-- LineEditied']); end;{$ENDIF} 4410end; 4411 4412procedure TSynEditFoldedView.FixFoldingAtTextIndex(AStartIndex: Integer; AMinEndLine : Integer); 4413begin 4414 FixFolding(AStartIndex + 1, AMinEndLine, fFoldTree); 4415end; 4416 4417function TSynEditFoldedView.OpenFoldCount(aStartIndex: Integer; AType: Integer = 0): Integer; 4418// Todo: move entirely to FoldProvider 4419var 4420 hl: TSynCustomFoldHighlighter; 4421begin 4422 hl := TSynCustomFoldHighlighter(HighLighter); 4423 if not assigned(hl) then 4424 exit(-1); 4425 Result := hl.FoldBlockEndLevel(AStartIndex-1, AType) + FoldProvider.FoldOpenCount(AStartIndex); 4426end; 4427 4428function TSynEditFoldedView.OpenFoldInfo(aStartIndex, ColIndex: Integer; AType: Integer = 0): TFoldViewNodeInfo; 4429var 4430 hl: TSynCustomFoldHighlighter; 4431 TypeCnt, Lvl: Integer; 4432 EndLvl, CurLvl: Array of integer; 4433 i, c, t, n, o: Integer; 4434 nd: TSynFoldNodeInfo; 4435 FN: TSynTextFoldAVLNode; 4436 4437 procedure GetEndLvl(l: Integer); 4438 var i: integer; 4439 begin 4440 if AType = 0 then begin; 4441 for i := 1 to TypeCnt do begin 4442 EndLvl[i] := hl.FoldBlockEndLevel(l-1, i); 4443 EndLvl[i] := EndLvl[i] + FoldProvider.FoldOpenCount(l, i); 4444 CurLvl[i] := EndLvl[i]; 4445 end; 4446 end 4447 else begin 4448 EndLvl[0] := hl.FoldBlockEndLevel(l-1, AType); 4449 EndLvl[0] := EndLvl[0] + FoldProvider.FoldOpenCount(l, AType); 4450 CurLvl[0] := EndLvl[0]; 4451 end; 4452 end; 4453 4454begin 4455 hl := TSynCustomFoldHighlighter(HighLighter); 4456 if not assigned(hl) then 4457 exit; // ToDo: Initialize Result 4458 4459 nd.LogXStart := 0; 4460 nd.LogXEnd := 0; 4461 nd.FoldAction := []; 4462 nd.FoldType := Nil; 4463 nd.FoldGroup := 0; 4464 n := 0; 4465 if AType <> 0 then 4466 TypeCnt := 1 4467 else 4468 TypeCnt := hl.FoldTypeCount; 4469 Lvl := hl.FoldBlockEndLevel(AStartIndex-1, AType); 4470 if ColIndex >= Lvl then begin 4471 n := ColIndex - Lvl; 4472 if AType = 0 then begin 4473 o := hl.FoldNodeInfo[aStartIndex].CountEx([sfaOpen, sfaFold]); 4474 nd := hl.FoldNodeInfo[aStartIndex].NodeInfoEx(n, [sfaOpen, sfaFold]); 4475 end else begin 4476 // no sfaFold 4477 o := hl.FoldNodeInfo[aStartIndex].CountEx([sfaOpenFold],AType); 4478 nd := hl.FoldNodeInfo[aStartIndex].NodeInfoEx(n, [sfaOpenFold], AType); 4479 end; 4480 end 4481 else begin 4482 SetLength(EndLvl, TypeCnt+1); 4483 SetLength(CurLvl, TypeCnt+1); 4484 GetEndLvl(aStartIndex); 4485 aStartIndex := aStartIndex; 4486 while (ColIndex < Lvl) and (aStartIndex > 0) do begin 4487 dec(aStartIndex); 4488 o := hl.FoldBlockOpeningCount(AStartIndex, AType); 4489 if (o > 0) or (hl.FoldBlockClosingCount(aStartIndex, AType) > 0) then begin 4490 n := o; 4491 c := hl.FoldNodeInfo[aStartIndex].CountEx([], AType) - 1; 4492 for i := c downto 0 do begin 4493 nd := hl.FoldNodeInfo[aStartIndex].NodeInfoEx(i, [], AType); 4494 if (AType = 0) and not(sfaFold in nd.FoldAction) then 4495 continue; 4496 if AType = 0 then 4497 t := nd.FoldGroup 4498 else 4499 t := 0; 4500 if sfaOpenFold in nd.FoldAction then begin 4501 dec(n); 4502 dec(CurLvl[t]); 4503 if CurLvl[t] < EndLvl[t] then begin 4504 dec(EndLvl[t]); 4505 dec(Lvl); 4506 if ColIndex = Lvl then begin 4507 break; 4508 end; 4509 end; 4510 end else 4511 if sfaCloseFold in nd.FoldAction then begin 4512 inc(CurLvl[t]); 4513 end; 4514 end; 4515 end 4516 else 4517 if hl.FoldBlockEndLevel(AStartIndex-1, AType) = 0 then break; 4518 end; 4519 end; 4520 Result.HNode := nd; 4521 Result.OpenCount := o; 4522 Result.Text := NextLines[aStartIndex]; 4523 if not(sfaInvalid in nd.FoldAction) then 4524 Result.Keyword := copy(Result.Text, 1 + nd.LogXStart, nd.LogXEnd-nd.LogXStart); 4525 Result.LineNum := aStartIndex + 1; 4526 Result.ColIndex := n; 4527 FN := FoldNodeAtTextIndex(aStartIndex, n); 4528 Result.IsFold := FN.IsInFold; 4529 Result.IsHide := fn.IsHide; 4530end; 4531 4532function TSynEditFoldedView.ExpandedLineForBlockAtLine(ALine : Integer; 4533 HalfExpanded: Boolean = True) : Integer; 4534var 4535 i, l : Integer; 4536 node: TSynTextFoldAVLNode; 4537 hl: TSynCustomFoldHighlighter; 4538begin 4539 Result := -1; 4540 hl := TSynCustomFoldHighlighter(HighLighter); 4541 if not assigned(hl) then 4542 exit; 4543 4544 i := ALine; 4545 l := hl.FoldBlockOpeningCount(i - 1); 4546 if l > 0 then begin 4547 node := fFoldTree.FindFoldForLine(ALine, true); 4548 if node.IsInFold and (node.StartLine = ALine +1) then begin 4549 dec(l); 4550 if HalfExpanded then while (l >= 0) do begin 4551 if not IsFoldedAtTextIndex(ALine-1, l) then exit(ALine); 4552 dec(l); 4553 end; 4554 dec(i); 4555 end 4556 else 4557 exit(ALine); 4558 end 4559 else if hl.FoldBlockClosingCount(i - 1) > 0 then 4560 dec(i); 4561 if (i < 0) or (hl.FoldBlockEndLevel(i-1) = 0) then 4562 exit; 4563 4564 l := 0; 4565 while (i > 0) and (l >= 0) do begin // (FoldMinLevel[i] >= l) do 4566 dec(i); 4567 l := l - hl.FoldBlockOpeningCount(i); 4568 if l >= 0 then 4569 l := l + hl.FoldBlockClosingCount(i); 4570 end; 4571 if (hl.FoldBlockEndLevel(i) > 0) then // TODO, check for collapsed at index = 0 4572 Result := i + 1; 4573end; 4574 4575function TSynEditFoldedView.GetPhysicalCharWidths(Index: Integer): TPhysicalCharWidths; 4576begin 4577 Result := NextLines.GetPhysicalCharWidths(InternViewToTextIndex(Index)); 4578end; 4579 4580function TSynEditFoldedView.CollapsedLineForFoldAtLine(ALine : Integer) : Integer; 4581// for hides => line before the hide 4582var 4583 node, tmpnode: TSynTextFoldAVLNode; 4584begin 4585 Result := -1; 4586 node := fFoldTree.FindFoldForLine(ALine, false); 4587 if node.IsInFold then begin 4588 tmpnode := node.Prev; 4589 while tmpnode.IsInFold and 4590 (tmpnode.StartLine + tmpnode.MergedLineCount = node.StartLine) 4591 do begin 4592 node := tmpnode; 4593 tmpnode := node.Prev; 4594 end; 4595 Result := node.StartLine-1; 4596 // Can be 0, if lines are hiden at begin of file 4597 end; 4598end; 4599 4600function dbgs(AClassification: TFoldNodeClassification): String; 4601begin 4602 WriteStr(Result{%H-}, AClassification); 4603end; 4604 4605{$IFDEF SynDebug} 4606procedure TSynEditFoldedView.debug; 4607begin 4608 fFoldTree.debug; 4609end; 4610{$ENDIF} 4611 4612initialization 4613 InitNumEncodeValues; 4614 //SYN_FOLD_DEBUG := DebugLogger.RegisterLogGroup('SynFoldDebug' {$IFDEF SynFoldDebug} , True {$ENDIF} ); 4615 4616end. 4617 4618