IDE: make Diff generation code more object oriented. Refactoring.

git-svn-id: trunk@33165 -
This commit is contained in:
juha 2011-10-30 09:56:32 +00:00
parent 7a64076544
commit a4a3bd1b33
3 changed files with 275 additions and 274 deletions

View File

@ -317,8 +317,8 @@ end;
procedure TDiffDlg.OnIdle(Sender: TObject; var Done: Boolean); procedure TDiffDlg.OnIdle(Sender: TObject; var Done: Boolean);
var var
Text1Src, Text2Src: string; Text1Src, Text2Src: string;
DiffTxt: String;
dat: TStrings; dat: TStrings;
DiffOutput: TDiffOutput;
begin begin
IdleConnected := false; IdleConnected := false;
if fUpdating then Exit; if fUpdating then Exit;
@ -353,24 +353,24 @@ begin
Text2Src := Text2.Editor.EditorComponent.Lines.Text; Text2Src := Text2.Editor.EditorComponent.Lines.Text;
end; end;
ProgressBar1.Max := Length(Text1Src);
ProgressBar1.Step := Length(Text1Src);
ProgressBar1.Position := 0;
Text1GroupBox.Enabled := False; Text1GroupBox.Enabled := False;
Text2GroupBox.Enabled := False; Text2GroupBox.Enabled := False;
OpenInEditorButton.Enabled := False; OpenInEditorButton.Enabled := False;
SaveDiffButton.Enabled := False; SaveDiffButton.Enabled := False;
//CancelScanningButton.Enabled := True; //CancelScanningButton.Enabled := True;
DiffTxt:=CreateTextDiff(Text1Src,Text2Src,GetDiffOptions,tdoContext,ProgressBar1); DiffOutput:=TDiffOutput.Create(Text1Src, Text2Src, GetDiffOptions, ProgressBar1);
DiffSynEdit.Lines.Text:=DiffTxt; try
DiffSynEdit.Lines.Text:=DiffOutput.CreateTextDiff;
finally
DiffOutput.Free;
end;
//CancelScanningButton.Enabled := False; //CancelScanningButton.Enabled := False;
SaveDiffButton.Enabled := True; SaveDiffButton.Enabled := True;
OpenInEditorButton.Enabled := True; OpenInEditorButton.Enabled := True;
Text2GroupBox.Enabled := True; Text2GroupBox.Enabled := True;
Text1GroupBox.Enabled := True; Text1GroupBox.Enabled := True;
ProgressBar1.Position := 0;
end; end;
fUpdating:=False; fUpdating:=False;
end; end;

View File

@ -57,29 +57,6 @@ type
tdoRCS tdoRCS
); );
function CreateTextDiff(const Text1, Text2: string; Flags: TTextDiffFlags;
OutputType: TTextDiffOutputType; ProgressBar: TProgressBar): string;
const
TextDiffFlagNames: array[TTextDiffFlag] of string = (
'IgnoreCase',
'IgnoreEmptyLineChanges',
'IgnoreHeadingSpaces',
'IgnoreLineEnds',
'IgnoreSpaceCharAmount',
'IgnoreSpaceChars',
'IgnoreTrailingSpaces'
);
implementation
const
LineBreak = #10;
ContextLineCount = 3;
type
TLineExtends = record TLineExtends = record
LineStart: integer; LineStart: integer;
LineEnd: integer; LineEnd: integer;
@ -96,33 +73,63 @@ type
destructor Destroy; override; destructor Destroy; override;
end; end;
{ TDiffOutput }
TDiffOutput = class TDiffOutput = class
private private
Part1: TDiffPart; fText1, fText2: string;
Part2: TDiffPart; fOutputType: TTextDiffOutputType;
procedure AddDefaultDiff(const Start1, End1: TLineExtends; fFlags: TTextDiffFlags;
const Start2, End2: TLineExtends); fProgressBar: TProgressBar;
procedure AddContextDiff(const Start1, End1: TLineExtends; fDiffStream: TStream;
const Start2, End2: TLineExtends); fPart1, fPart2: TDiffPart;
procedure FindNextEqualLine(const Start1, Start2: TLineExtends;
out EqualLine1, EqualLine2: TLineExtends);
function LinesAreEqual(Line1Start, Line1End, NextLine1Start: integer;
Line2Start, Line2End, NextLine2Start: integer): boolean;
function LinesAreEqual(const Line1Extends, Line2Extends: TLineExtends): boolean;
procedure GetNextLineExtends(const s: string;
var LineStart, LineEnd, NextLineStart, LineNumber: integer);
procedure GetNextLineExtends(const s: string; var LineExtends: TLineExtends);
procedure AddDefaultDiff(const Start1, End1, Start2, End2: TLineExtends);
procedure AddContextDiff(const Start1, End1, Start2, End2: TLineExtends);
procedure WriteLinesOfText(Stream: TStream; const s, Prefix: string; procedure WriteLinesOfText(Stream: TStream; const s, Prefix: string;
const StartLine: TLineExtends; EndPos: integer); const StartLine: TLineExtends; EndPos: integer);
procedure StartContextBlock(const Start1, Start2: TLineExtends); procedure StartContextBlock(const Start1, Start2: TLineExtends);
procedure FinishOldContextBlock; procedure FinishOldContextBlock;
procedure FinishDiff; procedure FinishDiff;
public procedure AddRestDiff(const Start1, Start2: TLineExtends);
OutputType: TTextDiffOutputType; procedure AddDiff(const Start1, End1, Start2, End2: TLineExtends);
DiffStream: TStream;
Text1: string;
Text2: string;
constructor Create(TheOutputType: TTextDiffOutputType);
destructor Destroy; override;
procedure AddRestDiff(const Start1: TLineExtends;
const Start2: TLineExtends);
procedure AddDiff(const Start1, End1: TLineExtends;
const Start2, End2: TLineExtends);
procedure SaveToString(var s: string); procedure SaveToString(var s: string);
procedure UpdateProgressBar(const Line: TLineExtends);
public
constructor Create(const AText1, AText2: string;
AFlags: TTextDiffFlags; AProgressBar: TProgressBar);
destructor Destroy; override;
function CreateTextDiff: string;
public
property OutputType: TTextDiffOutputType read fOutputType write fOutputType;
end; end;
const
TextDiffFlagNames: array[TTextDiffFlag] of string = (
'IgnoreCase',
'IgnoreEmptyLineChanges',
'IgnoreHeadingSpaces',
'IgnoreLineEnds',
'IgnoreSpaceCharAmount',
'IgnoreSpaceChars',
'IgnoreTrailingSpaces'
);
implementation
const
LineBreak = #10;
ContextLineCount = 3;
{ TDiffPart } { TDiffPart }
destructor TDiffPart.Destroy; destructor TDiffPart.Destroy;
@ -275,10 +282,9 @@ begin
end; end;
end; end;
function LinesAreEqual( function TDiffOutput.LinesAreEqual(
const Text1: string; Line1Start, Line1End, NextLine1Start: integer; Line1Start, Line1End, NextLine1Start: integer;
const Text2: string; Line2Start, Line2End, NextLine2Start: integer; Line2Start, Line2End, NextLine2Start: integer): boolean;
Flags: TTextDiffFlags): boolean;
var var
Start1, End1, Pos1, Start1, End1, Pos1,
Start2, End2, Pos2: integer; Start2, End2, Pos2: integer;
@ -287,28 +293,28 @@ begin
End1:=Line1End; End1:=Line1End;
Start2:=Line2Start; Start2:=Line2Start;
End2:=Line2End; End2:=Line2End;
if [tdfIgnoreHeadingSpaces,tdfIgnoreSpaceChars]*Flags<>[] then begin if [tdfIgnoreHeadingSpaces,tdfIgnoreSpaceChars]*fFlags<>[] then begin
// ignore spaces at start of line // ignore spaces at start of line
while (Start1<End1) and IsSpaceChars[Text1[Start1]] do inc(Start1); while (Start1<End1) and IsSpaceChars[fText1[Start1]] do inc(Start1);
while (Start2<End2) and IsSpaceChars[Text2[Start2]] do inc(Start2); while (Start2<End2) and IsSpaceChars[fText2[Start2]] do inc(Start2);
end; end;
if [tdfIgnoreTrailingSpaces,tdfIgnoreSpaceChars]*Flags<>[] then begin if [tdfIgnoreTrailingSpaces,tdfIgnoreSpaceChars]*fFlags<>[] then begin
// ignore spaces at end of line // ignore spaces at end of line
while (Start1<End1) and IsSpaceChars[Text1[End1-1]] do dec(End1); while (Start1<End1) and IsSpaceChars[fText1[End1-1]] do dec(End1);
while (Start2<End2) and IsSpaceChars[Text2[End2-1]] do dec(End2); while (Start2<End2) and IsSpaceChars[fText2[End2-1]] do dec(End2);
end; end;
// compare line content (i.e. the chars without the line end) // compare line content (i.e. the chars without the line end)
Pos1:=Start1; Pos1:=Start1;
Pos2:=Start2; Pos2:=Start2;
while (Pos1<End1) and (Pos2<End2) do begin while (Pos1<End1) and (Pos2<End2) do begin
if not IsSpaceChars[Text1[Pos1]] then begin if not IsSpaceChars[fText1[Pos1]] then begin
// Text1 contains a normal char // fText1 contains a normal char
if not IsSpaceChars[Text2[Pos2]] then begin if not IsSpaceChars[fText2[Pos2]] then begin
// Text2 contains a normal char // fText2 contains a normal char
if tdfIgnoreCase in Flags then begin if tdfIgnoreCase in fFlags then begin
// compare case insensitive // compare case insensitive
if UpperCaseChars[Text1[Pos1]]=UpperCaseChars[Text2[Pos2]] then begin if UpperCaseChars[fText1[Pos1]]=UpperCaseChars[fText2[Pos2]] then begin
// no diff -> proceed with next chars // no diff -> proceed with next chars
inc(Pos1); inc(Pos1);
inc(Pos2); inc(Pos2);
@ -319,7 +325,7 @@ begin
end; end;
end else begin end else begin
// compare case sensitive // compare case sensitive
if Text1[Pos1]=Text2[Pos2] then begin if fText1[Pos1]=fText2[Pos2] then begin
// no diff -> proceed with next chars // no diff -> proceed with next chars
inc(Pos1); inc(Pos1);
inc(Pos2); inc(Pos2);
@ -330,45 +336,45 @@ begin
end; end;
end; end;
end else begin end else begin
// Text2 contains a space // fText2 contains a space
if not (tdfIgnoreSpaceChars in Flags) then begin if not (tdfIgnoreSpaceChars in fFlags) then begin
// diff found -> lines differ // diff found -> lines differ
Result:=false; Result:=false;
exit; exit;
end else begin end else begin
// skip all spaces in Text2 and proceed the search // skip all spaces in fText2 and proceed the search
repeat repeat
inc(Pos2); inc(Pos2);
until (Pos2>=End2) or (not IsSpaceChars[Text2[Pos2]]); until (Pos2>=End2) or (not IsSpaceChars[fText2[Pos2]]);
end; end;
end; end;
end else begin end else begin
// Text1 contains a space // fText1 contains a space
if not IsSpaceChars[Text2[Pos2]] then begin if not IsSpaceChars[fText2[Pos2]] then begin
// Text2 contains a normal char // fText2 contains a normal char
if not (tdfIgnoreSpaceChars in Flags) then begin if not (tdfIgnoreSpaceChars in fFlags) then begin
// diff found -> lines differ // diff found -> lines differ
Result:=false; Result:=false;
exit; exit;
end else begin end else begin
// skip all spaces in Text1 and proceed the search // skip all spaces in fText1 and proceed the search
repeat repeat
inc(Pos1); inc(Pos1);
until (Pos1>=End1) or (not IsSpaceChars[Text1[Pos1]]); until (Pos1>=End1) or (not IsSpaceChars[fText1[Pos1]]);
end; end;
end else begin end else begin
// Text2 contains a space // fText2 contains a space
if [tdfIgnoreSpaceChars,tdfIgnoreSpaceCharAmount]*Flags<>[] then begin if [tdfIgnoreSpaceChars,tdfIgnoreSpaceCharAmount]*fFlags<>[] then begin
// skip all spaces in Text1 and Text2 and proceed the search // skip all spaces in fText1 and fText2 and proceed the search
repeat repeat
inc(Pos1); inc(Pos1);
until (Pos1>=End1) or (not IsSpaceChars[Text1[Pos1]]); until (Pos1>=End1) or (not IsSpaceChars[fText1[Pos1]]);
repeat repeat
inc(Pos2); inc(Pos2);
until (Pos2>=End2) or (not IsSpaceChars[Text2[Pos2]]); until (Pos2>=End2) or (not IsSpaceChars[fText2[Pos2]]);
end else begin end else begin
// compare the space chars // compare the space chars
if Text1[Pos1]=Text2[Pos2] then begin if fText1[Pos1]=fText2[Pos2] then begin
// no diff -> proceed with next chars // no diff -> proceed with next chars
inc(Pos1); inc(Pos1);
inc(Pos2); inc(Pos2);
@ -387,11 +393,11 @@ begin
exit; exit;
end; end;
// compare line ends // compare line ends
if not (tdfIgnoreLineEnds in Flags) then begin if not (tdfIgnoreLineEnds in fFlags) then begin
Pos1:=Line1End; Pos1:=Line1End;
Pos2:=Line2End; Pos2:=Line2End;
while (Pos1<NextLine1Start) and (Pos2<NextLine2Start) while (Pos1<NextLine1Start) and (Pos2<NextLine2Start)
and (Text1[Pos1]=Text2[Pos2]) do begin and (fText1[Pos1]=fText2[Pos2]) do begin
inc(Pos1); inc(Pos1);
inc(Pos2); inc(Pos2);
end; end;
@ -401,41 +407,36 @@ begin
end; end;
end; end;
function LinesAreEqual( function TDiffOutput.LinesAreEqual(
const Text1: string; const Line1Extends: TLineExtends; const Line1Extends, Line2Extends: TLineExtends): boolean;
const Text2: string; const Line2Extends: TLineExtends;
Flags: TTextDiffFlags): boolean;
begin begin
Result:=LinesAreEqual(Text1,Line1Extends.LineStart,Line1Extends.LineEnd, Result:=LinesAreEqual(Line1Extends.LineStart, Line1Extends.LineEnd,
Line1Extends.NextLineStart, Line1Extends.NextLineStart,
Text2,Line2Extends.LineStart,Line2Extends.LineEnd, Line2Extends.LineStart, Line2Extends.LineEnd,
Line2Extends.NextLineStart, Line2Extends.NextLineStart);
Flags);
end; end;
procedure GetNextLineExtends(const s: string; procedure TDiffOutput.GetNextLineExtends(const s: string;
var LineStart, LineEnd, NextLineStart, LineNumber: integer; var LineStart, LineEnd, NextLineStart, LineNumber: integer);
Flags: TTextDiffFlags); var
var Len: integer; Len: integer;
begin begin
Len:=length(s); Len:=length(s);
repeat repeat
GetLineExtends(s,LineStart,LineEnd,NextLineStart); GetLineExtends(s,LineStart,LineEnd,NextLineStart);
if (LineStart>Len) if (LineStart>Len)
or (not (tdfIgnoreEmptyLineChanges in Flags)) or (not (tdfIgnoreEmptyLineChanges in fFlags))
or (not IsEmptyLine(s,LineStart,LineEnd,Flags)) then or (not IsEmptyLine(s,LineStart,LineEnd,fFlags)) then
break; break;
LineStart:=NextLineStart; LineStart:=NextLineStart;
inc(LineNumber); inc(LineNumber);
until false; until false;
end; end;
procedure GetNextLineExtends(const s: string; procedure TDiffOutput.GetNextLineExtends(const s: string; var LineExtends: TLineExtends);
var LineExtends: TLineExtends; Flags: TTextDiffFlags);
begin begin
GetNextLineExtends(s,LineExtends.LineStart,LineExtends.LineEnd, GetNextLineExtends(s,LineExtends.LineStart,LineExtends.LineEnd,
LineExtends.NextLineStart,LineExtends.LineNumber, LineExtends.NextLineStart,LineExtends.LineNumber);
Flags);
end; end;
procedure WriteStrToStream(Stream: TStream; const s: string); procedure WriteStrToStream(Stream: TStream; const s: string);
@ -451,11 +452,8 @@ begin
Stream.Write(Source[Line.LineStart],Line.NextLineStart-Line.LineStart); Stream.Write(Source[Line.LineStart],Line.NextLineStart-Line.LineStart);
end; end;
procedure FindNextEqualLine( procedure TDiffOutput.FindNextEqualLine(const Start1, Start2: TLineExtends;
const Text1: string; const Start1: TLineExtends; out EqualLine1, EqualLine2: TLineExtends);
const Text2: string; const Start2: TLineExtends;
Flags: TTextDiffFlags;
out EqualLine1, EqualLine2: TLineExtends; ProgressBar: TProgressBar);
var var
Max1, Max2, Cur1, Cur2: TLineExtends; Max1, Max2, Cur1, Cur2: TLineExtends;
begin begin
@ -464,47 +462,44 @@ begin
Cur1:=Start1; Cur1:=Start1;
Cur2:=Start2; Cur2:=Start2;
try try
if LinesAreEqual(Text1,Cur1,Text2,Cur2,Flags) if LinesAreEqual(Cur1,Cur2)
and (not IsEmptyLine(Text1,Cur1.LineStart,Cur1.LineEnd,Flags)) then and (not IsEmptyLine(fText1,Cur1.LineStart,Cur1.LineEnd,fFlags)) then
exit; exit;
repeat repeat
// increase Max1 // increase Max1
if GotoNextLine(Max1) then begin if GotoNextLine(Max1) then begin
GetLineExtends(Text1,Max1); GetLineExtends(fText1,Max1);
// search Max1 Line in Text2 // search Max1 Line in fText2
if Max1.LineStart<Max1.NextLineStart then begin if Max1.LineStart<Max1.NextLineStart then begin
Cur1:=Max1; Cur1:=Max1;
Cur2:=Start2; Cur2:=Start2;
repeat repeat
if LinesAreEqual(Text1,Cur1,Text2,Cur2,Flags) if LinesAreEqual(Cur1,Cur2)
and (not IsEmptyLine(Text1,Cur1.LineStart,Cur1.LineEnd,Flags)) then and (not IsEmptyLine(fText1,Cur1.LineStart,Cur1.LineEnd,fFlags)) then
exit; exit;
if Cur2.LineStart>=Max2.LineStart then break; if Cur2.LineStart>=Max2.LineStart then break;
Cur2.LineStart:=Cur2.NextLineStart; Cur2.LineStart:=Cur2.NextLineStart;
inc(Cur2.LineNumber); inc(Cur2.LineNumber);
GetLineExtends(Text2,Cur2); GetLineExtends(fText2,Cur2);
until false; until false;
end; end;
if Assigned(ProgressBar) then begin UpdateProgressBar(Max1);
ProgressBar.Position := Max1.LineStart;
Application.ProcessMessages;
end;
end; end;
// increase Max2 // increase Max2
if GotoNextLine(Max2) then begin if GotoNextLine(Max2) then begin
GetLineExtends(Text2,Max2); GetLineExtends(fText2,Max2);
// search Max2 Line in Text1 // search Max2 Line in fText1
if Max2.LineStart<Max2.NextLineStart then begin if Max2.LineStart<Max2.NextLineStart then begin
Cur1:=Start1; Cur1:=Start1;
Cur2:=Max2; Cur2:=Max2;
repeat repeat
if LinesAreEqual(Text1,Cur1,Text2,Cur2,Flags) if LinesAreEqual(Cur1,Cur2)
and (not IsEmptyLine(Text1,Cur1.LineStart,Cur1.LineEnd,Flags)) then and (not IsEmptyLine(fText1,Cur1.LineStart,Cur1.LineEnd,fFlags)) then
exit; exit;
if Cur1.LineStart>=Max1.LineStart then break; if Cur1.LineStart>=Max1.LineStart then break;
Cur1.LineStart:=Cur1.NextLineStart; Cur1.LineStart:=Cur1.NextLineStart;
inc(Cur1.LineNumber); inc(Cur1.LineNumber);
GetLineExtends(Text1,Cur1); GetLineExtends(fText1,Cur1);
until false; until false;
end; end;
end; end;
@ -518,27 +513,24 @@ begin
EqualLine1:=Cur1; EqualLine1:=Cur1;
EqualLine2:=Cur2; EqualLine2:=Cur2;
// chomp empty lines at end // chomp empty lines at end
GetPrevLineExtends(Text1,Cur1); GetPrevLineExtends(fText1,Cur1);
GetPrevLineExtends(Text2,Cur2); GetPrevLineExtends(fText2,Cur2);
until not LinesAreEqual(Text1,Cur1,Text2,Cur2,Flags); until not LinesAreEqual(Cur1,Cur2);
end; end;
end; end;
function CreateTextDiff(const Text1, Text2: string; Flags: TTextDiffFlags; { TDiffOutput }
OutputType: TTextDiffOutputType; ProgressBar: TProgressBar): string;
function TDiffOutput.CreateTextDiff: string;
var var
Line1, Line2, EqualLine1, EqualLine2: TLineExtends; Line1, Line2, EqualLine1, EqualLine2: TLineExtends;
Len1, Len2: integer; Len1, Len2: integer;
DiffOutput: TDiffOutput;
begin begin
Result := ''; Result := '';
DiffOutput:=TDiffOutput.Create(OutputType);
DiffOutput.Text1:=Text1;
DiffOutput.Text2:=Text2;
try try
try try
Len1:=length(Text1); Len1:=length(fText1);
Len2:=length(Text2); Len2:=length(fText2);
Line1.LineStart:=1; Line1.LineStart:=1;
Line1.LineNumber:=1; Line1.LineNumber:=1;
Line2.LineStart:=1; Line2.LineStart:=1;
@ -546,13 +538,13 @@ begin
repeat repeat
// search for a difference line ... // search for a difference line ...
repeat repeat
// skip empty lines in Text1 and get line1 extends ... // skip empty lines in fText1 and get line1 extends ...
GetNextLineExtends(Text1,Line1,Flags); GetNextLineExtends(fText1,Line1);
// skip empty lines in Text2 and get line2 extends ... // skip empty lines in fText2 and get line2 extends ...
GetNextLineExtends(Text2,Line2,Flags); GetNextLineExtends(fText2,Line2);
// skip equal lines ... // skip equal lines ...
if (Line1.LineStart<=Len1) and (Line2.LineStart<=Len2) then begin if (Line1.LineStart<=Len1) and (Line2.LineStart<=Len2) then begin
if not LinesAreEqual(Text1,Line1,Text2,Line2,Flags) then if not LinesAreEqual(Line1,Line2) then
break; break;
Line1.LineStart:=Line1.NextLineStart; Line1.LineStart:=Line1.NextLineStart;
inc(Line1.LineNumber); inc(Line1.LineNumber);
@ -561,29 +553,23 @@ begin
end else begin end else begin
if (Line1.LineStart<=Len1) or (Line2.LineStart<=Len2) then begin if (Line1.LineStart<=Len1) or (Line2.LineStart<=Len2) then begin
// one text is longer than the other // one text is longer than the other
DiffOutput.AddRestDiff(Line1,Line2); AddRestDiff(Line1, Line2);
end else begin end else begin
// no more diff found // no more diff found
end; end;
exit; exit;
end; end;
if Assigned(ProgressBar) then begin UpdateProgressBar(Line1);
ProgressBar.Position := Line1.LineStart;
Application.ProcessMessages;
end;
until false; until false;
// difference line found -> search next equal line // difference line found -> search next equal line
FindNextEqualLine(Text1,Line1, Text2,Line2, Flags, EqualLine1,EqualLine2, ProgressBar); FindNextEqualLine(Line1, Line2, EqualLine1, EqualLine2);
DiffOutput.AddDiff(Line1,EqualLine1, Line2,EqualLine2); AddDiff(Line1, EqualLine1, Line2, EqualLine2);
// continue the search ... // continue the search ...
Line1:=EqualLine1; Line1:=EqualLine1;
GotoNextLine(Line1); GotoNextLine(Line1);
Line2:=EqualLine2; Line2:=EqualLine2;
GotoNextLine(Line2); GotoNextLine(Line2);
if Assigned(ProgressBar) then begin UpdateProgressBar(Line1);
ProgressBar.Position := Line1.LineStart;
Application.ProcessMessages;
end;
until false; until false;
except except
on E: Exception do begin on E: Exception do begin
@ -591,39 +577,30 @@ begin
end; end;
end; end;
finally finally
DiffOutput.SaveToString(Result); SaveToString(Result);
DiffOutput.Free;
end; end;
end; end;
{ TDiffOutput } procedure TDiffOutput.AddRestDiff(const Start1, Start2: TLineExtends);
procedure TDiffOutput.AddRestDiff(
const Start1: TLineExtends;
const Start2: TLineExtends);
var var
End1, End2: TLineExtends; End1, End2: TLineExtends;
begin begin
End1.LineStart:=length(Text1)+1; End1.LineStart:=length(fText1)+1;
End1.LineEnd:=End1.LineStart; End1.LineEnd:=End1.LineStart;
End1.NextLineStart:=End1.LineStart; End1.NextLineStart:=End1.LineStart;
End1.LineNumber:=Start1.LineNumber+CountLinesTillEnd(Text1,Start1.LineStart); End1.LineNumber:=Start1.LineNumber+CountLinesTillEnd(fText1,Start1.LineStart);
End2.LineStart:=length(Text2)+1; End2.LineStart:=length(fText2)+1;
End2.LineEnd:=End2.LineStart; End2.LineEnd:=End2.LineStart;
End2.NextLineStart:=End2.LineStart; End2.NextLineStart:=End2.LineStart;
End2.LineNumber:=Start2.LineNumber+CountLinesTillEnd(Text2,Start2.LineStart); End2.LineNumber:=Start2.LineNumber+CountLinesTillEnd(fText2,Start2.LineStart);
AddDiff(Start1,End1, Start2,End2); AddDiff(Start1,End1, Start2,End2);
end; end;
procedure TDiffOutput.AddDiff( procedure TDiffOutput.AddDiff(const Start1, End1, Start2, End2: TLineExtends);
const Start1, End1: TLineExtends;
const Start2, End2: TLineExtends);
begin begin
if (Start1.LineStart>length(Text1)) if (Start1.LineStart>length(fText1)) and (Start2.LineStart>length(fText2)) then
and (Start2.LineStart>length(Text2)) then exit; // no diff
// no diff case fOutputType of
exit;
case OutputType of
tdoContext: tdoContext:
AddContextDiff(Start1,End1,Start2,End2); AddContextDiff(Start1,End1,Start2,End2);
else else
@ -634,17 +611,24 @@ end;
procedure TDiffOutput.SaveToString(var s: string); procedure TDiffOutput.SaveToString(var s: string);
begin begin
FinishDiff; FinishDiff;
SetLength(s,DiffStream.Size); SetLength(s,fDiffStream.Size);
DiffStream.Position:=0; fDiffStream.Position:=0;
if s<>'' then if s<>'' then
DiffStream.Read(s[1],length(s)); fDiffStream.Read(s[1],length(s));
end;
procedure TDiffOutput.UpdateProgressBar(const Line: TLineExtends);
begin
if Assigned(fProgressBar) then begin
fProgressBar.Position := Line.LineStart;
Application.ProcessMessages;
end;
end; end;
procedure TDiffOutput.WriteLinesOfText(Stream: TStream; procedure TDiffOutput.WriteLinesOfText(Stream: TStream;
const s, Prefix: string; const s, Prefix: string;
const StartLine: TLineExtends; EndPos: integer); const StartLine: TLineExtends; EndPos: integer);
{ Write all lines in front of EndLine, starting with StartLine { Write all lines in front of EndLine, starting with StartLine }
}
var var
Line: TLineExtends; Line: TLineExtends;
begin begin
@ -683,9 +667,9 @@ procedure TDiffOutput.StartContextBlock(const Start1, Start2: TLineExtends);
begin begin
FinishOldContextBlock; FinishOldContextBlock;
WriteStrToStream(DiffStream,'***************'+LineBreak); WriteStrToStream(fDiffStream,'***************'+LineBreak);
InitPart(Part1,Text1,Start1); InitPart(fPart1,fText1,Start1);
InitPart(Part2,Text2,Start2); InitPart(fPart2,fText2,Start2);
end; end;
procedure TDiffOutput.FinishOldContextBlock; procedure TDiffOutput.FinishOldContextBlock;
@ -710,31 +694,29 @@ procedure TDiffOutput.FinishOldContextBlock;
Part.Stream.Position:=0; Part.Stream.Position:=0;
// write part // write part
WriteStrToStream(DiffStream, WriteStrToStream(fDiffStream,
HeaderPrefix HeaderPrefix
+IntToStr(Part.StartLine)+','+IntToStr(Part.EndLine) +IntToStr(Part.StartLine)+','+IntToStr(Part.EndLine)
+HeaderSuffix+LineBreak); +HeaderSuffix+LineBreak);
if Part.Stream.Size<>0 then if Part.Stream.Size<>0 then
DiffStream.CopyFrom(Part.Stream,Part.Stream.Size); fDiffStream.CopyFrom(Part.Stream,Part.Stream.Size);
end; end;
begin begin
if Part1.Stream<>nil then begin if fPart1.Stream<>nil then begin
WritePart(Part1,Text1,'*** ',' ****'); WritePart(fPart1,fText1,'*** ',' ****');
WritePart(Part2,Text2,'--- ',' ----'); WritePart(fPart2,fText2,'--- ',' ----');
end; end;
end; end;
procedure TDiffOutput.FinishDiff; procedure TDiffOutput.FinishDiff;
begin begin
case OutputType of case fOutputType of
tdoContext: FinishOldContextBlock; tdoContext: FinishOldContextBlock;
end; end;
end; end;
procedure TDiffOutput.AddDefaultDiff( procedure TDiffOutput.AddDefaultDiff(const Start1, End1, Start2, End2: TLineExtends);
const Start1, End1: TLineExtends;
const Start2, End2: TLineExtends);
{ Start1/2 is the first line that is different { Start1/2 is the first line that is different
End1/2 is the first line that is equal End1/2 is the first line that is equal
@ -770,10 +752,10 @@ var
procedure WriteActionLine; procedure WriteActionLine;
begin begin
// write line numbers of text 1 // write line numbers of text 1
WriteStrToStream(DiffStream,IntToStr(DiffStartLine1)); WriteStrToStream(fDiffStream,IntToStr(DiffStartLine1));
if DiffEndLine1>DiffStartLine1 then begin if DiffEndLine1>DiffStartLine1 then begin
WriteStrToStream(DiffStream,','); WriteStrToStream(fDiffStream,',');
WriteStrToStream(DiffStream,IntToStr(DiffEndLine1)); WriteStrToStream(fDiffStream,IntToStr(DiffEndLine1));
end; end;
// write action character 'a', 'd' or 'c' // write action character 'a', 'd' or 'c'
if (Start1.LineStart<End1.LineStart) then begin if (Start1.LineStart<End1.LineStart) then begin
@ -786,15 +768,15 @@ var
// insertion // insertion
ActionChar:='a'; ActionChar:='a';
end; end;
DiffStream.Write(ActionChar,1); fDiffStream.Write(ActionChar,1);
// write line numbers of text 2 // write line numbers of text 2
WriteStrToStream(DiffStream,IntToStr(DiffStartLine2)); WriteStrToStream(fDiffStream,IntToStr(DiffStartLine2));
if DiffEndLine2>DiffStartLine2 then begin if DiffEndLine2>DiffStartLine2 then begin
WriteStrToStream(DiffStream,','); WriteStrToStream(fDiffStream,',');
WriteStrToStream(DiffStream,IntToStr(DiffEndLine2)); WriteStrToStream(fDiffStream,IntToStr(DiffEndLine2));
end; end;
// write <newline> // write <newline>
WriteStrToStream(DiffStream,LineBreak); WriteStrToStream(fDiffStream,LineBreak);
end; end;
begin begin
@ -807,17 +789,15 @@ begin
if DiffStartLine2>DiffEndLine2 then if DiffStartLine2>DiffEndLine2 then
DiffStartLine2:=DiffEndLine2; DiffStartLine2:=DiffEndLine2;
WriteActionLine; WriteActionLine;
WriteLinesOfText(DiffStream,Text1,'< ',Start1,End1.LineStart); WriteLinesOfText(fDiffStream,fText1,'< ',Start1,End1.LineStart);
if ActionChar='c' then begin if ActionChar='c' then begin
WriteStrToStream(DiffStream,'---'); WriteStrToStream(fDiffStream,'---');
WriteStrToStream(DiffStream,LineBreak); WriteStrToStream(fDiffStream,LineBreak);
end; end;
WriteLinesOfText(DiffStream,Text2,'> ',Start2,End2.LineStart); WriteLinesOfText(fDiffStream,fText2,'> ',Start2,End2.LineStart);
end; end;
procedure TDiffOutput.AddContextDiff( procedure TDiffOutput.AddContextDiff(const Start1, End1, Start2, End2: TLineExtends);
const Start1, End1: TLineExtends;
const Start2, End2: TLineExtends);
{ Start1/2 is the first line that is different { Start1/2 is the first line that is different
End1/2 is the first line that is equal End1/2 is the first line that is equal
@ -868,35 +848,50 @@ var
Part1HasChangedLines: boolean; Part1HasChangedLines: boolean;
Part2HasChangedLines: boolean; Part2HasChangedLines: boolean;
begin begin
if (Part1.Stream<>nil) if (fPart1.Stream<>nil)
and (Start1.LineNumber-ContextLineCount<=Part1.EndLine-1) and (Start1.LineNumber-ContextLineCount<=fPart1.EndLine-1)
and (Start2.LineNumber-ContextLineCount<=Part2.EndLine-1) then begin and (Start2.LineNumber-ContextLineCount<=fPart2.EndLine-1) then begin
// append the new difference // append the new difference
end else begin end else begin
// start a new block // start a new block
StartContextBlock(Start1,Start2); StartContextBlock(Start1,Start2);
end; end;
Part1.EndLine:=End1.LineNumber+ContextLineCount-1; fPart1.EndLine:=End1.LineNumber+ContextLineCount-1;
Part2.EndLine:=End2.LineNumber+ContextLineCount-1; fPart2.EndLine:=End2.LineNumber+ContextLineCount-1;
Part1HasChangedLines:=End1.LineStart>Start1.LineStart; Part1HasChangedLines:=End1.LineStart>Start1.LineStart;
Part2HasChangedLines:=End2.LineStart>Start2.LineStart; Part2HasChangedLines:=End2.LineStart>Start2.LineStart;
WritePart(Part1,Text1,Start1,End1,Part2HasChangedLines,'-'); WritePart(fPart1,fText1,Start1,End1,Part2HasChangedLines,'-');
WritePart(Part2,Text2,Start2,End2,Part1HasChangedLines,'+'); WritePart(fPart2,fText2,Start2,End2,Part1HasChangedLines,'+');
end; end;
constructor TDiffOutput.Create(TheOutputType: TTextDiffOutputType); constructor TDiffOutput.Create(const AText1, AText2: string;
AFlags: TTextDiffFlags; AProgressBar: TProgressBar);
var
i: Integer;
begin begin
OutputType:=TheOutputType; fText1:=AText1;
DiffStream:=TMemoryStream.Create; fText2:=AText2;
Part1:=TDiffPart.Create; fFlags:=AFlags;
Part2:=TDiffPart.Create; fProgressBar:=AProgressBar;
if Assigned(fProgressBar) then begin
i := Length(AText1); // + Length(AText2);
fProgressBar.Max := i;
fProgressBar.Step := i;
fProgressBar.Position := 0;
end;
fOutputType:=tdoContext; // Default OutputType, can be changed later
fDiffStream:=TMemoryStream.Create;
fPart1:=TDiffPart.Create;
fPart2:=TDiffPart.Create;
end; end;
destructor TDiffOutput.Destroy; destructor TDiffOutput.Destroy;
begin begin
DiffStream.Free; if Assigned(fProgressBar) then
Part1.Free; fProgressBar.Position := 0;
Part2.Free; fDiffStream.Free;
fPart1.Free;
fPart2.Free;
inherited Destroy; inherited Destroy;
end; end;

View File

@ -277,6 +277,7 @@ var
AnUnitInfo: TUnitInfo; AnUnitInfo: TUnitInfo;
APackage: TLazPackage; APackage: TLazPackage;
Source: String; Source: String;
DiffOutput: TDiffOutput;
begin begin
if FCachedDiffs=nil then if FCachedDiffs=nil then
FCachedDiffs:=TFPList.Create; FCachedDiffs:=TFPList.Create;
@ -309,8 +310,13 @@ begin
if Result^.TxtOnDisk<>'' then if Result^.TxtOnDisk<>'' then
fs.Read(Result^.TxtOnDisk[1],length(Result^.TxtOnDisk)); fs.Read(Result^.TxtOnDisk[1],length(Result^.TxtOnDisk));
fs.Free; fs.Free;
Result^.Diff:=CreateTextDiff(Source,Result^.TxtOnDisk,[],
tdoContext, nil); DiffOutput:=TDiffOutput.Create(Source,Result^.TxtOnDisk, [], nil);
try
Result^.Diff:=DiffOutput.CreateTextDiff;
finally
DiffOutput.Free;
end;
except except
On E: Exception do On E: Exception do
Result^.Diff:='\ '+Format(lisDiskDiffErrorReadingFile, [E.Message]); Result^.Diff:='\ '+Format(lisDiskDiffErrorReadingFile, [E.Message]);