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

View File

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

View File

@ -17,14 +17,18 @@ type
protected protected
procedure SetRealLinesText; procedure SetRealLinesText;
procedure SetRealLinesText2; procedure SetRealLinesText2;
procedure SetRealLinesText3;
procedure DumpLines(ALines: TSynHLightMultiVirtualLines); procedure DumpLines(ALines: TSynHLightMultiVirtualLines);
procedure DumpSections(ASectList: TSynHLightMultiSectionList); procedure DumpSections(ASectList: TSynHLightMultiSectionList);
procedure DumpRanges(ARangeList: TSynHighlighterRangeList); procedure DumpRanges(ARangeList: TSynHighlighterRangeList);
procedure DumpAll(Hl: TSynMultiSyn); procedure DumpAll(Hl: TSynMultiSyn);
procedure CheckTokensForLine(Name: String; HL: TSynCustomHighlighter;
LineIdx: Integer; ExpAttr: Array of TSynHighlighterAttributes);
published published
procedure TestSectionList; procedure TestSectionList;
procedure TestVirtualLines; procedure TestVirtualLines;
procedure TestMultiHL; procedure TestMultiHL;
procedure TestMultiHLEdit;
end; end;
implementation implementation
@ -62,6 +66,25 @@ begin
]); ]);
end; 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); procedure TTestHighlightMulti.DumpLines(ALines: TSynHLightMultiVirtualLines);
var var
i: Integer; i: Integer;
@ -92,16 +115,39 @@ procedure TTestHighlightMulti.DumpAll(Hl: TSynMultiSyn);
var var
i: Integer; i: Integer;
begin // ensure CurrentLines are set begin // ensure CurrentLines are set
debugln(['--- Default']); DebugLnEnter(['>> --- Default / Lines']); DebugLnEnter;
DumpLines(hl.DefaultVirtualLines); DumpLines(hl.DefaultVirtualLines);
DumpSections(hl.DefaultVirtualLines.SectionList); DebugLnExit; DebugLnEnter(['-- Sections']);
debugln(['-']); DumpSections(hl.DefaultVirtualLines.SectionList);
DumpRanges(TSynHighlighterRangeList(hl.CurrentLines.Ranges[hl])); DebugLnExit; DebugLnEnter(['-- Ranges']);
for i := 0 to hl.Schemes.Count - 1 do begin DumpRanges(TSynHighlighterRangeList(hl.CurrentLines.Ranges[hl]));
debugln(['-- ',i,' ', dbgs(hl.Schemes[i].Highlighter)]); for i := 0 to hl.Schemes.Count - 1 do begin
DumpLines(hl.Schemes[i].VirtualLines); DebugLnExit; DebugLnEnter(['-- Scheme=',i,' ', dbgs(hl.Schemes[i].Highlighter)]);
DumpSections(hl.Schemes[i].VirtualLines.SectionList); 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; end;
AssertEquals(Name+ 'TokenId Line='+IntToStr(LineIdx)+' amount of tokens', length(ExpAttr), c );
end; end;
procedure TTestHighlightMulti.TestSectionList; procedure TTestHighlightMulti.TestSectionList;
@ -759,6 +805,189 @@ begin
RunXmlLfmPas('<dummy>foo<lfm>' + LineEnding); RunXmlLfmPas('<dummy>foo<lfm>' + LineEnding);
end; 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 initialization
RegisterTest(TTestHighlightMulti); RegisterTest(TTestHighlightMulti);