SynEdit: Update "change gutter" (yellow/green change indicators) to follow undo/redo. Issue #0032865 and issue #0037209

git-svn-id: trunk@63752 -
This commit is contained in:
martin 2020-08-16 12:40:34 +00:00
parent a733105c1c
commit 289361a48d
2 changed files with 164 additions and 7 deletions

View File

@ -199,7 +199,7 @@ type
fOnAdded: TNotifyEvent;
FOnNeedCaretUndo: TSynGetCaretUndoProc;
FOnNeedCaretUndoList: TSynEditUpdateCaretUndoProcList;
fUnModifiedItem: integer;
fUnModifiedItem, fSavedItem: integer;
FForceGroupEnd: Boolean;
procedure EnsureMaxEntries;
function GetCanUndo: boolean;
@ -213,7 +213,7 @@ type
{$ENDIF}
constructor Create;
destructor Destroy; override;
procedure AddChange(AChange: TSynEditUndoItem);
procedure AddChange(AChange: TSynEditUndoItem; AForceLocked: Boolean = False);
// "LastChange: Either in current Group, or in last Group, if no current
procedure AppendToLastChange(AChange: TSynEditUndoItem);
function GetLastChange: TSynEditUndoItem; // Excludes caret
@ -226,11 +226,18 @@ type
function PeekItem: TSynEditUndoGroup;
procedure Unlock;
function IsLocked: Boolean;
(* Historically SynEdit has
TSynEdit.MarkTextAsSaved; // Affects the "Changes Gutter" only
TSynEdit.Modified := False;
*)
procedure MarkTopAsUnmodified;
procedure MarkTopAsSaved;
procedure ForceGroupEnd;
function RealCount: Integer;
function IsTopMarkedAsUnmodified: boolean;
function UnModifiedMarkerExists: boolean;
function IsTopMarkedAsSaved: boolean;
function SavedMarkerExists: boolean;
{$IFDEF SynUndoDebugBeginEnd}
property InGroupCount: integer read FInGroupCount;
{$ENDIF}
@ -298,6 +305,7 @@ begin
fItems := TList.Create;
fMaxUndoActions := 1024;
fUnModifiedItem:=-1;
fSavedItem := -2; // -1 shall be that there was a SavedMarker, but it went out of scope due to max entries
FForceGroupEnd := False;
end;
@ -310,11 +318,12 @@ begin
inherited Destroy;
end;
procedure TSynEditUndoList.AddChange(AChange: TSynEditUndoItem);
procedure TSynEditUndoList.AddChange(AChange: TSynEditUndoItem;
AForceLocked: Boolean);
var
ugroup: TSynEditUndoGroup;
begin
if fLockCount > 0 then begin
if (not AForceLocked) and (fLockCount > 0) then begin
AChange.Free;
exit;
end;
@ -412,6 +421,7 @@ begin
fItems.Clear;
fFullUndoImposible := FALSE;
fUnModifiedItem:=-1;
fSavedItem := -2;
end;
procedure TSynEditUndoList.EndBlock;
@ -467,6 +477,7 @@ begin
Item.Free;
fItems.Delete(0);
if fUnModifiedItem>=0 then dec(fUnModifiedItem);
if fSavedItem >= 0 then dec(fSavedItem);
end;
end;
end;
@ -511,6 +522,8 @@ begin
fItems.Delete(iLast);
if fUnModifiedItem>fItems.Count then
fUnModifiedItem:=-1;
if fSavedItem>fItems.Count then
fSavedItem:=-2;
end;
end;
@ -557,6 +570,11 @@ begin
fUnModifiedItem := RealCount;
end;
procedure TSynEditUndoList.MarkTopAsSaved;
begin
fSavedItem := RealCount;
end;
procedure TSynEditUndoList.ForceGroupEnd;
begin
FForceGroupEnd := True;
@ -572,6 +590,16 @@ begin
Result := fUnModifiedItem >= 0;
end;
function TSynEditUndoList.IsTopMarkedAsSaved: boolean;
begin
Result := (RealCount = fSavedItem);
end;
function TSynEditUndoList.SavedMarkerExists: boolean;
begin
Result := fSavedItem >= -1;
end;
procedure TSynEditUndoList.RegisterUpdateCaretUndo(AnUpdateProc: TSynUpdateCaretUndoProc);
begin
FOnNeedCaretUndoList.Add(TMethod(AnUpdateProc));

View File

@ -355,6 +355,28 @@ type
function PerformUndo(Caller: TObject): Boolean; override;
end;
TSynEditStringFlagsArray = packed array of TSynEditStringFlags;
{ TSynEditUndoMarkModified }
TSynEditUndoMarkModified = class(TSynEditUndoItem)
private
FPosY: TLineIdx;
FWasSaved: TSynEditStringFlagsArray;
protected
function DebugString: String; override;
public
constructor Create(ALine: TLineIdx; AWasSaved: TSynEditStringFlagsArray);
function PerformUndo(Caller: TObject): Boolean; override;
end;
var
(* Re-usable arrays for the most common cases *)
SynEditUndoMarkModifiedOneEmpty: TSynEditStringFlagsArray; // = [];
SynEditUndoMarkModifiedOneSaved: TSynEditStringFlagsArray; // = [sfSaved];
SynEditUndoMarkModifiedOneModified: TSynEditStringFlagsArray; // = [sfModified];
{ TLazSynDisplayBuffer }
constructor TLazSynDisplayBuffer.Create(ABuffer: TSynEditStringList);
@ -565,6 +587,63 @@ begin
TSynEditStringList(Caller).EditLinesInsert(FPosY, FCount)
end;
{ TSynEditUndoMarkModified }
function TSynEditUndoMarkModified.DebugString: String;
begin
Result := 'Y='+dbgs(FPosY) + ' Cnt='+ dbgs(Length(FWasSaved));
end;
constructor TSynEditUndoMarkModified.Create(ALine: TLineIdx;
AWasSaved: TSynEditStringFlagsArray);
begin
FPosY := ALine;
FWasSaved := AWasSaved;
{$IFDEF SynUndoDebugItems}debugln(['--- Undo Insert ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
end;
function TSynEditUndoMarkModified.PerformUndo(Caller: TObject): Boolean;
var
i: Integer;
WasSaved: TSynEditStringFlagsArray;
Buffer: TSynEditStringList absolute Caller;
UnSaved: Boolean;
begin
Result := Caller is TSynEditStringList;
{$IFDEF SynUndoDebugItems}if Result then debugln(['--- Undo Perform ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
if Result then begin
UnSaved := Buffer.CurUndoList.SavedMarkerExists and (not Buffer.CurUndoList.IsTopMarkedAsSaved);
if Length(FWasSaved) = 1 then begin
if FPosY < Buffer.Count then begin
if sfSaved in Buffer.Flags[FPosY] then
WasSaved := SynEditUndoMarkModifiedOneSaved
else
if sfModified in Buffer.Flags[FPosY] then
WasSaved := SynEditUndoMarkModifiedOneModified
else
WasSaved := SynEditUndoMarkModifiedOneEmpty;
if (sfSaved in FWasSaved[0]) and UnSaved then
Buffer.Flags[FPosY] := [sfModified]
else
Buffer.Flags[FPosY] := FWasSaved[0];
end;
end
else begin
SetLength(WasSaved, Length(FWasSaved));
for i := 0 to Min(Length(FWasSaved), Buffer.Count - FPosY) - 1 do begin
WasSaved[i] := Buffer.Flags[FPosY + i];
if (sfSaved in FWasSaved[i]) and UnSaved then
Buffer.Flags[FPosY + i] := [sfModified]
else
Buffer.Flags[FPosY + i] := FWasSaved[i];
end;
end;
if WasSaved <> nil then
Buffer.CurUndoList.AddChange(TSynEditUndoMarkModified.Create(FPosY, WasSaved), True);
end;
end;
{ TSynEditStringList }
@ -823,6 +902,11 @@ begin
if not AValue then
SendNotification(senrEndUndoRedo, Self); // before UNDO ends
if (not AValue) and fUndoList.IsTopMarkedAsSaved then begin
fRedoList.MarkTopAsSaved;
MarkSaved;
end;
FIsUndoing := AValue;
if AValue then
@ -842,6 +926,11 @@ begin
if not AValue then
SendNotification(senrEndUndoRedo, Self); // before UNDO ends
if (not AValue) and fRedoList.IsTopMarkedAsSaved then begin
fUndoList.MarkTopAsSaved;
MarkSaved;
end;
FIsRedoing := AValue;
if AValue then
@ -1213,11 +1302,35 @@ end;
procedure TSynEditStringList.MarkModified(AFirst, ALast: Integer);
var
Index: Integer;
Index, i: Integer;
WasSaved: TSynEditStringFlagsArray;
NeedUndo: Boolean;
begin
for Index := AFirst - 1 to ALast - 1 do
if (Index >= 0) or (Index < Count) then
if IsUndoing or IsRedoing then
exit;
AFirst := ToIdx(AFirst);
ALast := ToIdx(ALast);
if ALast = AFirst then begin
if sfSaved in Flags[AFirst] then
CurUndoList.AddChange(TSynEditUndoMarkModified.Create(AFirst, SynEditUndoMarkModifiedOneSaved), True)
else
if not (sfModified in Flags[AFirst]) then
CurUndoList.AddChange(TSynEditUndoMarkModified.Create(AFirst, SynEditUndoMarkModifiedOneEmpty), True);
Flags[AFirst] := Flags[AFirst] + [sfModified] - [sfSaved];
end
else begin
SetLength(WasSaved, ALast - AFirst + 1);
i := 0;
NeedUndo := False;
for Index := Max(0, AFirst) to Min(ALast, Count - 1) do begin
NeedUndo := NeedUndo or (Flags[Index] <> [sfModified]);
WasSaved[i] := Flags[Index];
Flags[Index] := Flags[Index] + [sfModified] - [sfSaved];
inc(i);
end;
if NeedUndo then
CurUndoList.AddChange(TSynEditUndoMarkModified.Create(AFirst, WasSaved), True);
end;
end;
procedure TSynEditStringList.MarkSaved;
@ -1227,6 +1340,13 @@ begin
for Index := 0 to Count - 1 do
if sfModified in Flags[Index] then
Flags[Index] := Flags[Index] + [sfSaved];
if not (IsUndoing or IsRedoing) then begin
fUndoList.MarkTopAsSaved;
FRedoList.MarkTopAsSaved;
end;
SendNotification(senrHighlightChanged, Self, -1, -1);
end;
procedure TSynEditStringList.AddManagedHandler(AReason: TSynEditNotifyReason;
@ -1774,5 +1894,14 @@ begin
aLineBrkCnt, aText);
end;
initialization
SetLength(SynEditUndoMarkModifiedOneEmpty, 1);
SetLength(SynEditUndoMarkModifiedOneSaved, 1);
SetLength(SynEditUndoMarkModifiedOneModified, 1);
SynEditUndoMarkModifiedOneEmpty[0] := [];
SynEditUndoMarkModifiedOneSaved[0] := [sfSaved, sfModified];
SynEditUndoMarkModifiedOneModified[0] := [sfModified];
end.