mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-27 04:33:54 +02:00
287 lines
9.5 KiB
ObjectPascal
287 lines
9.5 KiB
ObjectPascal
{-------------------------------------------------------------------------------
|
|
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.
|
|
|
|
This file was added to the Lazarus branch of SynEdit.
|
|
The original Author is M Friebe
|
|
}
|
|
|
|
(* Provides folding for Xml and Html *)
|
|
unit SynEditHighlighterXMLBase;
|
|
|
|
interface
|
|
|
|
{$I SynEdit.inc}
|
|
|
|
uses
|
|
SysUtils, Classes, math, LCLType,
|
|
SynEditTextBase,
|
|
SynEditHighlighter, SynEditHighlighterFoldBase;
|
|
|
|
type
|
|
|
|
TSynXmlRangeInfo = record
|
|
ElementOpenList: Array of String; // List of words opened in this line (and still open at the end of line)
|
|
ElementCloseList: Array of Smallint; // include close, for open on same line
|
|
end;
|
|
|
|
{ TSynHighlighterXmlRangeList }
|
|
|
|
TSynHighlighterXmlRangeList = class(TSynHighlighterRangeList)
|
|
private
|
|
FItemOffset: Integer;
|
|
function GetXmlRangeInfo(Index: Integer): TSynXmlRangeInfo;
|
|
procedure SetXmlRangeInfo(Index: Integer; const AValue: TSynXmlRangeInfo);
|
|
protected
|
|
procedure SetCapacity(const AValue: Integer); override;
|
|
public
|
|
constructor Create;
|
|
procedure Move(AFrom, ATo, ALen: Integer); override;
|
|
property XmlRangeInfo[Index: Integer]: TSynXmlRangeInfo
|
|
read GetXmlRangeInfo write SetXmlRangeInfo;
|
|
end;
|
|
|
|
|
|
TSynCustomXmlHighlighter = class(TSynCustomFoldHighlighter)
|
|
private
|
|
FXmlRangeInfo: TSynXmlRangeInfo;
|
|
FXmlRangeInfoChanged: Boolean;
|
|
FXmlRangeInfoOpenPos: integer;
|
|
FXmlRangeInfoClosePos: integer;
|
|
protected
|
|
function CreateRangeList(ALines: TSynEditStringsBase): TSynHighlighterRangeList; override;
|
|
function UpdateRangeInfoAtLine(Index: Integer): Boolean; override; // Returns true if range changed
|
|
|
|
function StartXmlCodeFoldBlock(ABlockType: Integer): TSynCustomCodeFoldBlock;
|
|
function StartXmlNodeCodeFoldBlock(ABlockType: Integer; OpenPos: Integer;
|
|
AName: String): TSynCustomCodeFoldBlock;
|
|
procedure EndXmlCodeFoldBlock;
|
|
procedure EndXmlNodeCodeFoldBlock(ClosePos: Integer = -1; AName: String = '');
|
|
public
|
|
procedure SetLine(const NewValue: string; LineNumber:Integer); override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
function TSynCustomXmlHighlighter.CreateRangeList(ALines: TSynEditStringsBase): TSynHighlighterRangeList;
|
|
begin
|
|
Result := TSynHighlighterXmlRangeList.Create;
|
|
end;
|
|
|
|
function TSynCustomXmlHighlighter.UpdateRangeInfoAtLine(Index: Integer): Boolean;
|
|
var
|
|
InfoOpenLenChanged, InfoCloseLenChanged: Boolean;
|
|
begin
|
|
Result := inherited UpdateRangeInfoAtLine(Index);
|
|
InfoOpenLenChanged := Length(FXmlRangeInfo.ElementOpenList) <> FXmlRangeInfoOpenPos;
|
|
InfoCloseLenChanged := Length(FXmlRangeInfo.ElementCloseList) <> FXmlRangeInfoClosePos;
|
|
if FXmlRangeInfoChanged or InfoOpenLenChanged or InfoCloseLenChanged then begin
|
|
Result := True;
|
|
if InfoOpenLenChanged then
|
|
SetLength(FXmlRangeInfo.ElementOpenList, FXmlRangeInfoOpenPos);
|
|
if InfoCloseLenChanged then
|
|
SetLength(FXmlRangeInfo.ElementCloseList, FXmlRangeInfoClosePos);
|
|
TSynHighlighterXmlRangeList(CurrentRanges).XmlRangeInfo[LineIndex] := FXmlRangeInfo; // Store on this line
|
|
FXmlRangeInfoChanged := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynCustomXmlHighlighter.SetLine(const NewValue: string;
|
|
LineNumber:Integer);
|
|
begin
|
|
inherited;
|
|
FXmlRangeInfo := TSynHighlighterXmlRangeList(CurrentRanges).XmlRangeInfo[LineIndex]; // From this line, not from the previous line
|
|
FXmlRangeInfoChanged := False;
|
|
FXmlRangeInfoOpenPos := 0;
|
|
FXmlRangeInfoClosePos := 0;
|
|
end;
|
|
|
|
function TSynCustomXmlHighlighter.StartXmlCodeFoldBlock(ABlockType: Integer): TSynCustomCodeFoldBlock;
|
|
var
|
|
FoldBlock: Boolean;
|
|
p: PtrInt;
|
|
begin
|
|
FoldBlock := FFoldConfig[ABlockType].Enabled;
|
|
p := 0;
|
|
if not FoldBlock then
|
|
p := PtrInt(GetFoldConfigInternalCount);
|
|
Result := StartCodeFoldBlock(p + Pointer(PtrInt(ABlockType)), FoldBlock);
|
|
end;
|
|
|
|
function TSynCustomXmlHighlighter.StartXmlNodeCodeFoldBlock(ABlockType: Integer;
|
|
OpenPos: Integer; AName: String): TSynCustomCodeFoldBlock;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
If IsScanning then begin
|
|
AName := LowerCase(AName);
|
|
i := Length(FXmlRangeInfo.ElementOpenList);
|
|
if (FXmlRangeInfoOpenPos < i) then begin
|
|
if (FXmlRangeInfo.ElementOpenList[FXmlRangeInfoOpenPos] <> AName) then begin
|
|
FXmlRangeInfo.ElementOpenList[FXmlRangeInfoOpenPos] := AName;
|
|
FXmlRangeInfoChanged := true; // TODO:if this node closes on the same line, it may not be amodified ....
|
|
end;
|
|
end else begin // append - modified will be deteced by the new length
|
|
SetLength(FXmlRangeInfo.ElementOpenList, FXmlRangeInfoOpenPos + 10);
|
|
FXmlRangeInfo.ElementOpenList[FXmlRangeInfoOpenPos] := AName;
|
|
end;
|
|
end;
|
|
inc(FXmlRangeInfoOpenPos);
|
|
result := StartXmlCodeFoldBlock(ABlockType);
|
|
end;
|
|
|
|
procedure TSynCustomXmlHighlighter.EndXmlCodeFoldBlock;
|
|
var
|
|
DecreaseLevel: Boolean;
|
|
begin
|
|
DecreaseLevel := TopCodeFoldBlockType < Pointer(PtrInt(GetFoldConfigInternalCount));
|
|
EndCodeFoldBlock(DecreaseLevel);
|
|
end;
|
|
|
|
procedure TSynCustomXmlHighlighter.EndXmlNodeCodeFoldBlock(ClosePos: Integer = -1; AName: String = '');
|
|
var
|
|
cnt, i, k, lvl: Integer;
|
|
LInfo: Array of String;
|
|
begin
|
|
AName := LowerCase(AName);
|
|
|
|
cnt := 0;
|
|
If IsScanning then begin
|
|
if (AName = '') and (CodeFoldRange.CodeFoldStackSize > 0) then begin
|
|
cnt := 1;
|
|
end
|
|
else begin
|
|
cnt := 1;
|
|
i := FXmlRangeInfoOpenPos;
|
|
while i > 0 do begin
|
|
if (FXmlRangeInfo.ElementOpenList[i-1] = AName) then
|
|
break;
|
|
dec(i);
|
|
inc(cnt);
|
|
end;
|
|
|
|
if i = 0 then begin
|
|
i := LineIndex - 1;
|
|
lvl := FoldBlockEndLevel(i);
|
|
while i >= 0 do begin
|
|
if FoldBlockMinLevel(i) < lvl then begin
|
|
LInfo := TSynHighlighterXmlRangeList(CurrentRanges).XmlRangeInfo[i].ElementOpenList;
|
|
k := length(LInfo) - Max(FoldBlockEndLevel(i) - lvl, 0) - 1;
|
|
while (k >= 0) do begin
|
|
if (LInfo[k] = AName) then
|
|
break;
|
|
inc(cnt);
|
|
dec(k);
|
|
dec(lvl);
|
|
end;
|
|
if k >= 0 then break;
|
|
end;
|
|
dec(i);
|
|
end;
|
|
|
|
if (i < 0) or (cnt > CodeFoldRange.CodeFoldStackSize ) then cnt := 0; // never opened, do not close
|
|
end;
|
|
end;
|
|
|
|
i := Length(FXmlRangeInfo.ElementCloseList);
|
|
if (FXmlRangeInfoClosePos < i) then begin
|
|
if (FXmlRangeInfo.ElementCloseList[FXmlRangeInfoClosePos] <> cnt) then begin
|
|
FXmlRangeInfo.ElementCloseList[FXmlRangeInfoClosePos] := cnt;
|
|
FXmlRangeInfoChanged := true;
|
|
end;
|
|
end else begin // append - modified will be deteced by the new length
|
|
SetLength(FXmlRangeInfo.ElementCloseList, FXmlRangeInfoClosePos + 10);
|
|
FXmlRangeInfo.ElementCloseList[FXmlRangeInfoClosePos] := cnt;
|
|
end;
|
|
end
|
|
else begin
|
|
if FXmlRangeInfoClosePos < length(FXmlRangeInfo.ElementCloseList) then
|
|
cnt := FXmlRangeInfo.ElementCloseList[FXmlRangeInfoClosePos]
|
|
else
|
|
cnt := 0;
|
|
end;
|
|
inc(FXmlRangeInfoClosePos);
|
|
|
|
for i := 1 to cnt do begin
|
|
if FXmlRangeInfoOpenPos > 0 then
|
|
dec(FXmlRangeInfoOpenPos);
|
|
EndXmlCodeFoldBlock;
|
|
end;
|
|
end;
|
|
|
|
{ TSynHighlighterXmlRangeList }
|
|
|
|
function TSynHighlighterXmlRangeList.GetXmlRangeInfo(Index: Integer): TSynXmlRangeInfo;
|
|
begin
|
|
if (Index < 0) or (Index >= Count) then begin
|
|
Result.ElementOpenList := nil;
|
|
exit;
|
|
end;
|
|
Result := TSynXmlRangeInfo((ItemPointer[Index] + FItemOffset)^);
|
|
end;
|
|
|
|
procedure TSynHighlighterXmlRangeList.SetXmlRangeInfo(Index: Integer;
|
|
const AValue: TSynXmlRangeInfo);
|
|
begin
|
|
TSynXmlRangeInfo((ItemPointer[Index] + FItemOffset)^) := AValue;
|
|
end;
|
|
|
|
procedure TSynHighlighterXmlRangeList.SetCapacity(const AValue: Integer);
|
|
var
|
|
i: LongInt;
|
|
begin
|
|
for i := AValue to Capacity-1 do
|
|
with TSynXmlRangeInfo((ItemPointer[i] + FItemOffset)^) do begin
|
|
ElementOpenList := nil;
|
|
ElementCloseList := nil;
|
|
end;
|
|
inherited SetCapacity(AValue);
|
|
end;
|
|
|
|
constructor TSynHighlighterXmlRangeList.Create;
|
|
begin
|
|
inherited;
|
|
FItemOffset := ItemSize;
|
|
ItemSize := FItemOffset + SizeOf(TSynXmlRangeInfo);
|
|
end;
|
|
|
|
procedure TSynHighlighterXmlRangeList.Move(AFrom, ATo, ALen: Integer);
|
|
var
|
|
i: LongInt;
|
|
begin
|
|
if ATo > AFrom then
|
|
for i:= Max(AFrom + ALen, ATo) to ATo + ALen - 1 do // move forward
|
|
with TSynXmlRangeInfo((ItemPointer[i] + FItemOffset)^) do begin
|
|
ElementOpenList := nil;
|
|
ElementCloseList := nil;
|
|
end
|
|
else
|
|
for i:= ATo to Min(ATo + ALen , AFrom) - 1 do // move backward
|
|
with TSynXmlRangeInfo((ItemPointer[i] + FItemOffset)^) do begin
|
|
ElementOpenList := nil;
|
|
ElementCloseList := nil;
|
|
end;
|
|
inherited Move(AFrom, ATo, ALen);
|
|
end;
|
|
|
|
end.
|
|
|
|
|
|
|