doublecmd/src/uparitercontrols.pas

358 lines
10 KiB
ObjectPascal

{
Copyright (C) 2004 Flavio Etrusco
Copyright (C) 2011-2015 Alexander Koblov (alexx2000@mail.ru)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
unit uPariterControls;
{$mode objfpc}{$H+}
interface
uses
Controls,
SysUtils,
Classes,
SynEditHighlighter,
uSynDiffControls,
uDiffONP;
const
SynSpaceGlyph = Chr($B7); //'·'
SynTabGlyph = Chr($BB); //'»'
type
{ TSynDiffHighlighter }
TSynDiffHighlighter = class(TSynCustomHighlighter)
private
fWhitespaceAttribute: TSynHighlighterAttributes;
fAddedAttribute: TSynHighlighterAttributes;
fRemovedAttribute: TSynHighlighterAttributes;
fModifiedAttribute: TSynHighlighterAttributes;
fUnmodifiedAttribute: TSynHighlighterAttributes;
fDiff: TDiff;
fTokens: TStringList;
{}
fRun: Integer;
fTokenPos: Integer;
fTokenLen: Integer;
fTokenKind: TChangeKind;
{}
fAddedAttriPointer: TSynHighlighterAttributes;
fDefaultAttriPointer: TSynHighlighterAttributes;
function GetEditor: TSynDiffEdit;
procedure ComputeTokens(const aOldLine, aNewLine: String);
protected
function GetDefaultAttribute(Index: Integer): TSynHighlighterAttributes; override;
public
constructor Create(aOwner: TSynDiffEdit); reintroduce; overload;
constructor Create(aOwner: TComponent); overload; override;
destructor Destroy; override;
procedure ResetRange; override;
procedure SetLine(const aNewValue: String; aLineNumber: Integer); override;
procedure UpdateColors;
function GetEol: Boolean; override;
function GetToken: String; override;
procedure GetTokenEx(out TokenStart: PChar; out TokenLength: Integer); override;
function GetTokenAttribute: TSynHighlighterAttributes; override;
function GetTokenKind: Integer; override;
function GetTokenPos: Integer; override; // 0-based
procedure Next; override;
property Editor: TSynDiffEdit read GetEditor;
end;
implementation
uses
LazUTF8, Graphics, DCUnicodeUtils;
{ TSynDiffHighlighter }
procedure TSynDiffHighlighter.ComputeTokens(const aOldLine, aNewLine: String);
var
I: Integer;
LastToken: String;
LastKind: TChangeKind;
aOld, aNew: UCS4String;
FirstToken: Boolean = True;
procedure AddTokenIfNeed(Symbol: UCS4Char; Kind: TChangeKind);
begin
if (Kind = LastKind) then // Same Kind, no need to change colors
LastToken := LastToken + UnicodeToUTF8(Symbol)
else begin
fTokens.AddObject(LastToken, TObject(PtrInt(LastKind)));
LastToken := UnicodeToUTF8(Symbol);
LastKind := Kind;
end;
end;
begin
// Convert to UCS-4
aOld:= UTF8ToUCS4(aOldLine);
aNew:= UTF8ToUCS4(aNewLine);
// Compare lines
if not Assigned(Editor.OriginalFile) then // Original file
fDiff.Execute(PInteger(aNew), PInteger(aOld), Length(aNew), Length(aOld))
else if not Assigned(Editor.ModifiedFile) then // Modified file
fDiff.Execute(PInteger(aOld), PInteger(aNew), Length(aOld), Length(aNew));
// Prepare diffs to display
LastToken:= EmptyStr;
for I := 0 to fDiff.Count - 1 do
with fDiff.Compares[I] do
begin
if not Assigned(Editor.OriginalFile) then // Original file
begin
// Show changes for original file
if Kind <> ckAdd then
begin
if FirstToken then
begin
LastKind:= Kind;
FirstToken:= False;
end;
AddTokenIfNeed(int1, Kind);
end;
end
else if not Assigned(Editor.ModifiedFile) then // Modified file
begin
// Show changes for modified file
if Kind <> ckDelete then
begin
if FirstToken then
begin
LastKind:= Kind;
FirstToken:= False;
end;
AddTokenIfNeed(int2, Kind);
end;
end;
end;
// Add last token
fTokens.AddObject(LastToken, TObject(PtrInt(LastKind)));
end;
constructor TSynDiffHighlighter.Create(aOwner: TComponent);
begin
Create(aOwner as TSynDiffEdit);
end;
constructor TSynDiffHighlighter.Create(aOwner: TSynDiffEdit);
begin
inherited Create(aOwner);
fDiff := TDiff.Create(Self);
{}
fWhitespaceAttribute := TSynHighlighterAttributes.Create('Whitespace');
AddAttribute(fWhitespaceAttribute);
{}
fAddedAttribute := TSynHighlighterAttributes.Create('Added');
fAddedAttribute.Style := [fsBold];
AddAttribute(fAddedAttribute);
{}
fRemovedAttribute := TSynHighlighterAttributes.Create('Removed');
fRemovedAttribute.Style := [fsBold];
AddAttribute(fRemovedAttribute);
{}
fModifiedAttribute := TSynHighlighterAttributes.Create('Modified');
fModifiedAttribute.Style := [fsBold];
AddAttribute(fModifiedAttribute);
{}
fUnmodifiedAttribute := TSynHighlighterAttributes.Create('Unmodified');
AddAttribute(fUnmodifiedAttribute);
{}
UpdateColors;
SetAttributesOnChange(@DefHighlightChange);
fTokens := TStringList.Create;
end;
destructor TSynDiffHighlighter.Destroy;
begin
inherited Destroy;
fTokens.Free;
end;
function TSynDiffHighlighter.GetEditor: TSynDiffEdit;
begin
Result := TSynDiffEdit(inherited Owner);
end;
function TSynDiffHighlighter.GetDefaultAttribute(Index: Integer): TSynHighlighterAttributes;
begin
if Index = SYN_ATTR_WHITESPACE then
Result := fDefaultAttriPointer
else
Result := nil;
end;
function TSynDiffHighlighter.GetEol: Boolean;
begin
Result := (fTokenLen = 0);
end;
function TSynDiffHighlighter.GetToken: String;
var
cChar: Integer;
begin
Result := fTokens[fRun];
if (Editor.PaintStyle = psForeground) and (fTokenKind <> ckNone) then
for cChar := 1 to Length(Result) do
if Result[cChar] = #32 then
Result[cChar] := SynSpaceGlyph
else if Result[cChar] = #9 then
Result[cChar] := SynTabGlyph;
end;
function TSynDiffHighlighter.GetTokenAttribute: TSynHighlighterAttributes;
begin
case fTokenKind of
ckAdd:
Result := fAddedAttriPointer;
ckModify:
Result := fModifiedAttribute;
ckDelete:
Result := fAddedAttriPointer;
else
Result := fDefaultAttriPointer;
end;
end;
function TSynDiffHighlighter.GetTokenKind: integer;
begin
Result := Ord(fTokenKind);
end;
procedure TSynDiffHighlighter.GetTokenEx(out TokenStart: PChar; out
TokenLength: integer);
begin
TokenLength:= fTokenLen;
if TokenLength > 0 then
TokenStart:= PChar(fTokens[fRun])
else
TokenStart:= nil;
end;
function TSynDiffHighlighter.GetTokenPos: Integer;
begin
Result := fTokenPos;
end;
procedure TSynDiffHighlighter.Next;
begin
Inc(fRun);
if fRun = fTokens.Count then
begin
fTokenLen := 0;
Exit;
end;
Inc(fTokenPos, fTokenLen);
fTokenLen := Length(fTokens[fRun]);
fTokenKind := TChangeKind(PtrInt(fTokens.Objects[fRun]));
end;
procedure TSynDiffHighlighter.ResetRange;
begin
fDefaultAttriPointer := fWhitespaceAttribute;
end;
procedure TSynDiffHighlighter.SetLine(const aNewValue: String; aLineNumber: Integer);
var
vOtherEdit: TSynDiffEdit;
vOldLine: String;
vNewLine: String;
begin
fDiff.Clear;
fTokens.Clear;
fRun := -1;
fTokenPos := 0;
fTokenLen := 0;
if Editor.OriginalFile <> nil then
begin
fAddedAttriPointer := fAddedAttribute;
vOtherEdit := Editor.OriginalFile;
end
else
begin
fAddedAttriPointer := fRemovedAttribute;
vOtherEdit := Editor.ModifiedFile;
end;
if TChangeKind(Editor.Lines.Kind[aLineNumber]) = ckModify then
fDefaultAttriPointer := fUnmodifiedAttribute
else
fDefaultAttriPointer := fWhitespaceAttribute;
if (vOtherEdit <> nil) and (aLineNumber < vOtherEdit.Lines.Count) then
vOldLine := vOtherEdit.Lines[aLineNumber];
vNewLine := aNewValue;
if Length(vNewLine) <> 0 then
begin
if (Length(vOldLine) <> 0) and (TChangeKind(Editor.Lines.Kind[aLineNumber]) = ckModify) then
ComputeTokens(vOldLine, vNewLine)
else
fTokens.Add(vNewLine);
end;
Next;
end;
procedure TSynDiffHighlighter.UpdateColors;
begin
BeginUpdate;
try
if Editor.PaintStyle = psForeground then
begin
fAddedAttribute.Foreground := Editor.Colors.Added;
fAddedAttribute.Background := clBtnFace;
fRemovedAttribute.Foreground := Editor.Colors.Deleted;
fRemovedAttribute.Background := clBtnFace;
fModifiedAttribute.Foreground := Editor.Colors.Modified;
fModifiedAttribute.Background := clBtnFace;
fUnmodifiedAttribute.Foreground := clNone;
fUnmodifiedAttribute.Background := clNone;
end
else begin
fAddedAttribute.Foreground := clNone;
fAddedAttribute.Background := Editor.Colors.Added;
fRemovedAttribute.Foreground := clNone;
fRemovedAttribute.Background := Editor.Colors.Deleted;
fModifiedAttribute.Foreground := clNone;
fModifiedAttribute.Background := Editor.Colors.Modified;
fUnmodifiedAttribute.Foreground := clNone;
fUnmodifiedAttribute.Background := Editor.Colors.Modified;
end;
finally
EndUpdate;
end;
end;
end.