mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-06 01:18:14 +02:00
2067 lines
66 KiB
ObjectPascal
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.
|
|
|