/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
- {-------------------------------------------------------------------------------
- The contents of this file are subject to the Mozilla Public License
- Version 1.1 (the "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
- the specific language governing rights and limitations under the License.
- Alternatively, the contents of this file may be used under the terms of the
- GNU General Public License Version 2 or later (the "GPL"), in which case
- the provisions of the GPL are applicable instead of those above.
- If you wish to allow use of your version of this file only under the terms
- of the GPL and not to allow others to use your version of this file
- under the MPL, indicate your decision by deleting the provisions above and
- replace them with the notice and other provisions required by the GPL.
- If you do not delete the provisions above, a recipient may use your version
- of this file under either the MPL or the GPL.
- -------------------------------------------------------------------------------}
- (* some parts (AdjustBalance...) of this unit are based on the AVLTree unit *)
- (* TODO: Implement node.eof / node.bof *)
- unit SynEditFoldedView;
- {$mode objfpc}{$H+}
- {$coperators on}
- {$IFDEF CPUPOWERPC} {$INLINE OFF} {$ENDIF} (* Workaround for bug 12576 (fpc) see bugs.freepascal.org/view.php?id=12576 *)
- {$IFOPT C+}
- {$DEFINE SynAssertFold}
- {$ENDIF}
- {$IFDEF SynAssert}
- {$DEFINE SynAssertFold}
- {$ENDIF}
- {$IFDEF SynFoldDebug}
- {$DEFINE SynDebug}
- {$DEFINE SynFoldSaveDebug}
- {$ENDIF}
- {$IFDEF SynFoldSaveDebug}
- {$DEFINE SynDebug}
- {$ENDIF}
- interface
- uses
- LCLProc, LazLoggerBase, LazClasses, Graphics,
- Classes, SysUtils, LazSynEditText, SynEditTypes, SynEditMiscClasses,
- SynEditMiscProcs, SynEditPointClasses,
- SynEditHighlighter, SynEditHighlighterFoldBase;
- type
- TFoldNodeClassification = (fncInvalid, fncHighlighter, fncHighlighterEx, fncBlockSelection);
- TFoldNodeClassifications = set of TFoldNodeClassification;
- { TSynTextFoldAVLNodeData }
- TSynTextFoldAVLNodeData = class(TSynSizedDifferentialAVLNode)
- protected
- function Left: TSynTextFoldAVLNodeData;
- function Parent: TSynTextFoldAVLNodeData;
- function Right: TSynTextFoldAVLNodeData;
- procedure FreeAllChildrenAndNested;
- public (* Position / Size *)
- (* FullCount: Amount of lines in source for this fold only
- (excluding overlaps) *)
- FullCount : Integer;
- (* LineOffset: Line-Number Offset to parent node
- All line numbers are stored as offsets,
- for faster updates if lines are inserted/deleted *)
- property LineOffset: Integer read FPositionOffset write FPositionOffset;
- (* LeftCount: Lines folded in left tree.
- Used to calculate how many lines are folded up to a specified line *)
- property LeftCount: Integer read FLeftSizeSum write FLeftSizeSum;
- (* MergedLineCount: Amount of lines folded away by this fold,
- FullCount + Lines covered by overlaps *)
- property MergedLineCount: Integer read FSize write FSize;
- public
- (* Sub-Tree *)
- Nested : TSynTextFoldAVLNodeData; (* Nested folds (folds within this fold) do not need to be part of the searchable tree
- They will be restored, if the outer fold (this fold) is unfolded
- Nested points to a standalone tree, the root node in the nested tree, does *not* point back to this node *)
- (* Source Info *)
- FoldIndex: Integer; (* Index of fold in line; if a line has more than one fold starting *)
- FoldColumn, FoldColumnLen: Integer; (* The column (1-based) and len of the keywordm which starts this fold *)
- FoldTypeCompatible: Pointer; (* help identifying in FixFolding *)
- Classification: TFoldNodeClassification;
- VisibleLines: Integer; (* Visible Source lines, containing the "fold keyword"
- 0: Hiden block (the fold-keyword is inside the fold)
- 1: Normal fold (There is *1* visible line with the fold-keyword)
- *)
- function RecursiveFoldCount : Integer; (* Amount of lines covered by this and all child nodes *)
- function Precessor : TSynTextFoldAVLNodeData; reintroduce;
- function Successor : TSynTextFoldAVLNodeData; reintroduce;
- function Precessor(var aStartPosition, aSizesBeforeSum : Integer) : TSynTextFoldAVLNodeData; reintroduce;
- function Successor(var aStartPosition, aSizesBeforeSum : Integer) : TSynTextFoldAVLNodeData; reintroduce;
- end;
- { TSynTextFoldAVLNode }
- TSynTextFoldAVLNode = object
- private
- function GetClassification: TFoldNodeClassification;
- function GetFoldColumn: Integer;
- function GetFoldColumnLen: Integer;
- function GetFoldIndex: Integer;
- function GetMergedLineCount : Integer;
- function GetFullCount : Integer;
- function GetSourceLine: integer;
- function GetSourceLineOffset: integer;
- procedure SetFoldColumn(const AValue: Integer);
- protected
- fData : TSynTextFoldAVLNodeData; // nil if unfolded
- fStartLine : Integer; // start of folded
- fFoldedBefore : Integer;
- public
- procedure Init(aData : TSynTextFoldAVLNodeData; aStartLine, aFoldedBefore: Integer);
- function IsInFold : Boolean;
- function Next : TSynTextFoldAVLNode;
- function Prev : TSynTextFoldAVLNode;
- property MergedLineCount: Integer read GetMergedLineCount; // Zero, if Not in a fold
- property FullCount: Integer read GetFullCount; // Zero, if Not in a fold
- property StartLine: Integer read fStartLine; // 1st Line of Current Fold
- property FoldedBefore: Integer read fFoldedBefore; // Count of Lines folded before Startline
- function IsHide: Boolean;
- property FoldIndex: Integer read GetFoldIndex;
- property FoldColumn: Integer read GetFoldColumn write SetFoldColumn;
- property FoldColumnLen: Integer read GetFoldColumnLen;
- property SourceLine: integer read GetSourceLine; // The SourceLine with the fold-keyword
- property SourceLineOffset: integer read GetSourceLineOffset; // The SourceLine with the fold-keyword
- property Classification: TFoldNodeClassification read GetClassification;
- end;
- { TSynTextFoldAVLNodeNestedIterator:
- Iterates included nested nodes
- FoldedBefore is not valid in nested nodes
- }
- TSynTextFoldAVLNodeNestedIterator = class
- private
- FCurrentNode: TSynTextFoldAVLNode;
- FOuterNodes: Array of TSynTextFoldAVLNode;
- public
- constructor Create(ANode: TSynTextFoldAVLNode);
- destructor Destroy; override;
- function Next: TSynTextFoldAVLNode;
- function Prev: TSynTextFoldAVLNode;
- function EOF: Boolean;
- function BOF: Boolean;
- function IsInFold: Boolean;
- property Node: TSynTextFoldAVLNode read FCurrentNode;
- end;
- { TSynTextFoldAVLTree
- - Nodes in the tree cover the folded lines only.
- The (visible) cfCollapsed line at the start of a fold, is *not* part of a node.
- - In the public methods "ALine" indicates the first invisible/hidden line
- - TSynEditFoldedView uses this with 1-based lines (ToDo: make 0-based)
- }
- TSynTextFoldAVLTree = class(TSynSizedDifferentialAVLTree)
- protected
- fNestParent: TSynTextFoldAVLNodeData;
- fNestedNodesTree: TSynTextFoldAVLTree; // FlyWeight Tree used for any nested subtree.
- function NewNode : TSynTextFoldAVLNodeData; inline;
- Function RemoveFoldForNodeAtLine(ANode: TSynTextFoldAVLNode;
- ALine : Integer) : Integer; overload; // Line is for Nested Nodes
- // SetRoot, does not obbey fRootOffset => use SetRoot(node, -fRootOffset)
- procedure SetRoot(ANode : TSynSizedDifferentialAVLNode); overload; override;
- procedure SetRoot(ANode : TSynSizedDifferentialAVLNode; anAdjustChildLineOffset : Integer); overload; override;
- Function InsertNode(ANode : TSynTextFoldAVLNodeData) : Integer; reintroduce; // returns FoldedBefore // ANode may not have children
- function TreeForNestedNode(ANode: TSynTextFoldAVLNodeData; aOffset : Integer) : TSynTextFoldAVLTree;
- public
- constructor Create;
- destructor Destroy; override;
- procedure Clear; override;
- (* Find Fold by Line in Real Text *)
- Function FindFoldForLine(ALine : Integer; FindNextNode : Boolean = False) : TSynTextFoldAVLNode;
- (* Find Fold by Line in Folded Text // always returns unfolded, unless next=true *)
- Function FindFoldForFoldedLine(ALine : Integer; FindNextNode: Boolean = False) : TSynTextFoldAVLNode;
- Function InsertNewFold(ALine, AFoldIndex, AColumn, AColumnLen, ACount, AVisibleLines: Integer;
- AClassification: TFoldNodeClassification;
- AFoldTypeCompatible: Pointer
- ) : TSynTextFoldAVLNode;
- (* This will unfold the block which either contains tALine, or has Aline as its cgColapsed line
- If IgnoreFirst, the cfCollapsed will *not* unfold => Hint: IgnoreFirst = Make folded visible
- Returns the pos(1-based) of the cfCollapsed Line that was expanded; or ALine, if nothing was done
- *)
- Function RemoveFoldForLine(ALine : Integer; OnlyCol: Integer = -1) : Integer; overload;
- Procedure AdjustForLinesInserted(AStartLine, ALineCount, ABytePos: Integer);
- Procedure AdjustForLinesDeleted(AStartLine, ALineCount, ABytePos: Integer);
- procedure AdjustColumn(ALine, ABytePos, ACount: Integer; InLineBreak: boolean = False);
- Function FindLastFold : TSynTextFoldAVLNode;
- Function FindFirstFold : TSynTextFoldAVLNode;
- Function LastFoldedLine : integer; // The actual line; LastNode.StartLine + LastNode.LineCount - 1
- {$IFDEF SynDebug}
- procedure Debug; reintroduce;
- {$ENDIF}
- end;
- { TSynFoldNodeInfoHelper }
- TSynFoldNodeInfoHelper = class
- FCurInfo: TSynFoldNodeInfo;
- FActions: TSynFoldActions;
- FHighlighter: TSynCustomFoldHighlighter;
- protected
- procedure Invalidate;
- public
- constructor Create(AHighlighter: TSynCustomFoldHighlighter);
- function FirstOpen: TSynFoldNodeInfo;
- function Next: TSynFoldNodeInfo;
- function Prev: TSynFoldNodeInfo;
- function FindClose: TSynFoldNodeInfo;
- function GotoOpenPos(aLineIdx, aNodeIdx: integer): TSynFoldNodeInfo;
- function GotoOpenAtChar(aLineIdx, aXPos: integer): TSynFoldNodeInfo;
- function GotoNodeOpenPos(ANode : TSynTextFoldAVLNode): TSynFoldNodeInfo;
- function GotoNodeClosePos(ANode : TSynTextFoldAVLNode): TSynFoldNodeInfo;
- function IsAtNodeOpenPos(ANode : TSynTextFoldAVLNode): Boolean;
- function IsValid: Boolean;
- function Equals(AnInfo: TSynFoldNodeInfo): Boolean;
- function Equals(AHelper: TSynFoldNodeInfoHelper): Boolean;
- property Info: TSynFoldNodeInfo read FCurInfo write FCurInfo;
- property Actions: TSynFoldActions read FActions write FActions;
- end;
- TFoldChangedEvent = procedure(aLine: Integer) of object;
- TInvalidateLineProc = procedure(FirstLine, LastLine: integer) of object;
- TFoldViewNodeInfo = record
- HNode: TSynFoldNodeInfo; // Highlighter Node
- FNode: TSynTextFoldAVLNode; // AvlFoldNode
- Text, Keyword: String;
- LineNum, ColIndex: Integer;
- OpenCount: Integer; // Highlighter-Nodes opening on this line (limited to the FoldGroup requested)
- end;
- TSynEditFoldLineCapability = (
- // Capabilities of Line
- cfFoldStart, cfHideStart,
- cfFoldBody,
- cfFoldEnd,
- // State indicators
- cfCollapsedFold,
- cfCollapsedHide, // lines hidden, after this line
- // Special flags
- cfSingleLineHide,
- cfNone
- );
- TSynEditFoldLineCapabilities = set of TSynEditFoldLineCapability;
- TSynEditFoldType = (scftOpen, scftFold, scftHide, scftAll, scftInvalid);
- TSynEditFoldLineMapInfo = record
- Capability: TSynEditFoldLineCapabilities;
- Classifications :TFoldNodeClassifications;
- end;
- {$IFDEF SynFoldSaveDebug}
- const
- SynEditFoldTypeNames: Array [TSynEditFoldType] of string =
- ('scftOpen', 'scftFold', 'scftHide', 'scftAll', 'scftInvalid');
- type
- {$ENDIF}
- { TSynEditFoldProvider }
- TSynEditFoldProviderNodeInfo = record
- LineCount: Integer;
- Column, ColumnLen: Integer;
- DefaultCollapsed: Boolean;
- FoldTypeCompatible: Pointer; // eg begin, var, procedure
- FoldGroup: Integer; // eg.: pas, region, ifdef
- Classification: TFoldNodeClassification;
- end;
- TSynEditFoldProviderNodeInfoList = array of TSynEditFoldProviderNodeInfo;
- TSynEditFoldProvider = class;
- TSynEditFoldProvider = class
- private
- FHighlighter: TSynCustomFoldHighlighter;
- FLines : TSynEditStrings;
- FSelection: TSynEditSelection;
- FFoldTree : TSynTextFoldAVLTree;
- FNestedFoldsList: TLazSynEditNestedFoldsList;
- function GetFoldsAvailable: Boolean;
- function GetHighLighterWithLines: TSynCustomFoldHighlighter;
- function GetLineCapabilities(ALineIdx: Integer): TSynEditFoldLineCapabilities;
- function GetLineClassification(ALineIdx: Integer): TFoldNodeClassifications;
- function GetNestedFoldsList: TLazSynEditNestedFoldsList;
- procedure SetHighLighter(const AValue: TSynCustomFoldHighlighter);
- procedure SetLines(AValue: TSynEditStrings);
- protected
- property HighLighterWithLines: TSynCustomFoldHighlighter read GetHighLighterWithLines;
- public
- constructor Create(aTextView : TSynEditStrings; AFoldTree : TSynTextFoldAVLTree);
- destructor Destroy; override;
- // Info about Folds opening on ALineIdx
- function FoldOpenCount(ALineIdx: Integer; AType: Integer = 0): Integer;
- function FoldOpenInfo(ALineIdx, AFoldIdx: Integer; AType: Integer = 0): TSynFoldNodeInfo;
- //property FoldOpenInfo[ALineIdx, AColumnIdx: Integer]: Integer read GetFoldOpenInfo;
- function FoldLineLength(ALine, AFoldIndex: Integer): integer;
- function InfoForFoldAtTextIndex(ALine, AFoldIndex : Integer;
- HideLen: Boolean = False;
- NeedLen: Boolean = True): TSynEditFoldProviderNodeInfo;
- function InfoListForFoldsAtTextIndex(ALine: Integer; NeedLen: Boolean = False): TSynEditFoldProviderNodeInfoList;
- property LineCapabilities[ALineIdx: Integer]: TSynEditFoldLineCapabilities
- read GetLineCapabilities;
- property LineClassification[ALineIdx: Integer]: TFoldNodeClassifications
- read GetLineClassification;
- property Lines: TSynEditStrings read FLines write SetLines;
- property HighLighter: TSynCustomFoldHighlighter read FHighlighter write SetHighLighter;
- property FoldsAvailable: Boolean read GetFoldsAvailable;
- property NestedFoldsList: TLazSynEditNestedFoldsList read GetNestedFoldsList;
- end;
- { TFoldChangedHandlerList }
- TFoldChangedHandlerList = class(TMethodList)
- public
- procedure CallFoldChangedEvents(AnIndex: Integer);
- end;
- TSynEditFoldedView = class;
- { TLazSynDisplayFold }
- TLazSynDisplayFold = class(TLazSynDisplayViewEx)
- private
- FFoldView: TSynEditFoldedView;
- FLineState: integer;
- FTokenAttr: TSynHighlighterAttributesModifier;
- FMarkupLine: TSynSelectedColorMergeResult;
- FLineFlags, FLineFlags2: TSynEditFoldLineCapabilities;
- public
- constructor Create(AFoldView: TSynEditFoldedView);
- destructor Destroy; override;
- procedure SetHighlighterTokensLine(ALine: TLineIdx; out ARealLine: TLineIdx); override;
- function GetNextHighlighterToken(out ATokenInfo: TLazSynDisplayTokenInfo): Boolean; override;
- function GetLinesCount: Integer; override;
- function TextToViewIndex(AIndex: TLineIdx): TLineRange; override;
- function ViewToTextIndex(AIndex: TLineIdx): TLineIdx; override;
- end;
- { TSynTextFoldedView
- *Line = Line (0-based) on Screen (except TopLine which should be TopViewPos)
- *ViewPos = Line (1-based) in the array of viewable/visible lines
- *TextIndex = Line (0-based) in the complete text(folded and unfolded)
- }
- TSynEditFoldedViewFlag = (fvfNeedCaretCheck, fvfNeedCalcMaps);
- TSynEditFoldedViewFlags = set of TSynEditFoldedViewFlag;
- { TSynEditFoldedView }
- TSynEditFoldedView = class
- private
- fCaret: TSynEditCaret;
- FBlockSelection: TSynEditSelection;
- FFoldProvider: TSynEditFoldProvider;
- fLines : TSynEditStrings;
- fFoldTree : TSynTextFoldAVLTree; // Folds are stored 1-based (the 1st line is 1)
- FMarkupInfoFoldedCode: TSynSelectedColor;
- FMarkupInfoFoldedCodeLine: TSynSelectedColor;
- FMarkupInfoHiddenCodeLine: TSynSelectedColor;
- FOnLineInvalidate: TInvalidateLineProc;
- fTopLine : Integer;
- fLinesInWindow : Integer; // there may be an additional part visible line
- fTextIndexList : Array of integer; (* Map each Screen line into a line in textbuffer *)
- fFoldTypeList : Array of TSynEditFoldLineMapInfo;
- fOnFoldChanged : TFoldChangedEvent;
- fLockCount : Integer;
- fNeedFixFrom, fNeedFixMinEnd : Integer;
- FFlags: TSynEditFoldedViewFlags;
- FInTopLineChanged: Boolean;
- FDisplayView: TLazSynDisplayFold;
- FFoldChangedHandlerList: TFoldChangedHandlerList;
- function GetCount : integer;
- function GetDisplayView: TLazSynDisplayView;
- function GetFoldClasifications(index : Integer): TFoldNodeClassifications;
- function GetHighLighter: TSynCustomHighlighter;
- function GetLines(index : Integer) : String;
- function GetDisplayNumber(index : Integer) : Integer;
- function GetTextIndex(index : Integer) : Integer;
- function GetFoldType(index : Integer) : TSynEditFoldLineCapabilities;
- function IsFolded(index : integer) : Boolean; // TextIndex
- procedure SetBlockSelection(const AValue: TSynEditSelection);
- procedure SetHighLighter(AValue: TSynCustomHighlighter);
- procedure SetTopLine(const ALine : integer);
- function GetTopTextIndex : integer;
- procedure SetTopTextIndex(const AIndex : integer);
- procedure SetLinesInWindow(const AValue : integer);
- procedure DoFoldChanged(AnIndex: Integer);
- protected
- procedure DoBlockSelChanged(Sender: TObject);
- Procedure CalculateMaps;
- function FoldNodeAtTextIndex(AStartIndex, ColIndex: Integer): TSynTextFoldAVLNode; (* Returns xth Fold at nth TextIndex (all lines in buffer) / 1-based *)
- function FixFolding(AStart : Integer; AMinEnd : Integer; aFoldTree : TSynTextFoldAVLTree) : Boolean;
- procedure DoCaretChanged(Sender : TObject);
- Procedure LineCountChanged(Sender: TSynEditStrings; AIndex, ACount : Integer);
- Procedure LinesCleared(Sender: TObject);
- Procedure LineEdited(Sender: TSynEditStrings; aLinePos, aBytePos, aCount,
- aLineBrkCnt: Integer; aText: String);
- Procedure LinesInsertedAtTextIndex(AStartIndex, ALineCount, ABytePos: Integer;
- SkipFixFolding : Boolean = False);
- //Procedure LinesInsertedAtViewPos(AStartPos, ALineCount : Integer;
- // SkipFixFolding : Boolean = False);
- Procedure LinesDeletedAtTextIndex(AStartIndex, ALineCount, ABytePos: Integer;
- SkipFixFolding : Boolean = False);
- //Procedure LinesDeletedAtViewPos(AStartPos, ALineCount : Integer;
- // SkipFixFolding : Boolean = False);
- property FoldTree: TSynTextFoldAVLTree read fFoldTree;
- public
- constructor Create(aTextView : TSynEditStrings; ACaret: TSynEditCaret);
- destructor Destroy; override;
-
- // Converting between Folded and Unfolded Lines/Indexes
- function TextIndexToViewPos(aTextIndex : Integer) : Integer; (* Convert TextIndex (0-based) to ViewPos (1-based) *)
- function TextIndexToScreenLine(aTextIndex : Integer) : Integer; (* Convert TextIndex (0-based) to Screen (0-based) *)
- function ViewPosToTextIndex(aViewPos : Integer) : Integer; (* Convert ViewPos (1-based) to TextIndex (0-based) *)
- function ScreenLineToTextIndex(aLine : Integer) : Integer; (* Convert Screen (0-based) to TextIndex (0-based) *)
- function TextIndexAddLines(aTextIndex, LineOffset : Integer) : Integer; (* Add/Sub to/from TextIndex (0-based) skipping folded *)
- function TextPosAddLines(aTextpos, LineOffset : Integer) : Integer; (* Add/Sub to/from TextPos (1-based) skipping folded *)
- property BlockSelection: TSynEditSelection write SetBlockSelection;
- // Attributes for Visible-Lines-On-screen
- property Lines[index : Integer] : String (* Lines on screen / 0 = TopLine *)
- read GetLines; default;
- property DisplayNumber[index : Integer] : Integer (* LineNumber for display in Gutter / result is 1-based *)
- read GetDisplayNumber;
- property FoldType[index : Integer] : TSynEditFoldLineCapabilities (* FoldIcon / State *)
- read GetFoldType;
- property FoldClasifications[index : Integer] : TFoldNodeClassifications (* FoldIcon / State *)
- read GetFoldClasifications;
- property TextIndex[index : Integer] : Integer (* Position in SynTextBuffer / result is 0-based *)
- read GetTextIndex; // maybe writable
- // Define Visible Area
- property TopLine : integer (* refers to visible (unfolded) lines / 1-based *)
- read fTopLine write SetTopLine;
- property TopTextIndex : integer (* refers to TextIndex (folded + unfolded lines) / 1-based *)
- read GetTopTextIndex write SetTopTextIndex;
- property LinesInWindow : integer (* Fully Visible lines in Window; There may be one half visible line *)
- read fLinesInWindow write SetLinesInWindow;
- property Count : integer read GetCount; (* refers to visible (unfolded) lines *)
- property MarkupInfoFoldedCode: TSynSelectedColor read FMarkupInfoFoldedCode;
- property MarkupInfoFoldedCodeLine: TSynSelectedColor read FMarkupInfoFoldedCodeLine;
- property MarkupInfoHiddenCodeLine: TSynSelectedColor read FMarkupInfoHiddenCodeLine;
- public
- procedure Lock;
- procedure UnLock;
- {$IFDEF SynDebug}
- procedure debug;
- {$ENDIF}
- (* Arguments for (Un)FoldAt* (Line, ViewPos, TextIndex):
- - ColumnIndex (0-based)
- Can be negative, to access the highest(-1) available, 2nd highest(-2) ...
- If negative, count points downward
- - ColCount = 0 => all
- - Skip => Do not count nodes that are already in the desired state
- (or can not archive the desired state: e.g. can not hide)
- - AVisibleLines: 0 = Hide / 1 = Fold
- *)
- procedure FoldAtLine(AStartLine: Integer; ColIndex : Integer = -1; (* Folds at ScreenLine / 0-based *)
- ColCount : Integer = 1; Skip: Boolean = False;
- AVisibleLines: Integer = 1);
- procedure FoldAtViewPos(AStartPos: Integer; ColIndex : Integer = -1; (* Folds at nth visible/unfolded Line / 1-based *)
- ColCount : Integer = 1; Skip: Boolean = False;
- AVisibleLines: Integer = 1);
- procedure FoldAtTextIndex(AStartIndex: Integer; ColIndex : Integer = -1; (* Folds at nth TextIndex (all lines in buffer) / 1-based *)
- ColCount : Integer = 1; Skip: Boolean = False;
- AVisibleLines: Integer = 1);
- procedure UnFoldAtLine(AStartLine: Integer; ColIndex : Integer = -1; (* UnFolds at ScreenLine / 0-based *)
- ColCount : Integer = 0; Skip: Boolean = False;
- AVisibleLines: Integer = 1);
- procedure UnFoldAtViewPos(AStartPos: Integer; ColIndex : Integer = -1; (* UnFolds at nth visible/unfolded Line / 1-based *)
- ColCount : Integer = 0; Skip: Boolean = False;
- AVisibleLines: Integer = 1);
- procedure UnFoldAtTextIndex(AStartIndex: Integer; ColIndex : Integer = -1; (* UnFolds at nth TextIndex (all lines in buffer) / 1-based *)
- ColCount : Integer = 0; Skip: Boolean = False;
- AVisibleLines: Integer = 1);
- procedure UnFoldAtTextIndexCollapsed(AStartIndex: Integer); (* UnFolds only if Index is in the fold, ignores cfcollapsed line, if unfolded / 1-based *)
- function LogicalPosToNodeIndex(AStartIndex: Integer; LogX: Integer; (* Returns the index of the node, at the logical char pos *)
- Previous: Boolean = False): Integer;
- procedure CollapseDefaultFolds;
- // Load/Save folds to string
- // AStartIndex, AEndIndex: (0 based) First/last line (EndIndex = -1 = open end)
- // AStartCol, AEndCol: (1 based) Logical text pos in Line. (AEndCol = -1 = full line)
- function GetFoldDescription(AStartIndex, AStartCol, AEndIndex,
- AEndCol: Integer; AsText: Boolean = False;
- Extended: Boolean = False) :String;
- procedure ApplyFoldDescription(AStartIndex, AStartCol, AEndIndex,
- AEndCol: Integer; FoldDesc: PChar;
- FoldDescLen: Integer; IsText: Boolean = False);
- procedure UnfoldAll;
- procedure FoldAll(StartLevel : Integer = 0; IgnoreNested : Boolean = False);
- procedure FixFoldingAtTextIndex(AStartIndex: Integer; AMinEndLine: Integer = 0); // Real/All lines
- public
- function OpenFoldCount(aStartIndex: Integer; AType: Integer = 0): Integer;
- function OpenFoldInfo(aStartIndex, ColIndex: Integer; AType: Integer = 0): TFoldViewNodeInfo;
- public
- // Find the visible first line of the fold at ALine. Returns -1 if Aline is not folded
- function CollapsedLineForFoldAtLine(ALine : Integer) : Integer;
- function ExpandedLineForBlockAtLine(ALine : Integer; HalfExpanded: Boolean = True) : Integer;
- procedure AddFoldChangedHandler(AHandler: TFoldChangedEvent);
- procedure RemoveFoldChangedHandler(AHandler: TFoldChangedEvent);
- function GetPhysicalCharWidths(Index: Integer): TPhysicalCharWidths;
- function IsFoldedAtTextIndex(AStartIndex, ColIndex: Integer): Boolean; (* Checks xth Fold at nth TextIndex (all lines in buffer) / 1-based *)
- property FoldedAtTextIndex [index : integer] : Boolean read IsFolded;
- property OnFoldChanged: TFoldChangedEvent (* reports 1-based line *) {TODO: synedit expects 0 based }
- read fOnFoldChanged write fOnFoldChanged;
- property OnLineInvalidate: TInvalidateLineProc(* reports 1-based line *) {TODO: synedit expects 0 based }
- read FOnLineInvalidate write FOnLineInvalidate;
- property HighLighter: TSynCustomHighlighter read GetHighLighter
- write SetHighLighter;
- property FoldProvider: TSynEditFoldProvider read FFoldProvider;
- property DisplayView: TLazSynDisplayView read GetDisplayView;
- end;
- function dbgs(AClassification: TFoldNodeClassification): String; overload;
- implementation
- //var
- // SYN_FOLD_DEBUG: PLazLoggerLogGroup;
- type
- TFoldExportEntry = Record
- // Lines and Pos (o 1st line) are relative to Scan-Start
- Line, LogX, LogX2: Integer; // StartLine and Pos
- ELine, ELogX, ELogX2: Integer; // EndLine and pos
- FType: Integer; // e.g ord(cfbtBeginEnd)
- LinesFolded: Integer; // Lines Folded according to AVL-Node
- end;
- { TSynEditFoldExportStream }
- TSynEditFoldExportStream = class
- private
- FData: String;
- FLen, FPos: Integer;
- FMem: PChar;
- function GetLen: Integer;
- procedure SetLen(const AValue: Integer);
- function GetMem: PChar;
- procedure SetMem(const AValue: PChar);
- function GetText: String;
- procedure SetText(const AValue: String);
- protected
- function GrowData(AppendSize: Integer): PChar;
- function EncodeIntEx(Anum: Integer): String; // base 43, with leading continue bit
- function EncodeIntEx2(Anum: Integer): String; // for numbers expected below 467; specially 0..80
- function InternalReadNum(var APos: Integer): Integer;
- function InternalReadNumEx(var APos: Integer): Integer;
- public
- constructor Create;
- procedure Compress;
- procedure Decompress;
- procedure AddChecksum;
- function VerifyChecksum: Boolean;
- // see notes for Compression
- Procedure AppendMem(AMem: Pointer; ALen: Integer);
- Procedure AppendString(ATxt: String);
- Procedure AppendNum(ANum: Integer);
- Procedure AppendNumEx(ANum: Integer);
- Procedure Reset;
- Procedure Clear;
- function ReadMem(AMem: Pointer; ALen: Integer): Boolean;
- function PeakString(ALen: Integer): String;
- function FindChar(AChar: Char): Integer; // 0 based
- function ReadString(ALen: Integer): String;
- function ReadNum: Integer;
- function ReadNumEx: Integer;
- function EOF: Boolean;
- property Text: String read GetText write SetText;
- property Mem: PChar read GetMem write SetMem;
- property Len: Integer read GetLen write SetLen;
- property Pos: Integer read FPos;
- end;
- TSynEditFoldExportCoderEntry = record
- aX, aY, aLen: Integer;
- aFoldType: TSynEditFoldType;
- end;
- TSynEditFoldExportCoderStates =
- (sfecAtBegin, sfecAtPoint, sfecInRepeatCount, sfecInvalid, sfecAtEOF);
- {$IFDEF SynFoldSaveDebug}
- const
- SynEditFoldExportCoderStates: Array [TSynEditFoldExportCoderStates] of String =
- ('sfecAtBegin', 'sfecAtPoint', 'sfecInRepeatCount', 'sfecInvalid', 'sfecAtEOF');
- type
- {$ENDIF}
- { TSynEditFoldExportCoder }
- TSynEditFoldExportCoder = class
- private
- FExportStream: TSynEditFoldExportStream;
- FFoldType: Pointer;
- FReadY, FReadLastY, FReadX, FReadSumLen, FReadCount: Integer;
- FReadType: TSynEditFoldType;
- FReadDefaultType: TSynEditFoldType;
- FReadState: TSynEditFoldExportCoderStates;
- FWriteCache: Array of TSynEditFoldExportCoderEntry;
- FWriteCacheLen: Integer;
- FWriteCacheTypes: set of TSynEditFoldType;
- function GetReadIsValid: Boolean;
- public
- constructor Create(AFoldType: Pointer);
- constructor Create(AStream: TSynEditFoldExportStream);
- destructor Destroy; override;
- procedure AddNode(aX, aY, aLen: Integer; aFoldType: TSynEditFoldType);
- procedure Finish;
- function ReadNode(aX, aY: Integer; aLen: Integer): TSynEditFoldType;
- function EOF: Boolean;
- procedure Reset;
- property ReadIsValid: Boolean read GetReadIsValid;
- property FoldType: Pointer read FFoldType;
- property Stream: TSynEditFoldExportStream read FExportStream;
- end;
- const
- // use only xml encode-able ascii
- // do not use [ or ], they are reserved for compression
- // space can be used a special indicator
- NumEncode86Chars: string[86] = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-+;:,.@=*/\!?$%()''^{}~_#';
- NumEncodeAsOneMax = 80; // Maximum Value to encode as 1 char
- NumEncodeAsTwoMax = 81 + 4*86 + 43; // = 467; Maximum Value to encode as 2 char
- NumEncodeAsThreeMax = 81 + 4*86 + 43 * 43 - 1; // = 2273 Maximum Value to encode as 3 char
- SEQMaxNodeCount = 75; // New Full entry at least every 75 folds
- SEQMaxLineDistEach = 500; // New Full entry, if folds startlines are more than 500 appart
- SEQMaxLineDistTotal = 2500; // New Full entry at least every 2500; check position
- var
- NumEncode86Values: Array [Char] of integer;
- procedure InitNumEncodeValues;
- var
- i: integer;
- c : Char;
- begin
- for c := low(Char) to high(Char) do begin
- NumEncode86Values[c] := -1;
- end;
- for i := 1 to length(NumEncode86Chars) do
- NumEncode86Values[NumEncode86Chars[i]] := i - 1;
- end;
- { TFoldChangedHandlerList }
- procedure TFoldChangedHandlerList.CallFoldChangedEvents(AnIndex: Integer);
- var
- i: LongInt;
- begin
- i:=Count;
- while NextDownIndex(i) do
- TFoldChangedEvent(Items[i])(AnIndex);
- end;
- { TLazSynDisplayFold }
- constructor TLazSynDisplayFold.Create(AFoldView: TSynEditFoldedView);
- begin
- inherited Create;
- FFoldView := AFoldView;
- FTokenAttr := TSynHighlighterAttributesModifier.Create(nil);
- FMarkupLine := TSynSelectedColorMergeResult.Create(nil);
- end;
- destructor TLazSynDisplayFold.Destroy;
- begin
- FreeAndNil(FTokenAttr);
- FreeAndNil(FMarkupLine);
- inherited Destroy;
- end;
- procedure TLazSynDisplayFold.SetHighlighterTokensLine(ALine: TLineIdx; out ARealLine: TLineIdx);
- begin
- FLineState := 0;
- CurrentTokenLine := ALine;
- FLineFlags := FFoldView.FoldType[CurrentTokenLine + 1 - FFoldView.TopLine] * [cfCollapsedFold, cfCollapsedHide];
- FLineFlags2 := FLineFlags;
- if not FFoldView.MarkupInfoFoldedCodeLine.IsEnabled then
- Exclude(FLineFlags2, cfCollapsedFold);
- if not FFoldView.MarkupInfoHiddenCodeLine.IsEnabled then
- Exclude(FLineFlags2, cfCollapsedHide);
- if (FLineFlags2 <> []) then begin
- FFoldView.MarkupInfoFoldedCodeLine.SetFrameBoundsLog(1, MaxInt, 0);
- FFoldView.MarkupInfoHiddenCodeLine.SetFrameBoundsLog(1, MaxInt, 0);
- end;
- inherited SetHighlighterTokensLine(FFoldView.ViewPosToTextIndex(ALine + 1), ARealLine);
- end;
- function TLazSynDisplayFold.GetNextHighlighterToken(out ATokenInfo: TLazSynDisplayTokenInfo): Boolean;
- const
- MarkSpaces: string = ' ';
- MarkDots: string = '...';
- LSTATE_BOL = 0; // at BOL
- LSTATE_TEXT = 1; // in text
- LSTATE_BOL_GAP = 2; // BOL and in Gap (empty line) // must be LSTATE_BOL + 2
- LSTATE_GAP = 3; // In Gap betwen txt and dots // must be LSTATE_TEXT + 2
- LSTATE_DOTS = 4; // In Dots
- LSTATE_EOL = 5; // at start of EOL
- var
- EolAttr: TSynHighlighterAttributes;
- MergeStartX, MergeEndX: TLazSynDisplayTokenBound;
- begin
- case FLineState of
- LSTATE_BOL, LSTATE_TEXT: begin
- Result := inherited GetNextHighlighterToken(ATokenInfo);
- if ( (not Result) or (ATokenInfo.TokenStart = nil)) and (FLineFlags <> [])
- then begin
- inc(FLineState, 2); // LSTATE_BOL_GAP(2), if was at bol // LSTATE_GAP(3) otherwise
- ATokenInfo.TokenStart := PChar(MarkSpaces);
- ATokenInfo.TokenLength := 3;
- if Assigned(CurrentTokenHighlighter)
- then EolAttr := CurrentTokenHighlighter.GetEndOfLineAttribute
- else EolAttr := nil;
- if EolAttr <> nil then begin
- FTokenAttr.Assign(EolAttr);
- ATokenInfo.TokenAttr := FTokenAttr;
- end
- else begin
- ATokenInfo.TokenAttr := nil;
- end;
- Result := True;
- end;
- end;
- LSTATE_GAP: begin
- FLineState := LSTATE_DOTS;
- FTokenAttr.Assign(FFoldView.MarkupInfoFoldedCode);
- FTokenAttr.SetAllPriorities(MaxInt);
- ATokenInfo.TokenStart := PChar(MarkDots);
- ATokenInfo.TokenLength := 3;
- ATokenInfo.TokenAttr := FTokenAttr;
- Result := True;
- end;
- else begin
- Result := inherited GetNextHighlighterToken(ATokenInfo);
- end;
- end;
- if (FLineFlags2 <> []) then begin
- FMarkupLine.Clear;
- if ATokenInfo.TokenAttr = nil then begin
- // Text Area does not expect StartX/Endx
- // So we must merge, to eliminate unwanted borders
- // if (cfCollapsedFold in FLineFlags2)
- // then ATokenInfo.TokenAttr := FFoldView.MarkupInfoFoldedCodeLine
- // else ATokenInfo.TokenAttr := FFoldView.MarkupInfoHiddenCodeLine;
- // exit;
- FMarkupLine.Clear;
- end //;
- else
- FMarkupLine.Assign(ATokenInfo.TokenAttr);
- MergeStartX.Physical := -1;
- MergeStartX.Logical := -1;
- MergeEndX.Physical := -1;
- MergeEndX.Logical := -1;
- if FLineState in [LSTATE_BOL, LSTATE_BOL_GAP] then
- MergeStartX := FFoldView.MarkupInfoFoldedCodeLine.StartX;
- if FLineState = LSTATE_EOL then // LSTATE_GAP; // or result := true
- MergeEndX := FFoldView.MarkupInfoFoldedCodeLine.EndX;
- // fully expand all frames
- //FMarkupLine.SetFrameBoundsLog(0,0,0);
- //FMarkupLine.CurrentStartX := FMarkupLine.StartX;
- //FMarkupLine.CurrentEndX := FMarkupLine.EndX;
- if (cfCollapsedFold in FLineFlags2) then
- FMarkupLine.Merge(FFoldView.MarkupInfoFoldedCodeLine, MergeStartX, MergeEndX)
- else
- FMarkupLine.Merge(FFoldView.MarkupInfoHiddenCodeLine, MergeStartX, MergeEndX);
- ATokenInfo.TokenAttr := FMarkupLine;
- end;
- if FLineState in [LSTATE_BOL, LSTATE_BOL_GAP, LSTATE_DOTS, LSTATE_EOL] then
- inc(FLineState);
- end;
- function TLazSynDisplayFold.GetLinesCount: Integer;
- begin
- Result := FFoldView.Count;
- end;
- function TLazSynDisplayFold.TextToViewIndex(AIndex: TLineIdx): TLineRange;
- begin
- Result := inherited TextToViewIndex(AIndex);
- if Result.Top = Result.Bottom then begin
- Result.Top := FFoldView.TextIndexToViewPos(Result.Top) - 1;
- Result.Bottom := Result.Top;
- end
- else begin;
- Result.Top := FFoldView.TextIndexToViewPos(Result.Top) - 1;
- Result.Bottom := FFoldView.TextIndexToViewPos(Result.Bottom) - 1;
- end;
- end;
- function TLazSynDisplayFold.ViewToTextIndex(AIndex: TLineIdx): TLineIdx;
- begin
- Result := FFoldView.ViewPosToTextIndex(inherited ViewToTextIndex(AIndex)+1);
- end;
- { TSynEditFoldExportStream }
- constructor TSynEditFoldExportStream.Create;
- begin
- inherited;
- FPos := 0;
- FLen := 0;
- FMem := nil;
- end;
- function TSynEditFoldExportStream.GetLen: Integer;
- begin
- Result := FLen;
- end;
- procedure TSynEditFoldExportStream.SetLen(const AValue: Integer);
- begin
- FPos := 0;
- FLen:= AValue;
- end;
- function TSynEditFoldExportStream.GetMem: PChar;
- begin
- if FData <> '' then
- Result := @FData[1]
- else
- Result := FMem;
- end;
- procedure TSynEditFoldExportStream.SetMem(const AValue: PChar);
- begin
- FData := '';
- FMem := AValue;
- FPos := 0;
- end;
- function TSynEditFoldExportStream.GetText: String;
- begin
- // only valid for FData
- SetLength(FData, FLen);
- Result := FData;
- end;
- procedure TSynEditFoldExportStream.SetText(const AValue: String);
- begin
- FData := AValue;
- FMem := nil;
- FPos := 0;
- end;
- function TSynEditFoldExportStream.GrowData(AppendSize: Integer): PChar;
- var
- l: integer;
- begin
- l := length(FData);
- if l < FLen + AppendSize then
- SetLength(FData, l + AppendSize + Max((l+AppendSize) div 4, 1024));
- Result := @FData[FLen + 1];
- inc(FLen, AppendSize);
- end;
- function TSynEditFoldExportStream.EncodeIntEx(Anum: Integer): String;
- var
- n: integer;
- begin
- // 0 - 42 => 1 byte
- // 43 - 1848 => 2 byte
- // 1849 - .... => 3 and more
- Result := '';
- if ANum = 0 then Result := NumEncode86Chars[1];
- n := 0;
- while ANum > 0 do begin
- Result := NumEncode86Chars[1 + (Anum mod 43) + n] + Result;
- ANum := ANum div 43;
- n := 43;
- end;
- end;
- function TSynEditFoldExportStream.EncodeIntEx2(Anum: Integer): String;
- var
- n: Integer;
- begin
- // 0 - 80 => 1 char
- // 81 - 424 => 2 char (80 + 4 * 86)
- // 425 - 467 => 2 char (len(EncodeIntEx) = 1)
- // 468 - 2272 => 3 and more char
- //2273 - .... => 4 and more char
- Result := '';
- if Anum <= 80 then
- Result := NumEncode86Chars[1 + Anum]
- else
- begin
- n := (Anum-81) div 86;
- if n <= 3 then
- Result := NumEncode86Chars[1 + 81 + n] + NumEncode86Chars[1 + (Anum - 81) mod 86]
- else
- Result := NumEncode86Chars[1 + 85] + EncodeIntEx(Anum - 81 - 4*86);
- end;
- end;
- function TSynEditFoldExportStream.InternalReadNum(var APos: Integer): Integer;
- var
- n: Integer;
- begin
- Result := 0;
- while True do begin
- if FPos >= FLen then exit(-1);
- n := NumEncode86Values[(FMem + APos)^];
- if n < 43 then break;
- dec(n, 43);
- Result := Result * 43 + n;
- inc(APos);
- end;
- Result := Result * 43 + n;
- inc(APos);
- end;
- function TSynEditFoldExportStream.InternalReadNumEx(var APos: Integer): Integer;
- begin
- if FPos >= FLen then exit(-1);
- Result := NumEncode86Values[(FMem + APos)^];
- inc(APos);
- if Result <= 80 then
- exit;
- if FPos >= FLen then exit(-1);
- if Result < 85 then begin
- Result := 81 + (Result-81)*86 + NumEncode86Values[(FMem + APos)^];
- inc(APos);
- exit;
- end;
- Result := 81 + 4*86 + InternalReadNum(APos);
- end;
- procedure TSynEditFoldExportStream.Compress;
- (* Known Sequences: XX = Enc64Num (copy sequence from XX chars before)
- NN = ENc22 Num / n = enc22digit (copy n bytes)
- [XXn (up to 21 bytes, from up to 64*64 back)
- [NNXX[ (more then 21 bytes, from up to 64*64 back)
- ]X (3 bytes from max 64 back)
- ]nx ( reocurring space,x times, ever n pos)
- const
- max_single_len = 22 - 1;
- *)
- var
- CurPos, EndPos, SearchPos: Integer;
- FndLen, FndPos, FndPos2: Integer;
- BestLen, BestPos, BestPos2: Integer;
- s: string;
- begin
- AppendString(#0);
- dec(FLen);
- EndPos := FLen;
- CurPos := FLen - 3;
- while CurPos >= 4 do begin
- SearchPos := CurPos - 3;
- BestLen := 0;
- while (SearchPos >= 1) do begin
- if CompareMem(@FData[CurPos], @FData[SearchPos], 3) then begin
- FndLen := 3;
- FndPos := SearchPos;
- FndPos2 := CurPos;
- while (SearchPos + FndLen < FndPos2) and
- (FndPos2 + FndLen < EndPos - 1) and
- (FData[SearchPos + FndLen] = FData[CurPos + FndLen])
- do
- inc(FndLen);
- while (FndPos > 1) and (FndPos + FndLen < FndPos2) and
- (FData[FndPos - 1] = FData[FndPos2 - 1]) do
- begin
- dec(FndPos);
- dec(FndPos2);
- inc(FndLen);
- end;
- if (FndLen > BestLen) and
- ((FndPos2 - FndPos <= NumEncodeAsOneMax) or (FndLen >= 4)) and
- ((FndPos2 - FndPos <= NumEncodeAsTwoMax) or (FndLen >= 5)) and
- ((FndPos2 - FndPos <= NumEncodeAsThreeMax) or (FndLen >= 6))
- then begin
- BestLen := FndLen;
- BestPos := FndPos;
- BestPos2 := FndPos2;
- end;
- end;
- dec(SearchPos);
- end;
- s := '';
- if (BestLen >= 4) then
- s := '[' + EncodeIntEx2(BestPos2 - BestPos) + EncodeIntEx2(BestLen)
- else
- if (BestLen = 3) and (BestPos2 - BestPos <= NumEncodeAsOneMax) then
- s := ']' + EncodeIntEx2(BestPos2 - BestPos);
- if (s<>'') and (length(s) < BestLen) then begin
- System.Move(s[1], FData[BestPos2], length(s));
- System.Move(FData[BestPos2 + BestLen], FData[BestPos2 + length(s)], FLen + 1 - (BestPos2 + BestLen));
- dec(FLen, BestLen - length(s));
- EndPos := BestPos;
- CurPos := BestPos2 - 3;
- end
- else
- dec(CurPos);
- end;
- end;
- procedure TSynEditFoldExportStream.Decompress;
- var
- i, j, n: Integer;
- p, p2: PChar;
- NewLen: Integer;
- begin
- // curently assumes that FMem points NOT at FData
- if FLen = 0 then
- exit;
- NewLen := 0;
- i := 0;
- while i < Flen do begin
- case (FMem+i)^ of
- '[' :
- begin
- inc(i);
- j := InternalReadNumEx(i);
- n := InternalReadNumEx(i);
- if (j < n) or (j > NewLen) then raise ESynEditError.Create('fold format error');
- inc(NewLen, n);
- end;
- ']' :
- begin
- inc(i, 1);
- j := InternalReadNumEx(i);
- if (j < 3) or (j > NewLen) then raise ESynEditError.Create('fold format error');
- inc(NewLen, 3);
- end;
- else
- begin
- inc(NewLen);
- inc(i);
- end;
- end;
- end;
- SetLength(FData, NewLen);
- i := 0;
- p := PChar(FData);
- while i < Flen do begin
- case (FMem+i)^ of
- '[' :
- begin
- inc(i);
- j := InternalReadNumEx(i);
- n := InternalReadNumEx(i);
- p2 := p;
- while n > 0 do begin
- p^ := (p2 - j)^;
- inc(p);
- dec(j);
- dec(n);
- end;
- end;
- ']' :
- begin
- inc(i);
- j := InternalReadNumEx(i);
- p2 := p;
- for n := 0 to 2 do begin
- p^ := (p2 - j)^;
- inc(p);
- dec(j);
- end;
- end;
- else
- begin
- p^ := (FMem + i)^;
- inc(p);
- inc(i);
- end;
- end;
- end;
- FLen := NewLen;
- FMem := PChar(FData);
- FPos := 0;
- end;
- procedure TSynEditFoldExportStream.AddChecksum;
- var
- i, c: Integer;
- begin
- if FLen = 0 then
- exit;
- if FMem = nil then
- FMem := @FData[1];
- c := 0;
- for i := 0 to FLen - 1 do
- c := c xor (ord((FMem + i)^) * (i+1));
- c := (c mod 256) xor ((c div 256) mod 256) xor ((c div 65536) mod 256);
- AppendString(NumEncode86Chars[1 + (c mod 86)]);
- end;
- function TSynEditFoldExportStream.VerifyChecksum: Boolean;
- var
- i, c: Integer;
- begin
- if FLen = 0 then
- exit(True);
- if FMem = nil then
- FMem := @FData[1];
- dec(Flen);
- c := 0;
- for i := 0 to FLen - 1 do
- c := c xor (ord((FMem + i)^) * (i+1));
- c := (c mod 256) xor ((c div 256) mod 256) xor ((c div 65536) mod 256);
- Result := (FMem + FLen)^ = NumEncode86Chars[1 + (c mod 86)];
- end;
- procedure TSynEditFoldExportStream.AppendMem(AMem: Pointer; ALen: Integer);
- begin
- {$IFDEF SynFoldSaveDebug}
- DebugLn(['TSynEditFoldExportStream.AppendMem len=', ALen]);
- {$ENDIF}
- FMem := nil;
- if ALen > 0 then
- System.Move(AMem^, GrowData(ALen)^, ALen);
- end;
- procedure TSynEditFoldExportStream.AppendString(ATxt: String);
- var
- l: Integer;
- begin
- {$IFDEF SynFoldSaveDebug}
- DebugLn(['TSynEditFoldExportStream.AppendString ', ATxt]);
- {$ENDIF}
- FMem := nil;
- l := length(ATxt);
- if l > 0 then
- System.Move(ATxt[1], GrowData(l)^, l);
- end;
- procedure TSynEditFoldExportStream.AppendNum(ANum: Integer);
- begin
- {$IFDEF SynFoldSaveDebug}
- DebugLn(['TSynEditFoldExportStream.AppendNum ', ANum]);
- {$ENDIF}
- FMem := nil;
- AppendString(EncodeIntEx(ANum));
- end;
- procedure TSynEditFoldExportStream.AppendNumEx(ANum: Integer);
- begin
- {$IFDEF SynFoldSaveDebug}
- DebugLn(['TSynEditFoldExportStream.AppendNumEx ', ANum]);
- {$ENDIF}
- FMem := nil;
- AppendString(EncodeIntEx2(ANum));
- end;
- procedure TSynEditFoldExportStream.Reset;
- begin
- FPos := 0;
- if (FMem = nil) and (FData <> '') then
- FMem := @FData[1];
- end;
- procedure TSynEditFoldExportStream.Clear;
- begin
- FLen := 0;
- FMem := nil;
- FPos := 0;
- SetLength(FData, 0);
- end;
- function TSynEditFoldExportStream.ReadMem(AMem: Pointer; ALen: Integer): Boolean;
- begin
- Result := FPos+ ALen <= FLen;
- If not Result then
- exit;
- System.Move((FMem + FPos)^, AMem^, ALen);
- inc(FPos, ALen);
- end;
- function TSynEditFoldExportStream.PeakString(ALen: Integer): String;
- begin
- If not(FPos+ ALen <= FLen) then
- exit('');
- SetLength(Result, ALen);
- if ALen > 0 then
- System.Move((FMem + FPos)^, Result[1], ALen);
- end;
- function TSynEditFoldExportStream.FindChar(AChar: Char): Integer;
- begin
- Result := 0;
- While (FPos + Result < FLen) and ((FMem + FPos + Result)^ <> AChar) do
- inc(Result);
- if FPos + Result = FLen then
- Result := -1;
- end;
- function TSynEditFoldExportStream.ReadString(ALen: Integer): String;
- begin
- If not(FPos+ ALen <= FLen) then
- exit('');
- SetLength(Result, ALen);
- if ALen > 0 then
- System.Move((FMem + FPos)^, Result[1], ALen);
- inc(FPos, ALen);
- end;
- function TSynEditFoldExportStream.ReadNum: Integer;
- begin
- Result := InternalReadNum(FPos);
- {$IFDEF SynFoldSaveDebug}
- DebugLn(['TSynEditFoldExportStream.ReadNum ', Result]);
- {$ENDIF}
- end;
- function TSynEditFoldExportStream.ReadNumEx: Integer;
- begin
- Result := InternalReadNumEx(FPos);
- {$IFDEF SynFoldSaveDebug}
- DebugLn(['TSynEditFoldExportStream.ReadNumEx ', Result]);
- {$ENDIF}
- end;
- function TSynEditFoldExportStream.EOF: Boolean;
- begin
- Result := FPos >= FLen;
- end;
- { TSynEditFoldExportCoder }
- function TSynEditFoldExportCoder.GetReadIsValid: Boolean;
- begin
- Result := FReadState <> sfecInvalid;
- end;
- constructor TSynEditFoldExportCoder.Create(AFoldType: Pointer);
- begin
- inherited Create;
- FExportStream := TSynEditFoldExportStream.Create;
- FExportStream.AppendString(' T'); // Type Marker
- FExportStream.AppendNum(PtrUInt(AFoldType));
- FFoldType := AFoldType;
- FWriteCacheLen := 0;
- FWriteCache := nil;
- FWriteCacheTypes := [];
- end;
- constructor TSynEditFoldExportCoder.Create(AStream: TSynEditFoldExportStream);
- var
- i: Integer;
- begin
- inherited Create;
- FExportStream := TSynEditFoldExportStream.Create;
- FReadState := sfecInvalid;
- if AStream.PeakString(2) <> ' T' then exit;
- AStream.ReadString(2);
- FFoldType := Pointer(PtrUInt(AStream.ReadNum));
- while(true) do begin
- i := AStream.FindChar(' ');
- if i < 0 then i := AStream.Len - AStream.Pos;
- FExportStream.AppendString(AStream.ReadString(i));
- if AStream.EOF or (AStream.PeakString(2) = ' T') then
- break;
- FExportStream.AppendString(AStream.ReadString(2));
- end;
- {$IFDEF SynFoldSaveDebug}
- DebugLn(['TSynEditFoldExportCoder.Create(<from input-stream> FType=', dbgs(FFoldType), ' txtLen=', FExportStream.Len, ' Txt="', FExportStream.Text, '"']);
- {$ENDIF}
- Reset;
- end;
- destructor TSynEditFoldExportCoder.Destroy;
- begin
- FreeAndNil(FExportStream);
- Inherited;
- end;
- procedure TSynEditFoldExportCoder.AddNode(aX, aY, aLen: Integer; aFoldType: TSynEditFoldType);
- (* Format: [Num] <NumEX>
- ' T' [type] [yo] <X> <len> ( <c>* ' p' [sum] [yo] <X> <len> )* <c>* (' P' [sum] [yo] <X> <len>)?
- //////////////////////////
- // Version info
- V1 - no entries
- V2 July 2010 0.9.29
- - added fold-hide <HideInfo>
- //////////////////////////
- <Stream> = { <TypeStream> };
- <TypeStream> = " T" <TypeId> <TypeData>; [* Stores all folds for the given type (eg cfbtBeginEnd) *]
- <TypeId> = ord(cfbtBeginEnd) or similar
- <TypeData> = [<HideInfo>],
- <NodePos>,
- [ [<FoldList>,] [{ <FoldListEndCont>, <NodePos>, [<FoldList>] }] ],
- [ <FoldListEnd> ];
- <FoldList> = [{ <ConsecutiveFoldedCount>, <ConsecutiveUnFoldedCount> }],
- <ConsecutiveFoldedCount>,
- ;
- [* NodePos: is the position of a folded node (of the type matching the current stream)
- ConsecutiveFoldedCount: more folded nodes of the same type, without any
- unfolded node (of this type) inbetween.
- ConsecutiveUnFoldedCount: amount of unfolded nodes (of this type) before the next folded node.
- *]
- <NodePos> = <YOffset> <XPos> <len>;
- <YOffset>…