lazarus/components/synedit/synhighlighterxml.pas
martin f7794cff2a SynEdit: Added fold config for for XML/LFM
git-svn-id: trunk@22623 -
2009-11-16 13:42:30 +00:00

1329 lines
38 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.
The Original Code is: SynHighlighterXML.pas, released 2000-11-20.
The Initial Author of this file is Jeff Rafter.
All Rights Reserved.
Contributors to the SynEdit and mwEdit projects are listed in the
Contributors.txt file.
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.
$Id$
You may retrieve the latest version of this file at the SynEdit home page,
located at http://SynEdit.SourceForge.net
History:
-------------------------------------------------------------------------------
2000-11-30 Removed mHashTable and MakeIdentTable per Michael Hieke
Known Issues:
- Nothing is really constrained (properly) to valid name chars
- Entity Refs are not constrained to valid name chars
- Support for "Combining Chars and Extender Chars" in names are lacking
- The internal DTD is not parsed (and not handled correctly)
-------------------------------------------------------------------------------}
{
@abstract(Provides an XML highlighter for SynEdit)
@author(Jeff Rafter-- Phil 4:13, based on SynHighlighterHTML by Hideo Koiso)
@created(2000-11-17)
@lastmod(2001-03-12)
The SynHighlighterXML unit provides SynEdit with an XML highlighter.
}
unit SynHighlighterXML;
interface
{$I SynEdit.inc}
uses
SysUtils, Classes, math, LCLProc,
{$IFDEF SYN_CLX}
Qt, QControls, QGraphics,
{$ELSE}
{$IFDEF SYN_LAZARUS}
LCLIntf, LCLType,
{$ELSE}
Windows, Messages, Registry,
{$ENDIF}
Controls, Graphics,
{$ENDIF}
SynEditTypes, SynEditTextBuffer, SynEditHighlighter, SynEditHighlighterFoldBase;
type
TtkTokenKind = (tkAposAttrValue, tkAposEntityRef, tkAttribute, tkCDATA,
tkComment, tkElement, tkEntityRef, tkEqual, tkNull, tkProcessingInstruction,
tkQuoteAttrValue, tkQuoteEntityRef, tkSpace, tkSymbol, tkText,
//
tknsAposAttrValue, tknsAposEntityRef, tknsAttribute, tknsEqual,
tknsQuoteAttrValue, tknsQuoteEntityRef,
//These are unused at the moment
tkDocType
{tkDocTypeAposAttrValue, tkDocTypeAposEntityRef, tkDocTypeAttribute,
tkDocTypeElement, tkDocTypeEqual tkDocTypeQuoteAttrValue,
tkDocTypeQuoteEntityRef}
);
TRangeState = (rsAposAttrValue, rsAPosEntityRef, rsAttribute, rsCDATA,
rsComment, rsElement, rsCloseElement, rsOpenElement, rsEntityRef, rsEqual, rsProcessingInstruction,
rsQuoteAttrValue, rsQuoteEntityRef, rsText,
//
rsnsAposAttrValue, rsnsAPosEntityRef, rsnsEqual, rsnsQuoteAttrValue,
rsnsQuoteEntityRef,
//These are unused at the moment
rsDocType, rsDocTypeSquareBraces //ek 2001-11-11
{rsDocTypeAposAttrValue, rsDocTypeAposEntityRef, rsDocTypeAttribute,
rsDocTypeElement, rsDocTypeEqual, rsDocTypeQuoteAttrValue,
rsDocTypeQuoteEntityRef}
);
TXmlCodeFoldBlockType = (
cfbtXmlNone,
cfbtXmlNode, // <foo>...</node>
cfbtXmlComment, // <!-- -->
cfbtXmlCData, // <![CDATA[ ]]>
cfbtXmlDocType, // <!DOCTYPE
cfbtXmlProcess // <?
);
const
CountXmlCodeFoldBlockOffset: Pointer =
Pointer(PtrInt(Integer(high(TXmlCodeFoldBlockType))+1));
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
function GetXmlRangeInfo(Index: Integer): TSynXmlRangeInfo;
procedure SetXmlRangeInfo(Index: Integer; const AValue: TSynXmlRangeInfo);
protected
procedure SetCapacity(const AValue: Integer); override;
function ItemSize: Integer; override;
public
procedure Move(AFrom, ATo, ALen: Integer); override;
property XmlRangeInfo[Index: Integer]: TSynXmlRangeInfo
read GetXmlRangeInfo write SetXmlRangeInfo;
end;
TProcTableProc = procedure of object;
{ TSynXMLSyn }
TSynXMLSyn = class(TSynCustomFoldHighlighter)
private
fRange: TRangeState;
FXmlRangeInfo: TSynXmlRangeInfo;
FXmlRangeInfoChanged: Boolean;
FXmlRangeInfoOpenPos: integer;
FXmlRangeInfoClosePos: integer;
fLine: PChar;
Run: Longint;
fTokenPos: Integer;
fTokenID: TtkTokenKind;
fLineNumber: Integer;
fElementAttri: TSynHighlighterAttributes;
fSpaceAttri: TSynHighlighterAttributes;
fTextAttri: TSynHighlighterAttributes;
fEntityRefAttri: TSynHighlighterAttributes;
fProcessingInstructionAttri: TSynHighlighterAttributes;
fCDATAAttri: TSynHighlighterAttributes;
fCommentAttri: TSynHighlighterAttributes;
fDocTypeAttri: TSynHighlighterAttributes;
fAttributeAttri: TSynHighlighterAttributes;
fnsAttributeAttri: TSynHighlighterAttributes;
fAttributeValueAttri: TSynHighlighterAttributes;
fnsAttributeValueAttri: TSynHighlighterAttributes;
fSymbolAttri: TSynHighlighterAttributes;
fProcTable: array[#0..#255] of TProcTableProc;
FWantBracesParsed: Boolean;
FFoldConfig: Array [TXmlCodeFoldBlockType] of TSynCustomFoldConfig;
procedure NullProc;
procedure CarriageReturnProc;
procedure LineFeedProc;
procedure SpaceProc;
procedure LessThanProc;
procedure GreaterThanProc;
procedure CommentProc;
procedure ProcessingInstructionProc;
procedure DocTypeProc;
procedure CDATAProc;
procedure TextProc;
procedure ElementProc;
procedure AttributeProc;
procedure QAttributeValueProc;
procedure AAttributeValueProc;
procedure EqualProc;
procedure IdentProc;
procedure MakeMethodTables;
function NextTokenIs(T: String): Boolean;
procedure EntityRefProc;
procedure QEntityRefProc;
procedure AEntityRefProc;
procedure InitFoldConfig;
procedure DestroyFoldConfig;
protected
function UpdateRangeInfoAtLine(Index: Integer): Boolean; override; // Returns true if range changed
function GetIdentChars: TSynIdentChars; override;
function GetSampleSource : String; override;
protected
// folding
procedure CreateRootCodeFoldBlock; override;
function CreateRangeList: TSynHighlighterRangeList; override;
function StartXmlCodeFoldBlock(ABlockType: TXmlCodeFoldBlockType): TSynCustomCodeFoldBlock;
function StartXmlNodeCodeFoldBlock(ABlockType: TXmlCodeFoldBlockType;
OpenPos: Integer; AName: String): TSynCustomCodeFoldBlock;
procedure EndXmlCodeFoldBlock;
procedure EndXmlNodeCodeFoldBlock(ClosePos: Integer = -1; AName: String = '');
function TopXmlCodeFoldBlockType(DownIndex: Integer = 0): TXmlCodeFoldBlockType;
protected
function GetFoldConfig(Index: Integer): TSynCustomFoldConfig; override;
function GetFoldConfigCount: Integer; override;
procedure SetFoldConfig(Index: Integer; const AValue: TSynCustomFoldConfig); override;
public
{$IFNDEF SYN_CPPB_1} class {$ENDIF}
function GetLanguageName: string; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function GetDefaultAttribute(Index: integer): TSynHighlighterAttributes;
override;
function GetEol: Boolean; override;
function GetRange: Pointer; override;
function GetTokenID: TtkTokenKind;
procedure SetLine({$IFDEF FPC}const {$ENDIF}NewValue: string; LineNumber:Integer); override;
function GetToken: string; override;
{$IFDEF SYN_LAZARUS}
procedure GetTokenEx(out TokenStart: PChar; out TokenLength: integer); override;
{$ENDIF}
function GetTokenAttribute: TSynHighlighterAttributes; override;
function GetTokenKind: integer; override;
function GetTokenPos: Integer; override;
procedure Next; override;
procedure SetRange(Value: Pointer); override;
procedure ReSetRange; override;
property IdentChars;
public
// folding
function FoldOpenCount(ALineIndex: Integer; AType: Integer = 0): integer; override;
function FoldCloseCount(ALineIndex: Integer; AType: Integer = 0): integer; override;
function FoldNestCount(ALineIndex: Integer; AType: Integer = 0): integer; override;
function FoldLineLength(ALineIndex, FoldIndex: Integer): integer; override;
// TODO: make private
function MinimumFoldLevel(ALineIndex: Integer): integer; override;
function EndFoldLevel(ALineIndex: Integer): integer; override;
published
property ElementAttri: TSynHighlighterAttributes read fElementAttri
write fElementAttri;
property AttributeAttri: TSynHighlighterAttributes read fAttributeAttri
write fAttributeAttri;
property NamespaceAttributeAttri: TSynHighlighterAttributes
read fnsAttributeAttri write fnsAttributeAttri;
property AttributeValueAttri: TSynHighlighterAttributes
read fAttributeValueAttri write fAttributeValueAttri;
property NamespaceAttributeValueAttri: TSynHighlighterAttributes
read fnsAttributeValueAttri write fnsAttributeValueAttri;
property TextAttri: TSynHighlighterAttributes read fTextAttri
write fTextAttri;
property CDATAAttri: TSynHighlighterAttributes read fCDATAAttri
write fCDATAAttri;
property EntityRefAttri: TSynHighlighterAttributes read fEntityRefAttri
write fEntityRefAttri;
property ProcessingInstructionAttri: TSynHighlighterAttributes
read fProcessingInstructionAttri write fProcessingInstructionAttri;
property CommentAttri: TSynHighlighterAttributes read fCommentAttri
write fCommentAttri;
property DocTypeAttri: TSynHighlighterAttributes read fDocTypeAttri
write fDocTypeAttri;
property SpaceAttri: TSynHighlighterAttributes read fSpaceAttri
write fSpaceAttri;
property SymbolAttri: TSynHighlighterAttributes read fSymbolAttri
write fSymbolAttri;
property WantBracesParsed : Boolean read FWantBracesParsed
write FWantBracesParsed default True;
end;
implementation
uses
SynEditStrConst;
const
NameChars : set of char = ['0'..'9', 'a'..'z', 'A'..'Z', '_', '.', ':', '-'];
MaxFoldNestDeep = 500;
constructor TSynXMLSyn.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
InitFoldConfig;
fElementAttri:= TSynHighlighterAttributes.Create(SYNS_AttrElementName, SYNS_XML_AttrElementName);
fTextAttri:= TSynHighlighterAttributes.Create(SYNS_AttrText, SYNS_XML_AttrText);
fSpaceAttri:= TSynHighlighterAttributes.Create(SYNS_AttrWhitespace, SYNS_XML_AttrWhitespace);
fEntityRefAttri:= TSynHighlighterAttributes.Create(SYNS_AttrEntityReference, SYNS_XML_AttrEntityReference);
fProcessingInstructionAttri:= TSynHighlighterAttributes.Create(SYNS_AttrProcessingInstr, SYNS_XML_AttrProcessingInstr);
fCDATAAttri:= TSynHighlighterAttributes.Create(SYNS_AttrCDATASection, SYNS_XML_AttrCDATASection);
fCommentAttri:= TSynHighlighterAttributes.Create(SYNS_AttrComment, SYNS_XML_AttrComment);
fDocTypeAttri:= TSynHighlighterAttributes.Create(SYNS_AttrDOCTYPESection, SYNS_XML_AttrDOCTYPESection);
fAttributeAttri:= TSynHighlighterAttributes.Create(SYNS_AttrAttributeName, SYNS_XML_AttrAttributeName);
fnsAttributeAttri:= TSynHighlighterAttributes.Create(SYNS_AttrNamespaceAttrName, SYNS_XML_AttrNamespaceAttrName);
fAttributeValueAttri:= TSynHighlighterAttributes.Create(SYNS_AttrAttributeValue, SYNS_XML_AttrAttributeValue);
fnsAttributeValueAttri:= TSynHighlighterAttributes.Create(SYNS_AttrNamespaceAttrValue, SYNS_XML_AttrNamespaceAttrValue);
fSymbolAttri:= TSynHighlighterAttributes.Create(SYNS_AttrSymbol, SYNS_XML_AttrSymbol);
fElementAttri.Foreground:= clMaroon;
fElementAttri.Style:= [fsBold];
fDocTypeAttri.Foreground:= clblue;
fDocTypeAttri.Style:= [fsItalic];
fCDATAAttri.Foreground:= clOlive;
fCDATAAttri.Style:= [fsItalic];
fEntityRefAttri.Foreground:= clblue;
fEntityRefAttri.Style:= [fsbold];
fProcessingInstructionAttri.Foreground:= clblue;
fProcessingInstructionAttri.Style:= [];
fTextAttri.Foreground:= clBlack;
fTextAttri.Style:= [fsBold];
fAttributeAttri.Foreground:= clMaroon;
fAttributeAttri.Style:= [];
fnsAttributeAttri.Foreground:= clRed;
fnsAttributeAttri.Style:= [];
fAttributeValueAttri.Foreground:= clNavy;
fAttributeValueAttri.Style:= [fsBold];
fnsAttributeValueAttri.Foreground:= clRed;
fnsAttributeValueAttri.Style:= [fsBold];
fCommentAttri.Background:= clSilver;
fCommentAttri.Foreground:= clGray;
fCommentAttri.Style:= [fsbold, fsItalic];
fSymbolAttri.Foreground:= clblue;
fSymbolAttri.Style:= [];
AddAttribute(fSymbolAttri);
AddAttribute(fProcessingInstructionAttri);
AddAttribute(fDocTypeAttri);
AddAttribute(fCommentAttri);
AddAttribute(fElementAttri);
AddAttribute(fAttributeAttri);
AddAttribute(fnsAttributeAttri);
AddAttribute(fAttributeValueAttri);
AddAttribute(fnsAttributeValueAttri);
AddAttribute(fEntityRefAttri);
AddAttribute(fCDATAAttri);
AddAttribute(fSpaceAttri);
AddAttribute(fTextAttri);
SetAttributesOnChange({$IFDEF FPC}@{$ENDIF}DefHighlightChange);
MakeMethodTables;
fRange := rsText;
fDefaultFilter := SYNS_FilterXML;
end;
destructor TSynXMLSyn.Destroy;
begin
DestroyFoldConfig;
inherited Destroy;
end;
procedure TSynXMLSyn.MakeMethodTables;
var
i: Char;
begin
for i:= #0 To #255 do begin
case i of
#0:
begin
fProcTable[i] := {$IFDEF FPC}@{$ENDIF}NullProc;
end;
#10:
begin
fProcTable[i] := {$IFDEF FPC}@{$ENDIF}LineFeedProc;
end;
#13:
begin
fProcTable[i] := {$IFDEF FPC}@{$ENDIF}CarriageReturnProc;
end;
#1..#9, #11, #12, #14..#32:
begin
fProcTable[i] := {$IFDEF FPC}@{$ENDIF}SpaceProc;
end;
'<':
begin
fProcTable[i] := {$IFDEF FPC}@{$ENDIF}LessThanProc;
end;
'>':
begin
fProcTable[i] := {$IFDEF FPC}@{$ENDIF}GreaterThanProc;
end;
else
fProcTable[i] := {$IFDEF FPC}@{$ENDIF}IdentProc;
end;
end;
end;
procedure TSynXMLSyn.SetLine({$IFDEF FPC}const {$ENDIF}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;
fLine := PChar(NewValue);
Run := 0;
fLineNumber := LineNumber;
Next;
end;
procedure TSynXMLSyn.NullProc;
begin
fTokenID := tkNull;
end;
procedure TSynXMLSyn.CarriageReturnProc;
begin
fTokenID := tkSpace;
Inc(Run);
if fLine[Run] = #10 then Inc(Run);
end;
procedure TSynXMLSyn.LineFeedProc;
begin
fTokenID := tkSpace;
Inc(Run);
end;
procedure TSynXMLSyn.SpaceProc;
begin
Inc(Run);
fTokenID := tkSpace;
while fLine[Run] <= #32 do begin
if fLine[Run] in [#0, #9, #10, #13] then break;
Inc(Run);
end;
end;
procedure TSynXMLSyn.LessThanProc;
begin
Inc(Run);
if (fLine[Run] = '/') then begin
Inc(Run);
fTokenID := tkSymbol;
fRange := rsCloseElement;
exit;
end;
if (fLine[Run] = '!') then
begin
if NextTokenIs('--') then begin
fTokenID := tkSymbol;
fRange := rsComment;
StartXmlCodeFoldBlock(cfbtXmlComment);
Inc(Run, 3);
end else if NextTokenIs('DOCTYPE') then begin
fTokenID := tkDocType;
fRange := rsDocType;
StartXmlCodeFoldBlock(cfbtXmlDocType);
Inc(Run, 7);
end else if NextTokenIs('[CDATA[') then begin
fTokenID := tkCDATA;
fRange := rsCDATA;
StartXmlCodeFoldBlock(cfbtXmlCData);
Inc(Run, 7);
end else begin
fTokenID := tkSymbol;
fRange := rsElement;
Inc(Run);
end;
end else if fLine[Run]= '?' then begin
fTokenID := tkProcessingInstruction;
fRange := rsProcessingInstruction;
StartXmlCodeFoldBlock(cfbtXmlProcess);
Inc(Run);
end else begin
fTokenID := tkSymbol;
fRange := rsOpenElement;
end;
end;
procedure TSynXMLSyn.GreaterThanProc;
begin
if (Run > 0) and (fLine[Run - 1] = '/') then
if TopXmlCodeFoldBlockType = cfbtXmlNode then
EndXmlNodeCodeFoldBlock;
fTokenId := tkSymbol;
fRange:= rsText;
Inc(Run);
end;
procedure TSynXMLSyn.CommentProc;
begin
if (fLine[Run] = '-') and (fLine[Run + 1] = '-') and
(fLine[Run + 2] = '>')
then begin
fTokenID := tkSymbol;
fRange:= rsText;
Inc(Run, 3);
if TopXmlCodeFoldBlockType = cfbtXmlComment then
EndXmlCodeFoldBlock;
Exit;
end;
fTokenID := tkComment;
if (fLine[Run] In [#0, #10, #13]) then begin
fProcTable[fLine[Run]]{$IFDEF FPC}(){$ENDIF};
Exit;
end;
while not (fLine[Run] in [#0, #10, #13]) do begin
if (fLine[Run] = '-') and (fLine[Run + 1] = '-') and (fLine[Run + 2] = '>')
then begin
fRange := rsComment;
break;
end;
Inc(Run);
end;
end;
procedure TSynXMLSyn.ProcessingInstructionProc;
begin
fTokenID := tkProcessingInstruction;
if (fLine[Run] In [#0, #10, #13]) then begin
fProcTable[fLine[Run]]{$IFDEF FPC}(){$ENDIF};
Exit;
end;
while not (fLine[Run] in [#0, #10, #13]) do begin
if (fLine[Run] = '>') and (fLine[Run - 1] = '?')
then begin
fRange := rsText;
Inc(Run);
if TopXmlCodeFoldBlockType = cfbtXmlProcess then
EndXmlCodeFoldBlock;
break;
end;
Inc(Run);
end;
end;
procedure TSynXMLSyn.DocTypeProc; //ek 2001-11-11
begin
fTokenID := tkDocType;
if (fLine[Run] In [#0, #10, #13]) then begin
fProcTable[fLine[Run]]{$IFDEF FPC}(){$ENDIF};
Exit;
end;
case fRange of
rsDocType:
begin
while not (fLine[Run] in [#0, #10, #13]) do
begin
case fLine[Run] of
'[': begin
while True do
begin
inc(Run);
case fLine[Run] of
']':
begin
Inc(Run);
Exit;
end;
#0, #10, #13:
begin
fRange:=rsDocTypeSquareBraces;
Exit;
end;
end;
end;
end;
'>': begin
fRange := rsAttribute;
if TopXmlCodeFoldBlockType = cfbtXmlDocType then
EndXmlCodeFoldBlock;
Inc(Run);
Break;
end;
end;
inc(Run);
end;
end;
rsDocTypeSquareBraces:
begin
while not (fLine[Run] in [#0, #10, #13]) do
begin
if (fLine[Run]=']') then
begin
fRange := rsDocType;
Inc(Run);
Exit;
end;
inc(Run);
end;
end;
end;
end;
procedure TSynXMLSyn.CDATAProc;
begin
fTokenID := tkCDATA;
if (fLine[Run] In [#0, #10, #13]) then
begin
fProcTable[fLine[Run]]{$IFDEF FPC}(){$ENDIF};
Exit;
end;
while not (fLine[Run] in [#0, #10, #13]) do
begin
if (Run >= 2) and (fLine[Run] = '>') and (fLine[Run - 1] = ']') and
(fLine[Run - 2] = ']')
then begin
fRange := rsText;
Inc(Run);
if TopXmlCodeFoldBlockType = cfbtXmlCData then
EndXmlCodeFoldBlock;
break;
end;
Inc(Run);
end;
end;
procedure TSynXMLSyn.ElementProc;
var
NameStart: LongInt;
begin
if fLine[Run] = '/' then
Inc(Run);
NameStart := Run;
while (fLine[Run] in NameChars) do Inc(Run);
if fRange = rsOpenElement then
StartXmlNodeCodeFoldBlock(cfbtXmlNode, NameStart, Copy(fLine, NameStart + 1, Run - NameStart));
if fRange = rsCloseElement then
EndXmlNodeCodeFoldBlock(NameStart, Copy(fLine, NameStart + 1, Run - NameStart)); // TODO: defer until ">" reached
fRange := rsAttribute;
fTokenID := tkElement;
end;
procedure TSynXMLSyn.AttributeProc;
begin
//Check if we are starting on a closing quote
if (fLine[Run] in [#34, #39]) then
begin
fTokenID := tkSymbol;
fRange := rsAttribute;
Inc(Run);
Exit;
end;
//Read the name
while (fLine[Run] in NameChars) do Inc(Run);
//Check if this is an xmlns: attribute
if (Pos('xmlns', GetToken) > 0) then begin
fTokenID := tknsAttribute;
fRange := rsnsEqual;
end else begin
fTokenID := tkAttribute;
fRange := rsEqual;
end;
end;
procedure TSynXMLSyn.EqualProc;
begin
if fRange = rsnsEqual then
fTokenID := tknsEqual
else
fTokenID := tkEqual;
while not (fLine[Run] in [#0, #10, #13]) do
begin
if (fLine[Run] = '/') then
begin
fTokenID := tkSymbol;
fRange := rsElement;
Inc(Run);
Exit;
end else if (fLine[Run] = #34) then
begin
if fRange = rsnsEqual then
fRange := rsnsQuoteAttrValue
else
fRange := rsQuoteAttrValue;
Inc(Run);
Exit;
end else if (fLine[Run] = #39) then
begin
if fRange = rsnsEqual then
fRange := rsnsAPosAttrValue
else
fRange := rsAPosAttrValue;
Inc(Run);
Exit;
end;
Inc(Run);
end;
end;
procedure TSynXMLSyn.QAttributeValueProc;
begin
if fRange = rsnsQuoteAttrValue then
fTokenID := tknsQuoteAttrValue
else
fTokenID := tkQuoteAttrValue;
while not (fLine[Run] in [#0, #10, #13, '&', #34]) do Inc(Run);
if fLine[Run] = '&' then
begin
if fRange = rsnsQuoteAttrValue then
fRange := rsnsQuoteEntityRef
else
fRange := rsQuoteEntityRef;
Exit;
end else if fLine[Run] <> #34 then
begin
Exit;
end;
fRange := rsAttribute;
end;
procedure TSynXMLSyn.AAttributeValueProc;
begin
if fRange = rsnsAPosAttrValue then
fTokenID := tknsAPosAttrValue
else
fTokenID := tkAPosAttrValue;
while not (fLine[Run] in [#0, #10, #13, '&', #39]) do Inc(Run);
if fLine[Run] = '&' then
begin
if fRange = rsnsAPosAttrValue then
fRange := rsnsAPosEntityRef
else
fRange := rsAPosEntityRef;
Exit;
end else if fLine[Run] <> #39 then
begin
Exit;
end;
fRange := rsAttribute;
end;
procedure TSynXMLSyn.TextProc;
const StopSet = [#0..#31, '<', '&'];
begin
if fLine[Run] in (StopSet - ['&']) then begin
fProcTable[fLine[Run]]{$IFDEF FPC}(){$ENDIF};
exit;
end;
fTokenID := tkText;
while not (fLine[Run] in StopSet) do Inc(Run);
if (fLine[Run] = '&') then begin
fRange := rsEntityRef;
Exit;
end;
end;
procedure TSynXMLSyn.EntityRefProc;
begin
fTokenID := tkEntityRef;
fRange := rsEntityRef;
while not (fLine[Run] in [#0..#32, ';']) do Inc(Run);
if (fLine[Run] = ';') then Inc(Run);
fRange := rsText;
end;
procedure TSynXMLSyn.QEntityRefProc;
begin
if fRange = rsnsQuoteEntityRef then
fTokenID := tknsQuoteEntityRef
else
fTokenID := tkQuoteEntityRef;
while not (fLine[Run] in [#0..#32, ';']) do Inc(Run);
if (fLine[Run] = ';') then Inc(Run);
if fRange = rsnsQuoteEntityRef then
fRange := rsnsQuoteAttrValue
else
fRange := rsQuoteAttrValue;
end;
procedure TSynXMLSyn.AEntityRefProc;
begin
if fRange = rsnsAPosEntityRef then
fTokenID := tknsAPosEntityRef
else
fTokenID := tkAPosEntityRef;
while not (fLine[Run] in [#0..#32, ';']) do Inc(Run);
if (fLine[Run] = ';') then Inc(Run);
if fRange = rsnsAPosEntityRef then
fRange := rsnsAPosAttrValue
else
fRange := rsAPosAttrValue;
end;
procedure TSynXMLSyn.InitFoldConfig;
var
i: TXmlCodeFoldBlockType;
begin
for i := low(TXmlCodeFoldBlockType) to high(TXmlCodeFoldBlockType) do begin
FFoldConfig[i] := TSynCustomFoldConfig.Create;
FFoldConfig[i].OnChange := @DoFoldConfigChanged;
FFoldConfig[i].Enabled := True;
end;
end;
procedure TSynXMLSyn.DestroyFoldConfig;
var
i: TXmlCodeFoldBlockType;
begin
for i := low(TXmlCodeFoldBlockType) to high(TXmlCodeFoldBlockType) do
FFoldConfig[i].Free;
end;
function TSynXMLSyn.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 TSynXMLSyn.IdentProc;
begin
case fRange of
rsElement, rsOpenElement, rsCloseElement:
begin
ElementProc{$IFDEF FPC}(){$ENDIF};
end;
rsAttribute:
begin
AttributeProc{$IFDEF FPC}(){$ENDIF};
end;
rsEqual, rsnsEqual:
begin
EqualProc{$IFDEF FPC}(){$ENDIF};
end;
rsQuoteAttrValue, rsnsQuoteAttrValue:
begin
QAttributeValueProc{$IFDEF FPC}(){$ENDIF};
end;
rsAposAttrValue, rsnsAPosAttrValue:
begin
AAttributeValueProc{$IFDEF FPC}(){$ENDIF};
end;
rsQuoteEntityRef, rsnsQuoteEntityRef:
begin
QEntityRefProc{$IFDEF FPC}(){$ENDIF};
end;
rsAposEntityRef, rsnsAPosEntityRef:
begin
AEntityRefProc{$IFDEF FPC}(){$ENDIF};
end;
rsEntityRef:
begin
EntityRefProc{$IFDEF FPC}(){$ENDIF};
end;
else ;
end;
end;
procedure TSynXMLSyn.Next;
begin
fTokenPos := Run;
case fRange of
rsText:
begin
TextProc{$IFDEF FPC}(){$ENDIF};
end;
rsComment:
begin
CommentProc{$IFDEF FPC}(){$ENDIF};
end;
rsProcessingInstruction:
begin
ProcessingInstructionProc{$IFDEF FPC}(){$ENDIF};
end;
rsDocType, rsDocTypeSquareBraces: //ek 2001-11-11
begin
DocTypeProc{$IFDEF FPC}(){$ENDIF};
end;
rsCDATA:
begin
CDATAProc{$IFDEF FPC}(){$ENDIF};
end;
else
fProcTable[fLine[Run]]{$IFDEF FPC}(){$ENDIF};
end;
end;
function TSynXMLSyn.NextTokenIs(T : String) : Boolean;
var I, Len : Integer;
begin
Result:= True;
Len:= Length(T);
for I:= 1 to Len do
if (fLine[Run + I] <> T[I]) then
begin
Result:= False;
Break;
end;
end;
function TSynXMLSyn.GetDefaultAttribute(
Index: integer): TSynHighlighterAttributes;
begin
case Index of
SYN_ATTR_COMMENT: Result := fCommentAttri;
SYN_ATTR_IDENTIFIER: Result := fAttributeAttri;
SYN_ATTR_KEYWORD: Result := fElementAttri;
SYN_ATTR_WHITESPACE: Result := fSpaceAttri;
SYN_ATTR_SYMBOL: Result := fSymbolAttri;
else
Result := nil;
end;
end;
function TSynXMLSyn.GetEol: Boolean;
begin
Result := fTokenId = tkNull;
end;
function TSynXMLSyn.GetToken: string;
var
len: Longint;
begin
Result := '';
Len := (Run - fTokenPos);
SetString(Result, (FLine + fTokenPos), len);
end;
{$IFDEF SYN_LAZARUS}
procedure TSynXMLSyn.GetTokenEx(out TokenStart: PChar;
out TokenLength: integer);
begin
TokenLength:=Run-fTokenPos;
TokenStart:=FLine + fTokenPos;
end;
{$ENDIF}
function TSynXMLSyn.GetTokenID: TtkTokenKind;
begin
Result := fTokenId;
end;
function TSynXMLSyn.GetTokenAttribute: TSynHighlighterAttributes;
begin
case fTokenID of
tkElement: Result:= fElementAttri;
tkAttribute: Result:= fAttributeAttri;
tknsAttribute: Result:= fnsAttributeAttri;
tkEqual: Result:= fSymbolAttri;
tknsEqual: Result:= fSymbolAttri;
tkQuoteAttrValue: Result:= fAttributeValueAttri;
tkAPosAttrValue: Result:= fAttributeValueAttri;
tknsQuoteAttrValue: Result:= fnsAttributeValueAttri;
tknsAPosAttrValue: Result:= fnsAttributeValueAttri;
tkText: Result:= fTextAttri;
tkCDATA: Result:= fCDATAAttri;
tkEntityRef: Result:= fEntityRefAttri;
tkQuoteEntityRef: Result:= fEntityRefAttri;
tkAposEntityRef: Result:= fEntityRefAttri;
tknsQuoteEntityRef: Result:= fEntityRefAttri;
tknsAposEntityRef: Result:= fEntityRefAttri;
tkProcessingInstruction: Result:= fProcessingInstructionAttri;
tkComment: Result:= fCommentAttri;
tkDocType: Result:= fDocTypeAttri;
tkSymbol: Result:= fSymbolAttri;
tkSpace: Result:= fSpaceAttri;
else
Result := nil;
end;
end;
function TSynXMLSyn.GetTokenKind: integer;
begin
Result := Ord(fTokenId);
end;
function TSynXMLSyn.GetTokenPos: Integer;
begin
Result := fTokenPos;
end;
function TSynXMLSyn.GetRange: Pointer;
begin
CodeFoldRange.RangeType:=Pointer(PtrUInt(Integer(fRange)));
Result := inherited;
end;
procedure TSynXMLSyn.SetRange(Value: Pointer);
begin
inherited;
fRange := TRangeState(Integer(PtrUInt(CodeFoldRange.RangeType)));
end;
procedure TSynXMLSyn.ReSetRange;
begin
inherited;
fRange:= rsText;
end;
function TSynXMLSyn.FoldOpenCount(ALineIndex: Integer; AType: Integer): integer;
begin
Result := EndFoldLevel(ALineIndex) - MinimumFoldLevel(ALineIndex);
end;
function TSynXMLSyn.FoldCloseCount(ALineIndex: Integer; AType: Integer): integer;
begin
Result := EndFoldLevel(ALineIndex - 1) - MinimumFoldLevel(ALineIndex);
end;
function TSynXMLSyn.FoldNestCount(ALineIndex: Integer; AType: Integer): integer;
begin
Result := EndFoldLevel(ALineIndex);
end;
function TSynXMLSyn.FoldLineLength(ALineIndex, FoldIndex: Integer): integer;
var
i, lvl, cnt: Integer;
e, m: Integer;
begin
//atype := FoldTypeAtNodeIndex(ALineIndex, FoldIndex);
cnt := CurrentLines.Count;
e := EndFoldLevel(ALineIndex);
m := MinimumFoldLevel(ALineIndex);
lvl := Min(m+1+FoldIndex, e);
i := ALineIndex + 1;
while (i < cnt) and (MinimumFoldLevel(i) >= lvl) do inc(i);
// check if fold last line of block (not mixed "end begin")
// and not lastlinefix
if (i = cnt) or (EndFoldLevel(i) > MinimumFoldLevel(i)) then
dec(i);
// Amount of lines, that will become invisible (excludes the cfCollapsed line)
Result := i - ALineIndex;
end;
function TSynXMLSyn.MinimumFoldLevel(ALineIndex: Integer): integer;
var
r: TSynCustomHighlighterRange;
begin
if (ALineIndex < 0) or (ALineIndex >= CurrentLines.Count) then
exit(0);
r := TSynCustomHighlighterRange(CurrentRanges[ALineIndex]);
if (r <> nil) and (Pointer(r) <> NullRange) then
Result := r.MinimumCodeFoldBlockLevel
else
Result := 0;
end;
function TSynXMLSyn.EndFoldLevel(ALineIndex: Integer): integer;
var
r: TSynCustomHighlighterRange;
begin
if (ALineIndex < 0) or (ALineIndex >= CurrentLines.Count) then
exit(0);
r := TSynCustomHighlighterRange(CurrentRanges[ALineIndex]);
if (r <> nil) and (Pointer(r) <> NullRange) then
Result := r.CodeFoldStackSize
else
Result := 0;
end;
function TSynXMLSyn.GetIdentChars: TSynIdentChars;
begin
Result := ['0'..'9', 'a'..'z', 'A'..'Z', '_', '.', '-'] + TSynSpecialChars;
end;
{$IFNDEF SYN_CPPB_1} class {$ENDIF}
function TSynXMLSyn.GetLanguageName: string;
begin
Result := SYNS_LangXML;
end;
function TSynXMLSyn.GetSampleSource: String;
begin
Result:= '<?xml version="1.0"?>'#13#10+
'<!DOCTYPE root ['#13#10+
' ]>'#13#10+
'<!-- Comment -->'#13#10+
'<root version="&test;">'#13#10+
' <![CDATA[ **CDATA section** ]]>'#13#10+
'</root>';
end;
procedure TSynXMLSyn.CreateRootCodeFoldBlock;
begin
inherited CreateRootCodeFoldBlock;
RootCodeFoldBlock.InitRootBlockType(Pointer(PtrInt(cfbtXmlNone)));
end;
function TSynXMLSyn.CreateRangeList: TSynHighlighterRangeList;
begin
Result := TSynHighlighterXmlRangeList.Create;
end;
function TSynXMLSyn.StartXmlCodeFoldBlock(ABlockType: TXmlCodeFoldBlockType): TSynCustomCodeFoldBlock;
var
FoldBlock: Boolean;
p: PtrInt;
begin
FoldBlock := FFoldConfig[ABlockType].Enabled;
p := 0;
if not FoldBlock then
p := PtrInt(CountXmlCodeFoldBlockOffset);
Result := StartCodeFoldBlock(p + Pointer(PtrInt(ABlockType)), FoldBlock);
end;
function TSynXMLSyn.StartXmlNodeCodeFoldBlock(ABlockType: TXmlCodeFoldBlockType;
OpenPos: Integer; AName: String): TSynCustomCodeFoldBlock;
var
i: Integer;
begin
if not FFoldConfig[cfbtXmlNode].Enabled then exit;
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 TSynXMLSyn.EndXmlCodeFoldBlock;
var
DecreaseLevel: Boolean;
begin
DecreaseLevel := TopCodeFoldBlockType < CountXmlCodeFoldBlockOffset;
EndCodeFoldBlock(DecreaseLevel);
end;
procedure TSynXMLSyn.EndXmlNodeCodeFoldBlock(ClosePos: Integer = -1; AName: String = '');
var
cnt, i, k, lvl: Integer;
LInfo: Array of String;
begin
if not FFoldConfig[cfbtXmlNode].Enabled then exit;
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 := EndFoldLevel(i);
while i >= 0 do begin
if MinimumFoldLevel(i) < lvl then begin
LInfo := TSynHighlighterXmlRangeList(CurrentRanges).XmlRangeInfo[i].ElementOpenList;
k := length(LInfo) - Max(EndFoldLevel(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;
function TSynXMLSyn.TopXmlCodeFoldBlockType(DownIndex: Integer): TXmlCodeFoldBlockType;
begin
Result := TXmlCodeFoldBlockType(PtrUInt(TopCodeFoldBlockType(DownIndex)));
end;
function TSynXMLSyn.GetFoldConfig(Index: Integer): TSynCustomFoldConfig;
begin
// + 1 as we skip cfbtNone;
Result := FFoldConfig[TXmlCodeFoldBlockType(Index + 1)];
end;
function TSynXMLSyn.GetFoldConfigCount: Integer;
begin
// excluded cfbtNone;
Result := ord(high(TXmlCodeFoldBlockType)) - ord(low(TXmlCodeFoldBlockType));
end;
procedure TSynXMLSyn.SetFoldConfig(Index: Integer; const AValue: TSynCustomFoldConfig);
begin
BeginUpdate;
FFoldConfig[TXmlCodeFoldBlockType(Index + 1)].Assign(AValue);
EndUpdate;
// Todo: Since all synedits will rescan => delete all foldranges
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] + inherited ItemSize)^);
end;
procedure TSynHighlighterXmlRangeList.SetXmlRangeInfo(Index: Integer;
const AValue: TSynXmlRangeInfo);
begin
TSynXmlRangeInfo((ItemPointer[Index] + inherited ItemSize)^) := AValue;
end;
procedure TSynHighlighterXmlRangeList.SetCapacity(const AValue: Integer);
var
i: LongInt;
begin
for i := AValue to Capacity-1 do
with TSynXmlRangeInfo((ItemPointer[i] + inherited ItemSize)^) do begin
ElementOpenList := nil;
ElementCloseList := nil;
end;
inherited SetCapacity(AValue);
end;
function TSynHighlighterXmlRangeList.ItemSize: Integer;
begin
Result := inherited ItemSize + 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] + inherited ItemSize)^) do begin
ElementOpenList := nil;
ElementCloseList := nil;
end
else
for i:= ATo to Min(ATo + ALen , AFrom) - 1 do // move backward
with TSynXmlRangeInfo((ItemPointer[i] + inherited ItemSize)^) do begin
ElementOpenList := nil;
ElementCloseList := nil;
end;
inherited Move(AFrom, ATo, ALen);
end;
initialization
{$IFNDEF SYN_CPPB_1}
RegisterPlaceableHighlighter(TSynXMLSyn);
{$ENDIF}
end.