lazarus/components/synedit/synhighlightermulti.pas
2025-03-01 13:28:22 +01:00

2067 lines
66 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.
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.
You may retrieve the latest version of this file at the SynEdit home page,
located at http://SynEdit.SourceForge.net
-------------------------------------------------------------------------------}
{
@created(1999, converted to SynEdit 2000-06-23)
@author(Willo van der Merwe <willo@wack.co.za>
@converted to SynEdit by David Muir <dhm@dmsoftware.co.uk>)
@mostly rewritten for Lazarus by M. Friebe 04/2010
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}
{$IFDEF SynDebug}
{$DEFINE SynDebugMultiHL}
{$ENDIF}
interface
uses
Classes, Graphics, SysUtils, Math, RegExpr,
SynEditStrConst, SynEditTypes, SynEditTextBase,
SynEditHighlighter,
{$IFDEF SynDebugMultiHL}LazLoggerBase{$ELSE}LazLoggerDummy{$ENDIF}, LazUTF8
;
type
TSynHighlighterMultiScheme=class;
TSynMultiSyn = class;
TSynHLightMultiVirtualSection = record
// X(Char): 1-based
// Y(Line): 0-based
StartPos, EndPos: TPoint;
TokenStartPos, TokenEndPos: Integer;
VirtualLine: Integer;
end;
PSynHLightMultiVirtualSection = ^TSynHLightMultiVirtualSection;
{ TSynHLightMultiSectionList }
(* List of all parts of the original TextBuffer, which are to be scanned by one highlighter *)
TSynHLightMultiSectionList=class(TSynEditStorageMem)
private
function GetSection(Index: Integer): TSynHLightMultiVirtualSection;
function GetSectionPointer(Index: Integer): PSynHLightMultiVirtualSection;
procedure SetSection(Index: Integer; const AValue: TSynHLightMultiVirtualSection);
public
constructor Create;
procedure Debug;
procedure Insert(AnIndex: Integer; AnSection: TSynHLightMultiVirtualSection);
procedure Delete(AnIndex: Integer);
property Sections[Index: Integer]: TSynHLightMultiVirtualSection
read GetSection write SetSection; default;
property PSections[Index: Integer]: PSynHLightMultiVirtualSection
read GetSectionPointer;
function IndexOfFirstSectionAtLineIdx(ALineIdx: Integer; ACharPos: Integer = -1;
UseNext: Boolean = True): Integer;
function IndexOfFirstSectionAtVirtualIdx(ALineIdx: Integer; AGetLastSection: Boolean = False): Integer;
function VirtualIdxToRealIdx(AVLineIdx: Integer): Integer;
end;
{ TSynHLightMultiVirtualLines }
TSynHLightMultiVirtualLines=class(TSynEditStringsBase)
private
FFirstHLChangedLine: Integer;
FLastHLChangedLine: Integer;
FRangeList: TSynManagedStorageMemList;
FRealLines: TSynEditStringsBase;
FScheme: TSynHighlighterMultiScheme;
FSectionList: TSynHLightMultiSectionList;
FRScanStartedWithLineCount: Integer;
FRScanStartedAtVLine: Integer;
FRegionScanStartRangeIndex: Integer;
FRegionScanRangeIndex: Integer;
FLastPCharLine: String;
protected
function GetRange(Index: Pointer): TSynManagedStorageMem; override;
procedure PutRange(Index: Pointer; const ARange: TSynManagedStorageMem); override;
function Get(Index: integer): string; override;
procedure Put(Index: integer; const S: string); override; // should not be called ever
function GetCount: integer; override;
public
constructor Create(ALines: TSynEditStringsBase);
destructor Destroy; override;
procedure Debug;
procedure Clear; override; // should not be called ever
procedure Delete(Index: Integer); override; // should not be called ever
procedure Insert(Index: Integer; const S: string); override; // should not be called ever
function GetPChar(ALineIndex: Integer; out ALen: Integer): PChar; override; // experimental
procedure SendHighlightChanged(aIndex, aCount: Integer); override;
procedure PrepareRegionScan(AStartLineIdx: Integer);
procedure FinishRegionScan(AEndLineIdx: Integer);
procedure RegionScanUpdateFirstRegionEnd(AnEndPoint: TPoint; ATokenEndPos: Integer);
procedure RegionScanUpdateOrInsertRegion(AStartPoint, AnEndPoint: TPoint;
ATokenStartPos, ATokenEndPos: Integer);
procedure RegionScanUpdateLastRegionStart(AStartPoint: TPoint;
ATokenStartPos: Integer; ALineIndex: Integer);
procedure RealLinesInserted(AIndex, ACount: Integer);
procedure RealLinesDeleted(AIndex, ACount: Integer);
procedure RealLinesChanged(AIndex, ACount: Integer);
procedure ResetHLChangedLines;
property FirstHLChangedLine: Integer read FFirstHLChangedLine;
property LastHLChangedLine: Integer read FLastHLChangedLine;
property SectionList: TSynHLightMultiSectionList read FSectionList;
property Scheme: TSynHighlighterMultiScheme
read FScheme write FScheme;
end;
{ TSynHLightMultiVirtualLinesList }
TSynHLightMultiVirtualLinesList=class(TFPList)
private
function GetVLines(Index: Integer): TSynHLightMultiVirtualLines;
procedure PutVLines(Index: Integer; const AValue: TSynHLightMultiVirtualLines);
public
property Items[Index: Integer]: TSynHLightMultiVirtualLines
read GetVLines write PutVLines; default;
end;
TOnCheckMarker=procedure(Sender: TObject; var StartPos, MarkerLen: Integer;
var MarkerText: String) of object;
{ TSynHighlighterMultiScheme }
TSynHighlighterMultiScheme = class(TCollectionItem)
private
FNeedHLScan: Boolean;
FStartExpr, FEndExpr: string;
FConvertedStartExpr, FConvertedEndExpr: String;
FStartExprScanner, FEndExprScanner: TRegExpr;
FStartLineSet, FEndLineSet: Boolean;
FLastMatchLen: Integer;
FHighlighter: TSynCustomHighLighter;
fMarkerAttri: TSynHighlighterAttributes;
fSchemeName: TComponentName;
fCaseSensitive: Boolean;
fOnCheckStartMarker: TOnCheckMarker;
fOnCheckEndMarker: TOnCheckMarker;
FVirtualLines: TSynHLightMultiVirtualLines;
function GetConvertedLine: String;
function GetConvertedEndExpr: String;
function GetConvertedStartExpr: String;
procedure MarkerAttriChanged(Sender: TObject);
procedure SetMarkerAttri(const Value: TSynHighlighterAttributes);
procedure SetHighlighter(const Value: TSynCustomHighlighter);
procedure SetEndExpr(const Value: string);
procedure SetStartExpr(const Value: string);
procedure SetCaseSensitive(const Value: Boolean);
procedure SetVirtualLines(const AValue: TSynHLightMultiVirtualLines);
protected
function GetDisplayName: String; override;
procedure SetDisplayName(const Value: String); override;
public
constructor Create(TheCollection: TCollection); override;
destructor Destroy; override;
public
procedure ClearLinesSet;
function FindStartPosInLine(ASearchPos: Integer): Integer;
function FindEndPosInLine(ASearchPos: Integer): Integer;
property LastMatchLen: Integer read FLastMatchLen;
property NeedHLScan: Boolean read FNeedHLScan;
public
property VirtualLines: TSynHLightMultiVirtualLines
read FVirtualLines write SetVirtualLines;
published
property CaseSensitive: Boolean read fCaseSensitive write SetCaseSensitive
default True;
property StartExpr: string read fStartExpr write SetStartExpr;
property EndExpr: string 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;
{ TSynHighlighterMultiSchemeList }
TSynHighlighterMultiSchemeList = class(TCollection)
private
FCurrentLine, FConvertedCurrentLine: String;
FOwner: TSynMultiSyn;
function GetConvertedCurrentLine: String;
function GetItems(Index: integer): TSynHighlighterMultiScheme;
procedure SetCurrentLine(const AValue: String);
procedure SetItems(Index: integer; const Value: TSynHighlighterMultiScheme);
protected
function GetOwner: TPersistent; override;
procedure Update(Item: TCollectionItem); override;
procedure Notify(Item: TCollectionItem;Action: TCollectionNotification); override;
public
constructor Create(aOwner: TSynMultiSyn);
property Items[aIndex: integer]: TSynHighlighterMultiScheme read GetItems write SetItems;
default;
function IndexOf(AnItem: TSynHighlighterMultiScheme): Integer;
public
property ConvertedCurrentLine: String read GetConvertedCurrentLine;
property CurrentLine: String read FCurrentLine write SetCurrentLine;
property Owner: TSynMultiSyn read FOwner;
end;
{ TSynHighlighterMultiRangeList }
TSynHighlighterMultiRangeList = class(TSynHighlighterRangeList)
private
FLines: TSynEditStringsBase;
FDefaultVirtualLines: TSynHLightMultiVirtualLines;
FVirtualLines: TSynHLightMultiVirtualLinesList;
function GetVirtualLines(Index: TSynHighlighterMultiScheme): TSynHLightMultiVirtualLines;
protected
procedure LineTextChanged(AIndex: Integer; ACount: Integer = 1); override;
procedure InsertedLines(AIndex, ACount: Integer); override;
procedure DeletedLines(AIndex, ACount: Integer); override;
public
constructor Create(ALines: TSynEditStringsBase);
destructor Destroy; override;
procedure ClearVLines;
procedure UpdateForScheme(AScheme: TSynHighlighterMultiSchemeList);
procedure CleanUpForScheme(AScheme: TSynHighlighterMultiSchemeList);
procedure CopyToScheme(AScheme: TSynHighlighterMultiSchemeList);
property DefaultVirtualLines: TSynHLightMultiVirtualLines read FDefaultVirtualLines;
property VirtualLines[Index: TSynHighlighterMultiScheme]: TSynHLightMultiVirtualLines
read GetVirtualLines; // write SetVirtualLines;
end;
TRunSectionInfo = record
SectionIdx: Integer;
VirtualStartPos: Integer; // Position in the Virtual line (without token)
FirstChar, LastChar: Integer; // Position of the Real Line that is mapped
TokenFirstChar, TokenLastChar: Integer;
end;
{ TSynMultiSyn }
TSynMultiSyn = class(TSynCustomHighLighter)
private
FDefaultLanguageName: String;
FCurScheme: TSynHighlighterMultiScheme;
function GetCurrentRanges: TSynHighlighterMultiRangeList;
function GetDefaultVirtualLines: TSynHLightMultiVirtualLines;
function GetKnownMultiRanges(Index: Integer): TSynHighlighterMultiRangeList;
procedure SetDefaultHighlighter(const Value: TSynCustomHighLighter);
procedure SetSchemes(const Value: TSynHighlighterMultiSchemeList);
function CurrentVirtualLines: TSynHLightMultiVirtualLines;
protected
FSchemes: TSynHighlighterMultiSchemeList;
FDefaultHighlighter: TSynCustomHighLighter;
FLine: string;
FCurLineIndex, FLineLen: Integer;
FTokenPos: integer;
FTokenKind: integer;
FTokenAttr: TSynHighlighterAttributes;
FRun: Integer;
FRunSectionInfo: Array of TRunSectionInfo;
FSampleSource: string;
function GetIdentChars: TSynIdentChars; override;
function GetDefaultAttribute(Index: integer): TSynHighlighterAttributes; override;
function GetAttribCount: integer; override;
function GetAttribute(idx: integer): TSynHighlighterAttributes; override;
function GetSampleSource: string; override;
procedure SetSampleSource(Value: string); override;
procedure HookHighlighter(aHL: TSynCustomHighlighter);
procedure UnhookHighlighter(aHL: TSynCustomHighlighter);
procedure Notification(aComp: TComponent; aOp: TOperation); override;
function CreateRangeList(ALines: TSynEditStringsBase): TSynHighlighterRangeList; override;
procedure BeforeDetachedFromRangeList(ARangeList: TSynHighlighterRangeList); override;
procedure SetCurrentLines(const AValue: TSynEditStringsBase); override;
procedure SchemeItemChanged(Item: TObject);
procedure SchemeChanged;
procedure DetachHighlighter(AHighlighter: TSynCustomHighlighter; AScheme: TSynHighlighterMultiScheme);
procedure AttachHighlighter(AHighlighter: TSynCustomHighlighter; AScheme: TSynHighlighterMultiScheme);
function PerformScan(StartIndex, EndIndex: Integer; ForceEndIndex: Boolean = False): Integer; override;
property CurrentRanges: TSynHighlighterMultiRangeList read GetCurrentRanges;
property KnownRanges[Index: Integer]: TSynHighlighterMultiRangeList read GetKnownMultiRanges;
public
class function GetLanguageName: string; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Next; override;
function GetEol: Boolean; override;
function GetToken: string; override;
procedure GetTokenEx(out TokenStart: PChar; out TokenLength: integer); override;
function GetTokenAttribute: TSynHighlighterAttributes; override;
function GetTokenKind: integer; override;
function GetTokenPos: Integer; override; // 0-based
procedure SetLine(const NewValue: string; LineNumber: Integer); override;
function UpdateRangeInfoAtLine(Index: Integer): Boolean; override;
function GetRange: Pointer; override;
procedure SetRange(Value: Pointer); override;
procedure ResetRange; override;
public
property DefaultVirtualLines: TSynHLightMultiVirtualLines read GetDefaultVirtualLines;
published
property Schemes: TSynHighlighterMultiSchemeList read fSchemes write SetSchemes;
property DefaultHighlighter: TSynCustomHighLighter read fDefaultHighlighter
write SetDefaultHighlighter;
property DefaultLanguageName: String read fDefaultLanguageName
write fDefaultLanguageName;
end;
function dbgs(const ASect: TSynHLightMultiVirtualSection): String; overload;
implementation
var
SYNDEBUG_MULTIHL: PLazLoggerLogGroup;
const
TokenKindPerHighlighter = 100;
operator > (p1, p2 : TPoint) b : boolean;
begin
Result := (p1.y > p2.y) or ( (p1.y = p2.y) and (p1.x > p2.x) );
end;
operator >= (p1, p2 : TPoint) b : boolean;
begin
Result := (p1.y > p2.y) or ( (p1.y = p2.y) and (p1.x >= p2.x) );
end;
operator < (p1, p2 : TPoint) b : boolean;
begin
Result := (p1.y < p2.y) or ( (p1.y = p2.y) and (p1.x < p2.x) );
end;
function dbgs(const ASect: TSynHLightMultiVirtualSection): String;
begin
Result := Format('Start=%s, End=%s, VLine=%d, TokStart=%d, TokEnd=%d',
[dbgs(ASect.StartPos), dbgs(ASect.EndPos), ASect.VirtualLine, ASect.TokenStartPos, ASect.TokenEndPos]);
end;
{ TSynHLightMultiSectionList }
function TSynHLightMultiSectionList.GetSection(Index: Integer): TSynHLightMultiVirtualSection;
begin
{$IFDEF AssertSynMemIndex}
if (Index < 0) or (Index >= Count) then
raise Exception.Create(Format('TSynHLightMultiSectionList.GetSection - Bad Index cnt= %d idx= %d',[Count, Index]));
{$ENDIF}
Result := PSynHLightMultiVirtualSection(ItemPointer[Index])^;
end;
function TSynHLightMultiSectionList.GetSectionPointer(Index: Integer): PSynHLightMultiVirtualSection;
begin
{$IFDEF AssertSynMemIndex}
if (Index < 0) or (Index >= Count) then
raise Exception.Create(Format('TSynHLightMultiSectionList.GetSectionPointer - Bad Index cnt= %d idx= %d',[Count, Index]));
{$ENDIF}
Result := PSynHLightMultiVirtualSection(ItemPointer[Index]);
end;
procedure TSynHLightMultiSectionList.SetSection(Index: Integer;
const AValue: TSynHLightMultiVirtualSection);
begin
{$IFDEF AssertSynMemIndex}
if (Index < 0) or (Index >= Count) then
raise Exception.Create(Format('TSynHLightMultiSectionList.SetSection - Bad Index cnt= %d idx= %d',[Count, Index]));
{$ENDIF}
PSynHLightMultiVirtualSection(ItemPointer[Index])^ := AValue;
end;
constructor TSynHLightMultiSectionList.Create;
begin
inherited;
ItemSize := SizeOf(TSynHLightMultiVirtualSection);
end;
procedure TSynHLightMultiSectionList.Debug;
var
i: Integer;
begin
debugln(SYNDEBUG_MULTIHL, ['SectionList ', dbgs(self), ' Count=', Count]);
for i := 0 to Count - 1 do
debugln(SYNDEBUG_MULTIHL, [' ', i, ': ', dbgs(PSections[i]^)]);
end;
procedure TSynHLightMultiSectionList.Insert(AnIndex: Integer;
AnSection: TSynHLightMultiVirtualSection);
begin
InsertRows(AnIndex, 1);
Sections[AnIndex] := AnSection;
end;
procedure TSynHLightMultiSectionList.Delete(AnIndex: Integer);
begin
DeleteRows(AnIndex, 1);
if (Capacity > 16) and (Capacity > (Count * 2)) then
Capacity := Capacity - (Count div 2);
end;
function TSynHLightMultiSectionList.IndexOfFirstSectionAtLineIdx(ALineIdx: Integer;
ACharPos: Integer = -1; UseNext: Boolean = True): Integer;
var
p, p1, p2: Integer;
s: PSynHLightMultiVirtualSection;
begin
Result := -1;
p2 := Count;
if p2 = 0 then begin
if UseNext then Result := 0;
exit;
end;
p1 := p2 div 2;
dec(p2);
s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
if (ALineIdx < s^.StartPos.y) or ( (ALineIdx = s^.StartPos.y) and (ACharPos < s^.StartPos.x) )
then begin // target is in 0 .. p1-1
p2 := p1 - 1;
p1 := 0;
end;
while (p1 < p2) do begin
p := (p1 + p2 + 1) div 2;
s := PSynHLightMultiVirtualSection(ItemPointer[p]);
if (ALineIdx < s^.StartPos.y) or
( (ALineIdx = s^.StartPos.y) and (ACharPos < s^.StartPos.x) )
then
p2 := p - 1 // target is in p1 .. p-1
else
p1 := p; // target is in p .. p2
end;
s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
if ( (s^.StartPos.y > ALineIdx) or ((s^.StartPos.y = ALineIdx) and (s^.StartPos.x > ACharPos)) )
then begin
dec(p1);
if p1 >= 0 then
s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
end;
if (p1 < 0) or (s^.EndPos.y < ALineIdx) or
( (s^.EndPos.y = ALineIdx) and (s^.EndPos.x < ACharPos) )
then begin
if UseNext then
Result := p1 + 1 // Could be p1 = Count // behind end
else
Result := -1;
end
else begin
Result := p1;
end;
end;
function TSynHLightMultiSectionList.IndexOfFirstSectionAtVirtualIdx(ALineIdx: Integer;
AGetLastSection: Boolean): Integer;
var
p, p1, p2: Integer;
s: PSynHLightMultiVirtualSection;
begin
Result := -1;
p2 := Count;
if p2 = 0 then
exit;
p1 := p2 div 2;
dec(p2);
s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
if (ALineIdx < s^.VirtualLine) then begin
p2 := p1 - 1; // target is in 0 .. p1-1
p1 := 0;
end;
while (p1 < p2) do begin
p := (p1 + p2 + 1) div 2;
s := PSynHLightMultiVirtualSection(ItemPointer[p]);
if (ALineIdx < s^.VirtualLine) then
p2 := p - 1 // target is in p1 .. p-1
else
p1 := p; // target is in p .. p2
end;
s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
if (ALineIdx = s^.VirtualLine) and (not AGetLastSection) then begin
while (p1 >= 0) and (s^.VirtualLine = ALineIdx) do begin
dec(p1);
if p1 >= 0 then
s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
end;
if (p1 < 0) or (s^.VirtualLine + s^.EndPos.y - s^.StartPos.y < ALineIdx) then
inc(p1);
end else begin
p2 := Count;
while (p1 < p2) and (s^.VirtualLine < ALineIdx) do begin
inc(p1);
if p1 < p2 then
s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
end;
if (p1 = p2) or (s^.VirtualLine > ALineIdx) then
dec(p1);
end;
Result := p1;
end;
function TSynHLightMultiSectionList.VirtualIdxToRealIdx(AVLineIdx: Integer): Integer;
var
i: Integer;
begin
if Count = 0 then exit(AVLineIdx);
i := IndexOfFirstSectionAtVirtualIdx(AVLineIdx, True);
if i < 0 then exit(AVLineIdx);
Result := PSections[i]^.StartPos.y + AVLineIdx;
end;
{ TSynHLightMultiVirtualLines }
function TSynHLightMultiVirtualLines.GetRange(Index: Pointer): TSynManagedStorageMem;
begin
Result := FRangeList[Index];
end;
procedure TSynHLightMultiVirtualLines.PutRange(Index: Pointer; const ARange: TSynManagedStorageMem);
begin
FRangeList[Index] := ARange;
if ARange <> nil then begin
ARange.Capacity := Count;
ARange.Count := Count;
end;
end;
function TSynHLightMultiVirtualLines.Get(Index: integer): string;
var
i, i2, c1, c2: Integer;
s: TSynHLightMultiVirtualSection;
t: String;
begin
i := FSectionList.IndexOfFirstSectionAtVirtualIdx(Index);
if (i < 0) or (i >= FSectionList.Count) then
exit('');
s := FSectionList[i];
i2 := s.StartPos.y + Index - s.VirtualLine;
t := FRealLines[i2];
c1 := 1;
if Index = s.VirtualLine then c1 := s.StartPos.x;
c2 := length(t);
if Index = s.VirtualLine + s.EndPos.y - s.StartPos.y then c2 := s.EndPos.x;
Result := copy(t, c1, c2 - c1 + 1);
inc(i);
while (i < FSectionList.Count) do begin
s := FSectionList[i];
if Index <> s.VirtualLine then break;
t := FRealLines[s.StartPos.y];
c1 := s.StartPos.x;
c2 := length(t);
if s.EndPos.y = s.StartPos.y then c2 := s.EndPos.x;
Result := Result + copy(t, c1, c2 - c1 + 1);
inc(i);
end;
end;
procedure TSynHLightMultiVirtualLines.Put(Index: integer; const S: string);
begin
raise Exception.Create('Not allowed');
end;
procedure TSynHLightMultiVirtualLines.Clear;
begin
raise Exception.Create('Not allowed');
end;
procedure TSynHLightMultiVirtualLines.Delete(Index: Integer);
begin
raise Exception.Create('Not allowed');
end;
procedure TSynHLightMultiVirtualLines.Insert(Index: Integer; const S: string);
begin
raise Exception.Create('Not allowed');
end;
function TSynHLightMultiVirtualLines.GetPChar(ALineIndex: Integer; out ALen: Integer): PChar;
begin
FLastPCharLine := Get(ALineIndex);
ALen := length(FLastPCharLine);
Result := PChar(FLastPCharLine);
end;
function TSynHLightMultiVirtualLines.GetCount: integer;
var
s: TSynHLightMultiVirtualSection;
begin
if FSectionList.Count = 0 then
exit(0);
s := FSectionList[FSectionList.Count - 1];
Result := s.VirtualLine + 1 + s.EndPos.y - s.StartPos.y;
end;
procedure TSynHLightMultiVirtualLines.SendHighlightChanged(aIndex, aCount: Integer);
begin
if (FFirstHLChangedLine < 0) or (FFirstHLChangedLine > aIndex) then
FFirstHLChangedLine := aIndex;
if (FLastHLChangedLine < aIndex + aCount - 1) then
FLastHLChangedLine := aIndex + aCount - 1;
end;
constructor TSynHLightMultiVirtualLines.Create(ALines: TSynEditStringsBase);
begin
FRangeList := TSynManagedStorageMemList.Create;
FSectionList := TSynHLightMultiSectionList.Create;
FRealLines := ALines;
end;
destructor TSynHLightMultiVirtualLines.Destroy;
begin
inherited Destroy;
FreeAndNil(FSectionList);
FreeAndNil(FRangeList);
end;
procedure TSynHLightMultiVirtualLines.Debug;
var
i: Integer;
begin
DebugLnEnter(SYNDEBUG_MULTIHL, ['>> Virtual-Lines: ', ptruint(self), ' LineCnt: ', Count, ' FirstChg: ',FFirstHLChangedLine, ' LastChg: ', FLastHLChangedLine ]);
for i := 0 to Count - 1 do
debugln(SYNDEBUG_MULTIHL, [i,' len=',length(self[i]),': ',self[i]]);
debugln(SYNDEBUG_MULTIHL, ['- RangeList: ChildCnt: ', FRangeList.ChildCounts]);
for i := 0 to FRangeList.ChildCounts - 1 do
debugln(SYNDEBUG_MULTIHL, [' ', PtrUInt(FRangeList.Children[i]), ' Cnt: ', FRangeList.Children[i].Count ]);
if FScheme <> nil then
if FScheme.Highlighter <> nil then
debugln(SYNDEBUG_MULTIHL, ['- Scheme: ', PtrUInt(FScheme), ' ', FScheme.SchemeName, ' HL: ', DbgSName(FScheme.Highlighter), ' ', PtrUInt(FScheme.Highlighter), ' HL.Range: ', PtrUInt(FScheme.Highlighter.CurrentLines.Ranges[FScheme.Highlighter]) ])
else
debugln(SYNDEBUG_MULTIHL, ['- Scheme: ', PtrUInt(FScheme), ' ', FScheme.SchemeName, ' HL: ', DbgSName(FScheme.Highlighter), ' ', PtrUInt(FScheme.Highlighter) ])
else
debugln(SYNDEBUG_MULTIHL, ['- Scheme: ', PtrUInt(FScheme)]);
DebugLnEnter(SYNDEBUG_MULTIHL, ['- Sections: ', PtrUInt(FSectionList)]);
FSectionList.Debug;
DebugLnExit(SYNDEBUG_MULTIHL);
DebugLnExit(SYNDEBUG_MULTIHL);
end;
procedure TSynHLightMultiVirtualLines.PrepareRegionScan(AStartLineIdx: Integer);
var
p: PSynHLightMultiVirtualSection;
begin
FRegionScanRangeIndex := FSectionList.IndexOfFirstSectionAtLineIdx(AStartLineIdx, -1 ,True);
FRegionScanStartRangeIndex := FRegionScanRangeIndex;
FRScanStartedWithLineCount := Count;
if FRegionScanRangeIndex < FSectionList.Count then
FRScanStartedAtVLine := FSectionList[FRegionScanRangeIndex].VirtualLine
else if FSectionList.Count = 0 then
FRScanStartedAtVLine := 0
else begin
p := FSectionList.PSections[FSectionList.Count - 1];
FRScanStartedAtVLine := p^.VirtualLine + p^.EndPos.y - p^.StartPos.y + 1;
end;
{$IFDEF SynDebugMultiHL}
debugln(SYNDEBUG_MULTIHL, ['TSynHLightMultiVirtualLines.PrepareRegionScan ', dbgs(self),
' FRegionScanRangeIndex=', FRegionScanRangeIndex, ' FRScanStartedWithLineCount=', FRScanStartedWithLineCount,
' FSectionList.Count=', FSectionList.Count, ' FRScanStartedAtVLine=', FRScanStartedAtVLine
]);
{$ENDIF}
end;
procedure TSynHLightMultiVirtualLines.FinishRegionScan(AEndLineIdx: Integer);
var
i, NewVLine, LastVline, LastEnd: Integer;
s: TSynHLightMultiVirtualSection;
VDiff: Integer;
begin
{$IFDEF SynDebugMultiHL}
debugln(SYNDEBUG_MULTIHL, ['TSynHLightMultiVirtualLines.FinishRegionScan AEndLineIdx=', AEndLineIdx]);
{$ENDIF}
while (FRegionScanRangeIndex < FSectionList.Count) and
(FSectionList.Sections[FRegionScanRangeIndex].StartPos.y <= AEndLineIdx)
do
FSectionList.Delete(FRegionScanRangeIndex);
VDiff := 0;
{$IFDEF SynDebugMultiHL}
DebugLn(SYNDEBUG_MULTIHL, ['***** ', FRegionScanStartRangeIndex, ' cnt ', FSectionList.Count]);
{$ENDIF}
if FRegionScanStartRangeIndex < FSectionList.Count then begin
// fix virtual lines on sections
if (FRegionScanStartRangeIndex > 0) then begin
s := FSectionList.Sections[FRegionScanStartRangeIndex-1];
NewVLine := s.VirtualLine + s.EndPos.y - s.StartPos.y;
{$IFDEF SynDebugMultiHL}
DebugLn(SYNDEBUG_MULTIHL, ['A ', NewVLine]);
{$ENDIF}
LastEnd := s.EndPos.y;
end
else begin
NewVLine := 0;
{$IFDEF SynDebugMultiHL}
DebugLn(SYNDEBUG_MULTIHL, ['B ', NewVLine]);
{$ENDIF}
LastEnd := FSectionList.Sections[FRegionScanStartRangeIndex].StartPos.y;
end;
LastVline := NewVLine;
for i := FRegionScanStartRangeIndex to FSectionList.Count - 1 do begin
s := FSectionList.Sections[i];
if s.StartPos.y > LastEnd then
inc(NewVLine);
if i = FRegionScanRangeIndex then
VDiff := NewVLine - s.VirtualLine; // adjust ranges
FSectionList.PSections[i]^.VirtualLine := NewVLine;
NewVLine := NewVLine + s.EndPos.y - s.StartPos.y;
LastEnd := s.EndPos.y;
end;
end
else
LastVline := 0; // ToDo: Initialize LastVline properly.
if VDiff = 0 then
VDiff := Count - FRScanStartedWithLineCount;
if VDiff < 0 then begin
FRangeList.ChildDeleteRows(FRScanStartedAtVLine, -VDiff);
FRangeList.CallDeletedLines(FRScanStartedAtVLine, -VDiff);
end
else if VDiff > 0 then begin
FRangeList.ChildInsertRows(FRScanStartedAtVLine, VDiff);
FRangeList.CallInsertedLines(FRScanStartedAtVLine, VDiff);
end;
FRangeList.CallLineTextChanged(FRScanStartedAtVLine, LastVline - FRScanStartedAtVLine + 1);
end;
procedure TSynHLightMultiVirtualLines.RegionScanUpdateFirstRegionEnd(AnEndPoint: TPoint;
ATokenEndPos: Integer);
var
p: PSynHLightMultiVirtualSection;
begin
p := FSectionList.PSections[FRegionScanRangeIndex];
{$IFDEF SynDebugMultiHL}
debugln(SYNDEBUG_MULTIHL, ['TSynHLightMultiVirtualLines.RegionScanUpdateFirstRegionEnd',
' AnEndPoint', dbgs(AnEndPoint), ' ATokenEndPos=', ATokenEndPos, ' FRegionScanRangeIndex=', FRegionScanRangeIndex,
' p^.StartPos=', dbgs(p^.StartPos), ' p^.EndPos=', dbgs(p^.EndPos)
]);
{$ENDIF}
p^.EndPos := AnEndPoint;
p^.TokenEndPos := ATokenEndPos;
inc(FRegionScanRangeIndex);
end;
procedure TSynHLightMultiVirtualLines.RegionScanUpdateOrInsertRegion(AStartPoint,
AnEndPoint: TPoint; ATokenStartPos, ATokenEndPos: Integer);
var
Sect: TSynHLightMultiVirtualSection;
p: PSynHLightMultiVirtualSection;
begin
{$IFDEF SynDebugMultiHL}
debugln(SYNDEBUG_MULTIHL, ['TSynHLightMultiVirtualLines.RegionScanUpdateOrInsertRegion',
' AStartPoint=', dbgs(AStartPoint), ' AnEndPoint=', dbgs(AnEndPoint),
' ATokenStartPos=', ATokenStartPos, ' ATokenEndPos=', ATokenEndPos,
' FRegionScanRangeIndex=', FRegionScanRangeIndex
]);
{$ENDIF}
if (FRegionScanRangeIndex = FSectionList.Count)
or (FSectionList.Sections[FRegionScanRangeIndex].StartPos > AnEndPoint)
then begin
Sect.StartPos := AStartPoint;
Sect.EndPos := AnEndPoint;
Sect.TokenStartPos := ATokenStartPos;
Sect.TokenEndPos := ATokenEndPos;
Sect.VirtualLine := 0;
FSectionList.Insert(FRegionScanRangeIndex, Sect);
end else begin
p := FSectionList.PSections[FRegionScanRangeIndex];
p^.StartPos := AStartPoint;
p^.EndPos := AnEndPoint;
p^.TokenStartPos := ATokenStartPos;
p^.TokenEndPos := ATokenEndPos;
end;
inc(FRegionScanRangeIndex);
end;
procedure TSynHLightMultiVirtualLines.RegionScanUpdateLastRegionStart(AStartPoint: TPoint;
ATokenStartPos: Integer; ALineIndex: Integer);
var
p: PSynHLightMultiVirtualSection;
begin
while (FRegionScanRangeIndex < FSectionList.Count) and
(FSectionList.Sections[FRegionScanRangeIndex].EndPos.y <= ALineIndex)
do
FSectionList.Delete(FRegionScanRangeIndex);
p := FSectionList.PSections[FRegionScanRangeIndex];
p^.StartPos := AStartPoint;
p^.TokenStartPos := ATokenStartPos;
inc(FRegionScanRangeIndex);
end;
procedure TSynHLightMultiVirtualLines.RealLinesInserted(AIndex, ACount: Integer);
var
i, VLineDiff: Integer;
s: TSynHLightMultiVirtualSection;
p: PSynHLightMultiVirtualSection;
begin
i := FSectionList.IndexOfFirstSectionAtLineIdx(AIndex, -1, True);
if i = FSectionList.Count then exit;
VLineDiff := 0;
s := FSectionList[i];
if AIndex > s.StartPos.y then begin
p := FSectionList.PSections[i];
FRangeList.ChildInsertRows(p^.VirtualLine + AIndex - p^.StartPos.y, ACount);
FRangeList.CallInsertedLines(p^.VirtualLine + AIndex - p^.StartPos.y, ACount);
p^.EndPos.y := p^.EndPos.y + ACount;
inc(i);
VLineDiff := ACount;
end;
while i < FSectionList.Count do begin
p := FSectionList.PSections[i];
p^.StartPos.y := p^.StartPos.y + ACount;
p^.EndPos.y := p^.EndPos.y + ACount;
p^.VirtualLine := p^.VirtualLine + VLineDiff;
inc(i);
end;
end;
procedure TSynHLightMultiVirtualLines.RealLinesDeleted(AIndex, ACount: Integer);
var
i: Integer;
CountInSection, PrevEndVLine, FirstVLine, VLineCount: Integer;
p: PSynHLightMultiVirtualSection;
procedure DelVLines;
begin
if VLineCount > 0 then begin
FRangeList.ChildDeleteRows(FirstVLine, VLineCount);
FRangeList.CallDeletedLines(FirstVLine, VLineCount);
end;
end;
begin
i := FSectionList.IndexOfFirstSectionAtLineIdx(AIndex, -1, True);
if i = FSectionList.Count then exit;
p := FSectionList.PSections[i];
VLineCount := 0; // Count of deleted virtual lines
FirstVLine := p^.VirtualLine; // First deleted virtual line
PrevEndVLine := -1; // Keep track of overlap, when next section starts on the same V-line as previous sectian ends
if AIndex > p^.StartPos.y then begin
// Real-lines starting in the middle of the Section
VLineCount := Min(AIndex + ACount, p^.EndPos.y + 1) - AIndex;
FirstVLine := p^.VirtualLine + AIndex - p^.StartPos.y;
PrevEndVLine := p^.VirtualLine + p^.EndPos.y - p^.StartPos.y;
p^.EndPos.y := p^.EndPos.y - VLineCount;
inc(i);
if i = FSectionList.Count then begin
DelVLines;
exit;
end;
p := FSectionList.PSections[i];
end;
while p^.EndPos.y < AIndex + ACount do begin
// Completly delete node (All Real lines deleted)
VLineCount := VLineCount + p^.EndPos.y - p^.StartPos.y + 1;
if PrevEndVLine = p^.VirtualLine then
dec(VLineCount);
PrevEndVLine := p^.VirtualLine + p^.EndPos.y - p^.StartPos.y;
FSectionList.Delete(i);
if i = FSectionList.Count then begin
DelVLines;
exit;
end;
p := FSectionList.PSections[i];
end;
if AIndex + ACount > p^.StartPos.y then begin
// Some real-lines at the start of section are deleted
if PrevEndVLine = p^.VirtualLine then
dec(VLineCount);
p^.VirtualLine := p^.VirtualLine - VLineCount;
CountInSection := ACount - (p^.StartPos.y - AIndex);
VLineCount := VLineCount + CountInSection;
p^.StartPos.y := p^.StartPos.y - (ACount - CountInSection);
p^.EndPos.y := p^.EndPos.y - ACount;
assert(p^.EndPos.y >= p^.StartPos.y, 'TSynHLightMultiVirtualLines.RealLinesDeleted: p^.EndPos.y >= p^.StartPos.y');
inc(i);
end;
// Adjust StartPos for all sections, after the deleted.
while i < FSectionList.Count do begin
p := FSectionList.PSections[i];
p^.StartPos.y := p^.StartPos.y - ACount;
p^.EndPos.y := p^.EndPos.y - ACount;
p^.VirtualLine := p^.VirtualLine - VLineCount;
inc(i);
end;
DelVLines;
end;
procedure TSynHLightMultiVirtualLines.RealLinesChanged(AIndex, ACount: Integer);
var
i, VLine1, VLine2: Integer;
s: TSynHLightMultiVirtualSection;
begin
i := FSectionList.IndexOfFirstSectionAtLineIdx(AIndex, -1, True);
if i = FSectionList.Count then exit;
s := FSectionList[i];
VLine1 := s.VirtualLine + AIndex - s.StartPos.y;
i := FSectionList.IndexOfFirstSectionAtLineIdx(AIndex + ACount - 1, -1, True);
if i = FSectionList.Count then
VLine2 := Count-1
else begin
s := FSectionList[i];
VLine2 := s.VirtualLine + AIndex + ACount - 1 - s.StartPos.y;
end;
FRangeList.CallLineTextChanged(VLine1, VLine2 - VLine1 + 1);
end;
procedure TSynHLightMultiVirtualLines.ResetHLChangedLines;
begin
FFirstHLChangedLine := -1;
FLastHLChangedLine := -1;
end;
{ TSynHLightMultiVirtualLinesList }
function TSynHLightMultiVirtualLinesList.GetVLines(Index: Integer): TSynHLightMultiVirtualLines;
begin
Result := TSynHLightMultiVirtualLines(inherited Items[Index]);
end;
procedure TSynHLightMultiVirtualLinesList.PutVLines(Index: Integer;
const AValue: TSynHLightMultiVirtualLines);
begin
inherited Items[Index] := AValue;
end;
{ TSynHighlighterMultiRangeList }
function TSynHighlighterMultiRangeList.GetVirtualLines(Index: TSynHighlighterMultiScheme): TSynHLightMultiVirtualLines;
var
i: Integer;
begin
Result := nil;
for i := 0 to FVirtualLines.Count - 1 do
if FVirtualLines[i].Scheme = Index then
exit(FVirtualLines[i]);
end;
procedure TSynHighlighterMultiRangeList.LineTextChanged(AIndex: Integer; ACount: Integer);
var
i: Integer;
begin
inherited LineTextChanged(AIndex, ACount);
for i := 0 to FVirtualLines.Count - 1 do
FVirtualLines[i].RealLinesChanged(AIndex, ACount);
FDefaultVirtualLines.RealLinesChanged(AIndex, ACount);
end;
procedure TSynHighlighterMultiRangeList.InsertedLines(AIndex, ACount: Integer);
var
i: Integer;
begin
inherited InsertedLines(AIndex, ACount);
for i := 0 to FVirtualLines.Count - 1 do
FVirtualLines[i].RealLinesInserted(AIndex, ACount);
FDefaultVirtualLines.RealLinesInserted(AIndex, ACount);
end;
procedure TSynHighlighterMultiRangeList.DeletedLines(AIndex, ACount: Integer);
var
i: Integer;
begin
inherited DeletedLines(AIndex, ACount);
for i := 0 to FVirtualLines.Count - 1 do
FVirtualLines[i].RealLinesDeleted(AIndex, ACount);
FDefaultVirtualLines.RealLinesDeleted(AIndex, ACount);
end;
constructor TSynHighlighterMultiRangeList.Create(ALines: TSynEditStringsBase);
begin
inherited Create;
FLines := ALines;
FVirtualLines := TSynHLightMultiVirtualLinesList.Create;
end;
destructor TSynHighlighterMultiRangeList.Destroy;
begin
inherited Destroy;
ClearVLines;
FreeAndNil(FVirtualLines);
end;
procedure TSynHighlighterMultiRangeList.ClearVLines;
begin
FreeAndNil(FDefaultVirtualLines);
while FVirtualLines.Count > 0 do begin
FVirtualLines[0].Destroy;
FVirtualLines.Delete(0);
end;
FVirtualLines.Clear;
end;
procedure TSynHighlighterMultiRangeList.UpdateForScheme(AScheme: TSynHighlighterMultiSchemeList);
var
i: Integer;
NewVline: TSynHLightMultiVirtualLines;
begin
for i := FVirtualLines.Count - 1 downto 0 do
if AScheme.IndexOf(FVirtualLines[i].Scheme) < 0 then begin
FVirtualLines[i].Destroy;
FVirtualLines.Delete(i);
end;
if FDefaultVirtualLines = nil then
FDefaultVirtualLines := TSynHLightMultiVirtualLines.Create(FLines);
for i := 0 to AScheme.Count - 1 do
if VirtualLines[AScheme[i]] = nil then begin
NewVline := TSynHLightMultiVirtualLines.Create(FLines);
NewVline.Scheme := AScheme[i];
FVirtualLines.Add(NewVline);
if AScheme[i].Highlighter <> nil then
AScheme[i].Highlighter.AttachToLines(NewVline);
end;
end;
procedure TSynHighlighterMultiRangeList.CleanUpForScheme(AScheme: TSynHighlighterMultiSchemeList);
// Called before destruction / in detach
var
i: Integer;
begin
for i := 0 to AScheme.Count - 1 do
if (VirtualLines[AScheme[i]] <> nil) and (AScheme[i].Highlighter <> nil) then
AScheme[i].Highlighter.DetachFromLines(VirtualLines[AScheme[i]]);
end;
procedure TSynHighlighterMultiRangeList.CopyToScheme(AScheme: TSynHighlighterMultiSchemeList);
var
i: Integer;
begin
for i := 0 to AScheme.Count - 1 do
AScheme[i].VirtualLines := FVirtualLines[i];
end;
{ TSynMultiSyn }
function TSynMultiSyn.CurrentVirtualLines: TSynHLightMultiVirtualLines;
begin
if FCurScheme <> nil then
Result := FCurScheme.VirtualLines
else
Result := DefaultVirtualLines;
end;
constructor TSynMultiSyn.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fSchemes := TSynHighlighterMultiSchemeList.Create(Self);
FCurScheme := nil;
end;
destructor TSynMultiSyn.Destroy;
var
s: TSynHighlighterMultiSchemeList;
begin
s := FSchemes;
FSchemes := nil;
s.Free;
{ unhook notification handlers }
DefaultHighlighter := nil;
inherited Destroy;
end;
function TSynMultiSyn.PerformScan(StartIndex, EndIndex: Integer; ForceEndIndex: Boolean = False): Integer;
var
i, j, c: Integer;
SearchPos, NewSearchPos, TmpSearchPos: Integer;
CurRegStart: TPoint;
CurRegTokenPos: Integer;
LineText: string;
procedure StartScheme(NewScheme: TSynHighlighterMultiScheme;
StartAtLine, StartAtChar, TokenAtChar: Integer);
var
pt: TPoint;
begin
//debugln(['StartScheme NewScheme=',dbgs(NewScheme),' StartAtLine=',StartAtLine,' StartAtChar=',StartAtChar,' TokenAtChar=',TokenAtChar]);
pt := Point(TokenAtChar-1, StartAtLine);
if CurRegStart.y < 0 then
DefaultVirtualLines.RegionScanUpdateFirstRegionEnd(pt, 0)
else
if pt >= CurRegStart then
DefaultVirtualLines.RegionScanUpdateOrInsertRegion(CurRegStart, pt, 0, 0);
FCurScheme := NewScheme;
CurRegStart.y := StartAtLine;
CurRegStart.x := StartAtChar;
CurRegTokenPos := TokenAtChar;
end;
procedure EndScheme(EndAtLine, EndAtChar, TokenEndChar: Integer);
var
pt: TPoint;
begin
//debugln(['EndScheme EndAtLine=',EndAtLine,' EndAtChar=',EndAtChar,' TokenAtChar=',TokenEndChar]);
pt := Point(EndAtChar, EndAtLine);
if CurRegStart.y < 0 then
FCurScheme.VirtualLines.RegionScanUpdateFirstRegionEnd(pt, TokenEndChar)
else
if pt >= CurRegStart then
FCurScheme.VirtualLines.RegionScanUpdateOrInsertRegion
(CurRegStart, pt, CurRegTokenPos, TokenEndChar);
FCurScheme := nil;
CurRegStart.y := EndAtLine;
CurRegStart.x := TokenEndChar + 1;
CurRegTokenPos := 0;
end;
begin
(* Scan regions *)
Result := StartIndex;
{$IFDEF SynDebugMultiHL}
debugln(SYNDEBUG_MULTIHL, ['TSynMultiSyn.PerformScan StartIndex=', Result]);
{$ENDIF}
// last node may need to extend to next line
// TODO: instead check, that FCurScheme is cvered by region
// p := DefaultVirtualLines.SectionList.PSections[DefaultVirtualLines.FRegionScanRangeIndex]
// p := FCurScheme.VirtualLines .SectionList.PSections[FCurScheme.VirtualLines.FRegionScanRangeIndex];
if Result > 0 then dec(Result);
c := CurrentLines.Count - 1;
if c < 0 then begin
// Clear ?
exit;
end;
DefaultVirtualLines.PrepareRegionScan(Result);
for i := 0 to Schemes.Count - 1 do begin
Schemes[i].VirtualLines.ResetHLChangedLines;
Schemes[i].VirtualLines.PrepareRegionScan(Result);
end;
CurRegStart.y := -1;
if Result = 0 then begin
CurRegStart.y := 0;
CurRegStart.x := 1;
CurRegTokenPos := 1;
end
else
CurRegTokenPos := 0;
StartAtLineIndex(Result); // Set FCurScheme
dec(Result);
repeat
inc(Result);
if Result <> StartIndex then
ContinueNextLine;
LineText := CurrentLines[Result];
FSchemes.CurrentLine := LineText;
SearchPos := 1;
while SearchPos <= length(LineText) do begin
if FCurScheme <> nil then begin
// Find Endpoint for CurScheme
NewSearchPos := FCurScheme.FindEndPosInLine(SearchPos);
if NewSearchPos <= 0 then
break; // Ends in next line
SearchPos := NewSearchPos + FCurScheme.LastMatchLen;
EndScheme(Result, NewSearchPos - 1, SearchPos - 1);
end
else begin
// Find new start of a Scheme
NewSearchPos := -1;
for i := 0 to Schemes.Count - 1 do begin
TmpSearchPos := Schemes.Items[i].FindStartPosInLine(SearchPos);
if (NewSearchPos < 0) or ((TmpSearchPos > 0) and (TmpSearchPos < NewSearchPos)) then begin
j := i;
NewSearchPos := TmpSearchPos;
end;
end;
if NewSearchPos <= 0 then
break; // Not in this line
SearchPos := NewSearchPos + Schemes[j{%H-}].LastMatchLen;
StartScheme(Schemes[j], Result, SearchPos, NewSearchPos);
end;
end;
until ((not UpdateRangeInfoAtLine(Result)) and (Result > EndIndex))
or (Result = c);
if Result = c then begin
i := length(CurrentLines[c]) + 1;
if FCurScheme = nil then
StartScheme(nil, c, i, i) // DefaultVirtualLines.RegionScanUpdateFirstRegionEnd(pt, 0)
else
EndScheme(c, i, i);
end
else if CurRegStart.y > 0 then begin
if FCurScheme = nil
then DefaultVirtualLines.RegionScanUpdateLastRegionStart(CurRegStart, 0, Result)
else FCurScheme.VirtualLines.RegionScanUpdateLastRegionStart(CurRegStart, CurRegTokenPos, Result);
end
else begin
// nothing changed, keep current
if FCurScheme = nil
then inc(DefaultVirtualLines.FRegionScanRangeIndex)
else inc(FCurScheme.VirtualLines.FRegionScanRangeIndex);
end;
DefaultVirtualLines.FinishRegionScan(Result);
for i := 0 to Schemes.Count - 1 do
Schemes[i].VirtualLines.FinishRegionScan(Result);
(* Scan nested Highlighters *)
for i := 0 to Schemes.Count - 1 do
if Schemes[i].Highlighter <> nil then begin
Schemes[i].Highlighter.ScanRanges;
j := Schemes[i].VirtualLines.SectionList.VirtualIdxToRealIdx(Schemes[i].VirtualLines.LastHLChangedLine);
if Result < j then
Result := j;
end;
if FDefaultHighlighter <> nil then begin
FDefaultHighlighter.ScanRanges;
j := DefaultVirtualLines.SectionList.VirtualIdxToRealIdx(DefaultVirtualLines.LastHLChangedLine);
if Result < j then
Result := j;
end;
end;
function TSynMultiSyn.GetAttribCount: integer;
var
i: Integer;
begin
Result := Schemes.Count;
for i := 0 to Schemes.Count - 1 do
if Schemes[i].Highlighter <> nil then
inc(Result, Schemes[i].Highlighter.AttrCount);
if DefaultHighlighter <> nil then
Inc(Result, DefaultHighlighter.AttrCount);
end;
function TSynMultiSyn.GetAttribute(
idx: integer): TSynHighlighterAttributes;
var
i, j: Integer;
begin
if DefaultHighlighter <> nil then begin
j := DefaultHighlighter.AttrCount;
if idx < j then
exit(DefaultHighlighter.Attribute[idx]);
dec(idx, j);
end;
for i := 0 to Schemes.Count - 1 do begin
if idx = 0 then
exit(Schemes[i].MarkerAttri);
dec(idx);
if Schemes[i].Highlighter <> nil then begin
j := Schemes[i].Highlighter.AttrCount;
if idx < j then
exit(Schemes[i].Highlighter.Attribute[idx]);
dec(idx, j);
end;
end;
Result := nil;
raise Exception.Create('bad attr idx');
end;
function TSynMultiSyn.GetDefaultAttribute(Index: integer): TSynHighlighterAttributes;
var
iHL: TSynCustomHighlighter;
begin
if (FCurScheme <> nil) and (FCurScheme.Highlighter <> nil) then
iHL := FCurScheme.Highlighter
else
iHL := DefaultHighlighter;
{ the typecast to TSynMultiSyn is only necessary because the
GetDefaultAttribute method is protected.
And don't worry: this really works }
if iHL <> nil then begin
Result := TSynMultiSyn(iHL).GetDefaultAttribute(Index)
end else
Result := nil;
end;
function TSynMultiSyn.GetEol: Boolean;
begin
Result := FTokenPos > FLineLen;
end;
function TSynMultiSyn.GetIdentChars: TSynIdentChars;
begin
if FCurScheme <> nil then
Result := FCurScheme.Highlighter.IdentChars
else if DefaultHighlighter <> nil then
Result := DefaultHighlighter.IdentChars
else
Result := inherited GetIdentChars;
end;
class function TSynMultiSyn.GetLanguageName: string;
begin
Result := SYNS_LangGeneralMulti;
end;
function TSynMultiSyn.GetRange: Pointer;
begin
Result := FCurScheme;
end;
function TSynMultiSyn.GetToken: string;
begin
SetString(Result, (PChar(FLine) + FTokenPos - 1), FRun - FTokenPos);
end;
procedure TSynMultiSyn.GetTokenEx(out TokenStart: PChar;
out TokenLength: integer);
begin
TokenLength := FRun-FTokenPos;
if TokenLength > 0 then begin
TokenStart := @fLine[FTokenPos];
end else begin
TokenStart := nil;
end;
end;
function TSynMultiSyn.GetTokenAttribute: TSynHighlighterAttributes;
begin
Result := FTokenAttr;
end;
function TSynMultiSyn.GetTokenKind: integer;
begin
Result := FTokenKind;
end;
function TSynMultiSyn.GetTokenPos: Integer;
begin
Result := fTokenPos - 1;
end;
procedure TSynMultiSyn.HookHighlighter(aHL: TSynCustomHighlighter);
begin
aHL.HookAttrChangeEvent( @DefHighlightChange );
end;
procedure TSynMultiSyn.Next;
procedure NextRunSection(ASchemeIdx: Integer);
var
VLines: TSynHLightMultiVirtualLines;
idx: Integer;
s: TSynHLightMultiVirtualSection;
x1, x2, tx1, tx2: Integer;
begin
if ASchemeIdx > 0 then
VLines := Schemes[ASchemeIdx-1].VirtualLines
else
VLines := DefaultVirtualLines;
idx := FRunSectionInfo[ASchemeIdx].SectionIdx + 1;
FRunSectionInfo[ASchemeIdx].SectionIdx := -1;
if (idx < 0) or (idx >= VLines.SectionList.Count) then
exit;
s := VLines.SectionList[idx];
if s.StartPos.y > FCurLineIndex then
exit;
FRunSectionInfo[ASchemeIdx].SectionIdx := idx;
FRunSectionInfo[ASchemeIdx].VirtualStartPos :=
FRunSectionInfo[ASchemeIdx].VirtualStartPos +
FRunSectionInfo[ASchemeIdx].LastChar - FRunSectionInfo[ASchemeIdx].FirstChar + 1;
if s.StartPos.y = FCurLineIndex then begin
x1 := s.StartPos.x;
tx1 := s.TokenStartPos;
if tx1 = 0 then
tx1 := x1;
end else begin
x1 := 1;
tx1 := 1;
end;
if s.EndPos.y = FCurLineIndex then begin
x2 := s.EndPos.x;
tx2 := s.TokenEndPos;
if tx2 = 0 then
tx2 := x2;
end else begin
x2 := length(CurrentLines[FCurLineIndex]);
tx2 := x2;
end;
FRunSectionInfo[ASchemeIdx].FirstChar := x1;
FRunSectionInfo[ASchemeIdx].LastChar := x2;
FRunSectionInfo[ASchemeIdx].TokenFirstChar := tx1;
FRunSectionInfo[ASchemeIdx].TokenLastChar := tx2;
end;
var
idx: Integer;
RSect: TRunSectionInfo;
HL: TSynCustomHighlighter;
dummy: PChar;
tkpos, tklen: Integer;
begin
//debugln(['--- Next at ',FRun]);
FTokenPos := FRun;
FTokenAttr := nil;
FTokenKind := 0;
if FRun > FLineLen then
exit;
idx := high(FRunSectionInfo);
while (idx >= 0) and
( (FRunSectionInfo[idx].SectionIdx < 0) or
not ( (FRun >= FRunSectionInfo[idx].TokenFirstChar) and
(FRun <= FRunSectionInfo[idx].TokenLastChar) ) )
do
dec(idx);
if idx < 0 then begin
//debugln(['*** XXXXX No section found XXXXX ***']);
FRun := FLineLen + 1;
FTokenAttr := nil;
FTokenKind := 0;
exit;
end;
RSect := FRunSectionInfo[idx];
//with RSect do debugln([' RSect ',idx,': SectIdx=', SectionIdx, ' Fc=',FirstChar,' LC=',LastChar,' TkFC=',TokenFirstChar, ' TkLC=',TokenLastChar]);
if RSect.SectionIdx < 0 then begin
//debugln(['*** XXXXX section missing XXXXX ***']);
FRun := FLineLen + 1;
FTokenAttr := nil;
FTokenKind := 0;
exit;
end;
if (idx > 0) and (FRun < RSect.FirstChar) then begin
FTokenAttr := Schemes[idx-1].FMarkerAttri;
FTokenKind := 1;
FRun := RSect.FirstChar;
//debugln([' start-token ', FRun]);
end
else if (idx > 0) and (FRun > RSect.LastChar) then begin
FTokenAttr := Schemes[idx-1].FMarkerAttri;
FTokenKind := 1;
FRun := RSect.TokenLastChar + 1;
//debugln([' end-token ', FRun]);
end
else begin
if idx = 0 then
HL := DefaultHighlighter
else
HL := Schemes[idx-1].Highlighter;
if HL <> nil then begin
repeat
HL.GetTokenEx(dummy, tklen);
tkpos := HL.GetTokenPos + 1;
if tkpos - RSect.VirtualStartPos + RSect.FirstChar + tklen - 1 < FRun then begin
//debugln('>');
HL.Next
end else
break;
until HL.GetEol;
if not HL.GetEol then begin
FTokenAttr := HL.GetTokenAttribute;
FTokenKind := idx * TokenKindPerHighlighter + HL.GetTokenKind;
FRun := Min(tkpos - RSect.VirtualStartPos + RSect.FirstChar + tklen,
RSect.LastChar + 1);
//debugln([' FOUND-token ', FRun, ' t=',copy(FLine, FTokenPos, 2),'... kind=',FTokenKind, ' subhl: tkpos=',tkpos,' tklen=',tklen, ' t=', copy(dummy,1,tklen) ]);
end
else
HL := nil;
end;
if (HL = nil) then begin
FTokenAttr := nil;
FTokenKind := 0;
FRun := RSect.LastChar + 1;
//debugln([' no HL ', FRun]);
end;
end;
if (FRun > RSect.TokenLastChar) then
NextRunSection(idx);
end;
procedure TSynMultiSyn.Notification(aComp: TComponent; aOp: TOperation);
var
i: Integer;
begin
inherited;
if (aOp = opRemove) and (Schemes <> nil) then begin
if (aComp = DefaultHighlighter) then
DefaultHighlighter := nil;
for i := 0 to Schemes.Count - 1 do
if aComp = Schemes[i].Highlighter then
Schemes[i].Highlighter := nil;
end;
end;
function TSynMultiSyn.CreateRangeList(ALines: TSynEditStringsBase): TSynHighlighterRangeList;
var
NewRangeList: TSynHighlighterMultiRangeList;
begin
NewRangeList := TSynHighlighterMultiRangeList.Create(ALines);
NewRangeList.UpdateForScheme(Schemes);
NewRangeList.CopyToScheme(Schemes);
if FDefaultHighlighter <> nil then
FDefaultHighlighter.AttachToLines(NewRangeList.DefaultVirtualLines);
Result := NewRangeList;
end;
procedure TSynMultiSyn.BeforeDetachedFromRangeList(ARangeList: TSynHighlighterRangeList);
begin
inherited BeforeDetachedFromRangeList(ARangeList);
if (Schemes <> nil) and (ARangeList.RefCount = 0) then begin
TSynHighlighterMultiRangeList(ARangeList).CleanUpForScheme(Schemes);
if (TSynHighlighterMultiRangeList(ARangeList).DefaultVirtualLines <> nil) and
(DefaultHighlighter <> nil)
then
DefaultHighlighter.DetachFromLines(TSynHighlighterMultiRangeList(ARangeList).DefaultVirtualLines);
end;
end;
procedure TSynMultiSyn.SetCurrentLines(const AValue: TSynEditStringsBase);
begin
inherited SetCurrentLines(AValue);
CurrentRanges.CopyToScheme(Schemes);
if FDefaultHighlighter <> nil then
FDefaultHighlighter.CurrentLines := CurrentRanges.DefaultVirtualLines;
end;
procedure TSynMultiSyn.ResetRange;
begin
FCurScheme := nil;
if DefaultHighlighter <> nil then begin
DefaultHighlighter.ResetRange;
end;
end;
procedure TSynMultiSyn.SetDefaultHighlighter(
const Value: TSynCustomHighLighter);
const
sDefaultHlSetToSelf = 'Not allowed';
var
i: Integer;
begin
if DefaultHighlighter = Value then exit;
if Value = Self then
raise Exception.Create( sDefaultHlSetToSelf );
if DefaultHighlighter <> nil then begin
DefaultHighlighter.RemoveFreeNotification(Self);
UnhookHighlighter( DefaultHighlighter );
for i := 0 to KnownLines.Count - 1 do
DefaultHighlighter.DetachFromLines(KnownRanges[i].DefaultVirtualLines);
end;
fDefaultHighlighter := Value;
if DefaultHighlighter <> nil then begin
HookHighlighter( DefaultHighlighter );
DefaultHighlighter.FreeNotification(Self);
for i := 0 to KnownLines.Count - 1 do
DefaultHighlighter.AttachToLines(KnownRanges[i].DefaultVirtualLines);
end;
{ yes, it's necessary }
if not( csDestroying in ComponentState ) then
DefHighlightChange( Self );
end;
function TSynMultiSyn.GetDefaultVirtualLines: TSynHLightMultiVirtualLines;
begin
Result := CurrentRanges.DefaultVirtualLines;
end;
function TSynMultiSyn.GetKnownMultiRanges(Index: Integer): TSynHighlighterMultiRangeList;
begin
Result := TSynHighlighterMultiRangeList(inherited KnownRanges[Index])
end;
function TSynMultiSyn.GetCurrentRanges: TSynHighlighterMultiRangeList;
begin
Result := TSynHighlighterMultiRangeList(inherited CurrentRanges)
end;
procedure TSynMultiSyn.SetLine(const NewValue: string;
LineNumber: Integer);
procedure InitRunSection(ASchemeIdx: Integer);
var
VLines: TSynHLightMultiVirtualLines;
HL: TSynCustomHighlighter;
s: TSynHLightMultiVirtualSection;
idx, x1, x2, tx1, tx2: Integer;
begin
FRunSectionInfo[ASchemeIdx].SectionIdx := -1;
if ASchemeIdx > 0 then begin
VLines := Schemes[ASchemeIdx-1].VirtualLines;
HL := Schemes[ASchemeIdx-1].Highlighter;
end else begin
VLines := DefaultVirtualLines;
HL := DefaultHighlighter;
end;
idx := VLines.SectionList.IndexOfFirstSectionAtLineIdx(FCurLineIndex);
if (idx < 0) or (idx >= VLines.SectionList.Count) then
exit;
s := VLines.SectionList[idx];
if s.StartPos.y > FCurLineIndex then
exit;
FRunSectionInfo[ASchemeIdx].SectionIdx := idx;
FRunSectionInfo[ASchemeIdx].VirtualStartPos := 1;
if s.StartPos.y = FCurLineIndex then begin
x1 := s.StartPos.x;
tx1 := s.TokenStartPos;
if tx1 = 0 then
tx1 := x1;
end else begin
x1 := 1;
tx1 := 1;
end;
if s.EndPos.y = FCurLineIndex then begin
x2 := s.EndPos.x;
tx2 := s.TokenEndPos;
if tx2 = 0 then
tx2 := x2;
end else begin
x2 := length(CurrentLines[FCurLineIndex]);
tx2 := x2;
end;
FRunSectionInfo[ASchemeIdx].FirstChar := x1;
FRunSectionInfo[ASchemeIdx].LastChar := x2;
FRunSectionInfo[ASchemeIdx].TokenFirstChar := tx1;
FRunSectionInfo[ASchemeIdx].TokenLastChar := tx2;
if HL <> nil then
HL.StartAtLineIndex(s.VirtualLine + FCurLineIndex - s.StartPos.y);
//with FRunSectionInfo[ASchemeIdx] do debugln([' RunSection ',ASchemeIdx,': SectIdx=', SectionIdx, ' Fc=',FirstChar,' LC=',LastChar,' TkFC=',TokenFirstChar, ' TkLC=',TokenLastChar, ' VLine=',s.VirtualLine + FCurLineIndex - s.StartPos.y]);
end;
var
i: Integer;
begin
if IsScanning then exit;
inherited;
FCurLineIndex := LineNumber;
FLine := NewValue;
FLineLen := length(FLine);
fRun := 1;
FTokenPos := 1;
FTokenAttr := nil;
FTokenKind := 0;
//debugln(['>>>>> Setting Line ',FCurLineIndex,' = ',FLine]);
for i := 0 to high(FRunSectionInfo) do
InitRunSection(i);
Next;
end;
function TSynMultiSyn.UpdateRangeInfoAtLine(Index: Integer): Boolean;
begin
Result := inherited UpdateRangeInfoAtLine(Index);
if not Result then begin
if FCurScheme <> nil then
Result := (FCurScheme.VirtualLines <> nil) and
(FCurScheme.VirtualLines.SectionList <> nil) and
(FCurScheme.VirtualLines.SectionList.IndexOfFirstSectionAtLineIdx(Index, -1, False) < 0)
else
Result := (DefaultVirtualLines <> nil) and
(DefaultVirtualLines.SectionList <> nil) and
(DefaultVirtualLines.SectionList.IndexOfFirstSectionAtLineIdx(Index, -1, False) < 0);
end;
end;
procedure TSynMultiSyn.SetRange(Value: Pointer);
begin
inherited;
FCurScheme := TSynHighlighterMultiScheme(Value);
end;
procedure TSynMultiSyn.SetSchemes(const Value: TSynHighlighterMultiSchemeList);
begin
fSchemes.Assign(Value);
end;
procedure TSynMultiSyn.UnhookHighlighter(aHL: TSynCustomHighlighter);
begin
if csDestroying in aHL.ComponentState then
Exit;
aHL.UnhookAttrChangeEvent( @DefHighlightChange );
end;
function TSynMultiSyn.GetSampleSource: string;
begin
Result := fSampleSource;
end;
procedure TSynMultiSyn.SetSampleSource(Value: string);
begin
fSampleSource := Value;
end;
procedure TSynMultiSyn.SchemeItemChanged(Item: TObject);
var
i: Integer;
begin
if Schemes = nil then exit;
FAttributeChangeNeedScan := (Item <> nil) and (TSynHighlighterMultiScheme(Item).NeedHLScan);
DefHighlightChange( Item );
for i := 0 to KnownLines.Count - 1 do
KnownRanges[i].InvalidateAll;
end;
procedure TSynMultiSyn.SchemeChanged;
var
i: Integer;
begin
if Schemes = nil then exit;
SetLength(FRunSectionInfo, Schemes.Count + 1); // include default
for i := 0 to KnownLines.Count - 1 do
KnownRanges[i].UpdateForScheme(Schemes);
if CurrentLines <> nil then
CurrentRanges.CopyToScheme(Schemes);
SchemeItemChanged(nil);
end;
procedure TSynMultiSyn.DetachHighlighter(AHighlighter: TSynCustomHighlighter;
AScheme: TSynHighlighterMultiScheme);
var
i: Integer;
begin
for i := 0 to KnownLines.Count - 1 do
AHighlighter.DetachFromLines(KnownRanges[i].VirtualLines[AScheme]);
end;
procedure TSynMultiSyn.AttachHighlighter(AHighlighter: TSynCustomHighlighter;
AScheme: TSynHighlighterMultiScheme);
var
i: Integer;
begin
for i := 0 to KnownLines.Count - 1 do
AHighlighter.AttachToLines(KnownRanges[i].VirtualLines[AScheme]);
end;
{ TSynHighlighterMultiSchemeList }
constructor TSynHighlighterMultiSchemeList.Create(aOwner: TSynMultiSyn);
begin
inherited Create(TSynHighlighterMultiScheme);
FOwner := aOwner;
end;
function TSynHighlighterMultiSchemeList.IndexOf(AnItem: TSynHighlighterMultiScheme): Integer;
begin
Result := Count - 1;
while (Result >= 0) and (Items[Result] <> AnItem) do
dec(Result);
end;
function TSynHighlighterMultiSchemeList.GetItems(Index: integer): TSynHighlighterMultiScheme;
begin
Result := inherited Items[Index] as TSynHighlighterMultiScheme;
end;
function TSynHighlighterMultiSchemeList.GetConvertedCurrentLine: String;
begin
if FConvertedCurrentLine = '' then
FConvertedCurrentLine := UTF8UpperCase(FCurrentLine);
Result := FConvertedCurrentLine;
end;
procedure TSynHighlighterMultiSchemeList.SetCurrentLine(const AValue: String);
var
i: Integer;
begin
if FCurrentLine = AValue then exit;
FCurrentLine := AValue;
FConvertedCurrentLine := '';
for i := 0 to Count - 1 do
Items[i].ClearLinesSet;
end;
function TSynHighlighterMultiSchemeList.GetOwner: TPersistent;
begin
Result := Owner;
end;
procedure TSynHighlighterMultiSchemeList.SetItems(Index: integer; const Value: TSynHighlighterMultiScheme);
begin
inherited Items[Index] := Value;
end;
procedure TSynHighlighterMultiSchemeList.Update(Item: TCollectionItem);
begin
// property of an Item changed
Owner.SchemeItemChanged(Item);
end;
procedure TSynHighlighterMultiSchemeList.Notify(Item: TCollectionItem;
Action: TCollectionNotification);
begin
// Item added/removed
inherited Notify(Item, Action);
Owner.SchemeChanged;
end;
{ TSynHighlighterMultiScheme }
function TSynHighlighterMultiScheme.GetConvertedLine: String;
begin
if FCaseSensitive then
Result := TSynHighlighterMultiSchemeList(Collection).CurrentLine
else
Result := TSynHighlighterMultiSchemeList(Collection).ConvertedCurrentLine;
end;
function TSynHighlighterMultiScheme.GetConvertedEndExpr: String;
begin
if FCaseSensitive then
Result := FEndExpr
else begin
if FConvertedEndExpr = '' then
FConvertedEndExpr := Utf8UpperCase(FEndExpr);
Result := FConvertedEndExpr;
end;
end;
function TSynHighlighterMultiScheme.GetConvertedStartExpr: String;
begin
if FCaseSensitive then
Result := FStartExpr
else begin
if FConvertedStartExpr = '' then
FConvertedStartExpr := Utf8UpperCase(FStartExpr);
Result := FConvertedStartExpr;
end;
end;
constructor TSynHighlighterMultiScheme.Create(TheCollection: TCollection);
begin
FStartExprScanner := TRegExpr.Create;
FEndExprScanner := TRegExpr.Create;
fCaseSensitive := True;
fMarkerAttri := TSynHighlighterAttributes.Create(@SYNS_AttrMarker, SYNS_XML_AttrMarker);
fMarkerAttri.OnChange := @MarkerAttriChanged;
MarkerAttri.Background := clYellow;
MarkerAttri.Style := [fsBold];
MarkerAttri.InternalSaveDefaultValues;
inherited Create(TheCollection); // Calls notify, all setup must be done
end;
destructor TSynHighlighterMultiScheme.Destroy;
begin
{ unhook notification handlers }
Highlighter := nil;
fMarkerAttri.Free;
inherited Destroy;
FreeAndNil(FStartExprScanner);
FreeAndNil(FEndExprScanner);
end;
procedure TSynHighlighterMultiScheme.ClearLinesSet;
begin
FStartLineSet := False;
FEndLineSet := False;
end;
function TSynHighlighterMultiScheme.FindStartPosInLine(ASearchPos: Integer): Integer;
var
t: String;
begin
if (FStartExprScanner.Expression = '') or (FEndExprScanner.Expression = '') then
exit(-1);
if not FStartLineSet then begin
FStartExprScanner.InputString := GetConvertedLine;
FStartLineSet := True;
end;
Repeat
if FStartExprScanner.Exec(ASearchPos) then begin
Result := FStartExprScanner.MatchPos[0];
FLastMatchLen := FStartExprScanner.MatchLen[0];
if Assigned(OnCheckStartMarker) then begin
t := FStartExprScanner.Match[0];
OnCheckStartMarker(TSynHighlighterMultiSchemeList(Collection).Owner, Result, FLastMatchLen, t);
if (t <> '') and (FLastMatchLen > 0) then
exit;
ASearchPos := FStartExprScanner.MatchPos[0] + 1;
end
else
exit;
end
else begin
Result := -1;
FLastMatchLen := 0;
exit;
end;
until False;
end;
function TSynHighlighterMultiScheme.FindEndPosInLine(ASearchPos: Integer): Integer;
var
t: String;
begin
if not FEndLineSet then begin
FEndExprScanner.InputString := GetConvertedLine;
FEndLineSet:= True;
end;
Repeat
if FEndExprScanner.Exec(ASearchPos) then begin
Result := FEndExprScanner.MatchPos[0];
FLastMatchLen := FEndExprScanner.MatchLen[0];
if Assigned(OnCheckEndMarker) then begin
t := FEndExprScanner.Match[0];
OnCheckEndMarker(TSynHighlighterMultiSchemeList(Collection).Owner, Result, FLastMatchLen, t);
if (t <> '') and (FLastMatchLen > 0) then
exit;
ASearchPos := FEndExprScanner.MatchPos[0] + 1;
end
else
exit;
end
else begin
Result := -1;
FLastMatchLen := 0;
exit;
end;
until False;
end;
function TSynHighlighterMultiScheme.GetDisplayName: String;
begin
if SchemeName <> '' then
Result := SchemeName
else
Result := inherited GetDisplayName;
end;
procedure TSynHighlighterMultiScheme.MarkerAttriChanged(Sender: TObject);
begin
Changed( False );
end;
procedure TSynHighlighterMultiScheme.SetCaseSensitive(const Value: Boolean);
begin
if fCaseSensitive <> Value then
begin
fCaseSensitive := Value;
FStartExprScanner.Expression := GetConvertedStartExpr;
FEndExprScanner.Expression := GetConvertedEndExpr;
ClearLinesSet;
FNeedHLScan := True;
Changed( False );
FNeedHLScan := False;
end;
end;
procedure TSynHighlighterMultiScheme.SetVirtualLines(const AValue: TSynHLightMultiVirtualLines);
begin
FVirtualLines := AValue;
if FHighlighter <> nil then
FHighlighter.CurrentLines := AValue;
end;
procedure TSynHighlighterMultiScheme.SetDisplayName(const Value: String);
begin
SchemeName := Value;
end;
procedure TSynHighlighterMultiScheme.SetEndExpr(const Value: string);
var OldValue: String;
begin
if fEndExpr <> Value then
begin
OldValue := GetConvertedEndExpr;
FConvertedEndExpr := '';
FEndExpr := Value;
FEndExprScanner.Expression := GetConvertedEndExpr;
FNeedHLScan := True;
if GetConvertedEndExpr <> OldValue then
Changed( False );
FNeedHLScan := False;
end;
end;
procedure TSynHighlighterMultiScheme.SetHighlighter(const Value: TSynCustomHighlighter);
var
ParentHLighter: TSynMultiSyn;
begin
if Highlighter <> Value then
begin
if (Value = TSynHighlighterMultiSchemeList(Collection).Owner) then
raise Exception.Create('circular highlighter not allowed');
ParentHLighter := TSynHighlighterMultiSchemeList(Collection).Owner;
if Highlighter <> nil then begin
Highlighter.RemoveFreeNotification(ParentHLighter);
ParentHLighter.UnhookHighlighter(Highlighter);
ParentHLighter.DetachHighlighter(Highlighter, Self);
end;
fHighlighter := Value;
if Highlighter <> nil then begin
ParentHLighter.AttachHighlighter(Highlighter, Self);
Highlighter.FreeNotification(ParentHLighter);
if FVirtualLines <> nil then
FHighlighter.CurrentLines := FVirtualLines;
end;
FNeedHLScan := True;
Changed(False);
FNeedHLScan := False;
end;
end;
procedure TSynHighlighterMultiScheme.SetMarkerAttri(const Value: TSynHighlighterAttributes);
begin
fMarkerAttri.Assign(Value);
end;
procedure TSynHighlighterMultiScheme.SetStartExpr(const Value: string);
var OldValue: String;
begin
if fStartExpr <> Value then
begin
OldValue := GetConvertedStartExpr;
FConvertedStartExpr := '';
FStartExpr := Value;
FStartExprScanner.Expression := GetConvertedStartExpr;
FNeedHLScan := True; // TODO: only if EndScanne.Expression <> '' ?
if GetConvertedStartExpr <> OldValue then
Changed( False );
FNeedHLScan := False;
end;
end;
initialization
SYNDEBUG_MULTIHL := DebugLogger.RegisterLogGroup('SYNDEBUG_MULTIHL', False);
end.