mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-13 00:32:35 +02:00
1887 lines
60 KiB
ObjectPascal
1887 lines
60 KiB
ObjectPascal
(* SynEditMarkupIfDef
|
|
|
|
Provides a framework to high-(low-)light "{$IFDEF }" blocks. This unit is directly
|
|
bound to the pascal Highlighter.
|
|
The evaluation of IFDEF expression must be done by user code.
|
|
|
|
A differential AVL tree is build, with a Node for each line having "IFDEF" nodes.
|
|
The nodes contain a list of all IFDEF on the line.
|
|
|
|
This allows to quickly find and insert/delete nodes, as well as move nodes
|
|
(change line), if text is edited.
|
|
|
|
SynEdit may to multiple edits, between the requests to validate, and not all
|
|
edited areas may need to be validated everytime.
|
|
Therefore the goal is that edit can invalidate, with very little effort, and
|
|
work is done during validation.
|
|
|
|
|
|
Each line has a link to the next outer (nesting) line: "OuterNestingLine"
|
|
- The outer NestingLine is for the first node in the line. This means that the
|
|
OuterNestingLine of an OuterNestingLine does not always nest the originating
|
|
node
|
|
1: {$IFDEF }
|
|
2: {$IFDEF } // OuterNestingLine = 1
|
|
3: {$ENDIF} {$IFDEF } // OuterNestingLine = 2 (the start of the line is inside the IFDEF from line 2)
|
|
4: {$IFDEF} // OuterNestingLine = 3
|
|
Line 4 is NOT nested by line 2
|
|
- The OuterNestingLine can be Invalidated by setting HighestValidNestedLine.
|
|
All nodes with a line after HighestValidNestedLine will recalculate their
|
|
OuterNestingLine.
|
|
If a node is no longer needed, it must be kept in a disposed nodes list,
|
|
since other node's OuterNestingLine can still refer to them.
|
|
It can be re-used, since all nodes up to HighestValidNestedLine will have
|
|
valid OuterNestingLine.
|
|
Before freeing any nodes, a GarbageCollection must run through all nodes and
|
|
remove references.
|
|
|
|
|
|
Ifdef/Endif pairs/tripples are double linked. They move with the lines.
|
|
In case a node is inserted, it must go through it's outer lines and for all
|
|
nodes with forward refernces disolve the linked pairs.
|
|
For Speed it can flag the need to do so. (idlValidForPostPeers)
|
|
|
|
|
|
*)
|
|
unit SynEditMarkupIfDef;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
SysUtils, SynEditMarkup, SynEditMiscClasses, SynHighlighterPas, SynEditMarkupHighAll,
|
|
SynEditHighlighterFoldBase, SynEditFoldedView, LazSynEditText, SynEditMiscProcs, LazClasses,
|
|
LazLoggerBase, Graphics;
|
|
|
|
type
|
|
|
|
{ TSynRefCountedDict }
|
|
|
|
TSynRefCountedDict = class(TRefCountedObject)
|
|
private
|
|
FDict: TSynSearchDictionary; // used to check for single line nodes (avoid using highlighter)
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
property Dict: TSynSearchDictionary read FDict;
|
|
end;
|
|
|
|
|
|
TSynMarkupHighIfDefLinesNode = class;
|
|
TSynMarkupHighIfDefLinesTree = class;
|
|
|
|
{ TSynMarkupHighIfDefEntry - Information about a single $IfDef/$Else/$EndIf }
|
|
|
|
SynMarkupIfDefNodeFlag = (
|
|
idnIfdef, idnElse, idnEndIf,
|
|
//idnCommentedIfdef, // Keep Ifdef if commented
|
|
idnMultiLineTag, // The "{$IFDEF ... }" wraps across lines.
|
|
// Only allowed on last node AND if FLine.FEndLineOffs > 0
|
|
idnEnabled, idnDisabled
|
|
);
|
|
SynMarkupIfDefNodeFlags = set of SynMarkupIfDefNodeFlag;
|
|
|
|
PSynMarkupHighIfDefEntry = ^TSynMarkupHighIfDefEntry;
|
|
TSynMarkupHighIfDefEntry = class
|
|
private
|
|
FLine: TSynMarkupHighIfDefLinesNode;
|
|
FNodeFlags: SynMarkupIfDefNodeFlags;
|
|
FPeer1, FPeer2: TSynMarkupHighIfDefEntry;
|
|
FStartColumn, FEndColumn: Integer;
|
|
function GetIsDisabled: Boolean;
|
|
function GetIsEnabled: Boolean;
|
|
function GetNeedsEnabledCheck: Boolean;
|
|
function GetPeerField(AIndex: SynMarkupIfDefNodeFlag): PSynMarkupHighIfDefEntry;
|
|
function GetPeer(AIndex: SynMarkupIfDefNodeFlag): TSynMarkupHighIfDefEntry;
|
|
procedure SetIsDisabled(AValue: Boolean);
|
|
procedure SetIsEnabled(AValue: Boolean);
|
|
procedure SetNeedsEnabledCheck(AValue: Boolean);
|
|
procedure SetPeer(AIndex: SynMarkupIfDefNodeFlag; AValue: TSynMarkupHighIfDefEntry);
|
|
procedure ClearPeerField(APeer: PSynMarkupHighIfDefEntry);
|
|
procedure RemoveForwardPeers;
|
|
public
|
|
//constructor Create;
|
|
destructor Destroy; override;
|
|
procedure ClearPeers;
|
|
procedure MaybeInvalidatePeers;
|
|
|
|
function NodeType: SynMarkupIfDefNodeFlag;
|
|
property IsEnabled: Boolean read GetIsEnabled write SetIsEnabled;
|
|
property IsDisabled: Boolean read GetIsDisabled write SetIsDisabled;
|
|
property NeedsEnabledCheck: Boolean read GetNeedsEnabledCheck write SetNeedsEnabledCheck; // Can only be set to True
|
|
property NodeFlags: SynMarkupIfDefNodeFlags read FNodeFlags write FNodeFlags;
|
|
|
|
property Line: TSynMarkupHighIfDefLinesNode read FLine;
|
|
property StartColumn: Integer read FStartColumn write FStartColumn;
|
|
property EndColumn: Integer read FEndColumn write FEndColumn;
|
|
// COMMENT BEFORE AUTO COMPLETE !!!!!
|
|
property IfDefPeer: TSynMarkupHighIfDefEntry index idnIfdef read GetPeer write SetPeer;
|
|
property ElsePeer: TSynMarkupHighIfDefEntry index idnElse read GetPeer write SetPeer;
|
|
property EndIfPeer: TSynMarkupHighIfDefEntry index idnEndIf read GetPeer write SetPeer;
|
|
end;
|
|
|
|
{ TSynMarkupHighIfDefLinesNode - List of all nodes on the same line }
|
|
|
|
SynMarkupIfDefLineFlag = (
|
|
//idlScannedFrom, idlScannedTo,
|
|
idlValid,
|
|
idlValidForPostPeers, // peer referentials from following lines are valid
|
|
|
|
//idlChanged, idlChangedAtStart, idlChangedEnd, // Flag lines for which to send invaldate
|
|
//idlHasUnknowNodes,
|
|
|
|
idlEnabledAtStart, idlDisabledAtStart,
|
|
idlEnabledAtEnd, idlDisabledAtend,
|
|
idlContineousEnabled, idlContineousDisabled,
|
|
|
|
idlDisposed, // Node is disposed, may be re-used
|
|
idlInGlobalClear // Skip unlinking Peers
|
|
);
|
|
SynMarkupIfDefLineFlags = set of SynMarkupIfDefLineFlag;
|
|
|
|
TSynMarkupHighIfDefLinesNode = class(TSynSizedDifferentialAVLNode)
|
|
private
|
|
FEntries: Array of TSynMarkupHighIfDefEntry;
|
|
FEntryCount: Integer;
|
|
|
|
FLastEntryEndLineOffs: Integer;
|
|
FHighestValidNestedNode: TSynMarkupHighIfDefLinesNode;
|
|
FLineFlags: SynMarkupIfDefLineFlags;
|
|
FOuterNestingNode: TSynMarkupHighIfDefLinesNode;
|
|
FScanEndOffs: Integer;
|
|
function GetEntry(AIndex: Integer): TSynMarkupHighIfDefEntry;
|
|
function GetEntryCapacity: Integer;
|
|
function GetId: PtrUInt;
|
|
procedure SetEntry(AIndex: Integer; AValue: TSynMarkupHighIfDefEntry);
|
|
procedure SetEntryCapacity(AValue: Integer);
|
|
procedure SetEntryCount(AValue: Integer);
|
|
protected
|
|
procedure AdjustPositionOffset(AnAdjustment: integer); // Caller is responsible for staying between neighbours
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure MakeDisposed;
|
|
procedure MaybeInvalidatePeers;
|
|
property LineFlags: SynMarkupIfDefLineFlags read FLineFlags;
|
|
// LastEntryEndLineOffs: For last Entry only, if entry closing "}" is on a diff line. (can go one OVER ScanEndOffs)
|
|
property LastEntryEndLineOffs: Integer read FLastEntryEndLineOffs write FLastEntryEndLineOffs;
|
|
// ScanEndOffs: How many (empty) lines were scanned after this node
|
|
property ScanEndOffs: Integer read FScanEndOffs write FScanEndOffs;
|
|
// HighestValidNestedNode: Tte last (highest line num) node that is valid to refer to this node as OuterNestingLine
|
|
property HighestValidNestedNode: TSynMarkupHighIfDefLinesNode read FHighestValidNestedNode write FHighestValidNestedNode;
|
|
// OuterNestingNode: Line that contains the next outer IFDEF, in which this line is nested
|
|
property OuterNestingNode: TSynMarkupHighIfDefLinesNode read FOuterNestingNode write FOuterNestingNode;
|
|
property Id: PtrUInt read GetId;
|
|
//property OpenedCount: Integer read FOpenedCount;
|
|
//property ClosedCount: Integer read FClosedCount;
|
|
public
|
|
function AddEntry: TSynMarkupHighIfDefEntry;
|
|
procedure DeletEntry(AIndex: Integer; AFree: Boolean = false);
|
|
procedure ReduceCapacity;
|
|
property EntryCount: Integer read FEntryCount write SetEntryCount;
|
|
property EntryCapacity: Integer read GetEntryCapacity write SetEntryCapacity;
|
|
property Entry[AIndex: Integer]: TSynMarkupHighIfDefEntry read GetEntry write SetEntry; default;
|
|
end;
|
|
|
|
{ TSynMarkupHighIfDefLinesNodeInfo }
|
|
|
|
TSynMarkupHighIfDefLinesNodeInfo = object
|
|
private
|
|
FAtBOL: Boolean;
|
|
FAtEOL: Boolean;
|
|
FHighestValidNestedLine: Integer;
|
|
FOuterNestingLine: Integer;
|
|
FNode: TSynMarkupHighIfDefLinesNode;
|
|
FTree: TSynMarkupHighIfDefLinesTree;
|
|
FStartLine, Index: Integer; // Todo: Indek is not used
|
|
FCacheNestMinimum, FCacheNestStart, FCacheNestEnd: Integer;
|
|
function GetEndLineOffs: Integer;
|
|
function GetEntry(AIndex: Integer): TSynMarkupHighIfDefEntry;
|
|
function GetEntryCount: Integer;
|
|
function GetHighestValidNestedLine: Integer;
|
|
function GetHighestValidNestedNode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
function GetLineFlags: SynMarkupIfDefLineFlags;
|
|
function GetNodeId: PtrUInt;
|
|
function GetOuterNestingNode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
function GetOuterNestingLine: Integer;
|
|
function GetScanEndLine: Integer;
|
|
function GetScanEndOffs: Integer;
|
|
procedure SetEndLineOffs(AValue: Integer);
|
|
procedure SetHighestValidNestedNode(AValue: TSynMarkupHighIfDefLinesNodeInfo);
|
|
procedure SetOuterNestingNode(AValue: TSynMarkupHighIfDefLinesNodeInfo);
|
|
procedure SetScanEndLine(AValue: Integer);
|
|
procedure SetScanEndOffs(AValue: Integer);
|
|
procedure SetStartLine(AValue: Integer); // Caller is responsible for staying between neighbours
|
|
public
|
|
procedure ClearInfo;
|
|
function Precessor: TSynMarkupHighIfDefLinesNodeInfo;
|
|
function Successor: TSynMarkupHighIfDefLinesNodeInfo;
|
|
public
|
|
procedure ClearNestCache;
|
|
function NestMinimumDepthAtNode: Integer;
|
|
function NestDepthAtNodeStart: Integer;
|
|
function NestDepthAtNodeEnd: Integer;
|
|
public
|
|
function IsValid: Boolean;
|
|
function HasNode: Boolean;
|
|
property StartLine: Integer read FStartLine write SetStartLine;
|
|
property LineFlags: SynMarkupIfDefLineFlags read GetLineFlags;
|
|
property EndLineOffs: Integer read GetEndLineOffs write SetEndLineOffs;
|
|
property ScanEndOffs: Integer read GetScanEndOffs write SetScanEndOffs;
|
|
property ScanEndLine: Integer read GetScanEndLine write SetScanEndLine;
|
|
function ValidToLine(ANextNode: TSynMarkupHighIfDefLinesNodeInfo): Integer; // ScanEndLine or next node
|
|
|
|
function IsValidOuterNode(ANode: TSynMarkupHighIfDefLinesNodeInfo): Boolean;
|
|
procedure ClearHighestValidNestedNode;
|
|
property HighestValidNestedNode: TSynMarkupHighIfDefLinesNodeInfo read GetHighestValidNestedNode write SetHighestValidNestedNode;
|
|
property HighestValidNestedLine: Integer read GetHighestValidNestedLine;
|
|
property OuterNestingLine: Integer read GetOuterNestingLine;
|
|
property OuterNestingNode: TSynMarkupHighIfDefLinesNodeInfo read GetOuterNestingNode write SetOuterNestingNode;
|
|
|
|
//function AddEntry: TSynMarkupHighIfDefEntry;
|
|
//procedure DeletEntry(AIndex: Integer; AFree: Boolean = false);
|
|
property EntryCount: Integer read GetEntryCount;
|
|
property Entry[AIndex: Integer]: TSynMarkupHighIfDefEntry read GetEntry; // write SetEntry;
|
|
property AtBOL: Boolean read FAtBOL;
|
|
property AtEOL: Boolean read FAtEOL;
|
|
property Node: TSynMarkupHighIfDefLinesNode read FNode;
|
|
property NodeId: PtrUInt read GetNodeId;
|
|
end;
|
|
|
|
{ TSynMarkupHighIfDefLinesNodeInfoList }
|
|
|
|
TSynMarkupHighIfDefLinesNodeInfoList = object
|
|
private
|
|
FCount: Integer;
|
|
FNestOpenNodes: Array of TSynMarkupHighIfDefLinesNodeInfo;
|
|
function GetCapacity: Integer;
|
|
function GetCount: Integer;
|
|
function GetNode(AIndex: Integer): TSynMarkupHighIfDefLinesNodeInfo;
|
|
procedure SetCapacity(AValue: Integer);
|
|
procedure SetCount(AValue: Integer);
|
|
procedure SetNode(AIndex: Integer; AValue: TSynMarkupHighIfDefLinesNodeInfo);
|
|
procedure SetNodes(ALow, AHigh: Integer; AValue: TSynMarkupHighIfDefLinesNodeInfo);
|
|
public
|
|
property Count: Integer read GetCount write SetCount;
|
|
property Capacity: Integer read GetCapacity write SetCapacity;
|
|
property Node[AIndex: Integer]: TSynMarkupHighIfDefLinesNodeInfo read GetNode write SetNode;
|
|
property Nodes[ALow, AHigh: Integer]: TSynMarkupHighIfDefLinesNodeInfo write SetNodes;
|
|
// Set OuterLine, and HighestValidNestedNode
|
|
procedure FixOuterLineForNode(var ANode: TSynMarkupHighIfDefLinesNodeInfo); // "var" so carhe is updated
|
|
Procedure PushNodeLine(var ANode: TSynMarkupHighIfDefLinesNodeInfo);
|
|
procedure dbg;
|
|
end;
|
|
|
|
{ TSynMarkupHighIfDefLinesTree }
|
|
|
|
TSynMarkupHighIfDefLinesTree = class(TSynSizedDifferentialAVLTree)
|
|
private
|
|
FHighlighter: TSynPasSyn;
|
|
FLines: TSynEditStrings;
|
|
FClearing: Boolean;
|
|
FDisposedNodes: TSynMarkupHighIfDefLinesNode;
|
|
procedure SetHighlighter(AValue: TSynPasSyn);
|
|
procedure SetLines(AValue: TSynEditStrings);
|
|
private
|
|
procedure MaybeValidateNode(var ANode: TSynMarkupHighIfDefLinesNodeInfo);
|
|
procedure MaybeExtendNodeBackward(var ANode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
AStopAtLine: Integer = 0);
|
|
procedure MaybeExtendNodeForward(var ANode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
var ANextNode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
AStopBeforeLine: Integer = -1);
|
|
procedure ScanOuterNesting(ANode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
out ANestList: TSynMarkupHighIfDefLinesNodeInfoList);
|
|
procedure ConnectPeers(var ANode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
var ANestList: TSynMarkupHighIfDefLinesNodeInfoList);
|
|
protected
|
|
function CreateNode(APosition: Integer): TSynSizedDifferentialAVLNode; override;
|
|
procedure DisposeNode(var ANode: TSynSizedDifferentialAVLNode); override;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
function CheckLineForNodes(ALine: Integer): Boolean;
|
|
procedure ScanLine(ALine: Integer; var ANodeForLine: TSynMarkupHighIfDefLinesNode);
|
|
procedure ValidateRange(AStartLine, AEndLine: Integer);
|
|
public
|
|
procedure DebugPrint(Flat: Boolean = False);
|
|
procedure Clear; override;
|
|
function FindNodeAtPosition(ALine: Integer;
|
|
AMode: TSynSizedDiffAVLFindMode): TSynMarkupHighIfDefLinesNodeInfo;
|
|
overload;
|
|
property Highlighter: TSynPasSyn read FHighlighter write SetHighlighter;
|
|
property Lines : TSynEditStrings read FLines write SetLines;
|
|
end;
|
|
|
|
{ TSynEditMarkupIfDef }
|
|
|
|
TSynEditMarkupIfDef = class(TSynEditMarkup)
|
|
private
|
|
FFoldView: TSynEditFoldedView;
|
|
FHighlighter: TSynPasSyn;
|
|
FIfDefTree: TSynMarkupHighIfDefLinesTree;
|
|
FNeedValidate, FNeedValidatePaint: Boolean;
|
|
|
|
procedure SetFoldView(AValue: TSynEditFoldedView);
|
|
procedure SetHighlighter(AValue: TSynPasSyn);
|
|
|
|
Procedure ValidateMatches(SkipPaint: Boolean = False);
|
|
protected
|
|
procedure DoFoldChanged(aLine: Integer);
|
|
procedure DoTopLineChanged(OldTopLine : Integer); override;
|
|
procedure DoLinesInWindoChanged(OldLinesInWindow : Integer); override;
|
|
procedure DoMarkupChanged(AMarkup: TSynSelectedColor); override;
|
|
procedure DoTextChanged(StartLine, EndLine, ACountDiff: Integer); override; // 1 based
|
|
procedure DoVisibleChanged(AVisible: Boolean); override;
|
|
procedure SetLines(const AValue: TSynEditStrings); override;
|
|
public
|
|
constructor Create(ASynEdit : TSynEditBase);
|
|
destructor Destroy; override;
|
|
procedure IncPaintLock; override;
|
|
procedure DecPaintLock; override;
|
|
|
|
//procedure PrepareMarkupForRow(aRow: Integer); override;
|
|
//procedure EndMarkup; override;
|
|
//function GetMarkupAttributeAtRowCol(const aRow: Integer;
|
|
// const aStartCol: TLazSynDisplayTokenBound;
|
|
// const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor; override;
|
|
//procedure GetNextMarkupColAfterRowCol(const aRow: Integer;
|
|
// const aStartCol: TLazSynDisplayTokenBound;
|
|
// const AnRtlInfo: TLazSynDisplayRtlInfo;
|
|
// out ANextPhys, ANextLog: Integer); override;
|
|
|
|
// AFirst/ ALast are 1 based
|
|
//Procedure Invalidate(SkipPaint: Boolean = False);
|
|
//Procedure InvalidateLines(AFirstLine: Integer = 0; ALastLine: Integer = 0; SkipPaint: Boolean = False);
|
|
//Procedure SendLineInvalidation(AFirstIndex: Integer = -1;ALastIndex: Integer = -1);
|
|
|
|
property FoldView: TSynEditFoldedView read FFoldView write SetFoldView;
|
|
property Highlighter: TSynPasSyn read FHighlighter write SetHighlighter;
|
|
end;
|
|
|
|
function dbgs(AFlag: SynMarkupIfDefLineFlag): String; overload;
|
|
function dbgs(AFlags: SynMarkupIfDefLineFlags): String; overload;
|
|
function dbgs(AFlag: SynMarkupIfDefNodeFlag): String; overload;
|
|
function dbgs(AFlags: SynMarkupIfDefNodeFlags): String; overload;
|
|
|
|
implementation
|
|
|
|
var
|
|
TheDict: TSynRefCountedDict = nil;
|
|
|
|
procedure MaybeCreateDict;
|
|
begin
|
|
if TheDict = nil then
|
|
TheDict := TSynRefCountedDict.Create;
|
|
end;
|
|
|
|
function dbgs(AFlag: SynMarkupIfDefLineFlag): String;
|
|
begin
|
|
Result := '';
|
|
WriteStr(Result, AFlag);
|
|
end;
|
|
|
|
function dbgs(AFlags: SynMarkupIfDefLineFlags): String;
|
|
var
|
|
i: SynMarkupIfDefLineFlag;
|
|
begin
|
|
Result := '';
|
|
for i := low(AFlags) to high(AFlags) do
|
|
if i in AFlags then
|
|
if Result = '' then
|
|
Result := Result + dbgs(i)
|
|
else
|
|
Result := Result + ', ' + dbgs(i);
|
|
Result := '[' + Result + ']';
|
|
end;
|
|
|
|
function dbgs(AFlag: SynMarkupIfDefNodeFlag): String;
|
|
begin
|
|
Result := '';
|
|
WriteStr(Result, AFlag);
|
|
end;
|
|
|
|
function dbgs(AFlags: SynMarkupIfDefNodeFlags): String;
|
|
var
|
|
i: SynMarkupIfDefNodeFlag;
|
|
begin
|
|
Result := '';
|
|
for i := low(AFlags) to high(AFlags) do
|
|
if i in AFlags then
|
|
if Result = '' then
|
|
Result := Result + dbgs(i)
|
|
else
|
|
Result := Result + ', ' + dbgs(i);
|
|
Result := '[' + Result + ']';
|
|
end;
|
|
|
|
{ TSynMarkupHighIfDefLinesNodeInfoList }
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfoList.GetCapacity: Integer;
|
|
begin
|
|
Result := Length(FNestOpenNodes);
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfoList.GetCount: Integer;
|
|
begin
|
|
if Capacity = 0 then
|
|
FCount := 0;
|
|
Result := FCount;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfoList.GetNode(AIndex: Integer): TSynMarkupHighIfDefLinesNodeInfo;
|
|
begin
|
|
Assert((AIndex < Count) and ( AIndex >= 0), 'TSynMarkupHighIfDefLinesNodeInfoList.GetNode Index');
|
|
Result := FNestOpenNodes[ AIndex];
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfoList.SetCapacity(AValue: Integer);
|
|
begin
|
|
if Capacity = 0 then
|
|
FCount := 0;
|
|
SetLength(FNestOpenNodes, AValue);
|
|
FCount := Min(FCount, AValue);
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfoList.SetCount(AValue: Integer);
|
|
begin
|
|
if Capacity = 0 then
|
|
FCount := 0;
|
|
if Count = AValue then Exit;
|
|
Capacity := Max(FCount, Capacity);
|
|
FCount := AValue;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfoList.SetNode( AIndex: Integer;
|
|
AValue: TSynMarkupHighIfDefLinesNodeInfo);
|
|
begin
|
|
Assert(( AIndex < Count) and ( AIndex >= 0), 'TSynMarkupHighIfDefLinesNodeInfoList.SetNode Index');
|
|
FNestOpenNodes[ AIndex] := AValue;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfoList.SetNodes(ALow, AHigh: Integer;
|
|
AValue: TSynMarkupHighIfDefLinesNodeInfo);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if AHigh >= Count then begin
|
|
Capacity := 1 + AHigh + Min(AHigh div 2, 100);
|
|
Count := AHigh + 1;
|
|
end;
|
|
for i := ALow to AHigh do
|
|
FNestOpenNodes[i] := AValue;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfoList.FixOuterLineForNode(var ANode: TSynMarkupHighIfDefLinesNodeInfo);
|
|
var
|
|
j: Integer;
|
|
begin
|
|
j := ANode.NestDepthAtNodeStart;
|
|
if j = 0 then
|
|
exit;
|
|
ANode.OuterNestingNode := Node[j];
|
|
if (not Node[j].HighestValidNestedNode.HasNode) or
|
|
(Node[j].HighestValidNestedNode.StartLine < ANode.StartLine)
|
|
then
|
|
Node[j].HighestValidNestedNode := ANode;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfoList.PushNodeLine(var ANode: TSynMarkupHighIfDefLinesNodeInfo);
|
|
begin
|
|
Nodes[ANode.NestMinimumDepthAtNode+1, ANode.NestDepthAtNodeEnd] := ANode;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfoList.dbg;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to Count-1 do begin
|
|
DbgOut(['## ',i, ': ', dbgs(Node[i].HasNode)]);
|
|
if Node[i].HasNode then DbgOut(['(', Node[i].StartLine, ', ', Node[i].OuterNestingLine, ',', Node[i].HighestValidNestedLine, ')']);
|
|
end;
|
|
DebugLn();
|
|
end;
|
|
|
|
{ TSynRefCountedDict }
|
|
|
|
constructor TSynRefCountedDict.Create;
|
|
begin
|
|
FDict := TSynSearchDictionary.Create;
|
|
FDict.Add('{$if', 1);
|
|
FDict.Add('{$ifdef', 1);
|
|
FDict.Add('{$ifndef', 1);
|
|
FDict.Add('{$ifopt', 1);
|
|
FDict.Add('{$endif', 3);
|
|
FDict.Add('{$else', 2);
|
|
end;
|
|
|
|
destructor TSynRefCountedDict.Destroy;
|
|
begin
|
|
FDict.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TSynMarkupHighIfDefEntry }
|
|
|
|
function TSynMarkupHighIfDefEntry.NodeType: SynMarkupIfDefNodeFlag;
|
|
begin
|
|
if idnIfdef in FNodeFlags then
|
|
Result := idnIfdef
|
|
else
|
|
if idnElse in FNodeFlags then
|
|
Result := idnElse
|
|
else
|
|
if idnEndIf in FNodeFlags then
|
|
Result := idnEndIf
|
|
else
|
|
assert(false, 'Bad node type');
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefEntry.GetPeerField( AIndex: SynMarkupIfDefNodeFlag): PSynMarkupHighIfDefEntry;
|
|
begin
|
|
Result := nil;
|
|
case NodeType of
|
|
idnIfdef:
|
|
case AIndex of
|
|
idnIfdef: assert(false, 'Invalid node state for getting peer field');
|
|
idnElse: Result := @FPeer1;
|
|
idnEndIf: Result := @FPeer2;
|
|
end;
|
|
idnElse:
|
|
case AIndex of
|
|
idnIfdef: Result := @FPeer1;
|
|
idnElse: assert(false, 'Invalid node state for getting peer field');
|
|
idnEndIf: Result := @FPeer2;
|
|
end;
|
|
idnEndIf:
|
|
case AIndex of
|
|
idnIfdef: Result := @FPeer1;
|
|
idnElse: Result := @FPeer2;
|
|
idnEndIf: assert(false, 'Invalid node state for getting peer field');
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefEntry.GetIsDisabled: Boolean;
|
|
begin
|
|
Result := idnDisabled in FNodeFlags;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefEntry.GetIsEnabled: Boolean;
|
|
begin
|
|
Result := idnEnabled in FNodeFlags;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefEntry.GetNeedsEnabledCheck: Boolean;
|
|
begin
|
|
Result := FNodeFlags * [idnEnabled, idnDisabled] = [];
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefEntry.GetPeer( AIndex: SynMarkupIfDefNodeFlag): TSynMarkupHighIfDefEntry;
|
|
begin
|
|
Result := GetPeerField( AIndex)^;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefEntry.SetIsDisabled(AValue: Boolean);
|
|
begin
|
|
if IsDisabled = AValue then exit;
|
|
IsEnabled := not AValue;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefEntry.SetIsEnabled(AValue: Boolean);
|
|
begin
|
|
if IsEnabled = AValue then exit;
|
|
if AValue then begin
|
|
Include(FNodeFlags, idnEnabled);
|
|
Exclude(FNodeFlags, idnDisabled);
|
|
end
|
|
else begin
|
|
Exclude(FNodeFlags, idnEnabled);
|
|
Include(FNodeFlags, idnDisabled);
|
|
end;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefEntry.SetNeedsEnabledCheck(AValue: Boolean);
|
|
begin
|
|
if (NeedsEnabledCheck = AValue) or (not AValue) then exit;
|
|
Exclude(FNodeFlags, idnEnabled);
|
|
Exclude(FNodeFlags, idnDisabled);
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefEntry.SetPeer( AIndex: SynMarkupIfDefNodeFlag;
|
|
AValue: TSynMarkupHighIfDefEntry);
|
|
var
|
|
PeerField, OthersField: PSynMarkupHighIfDefEntry;
|
|
begin
|
|
PeerField := GetPeerField( AIndex);
|
|
if PeerField^ = AValue then
|
|
Exit;
|
|
|
|
if PeerField^ <> nil then begin
|
|
OthersField := PeerField^.GetPeerField(NodeType);
|
|
assert(OthersField^ = self, 'Peer does not point back to self');
|
|
OthersField^ := nil;
|
|
if NodeType = idnIfdef then
|
|
PeerField^.NodeFlags := PeerField^.NodeFlags - [idnEnabled, idnDisabled];
|
|
end;
|
|
|
|
PeerField^ := AValue;
|
|
|
|
if PeerField^ <> nil then begin
|
|
OthersField := PeerField^.GetPeerField(NodeType);
|
|
assert(OthersField^ = nil, 'Peer is not empty');
|
|
OthersField^ := self;
|
|
if NodeType = idnIfdef then begin
|
|
Assert(PeerField^.NodeFlags * [idnEnabled, idnDisabled] = [], 'PeerField^.NodeFlags * [idnEnabled, idnDisabled] = []');
|
|
if (PeerField^.NodeType = idnElse) and (not NeedsEnabledCheck) then
|
|
PeerField^.IsEnabled := not IsEnabled;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefEntry.ClearPeerField(APeer: PSynMarkupHighIfDefEntry);
|
|
begin
|
|
if APeer^ = nil then exit;
|
|
if APeer^.NodeType <> idnIfdef then
|
|
APeer^.NodeFlags := APeer^.NodeFlags - [idnEnabled, idnDisabled];
|
|
|
|
if APeer^.FPeer1 = self then
|
|
APeer^.FPeer1 := nil
|
|
else
|
|
if APeer^.FPeer2 = self then
|
|
APeer^.FPeer2 := nil
|
|
else
|
|
assert(false, 'ClearPeerField did not find back reference');
|
|
APeer^ := nil;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefEntry.RemoveForwardPeers;
|
|
begin
|
|
case NodeType of
|
|
idnIfdef: begin
|
|
SetPeer(idnElse, nil);
|
|
SetPeer(idnEndIf, nil);
|
|
end;
|
|
idnElse: begin
|
|
SetPeer(idnEndIf, nil);
|
|
end;
|
|
idnEndIf: begin
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
destructor TSynMarkupHighIfDefEntry.Destroy;
|
|
begin
|
|
if (FLine <> nil) and not(idlInGlobalClear in FLine.LineFlags) then
|
|
ClearPeers;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefEntry.ClearPeers;
|
|
begin
|
|
ClearPeerField(@FPeer1);
|
|
ClearPeerField(@FPeer2);
|
|
if NodeType <> idnIfdef then
|
|
NodeFlags := NodeFlags - [idnEnabled, idnDisabled];
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefEntry.MaybeInvalidatePeers;
|
|
begin
|
|
FLine.MaybeInvalidatePeers;
|
|
if FPeer1 <> nil then
|
|
FPeer1.FLine.MaybeInvalidatePeers;
|
|
if FPeer2 <> nil then
|
|
FPeer2.FLine.MaybeInvalidatePeers;
|
|
end;
|
|
|
|
{ TSynMarkupHighIfDefLinesNode }
|
|
|
|
function TSynMarkupHighIfDefLinesNode.GetEntry( AIndex: Integer): TSynMarkupHighIfDefEntry;
|
|
begin
|
|
Result := FEntries[AIndex];
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNode.GetEntryCapacity: Integer;
|
|
begin
|
|
Result := Length(FEntries);
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNode.GetId: PtrUInt;
|
|
begin
|
|
Result := PtrUInt(Self);
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNode.SetEntry( AIndex: Integer;
|
|
AValue: TSynMarkupHighIfDefEntry);
|
|
begin
|
|
FEntries[ AIndex] := AValue;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNode.SetEntryCapacity(AValue: Integer);
|
|
var
|
|
i, j: Integer;
|
|
begin
|
|
j := Length(FEntries);
|
|
if j = AValue then Exit;
|
|
for i := j - 1 downto AValue do
|
|
FreeAndNil(FEntries[i]);
|
|
SetLength(FEntries, AValue);
|
|
for i := j to AValue - 1 do
|
|
FEntries[i] := nil;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNode.SetEntryCount(AValue: Integer);
|
|
begin
|
|
if FEntryCount = AValue then Exit;
|
|
FEntryCount := AValue;
|
|
if EntryCapacity < FEntryCount then
|
|
EntryCapacity := FEntryCount;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNode.AdjustPositionOffset(AnAdjustment: integer);
|
|
begin
|
|
Assert(GetPosition + AnAdjustment < Successor.GetPosition, 'GetPosition + AnAdjustment < Successor.GetPosition');
|
|
Assert(GetPosition + AnAdjustment > Precessor.GetPosition, 'GetPosition + AnAdjustment > Precessor.GetPosition');
|
|
FPositionOffset := FPositionOffset + AnAdjustment;
|
|
end;
|
|
|
|
constructor TSynMarkupHighIfDefLinesNode.Create;
|
|
begin
|
|
FSize := 1; // used for index
|
|
FScanEndOffs := 0;
|
|
end;
|
|
|
|
destructor TSynMarkupHighIfDefLinesNode.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
MakeDisposed;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNode.MakeDisposed;
|
|
begin
|
|
FLineFlags := [idlDisposed];
|
|
while EntryCount > 0 do
|
|
DeletEntry(EntryCount-1);
|
|
if (FHighestValidNestedNode <> nil) and
|
|
(FHighestValidNestedNode.OuterNestingNode = Self)
|
|
then
|
|
FHighestValidNestedNode.OuterNestingNode := nil;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNode.MaybeInvalidatePeers;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if idlValidForPostPeers in FLineFlags then
|
|
exit;
|
|
for i := 0 to EntryCount - 1 do
|
|
Entry[i].RemoveForwardPeers;
|
|
Include(FLineFlags, idlValidForPostPeers);
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNode.AddEntry: TSynMarkupHighIfDefEntry;
|
|
var
|
|
c: Integer;
|
|
begin
|
|
c := EntryCount;
|
|
EntryCount := c + 1;
|
|
assert(FEntries[c]=nil, 'FEntries[c]=nil');
|
|
Result := TSynMarkupHighIfDefEntry.Create;
|
|
FEntries[c] := Result;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNode.DeletEntry( AIndex: Integer; AFree: Boolean);
|
|
begin
|
|
Assert(( AIndex >= 0) and ( AIndex < FEntryCount), 'DeletEntry');
|
|
if AFree then
|
|
FEntries[ AIndex].Free
|
|
else
|
|
FEntries[ AIndex] := nil;
|
|
while AIndex < FEntryCount - 1 do begin
|
|
FEntries[ AIndex] := FEntries[ AIndex + 1];
|
|
inc( AIndex);
|
|
end;
|
|
dec(FEntryCount);
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNode.ReduceCapacity;
|
|
begin
|
|
EntryCapacity := EntryCount;
|
|
end;
|
|
|
|
{ TSynMarkupHighIfDefLinesNodeInfo }
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfo.SetStartLine(AValue: Integer);
|
|
begin
|
|
Assert(FNode <> nil, 'TSynMarkupHighIfDefLinesNodeInfo.SetStartLine has node');
|
|
if FStartLine = AValue then Exit;
|
|
FNode.AdjustPositionOffset(AValue - FStartLine);
|
|
FStartLine := AValue;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.GetLineFlags: SynMarkupIfDefLineFlags;
|
|
begin
|
|
if not HasNode then
|
|
exit([]);
|
|
Result := FNode.LineFlags;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.GetNodeId: PtrUInt;
|
|
begin
|
|
if not HasNode then
|
|
exit(0);
|
|
Result := FNode.Id;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.GetOuterNestingNode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
begin
|
|
Assert(HasNode, 'HasNode for TSynMarkupHighIfDefLinesNodeInfo.GetOuterNestingNode');
|
|
|
|
Result := Self;
|
|
Result.ClearInfo;
|
|
Result.FNode := Node.OuterNestingNode;
|
|
Result.FStartLine := OuterNestingLine;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.GetEndLineOffs: Integer;
|
|
begin
|
|
if not HasNode then
|
|
exit(0);
|
|
Result := FNode.LastEntryEndLineOffs;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.GetEntry(AIndex: Integer): TSynMarkupHighIfDefEntry;
|
|
begin
|
|
Assert(HasNode, 'HasNode for TSynMarkupHighIfDefLinesNodeInfo.GetEntry');
|
|
Result := FNode.Entry[AIndex];
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.GetEntryCount: Integer;
|
|
begin
|
|
if not HasNode then
|
|
exit(0);
|
|
Result := FNode.EntryCount;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.GetHighestValidNestedLine: Integer;
|
|
begin
|
|
if not HasNode then
|
|
exit(0);
|
|
if FHighestValidNestedLine < 0 then begin
|
|
if Node.HighestValidNestedNode = nil then
|
|
FHighestValidNestedLine := 0
|
|
else
|
|
FHighestValidNestedLine := Node.HighestValidNestedNode.GetPosition;
|
|
end;
|
|
assert((FHighestValidNestedLine = 0) or (FHighestValidNestedLine = Node.HighestValidNestedNode.GetPosition), 'FHighestValidNestedLine correct');
|
|
Result := FHighestValidNestedLine;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.GetHighestValidNestedNode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
begin
|
|
Assert(HasNode, 'HasNode for TSynMarkupHighIfDefLinesNodeInfo.GetHighestValidNestedNode');
|
|
|
|
Result := Self;
|
|
Result.ClearInfo;
|
|
Result.FNode := Node.HighestValidNestedNode;
|
|
Result.FStartLine := HighestValidNestedLine;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.GetOuterNestingLine: Integer;
|
|
begin
|
|
if not HasNode then
|
|
exit(0);
|
|
if FOuterNestingLine < 0 then begin
|
|
if Node.OuterNestingNode = nil then
|
|
FOuterNestingLine := 0
|
|
else
|
|
FOuterNestingLine := Node.OuterNestingNode.GetPosition;
|
|
end;
|
|
assert((FOuterNestingLine = 0) or (FOuterNestingLine = Node.OuterNestingNode.GetPosition), 'FOuterNestingLine correct');
|
|
Result := FOuterNestingLine;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.GetScanEndLine: Integer;
|
|
begin
|
|
if not IsValid then
|
|
exit(StartLine);
|
|
if ScanEndOffs >= 0 then
|
|
Result := StartLine + ScanEndOffs
|
|
else
|
|
Result := StartLine;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.GetScanEndOffs: Integer;
|
|
begin
|
|
if not HasNode then
|
|
exit(0);
|
|
Result := FNode.ScanEndOffs;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfo.SetEndLineOffs(AValue: Integer);
|
|
begin
|
|
Assert(HasNode, 'HasNode for TSynMarkupHighIfDefLinesNodeInfo.SetEndLineOffs');
|
|
FNode.LastEntryEndLineOffs := AValue;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfo.SetHighestValidNestedNode(AValue: TSynMarkupHighIfDefLinesNodeInfo);
|
|
begin
|
|
Assert(HasNode, 'HasNode for TSynMarkupHighIfDefLinesNodeInfo.SetHighestValidNestedNode');
|
|
if Node.HighestValidNestedNode = AValue.Node then exit;
|
|
Node.HighestValidNestedNode := AValue.Node;
|
|
FHighestValidNestedLine := AValue.StartLine;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfo.SetOuterNestingNode(AValue: TSynMarkupHighIfDefLinesNodeInfo);
|
|
begin
|
|
Assert(HasNode, 'HasNode for TSynMarkupHighIfDefLinesNodeInfo.SetOuterNestingNode');
|
|
if Node.OuterNestingNode = AValue.Node then exit;
|
|
Node.OuterNestingNode := AValue.Node;
|
|
FOuterNestingLine := AValue.StartLine;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfo.SetScanEndLine(AValue: Integer);
|
|
begin
|
|
Assert(HasNode, 'HasNode for TSynMarkupHighIfDefLinesNodeInfo.SetScanEndLine');
|
|
ScanEndOffs := AValue - StartLine;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfo.SetScanEndOffs(AValue: Integer);
|
|
begin
|
|
Assert(HasNode, 'HasNode for TSynMarkupHighIfDefLinesNodeInfo.SetScanEndOffs');
|
|
FNode.ScanEndOffs := AValue;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfo.ClearInfo;
|
|
begin
|
|
FStartLine := 0;
|
|
Index := 0;
|
|
FNode := nil;
|
|
FAtBOL := False;
|
|
FAtEOL := False;
|
|
ClearNestCache;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.Precessor: TSynMarkupHighIfDefLinesNodeInfo;
|
|
begin
|
|
ClearNestCache;
|
|
Result.FTree := FTree;
|
|
If HasNode then begin
|
|
Result.FStartLine := FStartLine;
|
|
Result.Index := Index;
|
|
Result.FNode := TSynMarkupHighIfDefLinesNode(FNode.Precessor(Result.FStartLine, Result.Index));
|
|
Result.FAtBOL := not Result.HasNode;
|
|
Result.FAtEOL := False;
|
|
end
|
|
else
|
|
if AtEOL then begin
|
|
Result.FNode := TSynMarkupHighIfDefLinesNode(FTree.First(Result.FStartLine, Result.Index));
|
|
Result.FAtBOL := not Result.HasNode;
|
|
Result.FAtEOL := False;
|
|
end
|
|
else begin
|
|
Result.ClearInfo;
|
|
end;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.Successor: TSynMarkupHighIfDefLinesNodeInfo;
|
|
begin
|
|
ClearNestCache;
|
|
Result.FTree := FTree;
|
|
If HasNode then begin
|
|
Result.FStartLine := FStartLine;
|
|
Result.Index := Index;
|
|
Result.FNode := TSynMarkupHighIfDefLinesNode(FNode.Successor(Result.FStartLine, Result.Index));
|
|
Result.FAtBOL := False;
|
|
Result.FAtEOL := not Result.HasNode;
|
|
end
|
|
else
|
|
if FAtBOL then begin
|
|
Result.FNode := TSynMarkupHighIfDefLinesNode(FTree.Last(Result.FStartLine, Result.Index));
|
|
Result.FAtBOL := False;
|
|
Result.FAtEOL := not Result.HasNode;
|
|
end
|
|
else begin
|
|
Result.ClearInfo;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfo.ClearNestCache;
|
|
begin
|
|
FCacheNestMinimum := -1;
|
|
FCacheNestStart := -1;
|
|
FCacheNestEnd := -1;
|
|
FHighestValidNestedLine := -1;
|
|
FOuterNestingLine := -1;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.NestMinimumDepthAtNode: Integer;
|
|
begin
|
|
assert(FTree <> nil, 'NestWinimumDepthAtNode has tree');
|
|
if FCacheNestMinimum < 0 then
|
|
FCacheNestMinimum :=
|
|
FTree.FHighlighter.FoldBlockMinLevel(ToIdx(StartLine), FOLDGROUP_IFDEF,
|
|
[sfbIncludeDisabled]);
|
|
Result := FCacheNestMinimum;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.NestDepthAtNodeStart: Integer;
|
|
begin
|
|
assert(FTree <> nil, 'NestDepthAtNodeStart has tree');
|
|
if FCacheNestStart < 0 then
|
|
FCacheNestStart :=
|
|
FTree.FHighlighter.FoldBlockEndLevel(ToIdx(StartLine)-1, FOLDGROUP_IFDEF,
|
|
[sfbIncludeDisabled]);
|
|
Result := FCacheNestStart;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.NestDepthAtNodeEnd: Integer;
|
|
begin
|
|
assert(FTree <> nil, 'NestDepthAtNodeEnd has tree');
|
|
if FCacheNestEnd < 0 then
|
|
FCacheNestEnd :=
|
|
FTree.FHighlighter.FoldBlockEndLevel(ToIdx(StartLine), FOLDGROUP_IFDEF,
|
|
[sfbIncludeDisabled]);
|
|
Result := FCacheNestEnd;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.ValidToLine(ANextNode: TSynMarkupHighIfDefLinesNodeInfo): Integer;
|
|
begin
|
|
if not HasNode then
|
|
exit(-1);
|
|
if not IsValid then
|
|
exit(StartLine);
|
|
|
|
if ScanEndOffs >= 0 then begin
|
|
Result := StartLine + ScanEndOffs;
|
|
assert((not ANextNode.HasNode) or (Result<ANextNode.StartLine), '(ANextNode=nil) or (Result<ANextNode.StartLine)');
|
|
end
|
|
else
|
|
if ANextNode.HasNode then
|
|
Result := ANextNode.StartLine - 1
|
|
else
|
|
Result := StartLine;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.IsValidOuterNode(ANode: TSynMarkupHighIfDefLinesNodeInfo): Boolean;
|
|
begin
|
|
Result := (ANode.IsValid) and
|
|
(ANode.HighestValidNestedLine >= StartLine);
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesNodeInfo.ClearHighestValidNestedNode;
|
|
begin
|
|
Node.HighestValidNestedNode := nil;
|
|
FHighestValidNestedLine := 0;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.IsValid: Boolean;
|
|
begin
|
|
Result := HasNode and (idlValid in LineFlags);
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesNodeInfo.HasNode: Boolean;
|
|
begin
|
|
Result := FNode <> nil;
|
|
end;
|
|
|
|
{ TSynMarkupHighIfDefLinesTree }
|
|
|
|
procedure TSynMarkupHighIfDefLinesTree.SetHighlighter(AValue: TSynPasSyn);
|
|
begin
|
|
if FHighlighter = AValue then Exit;
|
|
FHighlighter := AValue;
|
|
Clear;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesTree.SetLines(AValue: TSynEditStrings);
|
|
begin
|
|
if FLines = AValue then Exit;
|
|
FLines := AValue;
|
|
Clear;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesTree.MaybeValidateNode(var ANode: TSynMarkupHighIfDefLinesNodeInfo);
|
|
begin
|
|
Assert(ANode.HasNode, 'ANode.HasNode in MaybeValidateNode');
|
|
// TODO: search first
|
|
if (not ANode.IsValid) then
|
|
ScanLine(ANode.StartLine, ANode.FNode);
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesTree.MaybeExtendNodeBackward(var ANode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
AStopAtLine: Integer);
|
|
var
|
|
Line: Integer;
|
|
begin
|
|
Assert(ANode.HasNode, 'ANode.HasNode in MaybeExtendNodeDownwards');
|
|
MaybeValidateNode(ANode);
|
|
if (ANode.EntryCount = 0) then begin
|
|
Assert(not ANode.HighestValidNestedNode.HasNode, 'ANode.HighestValidPeerLineOffs in MaybeExtendNodeDownwards');
|
|
// ANode is a Scan-Start-Marker and may be extended downto StartLine
|
|
Line := ANode.StartLine;
|
|
while Line > AStopAtLine do begin
|
|
dec(Line);
|
|
if CheckLineForNodes(Line) then begin
|
|
ScanLine(Line, ANode.FNode);
|
|
if ANode.EntryCount > 0 then
|
|
break;
|
|
end;
|
|
end;
|
|
ANode.StartLine := Line;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesTree.MaybeExtendNodeForward(var ANode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
var ANextNode: TSynMarkupHighIfDefLinesNodeInfo; AStopBeforeLine: Integer);
|
|
var
|
|
Line: Integer;
|
|
begin
|
|
ANextNode.ClearInfo;
|
|
Assert(ANode.IsValid, 'ANode.IsValid in MaybeExtendNodeForward');
|
|
if AStopBeforeLine < 0 then AStopBeforeLine := Lines.Count + 1;
|
|
// ANode is a Scan-Start-Marker and may be extended downto StartLine
|
|
Line := ANode.StartLine + ANode.ScanEndOffs;
|
|
while Line < AStopBeforeLine - 1 do begin
|
|
inc(Line);
|
|
if CheckLineForNodes(Line) then begin
|
|
ScanLine(Line, ANextNode.FNode);
|
|
if ANextNode.HasNode then begin
|
|
ANextNode.FStartLine := Line; // directly to field
|
|
ANode.ScanEndLine := Line - 1;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
// Line is empty, include in offs
|
|
ANode.ScanEndLine := Line;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesTree.ScanOuterNesting(ANode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
out ANestList: TSynMarkupHighIfDefLinesNodeInfoList);
|
|
|
|
function NestDepthAtLineStart(ALine: Integer): Integer; inline;
|
|
begin
|
|
Result := FHighlighter.FoldBlockEndLevel(ToIdx(ALine)-1, FOLDGROUP_IFDEF, [sfbIncludeDisabled]);
|
|
end;
|
|
function MinimumNestDepthAtLine(ALine: Integer): Integer; inline;
|
|
begin
|
|
Result := FHighlighter.FoldBlockMinLevel(ToIdx(ALine), FOLDGROUP_IFDEF, [sfbIncludeDisabled]);
|
|
end;
|
|
|
|
var
|
|
NestedMinDepth: Integer;
|
|
procedure AddNodeToNestList(ANodeToAdd: TSynMarkupHighIfDefLinesNodeInfo);
|
|
var
|
|
j, k: Integer;
|
|
begin
|
|
j := ANodeToAdd.NestMinimumDepthAtNode;
|
|
k := NestedMinDepth; // NestDepthAtNodeEnd
|
|
if j >= k then
|
|
exit;
|
|
ANestList.Nodes[j+1, k] := ANodeToAdd;
|
|
NestedMinDepth := j;
|
|
end;
|
|
|
|
var
|
|
Line, MaxNest, i: Integer;
|
|
NextNode, TmpNode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
NestedDepth: Integer;
|
|
NestOpenNodes: TSynMarkupHighIfDefLinesNodeInfoList;
|
|
begin
|
|
TmpNode.FTree := Self;
|
|
NestedDepth := ANode.NestDepthAtNodeStart;
|
|
NestedMinDepth := NestedDepth; // ANode.NestDepthAtNodeEnd;
|
|
MaxNest := NestedDepth;
|
|
//AddNodeToNestList(ANode);
|
|
assert((ANode.OuterNestingLine <= 0) or (NestedDepth > 0), 'No outer Anode, if nested=0');
|
|
|
|
while NestedDepth > 0 do begin
|
|
Assert(ANode.IsValid, 'ANode.IsValid while looking for OuterNestingLine');
|
|
|
|
if ANode.OuterNestingLine > 0 then begin
|
|
NextNode := FindNodeAtPosition(ANode.OuterNestingLine, afmNil);
|
|
if ANode.IsValidOuterNode(NextNode) then begin
|
|
assert(NestedDepth > NextNode.NestDepthAtNodeStart, 'dec NestedDepth');
|
|
ANode := NextNode;
|
|
AddNodeToNestList(NextNode);
|
|
NestedDepth := ANode.NestDepthAtNodeStart;
|
|
Continue;
|
|
end;
|
|
end;
|
|
|
|
// Search
|
|
Line := ANode.StartLine - 1;
|
|
i := MinimumNestDepthAtLine(Line);
|
|
while (NestedDepth <= i) do begin
|
|
dec(Line);
|
|
i := MinimumNestDepthAtLine(Line);
|
|
MaxNest := Max(MaxNest, NestDepthAtLineStart(Line))
|
|
end;
|
|
NestedDepth := i;
|
|
|
|
NextNode := FindNodeAtPosition(Line, afmCreate);
|
|
AddNodeToNestList(NextNode);
|
|
assert((NextNode.OuterNestingLine <= 0) or (NextNode.IsValid and (NestedDepth > 0)), 'No outer Nextnode, if not valid');
|
|
MaybeValidateNode(NextNode);
|
|
ANode.SetOuterNestingNode(NextNode);
|
|
Assert(NextNode.EntryCount > 0, 'NextNode.EntryCount > 0');
|
|
|
|
// Update nodes inbetweeen
|
|
NestOpenNodes.Capacity := Max(NestOpenNodes.Capacity, MaxNest + 1);
|
|
TmpNode := NextNode;
|
|
while TmpNode.Node <> ANode.Node do begin
|
|
Assert(TmpNode.HasNode, 'TmpNode.HasNode - Update nodes inbetweeen');
|
|
Assert(TmpNode.NestDepthAtNodeEnd > TmpNode.NestMinimumDepthAtNode, 'TmpNode.NestDepthAtNodeEnd >= TmpNode.NestMinimumDepthAtNode');
|
|
|
|
NestOpenNodes.PushNodeLine(TmpNode);
|
|
//NestOpenNodes.Nodes[TmpNode.NestMinimumDepthAtNode+1, TmpNode.NestDepthAtNodeEnd] := TmpNode;
|
|
TmpNode := TmpNode.Successor;
|
|
NestOpenNodes.FixOuterLineForNode(TmpNode);
|
|
end;
|
|
|
|
NextNode.HighestValidNestedNode := ANode;
|
|
ANode := NextNode;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesTree.ConnectPeers(var ANode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
var ANestList: TSynMarkupHighIfDefLinesNodeInfoList);
|
|
var
|
|
PeerList: array of TSynMarkupHighIfDefEntry;
|
|
NestDepth, LowerNestDepth: Integer;
|
|
i, j, OtherDepth: Integer;
|
|
OtherLine: TSynMarkupHighIfDefLinesNodeInfo;
|
|
begin
|
|
ANode.Node.MaybeInvalidatePeers;
|
|
NestDepth := ANode.NestDepthAtNodeEnd;
|
|
LowerNestDepth := MaxInt;
|
|
SetLength(PeerList, NestDepth + ANode.EntryCount + 1);
|
|
for i := ANode.EntryCount - 1 downto 0 do begin
|
|
case ANode.Entry[i].NodeType of
|
|
idnIfdef: begin
|
|
if LowerNestDepth <= NestDepth then
|
|
PeerList[NestDepth].IfDefPeer := ANode.Entry[i]; // update closing node
|
|
dec(NestDepth);
|
|
end;
|
|
idnElse: begin
|
|
if LowerNestDepth <= NestDepth then
|
|
PeerList[NestDepth].ElsePeer := ANode.Entry[i]; // update closing node
|
|
PeerList[NestDepth] := ANode.Entry[i];
|
|
if LowerNestDepth > NestDepth then
|
|
LowerNestDepth := NestDepth;
|
|
end;
|
|
idnEndIf: begin
|
|
inc(NestDepth);
|
|
PeerList[NestDepth] := ANode.Entry[i];
|
|
if LowerNestDepth > NestDepth then
|
|
LowerNestDepth := NestDepth;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// Find peers in previous lines.
|
|
// Opening (IfDef) nodes will be connected when there closing node is found.
|
|
|
|
for i := NestDepth downto LowerNestDepth do begin
|
|
PeerList[i].MaybeInvalidatePeers;
|
|
case PeerList[i].NodeType of
|
|
idnElse: if PeerList[i].IfDefPeer <> nil then continue;
|
|
idnEndIf: if PeerList[i].ElsePeer <> nil then continue;
|
|
end;
|
|
|
|
OtherLine := ANestList.Node[i];
|
|
OtherDepth := OtherLine.NestDepthAtNodeEnd;
|
|
j := OtherLine.EntryCount;
|
|
while j > 0 do begin
|
|
dec(j);
|
|
if OtherDepth = i then begin
|
|
case OtherLine.Entry[j].NodeType of
|
|
idnIfdef: begin
|
|
PeerList[i].IfDefPeer := OtherLine.Entry[j];
|
|
break;
|
|
end;
|
|
idnElse: begin
|
|
PeerList[i].ElsePeer := OtherLine.Entry[j];
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
case OtherLine.Entry[j].NodeType of
|
|
idnIfdef: inc(OtherDepth);
|
|
idnElse: ; //
|
|
idnEndIf: dec(OtherDepth);
|
|
end;
|
|
//if OtherLine.Entry[j].NodeType = idnElse then
|
|
// inc(OtherDepth);
|
|
end;
|
|
|
|
end;
|
|
|
|
// 5 end if if end end 4
|
|
// 5 6 5
|
|
|
|
// 5 end if if if if end end 6
|
|
// 5 8 7
|
|
|
|
// 5 end end end 2
|
|
// 5 4 3
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesTree.CreateNode(APosition: Integer): TSynSizedDifferentialAVLNode;
|
|
begin
|
|
if FDisposedNodes <> nil then begin
|
|
Result := FDisposedNodes;
|
|
FDisposedNodes := TSynMarkupHighIfDefLinesNode(Result).FOuterNestingNode;
|
|
TSynMarkupHighIfDefLinesNode(Result).FLineFlags := [];
|
|
end
|
|
else
|
|
Result := TSynMarkupHighIfDefLinesNode.Create;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesTree.DisposeNode(var ANode: TSynSizedDifferentialAVLNode);
|
|
begin
|
|
if FClearing then begin
|
|
Include(TSynMarkupHighIfDefLinesNode(ANode).FLineFlags, idlInGlobalClear);
|
|
inherited DisposeNode(ANode);
|
|
end
|
|
else begin
|
|
TSynMarkupHighIfDefLinesNode(ANode).MakeDisposed;
|
|
// Use FOuterNestingNode to link dispossed nodes
|
|
TSynMarkupHighIfDefLinesNode(ANode).FOuterNestingNode := FDisposedNodes;
|
|
FDisposedNodes := TSynMarkupHighIfDefLinesNode(ANode);
|
|
end;
|
|
end;
|
|
|
|
constructor TSynMarkupHighIfDefLinesTree.Create;
|
|
begin
|
|
inherited Create;
|
|
MaybeCreateDict;
|
|
TheDict.AddReference;
|
|
end;
|
|
|
|
destructor TSynMarkupHighIfDefLinesTree.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
ReleaseRefAndNil(TheDict);
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesTree.CheckLineForNodes(ALine: Integer): Boolean;
|
|
var
|
|
m, e: Integer;
|
|
LineText, LineTextLower: String;
|
|
begin
|
|
m := FHighlighter.FoldBlockMinLevel(ALine-1, FOLDGROUP_IFDEF, [sfbIncludeDisabled]);
|
|
e := FHighlighter.FoldBlockEndLevel(ALine-1, FOLDGROUP_IFDEF, [sfbIncludeDisabled]);
|
|
Result := (m < e);
|
|
if (not Result) and (ALine > 1) then
|
|
Result := (e > FHighlighter.FoldBlockEndLevel(ALine-2, FOLDGROUP_IFDEF, [sfbIncludeDisabled]));
|
|
|
|
if not Result then begin
|
|
LineText := Lines[ToIdx(ALine)];
|
|
if LineText = '' then
|
|
exit;
|
|
LineTextLower := LowerCase(LineText);
|
|
Result := TheDict.Dict.Search(@LineTextLower[1], Length(LineTextLower), nil) <> nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesTree.ScanLine(ALine: Integer;
|
|
var ANodeForLine: TSynMarkupHighIfDefLinesNode);
|
|
var
|
|
FoldNodeInfoList: TLazSynFoldNodeInfoList;
|
|
LineTextLower: String;
|
|
NodesAddedCnt: Integer;
|
|
|
|
function FindCloseCurlyBracket(StartX: Integer; out ALineOffs: Integer): Integer;
|
|
var
|
|
CurlyLvl: Integer;
|
|
i, l, c: Integer;
|
|
s: String;
|
|
begin
|
|
Result := StartX;
|
|
ALineOffs := 0;
|
|
CurlyLvl := 1;
|
|
|
|
l := Length(LineTextLower);
|
|
while Result <= l do begin
|
|
case LineTextLower[Result] of
|
|
'{': inc(CurlyLvl);
|
|
'}': if CurlyLvl = 1 then exit
|
|
else dec(CurlyLvl);
|
|
end;
|
|
inc(Result);
|
|
end;
|
|
|
|
c := Lines.Count;
|
|
i := ToIdx(ALine) + 1;
|
|
inc(ALineOffs);
|
|
while (i < c) do begin
|
|
// TODO: get range flag fron pas highlighter
|
|
s := Lines[i];
|
|
l := Length(s);
|
|
Result := 1;
|
|
while Result <= l do begin
|
|
case s[Result] of
|
|
'{': inc(CurlyLvl);
|
|
'}': if CurlyLvl = 1 then exit
|
|
else dec(CurlyLvl);
|
|
end;
|
|
inc(Result);
|
|
end;
|
|
inc(i);
|
|
inc(ALineOffs);
|
|
end;
|
|
|
|
Result := -1;
|
|
end;
|
|
|
|
function GetEntry(ALogStart, ALogEnd, ALineOffs: Integer;
|
|
AType: SynMarkupIfDefNodeFlag): TSynMarkupHighIfDefEntry;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if ANodeForLine = nil then begin
|
|
ANodeForLine := FindNodeAtPosition(ALine, afmCreate).FNode;
|
|
ANodeForLine.EntryCapacity := FoldNodeInfoList.Count;
|
|
end;
|
|
if NodesAddedCnt >= ANodeForLine.EntryCount then begin
|
|
Result := ANodeForLine.AddEntry;
|
|
end
|
|
else begin
|
|
i := NodesAddedCnt;
|
|
Result := ANodeForLine.Entry[i];
|
|
while (i < ANodeForLine.EntryCount-1) and (Result.EndColumn < ALogStart) do begin // Last node in line can not be compared
|
|
// TODO: check for commented notes, maybe keep
|
|
ANodeForLine.DeletEntry(i, True);
|
|
Result := ANodeForLine.Entry[i];
|
|
end;
|
|
// Check if existing node matches
|
|
if (AType = idnIfdef) and // only keep inf for opening nodes
|
|
(Result.NodeFlags * [idnEnabled, AType] = [idnEnabled, AType]) and
|
|
(Result.StartColumn = ALogStart) and
|
|
(Result.EndColumn = ALogEnd) and
|
|
((idnMultiLineTag in Result.NodeFlags) = (ALineOffs > 0)) and
|
|
( (ALineOffs = 0) or (ALineOffs = ANodeForLine.LastEntryEndLineOffs) )
|
|
then begin // Does match, keep as is
|
|
end
|
|
else begin // Does NOT match
|
|
//Result.ClearPeers;
|
|
Result.NodeFlags := [];
|
|
end;
|
|
Result.ClearPeers;
|
|
end;
|
|
inc(NodesAddedCnt);
|
|
Result.FLine := ANodeForLine;
|
|
Result.StartColumn := ALogStart;
|
|
Result.EndColumn := ALogEnd;
|
|
include(Result.FNodeFlags, AType);
|
|
end;
|
|
|
|
var
|
|
fn, fn2: TSynFoldNodeInfo;
|
|
LogStartX, LogEndX, LineLen, LineOffs: Integer;
|
|
Entry: TSynMarkupHighIfDefEntry;
|
|
NType: SynMarkupIfDefNodeFlag;
|
|
i, c: Integer;
|
|
begin
|
|
FoldNodeInfoList := Highlighter.FoldNodeInfo[ToIdx(ALine)];
|
|
FoldNodeInfoList.AddReference;
|
|
FoldNodeInfoList.ActionFilter := []; //[sfaOpen, sfaClose];
|
|
FoldNodeInfoList.GroupFilter := FOLDGROUP_IFDEF;
|
|
|
|
if (ANodeForLine <> nil) and (ANodeForLine.EntryCapacity < FoldNodeInfoList.Count) then
|
|
ANodeForLine.EntryCapacity := FoldNodeInfoList.Count;
|
|
NodesAddedCnt := 0;
|
|
|
|
LineTextLower := LowerCase(Lines[ToIdx(ALine)]);
|
|
LineLen := Length(LineTextLower);
|
|
|
|
i := -1;
|
|
LogEndX := 0;
|
|
c := FoldNodeInfoList.Count - 1;
|
|
while i < c do begin
|
|
inc(i);
|
|
fn := FoldNodeInfoList[i];
|
|
if sfaInvalid in fn.FoldAction then
|
|
continue;
|
|
|
|
LogStartX := ToPos(fn.LogXStart)-1; // LogXStart is at "$", we need "{"
|
|
if (LogStartX < 1) or (LogStartX > LineLen) then begin
|
|
assert(false, '(LogStartX < 1) or (LogStartX > LineLen) ');
|
|
continue;
|
|
end;
|
|
assert(LogStartX > LogEndX, 'ifdef xpos found before end of previous ifdef');
|
|
|
|
LogEndX := FindCloseCurlyBracket(LogStartX+1, LineOffs);
|
|
case TheDict.Dict.GetMatchAtChar(@LineTextLower[LogStartX], LineLen + 1 - LogStartX) of
|
|
1: // ifdef
|
|
begin
|
|
assert(sfaOpen in fn.FoldAction, 'sfaOpen in fn.FoldAction');
|
|
NType := idnIfdef;
|
|
end;
|
|
2: // else
|
|
begin
|
|
assert(i < c, '$ELSE i < c');
|
|
inc(i);
|
|
fn2 := FoldNodeInfoList[i];
|
|
assert(sfaClose in fn.FoldAction, 'sfaClose in fn.FoldAction');
|
|
assert(sfaOpen in fn2.FoldAction, 'sfaOpen in fn2.FoldAction');
|
|
assert(fn.LogXStart = fn2.LogXStart, 'sfaOpen in fn2.FoldAction');
|
|
NType := idnElse;
|
|
end;
|
|
3: // endif
|
|
begin
|
|
assert(sfaClose in fn.FoldAction, 'sfaOpen in fn.FoldAction');
|
|
NType := idnEndIf;
|
|
end;
|
|
else
|
|
begin
|
|
assert(false, 'not found ifdef');
|
|
continue;
|
|
end;
|
|
end;
|
|
|
|
Entry := GetEntry(LogStartX, LogEndX, LineOffs, NType);
|
|
|
|
if LineOffs > 0 then begin
|
|
ANodeForLine.LastEntryEndLineOffs := LineOffs;
|
|
Include(Entry.FNodeFlags, idnMultiLineTag);
|
|
break;
|
|
end;
|
|
|
|
end;
|
|
|
|
FoldNodeInfoList.ReleaseReference;
|
|
if ANodeForLine <> nil then begin
|
|
Include(ANodeForLine.FLineFlags, idlValid);
|
|
ANodeForLine.EntryCount := NodesAddedCnt;
|
|
ANodeForLine.ReduceCapacity;
|
|
ANodeForLine.ScanEndOffs := Max(0, LineOffs-1);
|
|
ANodeForLine.OuterNestingNode := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesTree.ValidateRange(AStartLine, AEndLine: Integer);
|
|
var
|
|
NestList: TSynMarkupHighIfDefLinesNodeInfoList;
|
|
|
|
procedure FixNodeOuterAndPeers(var ANode: TSynMarkupHighIfDefLinesNodeInfo);
|
|
begin
|
|
NestList.FixOuterLineForNode(ANode);
|
|
|
|
ConnectPeers(ANode, NestList);
|
|
|
|
if ANode.EntryCount > 0 then
|
|
NestList.PushNodeLine(ANode);
|
|
end;
|
|
|
|
var
|
|
NextNode, Node, TmpNode: TSynMarkupHighIfDefLinesNodeInfo;
|
|
NodeValidTo: Integer;
|
|
begin
|
|
TmpNode.FTree := Self;
|
|
|
|
(*** Find or create a node for StartLine ***)
|
|
|
|
Node := FindNodeAtPosition(AStartLine, afmPrev); // might be multiline
|
|
NextNode := Node.Successor;
|
|
assert((not NextNode.HasNode) or (AStartLine < NextNode.StartLine), 'AStartLine < NextNode.StartLine');
|
|
|
|
if NextNode.HasNode and
|
|
( (not Node.HasNode) or (AStartLine > Node.ValidToLine(NextNode)) )
|
|
then begin
|
|
// AStartLine has not been scanned yet
|
|
if (not NextNode.IsValid) and (NextNode.EntryCount = 0) then begin
|
|
// NextNode is a no longer used start-of-scan-marker => move it
|
|
Node := NextNode;
|
|
Node.StartLine := AStartLine;
|
|
Node.ClearHighestValidNestedNode;
|
|
NextNode.ClearInfo;
|
|
end
|
|
else
|
|
if (NextNode.StartLine <= AEndLine) then begin
|
|
MaybeExtendNodeBackward(NextNode, AStartLine);
|
|
if NextNode.StartLine = AStartLine then begin
|
|
Node := NextNode;
|
|
NextNode.ClearInfo;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if (not Node.HasNode) or (AStartLine > Node.ValidToLine(NextNode)) then
|
|
Node := FindNodeAtPosition(AStartLine, afmCreate);
|
|
MaybeValidateNode(Node);
|
|
assert((AStartLine >= Node.StartLine) and (AStartLine <= Node.StartLine + Node.EndLineOffs), 'AStartLine is in Node');
|
|
|
|
(*** Check outer nodes (nesting) ***)
|
|
ScanOuterNesting(Node, NestList);
|
|
DbgOut('--'); NestList.dbg;
|
|
|
|
|
|
(*** Scan to Endline ***)
|
|
NextNode := Node.Successor;
|
|
|
|
while (Node.HasNode) and (NextNode.HasNode) and
|
|
(Node.ValidToLine(NextNode) < AEndLine) and
|
|
(NextNode.StartLine <= AEndLine)
|
|
do begin
|
|
Assert(Node.IsValid, 'Node.IsValid while "Scan to Endline"');
|
|
|
|
FixNodeOuterAndPeers(Node);
|
|
MaybeValidateNode(NextNode);
|
|
NodeValidTo := Node.ValidToLine(NextNode);
|
|
MaybeExtendNodeBackward(NextNode, NodeValidTo);
|
|
|
|
TmpNode.ClearInfo;
|
|
MaybeExtendNodeForward(Node, TmpNode, NextNode.StartLine);
|
|
assert(NextNode.StartLine > Node.ScanEndLine, 'NextNode.StartLine > Node.ScanEndLine');
|
|
|
|
if NextNode.StartLine = Node.ScanEndLine + 1 then begin
|
|
Assert(not TmpNode.HasNode, 'not TmpNode.HasNode');
|
|
if NextNode.EntryCount = 0 then begin
|
|
// Merge nodes
|
|
Node.EndLineOffs := Node.EndLineOffs + NextNode.EndLineOffs + 1;
|
|
RemoveNode(NextNode.FNode);
|
|
NextNode := Node.Successor;
|
|
continue;
|
|
end
|
|
else begin
|
|
Node := NextNode;
|
|
NextNode := Node.Successor;
|
|
continue;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
// scan gap
|
|
Assert(TmpNode.HasNode, 'TmpNode.HasNode');
|
|
Assert(NextNode.EntryCount > 0, 'NextNode.EntryCount > 0');
|
|
Node := TmpNode;
|
|
TmpNode.ClearInfo;
|
|
while Node.ScanEndLine + 1 < NextNode.StartLine do begin
|
|
MaybeExtendNodeForward(Node, TmpNode, NextNode.StartLine);
|
|
if not TmpNode.HasNode then
|
|
break;
|
|
FixNodeOuterAndPeers(Node);
|
|
assert(Node.ScanEndLine + 1 < NextNode.StartLine, 'Scan gap still before next node');
|
|
Node := TmpNode;
|
|
TmpNode.ClearInfo;
|
|
end;
|
|
assert(Node.ScanEndLine + 1 = NextNode.StartLine, 'Scan gap has reached next node');
|
|
NextNode := Node.Successor;
|
|
end;
|
|
|
|
end;
|
|
|
|
assert(Node.HasNode);
|
|
FixNodeOuterAndPeers(Node);
|
|
|
|
|
|
while Node.ScanEndLine < AEndLine do begin
|
|
MaybeExtendNodeForward(Node, TmpNode, AEndLine + 1);
|
|
if not TmpNode.HasNode then
|
|
break;
|
|
assert(Node.ScanEndLine < AEndLine, 'Scan gap still before AEndLine');
|
|
Node := TmpNode;
|
|
TmpNode.ClearInfo;
|
|
|
|
FixNodeOuterAndPeers(Node);
|
|
end;
|
|
assert(Node.ScanEndLine >= AEndLine, 'Scan gap has reached AEndLine');
|
|
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesTree.DebugPrint(Flat: Boolean);
|
|
function PeerLine(AEntry: TSynMarkupHighIfDefEntry): string;
|
|
begin
|
|
if AEntry = nil then exit('');
|
|
Result := IntToStr(AEntry.FLine.GetPosition);
|
|
end;
|
|
|
|
procedure DebugPrintNode(ANode: TSynMarkupHighIfDefLinesNode; PreFix, PreFixOne: String);
|
|
function DbgsLine(ANode: TSynMarkupHighIfDefLinesNode): String;
|
|
begin
|
|
Result := 'nil';
|
|
if ANode <> nil then Result := IntToStr(ANode.GetPosition);
|
|
end;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
DebugLn([PreFixOne, 'Line=', ANode.GetPosition, ' ScannedTo=', ANode.ScanEndOffs,
|
|
' Cnt=', ANode.EntryCount,
|
|
' EndLine=', ANode.LastEntryEndLineOffs,
|
|
' Flags=', dbgs(ANode.LineFlags),
|
|
' Outer=', DbgsLine(ANode.OuterNestingNode),
|
|
' HighN=', DbgsLine(ANode.HighestValidNestedNode)
|
|
]);
|
|
for i := 0 to ANode.EntryCount - 1 do
|
|
DebugLn([PreFix, ' # ', dbgs(PtrUInt(ANode.Entry[i])),
|
|
' x1=', ANode.Entry[i].StartColumn, ' x2=', ANode.Entry[i].EndColumn,
|
|
' flg=',dbgs(ANode.Entry[i].NodeFlags),
|
|
' p1=', PeerLine(ANode.Entry[i].FPeer1), ':', dbgs(PtrUInt(ANode.Entry[i].FPeer1)),
|
|
' p2=', PeerLine(ANode.Entry[i].FPeer2), ':', dbgs(PtrUInt(ANode.Entry[i].FPeer2))
|
|
]);
|
|
if Flat then
|
|
exit;
|
|
if ANode.FLeft <> nil then
|
|
DebugPrintNode(TSynMarkupHighIfDefLinesNode(ANode.FLeft), PreFix+' ', PreFix + 'L: ');
|
|
if ANode.FRight <> nil then
|
|
DebugPrintNode(TSynMarkupHighIfDefLinesNode(ANode.FRight), PreFix+' ', PreFix + 'R: ');
|
|
end;
|
|
var
|
|
i: Integer;
|
|
n: TSynMarkupHighIfDefLinesNode;
|
|
begin
|
|
if Flat then begin
|
|
i := 1;
|
|
n := TSynMarkupHighIfDefLinesNode(First);
|
|
while n <> nil do begin
|
|
DebugPrintNode(n, ' ', format('%3d ', [i]));
|
|
n := TSynMarkupHighIfDefLinesNode(n.Successor);
|
|
inc(i);
|
|
end;
|
|
end
|
|
else
|
|
DebugPrintNode(TSynMarkupHighIfDefLinesNode(FRoot), '', '');
|
|
end;
|
|
|
|
procedure TSynMarkupHighIfDefLinesTree.Clear;
|
|
var
|
|
n: TSynMarkupHighIfDefLinesNode;
|
|
begin
|
|
FClearing := True;
|
|
inherited Clear;
|
|
while FDisposedNodes <> nil do begin
|
|
n := FDisposedNodes;
|
|
FDisposedNodes := TSynMarkupHighIfDefLinesNode(n).FOuterNestingNode;
|
|
n.Free;
|
|
end;
|
|
FClearing := False;
|
|
end;
|
|
|
|
function TSynMarkupHighIfDefLinesTree.FindNodeAtPosition(ALine: Integer;
|
|
AMode: TSynSizedDiffAVLFindMode): TSynMarkupHighIfDefLinesNodeInfo;
|
|
begin
|
|
Result.ClearInfo;
|
|
Result.FTree := Self;
|
|
Result.FNode :=
|
|
TSynMarkupHighIfDefLinesNode(FindNodeAtPosition(ALine, AMode,
|
|
Result.FStartLine, Result.Index));
|
|
if Result.FNode = nil then begin
|
|
Result.FAtBOL := AMode = afmPrev;
|
|
Result.FAtEOL := AMode = afmNext;
|
|
end;
|
|
end;
|
|
|
|
{ TSynEditMarkupIfDef }
|
|
|
|
procedure TSynEditMarkupIfDef.SetFoldView(AValue: TSynEditFoldedView);
|
|
begin
|
|
if FFoldView = AValue then Exit;
|
|
FFoldView := AValue;
|
|
end;
|
|
|
|
procedure TSynEditMarkupIfDef.SetHighlighter(AValue: TSynPasSyn);
|
|
begin
|
|
if FHighlighter = AValue then Exit;
|
|
FHighlighter := AValue;
|
|
FIfDefTree.Highlighter := AValue;
|
|
end;
|
|
|
|
procedure TSynEditMarkupIfDef.ValidateMatches(SkipPaint: Boolean);
|
|
var
|
|
i, LastLine: Integer;
|
|
n: TSynMarkupHighIfDefLinesNodeInfo;
|
|
begin
|
|
if (FPaintLock > 0) or (not SynEdit.IsVisible) then begin
|
|
FNeedValidate := True;
|
|
if not SkipPaint then
|
|
FNeedValidatePaint := True;
|
|
exit;
|
|
end;
|
|
FNeedValidate := False;
|
|
|
|
if Highlighter = nil then begin
|
|
FIfDefTree.Clear;
|
|
exit;
|
|
end;
|
|
|
|
LastLine := ScreenRowToRow(LinesInWindow+1);
|
|
FIfDefTree.ValidateRange(TopLine, TopLine + LastLine);
|
|
|
|
InvalidateSynLines(TopLine, TopLine + LastLine);
|
|
|
|
end;
|
|
|
|
procedure TSynEditMarkupIfDef.DoFoldChanged(aLine: Integer);
|
|
begin
|
|
//InvalidateLines(aLine+1, MaxInt, True);
|
|
ValidateMatches;
|
|
end;
|
|
|
|
procedure TSynEditMarkupIfDef.DoTopLineChanged(OldTopLine: Integer);
|
|
begin
|
|
ValidateMatches;
|
|
end;
|
|
|
|
procedure TSynEditMarkupIfDef.DoLinesInWindoChanged(OldLinesInWindow: Integer);
|
|
begin
|
|
ValidateMatches;
|
|
end;
|
|
|
|
procedure TSynEditMarkupIfDef.DoMarkupChanged(AMarkup: TSynSelectedColor);
|
|
begin
|
|
ValidateMatches;
|
|
end;
|
|
|
|
procedure TSynEditMarkupIfDef.DoTextChanged(StartLine, EndLine, ACountDiff: Integer);
|
|
begin
|
|
ValidateMatches;
|
|
end;
|
|
|
|
procedure TSynEditMarkupIfDef.DoVisibleChanged(AVisible: Boolean);
|
|
begin
|
|
ValidateMatches;
|
|
end;
|
|
|
|
procedure TSynEditMarkupIfDef.SetLines(const AValue: TSynEditStrings);
|
|
begin
|
|
inherited SetLines(AValue);
|
|
FIfDefTree.Lines := AValue;
|
|
end;
|
|
|
|
constructor TSynEditMarkupIfDef.Create(ASynEdit: TSynEditBase);
|
|
begin
|
|
inherited Create(ASynEdit);
|
|
FIfDefTree := TSynMarkupHighIfDefLinesTree.Create;
|
|
|
|
MarkupInfo.Clear;
|
|
MarkupInfo.Background := clLight;
|
|
end;
|
|
|
|
destructor TSynEditMarkupIfDef.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
FreeAndNil(FIfDefTree);
|
|
end;
|
|
|
|
procedure TSynEditMarkupIfDef.IncPaintLock;
|
|
begin
|
|
if FPaintLock = 0 then begin
|
|
FNeedValidatePaint := False;
|
|
FNeedValidate := False;
|
|
end;
|
|
inherited IncPaintLock;
|
|
end;
|
|
|
|
procedure TSynEditMarkupIfDef.DecPaintLock;
|
|
begin
|
|
inherited DecPaintLock;
|
|
if (FPaintLock = 0) and FNeedValidate then
|
|
ValidateMatches(not FNeedValidatePaint);
|
|
end;
|
|
|
|
end.
|
|
|