/components/synedit/syneditfoldedview.pp

http://github.com/graemeg/lazarus · Puppet · 4499 lines · 4108 code · 391 blank · 0 comment · 439 complexity · da30c3dc6ba5171c1b39893b06da585f MD5 · raw file

Large files are truncated click here to view the full file

  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>