mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-02 07:43:39 +02:00
MG: using strings instead of TStrings
git-svn-id: trunk@1814 -
This commit is contained in:
parent
a78ba5c3c4
commit
9524a6acd9
@ -39,108 +39,260 @@ uses
|
||||
|
||||
type
|
||||
TTextDiffFlag = (
|
||||
tdfIgnoreSpaceCharAmount,
|
||||
tdfIgnoreSpaceChars,
|
||||
tdfIgnoreEmptyLineChanges,
|
||||
tdfIgnoreCase
|
||||
tdfIgnoreSpaceCharAmount, // ignore if space chars were added or removed
|
||||
// except if all spaces were removed
|
||||
tdfIgnoreSpaceChars, // ignore spaces (newline chars not included)
|
||||
tdfIgnoreHeadingSpaces, // ignore spaces at start of line
|
||||
tdfIgnoreTrailingSpaces, // ignore spaces at end of line
|
||||
tdfIgnoreEmptyLineChanges,// ignore if empty lines were added or removed
|
||||
tdfIgnoreLineEnds, // ignore if line chars differ (e.g. #10 = #13#10)
|
||||
tdfIgnoreCase // ignore case of letters
|
||||
);
|
||||
TTextDiffFlags = set of TTextDiffFlag;
|
||||
|
||||
procedure CreateTextDiff(Text1, Text2, DiffText: TStrings;
|
||||
Flags: TTextDiffFlags);
|
||||
function CreateTextDiff(const Text1, Text2: string; Flags: TTextDiffFlags
|
||||
): string;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
var
|
||||
IsSpaceChars: array[char] of boolean;
|
||||
UpperCaseChars: array[char] of char;
|
||||
|
||||
const
|
||||
IsSpaceChars, UpperCaseChars: array[char] of boolean;
|
||||
|
||||
|
||||
function IsEmptyLine(const s: string; Flags: TTextDiffFlags): boolean;
|
||||
var i: integer;
|
||||
procedure GetLineExtends(const s: string; LineStart: integer;
|
||||
var LineEnd, NextLineStart: integer);
|
||||
var Len: integer;
|
||||
begin
|
||||
if ([tdfIgnoreSpaceCharAmount,tdfIgnoreSpaceChars]*Flags)<>[] then begin
|
||||
Len:=length(s);
|
||||
LineEnd:=LineStart;
|
||||
while (LineEnd<=Len) do begin
|
||||
if (not (s[LineEnd] in [#10,#13])) then begin
|
||||
inc(LineEnd);
|
||||
end else begin
|
||||
NextLineStart:=LineEnd+1;
|
||||
if (NextLineStart<=Len) and (s[NextLineStart] in [#10,#13])
|
||||
and (s[LineStart]<>s[NextLineStart]) then
|
||||
inc(NextLineStart);
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
// this was the last line and it has no line end
|
||||
NextLineStart:=LineEnd;
|
||||
end;
|
||||
|
||||
function IsEmptyLine(const s: string; LineStart, LineEnd: integer;
|
||||
Flags: TTextDiffFlags): boolean;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
if ([tdfIgnoreSpaceCharAmount,tdfIgnoreSpaceChars,tdfIgnoreHeadingSpaces,
|
||||
tdfIgnoreTrailingSpaces]*Flags)<>[] then
|
||||
begin
|
||||
Result:=true;
|
||||
for i:=1 to length(s) do begin
|
||||
for i:=LineStart to LineEnd-1 do begin
|
||||
if not IsSpaceChars[s[i]] then begin
|
||||
Result:=false;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
end else begin
|
||||
Result:=(s='');
|
||||
Result:=(LineEnd=LineStart);
|
||||
end;
|
||||
end;
|
||||
|
||||
function LinesAreEqual(const Line1, Line2: string; Flags: TTextDiffFlags
|
||||
): boolean;
|
||||
var Pos1, Pos2, Len1, Len2: integer;
|
||||
function LinesAreEqual(
|
||||
const Text1: string; Line1Start, Line1End, NextLine1Start: integer;
|
||||
const Text2: string; Line2Start, Line2End, NextLine2Start: integer;
|
||||
Flags: TTextDiffFlags): boolean;
|
||||
var
|
||||
Start1, End1, Pos1,
|
||||
Start2, End2, Pos2: integer;
|
||||
begin
|
||||
if ([tdfIgnoreSpaceCharAmount,tdfIgnoreSpaceChars]*Flags)<>[] then begin
|
||||
// completely ignore space chars
|
||||
Result:=true;
|
||||
Len1:=length(Line1);
|
||||
Len2:=length(Line2);
|
||||
Pos1:=1;
|
||||
Pos2:=1;
|
||||
while (Pos1<=Len1) and (Pos2<=Len2) do begin
|
||||
if ((not (tdfIgnoreCase in Flags))
|
||||
and (Line1[Pos1]=Line2[Pos2]))
|
||||
or ((tdfIgnoreCase in Flags)
|
||||
and (UpperCaseChars[Line1[Pos1]]=UpperCaseChars[Line2[Pos2]]))
|
||||
then begin
|
||||
// both chars are the same
|
||||
inc(Pos1);
|
||||
inc(Pos2);
|
||||
continue;
|
||||
end else begin
|
||||
// there is a difference
|
||||
if (tdfIgnoreSpaceChars in Flags) then begin
|
||||
if IsSpaceChars[Line1[Pos1]]
|
||||
or IsSpaceChars[Line2[Pos2]]
|
||||
then begin
|
||||
// skip spaces
|
||||
while (Pos1<=Len1) and IsSpaceChars[Line1[Pos1]] do inc(Pos1);
|
||||
while (Pos2<=Len2) and IsSpaceChars[Line2[Pos2]] do inc(Pos2);
|
||||
Start1:=Line1Start;
|
||||
End1:=Line1End;
|
||||
Start2:=Line2Start;
|
||||
End2:=Line2End;
|
||||
if [tdfIgnoreHeadingSpaces,tdfIgnoreSpaceChars]*Flags<>[] then begin
|
||||
// ignore spaces at start of line
|
||||
while (Start1<End1) and IsSpaceChars[Text1[Start1]] do inc(Start1);
|
||||
while (Start2<End2) and IsSpaceChars[Text2[Start2]] do inc(Start2);
|
||||
end;
|
||||
if [tdfIgnoreTrailingSpaces,tdfIgnoreSpaceChars]*Flags<>[] then begin
|
||||
// ignore spaces at end of line
|
||||
while (Start1<End1) and IsSpaceChars[Text1[End1-1]] do dec(End1);
|
||||
while (Start2<End2) and IsSpaceChars[Text2[End2-1]] do dec(End2);
|
||||
end;
|
||||
|
||||
// compare line content (i.e. the chars without the line end)
|
||||
Pos1:=Start1;
|
||||
Pos2:=Start2;
|
||||
while (Pos1<End1) and (Pos2<End2) do begin
|
||||
if not IsSpaceChars[Text1[Pos1]] then begin
|
||||
// Text1 contains a normal char
|
||||
if not IsSpaceChars[Text1[Pos1]] then begin
|
||||
// Text2 contains a normal char
|
||||
if tdfIgnoreCase in Flags then begin
|
||||
// compare case insensitive
|
||||
if UpperCaseChars[Text1[Pos1]]=UpperCaseChars[Text2[Pos2]] then begin
|
||||
// no diff -> proceed with next chars
|
||||
inc(Pos1);
|
||||
inc(Pos2);
|
||||
end else begin
|
||||
// diff found -> lines differ
|
||||
Result:=false;
|
||||
exit;
|
||||
end;
|
||||
end else begin
|
||||
xxx
|
||||
// compare case sensitive
|
||||
if Text1[Pos1]=Text2[Pos2] then begin
|
||||
// no diff -> proceed with next chars
|
||||
inc(Pos1);
|
||||
inc(Pos2);
|
||||
end else begin
|
||||
// diff found -> lines differ
|
||||
Result:=false;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
end else begin
|
||||
// Text2 contains a space
|
||||
if not (tdfIgnoreSpaceChars in Flags) then begin
|
||||
// diff found -> lines differ
|
||||
Result:=false;
|
||||
exit;
|
||||
end else begin
|
||||
// skip all spaces in Text2 and proceed the search
|
||||
repeat
|
||||
inc(Pos2);
|
||||
until (Pos2>=End2) or (IsSpaceChars[Text2[Pos2]]);
|
||||
end;
|
||||
end;
|
||||
end else begin
|
||||
// Text1 contains a space
|
||||
if not IsSpaceChars[Text2[Pos2]] then begin
|
||||
// Text2 contains a normal char
|
||||
if not (tdfIgnoreSpaceChars in Flags) then begin
|
||||
// diff found -> lines differ
|
||||
Result:=false;
|
||||
exit;
|
||||
end else begin
|
||||
// skip all spaces in Text1 and proceed the search
|
||||
repeat
|
||||
inc(Pos1);
|
||||
until (Pos1>=End1) or (IsSpaceChars[Text1[Pos1]]);
|
||||
end;
|
||||
end else begin
|
||||
// Text2 contains a space
|
||||
if [tdfIgnoreSpaceChars,tdfIgnoreSpaceCharAmount]*Flags<>[] then begin
|
||||
// skip all spaces in Text1 and Text2 and proceed the search
|
||||
repeat
|
||||
inc(Pos1);
|
||||
until (Pos1>=End1) or (IsSpaceChars[Text1[Pos1]]);
|
||||
repeat
|
||||
inc(Pos2);
|
||||
until (Pos2>=End2) or (IsSpaceChars[Text2[Pos2]]);
|
||||
end else begin
|
||||
// compare the space chars
|
||||
if Text1[Pos1]=Text2[Pos2] then begin
|
||||
// no diff -> proceed with next chars
|
||||
inc(Pos1);
|
||||
inc(Pos2);
|
||||
end else begin
|
||||
// diff found -> lines differ
|
||||
Result:=false;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end else begin
|
||||
if (tdfIgnoreCase in Flags) then begin
|
||||
Result:=(AnsiCompareText(Line1,Line2)=0);
|
||||
end else begin
|
||||
Result:=(AnsiCompareStr(Line1,Line2)=0);
|
||||
end;
|
||||
if (Pos1<End1) or (Pos2<End2) then begin
|
||||
// one line is longer -> lines differ
|
||||
Result:=false;
|
||||
exit;
|
||||
end;
|
||||
// compare line ends
|
||||
if not (tdfIgnoreLineEnds in Flags) then begin
|
||||
Pos1:=Line1End;
|
||||
Pos2:=Line2End;
|
||||
while (Pos1<NextLine1Start) and (Pos2<NextLine2Start)
|
||||
and (Text1[Pos1]=Text2[Pos2]) do begin
|
||||
inc(Pos1);
|
||||
inc(Pos2);
|
||||
end;
|
||||
Result:=(Pos1=NextLine1Start) and (Pos2=NextLine2Start);
|
||||
end else begin
|
||||
Result:=true;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CreateTextDiff(Text1, Text2, DiffText: TStrings;
|
||||
Flags: TTextDiffFlags);
|
||||
var Cnt1, Cnt2, Line1, Line2: integer;
|
||||
function CreateTextDiff(const Text1, Text2: string; Flags: TTextDiffFlags
|
||||
): string;
|
||||
var
|
||||
LineNumber1, LineNumber2: integer;
|
||||
Len1, Line1Start, Line1End, NextLine1Start: integer;
|
||||
Len2, Line2Start, Line2End, NextLine2Start: integer;
|
||||
DiffMemStream: TMemoryStream;
|
||||
begin
|
||||
DiffText.Clear;
|
||||
Cnt1:=Text1.Count;
|
||||
Cnt2:=Text2.Count;
|
||||
Line1:=1;
|
||||
Line2:=1;
|
||||
// read empty lines
|
||||
if (tdfIgnoreEmptyLineChanges in Flags) then begin
|
||||
while (Line1<Cnt1) and (IsEmptyLine(Text1[Line1],Flags)) do
|
||||
inc(Line1);
|
||||
while (Line2<Cnt2) and (IsEmptyLine(Text2[Line2],Flags)) do
|
||||
inc(Line2);
|
||||
end;
|
||||
// read lines that are equal
|
||||
while (Line1<Cnt1) and (Line2<Cnt2)
|
||||
and LinesAreEqual(Text1[Line1],Text2[Line2],Flags) do begin
|
||||
inc(Line1);
|
||||
inc(Line2);
|
||||
DiffMemStream:=TMemoryStream.Create;
|
||||
try
|
||||
Len1:=length(Text1);
|
||||
Len2:=length(Text2);
|
||||
LineNumber1:=1;
|
||||
LineNumber2:=1;
|
||||
Line1Start:=1;
|
||||
Line2Start:=1;
|
||||
// search for a differing line ...
|
||||
repeat
|
||||
// skip empty lines in Text1 and get line1 extends ...
|
||||
while (Line1Start<=Len1) do begin
|
||||
GetLineExtends(Text1,Line1Start,Line1End,NextLine1Start);
|
||||
if not (tdfIgnoreEmptyLineChanges in Flags)
|
||||
or not IsEmptyLine(Text1,Line1Start,Line1End,Flags) then
|
||||
break;
|
||||
Line1Start:=NextLine1Start;
|
||||
inc(LineNumber1);
|
||||
end;
|
||||
// skip empty lines in Text2 and get line2 extends ...
|
||||
while (Line2Start<=Len2) do begin
|
||||
GetLineExtends(Text2,Line2Start,Line2End,NextLine2Start);
|
||||
if not (tdfIgnoreEmptyLineChanges in Flags)
|
||||
or not IsEmptyLine(Text2,Line2Start,Line2End,Flags) then
|
||||
break;
|
||||
Line2Start:=NextLine2Start;
|
||||
inc(LineNumber2);
|
||||
end;
|
||||
// skip equal lines ...
|
||||
if (Line1Start<=Len1) and (Line2Start<=Len2) then begin
|
||||
if not LinesAreEqual(Text1,Line1Start,Line1End,NextLine1Start,
|
||||
Text2,Line2Start,Line2End,NextLine2Start,
|
||||
Flags)
|
||||
then
|
||||
break;
|
||||
Line1Start:=NextLine1Start;
|
||||
inc(LineNumber1);
|
||||
Line2Start:=NextLine2Start;
|
||||
inc(LineNumber2);
|
||||
end else begin
|
||||
// one text is longer than the other
|
||||
|
||||
// ToDo:
|
||||
|
||||
//AddDiff(Text1,Text2,Line1Start,Len1,Line2Start,Len2);
|
||||
exit;
|
||||
end;
|
||||
until false;
|
||||
// differing line found -> search next equal line
|
||||
|
||||
// ToDo:
|
||||
|
||||
finally
|
||||
SetLength(Result,DiffMemStream.Size);
|
||||
DiffMemStream.Position:=0;
|
||||
if Result<>'' then
|
||||
DiffMemStream.Read(Result[1],length(Result));
|
||||
DiffMemStream.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -148,7 +300,7 @@ procedure InternalInit;
|
||||
var c: char;
|
||||
begin
|
||||
for c:=Low(char) to High(char) do begin
|
||||
IsSpaceChars[c]:=c in [' ',#9,#10,#13];
|
||||
IsSpaceChars[c]:=c in [' ',#9];
|
||||
UpperCaseChars[c]:=upcase(c);
|
||||
end;
|
||||
end;
|
||||
|
@ -34,7 +34,7 @@ interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, IDEProcs, Forms, Controls, Buttons, ExtCtrls, StdCtrls,
|
||||
LResources, Project, SynEdit, LCLType;
|
||||
LResources, Project, SynEdit, LCLType, DiffPatch;
|
||||
|
||||
type
|
||||
TDiskDiffsDlg = class(TForm)
|
||||
|
Loading…
Reference in New Issue
Block a user