doublecmd/components/synunihighlighter/source/SynUniHighlighter.pas
2021-07-29 23:38:47 +03:00

1209 lines
40 KiB
ObjectPascal
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
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: SynUniHighlighter.pas, released 2003-01
All Rights Reserved.
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.
}{
@abstract(Provides a universal highlighter for SynEdit)
@authors(Fantasist [walking_in_the_sky@yahoo.com], Vit [nevzorov@yahoo.com],
Vitalik [vetal-x@mail.ru])
@created(2003)
@lastmod(2004-05-12)
}
(******************************************************************************
Authors: Fantasist (Kirill Burtsev walking_in_the_sky@yahoo.com)
Vit (Vitaly Nevzorov nevzorov@yahoo.com)
Vitalik (Vitaly Lyapota vetal-x@mail.ru)
Official Site: www.delphist.com
With all questions, please visit www.delphist.com/forum
******************************************************************************)
unit SynUniHighlighter;
{$mode delphi}
interface
uses
SysUtils, Classes, Graphics,
SynEditTypes, SynEditHighlighter, SynUniClasses, SynUniRules, Laz2_DOM;
Const
_Root = 'Root';
_New = 'New';
type
{ TSynUniSyn }
TSynUniSyn = class(TSynCustomHighlighter)
private
procedure ReadSyntax(Reader: TReader);
procedure WriteSyntax(Writer: TWriter);
protected
fMainRules: TSynRange;
fEol: boolean;
fPrEol: boolean;
fLine: PChar;
fTrueLine: String;
fLineNumber: Integer;
Run: LongInt;
fTokenPos: Integer;
fCurrToken: TSynSymbol;
fCurrentRule: TSynRange;
SymbolList: array[char] of TAbstractSymbol; //???
fPrepared: boolean;
fSchemes: TStringList; //Vitalik 2004
fSchemeIndex: integer; //Vitalik 2004
fImportFormats: TList;
procedure SpaceProc;
procedure NullProc;
function GetIdentChars: TSynIdentChars; override;
procedure DefineProperties(Filer: TFiler); override;
function GetSampleSource: string; override;
procedure SetSampleSource(Value: string); override;
public
class function GetLanguageName: string; override;
public
constructor Create(AOwner: TComponent); overload; override;
destructor Destroy; override;
function GetDefaultAttribute(Index: integer): TSynHighlighterAttributes;
override; {Abstract}
function GetEol: Boolean; override; {Abstract}
function GetRange: Pointer; override;
function GetToken: string; override; {Abstract}
procedure GetTokenEx(out TokenStart: PChar; out TokenLength: integer); override; {Abstract}
function GetTokenAttribute: TSynHighlighterAttributes; override; {Abstract}
function GetTokenID: Integer;
function GetTokenKind: integer; override; {Abstract}
function GetTokenPos: Integer; override; {Abstract}
function IsKeyword(const AKeyword: string): boolean; override;
procedure Next; override; {Abstract}
procedure ResetRange; override;
procedure SetLine(const NewValue: string; LineNumber: Integer); override; {Abstract}
procedure SetRange(Value: Pointer); override;
procedure Reset;
procedure Clear;
procedure Prepare;
procedure CreateStandardRules;
procedure ReadSchemes(xml: TDOMNode);
procedure ReadInfo(xml: TDOMNode);
procedure LoadHglFromXml(xml: TDOMNode);
procedure LoadHglFromStream(Stream: TStream);
procedure LoadHglFromFile(FileName: string);
procedure SaveHglToStream(Stream: TStream);
procedure SaveHglToFile(FileName: string);
procedure LoadFromXml(xml: TDOMNode);
procedure LoadFromStream(Stream: TStream; FreeStream: boolean = True);
procedure LoadFromFile(FileName: string);
function GetAsStream: TMemoryStream;
procedure SaveToStream(Stream: TStream; Rule: TSynRule = nil);
procedure SaveToFile(FileName: string; Rule: TSynRule = nil);
public
Info: TSynInfo;
Styles: TSynUniStyles;
SchemeFileName: string;
SchemeName: string;
property MainRules: TSynRange read fMainRules;
property SchemesList: TStringList read fSchemes write fSchemes; //Vitalik 2004
property SchemeIndex: integer read fSchemeIndex write fSchemeIndex; //Vitalik 2004
end;
implementation
uses
LazUTF8Classes, Laz2_XMLRead;
const
SYNS_AttrTest = 'Test';
//==== TSynUniSyn ============================================================
constructor TSynUniSyn.Create(AOwner: TComponent);
var
fTestAttri: TSynHighlighterAttributes;
begin
inherited Create(AOwner);
Info := TSynInfo.Create;
Info.History := TStringList.Create;
Info.Sample := TStringList.Create;
fPrepared := False;
//Вот так вот нужно все атрибуты будет добавлять! Потому как нужно еще и обработать [Underline + Italic]
fTestAttri := TSynHighLighterAttributes.Create(SYNS_AttrTest);
fTestAttri.Style := [fsUnderline, fsItalic];
fTestAttri.Foreground := clBlue;
fTestAttri.Background := clSilver;
AddAttribute(fTestAttri);
fSchemes := TStringList.Create;
fSchemeIndex := -1;
fMainRules := TSynRange.Create;
MainRules.Name := _Root;
fEol := False;
fPrEol := False;
fCurrentRule := MainRules;
// AddNewScheme('Noname');
fImportFormats := TList.Create;
end;
destructor TSynUniSyn.Destroy;
//: Destructor of TSynUniSyn
begin
MainRules.Free;
Info.History.Free;
Info.Sample.Free;
Info.Free;
fSchemes.Free;
fImportFormats.Free;
inherited;
end;
procedure TSynUniSyn.SetLine(const NewValue: string; LineNumber: Integer);
//: Set current line in SynEdit for highlighting
function HaveNodeAnyStart(Node: TSymbolNode): boolean;
var
i: integer;
begin
Result := False;
if Node.StartType = stAny then
begin
Result := True;
Exit;
end;
for i := 0 to Node.NextSymbs.Count-1 do
if (Node.NextSymbs.Nodes[i].StartType = stAny) or HaveNodeAnyStart(Node.NextSymbs.Nodes[i]) then
begin
Result := True;
Exit;
end
end;
var i: integer;
begin
if LineNumber = 1 then begin
MainRules.ResetParents(MainRules);
MainRules.ClearParsingFields();
end;
if not fCurrentRule.Prepared then begin //: If current Range isn't prepared,
Prepare; //: then prepare it and its sub-ranges
(*{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{
!!!!!!! Это я писал и это зачем-то нужно !!!!!!!!!!!!!!!!!!!!!!!!*)
for i := 0 to 255 do
if (SymbolList[char(i)] <> nil) {temp}and (TSymbols(SymbolList[char(i)]).HeadNode <> nil){/temp} then
fCurrentRule.HasNodeAnyStart[char(i)] := HaveNodeAnyStart(TSymbols(SymbolList[fCurrentRule.CaseFunct(char(i))]).HeadNode);
(*}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}*)
end;
{begin} //Vitalik 2004
{was:
fTrueLine := PChar(NewValue);
l := Length(NewValue);
ReallocMem(fLine, l+1);
for i := 0 to l do
fLine[i] := fCurrentRule.CaseFunct(fTrueLine[i]);
}
fTrueLine := NewValue;
fLine := PChar(NewValue); //: Current string of SynEdit
{end} //Vitalik 2004
Run := 0; //: Set Position of "parser" at the first char of string
fTokenPos := 0; //: Set Position of current token at the first char of string
fLineNumber := LineNumber; //: Number of current line in SynEdit
fEol := False; //: ???
fPrEol := False; //: ???
Next; //: Find first token in the line
end;
procedure TSynUniSyn.Next;
//: Goes to the next token and open/close ranges
var
ParentCycle, CurrentParent: TSynRange;
RangeLink: TSynRangeLink;
isFindSame: boolean;
begin
if fPrEol then //: if it was end of line then
begin //: if current range close on end of line then
if (fCurrentRule.fRule.fCloseOnEol) or (fCurrentRule.fRule.fCloseOnTerm) then begin
if fCurrentRule.OpenCount > 0 then
fCurrentRule.OpenCount := fCurrentRule.OpenCount - 1
else
if fCurrentRule.ParentBackup <> nil then
fCurrentRule.Parent := fCurrentRule.ParentBackup;
if fCurrentRule.fRule.fAllowPredClose then begin
fCurrentRule := fCurrentRule.Parent;
while (fCurrentRule.fRule.fCloseOnEol) or (fCurrentRule.fRule.fCloseOnTerm) do
fCurrentRule := fCurrentRule.Parent;
end else
fCurrentRule := fCurrentRule.Parent;
end;
fEol := True; //: ???
Exit;
end;
fTokenPos := Run; //: Start of cf current token is end of previsious
//: if current range close on delimeter and current symbol is delimeter then
if (fCurrentRule.fRule.fCloseOnTerm) and (fLine[Run] in fCurrentRule.fTermSymbols) then begin
if fCurrentRule.OpenCount > 0 then
fCurrentRule.OpenCount := fCurrentRule.OpenCount - 1
else
if fCurrentRule.ParentBackup <> nil then
fCurrentRule.Parent := fCurrentRule.ParentBackup;
if fCurrentRule.fRule.fAllowPredClose then begin
fCurrentRule := fCurrentRule.Parent;
while (fCurrentRule.fRule.fCloseOnTerm) do
fCurrentRule := fCurrentRule.Parent;
end
else
fCurrentRule := fCurrentRule.Parent;
end;
//: if we can't find token from current position:
if not fCurrentRule.SymbolList[fCurrentRule.CaseFunct(fLine[Run])].GetToken(fCurrentRule, fLine, Run, fCurrToken) then //Vitalik 2004
begin
fCurrToken := fCurrentRule.fDefaultSynSymbol; //: Current token is just default symbol
while not ((fLine[Run] in fCurrentRule.fTermSymbols) or fCurrentRule.HasNodeAnyStart[fCurrentRule.CaseFunct(fLine[Run])]) do
inc(Run); //: goes to the first non-delimeter symbol
end
else //: else (we find token!)
if (fCurrentRule.fClosingSymbol = fCurrToken) then begin //: if current token close current range
// if (fCurrentRule.fClosingSymbol <> nil) and (fCurrentRule.fClosingSymbol.Symbol = fCurrToken.Symbol) then
if fCurrentRule.OpenCount > 0 then
fCurrentRule.OpenCount := fCurrentRule.OpenCount - 1
else
if fCurrentRule.ParentBackup <> nil then
fCurrentRule.Parent := fCurrentRule.ParentBackup;
if fCurrentRule.fRule.fAllowPredClose then begin
fCurrentRule := fCurrentRule.Parent;
while (fCurrentRule.fClosingSymbol <> nil) and (fCurrentRule.fClosingSymbol.Symbol = fCurrToken.Symbol) do
fCurrentRule := fCurrentRule.Parent;
end else
fCurrentRule := fCurrentRule.Parent
end else
if fCurrToken.fOpenRule <> nil then begin //: else if current token open range then
CurrentParent := fCurrentRule;
if fCurrToken.fOpenRule is TSynRangeLink then begin
RangeLink := TSynRangeLink(fCurrToken.fOpenRule);
fCurrentRule := RangeLink.Range;
ParentCycle := CurrentParent;
isFindSame := False;
while ParentCycle <> nil do begin // Ищем есть ли у тек. правила такой же родитель
if ParentCycle = fCurrentRule then begin
if RangeLink.Range.OpenCount = 0 then begin // Первое открытие вложенного в себя правила.
fCurrentRule.ParentBackup := RangeLink.Range.Parent;
fCurrentRule.Parent := CurrentParent;
RangeLink.Range.OpenCount := 1;
end else begin
RangeLink.Range.OpenCount := RangeLink.Range.OpenCount + 1;
end;
isFindSame := True;
break;
end;
ParentCycle := ParentCycle.Parent;
end;
if not isFindSame then begin
{ fCurrentRule.ParentBackup := RangeLink.Range.Parent;
fCurrentRule.Parent := CurrentParent;
RangeLink.Range.OpenCount := 1;
// fCurrentRule.Parent := RangeLink.Parent;}
end
end
else if fCurrToken.fOpenRule is TSynRange then begin
fCurrentRule := TSynRange(fCurrToken.fOpenRule); //: open range
fCurrentRule.Parent := CurrentParent;
end;
end;
if fLine[Run] = #0 then //: If end of line
fPrEol := True; //: ???
end;
procedure TSynUniSyn.SpaceProc;
//! Never used!!! SSS
begin
repeat
Inc(Run);
until (fLine[Run] > #32) or (fLine[Run] in [#0, #10, #13]);
end;
function TSynUniSyn.IsKeyword(const aKeyword: string): boolean;
//! Never used!!!! ??? SSS
begin
// Result := fSymbols.FindSymbol(aKeyword) <> nil;
end;
function TSynUniSyn.GetDefaultAttribute(Index: integer): TSynHighlighterAttributes;
//: Returns default attribute
//: Неопнятно зачем это нужно, но функция предка - абстрактная (может что-нить с ней сделать...)
begin
case Index of
SYN_ATTR_COMMENT: Result := fCurrentRule.Attribs;
SYN_ATTR_IDENTIFIER: Result := fCurrentRule.Attribs;
SYN_ATTR_KEYWORD: Result := fCurrentRule.Attribs;
SYN_ATTR_STRING: Result := fCurrentRule.Attribs;
SYN_ATTR_WHITESPACE: Result := fCurrentRule.Attribs;
else
Result := nil;
end;
end;
function TSynUniSyn.GetEol: Boolean;
begin
Result := fEol;
end;
function TSynUniSyn.GetRange: Pointer;
//: Returns current Range
begin
Result := fCurrentRule;
end;
function TSynUniSyn.GetToken: string;
//: Returns current token (string from fTokenPos to Run)
var
Len: LongInt;
begin
Len := Run - fTokenPos;
Setstring(Result, (fLine + fTokenPos), Len);
end;
procedure TSynUniSyn.GetTokenEx(out TokenStart: PChar; out TokenLength: integer);
begin
TokenLength := Run - fTokenPos;
TokenStart := PAnsiChar(fTrueLine) + fTokenPos;
end;
function TSynUniSyn.GetTokenID: Integer;
//: Return ID of current token
//: ??? Оставлена для непонятной совместимости? Нигде же не вызывается и не используется!
//: Можено что-нить с ней сделать...
begin
Result := 1; //# CODE_REVIEW fCurrToken.ID;
end;
function TSynUniSyn.GetTokenAttribute: TSynHighlighterAttributes;
//: Returns attribute of current token
begin
// fCurrToken.Attr.Style := fCurrToken.Attr.Style + [fsUnderline];
// if GetEol then
// Result := nil
Result := fCurrToken.Attributes;
end;
function TSynUniSyn.GetTokenKind: integer;
//~ Можно в Kind у токена fCurrToken хранить что это ?: слово или Range или Set
begin
Result := 1; //# CODE_REVIEW fCurrToken.ID;
end;
function TSynUniSyn.GetTokenPos: Integer;
//: Returns position of current token
begin
Result := fTokenPos;
end;
procedure TSynUniSyn.ResetRange;
//: Reset current range to MainRules
begin
fCurrentRule := MainRules;
end;
procedure TSynUniSyn.SetRange(Value: Pointer);
//: Set current range
begin
fCurrentRule := TSynRange(Value);
end;
class function TSynUniSyn.GetLanguageName: string;
begin
Result := 'UniLanguage';
end;
procedure TSynUniSyn.Clear;
begin
MainRules.Clear;
Info.Clear;
end;
procedure TSynUniSyn.CreateStandardRules;
//: Create sample rools
var r: TSynRange;
kw: TSynKeyList;
begin
self.MainRules.Clear;
self.MainRules.Attribs.Foreground := clBlack;
self.MainRules.Attribs.Background := clWhite;
self.MainRules.CaseSensitive := False;
r := TSynRange.Create('''', '''');
r.Name := 'Strings ''..''';
r.Attribs.Foreground := clRed;
r.Attribs.Background := clWhite;
r.CaseSensitive := False;
r.fRule.fOpenSymbol.BrakeType := btAny;
self.MainRules.AddRange(r);
r := TSynRange.Create('"', '"');
r.Name := 'Strings ".."';
r.Attribs.Foreground := clRed;
r.Attribs.Background := clWhite;
r.CaseSensitive := False;
r.fRule.fOpenSymbol.BrakeType := btAny;
self.MainRules.AddRange(r);
r := TSynRange.Create('{', '}');
r.Name := 'Remarks {..}';
r.Attribs.Foreground := clNavy;
r.Attribs.Background := clWhite;
r.CaseSensitive := False;
r.fRule.fOpenSymbol.BrakeType := btAny;
self.MainRules.AddRange(r);
r := TSynRange.Create('(*', '*)');
r.Name := 'Remarks (*..*)';
r.Attribs.Foreground := clNavy;
r.Attribs.Background := clWhite;
r.CaseSensitive := False;
r.fRule.fOpenSymbol.BrakeType := btAny;
self.MainRules.AddRange(r);
r := TSynRange.Create('/*', '*/');
r.Name := 'Remarks /*..*/';
r.Attribs.Foreground := clNavy;
r.Attribs.Background := clWhite;
r.CaseSensitive := False;
r.fRule.fOpenSymbol.BrakeType := btAny;
self.MainRules.AddRange(r);
kw := TSynKeyList.Create('');
kw.Name := 'Key words';
kw.Attribs.Foreground := clGreen;
kw.Attribs.Background := clWhite;
self.MainRules.AddKeyList(kw);
end;
procedure TSynUniSyn.Prepare;
//: Prepare of SynUniSyn is Prepare of SynUniSyn.fMailRules
function HaveNodeAnyStart(Node: TSymbolNode): boolean;
var
i: integer;
begin
Result := False;
if Node.StartType = stAny then
begin
Result := True;
Exit;
end;
for i := 0 to Node.NextSymbs.Count-1 do
if (Node.NextSymbs.Nodes[i].StartType = stAny) or HaveNodeAnyStart(Node.NextSymbs.Nodes[i]) then
begin
Result := True;
Exit;
end
end;
var i: integer;
begin
MainRules.Prepare(MainRules);
// for i := 0 to 255 do
// if (MainRules.SymbolList[char(i)] <> MainRules.fDefaultTermSymbol) and (MainRules.SymbolList[char(i)] <> MainRules.fDefaultSymbols) then
// MessageBox(0,PChar(TSymbols(MainRules.SymbolList[char(i)]).HeadNode.tkSynSymbol.Symbol),'1',0);
for i := 0 to 255 do
// if (MainRules.SymbolList[char(i)] <> nil) {temp}and (TSymbols(MainRules.SymbolList[char(i)]).HeadNode <> nil){/temp} then
if (MainRules.SymbolList[char(i)] <> MainRules.fDefaultTermSymbol) and (MainRules.SymbolList[char(i)] <> MainRules.fDefaultSymbols) and (TSymbols(MainRules.SymbolList[char(i)]).HeadNode <> nil) then
MainRules.HasNodeAnyStart[char(i)] := HaveNodeAnyStart(TSymbols(MainRules.SymbolList[MainRules.CaseFunct(char(i))]).HeadNode);
end;
procedure TSynUniSyn.NullProc;
//: Never used!!! SSS ???
begin
// fEol := True;
end;
procedure TSynUniSyn.Reset;
//: Reset of SynUniSyn is Reset of SynUniSyn.MainRules
begin
MainRules.Reset;
end;
procedure TSynUniSyn.DefineProperties(Filer: TFiler);
//! Never used ????
var
iHasData: boolean;
begin
inherited;
if Filer.Ancestor <> nil then
iHasData := True
else
iHasData := MainRules.RangeCount > 0;
Filer.DefineProperty( 'Syntax', ReadSyntax, WriteSyntax, {True}iHasData );
end;
procedure TSynUniSyn.ReadSyntax(Reader: TReader);
//: This is some metods for reading ??? ??? ???
var
iBuffer: TStringStream;
begin
// iBuffer := nil;
// try
iBuffer := TStringStream.Create( Reader.ReadString );
iBuffer.Position := 0;
LoadFromStream( iBuffer );
// finally
// iBuffer.Free;
// end;
end;
procedure TSynUniSyn.WriteSyntax(Writer: TWriter);
//: This is some metods for writing ??? ??? ???
var
iBuffer: TStringStream;
begin
iBuffer := TStringStream.Create( '' );
try
SaveToStream( iBuffer );
iBuffer.Position := 0;
Writer.WriteString( iBuffer.DataString );
finally
iBuffer.Free;
end;
end;
function TSynUniSyn.GetIdentChars: TSynIdentChars;
//: Return IdentChars - hmm... What for ??? word selection?
begin
Result := [#32..#255] - fCurrentRule.TermSymbols;
end;
function TSynUniSyn.GetSampleSource: string;
//: Get sample text
begin
Result := Info.Sample.Text;
end;
procedure TSynUniSyn.SetSampleSource(Value: string);
//: Set sample text
begin
Info.Sample.Text := Value;
end;
procedure TSynUniSyn.LoadFromXml(xml: TDOMNode);
var
i, J, K: integer;
ChildNode1,
ChildNode2: TDOMNode;
Key, Value: string;
begin
Clear;
for J := 0 to Int32(xml.ChildNodes.Count) - 1 do
begin
ChildNode1:= xml.ChildNodes.Item[J];
if SameText(ChildNode1.NodeName, 'UniHighlighter') then
if ChildNode1.Attributes.Length = 0 then
begin
LoadHglFromXml(ChildNode1);
Exit;
end
else begin
for I := 0 to Int32(ChildNode1.ChildNodes.Count) - 1 do
begin
ChildNode2:= ChildNode1.ChildNodes.Item[I];
if SameText(ChildNode2.NodeName, 'Info') then
Info.LoadFromXml(ChildNode2)
else
if SameText(ChildNode2.NodeName, 'Scheme') then begin
SchemeFileName := ''; SchemeName := '';
for K := 0 to Int32(ChildNode2.Attributes.Length) - 1 do begin
Key := ChildNode2.Attributes[K].NodeName;
Value := ChildNode2.Attributes[K].NodeValue;
if SameText('File', Key) then SchemeFileName := Value else
if SameText('Name', Key) then SchemeName := Value;
end;
if FileExists(SchemeFileName) then begin
if Styles <> nil then
Styles.Free;
Styles := TSynUniStyles.Create;
Styles.FileName := SchemeFileName;
Styles.Load;
end;
end else
if SameText(ChildNode2.NodeName, 'Range') then begin
// fMainRules.SetStyles(fStyles);
fMainRules.Styles := Styles;
fMainRules.LoadFromXml(ChildNode2);
Break;
end
end;
end;
end
end;
procedure TSynUniSyn.LoadFromStream(Stream: TStream; FreeStream: boolean);
var
Len: Integer;
Temp: PAnsiChar;
Target: PAnsiChar;
Source: PAnsiChar;
Finish: PAnsiChar;
Memory: PAnsiChar;
Xml: TXMLDocument;
TargetStream: TMemoryStream;
begin
TargetStream:= TMemoryStream.Create;
try
Len:= Stream.Size;
Source:= GetMem(Len);
TargetStream.SetSize(Len * 2);
Stream.ReadBuffer(Source^, Len);
Temp:= Source;
Memory:= Source;
Finish:= Temp + Len - 4;
Target:= TargetStream.Memory;
// Convert '&qt;' to '&quot;'
while (Temp < Finish) do
begin
if (Temp^ <> '&') then
Inc(Temp, 1)
else if ((Temp + 1)^ <> 'q') then
Inc(Temp, 2)
else if ((Temp + 2)^ <> 't') then
Inc(Temp, 3)
else begin
Len:= (Temp - Source) + 2;
Move(Source^, Target^, Len);
Inc(Temp, 4);
Inc(Target, Len);
Move('uot;', Target^, 4);
Inc(Target, 4);
Source:= Temp;
end;
end;
Len:= (Temp - Source) + 4;
Move(Source^, Target^, Len);
Inc(Target, Len);
TargetStream.SetSize(Target - TargetStream.Memory);
try
TargetStream.Position:= 0;
ReadXMLFile(Xml, TargetStream);
try
LoadFromXml(Xml);
finally
Xml.Free;
end;
finally
DefHighlightChange(Self);
end;
finally
TargetStream.Free;
if FreeStream then Stream.Free;
if (Memory <> nil) then FreeMem(Memory);
end;
end;
procedure TSynUniSyn.LoadFromFile(FileName: string);
begin
LoadFromStream(TFileStreamUTF8.Create(FileName, fmOpenRead or fmShareDenyNone));
end;
procedure TSynUniSyn.SaveToStream(Stream: TStream; Rule: TSynRule);
var
StreamWriter: TStreamWriter;
begin
StreamWriter := TStreamWriter.Create(Stream);
with StreamWriter do begin
WriteTag(0, 'UniHighlighter');
WriteParam('version', '1.8', CloseStartTag);
Info.SaveToStream(StreamWriter, 2);
WriteTag(2, 'Scheme');
WriteParam('File', SchemeFileName);
WriteParam('Name', SchemeName, CloseEmptyTag);
if Rule = nil then
MainRules.SaveToStream(StreamWriter, 2)
else
Rule.SaveToStream(StreamWriter, 2);
WriteTag(0, '/UniHighlighter', True);
end;
StreamWriter.Free;
end;
function TSynUniSyn.GetAsStream: TMemoryStream;
begin
Result := TMemoryStream.Create;
SaveToStream(Result);
end;
procedure TSynUniSyn.SaveToFile(FileName: string; Rule: TSynRule);
var
F: TFileStream;
begin
if FileName = '' then
raise exception.Create(ClassName + '.SaveToFile - FileName is empty');
F := TFileStream.Create(FileName, fmCreate);
try
SaveToStream(F, Rule)
finally
F.Free;
end;
end;
function ReadValue(ANode: TDOMNode): String;
begin
if Assigned(ANode.FirstChild) then
Result:= ANode.FirstChild.NodeValue
else
Result:= EmptyStr;
end;
procedure TSynUniSyn.SaveHglToStream(Stream: TStream);
//: Save Highlighter to stream
procedure WriteString(const aStr: string);
begin
Stream.Write( aStr[1], Length(aStr) );
Stream.Write( #10#13, 1 );
end;
function Indent(i: integer): string;
begin
SetLength( Result, i );
FillChar( Result[1], i, #32 );
end;
function GetValidValue(Value: string): string;
begin
Value := StringReplace(Value, '&', '&amp;', [rfReplaceAll, rfIgnoreCase]);
Value := StringReplace(Value, '<', '&lt;', [rfReplaceAll, rfIgnoreCase]);
Value := StringReplace(Value, '"', '&quot;', [rfReplaceAll, rfIgnoreCase]);
Result := StringReplace(Value, '>', '&gt;', [rfReplaceAll, rfIgnoreCase]);
end;
procedure InsertTag(Ind: integer; Name: string; Value: string);
begin
WriteString( Format('%s<%s>%s</%s>', [Indent(Ind), Name, GetValidValue(Value), Name]) );
end;
procedure OpenTag(Ind: integer; Name: string; Param: string = ''; ParamValue: string = '');
begin
if Param = '' then
WriteString(Format('%s<%s>', [Indent(Ind), Name]))
else
WriteString(Format('%s<%s %s="%s">', [Indent(Ind), Name, Param, GetValidValue(ParamValue)]));
end;
procedure SaveColor(MainTag: string; Ind, Fore, Back: integer; Style: TFontStyles; PFore, PBack: boolean);
procedure InsertTagBool(Ind: integer; Name: string; Value: Boolean);
begin
if Value then
WriteString(Format('%s<%s>True</%s>', [Indent(Ind), Name, Name]))
else
WriteString(Format('%s<%s>False</%s>', [Indent(Ind), Name, Name]))
end;
begin
OpenTag(Ind, MainTag);
InsertTag(Ind+1, 'Back', Inttostr(Back));
InsertTag(Ind+1, 'Fore', Inttostr(Fore));
InsertTag(Ind+1, 'Style', FontStyleToStr(Style));
InsertTagBool(Ind+1, 'ParentForeground', PFore);
InsertTagBool(Ind+1, 'ParentBackground', PBack);
OpenTag(Ind, '/'+MainTag);
end;
procedure SaveKWGroup(Ind: integer; G: TSynKeyList);
var i: integer;
procedure InsertTagBool(Ind: integer; Name: string; Value: Boolean);
begin
if Value then
WriteString(Format('%s<%s>True</%s>', [Indent(Ind), Name, Name]))
else
WriteString(Format('%s<%s>False</%s>', [Indent(Ind), Name, Name]))
end;
begin
OpenTag(Ind, 'KW', 'Name', G.Name);
for i := 0 to fSchemes.Count-1 do begin
G.ind := i;
SaveColor('Attri', Ind+1, G.Attribs.Foreground, G.Attribs.Background, G.Attribs.Style, G.Attribs.ParentForeground, G.Attribs.ParentBackground);
end;
G.ind := fSchemeIndex;
InsertTagBool(Ind+1, 'Enabled', G.Enabled);
For i := 0 to G.KeyList.Count-1 do InsertTag(Ind+1, 'W', G.KeyList[i]);
OpenTag(Ind, '/KW');
end;
procedure SaveSet(Ind: integer; S: TSynSet);
var i: integer;
procedure InsertTagBool(Ind: integer; Name: string; Value: Boolean);
begin
if Value then
WriteString(Format('%s<%s>True</%s>', [Indent(Ind), Name, Name]))
else
WriteString(Format('%s<%s>False</%s>', [Indent(Ind), Name, Name]))
end;
begin
OpenTag(Ind, 'Set', 'Name', S.Name);
for i := 0 to fSchemes.Count-1 do begin
S.ind := i;
SaveColor('Attri', Ind+1, S.Attribs.Foreground, S.Attribs.Background, S.Attribs.Style, S.Attribs.ParentForeground, S.Attribs.ParentBackground);
end;
S.ind := fSchemeIndex;
InsertTagBool(Ind+1, 'Enabled', S.Enabled);
{ if S.StartType = stAny then
if S.BrakeType = btAny then
InsertTag(Ind+1, 'SymbolSetPartOfTerm', 'True')
else
InsertTag(Ind+1, 'SymbolSetPartOfTerm', 'Left')
else
if S.BrakeType = btAny then
InsertTag(Ind+1, 'SymbolSetPartOfTerm', 'Right')
else
InsertTag(Ind+1, 'SymbolSetPartOfTerm', 'False');}
InsertTag(Ind+1, 'S', SetToStr(S.SymbSet));
OpenTag(Ind, '/Set');
end;
procedure SaveRange(Ind: integer; R: TSynRange);
var i: integer;
procedure InsertTagBool(Ind: integer; Name: string; Value: Boolean);
begin
if Value then
WriteString(Format('%s<%s>True</%s>', [Indent(Ind), Name, Name]))
else
WriteString(Format('%s<%s>False</%s>', [Indent(Ind), Name, Name]))
end;
begin
OpenTag(Ind, 'Range', 'Name', R.Name);
for i := 0 to fSchemes.Count-1 do begin
R.ind := i;
SaveColor('Attri', Ind, R.Attribs.Foreground, R.Attribs.Background, R.Attribs.Style, R.Attribs.ParentForeground, R.Attribs.ParentBackground);
end;
R.ind := fSchemeIndex;
InsertTagBool(Ind, 'Enabled', R.Enabled);
if (Length(R.fRule.fOpenSymbol.Symbol) > 0) and (R.fRule.fOpenSymbol.Symbol[Length(R.fRule.fOpenSymbol.Symbol)] = #0) then begin
InsertTag(Ind, 'OpenSymbol', copy(R.fRule.fOpenSymbol.Symbol,1,Length(R.fRule.fOpenSymbol.Symbol)-1));
InsertTagBool(Ind, 'OpenSymbolFinishOnEol', true);
end else begin
InsertTag(Ind, 'OpenSymbol', R.fRule.fOpenSymbol.Symbol);
InsertTagBool(Ind, 'OpenSymbolFinishOnEol', false);
end;
if (Length(R.fRule.fCloseSymbol.Symbol) > 0) and (R.fRule.fCloseSymbol.Symbol[Length(R.fRule.fCloseSymbol.Symbol)] = #0) then begin
InsertTag(Ind, 'CloseSymbol', copy(R.fRule.fCloseSymbol.Symbol,1,Length(R.fRule.fCloseSymbol.Symbol)-1));
InsertTagBool(Ind, 'CloseSymbolFinishOnEol', true);
end else begin
InsertTag(Ind, 'CloseSymbol', R.fRule.fCloseSymbol.Symbol);
InsertTagBool(Ind, 'CloseSymbolFinishOnEol', false);
end;
if R.fRule.fOpenSymbol.StartLine = slFirst then
InsertTag(Ind, 'OpenSymbolStartLine', 'True')
else if R.fRule.fOpenSymbol.StartLine = slFirstNonSpace then
InsertTag(Ind, 'OpenSymbolStartLine', 'NonSpace')
else
InsertTag(Ind, 'OpenSymbolStartLine', 'False');
if R.fRule.fCloseSymbol.StartLine = slFirst then
InsertTag(Ind, 'CloseSymbolStartLine', 'True')
else if R.fRule.fCloseSymbol.StartLine = slFirstNonSpace then
InsertTag(Ind, 'CloseSymbolStartLine', 'NonSpace')
else
InsertTag(Ind, 'CloseSymbolStartLine', 'False');
InsertTag(Ind, 'DelimiterChars', SetToStr(R.TermSymbols));
if R.fRule.fOpenSymbol.StartType = stAny then
if R.fRule.fOpenSymbol.BrakeType = btAny then
InsertTag(Ind, 'OpenSymbolPartOfTerm', 'True')
else
InsertTag(Ind, 'OpenSymbolPartOfTerm', 'Left')
else
if R.fRule.fOpenSymbol.BrakeType = btAny then
InsertTag(Ind, 'OpenSymbolPartOfTerm', 'Right')
else
InsertTag(Ind, 'OpenSymbolPartOfTerm', 'False');
if R.fRule.fCloseSymbol.StartType = stAny then
if R.fRule.fCloseSymbol.BrakeType = btAny then
InsertTag(Ind, 'CloseSymbolPartOfTerm', 'True')
else
InsertTag(Ind, 'CloseSymbolPartOfTerm', 'Left')
else
if R.fRule.fCloseSymbol.BrakeType = btAny then
InsertTag(Ind, 'CloseSymbolPartOfTerm', 'Right')
else
InsertTag(Ind, 'CloseSymbolPartOfTerm', 'False');
InsertTagBool(Ind, 'CloseOnTerm', R.fRule.fCloseOnTerm);
InsertTagBool(Ind, 'CloseOnEol', R.fRule.fCloseOnEol);
InsertTagBool(Ind, 'AllowPredClose', R.fRule.fAllowPredClose);
InsertTagBool(Ind, 'CaseSensitive', R.CaseSensitive);
For i := 0 to R.KeyListCount-1 do SaveKWGroup(Ind, R.KeyLists[i]);
For i := 0 to R.SetCount-1 do SaveSet(Ind, R.Sets[i]);
For i := 0 to R.RangeCount-1 do SaveRange(Ind+1, R.Ranges[i]);
OpenTag(Ind, '/Range');
end;
procedure SaveInfo;
var i: integer;
begin
OpenTag(1, 'Info');
OpenTag(2, 'General');
InsertTag(3, 'Name', info.General.Name);
InsertTag(3, 'FileTypeName', info.General.Extensions);
// InsertTag(3, 'Layout', info.General.Layout);
OpenTag(2, '/General');
OpenTag(2, 'Author');
InsertTag(3, 'Name', Info.Author.Name);
InsertTag(3, 'Email', Info.Author.Email);
InsertTag(3, 'Web', Info.Author.Web);
InsertTag(3, 'Copyright', Info.Author.Copyright);
InsertTag(3, 'Company', Info.Author.Company);
InsertTag(3, 'Remark', Info.Author.Remark);
OpenTag(2, '/Author');
OpenTag(2, 'Version');
InsertTag(3, 'Version', IntToStr(Info.Version.Version));
InsertTag(3, 'Revision', IntToStr(Info.Version.Revision));
InsertTag(3, 'Date', FloatToStr(Info.Version.ReleaseDate));
case Info.Version.VersionType of
vtInternalTest: InsertTag(3, 'Type', 'Internal Test');
vtBeta: InsertTag(3, 'Type', 'Beta');
vtRelease: InsertTag(3, 'Type', 'Release');
end;
OpenTag(2, '/Version');
OpenTag(2, 'History');
for i := 0 to Info.history.count-1 do InsertTag(3, 'H', Info.history[i]);
OpenTag(2, '/History');
OpenTag(2, 'Sample');
for i := 0 to Info.Sample.count-1 do InsertTag(3, 'S', Info.Sample[i]);
OpenTag(2, '/Sample');
OpenTag(1, '/Info');
end;
procedure SaveSchemes;
var
i: integer;
begin
InsertTag(1, 'SchemeIndex', IntToStr(fSchemeIndex));
OpenTag(1, 'Schemes');
for i := 0 to self.SchemesList.Count-1 do
InsertTag(2, 'S', fSchemes.Strings[i]);
OpenTag(1, '/Schemes');
end;
begin
OpenTag(0, 'UniHighlighter');
OpenTag(1, 'ImportantInfo');
WriteString(Indent(2)+'******* Please read carefully *************************');
WriteString(Indent(2)+'* Please, make any changes in this file very carefuly!*');
WriteString(Indent(2)+'* It is much more convenient to use native designer! *');
WriteString(Indent(2)+'*******************************************************');
OpenTag(1, '/ImportantInfo');
SaveInfo;
SaveSchemes;
SaveRange(1, self.MainRules);
OpenTag(0, '/UniHighlighter');
end;
procedure TSynUniSyn.LoadHglFromXml(xml: TDOMNode);
var
J: Integer;
ChildNode: TDOMNode;
begin
Clear;
SchemeIndex := 0;
for J := 0 to Int32(xml.ChildNodes.Count) - 1 do
begin
ChildNode:= xml.ChildNodes.Item[J];
if SameText(ChildNode.NodeName, 'Info') then
ReadInfo(ChildNode)
else if SameText(ChildNode.NodeName, 'SchemeIndex') then
SchemeIndex := StrToInt(ReadValue(ChildNode))
else if SameText(ChildNode.NodeName, 'Schemes') then
ReadSchemes(ChildNode)
else if SameText(ChildNode.NodeName, 'Range') then
begin
fMainRules.LoadHglFromXml(ChildNode, fSchemes.Count, SchemeIndex);
end
end
end;
procedure TSynUniSyn.ReadInfo(xml: TDOMNode);
var
I, J: Integer;
ChildNode1, ChildNode2: TDOMNode;
AFormatSettings: TFormatSettings;
begin
for J := 0 to Int32(xml.ChildNodes.Count) - 1 do
begin
ChildNode1:= xml.ChildNodes.Item[J];
if ChildNode1.NodeName = 'General' then
begin
for I:= 0 to Int32(ChildNode1.ChildNodes.Count) - 1 do
begin
ChildNode2 := ChildNode1.ChildNodes.Item[I];
if ChildNode2.NodeName = 'Name' then Info.General.Name:= ReadValue(ChildNode2)
else if ChildNode2.NodeName = 'FileTypeName' then Info.General.Extensions := ReadValue(ChildNode2)
// else if ChildNode2.NodeName = 'Layout' then Info.General.Layout := ReadValue(ChildNode2);
end;
end
else if ChildNode1.NodeName = 'Author' then
begin
for I:= 0 to Int32(ChildNode1.ChildNodes.Count) - 1 do
begin
ChildNode2 := ChildNode1.ChildNodes.Item[I];
if ChildNode2.NodeName = 'Name' then Info.Author.Name:= ReadValue(ChildNode2)
else if ChildNode2.NodeName = 'Email' then Info.Author.Email:= ReadValue(ChildNode2)
else if ChildNode2.NodeName = 'Web' then Info.Author.Web:= ReadValue(ChildNode2)
else if ChildNode2.NodeName = 'Copyright' then Info.Author.Copyright:= ReadValue(ChildNode2)
else if ChildNode2.NodeName = 'Company' then Info.Author.Company:= ReadValue(ChildNode2)
else if ChildNode2.NodeName = 'Remark' then Info.Author.Remark:= ReadValue(ChildNode2)
end;
end
else if ChildNode1.NodeName = 'Version' then
begin
for I:= 0 to Int32(ChildNode1.ChildNodes.Count) - 1 do
begin
ChildNode2 := ChildNode1.ChildNodes.Item[I];
if ChildNode2.NodeName = 'Version' then Info.Version.Version := StrToIntDef(ReadValue(ChildNode2), 0)
else if ChildNode2.NodeName = 'Revision' then Info.Version.Revision := StrToIntDef(ReadValue(ChildNode2), 0)
else if ChildNode2.NodeName = 'Date' then
{
try
AFormatSettings:= DefaultFormatSettings;
Info.Version.ReleaseDate := StrToFloat(ReadValue(ChildNode2), AFormatSettings);
except
AFormatSettings.DecimalSeparator := '.';
try
Info.Version.ReleaseDate := StrToFloat(ReadValue(ChildNode2), AFormatSettings);
except
// Ignore
end;
end
}
else if ChildNode2.NodeName = 'Type' then
begin
if ReadValue(ChildNode2) = 'Beta' then Info.Version.VersionType := vtBeta
else if ReadValue(ChildNode2) = 'Release' then Info.Version.VersionType := vtRelease
else Info.Version.VersionType := vtInternalTest
end
end;
end
else if ChildNode1.NodeName = 'History' then
begin
Info.History.Clear;
for I:= 0 to Int32(ChildNode1.ChildNodes.Count) - 1 do
begin
ChildNode2 := ChildNode1.ChildNodes.Item[I];
if ChildNode2.NodeName = 'H' then
Info.History.Add(ReadValue(ChildNode2));
end;
end
else if ChildNode1.NodeName = 'Sample' then
begin
Info.Sample.Clear;
for I:= 0 to Int32(ChildNode1.ChildNodes.Count) - 1 do
begin
ChildNode2 := ChildNode1.ChildNodes.Item[I];
if ChildNode2.NodeName = 'S' then
Info.Sample.Add(ReadValue(ChildNode2));
end;
end;
end;
end;
procedure TSynUniSyn.ReadSchemes(xml: TDOMNode);
var
J: Integer;
ChildNode: TDOMNode;
begin
if fSchemes <> nil then begin
fSchemes.Clear();
//MainRules.ClearAttributes();
end
else
raise Exception.Create(ClassName + '.ReadSchemes - property Schemes not initialized.');
for J := 0 to Int32(xml.ChildNodes.Count) - 1 do
begin
ChildNode:= xml.ChildNodes.Item[J];
if ChildNode.NodeName = 'S' then
fSchemes.Add(ReadValue(ChildNode));
end;
end;
procedure TSynUniSyn.LoadHglFromFile(FileName: string);
//: Load Highlighter'a from file
var
F: TFileStreamUTF8;
begin
if not FileExists(FileName) then
raise Exception.Create(ClassName + '.LoadHglFromFile - "'+FileName+'" does not exists.');
F := TFileStreamUTF8.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
LoadHglFromStream( F );
finally
F.Free;
end;
end;
procedure TSynUniSyn.SaveHglToFile(FileName: string);
//: Save Highlighter to file
var
F: TFileStreamUTF8;
begin
if FileName = '' then
raise exception.Create(ClassName + '.SaveHglToFile - FileName is empty');
F := TFileStreamUTF8.Create(FileName, fmCreate);
try
SaveHglToStream( F );
finally
F.Free;
end;
end;
procedure TSynUniSyn.LoadHglFromStream(Stream: TStream);
var
xml: TXMLDocument = nil;
begin
try
ReadXMLFile(xml, Stream);
LoadHglFromXml(xml);
finally
xml.Free;
end;
DefHighlightChange( Self );
end;
initialization
{$IFNDEF SYN_CPPB_1}
RegisterPlaceableHighlighter(TSynUniSyn);
{$ENDIF}
end.