PageRenderTime 72ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/components/synedit/syneditfoldedview.pp

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