303 lines
7.1 KiB
ObjectPascal
303 lines
7.1 KiB
ObjectPascal
unit ATSynEdit_Ranges;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
//{$define show_unfold_rng}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Dialogs,
|
|
ATStringProc;
|
|
|
|
type
|
|
{ TATSynRange }
|
|
|
|
TATSynRange = class
|
|
private
|
|
FX, //start column
|
|
FY, //start line
|
|
FY2: integer; //end line which is fully folded (can't partially fold)
|
|
FFolded: boolean;
|
|
FStaple: boolean;
|
|
FHint: string;
|
|
public
|
|
property X: integer read FX;
|
|
property Y: integer read FY;
|
|
property Y2: integer read FY2;
|
|
property Folded: boolean read FFolded write FFolded;
|
|
property Staple: boolean read FStaple;
|
|
property Hint: string read FHint write FHint;
|
|
constructor Create(AX, AY, AY2: integer; AStaple: boolean; const AHint: string); virtual;
|
|
function IsSimple: boolean;
|
|
function IsLineInside(ALine: integer): boolean;
|
|
function MessageText: string;
|
|
end;
|
|
|
|
type
|
|
TATRangeHasLines = (
|
|
cRngIgnore,
|
|
cRngHasAllLines,
|
|
cRngHasAnyOfLines,
|
|
cRngExceptThisRange
|
|
);
|
|
|
|
type
|
|
{ TATSynRanges }
|
|
|
|
TATSynRanges = class
|
|
private
|
|
FList: TList;
|
|
function GetCount: integer;
|
|
function GetItems(Index: integer): TATSynRange;
|
|
function MessageTextForIndexList(L: TList): string;
|
|
public
|
|
constructor Create; virtual;
|
|
destructor Destroy; override;
|
|
property Count: integer read GetCount;
|
|
function IsIndexValid(N: integer): boolean;
|
|
procedure Clear;
|
|
procedure Add(AX, AY, AY2: integer; AWithStaple: boolean; const AHint: string);
|
|
procedure Insert(Index: integer; AX, AY, AY2: integer; AWithStaple: boolean;
|
|
const AHint: string);
|
|
procedure Delete(Index: integer);
|
|
property Items[Index: integer]: TATSynRange read GetItems; default;
|
|
function IsRangeInsideOther(R1, R2: TATSynRange): boolean;
|
|
function IsRangesSame(R1, R2: TATSynRange): boolean;
|
|
function FindRangesContainingLines(ALineFrom, ALineTo: integer;
|
|
AInRange: TATSynRange; AOnlyFolded, ATopLevelOnly: boolean;
|
|
ALineMode: TATRangeHasLines): TATIntArray;
|
|
function FindRangeWithPlusAtLine(ALine: integer): TATSynRange;
|
|
function MessageText(Cnt: integer): string;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Math,
|
|
ATSynEdit_Carets;
|
|
|
|
//we allow one block to hangout 1 line by Y2 from outer block:
|
|
//it's needed for Pascal econtrol lexer
|
|
//(don't know why it gives such blocks)
|
|
const
|
|
cAllowHangoutLines = 1; //0 or 1, do not bigger
|
|
|
|
|
|
{ TATSynRange }
|
|
|
|
constructor TATSynRange.Create(AX, AY, AY2: integer; AStaple: boolean;
|
|
const AHint: string);
|
|
begin
|
|
if (AX<=0) then raise Exception.Create('Incorrect range with x<=0: '+MessageText);
|
|
if (AY<0) then raise Exception.Create('Incorrect range with y<0: '+MessageText);
|
|
if (AY>AY2) then raise Exception.Create('Incorrect range with y>y2: '+MessageText);
|
|
|
|
FX:= AX;
|
|
FY:= AY;
|
|
FY2:= AY2;
|
|
FStaple:= AStaple;
|
|
FHint:= AHint;
|
|
end;
|
|
|
|
function TATSynRange.IsSimple: boolean;
|
|
begin
|
|
Result:= Y=Y2;
|
|
end;
|
|
|
|
function TATSynRange.IsLineInside(ALine: integer): boolean;
|
|
begin
|
|
Result:= (ALine>=Y) and (ALine<=Y2);
|
|
end;
|
|
|
|
function TATSynRange.MessageText: string;
|
|
begin
|
|
Result:= Format('%d..%d', [Y+1, Y2+1]);
|
|
end;
|
|
|
|
{ TATSynRanges }
|
|
|
|
function TATSynRanges.IsIndexValid(N: integer): boolean;
|
|
begin
|
|
Result:= (N>=0) and (N<FList.Count);
|
|
end;
|
|
|
|
function TATSynRanges.GetCount: integer;
|
|
begin
|
|
Result:= FList.Count;
|
|
end;
|
|
|
|
function TATSynRanges.GetItems(Index: integer): TATSynRange;
|
|
begin
|
|
Result:= TATSynRange(FList[Index]);
|
|
{
|
|
if IsIndexValid(Index) then
|
|
Result:= TATSynRange(FList[Index])
|
|
else
|
|
Result:= nil;
|
|
}
|
|
end;
|
|
|
|
constructor TATSynRanges.Create;
|
|
begin
|
|
FList:= TList.Create;
|
|
FList.Capacity:= 4000;
|
|
end;
|
|
|
|
destructor TATSynRanges.Destroy;
|
|
begin
|
|
Clear;
|
|
FreeAndNil(FList);
|
|
inherited;
|
|
end;
|
|
|
|
procedure TATSynRanges.Clear;
|
|
var
|
|
i: integer;
|
|
begin
|
|
for i:= Count-1 downto 0 do
|
|
TObject(FList[i]).Free;
|
|
FList.Clear;
|
|
end;
|
|
|
|
procedure TATSynRanges.Add(AX, AY, AY2: integer; AWithStaple: boolean; const AHint: string);
|
|
begin
|
|
FList.Add(TATSynRange.Create(AX, AY, AY2, AWithStaple, AHint));
|
|
end;
|
|
|
|
procedure TATSynRanges.Insert(Index: integer; AX, AY, AY2: integer; AWithStaple: boolean; const AHint: string);
|
|
begin
|
|
FList.Insert(Index, TATSynRange.Create(AX, AY, AY2, AWithStaple, AHint));
|
|
end;
|
|
|
|
procedure TATSynRanges.Delete(Index: integer);
|
|
begin
|
|
if IsIndexValid(Index) then
|
|
begin
|
|
TObject(FList[Index]).Free;
|
|
FList.Delete(Index);
|
|
end;
|
|
end;
|
|
|
|
function TATSynRanges.IsRangeInsideOther(R1, R2: TATSynRange): boolean;
|
|
begin
|
|
Result:=
|
|
IsPosSorted(R2.X, R2.Y, R1.X, R1.Y, true)
|
|
and (R1.Y2-cAllowHangoutLines<=R2.Y2);
|
|
end;
|
|
|
|
function TATSynRanges.IsRangesSame(R1, R2: TATSynRange): boolean;
|
|
begin
|
|
if R1=R2 then
|
|
begin Result:= true; Exit end;
|
|
if (R1.X=R2.X) and (R1.Y=R2.Y) and (Abs(R1.Y2-R2.Y2)<=cAllowHangoutLines) then
|
|
begin Result:= true; Exit end;
|
|
|
|
Result:= false;
|
|
end;
|
|
|
|
function TATSynRanges.FindRangesContainingLines(ALineFrom, ALineTo: integer;
|
|
AInRange: TATSynRange; AOnlyFolded, ATopLevelOnly: boolean; ALineMode: TATRangeHasLines): TATIntArray;
|
|
var
|
|
L: TList;
|
|
R: TATSynRange;
|
|
i, j: integer;
|
|
Ok: boolean;
|
|
begin
|
|
SetLength(Result, 0);
|
|
L:= TList.Create;
|
|
L.Capacity:= 512;
|
|
try
|
|
for i:= 0 to Count-1 do
|
|
begin
|
|
R:= Items[i];
|
|
if (not R.IsSimple) then
|
|
if (not AOnlyFolded or R.Folded) then
|
|
begin
|
|
case ALineMode of
|
|
cRngIgnore: Ok:= true;
|
|
cRngHasAllLines: Ok:= (R.Y<=ALineFrom) and (R.Y2>=ALineTo);
|
|
cRngHasAnyOfLines: Ok:= (R.Y<=ALineTo) and (R.Y2>=ALineFrom);
|
|
cRngExceptThisRange: Ok:= R<>AInRange;
|
|
else raise Exception.Create('unknown LineMode');
|
|
end;
|
|
if not Ok then Continue;
|
|
|
|
if AInRange=nil then
|
|
Ok:= true
|
|
else
|
|
Ok:= not IsRangesSame(AInRange, R) and IsRangeInsideOther(R, AInRange);
|
|
|
|
if Ok then
|
|
L.Add(pointer(i));
|
|
end;
|
|
end;
|
|
|
|
if ATopLevelOnly then
|
|
begin
|
|
{$ifdef show_unfold_rng}
|
|
s1:= 'toplevel: ranges shortlist'#13+MessageTextForIndexList(L);
|
|
{$endif}
|
|
|
|
for i:= L.Count-1 downto 1 do
|
|
for j:= 0 to i-1 do
|
|
if IsRangeInsideOther(Items[integer(L[i])], Items[integer(L[j])]) then
|
|
begin
|
|
L.Delete(i);
|
|
Break
|
|
end;
|
|
|
|
{$ifdef show_unfold_rng}
|
|
s2:= 'toplevel: ranges done'#13+MessageTextForIndexList(L);
|
|
if l.count>0 then
|
|
showmessage(s1+#13+s2);
|
|
{$endif}
|
|
end;
|
|
|
|
SetLength(Result, L.Count);
|
|
for i:= 0 to L.Count-1 do
|
|
Result[i]:= integer(L[i]);
|
|
finally
|
|
FreeAndNil(L);
|
|
end;
|
|
end;
|
|
|
|
function TATSynRanges.FindRangeWithPlusAtLine(ALine: integer): TATSynRange;
|
|
var
|
|
i: integer;
|
|
R: TATSynRange;
|
|
begin
|
|
Result:= nil;
|
|
for i:= 0 to Count-1 do
|
|
begin
|
|
R:= Items[i];
|
|
if (not R.IsSimple) and (R.Y=ALine) then
|
|
begin
|
|
Result:= R;
|
|
Break
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TATSynRanges.MessageText(Cnt: integer): string;
|
|
var
|
|
i: integer;
|
|
begin
|
|
Result:= '';
|
|
for i:= 0 to Min(Count-1, Cnt) do
|
|
Result:= Result+Items[i].MessageText+#13;
|
|
end;
|
|
|
|
function TATSynRanges.MessageTextForIndexList(L: TList): string;
|
|
var
|
|
i: integer;
|
|
begin
|
|
Result:= '';
|
|
if L.Count=0 then exit;
|
|
for i:= 0 to L.Count-1 do
|
|
Result:= Result+items[integer(L[i])].MessageText+#13;
|
|
end;
|
|
|
|
end.
|
|
|