SynEdit TSynMultiSyn: Fixed crash when deleting lines. Part of issue #0022519

git-svn-id: trunk@38150 -
This commit is contained in:
martin 2012-08-04 12:00:32 +00:00
parent 7a161299ec
commit 242370b71e
3 changed files with 289 additions and 31 deletions

View File

@ -1557,7 +1557,9 @@ begin
if AValue = FCurrentLines then
exit;
FCurrentLines := AValue;
FCurrentRanges := TSynHighlighterRangeList(AValue.Ranges[GetRangeIdentifier]);
if FCurrentLines <> nil
then FCurrentRanges := TSynHighlighterRangeList(AValue.Ranges[GetRangeIdentifier])
else FCurrentRanges := nil;
end;
procedure TSynCustomHighlighter.AttachToLines(Lines: TSynEditStringsBase);
@ -1581,6 +1583,8 @@ procedure TSynCustomHighlighter.DetachFromLines(Lines: TSynEditStringsBase);
var
r: TSynHighlighterRangeList;
begin
//if Lines = CurrentLines then
// CurrentLines := nil;
r := TSynHighlighterRangeList(Lines.Ranges[GetRangeIdentifier]);
if not assigned(r) then exit;
r.DecRefCount;

View File

@ -614,18 +614,18 @@ begin
do
FSectionList.Delete(FRegionScanRangeIndex);
VDiff := 0;
DebugLn(['***** ', FRegionScanStartRangeIndex, ' cnt ', Count]);
//DebugLn(['***** ', FRegionScanStartRangeIndex, ' cnt ', Count]);
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]);
//DebugLn(['A ', NewVLine]);
LastEnd := s.EndPos.y;
end
else begin
NewVLine := 0;
DebugLn(['B ', NewVLine]);
//DebugLn(['B ', NewVLine]);
LastEnd := FSectionList.Sections[FRegionScanStartRangeIndex].StartPos.y;
end;
LastVline := NewVLine;
@ -734,49 +734,74 @@ end;
procedure TSynHLightMultiVirtualLines.RealLinesDeleted(AIndex, ACount: Integer);
var
i: Integer;
PartCnt, VLineDiff: 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;
VLineDiff := 0;
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
PartCnt := p^.EndPos.y - AIndex + 1;
FRangeList.ChildDeleteRows(p^.VirtualLine + AIndex - p^.StartPos.y, PartCnt);
FRangeList.CallDeletedLines(p^.VirtualLine + AIndex - p^.StartPos.y, PartCnt);
p^.EndPos.y := p^.EndPos.y - PartCnt;
// Real-lines starting in the middle of the Section
CountInSection := Min(AIndex + ACount, p^.EndPos.y + 1) - AIndex;
FirstVLine := p^.VirtualLine + AIndex - p^.StartPos.y;
PrevEndVLine := p^.VirtualLine + p^.EndPos.y - p^.EndPos.y;
p^.EndPos.y := p^.EndPos.y - CountInSection;
inc(i);
if i = FSectionList.Count then begin
DelVLines;
exit;
end;
p := FSectionList.PSections[i];
VLineDiff := PartCnt;
VLineCount := CountInSection;
end;
while p^.EndPos.y < AIndex + ACount do begin
VLineDiff := VLineDiff + p^.EndPos.y - p^.StartPos.y + 1;
FRangeList.ChildDeleteRows(p^.VirtualLine, p^.EndPos.y - p^.StartPos.y + 1);
FRangeList.CallDeletedLines(p^.VirtualLine, p^.EndPos.y - p^.StartPos.y + 1);
// 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^.EndPos.y;
FSectionList.Delete(i);
if i = FSectionList.Count then
if i = FSectionList.Count then begin
DelVLines;
exit;
end;
p := FSectionList.PSections[i];
end;
if AIndex + ACount > p^.StartPos.y then begin
PartCnt := ACount - (p^.StartPos.y - AIndex);
FRangeList.ChildDeleteRows(p^.VirtualLine, PartCnt);
FRangeList.CallDeletedLines(p^.VirtualLine, PartCnt);
p^.EndPos.y := p^.EndPos.y - PartCnt;
p^.VirtualLine := p^.VirtualLine - VLineDiff;
VLineDiff := VLineDiff + PartCnt;
// Some real-lines at the start of section are deleted
p^.VirtualLine := p^.VirtualLine - VLineCount;
CountInSection := ACount - (p^.StartPos.y - AIndex);
VLineCount := VLineCount + CountInSection;
if PrevEndVLine = p^.VirtualLine then
dec(VLineCount);
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 - VLineDiff;
p^.VirtualLine := p^.VirtualLine - VLineCount;
inc(i);
end;
DelVLines;
end;
procedure TSynHLightMultiVirtualLines.RealLinesChanged(AIndex, ACount: Integer);

View File

@ -17,14 +17,18 @@ type
protected
procedure SetRealLinesText;
procedure SetRealLinesText2;
procedure SetRealLinesText3;
procedure DumpLines(ALines: TSynHLightMultiVirtualLines);
procedure DumpSections(ASectList: TSynHLightMultiSectionList);
procedure DumpRanges(ARangeList: TSynHighlighterRangeList);
procedure DumpAll(Hl: TSynMultiSyn);
procedure CheckTokensForLine(Name: String; HL: TSynCustomHighlighter;
LineIdx: Integer; ExpAttr: Array of TSynHighlighterAttributes);
published
procedure TestSectionList;
procedure TestVirtualLines;
procedure TestMultiHL;
procedure TestMultiHLEdit;
end;
implementation
@ -62,6 +66,25 @@ begin
]);
end;
procedure TTestHighlightMulti.SetRealLinesText3;
begin
SetLines(['abc',
'abc',
'test<pas>unit a;',
'//Comment',
'//Comment',
'uses Foo;',
'//</pas><x>',
'def',
'def',
'def',
'x<pas>interface',
'//Comment',
'//</pas></x>',
'abc'
]);
end;
procedure TTestHighlightMulti.DumpLines(ALines: TSynHLightMultiVirtualLines);
var
i: Integer;
@ -92,16 +115,39 @@ procedure TTestHighlightMulti.DumpAll(Hl: TSynMultiSyn);
var
i: Integer;
begin // ensure CurrentLines are set
debugln(['--- Default']);
DumpLines(hl.DefaultVirtualLines);
DumpSections(hl.DefaultVirtualLines.SectionList);
debugln(['-']);
DumpRanges(TSynHighlighterRangeList(hl.CurrentLines.Ranges[hl]));
for i := 0 to hl.Schemes.Count - 1 do begin
debugln(['-- ',i,' ', dbgs(hl.Schemes[i].Highlighter)]);
DumpLines(hl.Schemes[i].VirtualLines);
DumpSections(hl.Schemes[i].VirtualLines.SectionList);
DebugLnEnter(['>> --- Default / Lines']); DebugLnEnter;
DumpLines(hl.DefaultVirtualLines);
DebugLnExit; DebugLnEnter(['-- Sections']);
DumpSections(hl.DefaultVirtualLines.SectionList);
DebugLnExit; DebugLnEnter(['-- Ranges']);
DumpRanges(TSynHighlighterRangeList(hl.CurrentLines.Ranges[hl]));
for i := 0 to hl.Schemes.Count - 1 do begin
DebugLnExit; DebugLnEnter(['-- Scheme=',i,' ', dbgs(hl.Schemes[i].Highlighter)]);
DumpLines(hl.Schemes[i].VirtualLines);
DumpSections(hl.Schemes[i].VirtualLines.SectionList);
end;
DebugLnExit;
DebugLnExit('<<');
end;
procedure TTestHighlightMulti.CheckTokensForLine(Name: String; HL: TSynCustomHighlighter;
LineIdx: Integer; ExpAttr: array of TSynHighlighterAttributes);
var
c: Integer;
begin
HL.StartAtLineIndex(LineIdx);
c := 0;
while not HL.GetEol do begin
//DebugLn([HL.GetToken,' (',HL.GetTokenID ,') at ', HL.GetTokenPos]);
AssertTrue(Format('%s Attrib Line=%d pos=%d exp=%s got=%s',
[Name, LineIdx, c, ExpAttr[c].StoredName, HL.GetTokenAttribute.StoredName]),
ExpAttr[c] = HL.GetTokenAttribute);
HL.Next;
inc(c);
if c >= length(ExpAttr) then
break;
end;
AssertEquals(Name+ 'TokenId Line='+IntToStr(LineIdx)+' amount of tokens', length(ExpAttr), c );
end;
procedure TTestHighlightMulti.TestSectionList;
@ -759,6 +805,189 @@ begin
RunXmlLfmPas('<dummy>foo<lfm>' + LineEnding);
end;
procedure TTestHighlightMulti.TestMultiHLEdit;
var
MultiHl: TSynMultiSyn;
XmlHl: TSynXMLSyn;
EmptyScheme, PasScheme: TSynHighlighterMultiScheme;
AttEl, AttSym: TSynHighlighterAttributes;
PasHl: TSynPasSyn;
procedure InitMultiXmlPasHl;
begin
MultiHl := TSynMultiSyn.Create(Form);
XmlHl := TSynXMLSyn.Create(Form);
MultiHl.DefaultHighlighter := XmlHl;
PasScheme := TSynHighlighterMultiScheme(MultiHl.Schemes.Add);
PasScheme.CaseSensitive := False;
PasScheme.StartExpr := '<pas>';
PasScheme.EndExpr := '</pas>';
PasHl := TSynPasSyn.Create(Form);
PasScheme.Highlighter := PasHl;
SynEdit.Highlighter := MultiHl;
end;
procedure FinishMultiXmlPasHl;
begin
SynEdit.Highlighter := nil;
FreeAndNil(XmlHl);
FreeAndNil(PasHl);
FreeAndNil(MultiHl);
end;
begin
{%region}
// Issue 0022519
MultiHl := TSynMultiSyn.Create(Form);
XmlHl := TSynXMLSyn.Create(Form);
MultiHl.DefaultHighlighter := XmlHl;
EmptyScheme := TSynHighlighterMultiScheme(MultiHl.Schemes.Add);
AttEl := XmlHl.ElementAttri;
AttSym := XmlHl.SymbolAttri;
SynEdit.Highlighter := MultiHl;
SynEdit.SetTextBetweenPoints(Point(1,1), Point(1,1), '<html>'+LineEnding);
DumpAll(MultiHl);
MultiHl.CurrentLines := SynEdit.TextBuffer;
CheckTokensForLine('1st line=html', MultiHl, 0, [AttSym, AttEl, AttSym]);
SynEdit.SetTextBetweenPoints(Point(1,2), Point(1,2), '<a>'+LineEnding);
DumpAll(MultiHl);
SynEdit.SetTextBetweenPoints(Point(1,1), Point(1,1), ''+LineEnding);
DumpAll(MultiHl);
SynEdit.SetTextBetweenPoints(Point(1,1), Point(1,2), '');
DumpAll(MultiHl);
SynEdit.Highlighter := nil;
FreeAndNil(XmlHl);
FreeAndNil(MultiHl);
{%endregion}
{%region}
// multiple section (same scheme) on one line
SynEdit.ClearAll;
InitMultiXmlPasHl;
SynEdit.SetTextBetweenPoints(Point(1,1), Point(1,1),
'abc'+LineEnding+
'test<pas>unit a;</pas><x><pas>uses </pas></x><pas> Foo;</pas>a'+LineEnding+
'abc'+LineEnding
);
DumpAll(MultiHl);
//Application.ProcessMessages; readln;
SynEdit.SetTextBetweenPoints(Point(1,1), Point(1,3), '');
DumpAll(MultiHl);
//Application.ProcessMessages; readln;
FinishMultiXmlPasHl;
{%endregion}
{%region}
// longer section, delete part at begin of section (no overlap / 1 line)
SetRealLinesText3;
InitMultiXmlPasHl;
SynEdit.SimulatePaintText;
DumpAll(MultiHl); //Application.ProcessMessages; readln;
SynEdit.SetTextBetweenPoints(Point(1,3), Point(1,4), '');
DumpAll(MultiHl); //Application.ProcessMessages; readln;
FinishMultiXmlPasHl;
{%endregion}
{%region}
// longer section, delete part at begin of section (no overlap / 2 line)
SetRealLinesText3;
InitMultiXmlPasHl;
SynEdit.SimulatePaintText;
DumpAll(MultiHl); //Application.ProcessMessages; readln;
SynEdit.SetTextBetweenPoints(Point(1,3), Point(1,5), '');
DumpAll(MultiHl); //Application.ProcessMessages; readln;
FinishMultiXmlPasHl;
{%endregion}
{%region}
// longer section, delete part at begin of section (overlap / 1 line of sect)
SetRealLinesText3;
InitMultiXmlPasHl;
SynEdit.SimulatePaintText;
DumpAll(MultiHl); //Application.ProcessMessages; readln;
SynEdit.SetTextBetweenPoints(Point(1,2), Point(1,4), '');
DumpAll(MultiHl); //Application.ProcessMessages; readln;
FinishMultiXmlPasHl;
{%endregion}
{%region}
// longer section, delete part at begin of section (overlap / 2 line of sect)
SetRealLinesText3;
InitMultiXmlPasHl;
SynEdit.SimulatePaintText;
DumpAll(MultiHl); //Application.ProcessMessages; readln;
SynEdit.SetTextBetweenPoints(Point(1,2), Point(1,5), '');
DumpAll(MultiHl); //Application.ProcessMessages; readln;
FinishMultiXmlPasHl;
{%endregion}
{%region}
// longer section, delete part at middle of section (1 line)
SetRealLinesText3;
InitMultiXmlPasHl;
SynEdit.SimulatePaintText;
DumpAll(MultiHl); //Application.ProcessMessages; readln;
SynEdit.SetTextBetweenPoints(Point(1,4), Point(1,5), '');
DumpAll(MultiHl); //Application.ProcessMessages; readln;
FinishMultiXmlPasHl;
{%endregion}
{%region}
// longer section, delete part at end of section (no overlap 1 line)
SetRealLinesText3;
InitMultiXmlPasHl;
SynEdit.SimulatePaintText;
DumpAll(MultiHl); //Application.ProcessMessages; readln;
SynEdit.SetTextBetweenPoints(Point(1,7), Point(1,8), '');
DumpAll(MultiHl); //Application.ProcessMessages; readln;
FinishMultiXmlPasHl;
{%endregion}
{%region}
// longer section, delete part at end of section (no overlap 2 line)
SetRealLinesText3;
InitMultiXmlPasHl;
SynEdit.SimulatePaintText;
DumpAll(MultiHl); //Application.ProcessMessages; readln;
SynEdit.SetTextBetweenPoints(Point(1,6), Point(1,8), '');
DumpAll(MultiHl); //Application.ProcessMessages; readln;
FinishMultiXmlPasHl;
{%endregion}
{%region}
// longer section, delete part at end of section (overlap 1 line of sect)
SetRealLinesText3;
InitMultiXmlPasHl;
SynEdit.SimulatePaintText;
DumpAll(MultiHl); //Application.ProcessMessages; readln;
SynEdit.SetTextBetweenPoints(Point(1,7), Point(1,9), '');
DumpAll(MultiHl); //Application.ProcessMessages; readln;
FinishMultiXmlPasHl;
{%endregion}
end;
initialization
RegisterTest(TTestHighlightMulti);