SynEdit: rewrite FoldHighlighter range class. Use TDictionary.

This commit is contained in:
Martin 2025-03-26 18:40:18 +01:00
parent 6e169e5797
commit 3eb2c20d38
5 changed files with 284 additions and 131 deletions

View File

@ -40,6 +40,10 @@ Additional licenses may be granted in each individual file. See the headers in e
<Filename Value="lazeditmiscprocs.pas"/>
<UnitName Value="lazeditmiscprocs"/>
</Item>
<Item>
<Filename Value="lazedithighlighterutils.pas"/>
<UnitName Value="lazedithighlighterutils"/>
</Item>
</Files>
<RequiredPkgs>
<Item>

View File

@ -8,7 +8,8 @@ unit LazEdit;
interface
uses
TextMateGrammar, xHyperLinksDecorator, xregexpr, xregexpr_unicodedata, LazEditMiscProcs;
TextMateGrammar, xHyperLinksDecorator, xregexpr, xregexpr_unicodedata, LazEditMiscProcs,
LazEditHighlighterUtils;
implementation

View File

@ -0,0 +1,231 @@
{
*****************************************************************************
This file is part of the LazEdit package from the Lazarus IDE.
This content of this file is licensensed: Modified LGPL-2
Or at the users choice: Modified LGPL-3
See the file COPYING.modifiedLGPL.txt, included in the Lazarus distribution,
for details about the license.
Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License Version 1.1 http://www.mozilla.org/MPL/
A copy used under either License can have the other Licenses removed from this
header. A note should be added that the original file is available with the
above choice of License.
*****************************************************************************
}
unit LazEditHighlighterUtils;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, fgl, LazClasses, Generics.Collections, Generics.Defaults;
type
{ TLazHighlighterRange }
TLazHighlighterRange = class
protected
function DoCompare(Range: TLazHighlighterRange; ASize: integer): integer; inline;
function Compare(Range: TLazHighlighterRange): integer; virtual;
public
constructor Create(Template: TLazHighlighterRange); virtual;
procedure Assign(Src: TLazHighlighterRange); virtual; abstract;
end;
TLazHighlighterRangeClass = class of TLazHighlighterRange;
{ TLazHighlighterRanges }
TLazHighlighterRanges = class(TRefCountedObject)
constructor Create; virtual;
destructor Destroy; override;
function GetEqual(ARange: TLazHighlighterRange): TLazHighlighterRange; virtual; abstract;
procedure Allocate; deprecated 'use AddReference // will be removed in 5.99';
procedure Release; deprecated 'use ReleaseReference // will be removed in 5.99';
end;
TLazHighlighterRangesClass = class of TLazHighlighterRanges;
{ TLazHighlighterRangeForDictionary }
TLazHighlighterRangeForDictionary = class(TLazHighlighterRange)
function GetHashCode(AnInitVal: UInt32 = 0): UInt32; reintroduce; virtual;
function GetHashCodeWithoutClass(AnInitVal: UInt32 = 0): UInt32;
end;
{ TLazHighlighterRangesDictionary }
TLazHighlighterRangesDictionary = class(TLazHighlighterRanges)
private type
TLazHLrRangeDictionary = specialize TDictionary<TLazHighlighterRangeForDictionary, TLazHighlighterRangeForDictionary>;
private
FRangeDict: TLazHLrRangeDictionary;
protected
procedure FreeItems;
public
constructor Create; override;
destructor Destroy; override;
function GetEqual(ARange: TLazHighlighterRange): TLazHighlighterRange; override;
end;
function GetHighlighterRangesForHighlighter(
AnHighlighterClass: TClass {TSynCustomHighlighterClass};
ANewClass: TLazHighlighterRangesClass
): TLazHighlighterRanges;
implementation
type
TLazHighlighterRangesList = specialize TFPGMap<Pointer, TLazHighlighterRanges>;
ILazHighlighterRangeEqualityComparer = specialize IEqualityComparer<TLazHighlighterRangeForDictionary>;
{ TLazHighlighterRangeEqualityComparer }
TLazHighlighterRangeEqualityComparer = class(TInterfacedObject, ILazHighlighterRangeEqualityComparer)
public
function Equals(const ALeft, ARight: TLazHighlighterRangeForDictionary): Boolean; reintroduce;
function GetHashCode(const AValue: TLazHighlighterRangeForDictionary): UInt32; reintroduce;
end;
var
LazHighlighterRangesList: TLazHighlighterRangesList;
function GetHighlighterRangesForHighlighter(AnHighlighterClass: TClass;
ANewClass: TLazHighlighterRangesClass): TLazHighlighterRanges;
begin
if LazHighlighterRangesList = nil then
LazHighlighterRangesList := TLazHighlighterRangesList.Create;
if not LazHighlighterRangesList.TryGetData(Pointer(AnHighlighterClass), Result) then begin
Result := ANewClass.Create;
LazHighlighterRangesList.Add(Pointer(AnHighlighterClass), Result);
end;
end;
{ TLazHighlighterRangeEqualityComparer }
function TLazHighlighterRangeEqualityComparer.Equals(const ALeft,
ARight: TLazHighlighterRangeForDictionary): Boolean;
begin
Result := ALeft.Compare(ARight) = 0;
end;
function TLazHighlighterRangeEqualityComparer.GetHashCode(
const AValue: TLazHighlighterRangeForDictionary): UInt32;
begin
Result := AValue.GetHashCode;
end;
{ TLazHighlighterRange }
function TLazHighlighterRange.DoCompare(Range: TLazHighlighterRange; ASize: integer): integer;
begin
Result := InstanceSize - Range.InstanceSize;
if Result = 0 then
Result := CompareByte(Pointer(Self)^, Pointer(Range)^, ASize);
end;
function TLazHighlighterRange.Compare(Range: TLazHighlighterRange): integer;
begin
Result := DoCompare(Range, InstanceSize);
end;
constructor TLazHighlighterRange.Create(Template: TLazHighlighterRange);
begin
if (Template<>nil) and (ClassType<>Template.ClassType) then
raise Exception.Create('wrong tmpl class');
if Template<>nil then
Assign(Template);
end;
{ TLazHighlighterRanges }
constructor TLazHighlighterRanges.Create;
begin
inherited Create;
end;
destructor TLazHighlighterRanges.Destroy;
var
i: Integer;
begin
if LazHighlighterRangesList <> nil then begin
i := LazHighlighterRangesList.IndexOfData(Self);
assert(i>=0, 'TLazHighlighterRangesDictionary.Destroy: i>=0');
LazHighlighterRangesList.Delete(i);
if LazHighlighterRangesList.Count = 0 then
FreeAndNil(LazHighlighterRangesList);
end;
inherited Destroy;
end;
procedure TLazHighlighterRanges.Allocate;
begin
AddReference;
end;
procedure TLazHighlighterRanges.Release;
begin
ReleaseReference;
end;
{ TLazHighlighterRangeForDictionary }
function TLazHighlighterRangeForDictionary.GetHashCode(AnInitVal: UInt32): UInt32;
begin
Result := TDefaultHashFactory.GetHashCode(Pointer(Self), InstanceSize, AnInitVal);
end;
function TLazHighlighterRangeForDictionary.GetHashCodeWithoutClass(AnInitVal: UInt32): UInt32;
begin
Result := TDefaultHashFactory.GetHashCode(Pointer(Self) + SizeOf(Pointer), InstanceSize - SizeOf(Pointer), AnInitVal);
end;
{ TLazHighlighterRangesDictionary }
procedure TLazHighlighterRangesDictionary.FreeItems;
var
i: TLazHighlighterRangeForDictionary;
begin
for i in FRangeDict.Values do i.Free;
FRangeDict.Clear;
end;
constructor TLazHighlighterRangesDictionary.Create;
begin
FRangeDict := TLazHLrRangeDictionary.Create(TLazHighlighterRangeEqualityComparer.Create);
inherited Create;
end;
destructor TLazHighlighterRangesDictionary.Destroy;
begin
inherited Destroy;
FreeItems;
FRangeDict.Free;
end;
function TLazHighlighterRangesDictionary.GetEqual(ARange: TLazHighlighterRange
): TLazHighlighterRange;
var
DictRange: TLazHighlighterRangeForDictionary absolute ARange;
DictResult: TLazHighlighterRangeForDictionary absolute Result;
begin
Result := nil;
if ARange=nil then
exit;
if not FRangeDict.TryGetValue(DictRange, DictResult) then begin
Result := TLazHighlighterRangeClass(DictRange.ClassType).Create(DictRange);
FRangeDict.Add(DictResult, DictResult);
end;
end;
finalization
LazHighlighterRangesList.Free;
end.

View File

@ -61,7 +61,7 @@ uses
// LazUtils
LazClasses, LazLoggerBase, LazTracer,
// SynEdit
SynEditHighlighter, SynEditTypes, LazSynEditText;
SynEditHighlighter, SynEditTypes, LazSynEditText, LazEditHighlighterUtils;
const
NullRange = TSynEditRange(nil);
@ -366,7 +366,7 @@ type
{ TSynCustomHighlighterRange }
TSynCustomHighlighterRange = class
TSynCustomHighlighterRange = class(TLazHighlighterRangeForDictionary)
private
// TODO: either reduce to one level, or create subclass for 2nd level
FCodeFoldStackSize: integer; // EndLevel
@ -376,15 +376,15 @@ type
FRangeType: Pointer;
FTop: TSynCustomCodeFoldBlock;
public
constructor Create(Template: TSynCustomHighlighterRange); virtual;
constructor Create(Template: TLazHighlighterRange); override;
destructor Destroy; override;
function Compare(Range: TSynCustomHighlighterRange): integer; virtual;
function Compare(Range: TLazHighlighterRange): integer; override;
function Add(ABlockType: Pointer = nil; IncreaseLevel: Boolean = True):
TSynCustomCodeFoldBlock; virtual;
procedure Pop(DecreaseLevel: Boolean = True); virtual;
function MaxFoldLevel: Integer; virtual;
procedure Clear; virtual;
procedure Assign(Src: TSynCustomHighlighterRange); virtual;
procedure Assign(ASrc: TLazHighlighterRange); override;
procedure WriteDebugReport;
property FoldRoot: TSynCustomCodeFoldBlock read FTop write FTop;
public
@ -397,9 +397,7 @@ type
read FMinimumNestFoldBlockLevel; // write FMinimumNestFoldBlockLevel;
property Top: TSynCustomCodeFoldBlock read FTop;
end;
TSynCustomHighlighterRangeClass = class of TSynCustomHighlighterRange;
TSynCustomHighlighterRanges = class;
TSynCustomHighlighterRangeClass = class of TSynCustomHighlighterRange deprecated 'use TLazHighlighterRangeClass // will be removed in 5.99';
{ TSynCustomFoldHighlighter }
@ -419,14 +417,14 @@ type
private
FCodeFoldRange: TSynCustomHighlighterRange;
FIsCollectingNodeInfo: boolean;
fRanges: TSynCustomHighlighterRanges;
fRanges: TLazHighlighterRangesDictionary;
FRootCodeFoldBlock: TSynCustomCodeFoldBlock;
FFoldNodeInfoList: TLazSynFoldNodeInfoList;
FCollectingNodeInfoList: TLazSynFoldNodeInfoList;
procedure ClearFoldNodeList;
protected
// "Range"
function GetRangeClass: TSynCustomHighlighterRangeClass; virtual;
function GetRangeClass: TLazHighlighterRangeClass; virtual;
procedure CreateRootCodeFoldBlock; virtual; // set RootCodeFoldBlock
property CodeFoldRange: TSynCustomHighlighterRange read FCodeFoldRange;
function TopCodeFoldBlockType(DownIndex: Integer = 0): Pointer;
@ -536,27 +534,22 @@ type
end;
{ TSynCustomHighlighterRanges }
{ TSynCustomHighlighterRangeTree }
TSynCustomHighlighterRanges = class
TSynCustomHighlighterRangeTree = class(TLazHighlighterRanges)
private
FAllocatedCount: integer;
FHighlighterClass: TSynCustomHighlighterClass;
FItems: TAvlTree;
public
constructor Create(TheHighlighterClass: TSynCustomHighlighterClass);
constructor Create; override;
destructor Destroy; override;
function GetEqual(Range: TSynCustomHighlighterRange
): TSynCustomHighlighterRange;
procedure Allocate;
procedure Release;
property HighlighterClass: TSynCustomHighlighterClass read FHighlighterClass;
property AllocatedCount: integer read FAllocatedCount;
end;
function GetEqual(Range: TLazHighlighterRange
): TLazHighlighterRange; override;
end experimental; // replaced by TLazHighlighterRangesDictionary
function CompareSynHighlighterRanges(Data1, Data2: Pointer): integer;
function AllocateHighlighterRanges(
HighlighterClass: TSynCustomHighlighterClass): TSynCustomHighlighterRanges;
HighlighterClass: TSynCustomHighlighterClass): TLazHighlighterRangesDictionary;
deprecated 'use GetHighlighterRangesForHighlighter // will be removed in 5.99';
function dbgs(AFoldActions: TSynFoldActions): String; overload;
function dbgs(ANode: TSynFoldNodeInfo):string; overload;
@ -585,38 +578,11 @@ begin
Result:=Range1.Compare(Range2);
end;
var
HighlighterRanges: TFPList = nil;
function IndexOfHighlighterRanges(
HighlighterClass: TSynCustomHighlighterClass): integer;
begin
if HighlighterRanges=nil then
Result:=-1
else begin
Result:=HighlighterRanges.Count-1;
while (Result>=0)
and (TSynCustomHighlighterRanges(HighlighterRanges[Result]).HighlighterClass
<>HighlighterClass)
do
dec(Result);
end;
end;
function AllocateHighlighterRanges(
HighlighterClass: TSynCustomHighlighterClass): TSynCustomHighlighterRanges;
var
i: LongInt;
HighlighterClass: TSynCustomHighlighterClass): TLazHighlighterRangesDictionary;
begin
if HighlighterRanges=nil then HighlighterRanges:=TFPList.Create;
i:=IndexOfHighlighterRanges(HighlighterClass);
if i>=0 then begin
Result:=TSynCustomHighlighterRanges(HighlighterRanges[i]);
Result.Allocate;
end else begin
Result:=TSynCustomHighlighterRanges.Create(HighlighterClass);
HighlighterRanges.Add(Result);
end;
Result := TLazHighlighterRangesDictionary(GetHighlighterRangesForHighlighter(HighlighterClass, TLazHighlighterRangesDictionary));
Result.AddReference;
end;
function dbgs(AFoldActions: TSynFoldActions): String;
@ -1711,10 +1677,13 @@ constructor TSynCustomFoldHighlighter.Create(AOwner: TComponent);
begin
SetLength(FFoldConfig, GetFoldConfigInternalCount);
InitFoldConfig;
fRanges:=AllocateHighlighterRanges(TSynCustomHighlighterClass(ClassType));
fRanges := TLazHighlighterRangesDictionary(
GetHighlighterRangesForHighlighter(TSynCustomHighlighterClass(ClassType), TLazHighlighterRangesDictionary)
);
fRanges.AddReference;
CreateRootCodeFoldBlock;
inherited Create(AOwner);
FCodeFoldRange:=GetRangeClass.Create(nil);
FCodeFoldRange:=TSynCustomHighlighterRange(GetRangeClass.Create(nil));
FCodeFoldRange.FoldRoot := FRootCodeFoldBlock;
FFoldNodeInfoList := nil;;
end;
@ -1726,7 +1695,7 @@ begin
FreeAndNil(FCodeFoldRange);
FreeAndNil(FRootCodeFoldBlock);
ReleaseRefAndNil(FFoldNodeInfoList);
fRanges.Release;
fRanges.ReleaseReference;
FFoldConfig := nil;
end;
@ -2109,7 +2078,7 @@ begin
Result := TLazSynFoldNodeInfoList.Create;
end;
function TSynCustomFoldHighlighter.GetRangeClass: TSynCustomHighlighterRangeClass;
function TSynCustomFoldHighlighter.GetRangeClass: TLazHighlighterRangeClass;
begin
Result:=TSynCustomHighlighterRange;
end;
@ -2474,7 +2443,7 @@ end;
{ TSynCustomHighlighterRange }
constructor TSynCustomHighlighterRange.Create(
Template: TSynCustomHighlighterRange);
Template: TLazHighlighterRange);
begin
if (Template<>nil) and (ClassType<>Template.ClassType) then
RaiseGDBException('');
@ -2488,29 +2457,9 @@ begin
inherited Destroy;
end;
function TSynCustomHighlighterRange.Compare(Range: TSynCustomHighlighterRange
): integer;
function TSynCustomHighlighterRange.Compare(Range: TLazHighlighterRange): integer;
begin
if RangeType < Range.RangeType then
Result:=1
else if RangeType > Range.RangeType then
Result:=-1
else if Pointer(FTop) < Pointer(Range.FTop) then
Result:= -1
else if Pointer(FTop) > Pointer(Range.FTop) then
Result:= 1
else
Result := FMinimumNestFoldBlockLevel - Range.FMinimumNestFoldBlockLevel;
if Result <> 0 then
exit;
Result := FNestFoldStackSize - Range.FNestFoldStackSize;
if Result <> 0 then
exit;
Result := FMinimumCodeFoldBlockLevel - Range.FMinimumCodeFoldBlockLevel;
if Result <> 0 then
exit;
Result := FCodeFoldStackSize - Range.FCodeFoldStackSize;
Result := DoCompare(Range, TSynCustomHighlighterRange.InstanceSize);
end;
function TSynCustomHighlighterRange.Add(ABlockType: Pointer;
@ -2563,7 +2512,8 @@ begin
FTop:=nil;
end;
procedure TSynCustomHighlighterRange.Assign(Src: TSynCustomHighlighterRange);
procedure TSynCustomHighlighterRange.Assign(ASrc: TLazHighlighterRange);
var Src: TSynCustomHighlighterRange absolute ASrc;
begin
if (Src<>nil) and (Src<>TSynCustomHighlighterRange(NullRange)) then begin
FTop := Src.FTop;
@ -2591,29 +2541,22 @@ begin
FTop.WriteDebugReport;
end;
{ TSynCustomHighlighterRanges }
{ TSynCustomHighlighterRangeTree }
constructor TSynCustomHighlighterRanges.Create(
TheHighlighterClass: TSynCustomHighlighterClass);
constructor TSynCustomHighlighterRangeTree.Create;
begin
Allocate;
FItems:=TAvlTree.Create(@CompareSynHighlighterRanges);
inherited Create;
end;
destructor TSynCustomHighlighterRanges.Destroy;
destructor TSynCustomHighlighterRangeTree.Destroy;
begin
if HighlighterRanges<>nil then begin
HighlighterRanges.Remove(Self);
if HighlighterRanges.Count=0 then
FreeAndNil(HighlighterRanges);
end;
FItems.FreeAndClear;
FreeAndNil(FItems);
inherited Destroy;
end;
function TSynCustomHighlighterRanges.GetEqual(Range: TSynCustomHighlighterRange
): TSynCustomHighlighterRange;
function TSynCustomHighlighterRangeTree.GetEqual(Range: TLazHighlighterRange): TLazHighlighterRange;
var
Node: TAvlTreeNode;
begin
@ -2623,22 +2566,11 @@ begin
Result:=TSynCustomHighlighterRange(Node.Data);
end else begin
// add a copy
Result:=TSynCustomHighlighterRangeClass(Range.ClassType).Create(Range);
Result:=TLazHighlighterRangeClass(Range.ClassType).Create(Range);
FItems.Add(Result);
//if FItems.Count mod 32 = 0 then debugln(['FOLDRANGE Count=', FItems.Count]);
end;
//debugln('TSynCustomHighlighterRanges.GetEqual A ',dbgs(Node),' ',dbgs(Result.Compare(Range)),' ',dbgs(Result.CodeFoldStackSize));
end;
procedure TSynCustomHighlighterRanges.Allocate;
begin
inc(FAllocatedCount);
end;
procedure TSynCustomHighlighterRanges.Release;
begin
dec(FAllocatedCount);
if FAllocatedCount=0 then Free;
//debugln('TSynCustomHighlighterRangeTree.GetEqual A ',dbgs(Node),' ',dbgs(Result.Compare(Range)),' ',dbgs(Result.CodeFoldStackSize));
end;
{ TSynCustomFoldConfig }

View File

@ -52,9 +52,9 @@ unit SynHighlighterPas;
interface
uses
SysUtils, Classes, fgl, Registry, Graphics, SynEditHighlighterFoldBase,
SysUtils, Classes, fgl, Registry, Graphics, Generics.Defaults, SynEditHighlighterFoldBase,
SynEditMiscProcs, SynEditTypes, SynEditHighlighter, SynEditTextBase,
SynEditStrConst, SynEditMiscClasses, LazLoggerBase, LazEditMiscProcs;
SynEditStrConst, SynEditMiscClasses, LazLoggerBase, LazEditMiscProcs, LazEditHighlighterUtils;
type
TSynPasStringMode = (spsmDefault, spsmStringOnly, spsmNone);
@ -552,8 +552,8 @@ type
procedure SetBracketNestLevel(AValue: integer); inline;
public
procedure Clear; override;
function Compare(Range: TSynCustomHighlighterRange): integer; override;
procedure Assign(Src: TSynCustomHighlighterRange); override;
function Compare(Range: TLazHighlighterRange): integer; override;
procedure Assign(Src: TLazHighlighterRange); override;
function MaxFoldLevel: Integer; override;
procedure ResetBracketNestLevel;
procedure IncBracketNestLevel;
@ -870,7 +870,7 @@ type
procedure EndStatementLastLine(ACurTfb: TPascalCodeFoldBlockType;
ACloseFolds: TPascalCodeFoldBlockTypes); inline;
// "Range"
function GetRangeClass: TSynCustomHighlighterRangeClass; override;
function GetRangeClass: TLazHighlighterRangeClass; override;
procedure CreateRootCodeFoldBlock; override;
function CreateRangeList(ALines: TSynEditStringsBase): TSynHighlighterRangeList; override;
function UpdateRangeInfoAtLine(Index: Integer): Boolean; override; // Returns true if range changed
@ -7160,7 +7160,7 @@ begin
- ord(low(TSynPasDividerDrawLocation)) + 1;
end;
function TSynPasSyn.GetRangeClass: TSynCustomHighlighterRangeClass;
function TSynPasSyn.GetRangeClass: TLazHighlighterRangeClass;
begin
Result:=TSynPasSynRange;
end;
@ -7463,27 +7463,12 @@ begin
FTokenState := tsNone;
end;
function TSynPasSynRange.Compare(Range: TSynCustomHighlighterRange): integer;
function TSynPasSynRange.Compare(Range: TLazHighlighterRange): integer;
begin
Result:=inherited Compare(Range);
if Result<>0 then exit;
Result:=ord(FTokenState)-ord(TSynPasSynRange(Range).FTokenState);
if Result<>0 then exit;
Result:=ord(FMode)-ord(TSynPasSynRange(Range).FMode);
if Result<>0 then exit;
Result:=Integer(FModeSwitches)-Integer(TSynPasSynRange(Range).FModeSwitches);
if Result<>0 then exit;
Result := FBracketNestLevel - TSynPasSynRange(Range).FBracketNestLevel;
if Result<>0 then exit;
Result := FRoundBracketNestLevel - TSynPasSynRange(Range).FRoundBracketNestLevel;
if Result<>0 then exit;
Result := FLastLineCodeFoldLevelFix - TSynPasSynRange(Range).FLastLineCodeFoldLevelFix;
if Result<>0 then exit;
Result := FPasFoldFixLevel - TSynPasSynRange(Range).FPasFoldFixLevel;
Result := DoCompare(Range, TSynPasSynRange.InstanceSize);
end;
procedure TSynPasSynRange.Assign(Src: TSynCustomHighlighterRange);
procedure TSynPasSynRange.Assign(Src: TLazHighlighterRange);
begin
if (Src<>nil) and (Src<>TSynCustomHighlighterRange(NullRange)) then begin
inherited Assign(Src);