SynEdit TSynMultiSyn: Several fixes issue #0022519

git-svn-id: trunk@38223 -
This commit is contained in:
martin 2012-08-10 00:04:11 +00:00
parent d7308609fb
commit b56c9c69de
3 changed files with 500 additions and 42 deletions

View File

@ -43,12 +43,19 @@ unit SynHighlighterMulti;
{$I synedit.inc}
{$IFDEF SynDebug}
{$DEFINE SynDebugMultiHL}
{$ENDIF}
interface
uses
Classes, Graphics, SysUtils, LCLProc, math,
SynRegExpr, SynEditStrConst, SynEditTypes, SynEditTextBase,
SynEditHighlighter;
SynEditHighlighter,
{$IFDEF SynDebugMultiHL}LazLoggerBase{$ELSE}LazLoggerDummy{$ENDIF}
;
type
@ -75,6 +82,7 @@ type
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
@ -83,7 +91,8 @@ type
read GetSectionPointer;
function IndexOfFirstSectionAtLineIdx(ALineIdx: Integer; ACharPos: Integer = -1;
UseNext: Boolean = True): Integer;
function IndexOfFirstSectionAtVirtualIdx(ALineIdx: Integer): Integer;
function IndexOfFirstSectionAtVirtualIdx(ALineIdx: Integer; AGetLastSection: Boolean = False): Integer;
function VirtualIdxToRealIdx(AVLineIdx: Integer): Integer;
end;
{ TSynHLightMultiVirtualLines }
@ -237,6 +246,7 @@ type
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
@ -327,8 +337,13 @@ type
write fDefaultLanguageName;
end;
function dbgs(const ASect: TSynHLightMultiVirtualSection): String; overload;
implementation
var
SYNDEBUG_MULTIHL: PLazLoggerLogGroup;
const
TokenKindPerHighlighter = 100;
@ -347,10 +362,20 @@ 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;
@ -366,6 +391,10 @@ 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;
@ -375,6 +404,15 @@ begin
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
@ -442,7 +480,8 @@ begin
end;
end;
function TSynHLightMultiSectionList.IndexOfFirstSectionAtVirtualIdx(ALineIdx: Integer): Integer;
function TSynHLightMultiSectionList.IndexOfFirstSectionAtVirtualIdx(ALineIdx: Integer;
AGetLastSection: Boolean): Integer;
var
p, p1, p2: Integer;
s: PSynHLightMultiVirtualSection;
@ -469,7 +508,7 @@ begin
end;
s := PSynHLightMultiVirtualSection(ItemPointer[p1]);
if ALineIdx = s^.VirtualLine then begin
if (ALineIdx = s^.VirtualLine) and (not AGetLastSection) then begin
while (p1 >= 0) and (s^.VirtualLine = ALineIdx) do begin
dec(p1);
if p1 >= 0 then
@ -491,6 +530,16 @@ begin
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;
@ -579,7 +628,7 @@ begin
if (FFirstHLChangedLine < 0) or (FFirstHLChangedLine > aIndex) then
FFirstHLChangedLine := aIndex;
if (FLastHLChangedLine < aIndex + aCount - 1) then
FFirstHLChangedLine := aIndex + aCount - 1;
FLastHLChangedLine := aIndex + aCount - 1;
end;
constructor TSynHLightMultiVirtualLines.Create(ALines: TSynEditStringsBase);
@ -611,6 +660,12 @@ 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);
@ -619,23 +674,32 @@ var
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;
//DebugLn(['***** ', FRegionScanStartRangeIndex, ' cnt ', Count]);
{$IFDEF SynDebugMultiHL}
DebugLn(SYNDEBUG_MULTIHL, ['***** ', FRegionScanStartRangeIndex, ' cnt ', Count]);
{$ENDIF}
if FRegionScanStartRangeIndex < 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;
//DebugLn(['A ', NewVLine]);
{$IFDEF SynDebugMultiHL}
DebugLn(SYNDEBUG_MULTIHL, ['A ', NewVLine]);
{$ENDIF}
LastEnd := s.EndPos.y;
end
else begin
NewVLine := 0;
//DebugLn(['B ', NewVLine]);
{$IFDEF SynDebugMultiHL}
DebugLn(SYNDEBUG_MULTIHL, ['B ', NewVLine]);
{$ENDIF}
LastEnd := FSectionList.Sections[FRegionScanStartRangeIndex].StartPos.y;
end;
LastVline := NewVLine;
@ -669,6 +733,12 @@ 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);
@ -680,6 +750,13 @@ 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
@ -864,6 +941,16 @@ begin
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;
@ -1024,8 +1111,14 @@ var
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;
@ -1035,8 +1128,11 @@ begin
end;
DefaultVirtualLines.PrepareRegionScan(Result);
for i := 0 to Schemes.Count - 1 do
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
@ -1095,7 +1191,14 @@ 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);
@ -1104,13 +1207,15 @@ begin
for i := 0 to Schemes.Count - 1 do
if Schemes[i].Highlighter <> nil then begin
Schemes[i].Highlighter.ScanRanges;
if Result < Schemes[i].VirtualLines.LastHLChangedLine then
Result := Schemes[i].VirtualLines.LastHLChangedLine;
j := Schemes[i].VirtualLines.SectionList.VirtualIdxToRealIdx(Schemes[i].VirtualLines.LastHLChangedLine);
if Result < j then
Result := j;
end;
if FDefaultHighlighter <> nil then begin
FDefaultHighlighter.ScanRanges;
if Result < DefaultVirtualLines.LastHLChangedLine then
Result := DefaultVirtualLines.LastHLChangedLine;
j := DefaultVirtualLines.SectionList.VirtualIdxToRealIdx(DefaultVirtualLines.LastHLChangedLine);
if Result < j then
Result := j;
end;
end;
@ -1904,5 +2009,8 @@ begin
end;
end;
initialization
SYNDEBUG_MULTIHL := DebugLogger.RegisterLogGroup('SYNDEBUG_MULTIHL', False);
end.

View File

@ -7,7 +7,7 @@ interface
uses
Classes, SysUtils, Forms, fpcunit, SynEdit, LCLType, LCLProc, math,
SynEditTypes, SynEditPointClasses, Clipbrd;
SynEditTypes, SynEditPointClasses, SynEditKeyCmds, Clipbrd;
type
@ -25,6 +25,8 @@ type
TTestSynEdit = class(TSynEdit)
public
procedure TestKeyPress(Key: Word; Shift: TShiftState);
procedure TestTypeText(ALogCaretX, ALogCaretY: Integer; Input: String);
procedure TestTypeText(Input: String);
function TestFullText: String;
procedure TestSetSelText(Value: String;
PasteMode: TSynSelectionMode = smNormal;
@ -160,6 +162,34 @@ begin
{$IFDEF WITH_APPMSG}Application.ProcessMessages;{$ENDIF}
end;
procedure TTestSynEdit.TestTypeText(ALogCaretX, ALogCaretY: Integer; Input: String);
begin
LogicalCaretXY := Point(ALogCaretX, ALogCaretY);
TestTypeText(Input);
end;
procedure TTestSynEdit.TestTypeText(Input: String);
var
l: Integer;
begin
while Input <> '' do begin
if Input[1] = #13 then begin
CommandProcessor(ecLineBreak, '', nil);
delete(Input, 1, 1);
Continue;
end;
if Input[1] = #8 then begin
CommandProcessor(ecDeleteLastChar, '', nil);
delete(Input, 1, 1);
Continue;
end;
l := UTF8CharacterLength(@Input[1]);
if l < 1 then Break;
CommandProcessor(ecChar, copy(Input, 1, l), nil);
delete(Input, 1, l);
end;
end;
function TTestSynEdit.TestFullText: String;
begin
Result := ViewedTextBuffer.Text;

View File

@ -5,15 +5,20 @@ unit TestHighlightMulti;
interface
uses
Classes, SysUtils, LCLProc, fpcunit, testregistry, TestBase, Forms,
SynEditHighlighter, SynHighlighterMulti,
SynHighlighterLFM, SynHighlighterXML, SynHighlighterPas, SynEditKeyCmds;
Classes, SysUtils, math, LCLProc, fpcunit, testregistry, TestBase, Forms, SynEditHighlighter,
SynHighlighterMulti, SynHighlighterLFM, SynHighlighterXML, SynHighlighterPas, SynEditKeyCmds,
LazSynEditText, SynEditTextBuffer, LazLogger;
type
{ TTestHighlightMulti }
TTestHighlightMulti = class(TTestBase)
private
// Track lines that SynEdit invalidates
FHLCLow, FHLCHigh:Integer;
procedure ResetHighlightChanged;
procedure DoHighlightChanged(Sender: TSynEditStrings; AIndex, ACount : Integer);
protected
procedure SetRealLinesText;
procedure SetRealLinesText2;
@ -35,6 +40,24 @@ implementation
{ TTestHighlightMulti }
procedure TTestHighlightMulti.ResetHighlightChanged;
begin
FHLCLow := -1;
FHLCHigh := -1;
end;
procedure TTestHighlightMulti.DoHighlightChanged(Sender: TSynEditStrings; AIndex,
ACount: Integer);
begin
if FHLCLow < 0
then FHLCLow := AIndex
else FHLCLow := Min(FHLCLow, AIndex);
if FHLCHigh < 0
then FHLCHigh := AIndex + ACount
else FHLCHigh := Max(FHLCHigh, AIndex + ACount);
end;
procedure TTestHighlightMulti.SetRealLinesText;
begin
ReCreateEdit;
@ -98,10 +121,7 @@ var
s: TSynHLightMultiVirtualSection;
i: Integer;
begin
for i := 0 to ASectList.Count - 1 do begin
s := ASectList[i];
debugln([i,' Start=',dbgs(s.StartPos),' End=',dbgs(s.EndPos),' VLine=',s.VirtualLine,' TokenStart=',dbgs(s.TokenStartPos),' TokenEnd=',dbgs(s.TokenEndPos)]);
end;
ASectList.Debug;
end;
procedure TTestHighlightMulti.DumpRanges(ARangeList: TSynHighlighterRangeList);
@ -140,6 +160,10 @@ begin
HL.StartAtLineIndex(LineIdx);
c := 0;
while not HL.GetEol do begin
if c >= length(ExpAttr) then begin
inc(c);
break;
end;
//DebugLn([HL.GetToken,' (',HL.GetTokenID ,') at ', HL.GetTokenPos]);
tk := HL.GetTokenAttribute;
if tk <> nil
@ -147,13 +171,9 @@ begin
else tkName := '<nil>';
AssertTrue(Format('%s Attrib Line=%d pos=%d exp=%s got=%s',
[Name, LineIdx, c, ExpAttr[c].StoredName, tkName]),
ExpAttr[c] = HL.GetTokenAttribute);
ExpAttr[c] = tk);
HL.Next;
inc(c);
if c >= length(ExpAttr) then begin
if not HL.GetEol then inc(c);
break;
end;
end;
AssertEquals(Name+ 'TokenId Line='+IntToStr(LineIdx)+' amount of tokens', length(ExpAttr), c );
end;
@ -684,6 +704,7 @@ var
end;
//if synedit.Highlighter <> nil then begin MultiHl.CurrentLines := SynEdit.TextBuffer; DumpAll(MultiHl); end;
SynEdit.SimulatePaintText;
if MultiHl.CurrentLines <> nil then DumpAll(MultiHl) else debugln('#');
//SynEdit.Invalidate;
//Application.ProcessMessages;
end;
@ -821,6 +842,30 @@ var
AtXmlEl, AtXmlSym, AtXmlTxt: TSynHighlighterAttributes;
AtPasMark, AtPasSym, AtPasId, AtPasKey, AtPasSp, AtPasCom: TSynHighlighterAttributes;
PasHl: TSynPasSyn;
i, j: Integer;
procedure InitMultiEMpty;
begin
MultiHl := TSynMultiSyn.Create(Form);
XmlHl := TSynXMLSyn.Create(Form);
XmlHl.ElementAttri.Foreground := 255;
MultiHl.DefaultHighlighter := XmlHl;
EmptyScheme := TSynHighlighterMultiScheme(MultiHl.Schemes.Add);
AtXmlEl := XmlHl.ElementAttri;
AtXmlSym := XmlHl.SymbolAttri;
AtXmlTxt := XmlHl.TextAttri;
SynEdit.Highlighter := MultiHl;
MultiHl.CurrentLines := SynEdit.TextBuffer;
end;
procedure FinishMultiEmpty;
begin
SynEdit.Highlighter := nil;
FreeAndNil(XmlHl);
FreeAndNil(MultiHl);
end;
procedure InitMultiXmlPasHl;
begin
@ -859,21 +904,13 @@ var
FreeAndNil(MultiHl);
end;
begin
TSynEditStringList(SynEdit.TextBuffer).AddChangeHandler(senrHighlightChanged, @DoHighlightChanged);
{%region Issue 0022519}
PushBaseName('append after EOT (default scheme)'); // Issue 0022519
MultiHl := TSynMultiSyn.Create(Form);
XmlHl := TSynXMLSyn.Create(Form);
XmlHl.ElementAttri.Foreground := 255;
MultiHl.DefaultHighlighter := XmlHl;
EmptyScheme := TSynHighlighterMultiScheme(MultiHl.Schemes.Add);
AtXmlEl := XmlHl.ElementAttri;
AtXmlSym := XmlHl.SymbolAttri;
AtXmlTxt := XmlHl.TextAttri;
SynEdit.Highlighter := MultiHl;
MultiHl.CurrentLines := SynEdit.TextBuffer;
SynEdit.ClearAll;
InitMultiEMpty;
PushBaseName('Insert "html"');
SynEdit.SetTextBetweenPoints(Point(1,1), Point(1,1), '<html>');
@ -955,13 +992,294 @@ begin
AssertEquals(BaseTestName + 'Section Count', 1, MultiHl.DefaultVirtualLines.SectionList.Count);
SynEdit.Undo;
SynEdit.Highlighter := nil;
FreeAndNil(XmlHl);
FreeAndNil(MultiHl);
FinishMultiEmpty;
PopBaseName;
PopBaseName;
{%endregion}
{%region edit text in single default region (scan end before eot) // Issue 0022519}
for j := 0 to 5 do begin
// 0: do a loop for lines 1 to 5 in the same setup
// 1..5 do each line with an entire setup of it's own
PushBaseName('edit text in single default region (scan end before eot) j='+ IntToStr(j)); // Issue 0022519
SynEdit.ClearAll;
InitMultiEMpty;
PushBaseName('Insert "html"');
SynEdit.TestTypeText(1, 1, '<html>'+#13+'a<body>');
CheckTokensForLine('1st line=html', MultiHl, 0, [AtXmlSym, AtXmlEl, AtXmlSym]);
CheckTokensForLine('2nd line=html', MultiHl, 1, [AtXmlTxt, AtXmlSym, AtXmlEl, AtXmlSym]);
AssertEquals(BaseTestName + 'Section Count', 1, MultiHl.DefaultVirtualLines.SectionList.Count);
PushBaseName('Insert "p"');
SynEdit.TestTypeText(1, 2, '<p>'+#13+'</p>a'+#13+'<foo></foo>'+#13);
CheckTokensForLine('1st line=html', MultiHl, 0, [AtXmlSym, AtXmlEl, AtXmlSym]);
CheckTokensForLine('2nd line=html', MultiHl, 1, [AtXmlSym, AtXmlEl, AtXmlSym]);
CheckTokensForLine('3rd line=html', MultiHl, 2, [AtXmlSym, AtXmlEl, AtXmlSym, AtXmlTxt]);
CheckTokensForLine('4th line=html', MultiHl, 3, [AtXmlSym, AtXmlEl, AtXmlSym,AtXmlSym, AtXmlEl, AtXmlSym]);
CheckTokensForLine('5th line=html', MultiHl, 4, [AtXmlTxt, AtXmlSym, AtXmlEl, AtXmlSym]);
AssertEquals(BaseTestName + 'Section Count', 1, MultiHl.DefaultVirtualLines.SectionList.Count);
i := j;
if j = 0 then i := 1;
while ( (j = 0) and (i <= 5)) or (i = j) do begin
PushBaseName('"a" line '+IntToStr(i));
SynEdit.TestTypeText(3, i, 'a');
CheckTokensForLine('1st line=html', MultiHl, 0, [AtXmlSym, AtXmlEl, AtXmlSym]);
CheckTokensForLine('2nd line=html', MultiHl, 1, [AtXmlSym, AtXmlEl, AtXmlSym]);
CheckTokensForLine('3rd line=html', MultiHl, 2, [AtXmlSym, AtXmlEl, AtXmlSym, AtXmlTxt]);
CheckTokensForLine('4th line=html', MultiHl, 3, [AtXmlSym, AtXmlEl, AtXmlSym,AtXmlSym, AtXmlEl, AtXmlSym]);
CheckTokensForLine('5th line=html', MultiHl, 4, [AtXmlTxt, AtXmlSym, AtXmlEl, AtXmlSym]);
AssertEquals(BaseTestName + 'Section Count', 1, MultiHl.DefaultVirtualLines.SectionList.Count);
PushBaseName('"a" line '+IntToStr(i)+' undo');
SynEdit.Undo;
CheckTokensForLine('1st line=html', MultiHl, 0, [AtXmlSym, AtXmlEl, AtXmlSym]);
CheckTokensForLine('2nd line=html', MultiHl, 1, [AtXmlSym, AtXmlEl, AtXmlSym]);
CheckTokensForLine('3rd line=html', MultiHl, 2, [AtXmlSym, AtXmlEl, AtXmlSym, AtXmlTxt]);
CheckTokensForLine('4th line=html', MultiHl, 3, [AtXmlSym, AtXmlEl, AtXmlSym,AtXmlSym, AtXmlEl, AtXmlSym]);
CheckTokensForLine('5th line=html', MultiHl, 4, [AtXmlTxt, AtXmlSym, AtXmlEl, AtXmlSym]);
AssertEquals(BaseTestName + 'Section Count', 1, MultiHl.DefaultVirtualLines.SectionList.Count);
PushBaseName('"a" line '+IntToStr(i)+' redo');
SynEdit.Redo;
CheckTokensForLine('1st line=html', MultiHl, 0, [AtXmlSym, AtXmlEl, AtXmlSym]);
CheckTokensForLine('2nd line=html', MultiHl, 1, [AtXmlSym, AtXmlEl, AtXmlSym]);
CheckTokensForLine('3rd line=html', MultiHl, 2, [AtXmlSym, AtXmlEl, AtXmlSym, AtXmlTxt]);
CheckTokensForLine('4th line=html', MultiHl, 3, [AtXmlSym, AtXmlEl, AtXmlSym,AtXmlSym, AtXmlEl, AtXmlSym]);
CheckTokensForLine('5th line=html', MultiHl, 4, [AtXmlTxt, AtXmlSym, AtXmlEl, AtXmlSym]);
AssertEquals(BaseTestName + 'Section Count', 1, MultiHl.DefaultVirtualLines.SectionList.Count);
inc(i);
end;
FinishMultiEmpty;
PopBaseName;
PopBaseName;
end; // for j
{%endregion}
{%region edit text in single pascal region (scan end before eot) // Issue 0022519}
for i := 1 to 6 do begin
PushBaseName('edit text in single pascal region (scan end before eot) j='+ IntToStr(j)); // Issue 0022519
SynEdit.ClearAll;
InitMultiXmlPasHl;
PushBaseName('Insert "<pas>"');
SynEdit.TestTypeText(1, 1, '<pas>'+#13+'</pas>');
CheckTokensForLine('1st line=html', MultiHl, 0, [AtPasMark]);
CheckTokensForLine('2nd line=html', MultiHl, 1, [AtPasMark]);
AssertEquals(BaseTestName + 'Section Count def', 0, MultiHl.DefaultVirtualLines.SectionList.Count);
AssertEquals(BaseTestName + 'Section Count pas', 1, PasScheme.VirtualLines.SectionList.Count);
PushBaseName('Insert "unit..."');
SynEdit.TestTypeText(1, 2, 'unit'+#13+'Foo;'+#13+'uses'+#13+'Bar;'+#13);
//DumpAll(MultiHl);
CheckTokensForLine('1st line=html', MultiHl, 0, [AtPasMark]);
CheckTokensForLine('2nd line=html', MultiHl, 1, [AtPasKey]); // unit
CheckTokensForLine('3rd line=html', MultiHl, 2, [AtPasId, AtPasSym]); // Foo;
CheckTokensForLine('4th line=html', MultiHl, 3, [AtPasKey]); // uses
CheckTokensForLine('5th line=html', MultiHl, 4, [AtPasId, AtPasSym]); // Bar;
CheckTokensForLine('6th line=html', MultiHl, 5, [AtPasMark]);
AssertEquals(BaseTestName + 'Section Count def', 0, MultiHl.DefaultVirtualLines.SectionList.Count);
AssertEquals(BaseTestName + 'Section Count pas', 1, PasScheme.VirtualLines.SectionList.Count);
PushBaseName('"//" line '+IntToStr(i));
if i = 1
then SynEdit.TestTypeText(6, i, '//')
else if i = 6
then SynEdit.TestTypeText(1, i, '//')
else SynEdit.TestTypeText(5, i, '//');
//DumpAll(MultiHl);
if i = 1
then CheckTokensForLine('1st line=html', MultiHl, 0, [AtPasMark, AtPasCom])
else CheckTokensForLine('1st line=html', MultiHl, 0, [AtPasMark]);
if i = 2
then CheckTokensForLine('2nd line=html', MultiHl, 1, [AtPasKey, AtPasCom]) // unit
else CheckTokensForLine('2nd line=html', MultiHl, 1, [AtPasKey]); // unit
if i = 3
then CheckTokensForLine('3rd line=html', MultiHl, 2, [AtPasId, AtPasSym, AtPasCom]) // Foo;
else CheckTokensForLine('3rd line=html', MultiHl, 2, [AtPasId, AtPasSym]); // Foo;
if i = 4
then CheckTokensForLine('4th line=html', MultiHl, 3, [AtPasKey, AtPasCom]) // uses
else CheckTokensForLine('4th line=html', MultiHl, 3, [AtPasKey]); // uses
if i = 5
then CheckTokensForLine('5th line=html', MultiHl, 4, [AtPasId, AtPasSym, AtPasCom]) // Bar;
else CheckTokensForLine('5th line=html', MultiHl, 4, [AtPasId, AtPasSym]); // Bar;
AssertEquals(BaseTestName + 'Section Count def', 0, MultiHl.DefaultVirtualLines.SectionList.Count);
AssertEquals(BaseTestName + 'Section Count pas', 1, PasScheme.VirtualLines.SectionList.Count);
if i = 6
then CheckTokensForLine('6th line=html', MultiHl, 5, [AtPasCom, AtPasMark])
else CheckTokensForLine('6th line=html', MultiHl, 5, [AtPasMark]);
PushBaseName('undo //');
SynEdit.Undo;
//DumpAll(MultiHl);
CheckTokensForLine('1st line=html', MultiHl, 0, [AtPasMark]);
CheckTokensForLine('2nd line=html', MultiHl, 1, [AtPasKey]); // unit
CheckTokensForLine('3rd line=html', MultiHl, 2, [AtPasId, AtPasSym]); // Foo;
CheckTokensForLine('4th line=html', MultiHl, 3, [AtPasKey]); // uses
CheckTokensForLine('5th line=html', MultiHl, 4, [AtPasId, AtPasSym]); // Bar;
CheckTokensForLine('6th line=html', MultiHl, 5, [AtPasMark]);
AssertEquals(BaseTestName + 'Section Count def', 0, MultiHl.DefaultVirtualLines.SectionList.Count);
AssertEquals(BaseTestName + 'Section Count pas', 1, PasScheme.VirtualLines.SectionList.Count);
PushBaseName('"(*" line '+IntToStr(i));
ResetHighlightChanged;
if i = 1
then SynEdit.TestTypeText(6, i, '(*')
else if i = 6
then SynEdit.TestTypeText(1, i, '(*')
else SynEdit.TestTypeText(5, i, '(*');
//DumpAll(MultiHl);
if i = 1
then CheckTokensForLine('1st line=html', MultiHl, 0, [AtPasMark, AtPasCom])
else CheckTokensForLine('1st line=html', MultiHl, 0, [AtPasMark]);
if i = 2
then CheckTokensForLine('2nd line=html', MultiHl, 1, [AtPasKey, AtPasCom]) // unit
else if i < 2
then CheckTokensForLine('2nd line=html', MultiHl, 1, [AtPasCom])
else CheckTokensForLine('2nd line=html', MultiHl, 1, [AtPasKey]); // unit
if i = 3
then CheckTokensForLine('3rd line=html', MultiHl, 2, [AtPasId, AtPasSym, AtPasCom]) // Foo;
else if i < 3
then CheckTokensForLine('3rd line=html', MultiHl, 2, [AtPasCom]) // Foo;
else CheckTokensForLine('3rd line=html', MultiHl, 2, [AtPasId, AtPasSym]); // Foo;
if i = 4
then CheckTokensForLine('4th line=html', MultiHl, 3, [AtPasKey, AtPasCom]) // uses
else if i < 4
then CheckTokensForLine('4th line=html', MultiHl, 3, [AtPasCom]) // uses
else CheckTokensForLine('4th line=html', MultiHl, 3, [AtPasKey]);
if i = 5
then CheckTokensForLine('5th line=html', MultiHl, 4, [AtPasId, AtPasSym, AtPasCom]) // Bar;
else if i < 5
then CheckTokensForLine('5th line=html', MultiHl, 4, [AtPasCom]) // Bar;
else CheckTokensForLine('5th line=html', MultiHl, 4, [AtPasId, AtPasSym]); // Bar;
if i = 6
then CheckTokensForLine('6th line=html', MultiHl, 5, [AtPasCom, AtPasMark])
else CheckTokensForLine('6th line=html', MultiHl, 5, [AtPasMark]);
AssertEquals(BaseTestName + 'Section Count def', 0, MultiHl.DefaultVirtualLines.SectionList.Count);
AssertEquals(BaseTestName + 'Section Count pas', 1, PasScheme.VirtualLines.SectionList.Count);
// either invalidate from line-index i-1, or 1 before (max 1 before)
//DebugLn(['FHLCLow=', FHLCLow, ' FHLCHigh=',FHLCHigh]);
AssertTrue('Top inval', (FHLCLow = Max(-1,i-2)) or (FHLCLow = i-1)); // HL currently sends -1
//AssertTrue('Top inval', FHLCLow in [Max(0,i-2), i-1]);
AssertTrue('bottom inval', FHLCHigh = 5);
PushBaseName('undo (*');
SynEdit.Undo;
//DumpAll(MultiHl);
CheckTokensForLine('1st line=html', MultiHl, 0, [AtPasMark]);
CheckTokensForLine('2nd line=html', MultiHl, 1, [AtPasKey]); // unit
CheckTokensForLine('3rd line=html', MultiHl, 2, [AtPasId, AtPasSym]); // Foo;
CheckTokensForLine('4th line=html', MultiHl, 3, [AtPasKey]); // uses
CheckTokensForLine('5th line=html', MultiHl, 4, [AtPasId, AtPasSym]); // Bar;
CheckTokensForLine('6th line=html', MultiHl, 5, [AtPasMark]);
AssertEquals(BaseTestName + 'Section Count def', 0, MultiHl.DefaultVirtualLines.SectionList.Count);
AssertEquals(BaseTestName + 'Section Count pas', 1, PasScheme.VirtualLines.SectionList.Count);
FinishMultiXmlPasHl;
PopBaseName;
PopBaseName;
end; // for j
{%endregion}
{%region edit text in single pascal region after default}
// ensure virtuallines by nested HL are mapped for invalidation
PushBaseName('edit text in pascal region after default (scan end before eot) j='+ IntToStr(j)); // Issue 0022519
SynEdit.ClearAll;
InitMultiXmlPasHl;
PushBaseName('Insert "<pas>"');
SynEdit.TestTypeText(1, 1, 'x'+#13+'x'+#13+'<pas>'+#13+'</pas>');
PushBaseName('Insert "unit..."');
SynEdit.TestTypeText(1, 4, 'unit'+#13+'Foo;'+#13+'uses'+#13+'Bar;'+#13+'var'+#13+'xx');
//DumpAll(MultiHl);
CheckTokensForLine('1st line=html', MultiHl, 0, [AtXmlTxt]);
CheckTokensForLine('2st line=html', MultiHl, 1, [AtXmlTxt]);
CheckTokensForLine('3st line=html', MultiHl, 2, [AtPasMark]);
CheckTokensForLine('4nd line=html', MultiHl, 3, [AtPasKey]); // unit
CheckTokensForLine('5rd line=html', MultiHl, 4, [AtPasId, AtPasSym]); // Foo;
CheckTokensForLine('6th line=html', MultiHl, 5, [AtPasKey]); // uses
CheckTokensForLine('7th line=html', MultiHl, 6, [AtPasId, AtPasSym]); // Bar;
CheckTokensForLine('8th line=html', MultiHl, 7, [AtPasKey]);
CheckTokensForLine('9th line=html', MultiHl, 8, [AtPasId, AtPasMark]);
AssertEquals(BaseTestName + 'Section Count def', 1, MultiHl.DefaultVirtualLines.SectionList.Count);
AssertEquals(BaseTestName + 'Section Count pas', 1, PasScheme.VirtualLines.SectionList.Count);
PushBaseName('"(*" line '+IntToStr(i));
ResetHighlightChanged;
SynEdit.TestTypeText(5, 5, '(*'); // after FOO
DumpAll(MultiHl);
CheckTokensForLine('1st line=html', MultiHl, 0, [AtXmlTxt]);
CheckTokensForLine('2st line=html', MultiHl, 1, [AtXmlTxt]);
CheckTokensForLine('3st line=html', MultiHl, 2, [AtPasMark]);
CheckTokensForLine('4nd line=html', MultiHl, 3, [AtPasKey]); // unit
CheckTokensForLine('5rd line=html', MultiHl, 4, [AtPasId, AtPasSym, AtPasCom]); // Foo;
CheckTokensForLine('6th line=html', MultiHl, 5, [AtPasCom]); // uses
CheckTokensForLine('7th line=html', MultiHl, 6, [AtPasCom]); // Bar;
CheckTokensForLine('8th line=html', MultiHl, 7, [AtPasCom]);
CheckTokensForLine('9th line=html', MultiHl, 8, [AtPasCom, AtPasMark]);
AssertEquals(BaseTestName + 'Section Count def', 1, MultiHl.DefaultVirtualLines.SectionList.Count);
AssertEquals(BaseTestName + 'Section Count pas', 1, PasScheme.VirtualLines.SectionList.Count);
DebugLn(['FHLCLow=', FHLCLow, ' FHLCHigh=',FHLCHigh]);
AssertTrue('Top inval', (FHLCLow = 3) or (FHLCLow = 4)); // HL currently sends -1
//AssertTrue('Top inval', FHLCLow in [Max(0,i-2), i-1]);
AssertTrue('bottom inval', FHLCHigh = 8);
PushBaseName('undo (*');
SynEdit.Undo;
DumpAll(MultiHl);
CheckTokensForLine('1st line=html', MultiHl, 0, [AtXmlTxt]);
CheckTokensForLine('2st line=html', MultiHl, 1, [AtXmlTxt]);
CheckTokensForLine('3st line=html', MultiHl, 2, [AtPasMark]);
CheckTokensForLine('4nd line=html', MultiHl, 3, [AtPasKey]); // unit
CheckTokensForLine('5rd line=html', MultiHl, 4, [AtPasId, AtPasSym]); // Foo;
CheckTokensForLine('6th line=html', MultiHl, 5, [AtPasKey]); // uses
CheckTokensForLine('7th line=html', MultiHl, 6, [AtPasId, AtPasSym]); // Bar;
CheckTokensForLine('8th line=html', MultiHl, 7, [AtPasKey]);
CheckTokensForLine('9th line=html', MultiHl, 8, [AtPasId, AtPasMark]);
AssertEquals(BaseTestName + 'Section Count def', 1, MultiHl.DefaultVirtualLines.SectionList.Count);
AssertEquals(BaseTestName + 'Section Count pas', 1, PasScheme.VirtualLines.SectionList.Count);
FinishMultiXmlPasHl;
PopBaseName;
PopBaseName;
{%endregion}
{%region}
PushBaseName('append after EOT - after sub-scheme');
SynEdit.ClearAll;
SynEdit.SetTextBetweenPoints(Point(1,1), Point(1,1), 'a<pas>unit</pas>');
InitMultiXmlPasHl;
MultiHl.CurrentLines := SynEdit.TextBuffer;
SynEdit.SimulatePaintText;
CheckTokensForLine('1st line', MultiHl, 0, [AtXmlTxt, AtPasMark, AtPasKey, AtPasMark]);
AssertEquals(BaseTestName + 'Section Count def', 1, MultiHl.DefaultVirtualLines.SectionList.Count);
AssertEquals(BaseTestName + 'Section Count pas', 1, PasScheme.VirtualLines.SectionList.Count);
SynEdit.CaretXY := point(17,1);
SynEdit.CommandProcessor(ecLineBreak, '', nil);
SynEdit.CommandProcessor(ecChar, '<', nil);
SynEdit.CommandProcessor(ecChar, 'a', nil);
SynEdit.CommandProcessor(ecChar, '>', nil);
//DumpAll(MultiHl);
CheckTokensForLine('1st line', MultiHl, 0, [AtXmlTxt, AtPasMark, AtPasKey, AtPasMark]);
CheckTokensForLine('2nd line', MultiHl, 1, [AtXmlSym, AtXmlEl, AtXmlSym]);
AssertEquals(BaseTestName + 'Section Count def', 2, MultiHl.DefaultVirtualLines.SectionList.Count);
AssertEquals(BaseTestName + 'Section Count pas', 1, PasScheme.VirtualLines.SectionList.Count);
FinishMultiXmlPasHl;
PopBaseName;
{%endregion}
{%region}
PushBaseName('append after EOT - after sub-scheme');
SynEdit.ClearAll;
@ -1129,5 +1447,7 @@ end;
initialization
RegisterTest(TTestHighlightMulti);
DebugLogger.FindOrRegisterLogGroup('SYNDEBUG_MULTIHL', True)^.Enabled := True;
end.