mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-06 18:24:26 +08:00
1114 lines
32 KiB
ObjectPascal
1114 lines
32 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: SynHighlighterMulti.pas, released 2000-06-23.
|
|
The Original Code is based on mwMultiSyn.pas by Willo van der Merwe, part of the
|
|
mwEdit component suite.
|
|
Unicode translation by Maël Hörz.
|
|
|
|
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: SynHighlighterMulti.pas,v 1.34.2.11 2008/09/14 16:25:00 maelh Exp $
|
|
|
|
You may retrieve the latest version of this file at the SynEdit home page,
|
|
located at http://SynEdit.SourceForge.net
|
|
|
|
Known Issues:
|
|
-------------------------------------------------------------------------------}
|
|
{
|
|
@abstract(Provides a Multiple-highlighter syntax highlighter for SynEdit)
|
|
@author(Willo van der Merwe <willo@wack.co.za>, converted to SynEdit by David Muir <dhm@dmsoftware.co.uk>)
|
|
@created(1999, converted to SynEdit 2000-06-23)
|
|
@lastmod(2000-06-23)
|
|
The SynHighlighterMulti unit provides SynEdit with a multiple-highlighter syntax highlighter.
|
|
This highlighter can be used to highlight text in which several languages are present, such as HTML.
|
|
For example, in HTML as well as HTML tags there can also be JavaScript and/or VBScript present.
|
|
}
|
|
|
|
unit SynHighlighterMulti;
|
|
|
|
{$I SynEdit.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Windows,
|
|
SynEditTypes,
|
|
SynEditHighlighter,
|
|
SynUnicode,
|
|
Classes;
|
|
|
|
type
|
|
TOnCheckMarker = procedure (Sender: TObject; var StartPos, MarkerLen: Integer;
|
|
var MarkerText: UnicodeString; Line: Integer; const LineStr: string) of object;
|
|
|
|
TScheme = class(TCollectionItem)
|
|
private
|
|
FEndExpr: UnicodeString;
|
|
FStartExpr: UnicodeString;
|
|
FHighlighter: TSynCustomHighLighter;
|
|
FMarkerAttri: TSynHighlighterAttributes;
|
|
FSchemeName: TComponentName;
|
|
FCaseSensitive: Boolean;
|
|
FOnCheckStartMarker: TOnCheckMarker;
|
|
FOnCheckEndMarker: TOnCheckMarker;
|
|
function ConvertExpression(const Value: UnicodeString): UnicodeString;
|
|
procedure MarkerAttriChanged(Sender: TObject);
|
|
procedure SetMarkerAttri(const Value: TSynHighlighterAttributes);
|
|
procedure SetHighlighter(const Value: TSynCustomHighlighter);
|
|
procedure SetEndExpr(const Value: UnicodeString);
|
|
procedure SetStartExpr(const Value: UnicodeString);
|
|
procedure SetCaseSensitive(const Value: Boolean);
|
|
protected
|
|
procedure DefineProperties(Filer: TFiler); override;
|
|
{$IFDEF SYN_COMPILER_3_UP}
|
|
function GetDisplayName: string; override;
|
|
procedure SetDisplayName(const Value: string); override;
|
|
{$ENDIF}
|
|
public
|
|
constructor Create(Collection: TCollection); override;
|
|
destructor Destroy; override;
|
|
published
|
|
property CaseSensitive: Boolean read FCaseSensitive write SetCaseSensitive
|
|
default True;
|
|
property StartExpr: UnicodeString read FStartExpr write SetStartExpr;
|
|
property EndExpr: UnicodeString read FEndExpr write SetEndExpr;
|
|
property Highlighter: TSynCustomHighlighter read FHighlighter
|
|
write SetHighlighter;
|
|
property MarkerAttri: TSynHighlighterAttributes read FMarkerAttri
|
|
write SetMarkerAttri;
|
|
property SchemeName: TComponentName read FSchemeName write FSchemeName;
|
|
property OnCheckStartMarker: TOnCheckMarker read FOnCheckStartMarker write FOnCheckStartMarker;
|
|
property OnCheckEndMarker: TOnCheckMarker read FOnCheckEndMarker write FOnCheckEndMarker;
|
|
end;
|
|
|
|
TgmSchemeClass = class of TScheme;
|
|
|
|
TSynMultiSyn = class;
|
|
|
|
TSchemes = class(TCollection)
|
|
private
|
|
FOwner: TSynMultiSyn;
|
|
function GetItems(Index: Integer): TScheme;
|
|
procedure SetItems(Index: Integer; const Value: TScheme);
|
|
{$IFDEF SYN_COMPILER_3_UP}
|
|
protected
|
|
function GetOwner: TPersistent; override;
|
|
procedure Update(Item: TCollectionItem); override;
|
|
{$ENDIF}
|
|
public
|
|
constructor Create(aOwner: TSynMultiSyn);
|
|
property Items[aIndex: Integer]: TScheme read GetItems write SetItems;
|
|
default;
|
|
end;
|
|
|
|
TMarker = class
|
|
protected
|
|
FScheme: Integer;
|
|
FStartPos: Integer;
|
|
FMarkerLen: Integer;
|
|
FMarkerText: UnicodeString;
|
|
FIsOpenMarker: Boolean;
|
|
public
|
|
constructor Create(aScheme, aStartPos, aMarkerLen: Integer;
|
|
aIsOpenMarker: Boolean; const aMarkerText: UnicodeString);
|
|
end;
|
|
|
|
|
|
TRangeOperation = (roGet, roSet);
|
|
|
|
{$IFDEF SYN_COMPILER_16_UP}
|
|
TRangeUNativeInt = NativeUInt;
|
|
{$ELSE}
|
|
TRangeUNativeInt = Cardinal;
|
|
{$ENDIF}
|
|
TRangeProc = procedure (Operation: TRangeOperation; var Range: TRangeUNativeInt) of object;
|
|
|
|
TCustomRangeEvent = procedure (Sender: TSynMultiSyn; Operation: TRangeOperation;
|
|
var Range: Pointer) of object;
|
|
|
|
{
|
|
* Usage notes *
|
|
If you don't need to nest MultiSyns as Schemes, just as DefaultHighlighter,
|
|
you can nest up to 2 MultiSyns, each of them containing up to 7 Schemes. This
|
|
is the way MultiSyn works best. (implemented in NewRangeProc)
|
|
If you need to use a MultiSyn nested as Scheme, then you can nest up to
|
|
5 MultiSyns, but Ranges aren't persisted across occurrences of Schemes that
|
|
have multiple lines. (implemented in OldRangeProc)
|
|
Clarification: when I say "you can nest up to X" MultiSyns, I mean having
|
|
X+1 levels of MultiSyns.
|
|
|
|
MultiSyn doesn't work by default with dynamic highlighters; you must use
|
|
OnCustomRange. This is because dynamic highlighters' Ranges are pointers,
|
|
but MultiSyn needs Ranges to be ordinal values smaller than 16 (4 bits).
|
|
|
|
OnCustomRange:
|
|
When Operation is roGet, user should store in the 'Range' parameter the
|
|
information to allow restoring the current state of the highlighter.
|
|
When Operation is roSet, user should restore highlighter state (CurrScheme,
|
|
DefaultHighlighter.Range and, if the case, Schemes[CurrScheme].Range)
|
|
according to 'Range' value.
|
|
CurrScheme:
|
|
Index of the scheme that is currently parsing. DefaultHighlighter maps to -1.
|
|
|
|
* Implementation notes *
|
|
FTmpRange:
|
|
Using the OldRangeProc, FTmpRange was the only way to restore the Range
|
|
of the DefaultHighlighter after a Scheme spanned across multiple lines.
|
|
With the NewRangeProc, the only use for it is restoring DefaultHighLighter's
|
|
Range in case a nested MultiSyn uses the highlighter too.
|
|
}
|
|
|
|
TSynMultiSyn = class(TSynCustomHighlighter)
|
|
private
|
|
FRangeProc: TRangeProc;
|
|
FDefaultLanguageName: string;
|
|
FMarkers: TList;
|
|
FMarker: TMarker;
|
|
FNextMarker: Integer;
|
|
FCurrScheme: Integer;
|
|
FTmpRange: Pointer;
|
|
FOnCustomRange: TCustomRangeEvent;
|
|
FLineStr: UnicodeString;
|
|
procedure SetDefaultHighlighter(const Value: TSynCustomHighLighter);
|
|
function GetMarkers(Index: Integer): TMarker;
|
|
property Markers[Index: Integer]: TMarker read GetMarkers;
|
|
procedure DoCheckMarker(Scheme:TScheme; StartPos, MarkerLen: Integer;
|
|
const MarkerText: UnicodeString; Start: Boolean; Line: Integer;
|
|
const LineStr: string);
|
|
procedure SetOnCustomRange(const Value: TCustomRangeEvent);
|
|
protected
|
|
FSchemes: TSchemes;
|
|
FDefaultHighlighter: TSynCustomHighLighter;
|
|
FLineNumber: Integer;
|
|
FSampleSource: UnicodeString;
|
|
procedure Loaded; override;
|
|
procedure SetSchemes(const Value: TSchemes);
|
|
procedure ClearMarkers;
|
|
function GetDefaultAttribute(Index: Integer): TSynHighlighterAttributes; override;
|
|
function GetAttribCount: Integer; override;
|
|
function GetAttribute(Index: Integer): TSynHighlighterAttributes; override;
|
|
procedure HookHighlighter(aHL: TSynCustomHighlighter);
|
|
procedure UnhookHighlighter(aHL: TSynCustomHighlighter);
|
|
procedure Notification(aComp: TComponent; aOp: TOperation); override;
|
|
function GetSampleSource: UnicodeString; override;
|
|
procedure SetSampleSource(Value: UnicodeString); override;
|
|
procedure DoSetLine(const Value: UnicodeString; LineNumber: Integer); override;
|
|
//
|
|
procedure OldRangeProc(Operation: TRangeOperation; var Range: TRangeUNativeInt);
|
|
procedure NewRangeProc(Operation: TRangeOperation; var Range: TRangeUNativeInt);
|
|
procedure UserRangeProc(Operation: TRangeOperation; var Range: TRangeUNativeInt);
|
|
public
|
|
class function GetLanguageName: string; override;
|
|
class function GetFriendlyLanguageName: UnicodeString; override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
function GetEol: Boolean; override;
|
|
function GetExpandedToken: UnicodeString; override;
|
|
function GetRange: Pointer; override;
|
|
function GetToken: UnicodeString; override;
|
|
function GetTokenAttribute: TSynHighlighterAttributes; override;
|
|
function GetTokenKind: Integer; override;
|
|
procedure Next; override;
|
|
procedure SetRange(Value: Pointer); override;
|
|
procedure ResetRange; override;
|
|
function UpdateRangeProcs: Boolean;
|
|
property CurrScheme: Integer read FCurrScheme write FCurrScheme;
|
|
property CurrLine: UnicodeString read FLineStr;
|
|
function LoadFromRegistry(RootKey: HKEY; Key: string): Boolean; override;
|
|
function SaveToRegistry(RootKey: HKEY; Key: string): Boolean; override;
|
|
function IsIdentChar(AChar: WideChar): Boolean; override;
|
|
published
|
|
property Schemes: TSchemes read FSchemes write SetSchemes;
|
|
property DefaultHighlighter: TSynCustomHighLighter read FDefaultHighlighter
|
|
write SetDefaultHighlighter;
|
|
property DefaultLanguageName: string read FDefaultLanguageName
|
|
write FDefaultLanguageName;
|
|
property OnCustomRange: TCustomRangeEvent read FOnCustomRange write SetOnCustomRange;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Graphics,
|
|
SynEditMiscProcs,
|
|
SynRegExpr,
|
|
SynEditStrConst,
|
|
SysUtils;
|
|
|
|
procedure CheckExpression(const Expr: UnicodeString);
|
|
var
|
|
Parser: TRegExpr;
|
|
begin
|
|
Parser := TRegExpr.Create;
|
|
try
|
|
Parser.Expression := Expr;
|
|
try
|
|
Parser.Compile;
|
|
except
|
|
on E: ERegExpr do
|
|
begin
|
|
if E.ErrorCode < 1000 then
|
|
E.Message := Format('"%s" is not a valid Regular Expression.'#13'Error (pos %d): %s',
|
|
[Expr, E.CompilerErrorPos, Copy(Parser.ErrorMsg(E.ErrorCode), 16, MaxInt)]);
|
|
raise;
|
|
end;
|
|
end;
|
|
finally
|
|
Parser.Free;
|
|
end;
|
|
end;
|
|
|
|
{ TMarker }
|
|
|
|
constructor TMarker.Create(aScheme, aStartPos,
|
|
aMarkerLen: Integer; aIsOpenMarker: Boolean; const aMarkerText: UnicodeString);
|
|
begin
|
|
FScheme := aScheme;
|
|
FStartPos := aStartPos;
|
|
FMarkerLen := aMarkerLen;
|
|
FIsOpenMarker := aIsOpenMarker;
|
|
FMarkerText := aMarkerText;
|
|
end;
|
|
|
|
{ TSynMultiSyn }
|
|
|
|
procedure TSynMultiSyn.ClearMarkers;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to FMarkers.Count - 1 do
|
|
TObject(FMarkers[i]).Free;
|
|
FMarkers.Clear;
|
|
end;
|
|
|
|
constructor TSynMultiSyn.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FSchemes := TSchemes.Create(Self);
|
|
FCurrScheme := -1;
|
|
FMarkers := TList.Create;
|
|
FRangeProc := NewRangeProc;
|
|
end;
|
|
|
|
destructor TSynMultiSyn.Destroy;
|
|
begin
|
|
ClearMarkers;
|
|
{ unhook notification handlers }
|
|
Schemes.Clear;
|
|
DefaultHighlighter := nil;
|
|
inherited Destroy;
|
|
FSchemes.Free;
|
|
FMarkers.Free;
|
|
end;
|
|
|
|
function TSynMultiSyn.GetAttribCount: Integer;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := Schemes.Count;
|
|
if DefaultHighlighter <> nil then
|
|
Inc(Result, DefaultHighlighter.AttrCount);
|
|
for i := 0 to Schemes.Count - 1 do
|
|
if Schemes[i].Highlighter <> nil then
|
|
Inc(Result, Schemes[i].Highlighter.AttrCount);
|
|
end;
|
|
|
|
function TSynMultiSyn.GetAttribute(Index: Integer): TSynHighlighterAttributes;
|
|
var
|
|
i: Integer;
|
|
HL: TSynCustomHighlighter;
|
|
begin
|
|
if Index < Schemes.Count then
|
|
Result := Schemes[Index].MarkerAttri
|
|
else
|
|
begin
|
|
Dec(Index, Schemes.Count);
|
|
if DefaultHighlighter <> nil then
|
|
if Index < DefaultHighlighter.AttrCount then
|
|
begin
|
|
Result := DefaultHighlighter.Attribute[Index];
|
|
Exit;
|
|
end
|
|
else
|
|
Dec(Index, DefaultHighlighter.AttrCount);
|
|
for i := 0 to Schemes.Count - 1 do
|
|
begin
|
|
HL := Schemes[i].Highlighter;
|
|
if HL <> nil then
|
|
if Index < HL.AttrCount then
|
|
begin
|
|
Result := HL.Attribute[Index];
|
|
Exit;
|
|
end
|
|
else
|
|
Dec(Index, HL.AttrCount);
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
end;
|
|
|
|
function TSynMultiSyn.GetDefaultAttribute(Index: Integer): TSynHighlighterAttributes;
|
|
var
|
|
HL: TSynCustomHighlighter;
|
|
begin
|
|
if (CurrScheme >= 0) and (Schemes[CurrScheme].Highlighter <> nil) then
|
|
HL := Schemes[CurrScheme].Highlighter
|
|
else
|
|
HL := DefaultHighlighter;
|
|
{ the typecast to TSynMultiSyn is only necessary because the
|
|
GetDefaultAttribute method is protected.
|
|
And don't worry: this really works }
|
|
if HL <> nil then
|
|
Result := TSynMultiSyn(HL).GetDefaultAttribute(Index)
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TSynMultiSyn.GetEol: Boolean;
|
|
begin
|
|
if FMarker <> nil then
|
|
Result := False
|
|
else if FCurrScheme >= 0 then
|
|
Result := Schemes[CurrScheme].Highlighter.GetEol
|
|
else if DefaultHighlighter <> nil then
|
|
Result := DefaultHighlighter.GetEol
|
|
else
|
|
Result := Run > FLineLen + 1;
|
|
end;
|
|
|
|
class function TSynMultiSyn.GetLanguageName: string;
|
|
begin
|
|
Result := SYNS_LangGeneralMulti;
|
|
end;
|
|
|
|
function TSynMultiSyn.GetMarkers(Index: Integer): TMarker;
|
|
begin
|
|
Result := TMarker(FMarkers[Index]);
|
|
end;
|
|
|
|
procedure TSynMultiSyn.OldRangeProc(Operation: TRangeOperation; var Range: TRangeUNativeInt);
|
|
const
|
|
MaxNestedMultiSyn = 6;
|
|
{ number of bits of the Range that will be used to store the SchemeIndex }
|
|
SchemeIndexSize = 4;
|
|
MaxSchemeCount = (1 shl SchemeIndexSize) - 1;
|
|
{ number of bits of the Range that will be used to store the SchemeRange }
|
|
SchemeRangeSize = 8;
|
|
MaxSchemeRange = (1 shl SchemeRangeSize) - 1;
|
|
var
|
|
iHL: TSynCustomHighlighter;
|
|
iSchemeIndex: Cardinal;
|
|
iSchemeRange: Cardinal;
|
|
begin
|
|
if Operation = roGet then
|
|
begin
|
|
if (FCurrScheme < 0) then
|
|
iHL := DefaultHighlighter
|
|
else
|
|
iHL := Schemes[FCurrScheme].Highlighter;
|
|
iSchemeIndex := FCurrScheme + 2;
|
|
Assert(iSchemeIndex <= MaxSchemeCount);
|
|
if iHL <> nil then
|
|
begin
|
|
iSchemeRange := Cardinal(iHL.GetRange);
|
|
Assert((iSchemeRange <= MaxSchemeRange) or (iHL is TSynMultiSyn));
|
|
end
|
|
else
|
|
iSchemeRange := 0;
|
|
{ checks the limit of nested MultiSyns }
|
|
Assert(iSchemeRange shr ((MaxNestedMultiSyn - 1) * SchemeIndexSize + SchemeRangeSize) = 0);
|
|
iSchemeRange := (iSchemeRange shl SchemeIndexSize) or iSchemeIndex;
|
|
Range := iSchemeRange;
|
|
end
|
|
else
|
|
begin
|
|
if Range = 0 then
|
|
Exit;
|
|
iSchemeRange := Cardinal(Range);
|
|
FCurrScheme := Integer(iSchemeRange and MaxSchemeCount) - 2;
|
|
iSchemeRange := iSchemeRange shr SchemeIndexSize;
|
|
if (CurrScheme < 0) then
|
|
begin
|
|
if DefaultHighlighter <> nil then
|
|
DefaultHighlighter.SetRange(Pointer(iSchemeRange));
|
|
end
|
|
else
|
|
Schemes[CurrScheme].Highlighter.SetRange(Pointer(iSchemeRange));
|
|
end;
|
|
end;
|
|
|
|
function TSynMultiSyn.GetToken: UnicodeString;
|
|
begin
|
|
if DefaultHighlighter = nil then
|
|
Result := FLineStr
|
|
else
|
|
Result := inherited GetToken;
|
|
end;
|
|
|
|
function TSynMultiSyn.GetTokenAttribute: TSynHighlighterAttributes;
|
|
begin
|
|
if FMarker <> nil then
|
|
Result := Schemes[FMarker.FScheme].MarkerAttri
|
|
else if CurrScheme >= 0 then
|
|
Result := Schemes[CurrScheme].Highlighter.GetTokenAttribute
|
|
else if DefaultHighlighter <> nil then
|
|
Result := DefaultHighlighter.GetTokenAttribute
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TSynMultiSyn.GetTokenKind: Integer;
|
|
begin
|
|
if FMarker <> nil then
|
|
Result := 0
|
|
else if FCurrScheme >= 0 then
|
|
Result := Schemes[FCurrScheme].Highlighter.GetTokenKind
|
|
else if DefaultHighlighter <> nil then
|
|
Result := DefaultHighlighter.GetTokenKind
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
procedure TSynMultiSyn.HookHighlighter(aHL: TSynCustomHighlighter);
|
|
begin
|
|
aHL.FreeNotification(Self);
|
|
aHL.HookAttrChangeEvent(DefHighlightChange);
|
|
end;
|
|
|
|
procedure TSynMultiSyn.Next;
|
|
var
|
|
iToken, TmpLine, ExpandedTmpLine: UnicodeString;
|
|
iHL: TSynCustomHighlighter;
|
|
begin
|
|
if DefaultHighlighter = nil then
|
|
begin
|
|
if Run > 0 then
|
|
Inc(Run)
|
|
else
|
|
Run := Length(FLineStr) + 1;
|
|
inherited;
|
|
Exit;
|
|
end;
|
|
|
|
if (FNextMarker < FMarkers.Count) and (Run + 1 >= Markers[FNextMarker].FStartPos) then
|
|
begin
|
|
FMarker := Markers[FNextMarker];
|
|
if FMarker.FIsOpenMarker then
|
|
begin
|
|
FCurrScheme := FMarker.FScheme;
|
|
FTmpRange := DefaultHighlighter.GetRange;
|
|
Schemes[CurrScheme].Highlighter.ResetRange;
|
|
end;
|
|
Inc(FNextMarker);
|
|
FTokenPos := Run;
|
|
Inc(Run, FMarker.FMarkerLen);
|
|
inherited;
|
|
Exit;
|
|
end;
|
|
|
|
if Run = 0 then
|
|
begin
|
|
if CurrScheme >= 0 then
|
|
iHL := Schemes[CurrScheme].Highlighter
|
|
else
|
|
iHL := DefaultHighlighter;
|
|
|
|
if FMarkers.Count = 0 then
|
|
TmpLine := FLineStr
|
|
else
|
|
TmpLine := Copy(FLineStr, 1, Markers[FNextMarker].FStartPos - 1);
|
|
|
|
if fExpandedLine <> nil then
|
|
begin
|
|
if FMarkers.Count = 0 then
|
|
ExpandedTmpLine := FExpandedLineStr
|
|
else
|
|
ExpandedTmpLine := Copy(FExpandedLineStr, 1,
|
|
PosToExpandedPos(Markers[FNextMarker].FStartPos - 1));
|
|
iHL.SetLineExpandedAtWideGlyphs(TmpLine, ExpandedTmpLine, FLineNumber);
|
|
end
|
|
else
|
|
iHL.SetLine(TmpLine, FLineNumber);
|
|
end
|
|
else if FMarker <> nil then
|
|
begin
|
|
if not FMarker.FIsOpenMarker then
|
|
begin
|
|
FCurrScheme := -1;
|
|
DefaultHighlighter.SetRange(FTmpRange);
|
|
end;
|
|
FMarker := nil;
|
|
|
|
if CurrScheme >= 0 then
|
|
iHL := Schemes[CurrScheme].Highlighter
|
|
else
|
|
iHL := DefaultHighlighter;
|
|
|
|
if FNextMarker < FMarkers.Count then
|
|
TmpLine := Copy(FLineStr, Run + 1, Markers[FNextMarker].FStartPos - Run - 1)
|
|
else
|
|
TmpLine := Copy(FLineStr, Run + 1, MaxInt);
|
|
|
|
if fExpandedLine <> nil then
|
|
begin
|
|
if FNextMarker < FMarkers.Count then
|
|
ExpandedTmpLine := Copy(FExpandedLineStr, FExpandedRun + 1,
|
|
PosToExpandedPos(Markers[FNextMarker].FStartPos - Run - 1))
|
|
else
|
|
ExpandedTmpLine := Copy(FExpandedLineStr, FExpandedRun + 1, MaxInt);
|
|
|
|
iHL.SetLineExpandedAtWideGlyphs(TmpLine, ExpandedTmpLine, FLineNumber);
|
|
end
|
|
else
|
|
iHL.SetLine(TmpLine, FLineNumber);
|
|
end
|
|
else
|
|
begin
|
|
if CurrScheme >= 0 then
|
|
iHL := Schemes[CurrScheme].Highlighter
|
|
else
|
|
iHL := DefaultHighlighter;
|
|
iHL.Next;
|
|
end;
|
|
|
|
FTokenPos := iHL.GetTokenPos;
|
|
iToken := iHL.GetToken;
|
|
if FNextMarker > 0 then
|
|
with Markers[FNextMarker - 1] do
|
|
Inc(FTokenPos, FStartPos + FMarkerLen - 1);
|
|
Inc(Run, (FTokenPos - Run) + Length(iToken));
|
|
inherited;
|
|
end;
|
|
|
|
procedure TSynMultiSyn.Notification(aComp: TComponent; aOp: TOperation);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
inherited;
|
|
// 'opRemove' doesn't mean the component is being destroyed. It means it's
|
|
// being removed from its Owner's list of Components.
|
|
if (aOp = opRemove) and (aComp is TSynCustomHighlighter) and
|
|
(csDestroying in aComp.ComponentState) then
|
|
begin
|
|
if DefaultHighlighter = aComp then
|
|
DefaultHighlighter := nil;
|
|
for i := 0 to Schemes.Count - 1 do
|
|
if Schemes[i].Highlighter = aComp then
|
|
Schemes[i].Highlighter := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynMultiSyn.ResetRange;
|
|
begin
|
|
FCurrScheme := -1;
|
|
if DefaultHighlighter <> nil then
|
|
begin
|
|
DefaultHighlighter.ResetRange;
|
|
FTmpRange := DefaultHighlighter.GetRange;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynMultiSyn.SetDefaultHighlighter(
|
|
const Value: TSynCustomHighLighter);
|
|
const
|
|
sDefaultHlSetToSelf = 'A SynMultiSyn cannot be its own DefaultHighlighter.';
|
|
begin
|
|
if DefaultHighlighter <> Value then begin
|
|
if Value = Self then
|
|
raise Exception.Create(sDefaultHlSetToSelf);
|
|
if DefaultHighlighter <> nil then
|
|
UnhookHighlighter(DefaultHighlighter);
|
|
FDefaultHighlighter := Value;
|
|
if DefaultHighlighter <> nil then
|
|
HookHighlighter(DefaultHighlighter);
|
|
DefHighlightChange(Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TSynMultiSyn.DoCheckMarker(Scheme:TScheme; StartPos, MarkerLen: Integer;
|
|
const MarkerText: UnicodeString; Start: Boolean; Line: Integer;
|
|
const LineStr: string);
|
|
var
|
|
aStartPos: Integer;
|
|
aMarkerLen: Integer;
|
|
aMarkerText: UnicodeString;
|
|
begin
|
|
aStartPos := StartPos;
|
|
aMarkerLen := MarkerLen;
|
|
aMarkerText := MarkerText;
|
|
if Start and Assigned(Scheme.OnCheckStartMarker) then
|
|
Scheme.OnCheckStartMarker(Self, aStartPos, aMarkerLen, aMarkerText, Line, LineStr)
|
|
else if not Start and Assigned(Scheme.OnCheckEndMarker) then
|
|
Scheme.OnCheckEndMarker(Self, aStartPos, aMarkerLen, aMarkerText, Line, LineStr);
|
|
if (aMarkerText <> '') and (aMarkerLen > 0) then
|
|
begin
|
|
FMarkers.Add(TMarker.Create(Scheme.Index, aStartPos, aMarkerLen, Start,
|
|
aMarkerText));
|
|
end;
|
|
end;
|
|
|
|
procedure TSynMultiSyn.SetSchemes(const Value: TSchemes);
|
|
begin
|
|
FSchemes.Assign(Value);
|
|
end;
|
|
|
|
procedure TSynMultiSyn.UnhookHighlighter(aHL: TSynCustomHighlighter);
|
|
begin
|
|
aHL.UnhookAttrChangeEvent(DefHighlightChange);
|
|
{$IFDEF SYN_COMPILER_5_UP}
|
|
aHL.RemoveFreeNotification(Self);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TSynMultiSyn.GetSampleSource: UnicodeString;
|
|
begin
|
|
Result := FSampleSource;
|
|
end;
|
|
|
|
procedure TSynMultiSyn.SetSampleSource(Value: UnicodeString);
|
|
begin
|
|
FSampleSource := Value;
|
|
end;
|
|
|
|
function TSynMultiSyn.LoadFromRegistry(RootKey: HKEY;
|
|
Key: string): Boolean;
|
|
var
|
|
r: TBetterRegistry;
|
|
i: Integer;
|
|
begin
|
|
if DefaultHighlighter <> nil then
|
|
Result := DefaultHighlighter.LoadFromRegistry(RootKey, Key + '\DefaultHighlighter')
|
|
else
|
|
Result := False;
|
|
r := TBetterRegistry.Create;
|
|
try
|
|
r.RootKey := RootKey;
|
|
for i := 0 to Schemes.Count-1 do
|
|
if (Schemes[i].SchemeName <> '') and
|
|
r.OpenKeyReadOnly(Key + '\' + Schemes[i].SchemeName) then
|
|
begin
|
|
Result := Schemes[i].MarkerAttri.LoadFromRegistry(r) and Result;
|
|
r.CloseKey;
|
|
Result := (Schemes[i].Highlighter <> nil) and
|
|
Schemes[i].Highlighter.LoadFromRegistry(RootKey,
|
|
Key + '\' + Schemes[i].SchemeName) and Result;
|
|
end
|
|
else
|
|
Result := False;
|
|
finally
|
|
r.Free;
|
|
end;
|
|
end;
|
|
|
|
function TSynMultiSyn.SaveToRegistry(RootKey: HKEY; Key: string): Boolean;
|
|
var
|
|
r: TBetterRegistry;
|
|
i: Integer;
|
|
begin
|
|
if DefaultHighlighter <> nil then
|
|
Result := DefaultHighlighter.SaveToRegistry(RootKey, Key + '\DefaultHighlighter')
|
|
else
|
|
Result := False;
|
|
r := TBetterRegistry.Create;
|
|
try
|
|
r.RootKey := RootKey;
|
|
for i := 0 to Schemes.Count-1 do
|
|
if (Schemes[i].SchemeName <> '') and
|
|
r.OpenKey(Key + '\' + Schemes[i].SchemeName, True) then
|
|
begin
|
|
Result := Schemes[i].MarkerAttri.SaveToRegistry(r) and Result;
|
|
r.CloseKey;
|
|
Result := (Schemes[i].Highlighter <> nil) and
|
|
Schemes[i].Highlighter.SaveToRegistry(RootKey,
|
|
Key + '\' + Schemes[i].SchemeName) and Result;
|
|
end
|
|
else
|
|
Result := False;
|
|
finally
|
|
r.Free;
|
|
end;
|
|
end;
|
|
|
|
function TSynMultiSyn.GetRange: Pointer;
|
|
begin
|
|
Result := nil;
|
|
FRangeProc(roGet, TRangeUNativeInt(Result));
|
|
end;
|
|
|
|
procedure TSynMultiSyn.SetRange(Value: Pointer);
|
|
begin
|
|
FRangeProc(roSet, TRangeUNativeInt(Value));
|
|
end;
|
|
|
|
procedure TSynMultiSyn.NewRangeProc(Operation: TRangeOperation; var Range: TRangeUNativeInt);
|
|
const
|
|
SchemeIndexSize = 3;
|
|
MaxSchemeCount = (1 shl SchemeIndexSize) - 1;
|
|
SchemeRangeSize = 4;
|
|
MaxSchemeRange = (1 shl SchemeRangeSize) - 1;
|
|
begin
|
|
if Operation = roGet then
|
|
begin
|
|
if DefaultHighlighter <> nil then
|
|
Range := Cardinal(DefaultHighlighter.GetRange)
|
|
else
|
|
Range := 0;
|
|
if CurrScheme >= 0 then
|
|
begin
|
|
Assert(Cardinal(Schemes[CurrScheme].Highlighter.GetRange) <= MaxSchemeRange);
|
|
Range := Range shl SchemeRangeSize;
|
|
Range := Range or Cardinal(Schemes[CurrScheme].Highlighter.GetRange);
|
|
end;
|
|
Assert(CurrScheme <= MaxSchemeCount);
|
|
Range := Range shl SchemeIndexSize;
|
|
Range := Range or Cardinal(CurrScheme + 1);
|
|
end
|
|
else
|
|
begin
|
|
CurrScheme := Integer(Range and MaxSchemeCount) - 1;
|
|
Range := Range shr SchemeIndexSize;
|
|
if CurrScheme >= 0 then
|
|
begin
|
|
Schemes[CurrScheme].Highlighter.SetRange(Pointer(Range and MaxSchemeRange));
|
|
Range := Range shr SchemeRangeSize;
|
|
end;
|
|
if DefaultHighlighter <> nil then
|
|
begin
|
|
FTmpRange := Pointer(Range);
|
|
DefaultHighlighter.SetRange(FTmpRange);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TSynMultiSyn.UpdateRangeProcs: Boolean;
|
|
// determines the appropriate RangeProcs and returns whether they were changed
|
|
var
|
|
i: Integer;
|
|
OldProc: TRangeProc;
|
|
begin
|
|
OldProc := FRangeProc;
|
|
if Assigned(OnCustomRange) then
|
|
FRangeProc := UserRangeProc
|
|
else begin
|
|
FRangeProc := NewRangeProc;
|
|
for i := 0 to Schemes.Count -1 do
|
|
if Schemes[i].Highlighter is TSynMultiSyn then
|
|
begin
|
|
FRangeProc := OldRangeProc;
|
|
Break;
|
|
end;
|
|
end;
|
|
Result := TMethod(OldProc).Code <> TMethod(FRangeProc).Code;
|
|
if Result then
|
|
DefHighlightChange(Self);
|
|
end;
|
|
|
|
procedure TSynMultiSyn.UserRangeProc(Operation: TRangeOperation; var Range: TRangeUNativeInt);
|
|
begin
|
|
OnCustomRange(Self, Operation, Pointer(Range));
|
|
if (Operation = roSet) and (DefaultHighlighter <> nil) then
|
|
FTmpRange := DefaultHighlighter.GetRange;
|
|
end;
|
|
|
|
procedure TSynMultiSyn.SetOnCustomRange(const Value: TCustomRangeEvent);
|
|
begin
|
|
if (TMethod(OnCustomRange).Code <> TMethod(Value).Code) or
|
|
(TMethod(OnCustomRange).Data <> TMethod(Value).Data) then
|
|
begin
|
|
FOnCustomRange := Value;
|
|
UpdateRangeProcs;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynMultiSyn.Loaded;
|
|
begin
|
|
inherited;
|
|
DefHighlightChange(Self);
|
|
end;
|
|
|
|
function TSynMultiSyn.IsIdentChar(AChar: WideChar): Boolean;
|
|
begin
|
|
if CurrScheme >= 0 then
|
|
Result := Schemes[CurrScheme].Highlighter.IsIdentChar(AChar)
|
|
else if DefaultHighlighter <> nil then
|
|
Result := DefaultHighlighter.IsIdentChar(AChar)
|
|
else
|
|
Result := inherited IsIdentChar(AChar);
|
|
end;
|
|
|
|
class function TSynMultiSyn.GetFriendlyLanguageName: UnicodeString;
|
|
begin
|
|
Result := SYNS_FriendlyLangGeneralMulti;
|
|
end;
|
|
|
|
procedure TSynMultiSyn.DoSetLine(const Value: UnicodeString; LineNumber: Integer);
|
|
var
|
|
iParser: TRegExpr;
|
|
iScheme: TScheme;
|
|
iExpr: UnicodeString;
|
|
iLine: UnicodeString;
|
|
iEaten: Integer;
|
|
i: Integer;
|
|
begin
|
|
ClearMarkers;
|
|
|
|
iParser := TRegExpr.Create;
|
|
try
|
|
iEaten := 0;
|
|
iLine := Value;
|
|
if CurrScheme >= 0
|
|
then
|
|
iScheme := FSchemes[CurrScheme]
|
|
else
|
|
iScheme := nil;
|
|
while iLine <> '' do
|
|
if iScheme <> nil then
|
|
begin
|
|
iParser.Expression := iScheme.EndExpr;
|
|
iParser.ModifierI := not iScheme.CaseSensitive;
|
|
if iParser.Exec(iLine) then
|
|
begin
|
|
iExpr := Copy(Value, iParser.MatchPos[0] + iEaten, iParser.MatchLen[0]);
|
|
DoCheckMarker(iScheme, iParser.MatchPos[0] + iEaten, iParser.MatchLen[0],
|
|
iExpr, False, LineNumber, Value);
|
|
Delete(iLine, 1, iParser.MatchPos[0] - 1 + iParser.MatchLen[0]);
|
|
Inc(iEaten, iParser.MatchPos[0] - 1 + iParser.MatchLen[0]);
|
|
iScheme := nil;
|
|
end
|
|
else
|
|
Break;
|
|
end
|
|
else
|
|
begin
|
|
for i := 0 to Schemes.Count - 1 do
|
|
begin
|
|
iScheme := Schemes[i];
|
|
if (iScheme.StartExpr = '') or (iScheme.EndExpr = '') or
|
|
(iScheme.Highlighter = nil) or (not iScheme.Highlighter.Enabled) then
|
|
begin
|
|
continue;
|
|
end;
|
|
iParser.Expression := iScheme.StartExpr;
|
|
iParser.ModifierI := not iScheme.CaseSensitive;
|
|
if iParser.Exec(iLine) then begin
|
|
iExpr := Copy(Value, iParser.MatchPos[0] + iEaten, iParser.MatchLen[0]);
|
|
DoCheckMarker(iScheme, iParser.MatchPos[0] + iEaten, iParser.MatchLen[0],
|
|
iExpr, True, LineNumber, Value);
|
|
Delete(iLine, 1, iParser.MatchPos[0] - 1 + iParser.MatchLen[0]);
|
|
Inc(iEaten, iParser.MatchPos[0] - 1 + iParser.MatchLen[0]);
|
|
Break;
|
|
end;
|
|
end; {for}
|
|
if i >= Schemes.Count then
|
|
Break;
|
|
end; {else}
|
|
|
|
finally
|
|
iParser.Free;
|
|
end;
|
|
|
|
FLineStr := Value;
|
|
fLine := PWideChar(FLineStr);
|
|
fCasedLineStr := '';
|
|
fCasedLine := PWideChar(FLineStr);
|
|
|
|
FMarker := nil;
|
|
Run := 0;
|
|
FExpandedRun := 0;
|
|
fOldRun := Run;
|
|
FTokenPos := 0;
|
|
fExpandedTokenPos := 0;
|
|
FNextMarker := 0;
|
|
FLineNumber := LineNumber;
|
|
end;
|
|
|
|
function TSynMultiSyn.GetExpandedToken: UnicodeString;
|
|
begin
|
|
if (DefaultHighlighter = nil) and (fExpandedLine <> nil) then
|
|
Result := FExpandedLineStr
|
|
else
|
|
Result := inherited GetExpandedToken;
|
|
end;
|
|
|
|
{ TSchemes }
|
|
|
|
constructor TSchemes.Create(aOwner: TSynMultiSyn);
|
|
begin
|
|
inherited Create(TScheme);
|
|
FOwner := aOwner;
|
|
end;
|
|
|
|
function TSchemes.GetItems(Index: Integer): TScheme;
|
|
begin
|
|
Result := inherited Items[Index] as TScheme;
|
|
end;
|
|
|
|
{$IFDEF SYN_COMPILER_3_UP}
|
|
function TSchemes.GetOwner: TPersistent;
|
|
begin
|
|
Result := FOwner;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure TSchemes.SetItems(Index: Integer; const Value: TScheme);
|
|
begin
|
|
inherited Items[Index] := Value;
|
|
end;
|
|
|
|
{$IFDEF SYN_COMPILER_3_UP}
|
|
procedure TSchemes.Update(Item: TCollectionItem);
|
|
begin
|
|
if Item <> nil then
|
|
FOwner.DefHighlightChange(Item)
|
|
else // pass the MultiSyn as the Sender so Editors reparse their text
|
|
FOwner.DefHighlightChange(FOwner);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{ TScheme }
|
|
|
|
function TScheme.ConvertExpression(const Value: UnicodeString): UnicodeString;
|
|
begin
|
|
if not CaseSensitive then
|
|
Result := SynWideUpperCase(Value)
|
|
else
|
|
Result := Value;
|
|
end;
|
|
|
|
constructor TScheme.Create(Collection: TCollection);
|
|
begin
|
|
inherited Create(Collection);
|
|
FCaseSensitive := True;
|
|
FMarkerAttri := TSynHighlighterAttributes.Create(SYNS_AttrMarker, SYNS_FriendlyAttrMarker);
|
|
FMarkerAttri.OnChange := MarkerAttriChanged;
|
|
MarkerAttri.Background := clYellow;
|
|
MarkerAttri.Style := [fsBold];
|
|
MarkerAttri.InternalSaveDefaultValues;
|
|
end;
|
|
|
|
destructor TScheme.Destroy;
|
|
begin
|
|
{ unhook notification handlers }
|
|
Highlighter := nil;
|
|
inherited Destroy;
|
|
FMarkerAttri.Free;
|
|
end;
|
|
|
|
procedure TScheme.DefineProperties(Filer: TFiler);
|
|
begin
|
|
inherited;
|
|
{$IFNDEF UNICODE}
|
|
UnicodeDefineProperties(Filer, Self);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{$IFDEF SYN_COMPILER_3_UP}
|
|
function TScheme.GetDisplayName: string;
|
|
begin
|
|
if SchemeName <> '' then
|
|
Result := SchemeName
|
|
else
|
|
Result := inherited GetDisplayName;
|
|
end;
|
|
{$ENDIF SYN_COMPILER_3_UP}
|
|
|
|
procedure TScheme.MarkerAttriChanged(Sender: TObject);
|
|
begin
|
|
Changed(False);
|
|
end;
|
|
|
|
procedure TScheme.SetCaseSensitive(const Value: Boolean);
|
|
begin
|
|
if FCaseSensitive <> Value then
|
|
begin
|
|
FCaseSensitive := Value;
|
|
Changed(True);
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF SYN_COMPILER_3_UP}
|
|
procedure TScheme.SetDisplayName(const Value: string);
|
|
begin
|
|
SchemeName := Value;
|
|
end;
|
|
{$ENDIF SYN_COMPILER_3_UP}
|
|
|
|
procedure TScheme.SetEndExpr(const Value: UnicodeString);
|
|
var
|
|
OldValue: UnicodeString;
|
|
begin
|
|
if FEndExpr <> Value then
|
|
begin
|
|
if Value <> '' then
|
|
CheckExpression(Value);
|
|
OldValue := FEndExpr;
|
|
FEndExpr := Value;
|
|
if ConvertExpression(OldValue) <> ConvertExpression(Value) then
|
|
Changed(True);
|
|
end;
|
|
end;
|
|
|
|
procedure TScheme.SetHighlighter(const Value: TSynCustomHighLighter);
|
|
var
|
|
iOwner: TSynMultiSyn;
|
|
iAlreadyRepainted: Boolean;
|
|
begin
|
|
if Highlighter <> Value then
|
|
begin
|
|
iOwner := TSchemes(Collection).FOwner;
|
|
if (Highlighter <> nil) and (Highlighter <> iOwner) then
|
|
iOwner.UnhookHighlighter(Highlighter);
|
|
FHighlighter := Value;
|
|
if (Highlighter <> nil) and (Highlighter <> iOwner) then
|
|
iOwner.HookHighlighter(Highlighter);
|
|
if Highlighter is TSynMultiSyn then
|
|
iAlreadyRepainted := iOwner.UpdateRangeProcs
|
|
else
|
|
iAlreadyRepainted := False;
|
|
if not iAlreadyRepainted then
|
|
Changed(True);
|
|
end;
|
|
end;
|
|
|
|
procedure TScheme.SetMarkerAttri(const Value: TSynHighlighterAttributes);
|
|
begin
|
|
FMarkerAttri.Assign(Value);
|
|
end;
|
|
|
|
procedure TScheme.SetStartExpr(const Value: UnicodeString);
|
|
var
|
|
OldValue: UnicodeString;
|
|
begin
|
|
if FStartExpr <> Value then
|
|
begin
|
|
if Value <> '' then
|
|
CheckExpression(Value);
|
|
OldValue := FStartExpr;
|
|
FStartExpr := Value;
|
|
if ConvertExpression(Value) <> ConvertExpression(OldValue) then
|
|
Changed(True);
|
|
end;
|
|
end;
|
|
|
|
end.
|