mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-02 00:43:41 +02:00
SynEdit: Refactor the Undo/Redo system. Should fix some issues with column selection too; and enables group-undo for all kind of edit-actions; also fixes bug #13298
git-svn-id: trunk@19001 -
This commit is contained in:
parent
b8e7143c97
commit
a26d1805de
@ -84,7 +84,6 @@ begin
|
||||
end;
|
||||
|
||||
function TSynBeautifier.UnIndentLine(const Editor: TSynEditBase;
|
||||
// XXXXX viewed
|
||||
const Line: string; const Lines: TSynEditStrings; const PhysCaret: TPoint;
|
||||
out DelChars, InsChars: String; out CaretNewX: Integer): String;
|
||||
var
|
||||
@ -109,13 +108,13 @@ begin
|
||||
LogSpacePos := TSynEdit(Editor).PhysicalToLogicalCol(Line, PhysCaret.y-1, SpaceCount2 + 1);
|
||||
LogCaret := TSynEdit(Editor).PhysicalToLogicalPos(PhysCaret);
|
||||
CaretNewX := SpaceCount2 + 1;
|
||||
Lines.EditDelete(LogSpacePos, PhysCaret.Y, LogCaret.X - LogSpacePos);
|
||||
DelChars := copy(Line, LogSpacePos, LogCaret.X - LogSpacePos);
|
||||
InsChars := ''; // TODO: if tabs were removed, maybe fill-up with spaces
|
||||
Result :=copy(Line, 1, LogSpacePos-1) + copy(Line, LogCaret.X, MaxInt);
|
||||
end;
|
||||
|
||||
function TSynBeautifier.IndentLine(const Editor: TSynEditBase; Line: string;
|
||||
// XXXXX viewed
|
||||
const Lines: TSynEditStrings; const PhysCaret: TPoint; out DelChars,
|
||||
InsChars: String; out CaretNewX: Integer; RemoveCurrentIndent: Boolean): String;
|
||||
var
|
||||
@ -151,8 +150,8 @@ begin
|
||||
else
|
||||
InsChars := '';
|
||||
end;
|
||||
Result := InsChars + Line;
|
||||
CaretNewX := TSynEdit(Editor).LogicalToPhysicalCol(Result, PhysCaret.y - 1, SpaceCount2+1);
|
||||
Result := InsChars;
|
||||
CaretNewX := TSynEdit(Editor).LogicalToPhysicalCol(Result + Line, PhysCaret.y - 1, SpaceCount2+1);
|
||||
end;
|
||||
|
||||
function TSynBeautifier.GetIndentForLine(Editor: TSynEditBase;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -38,7 +38,7 @@ uses
|
||||
{$IFDEF SYN_MBCSSUPPORT}
|
||||
Imm,
|
||||
{$ENDIF}
|
||||
SynEditTextBase, SynEditTypes, SynEditMiscProcs, SynEditTextBuffer;
|
||||
SynEditTextBase, SynEditTypes, SynEditMiscProcs;//, SynEditTextBuffer;
|
||||
|
||||
type
|
||||
|
||||
@ -78,7 +78,6 @@ type
|
||||
FLinesDeletedMethod: TLinesCountChanged;
|
||||
FLinesInsertedMethod: TLinesCountChanged;
|
||||
FEnabled: Boolean;
|
||||
FSpacesToTabs: Boolean;
|
||||
FTabWidth: Integer;
|
||||
FActiveSelectionMode: TSynSelectionMode;
|
||||
FSelectionMode: TSynSelectionMode;
|
||||
@ -103,13 +102,11 @@ type
|
||||
constructor Create(ALines: TSynEditStrings);
|
||||
//destructor Destroy; override;
|
||||
procedure AdjustAfterTrimming; // TODO: Move into TrimView
|
||||
procedure SetSelTextPrimitive(PasteMode: TSynSelectionMode; Value: PChar;
|
||||
AddToUndoList: Boolean = false; ChangeReason: TSynChangeReason = crInsert);
|
||||
procedure SetSelTextPrimitive(PasteMode: TSynSelectionMode; Value: PChar);
|
||||
function SelAvail: Boolean;
|
||||
function SelCanContinue(ACaret: TSynEditCaret): Boolean;
|
||||
function IsBackwardSel: Boolean; // SelStart < SelEnd ?
|
||||
property Enabled: Boolean read FEnabled write SetEnabled;
|
||||
property SpacesToTabs: Boolean read FSpacesToTabs write FSpacesToTabs;
|
||||
property ActiveSelectionMode: TSynSelectionMode
|
||||
read FActiveSelectionMode write SetActiveSelectionMode;
|
||||
property SelectionMode: TSynSelectionMode
|
||||
@ -142,7 +139,7 @@ type
|
||||
TSynEditCaret = class(TSynEditPointBase)
|
||||
private
|
||||
FAllowPastEOL: Boolean;
|
||||
FForcePastEOL: Boolean;
|
||||
FForcePastEOL: Integer;
|
||||
FLinePos: Integer; // 1 based
|
||||
FCharPos: Integer; // 1 based
|
||||
FOldLinePos: Integer; // 1 based
|
||||
@ -166,6 +163,8 @@ type
|
||||
Procedure DoUnlock; override;
|
||||
public
|
||||
constructor Create;
|
||||
procedure IncForcePastEOL;
|
||||
procedure DecForcePastEOL;
|
||||
property OldLinePos: Integer read FOldLinePos;
|
||||
property OldCharPos: Integer read FOldCharPos;
|
||||
property LinePos: Integer read fLinePos write setLinePos;
|
||||
@ -176,7 +175,6 @@ type
|
||||
property LineText: string read GetLineText write SetLineText;
|
||||
property AdjustToNextChar: Boolean read FAdjustToNextChar write FAdjustToNextChar;
|
||||
property AllowPastEOL: Boolean read FAllowPastEOL write SetAllowPastEOL;
|
||||
property ForcePastEOL: Boolean read FForcePastEOL write FForcePastEOL;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@ -240,7 +238,17 @@ begin
|
||||
fLinePos:= 1;
|
||||
fCharPos:= 1;
|
||||
FAllowPastEOL := True;
|
||||
FForcePastEOL := False;
|
||||
FForcePastEOL := 0;
|
||||
end;
|
||||
|
||||
procedure TSynEditCaret.IncForcePastEOL;
|
||||
begin
|
||||
inc(FForcePastEOL);
|
||||
end;
|
||||
|
||||
procedure TSynEditCaret.DecForcePastEOL;
|
||||
begin
|
||||
dec(FForcePastEOL);
|
||||
end;
|
||||
|
||||
procedure TSynEditCaret.setLinePos(const AValue : Integer);
|
||||
@ -316,10 +324,10 @@ begin
|
||||
if NewLine < 1 then begin
|
||||
// this is just to make sure if Lines stringlist should be empty
|
||||
NewLine := 1;
|
||||
if not (FAllowPastEOL or FForcePastEOL) then
|
||||
if (not FAllowPastEOL) and (FForcePastEOL = 0) then
|
||||
nMaxX := 1;
|
||||
end else begin
|
||||
if not (FAllowPastEOL or FForcePastEOL) then begin
|
||||
if (not FAllowPastEOL) and (FForcePastEOL = 0) then begin
|
||||
Line := Lines[NewLine - 1];
|
||||
nMaxX := Lines.LogicalToPhysicalCol(Line, NewLine - 1, length(Line)+1);
|
||||
end;
|
||||
@ -581,23 +589,18 @@ end;
|
||||
|
||||
procedure TSynEditSelection.SetSelText(const Value : string);
|
||||
begin
|
||||
SetSelTextPrimitive(smNormal, PChar(Value), true);
|
||||
SetSelTextPrimitive(smNormal, PChar(Value));
|
||||
end;
|
||||
|
||||
procedure TSynEditSelection.SetSelTextPrimitive(PasteMode : TSynSelectionMode;
|
||||
Value : PChar; AddToUndoList: Boolean = false;
|
||||
ChangeReason: TSynChangeReason = crInsert);
|
||||
Value : PChar);
|
||||
var
|
||||
BB, BE: TPoint;
|
||||
TempString: string;
|
||||
|
||||
procedure DeleteSelection;
|
||||
var
|
||||
x, MarkOffset: Integer;
|
||||
y, l, r, xb, xe, MarkOffset: Integer;
|
||||
UpdateMarks: boolean;
|
||||
{$IFDEF SYN_MBCSSUPPORT}
|
||||
l, r: Integer;
|
||||
{$ENDIF}
|
||||
begin
|
||||
UpdateMarks := FALSE;
|
||||
MarkOffset := 0;
|
||||
@ -605,62 +608,59 @@ var
|
||||
smNormal:
|
||||
begin
|
||||
if FLines.Count > 0 then begin
|
||||
// Create a string that contains everything on the first line up
|
||||
// to the selection mark, and everything on the last line after
|
||||
// the selection mark.
|
||||
TempString := Copy(FLines[BB.Y - 1], 1, BB.X - 1) +
|
||||
Copy(FLines[BE.Y - 1], BE.X, MaxInt);
|
||||
// Delete all FLines in the selection range.
|
||||
FLines.DeleteLines(BB.Y-1, BE.Y - BB.Y);
|
||||
FLines[BB.Y - 1] := TempString;
|
||||
if BE.Y > BB.Y + 1 then begin
|
||||
FLines.EditLinesDelete(BB.Y + 1, BE.Y - BB.Y - 1);
|
||||
BE.Y := BB.Y + 1;
|
||||
end;
|
||||
if BE.Y > BB.Y then begin
|
||||
BE.X := BE.X + length(FLines[BB.Y - 1]);
|
||||
FLines.EditLineJoin(BB.Y);
|
||||
BE.Y := BB.Y;
|
||||
end;
|
||||
FLines.EditDelete(BB.X, BB.Y, BE.X - BB.X);
|
||||
end;
|
||||
UpdateMarks := TRUE;
|
||||
FCaret.LineBytePos := BB;
|
||||
end;
|
||||
smColumn:
|
||||
begin
|
||||
// swap X if needed
|
||||
if BB.X > BE.X then
|
||||
FCaret.LineBytePos := BB;
|
||||
l := FCaret.CharPos;
|
||||
FCaret.LineBytePos := BE;
|
||||
r := FCaret.CharPos;
|
||||
// swap l, r if needed
|
||||
if l > r then
|
||||
{$IFDEF SYN_COMPILER_3_UP}
|
||||
SwapInt(BB.X, BE.X);
|
||||
SwapInt(l, r);
|
||||
{$ELSE}
|
||||
begin
|
||||
x := BB.X;
|
||||
BB.X := BE.X;
|
||||
BE.X := x;
|
||||
y := l;
|
||||
l := r;
|
||||
r := y;
|
||||
end;
|
||||
{$ENDIF}
|
||||
for x := BB.Y - 1 to BE.Y - 1 do begin
|
||||
TempString := FLines[x];
|
||||
{$IFNDEF SYN_MBCSSUPPORT}
|
||||
Delete(TempString, BB.X, BE.X - BB.X);
|
||||
{$ELSE}
|
||||
l := BB.X;
|
||||
r := BE.X;
|
||||
MBCSGetSelRangeInLineWhenColumnActiveSelectionMode(TempString, l, r);
|
||||
{$IFDEF USE_UTF8BIDI_LCL}
|
||||
VDelete(TempString, l, r - 1);
|
||||
{$ELSE USE_UTF8BIDI_LCL}
|
||||
Delete(TempString, l, r - l);
|
||||
{$ENDIF USE_UTF8BIDI_LCL}
|
||||
{$ENDIF}
|
||||
FLines[x] := TempString;
|
||||
for y := BB.Y to BE.Y do begin
|
||||
FCaret.LineCharPos := Point(l, y);
|
||||
xb := FCaret.BytePos;
|
||||
FCaret.LineCharPos := Point(r, y);
|
||||
xe := Min(FCaret.BytePos, 1 + length(FCaret.LineText));
|
||||
if xe > xb then
|
||||
FLines.EditDelete(xb, y, xe - xb);
|
||||
end;
|
||||
// FLines never get deleted completely, so keep caret at end.
|
||||
FCaret.LineBytePos := Point(BB.X, FEndLinePos);
|
||||
FCaret.LineCharPos := Point(l, FEndLinePos);
|
||||
// Column deletion never removes a line entirely, so no mark
|
||||
// updating is needed here.
|
||||
end;
|
||||
smLine:
|
||||
begin
|
||||
if BE.Y = FLines.Count then begin
|
||||
FLines[BE.Y - 1] := '';
|
||||
for x := BE.Y - 2 downto BB.Y - 1 do
|
||||
FLines.Delete(x);
|
||||
end else
|
||||
for x := BE.Y - 1 downto BB.Y - 1 do
|
||||
FLines.Delete(x);
|
||||
// smLine deletion always resets to first column.
|
||||
// Keep the (CrLf of) last line, since no Line exists to replace it
|
||||
FLines.EditDelete(1, BE.Y, length(FLines[BE.Y - 1]));
|
||||
dec(BE.Y)
|
||||
end;
|
||||
if BE.Y >= BB.Y then
|
||||
FLines.EditLinesDelete(BB.Y, BE.Y - BB.Y + 1);
|
||||
FCaret.LineCharPos := Point(1, BB.Y);
|
||||
UpdateMarks := TRUE;
|
||||
MarkOffset := 1;
|
||||
@ -688,62 +688,43 @@ var
|
||||
|
||||
function InsertNormal: Integer;
|
||||
var
|
||||
sLeftSide: string;
|
||||
sRightSide: string;
|
||||
Str: string;
|
||||
Start: PChar;
|
||||
P: PChar;
|
||||
LogCaretXY: TPoint;
|
||||
PhysicalLineEndPos: LongInt;
|
||||
begin
|
||||
Result := 0;
|
||||
LogCaretXY := FCaret.LineBytePos;
|
||||
sLeftSide := Copy(FCaret.LineText, 1, LogCaretXY.X - 1);
|
||||
if LogCaretXY.X - 1 > Length(sLeftSide) then begin
|
||||
PhysicalLineEndPos:= FLines.LogicalToPhysicalPos
|
||||
(Point(Length(sLeftSide)+1, FCaret.LinePos)).X-1;
|
||||
sLeftSide := sLeftSide
|
||||
+ CreateTabsAndSpaces(FCaret.CharPos,
|
||||
FCaret.CharPos-1-PhysicalLineEndPos,
|
||||
FTabWidth, FSpacesToTabs);
|
||||
end;
|
||||
sRightSide := Copy(FCaret.LineText, LogCaretXY.X,
|
||||
Length(FCaret.LineText) - (LogCaretXY.X - 1));
|
||||
// step1: insert the first line of Value into current line
|
||||
|
||||
Start := PChar(Value);
|
||||
P := GetEOL(Start);
|
||||
if P^ <> #0 then begin
|
||||
SetString(Str, Value, P - Start);
|
||||
FLines.InsertLines(FCaret.LinePos - 1, CountLines(P));
|
||||
FLines[FCaret.LinePos - 1] := sLeftSide + Str;
|
||||
if P^ = #0 then begin
|
||||
FLines.EditInsert(LogCaretXY.X, LogCaretXY.Y, Value);
|
||||
FCaret.BytePos := FCaret.BytePos + Length(Value);
|
||||
end else begin
|
||||
FLines[FCaret.LinePos - 1] := sLeftSide + Value + sRightSide;
|
||||
FCaret.BytePos := 1 + Length(sLeftSide + Value);
|
||||
end;
|
||||
// step2: insert left lines of Value
|
||||
while P^ <> #0 do begin
|
||||
if P^ = #13 then
|
||||
Inc(P);
|
||||
if P^ = #10 then
|
||||
Inc(P);
|
||||
FCaret.LinePos := FCaret.LinePos + 1;
|
||||
Start := P;
|
||||
P := GetEOL(Start);
|
||||
if P = Start then begin
|
||||
if p^ <> #0 then
|
||||
FLines[FCaret.LinePos - 1] := ''
|
||||
SetString(Str, Value, P - Start);
|
||||
FLines.EditInsert(LogCaretXY.X, LogCaretXY.Y, Str);
|
||||
FLines.EditLineBreak(LogCaretXY.X + Length(Str), LogCaretXY.Y);
|
||||
Result := CountLines(P);
|
||||
if Result > 1 then
|
||||
FLines.EditLinesInsert(LogCaretXY.Y + 1, Result - 1);
|
||||
while P^ <> #0 do begin
|
||||
if P^ = #13 then
|
||||
Inc(P);
|
||||
if P^ = #10 then
|
||||
Inc(P);
|
||||
LogCaretXY.Y := LogCaretXY.Y + 1;
|
||||
Start := P;
|
||||
P := GetEOL(Start);
|
||||
if P <> Start then begin
|
||||
SetString(Str, Start, P - Start);
|
||||
FLines.EditInsert(1, LogCaretXY.Y, Str);
|
||||
end
|
||||
else
|
||||
FLines[FCaret.LinePos - 1] := sRightSide;
|
||||
end else begin
|
||||
SetString(Str, Start, P - Start);
|
||||
if p^ <> #0 then
|
||||
FLines[FCaret.LinePos - 1] := Str
|
||||
else
|
||||
FLines[FCaret.LinePos - 1] := Str + sRightSide
|
||||
Str := '';
|
||||
end;
|
||||
if p^=#0 then
|
||||
FCaret.BytePos := 1 + Length(FLines[FCaret.LinePos - 1]) - Length(sRightSide);
|
||||
Inc(Result);
|
||||
FCaret.LinePos := LogCaretXY.Y;
|
||||
FCaret.BytePos := 1 + Length(Str);
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -752,40 +733,17 @@ var
|
||||
Str: string;
|
||||
Start: PChar;
|
||||
P: PChar;
|
||||
Len: Integer;
|
||||
InsertPos: Integer;
|
||||
LogicalInsertPos: Integer;
|
||||
begin
|
||||
// Insert string at current position
|
||||
InsertPos := FCaret.CharPos;
|
||||
Result := 0;
|
||||
FCaret.IncForcePastEOL;
|
||||
Start := PChar(Value);
|
||||
repeat
|
||||
P := GetEOL(Start);
|
||||
if P <> Start then begin
|
||||
SetLength(Str, P - Start);
|
||||
Move(Start^, Str[1], P - Start);
|
||||
if FCaret.LinePos > FLines.Count then {useless check. FCaret.LinePos cannot exceed FLines.Count}
|
||||
FLines.Add(StringOfChar(' ', InsertPos - 1) + Str)
|
||||
else begin
|
||||
TempString := FLines[FCaret.LinePos - 1];
|
||||
Len := Length(TempString);
|
||||
LogicalInsertPos := FLines.PhysicalToLogicalCol(TempString,
|
||||
FCaret.LinePos - 1, InsertPos);
|
||||
if Len < LogicalInsertPos
|
||||
then begin
|
||||
TempString :=
|
||||
TempString + StringOfChar(' ', LogicalInsertPos - Len - 1)
|
||||
+ Str
|
||||
end else begin
|
||||
{$IFDEF SYN_MBCSSUPPORT}
|
||||
if mbTrailByte = ByteType(TempString, InsertPos) then
|
||||
Insert(Str, TempString, InsertPos + 1)
|
||||
else
|
||||
{$ENDIF}
|
||||
System.Insert(Str, TempString, LogicalInsertPos);
|
||||
end;
|
||||
FLines[FCaret.LinePos - 1] := TempString;
|
||||
end;
|
||||
FLines.EditInsert(FCaret.BytePos, FCaret.LinePos, Str);
|
||||
end;
|
||||
if p^ in [#10,#13] then begin
|
||||
if (p[1] in [#10,#13]) and (p[1]<>p^) then
|
||||
@ -793,13 +751,14 @@ var
|
||||
else
|
||||
Inc(P);
|
||||
if FCaret.LinePos = FLines.Count then
|
||||
FLines.Add(StringOfChar(' ', InsertPos - 1));
|
||||
FLines.EditLinesInsert(FCaret.LinePos + 1, 1);
|
||||
// No need to inc result => adding at EOF
|
||||
FCaret.LinePos := FCaret.LinePos + 1;
|
||||
end;
|
||||
Start := P;
|
||||
until P^ = #0;
|
||||
FCaret.BytePos:= FCaret.BytePos + Length(Str);
|
||||
Result := 0;
|
||||
FCaret.DecForcePastEOL;
|
||||
end;
|
||||
|
||||
function InsertLine: Integer;
|
||||
@ -807,7 +766,6 @@ var
|
||||
Start: PChar;
|
||||
P: PChar;
|
||||
Str: string;
|
||||
n: Integer;
|
||||
begin
|
||||
Result := 0;
|
||||
FCaret.CharPos := 1;
|
||||
@ -821,14 +779,10 @@ var
|
||||
end else
|
||||
Str := '';
|
||||
if (P^ = #0) then begin // Not a full line?
|
||||
n := FLines.Count;
|
||||
if (n >= FCaret.LinePos) then
|
||||
FLines[FCaret.LinePos - 1] := Str + FLines[FCaret.LinePos - 1]
|
||||
else
|
||||
FLines.Add(Str);
|
||||
FCaret.CharPos := 1 + Length(Str);
|
||||
FLines.EditInsert(1, FCaret.LinePos, Str);
|
||||
FCaret.BytePos := 1 + Length(Str);
|
||||
end else begin
|
||||
FLines.Insert(FCaret.LinePos - 1, Str);
|
||||
FLines.EditLinesInsert(FCaret.LinePos, 1, Str);
|
||||
FCaret.LinePos := FCaret.LinePos + 1;
|
||||
Inc(Result);
|
||||
if P^ = #13 then
|
||||
@ -876,8 +830,6 @@ var
|
||||
FLinesInsertedMethod(StartLine, InsertedLines);
|
||||
end;
|
||||
|
||||
var
|
||||
StartInsert, EndInsert: TPoint;
|
||||
begin
|
||||
FLines.BeginUpdate;
|
||||
try
|
||||
@ -885,45 +837,11 @@ begin
|
||||
BB := FirstLineBytePos;
|
||||
BE := LastLineBytePos;
|
||||
if SelAvail then begin
|
||||
// todo: better move add-undo past actual delete
|
||||
FLines[BB.Y - 1] := FLines[BB.Y - 1]; // TrimRealSpaces (in case of smNormal or smLine
|
||||
if AddToUndoList then begin
|
||||
if ChangeReason in [crSilentDelete, crSilentDeleteAfterCursor] then begin
|
||||
if IsBackwardSel then
|
||||
fUndoList.AddChange(crSilentDeleteAfterCursor, StartLineBytePos, EndLineBytePos,
|
||||
GetSelText, ActiveSelectionMode)
|
||||
else
|
||||
fUndoList.AddChange(crSilentDelete, StartLineBytePos, EndLineBytePos,
|
||||
GetSelText, ActiveSelectionMode);
|
||||
end else begin
|
||||
if IsBackwardSel then
|
||||
fUndoList.AddChange(crDeleteAfterCursor, StartLineBytePos, EndLineBytePos,
|
||||
GetSelText, ActiveSelectionMode)
|
||||
else
|
||||
fUndoList.AddChange(crDelete, StartLineBytePos, EndLineBytePos,
|
||||
GetSelText, ActiveSelectionMode);
|
||||
end;
|
||||
end;
|
||||
DeleteSelection;
|
||||
EndLineBytePos := BB; // deletes selection // calls selection changed
|
||||
end;
|
||||
if (Value <> nil) and (Value[0] <> #0) then begin
|
||||
StartInsert := FCaret.LineBytePos;
|
||||
InsertText;
|
||||
if AddToUndoList then begin
|
||||
EndInsert := FCaret.LineBytePos;
|
||||
if ActiveSelectionMode = smLine then begin // The ActiveSelectionMode of the deleted block
|
||||
StartInsert.x := 1;
|
||||
if EndInsert.x = 1 then begin
|
||||
dec(EndInsert.y);
|
||||
EndInsert.x := Length(FLines[EndInsert.y - 1]);
|
||||
end;
|
||||
end;
|
||||
if ChangeReason in [crSilentDelete, crSilentDeleteAfterCursor, crDelete,
|
||||
crDeleteAfterCursor] then
|
||||
ChangeReason := crInsert;
|
||||
fUndoList.AddChange(ChangeReason, StartInsert, EndInsert, '', PasteMode);
|
||||
end;
|
||||
StartLineBytePos := FCaret.LineBytePos; // reset selection
|
||||
end;
|
||||
finally
|
||||
|
@ -26,7 +26,7 @@ unit SynEditTextBase;
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, LCLProc, SynEditTypes;
|
||||
Classes, SysUtils, LCLProc, SynEditTypes, SynEditMiscProcs, SynEditKeyCmds;
|
||||
|
||||
type
|
||||
TSynEditStrings = class;
|
||||
@ -37,6 +37,11 @@ type
|
||||
|
||||
TPhysicalCharWidths = Array of Shortint;
|
||||
|
||||
TSynEditUndoList = class;
|
||||
TSynEditUndoItem = class;
|
||||
|
||||
type
|
||||
|
||||
{ TSynEditStrings }
|
||||
|
||||
TSynEditStrings = class(TStrings)
|
||||
@ -52,6 +57,12 @@ type
|
||||
function GetExpandedString(Index: integer): string; virtual; abstract;
|
||||
function GetLengthOfLongestLine: integer; virtual; abstract;
|
||||
procedure SetTextStr(const Value: string); override;
|
||||
|
||||
function GetRedoList: TSynEditUndoList; virtual; abstract;
|
||||
function GetUndoList: TSynEditUndoList; virtual; abstract;
|
||||
procedure SetIsUndoing(const AValue: Boolean); virtual; abstract;
|
||||
procedure SendNotification(AReason: TSynEditNotifyReason;
|
||||
ASender: TSynEditStrings; aIndex, aCount: Integer); virtual; abstract;
|
||||
public
|
||||
constructor Create;
|
||||
procedure DeleteLines(Index, NumLines: integer); virtual; abstract;
|
||||
@ -77,6 +88,18 @@ type
|
||||
function PhysicalToLogicalPos(const p: TPoint): TPoint;
|
||||
function PhysicalToLogicalCol(const Line: string;
|
||||
Index, PhysicalPos: integer): integer; virtual;
|
||||
public
|
||||
procedure EditInsert(LogX, LogY: Integer; AText: String); virtual; abstract;
|
||||
function EditDelete(LogX, LogY, ByteLen: Integer): String; virtual; abstract;
|
||||
procedure EditLineBreak(LogX, LogY: Integer); virtual; abstract;
|
||||
procedure EditLineJoin(LogY: Integer; FillText: String = ''); virtual; abstract;
|
||||
procedure EditLinesInsert(LogY, ACount: Integer; AText: String = ''); virtual; abstract;
|
||||
procedure EditLinesDelete(LogY, ACount: Integer); virtual; abstract;
|
||||
procedure EditUndo(Item: TSynEditUndoItem); virtual; abstract;
|
||||
procedure EditRedo(Item: TSynEditUndoItem); virtual; abstract;
|
||||
property UndoList: TSynEditUndoList read GetUndoList;
|
||||
property RedoList: TSynEditUndoList read GetRedoList;
|
||||
property IsUndoing: Boolean write SetIsUndoing;
|
||||
public
|
||||
property ExpandedStrings[Index: integer]: string read GetExpandedString;
|
||||
property LengthOfLongestLine: integer read GetLengthOfLongestLine;
|
||||
@ -101,6 +124,12 @@ type
|
||||
|
||||
function GetExpandedString(Index: integer): string; override;
|
||||
function GetLengthOfLongestLine: integer; override;
|
||||
|
||||
procedure SendNotification(AReason: TSynEditNotifyReason;
|
||||
ASender: TSynEditStrings; aIndex, aCount: Integer); override;
|
||||
function GetRedoList: TSynEditUndoList; override;
|
||||
function GetUndoList: TSynEditUndoList; override;
|
||||
procedure SetIsUndoing(const AValue: Boolean); override;
|
||||
protected
|
||||
function GetCount: integer; override;
|
||||
function GetCapacity: integer;
|
||||
@ -135,10 +164,114 @@ type
|
||||
AHandler: TStringListLineCountEvent); override;
|
||||
|
||||
function GetPhysicalCharWidths(const Line: String; Index: Integer): TPhysicalCharWidths; override;
|
||||
public
|
||||
// LogX, LogY are 1-based
|
||||
procedure EditInsert(LogX, LogY: Integer; AText: String); override;
|
||||
function EditDelete(LogX, LogY, ByteLen: Integer): String; override;
|
||||
procedure EditLineBreak(LogX, LogY: Integer); override;
|
||||
procedure EditLineJoin(LogY: Integer; FillText: String = ''); override;
|
||||
procedure EditLinesInsert(LogY, ACount: Integer; AText: String = ''); override;
|
||||
procedure EditLinesDelete(LogY, ACount: Integer); override;
|
||||
procedure EditUndo(Item: TSynEditUndoItem); override;
|
||||
procedure EditRedo(Item: TSynEditUndoItem); override;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoItem }
|
||||
|
||||
TSynEditUndoItem = class(TObject)
|
||||
protected
|
||||
// IsEqual is only needed/implemented for Carets
|
||||
function IsEqualContent(AnItem: TSynEditUndoItem): Boolean; virtual;
|
||||
function IsEqual(AnItem: TSynEditUndoItem): Boolean;
|
||||
public
|
||||
function IsCaretInfo: Boolean; virtual;
|
||||
function PerformUndo(Caller: TObject): Boolean; virtual; abstract;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoGroup }
|
||||
|
||||
TSynEditUndoGroup = class(TObject)
|
||||
private
|
||||
FItems: Array of TSynEditUndoItem;
|
||||
FCount, FCapacity: Integer;
|
||||
FReason: TSynEditorCommand;
|
||||
function GetItem(Index: Integer): TSynEditUndoItem;
|
||||
procedure Grow;
|
||||
protected
|
||||
Function HasUndoInfo: Boolean;
|
||||
procedure Append(AnUndoGroup: TSynEditUndoItem);
|
||||
procedure TranferTo(AnUndoGroup: TSynEditUndoGroup);
|
||||
function CanMergeWith(AnUndoGroup: TSynEditUndoGroup): Boolean;
|
||||
procedure MergeWith(AnUndoGroup: TSynEditUndoGroup);
|
||||
public
|
||||
constructor Create;
|
||||
Destructor Destroy; override;
|
||||
|
||||
procedure Assign(AnUndoGroup: TSynEditUndoGroup);
|
||||
procedure Add(AnItem: TSynEditUndoItem);
|
||||
procedure Clear;
|
||||
procedure Insert(AIndex: Integer; AnItem: TSynEditUndoItem);
|
||||
function Pop: TSynEditUndoItem;
|
||||
property Count: Integer read FCount;
|
||||
property Items [Index: Integer]: TSynEditUndoItem read GetItem;
|
||||
property Reason: TSynEditorCommand read FReason write FReason;
|
||||
end;
|
||||
|
||||
|
||||
TSynGetCaretUndoProc = function: TSynEditUndoItem of object;
|
||||
|
||||
{ TSynEditUndoList }
|
||||
|
||||
TSynEditUndoList = class(TObject)
|
||||
private
|
||||
FGroupUndo: Boolean;
|
||||
FIsInsideRedo: Boolean;
|
||||
FUndoGroup: TSynEditUndoGroup;
|
||||
FInGroupCount: integer;
|
||||
fFullUndoImposible: boolean;
|
||||
fItems: TList;
|
||||
fLockCount: integer;
|
||||
fMaxUndoActions: integer;
|
||||
fOnAdded: TNotifyEvent;
|
||||
FOnNeedCaretUndo: TSynGetCaretUndoProc;
|
||||
fUnModifiedItem: integer;
|
||||
procedure EnsureMaxEntries;
|
||||
function GetCanUndo: boolean;
|
||||
function GetCurrentReason: TSynEditorCommand;
|
||||
function GetItemCount: integer;
|
||||
procedure SetCurrentReason(const AValue: TSynEditorCommand);
|
||||
procedure SetMaxUndoActions(Value: integer);
|
||||
function RealCount: Integer;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
procedure AddChange(AChange: TSynEditUndoItem);
|
||||
procedure AppendToLastChange(AChange: TSynEditUndoItem);
|
||||
procedure BeginBlock;
|
||||
procedure EndBlock;
|
||||
procedure Clear;
|
||||
procedure Lock;
|
||||
function PopItem: TSynEditUndoGroup;
|
||||
procedure Unlock;
|
||||
function IsLocked: Boolean;
|
||||
procedure MarkTopAsUnmodified;
|
||||
function IsTopMarkedAsUnmodified: boolean;
|
||||
function UnModifiedMarkerExists: boolean;
|
||||
public
|
||||
property CanUndo: boolean read GetCanUndo;
|
||||
property FullUndoImpossible: boolean read fFullUndoImposible;
|
||||
property ItemCount: integer read GetItemCount;
|
||||
property MaxUndoActions: integer read fMaxUndoActions
|
||||
write SetMaxUndoActions;
|
||||
property IsInsideRedo: Boolean read FIsInsideRedo write FIsInsideRedo;
|
||||
property OnAddedUndo: TNotifyEvent read fOnAdded write fOnAdded;
|
||||
property OnNeedCaretUndo : TSynGetCaretUndoProc
|
||||
read FOnNeedCaretUndo write FOnNeedCaretUndo;
|
||||
property GroupUndo: Boolean read FGroupUndo write FGroupUndo;
|
||||
property CurrentGroup: TSynEditUndoGroup read FUndoGroup;
|
||||
property CurrentReason: TSynEditorCommand read GetCurrentReason
|
||||
write SetCurrentReason;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
@ -305,6 +438,11 @@ begin
|
||||
fSynStrings.InsertStrings(Index, NewStrings);
|
||||
end;
|
||||
|
||||
procedure TSynEditStringsLinked.SetIsUndoing(const AValue: Boolean);
|
||||
begin
|
||||
fSynStrings.IsUndoing := AValue;
|
||||
end;
|
||||
|
||||
function TSynEditStringsLinked.GetIsUtf8: Boolean;
|
||||
begin
|
||||
Result := FSynStrings.IsUtf8;
|
||||
@ -347,6 +485,16 @@ begin
|
||||
Result:= fSynStrings.GetLengthOfLongestLine;
|
||||
end;
|
||||
|
||||
function TSynEditStringsLinked.GetRedoList: TSynEditUndoList;
|
||||
begin
|
||||
Result := fSynStrings.GetRedoList;
|
||||
end;
|
||||
|
||||
function TSynEditStringsLinked.GetUndoList: TSynEditUndoList;
|
||||
begin
|
||||
Result := fSynStrings.GetUndoList;
|
||||
end;
|
||||
|
||||
procedure TSynEditStringsLinked.RegisterAttribute(const Index: TClass; const Size: Word);
|
||||
begin
|
||||
fSynStrings.RegisterAttribute(Index, Size);
|
||||
@ -416,5 +564,433 @@ begin
|
||||
fSynStrings.EndUpdate;
|
||||
end;
|
||||
|
||||
procedure TSynEditStringsLinked.EditInsert(LogX, LogY: Integer; AText: String);
|
||||
begin
|
||||
fSynStrings.EditInsert(LogX, LogY, AText);
|
||||
end;
|
||||
|
||||
function TSynEditStringsLinked.EditDelete(LogX, LogY, ByteLen: Integer): String;
|
||||
begin
|
||||
Result := fSynStrings.EditDelete(LogX, LogY, ByteLen);
|
||||
end;
|
||||
|
||||
procedure TSynEditStringsLinked.EditLineBreak(LogX, LogY: Integer);
|
||||
begin
|
||||
fSynStrings.EditLineBreak(LogX, LogY);
|
||||
end;
|
||||
|
||||
procedure TSynEditStringsLinked.EditLineJoin(LogY: Integer;
|
||||
FillText: String = '');
|
||||
begin
|
||||
fSynStrings.EditLineJoin(LogY, FillText);
|
||||
end;
|
||||
|
||||
procedure TSynEditStringsLinked.EditLinesInsert(LogY, ACount: Integer; AText: String = '');
|
||||
begin
|
||||
fSynStrings.EditLinesInsert(LogY, ACount, AText);
|
||||
end;
|
||||
|
||||
procedure TSynEditStringsLinked.EditLinesDelete(LogY, ACount: Integer);
|
||||
begin
|
||||
fSynStrings.EditLinesDelete(LogY, ACount);
|
||||
end;
|
||||
|
||||
procedure TSynEditStringsLinked.EditUndo(Item: TSynEditUndoItem);
|
||||
begin
|
||||
fSynStrings.EditUndo(Item);
|
||||
end;
|
||||
|
||||
procedure TSynEditStringsLinked.EditRedo(Item: TSynEditUndoItem);
|
||||
begin
|
||||
fSynStrings.EditRedo(Item);
|
||||
end;
|
||||
|
||||
procedure TSynEditStringsLinked.SendNotification(AReason: TSynEditNotifyReason;
|
||||
ASender: TSynEditStrings; aIndex, aCount: Integer);
|
||||
begin
|
||||
fSynStrings.SendNotification(AReason, ASender, aIndex, aCount);
|
||||
end;
|
||||
|
||||
{ TSynEditUndoList }
|
||||
|
||||
constructor TSynEditUndoList.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
// Create and keep one undo group => avoids resizing the FItems list
|
||||
FUndoGroup := TSynEditUndoGroup.Create;
|
||||
FIsInsideRedo := False;
|
||||
fItems := TList.Create;
|
||||
fMaxUndoActions := 1024;
|
||||
fUnModifiedItem:=-1;
|
||||
end;
|
||||
|
||||
destructor TSynEditUndoList.Destroy;
|
||||
begin
|
||||
Clear;
|
||||
fItems.Free;
|
||||
FreeAndNil(FUndoGroup);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.AddChange(AChange: TSynEditUndoItem);
|
||||
var
|
||||
ugroup: TSynEditUndoGroup;
|
||||
begin
|
||||
if fLockCount > 0 then begin
|
||||
AChange.Free;
|
||||
exit;
|
||||
end;
|
||||
|
||||
if FInGroupCount > 0 then
|
||||
FUndoGroup.Add(AChange)
|
||||
else begin
|
||||
ugroup := TSynEditUndoGroup.Create;
|
||||
ugroup.Add(AChange);
|
||||
fItems.Add(ugroup);
|
||||
if Assigned(fOnAdded) then
|
||||
fOnAdded(Self);
|
||||
end;
|
||||
|
||||
EnsureMaxEntries;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.AppendToLastChange(AChange: TSynEditUndoItem);
|
||||
var
|
||||
cur: Boolean;
|
||||
begin
|
||||
cur := FUndoGroup.HasUndoInfo;
|
||||
if (fLockCount <> 0) or ((fItems.Count = 0) and not cur) then begin
|
||||
AChange.Free;
|
||||
exit;
|
||||
end;
|
||||
|
||||
if cur then
|
||||
FUndoGroup.Append(AChange)
|
||||
else
|
||||
TSynEditUndoGroup(fItems[fItems.Count-1]).Append(AChange);
|
||||
|
||||
// Do not callback to synedit, or Redo Info is lost
|
||||
EnsureMaxEntries;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.BeginBlock;
|
||||
begin
|
||||
Inc(FInGroupCount);
|
||||
if (FInGroupCount = 1) then begin
|
||||
FUndoGroup.Clear;
|
||||
if assigned(FOnNeedCaretUndo) then
|
||||
FUndoGroup.add(FOnNeedCaretUndo());
|
||||
end
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.Clear;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i := 0 to fItems.Count - 1 do
|
||||
TSynEditUndoGroup(fItems[i]).Free;
|
||||
fItems.Clear;
|
||||
fFullUndoImposible := FALSE;
|
||||
fUnModifiedItem:=-1;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.EndBlock;
|
||||
var
|
||||
ugroup: TSynEditUndoGroup;
|
||||
begin
|
||||
if FInGroupCount > 0 then begin
|
||||
Dec(FInGroupCount);
|
||||
if (FInGroupCount = 0) and FUndoGroup.HasUndoInfo then
|
||||
begin
|
||||
// Keep position for REDO; Do not replace if present
|
||||
if (not FUndoGroup.Items[FUndoGroup.Count - 1].IsCaretInfo)
|
||||
and assigned(FOnNeedCaretUndo) then
|
||||
FUndoGroup.Add(FOnNeedCaretUndo());
|
||||
if (fItems.Count > 0) and FGroupUndo and
|
||||
FUndoGroup.CanMergeWith(TSynEditUndoGroup(fItems[fItems.Count - 1])) then
|
||||
begin
|
||||
FUndoGroup.MergeWith(TSynEditUndoGroup(fItems[fItems.Count - 1]));
|
||||
FUndoGroup.TranferTo(TSynEditUndoGroup(fItems[fItems.Count - 1]));
|
||||
end else begin
|
||||
ugroup := TSynEditUndoGroup.Create;
|
||||
FUndoGroup.TranferTo(ugroup);
|
||||
fItems.Add(ugroup);
|
||||
end;
|
||||
if Assigned(fOnAdded) then
|
||||
fOnAdded(Self);
|
||||
FIsInsideRedo := False;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.EnsureMaxEntries;
|
||||
var
|
||||
Item: TSynEditUndoGroup;
|
||||
begin
|
||||
if fItems.Count > fMaxUndoActions then begin
|
||||
fFullUndoImposible := TRUE;
|
||||
while fItems.Count > fMaxUndoActions do begin
|
||||
Item := TSynEditUndoGroup(fItems[0]);
|
||||
Item.Free;
|
||||
fItems.Delete(0);
|
||||
if fUnModifiedItem>=0 then dec(fUnModifiedItem);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.GetCanUndo: boolean;
|
||||
begin
|
||||
Result := fItems.Count > 0;
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.GetCurrentReason: TSynEditorCommand;
|
||||
begin
|
||||
Result := FUndoGroup.Reason;
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.GetItemCount: integer;
|
||||
begin
|
||||
Result := fItems.Count;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.SetCurrentReason(const AValue: TSynEditorCommand);
|
||||
begin
|
||||
if FUndoGroup.Reason = ecNone then
|
||||
FUndoGroup.Reason := AValue;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.Lock;
|
||||
begin
|
||||
Inc(fLockCount);
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.PopItem: TSynEditUndoGroup;
|
||||
var
|
||||
iLast: integer;
|
||||
begin
|
||||
Result := nil;
|
||||
iLast := fItems.Count - 1;
|
||||
if iLast >= 0 then begin
|
||||
Result := TSynEditUndoGroup(fItems[iLast]);
|
||||
fItems.Delete(iLast);
|
||||
if fUnModifiedItem>fItems.Count then
|
||||
fUnModifiedItem:=-1;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.SetMaxUndoActions(Value: integer);
|
||||
begin
|
||||
if Value < 0 then
|
||||
Value := 0;
|
||||
if Value <> fMaxUndoActions then begin
|
||||
fMaxUndoActions := Value;
|
||||
EnsureMaxEntries;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.RealCount: Integer;
|
||||
begin
|
||||
Result := fItems.Count;
|
||||
if (FInGroupCount > 0) and FUndoGroup.HasUndoInfo then
|
||||
Result := Result + 1;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.Unlock;
|
||||
begin
|
||||
if fLockCount > 0 then
|
||||
Dec(fLockCount);
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.IsLocked: Boolean;
|
||||
begin
|
||||
Result := fLockCount > 0;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.MarkTopAsUnmodified;
|
||||
begin
|
||||
fUnModifiedItem := RealCount;
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.IsTopMarkedAsUnmodified: boolean;
|
||||
begin
|
||||
Result := (RealCount = fUnModifiedItem);
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.UnModifiedMarkerExists: boolean;
|
||||
begin
|
||||
Result := fUnModifiedItem >= 0;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoItem }
|
||||
|
||||
function TSynEditUndoItem.IsEqualContent(AnItem: TSynEditUndoItem): Boolean;
|
||||
begin
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
function TSynEditUndoItem.IsEqual(AnItem: TSynEditUndoItem): Boolean;
|
||||
begin
|
||||
Result := (ClassType = AnItem.ClassType);
|
||||
if Result then Result := Result and IsEqualContent(AnItem);
|
||||
end;
|
||||
|
||||
function TSynEditUndoItem.IsCaretInfo: Boolean;
|
||||
begin
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
(*
|
||||
function TSynEditUndoItem.ChangeStartPos: TPoint;
|
||||
begin
|
||||
if (fChangeStartPos.Y < fChangeEndPos.Y)
|
||||
or ((fChangeStartPos.Y = fChangeEndPos.Y) and (fChangeStartPos.X < fChangeEndPos.X))
|
||||
then result := fChangeStartPos
|
||||
else result := fChangeEndPos;
|
||||
end;
|
||||
|
||||
function TSynEditUndoItem.ChangeEndPos: TPoint;
|
||||
begin
|
||||
if (fChangeStartPos.Y < fChangeEndPos.Y)
|
||||
or ((fChangeStartPos.Y = fChangeEndPos.Y) and (fChangeStartPos.X < fChangeEndPos.X))
|
||||
then result := fChangeEndPos
|
||||
else result := fChangeStartPos;
|
||||
end;
|
||||
*)
|
||||
|
||||
{ TSynEditUndoGroup }
|
||||
|
||||
procedure TSynEditUndoGroup.Grow;
|
||||
begin
|
||||
FCapacity := FCapacity + Max(10, FCapacity Div 8);
|
||||
SetLength(FItems, FCapacity);
|
||||
end;
|
||||
|
||||
function TSynEditUndoGroup.HasUndoInfo: Boolean;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
i := 0;
|
||||
while i < FCount do
|
||||
if FItems[i].IsCaretInfo then
|
||||
inc(i)
|
||||
else
|
||||
exit(true);
|
||||
Result := False;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoGroup.Append(AnUndoGroup: TSynEditUndoItem);
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
i := FCount - 1;
|
||||
while (i >= 0) and FItems[i].IsCaretInfo do
|
||||
dec(i);
|
||||
inc(i);
|
||||
Insert(i, AnUndoGroup);
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoGroup.TranferTo(AnUndoGroup: TSynEditUndoGroup);
|
||||
begin
|
||||
AnUndoGroup.Assign(self);
|
||||
FCount := 0; // Do not clear; that would free the items
|
||||
end;
|
||||
|
||||
function TSynEditUndoGroup.CanMergeWith(AnUndoGroup: TSynEditUndoGroup): Boolean;
|
||||
begin
|
||||
// Check if the other group can be merged to the START of this node
|
||||
if AnUndoGroup.Count = 0 then exit(True);
|
||||
Result := (FReason <> ecNone) and (AnUndoGroup.Reason = FReason)
|
||||
and Items[0].IsCaretInfo
|
||||
and AnUndoGroup.Items[AnUndoGroup.Count - 1].IsEqual(Items[0]);
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoGroup.MergeWith(AnUndoGroup: TSynEditUndoGroup);
|
||||
begin
|
||||
// Merge other group to start
|
||||
AnUndoGroup.Pop.Free;
|
||||
if AnUndoGroup.Count > 0 then begin
|
||||
fItems[0].Free;
|
||||
fItems[0] := AnUndoGroup.Pop;
|
||||
end;
|
||||
while AnUndoGroup.Count > 0 do
|
||||
Insert(0, AnUndoGroup.Pop);
|
||||
end;
|
||||
|
||||
function TSynEditUndoGroup.GetItem(Index: Integer): TSynEditUndoItem;
|
||||
begin
|
||||
Result := FItems[Index];
|
||||
end;
|
||||
|
||||
constructor TSynEditUndoGroup.Create;
|
||||
begin
|
||||
FCount := 0;
|
||||
FCapacity := 0;
|
||||
end;
|
||||
|
||||
destructor TSynEditUndoGroup.Destroy;
|
||||
begin
|
||||
Clear;
|
||||
FItems := nil;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoGroup.Assign(AnUndoGroup: TSynEditUndoGroup);
|
||||
begin
|
||||
Clear;
|
||||
FCapacity := AnUndoGroup.Count;
|
||||
FCount := FCapacity;
|
||||
SetLength(FItems, FCapacity);
|
||||
if FCapacity = 0 then
|
||||
exit;
|
||||
System.Move(AnUndoGroup.FItems[0], FItems[0], FCapacity * SizeOf(TSynEditUndoItem));
|
||||
FReason := AnUndoGroup.Reason;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoGroup.Add(AnItem: TSynEditUndoItem);
|
||||
begin
|
||||
if (FCount > 0) and AnItem.IsCaretInfo
|
||||
and FItems[FCount - 1].IsCaretInfo then
|
||||
begin
|
||||
FItems[FCount - 1].Free;
|
||||
FItems[FCount - 1] := AnItem;
|
||||
exit;
|
||||
end;
|
||||
if FCount >= FCapacity then
|
||||
Grow;
|
||||
FItems[FCount] := AnItem;
|
||||
inc (FCount);
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoGroup.Clear;
|
||||
begin
|
||||
while FCount > 0 do begin
|
||||
dec(FCount);
|
||||
FItems[FCount].Free;
|
||||
end;
|
||||
if FCapacity > 100 then begin
|
||||
FCapacity := 100;
|
||||
SetLength(FItems, FCapacity);
|
||||
end;
|
||||
FReason := ecNone;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoGroup.Insert(AIndex: Integer; AnItem: TSynEditUndoItem);
|
||||
begin
|
||||
if FCount >= FCapacity then
|
||||
Grow;
|
||||
System.Move(FItems[AIndex], FItems[AIndex+1],
|
||||
(FCount - AIndex) * SizeOf(TSynEditUndoItem));
|
||||
FItems[AIndex] := AnItem;
|
||||
inc (FCount);
|
||||
end;
|
||||
|
||||
function TSynEditUndoGroup.Pop: TSynEditUndoItem;
|
||||
begin
|
||||
if FCount <= 0 then
|
||||
exit(nil);
|
||||
dec(FCount);
|
||||
Result := FItems[FCount];
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
@ -43,18 +43,13 @@ interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, SynEditTextBase,
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
FileUtil, LCLProc, FPCAdds, LCLIntf, LCLType,
|
||||
{$ELSE}
|
||||
Windows,
|
||||
{$ENDIF}
|
||||
SynEditTypes, SynEditMiscProcs; //mh 2000-10-19
|
||||
SynEditTypes, SynEditMiscProcs;
|
||||
|
||||
const
|
||||
NullRange = TSynEditRange(-1);
|
||||
|
||||
type
|
||||
{begin} //mh 2000-10-10
|
||||
{$IFNDEF SYN_LAZARUS}
|
||||
TSynEditRange = pointer;
|
||||
{$ENDIF}
|
||||
TSynEditRangeClass = class end; // For Register
|
||||
TSynEditFlagsClass = class end; // For Register
|
||||
|
||||
@ -65,26 +60,6 @@ type
|
||||
);
|
||||
TSynEditStringFlags = set of TSynEditStringFlag;
|
||||
|
||||
TSynChangeReason = (crInsert, crPaste, crDragDropInsert,
|
||||
// Note: crSelDelete and crDragDropDelete have been deleted, because
|
||||
// several undo entries can be chained together now via the ChangeNumber
|
||||
// see also TCustomSynEdit.[Begin|End]UndoBlock methods
|
||||
crDeleteAfterCursor, crDelete, {crSelDelete, crDragDropDelete, } //mh 2000-11-20
|
||||
crLineBreak, crIndent, crUnindent,
|
||||
crSilentDelete, crSilentDeleteAfterCursor, //mh 2000-10-30
|
||||
crNothing {$IFDEF SYN_LAZARUS}, crTrimSpace, crTrimRealSpace {$ENDIF});
|
||||
|
||||
const
|
||||
SynChangeReasonNames : Array [TSynChangeReason] of string =
|
||||
('crInsert', 'crPaste', 'crDragDropInsert',
|
||||
'crDeleteAfterCursor', 'crDelete', {'crSelDelete', 'crDragDropDelete', }
|
||||
'crLineBreak', 'crIndent', 'crUnindent',
|
||||
'crSilentDelete', 'crSilentDeleteAfterCursor',
|
||||
'crNothing' {$IFDEF SYN_LAZARUS}, 'crTrimSpace', 'crTrimRealSpace' {$ENDIF});
|
||||
|
||||
NullRange = TSynEditRange(-1);
|
||||
|
||||
type
|
||||
TStringListIndexEvent = procedure(Index: Integer) of object;
|
||||
|
||||
TSynEditStringAttribute = record
|
||||
@ -97,7 +72,7 @@ type
|
||||
|
||||
TLineRangeNotificationList = Class(TMethodList)
|
||||
public
|
||||
procedure CallRangeNotifyEvents(Sender: TSynEditStrings; aIndex, aCount: Integer);
|
||||
Procedure CallRangeNotifyEvents(Sender: TSynEditStrings; aIndex, aCount: Integer);
|
||||
end;
|
||||
|
||||
|
||||
@ -146,6 +121,9 @@ type
|
||||
fIndexOfLongestLine: integer;
|
||||
fOnChange: TNotifyEvent;
|
||||
fOnChanging: TNotifyEvent;
|
||||
fRedoList: TSynEditUndoList;
|
||||
fUndoList: TSynEditUndoList;
|
||||
FIsUndoing: Boolean;
|
||||
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
function GetFlags(Index: Integer): TSynEditStringFlags;
|
||||
@ -159,6 +137,13 @@ type
|
||||
fOnCleared: TNotifyEvent;
|
||||
function GetExpandedString(Index: integer): string; override;
|
||||
function GetLengthOfLongestLine: integer; override;
|
||||
|
||||
function GetRedoList: TSynEditUndoList; override;
|
||||
function GetUndoList: TSynEditUndoList; override;
|
||||
procedure SetIsUndoing(const AValue: Boolean); override;
|
||||
procedure SendNotification(AReason: TSynEditNotifyReason;
|
||||
ASender: TSynEditStrings; aIndex, aCount: Integer); override;
|
||||
|
||||
function GetRange(Index: integer): TSynEditRange; {$IFDEF SYN_LAZARUS}override;{$ENDIF}
|
||||
procedure PutRange(Index: integer; ARange: TSynEditRange); {$IFDEF SYN_LAZARUS}override;{$ENDIF}
|
||||
function GetAttribute(const Owner: TClass; const Index: Integer): Pointer; override;
|
||||
@ -174,6 +159,8 @@ type
|
||||
procedure SetCapacity(NewCapacity: integer);
|
||||
{$IFDEF SYN_COMPILER_3_UP} override; {$ENDIF} //mh 2000-10-18
|
||||
procedure SetUpdateState(Updating: Boolean); override;
|
||||
|
||||
procedure UndoEditLinesDelete(LogY, ACount: Integer);
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
@ -191,7 +178,7 @@ type
|
||||
{$IFDEF SYN_LAZARUS}override;{$ENDIF}
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
procedure ClearRanges(ARange: TSynEditRange); override;
|
||||
procedure MarkModified(AFirst, ALast: Integer; AUndo: Boolean; AReason: TSynChangeReason);
|
||||
procedure MarkModified(AFirst, ALast: Integer);
|
||||
procedure MarkSaved;
|
||||
procedure SetDebugMarks(AFirst, ALast: Integer);
|
||||
procedure ClearDebugMarks;
|
||||
@ -212,78 +199,22 @@ type
|
||||
property Flags[Index: Integer]: TSynEditStringFlags read GetFlags
|
||||
write SetFlags;
|
||||
{$ENDIF}
|
||||
public
|
||||
property UndoList: TSynEditUndoList read GetUndoList write fUndoList;
|
||||
property RedoList: TSynEditUndoList read GetRedoList write fRedoList;
|
||||
procedure EditInsert(LogX, LogY: Integer; AText: String); override;
|
||||
function EditDelete(LogX, LogY, ByteLen: Integer): String; override;
|
||||
procedure EditLineBreak(LogX, LogY: Integer); override;
|
||||
procedure EditLineJoin(LogY: Integer; FillText: String = ''); override;
|
||||
procedure EditLinesInsert(LogY, ACount: Integer; AText: String = ''); override;
|
||||
procedure EditLinesDelete(LogY, ACount: Integer); override;
|
||||
procedure EditUndo(Item: TSynEditUndoItem); override;
|
||||
procedure EditRedo(Item: TSynEditUndoItem); override;
|
||||
end;
|
||||
|
||||
ESynEditStringList = class(Exception);
|
||||
{end} //mh 2000-10-10
|
||||
|
||||
{ TSynEditUndoItem }
|
||||
|
||||
TSynEditUndoItem = class(TObject)
|
||||
public
|
||||
fChangeReason: TSynChangeReason;
|
||||
fChangeSelMode: TSynSelectionMode;
|
||||
fChangeStartPos: TPoint; // logical position (byte)
|
||||
fChangeEndPos: TPoint; // logical position (byte)
|
||||
fChangeStr: string;
|
||||
fChangeNumber: integer; //sbs 2000-11-19
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
function ChangeStartPos: TPoint; // logical position (byte)
|
||||
function ChangeEndPos: TPoint; // logical position (byte)
|
||||
{$ENDIF}
|
||||
end;
|
||||
|
||||
{ TSynEditUndoList }
|
||||
|
||||
TSynEditUndoList = class(TObject)
|
||||
private
|
||||
fBlockChangeNumber: integer; //sbs 2000-11-19
|
||||
fBlockCount: integer; //sbs 2000-11-19
|
||||
fFullUndoImposible: boolean; //mh 2000-10-03
|
||||
fItems: TList;
|
||||
fLockCount: integer;
|
||||
fMaxUndoActions: integer;
|
||||
fNextChangeNumber: integer; //sbs 2000-11-19
|
||||
fOnAdded: TNotifyEvent;
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
fUnModifiedItem: integer;
|
||||
{$ENDIF}
|
||||
procedure EnsureMaxEntries;
|
||||
function GetCanUndo: boolean;
|
||||
function GetItemCount: integer;
|
||||
procedure SetMaxUndoActions(Value: integer);
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
procedure AddChange(AReason: TSynChangeReason; AStart, AEnd: TPoint;
|
||||
ChangeText: string; SelMode: TSynSelectionMode);
|
||||
procedure AppendToLastChange(AReason: TSynChangeReason; AStart, AEnd: TPoint;
|
||||
ChangeText: string; SelMode: TSynSelectionMode);
|
||||
procedure BeginBlock; //sbs 2000-11-19
|
||||
procedure Clear;
|
||||
procedure EndBlock; //sbs 2000-11-19
|
||||
procedure Lock;
|
||||
function PeekItem: TSynEditUndoItem;
|
||||
function PopItem: TSynEditUndoItem;
|
||||
procedure PushItem(Item: TSynEditUndoItem);
|
||||
procedure Unlock;
|
||||
function IsLocked: Boolean;
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
procedure MarkTopAsUnmodified;
|
||||
function IsTopMarkedAsUnmodified: boolean;
|
||||
function UnModifiedMarkerExists: boolean;
|
||||
{$ENDIF}
|
||||
public
|
||||
property BlockChangeNumber: integer read fBlockChangeNumber //sbs 2000-11-19
|
||||
write fBlockChangeNumber;
|
||||
property CanUndo: boolean read GetCanUndo;
|
||||
property FullUndoImpossible: boolean read fFullUndoImposible; //mh 2000-10-03
|
||||
property ItemCount: integer read GetItemCount;
|
||||
property MaxUndoActions: integer read fMaxUndoActions
|
||||
write SetMaxUndoActions;
|
||||
property OnAddedUndo: TNotifyEvent read fOnAdded write fOnAdded;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{$IFNDEF FPC}
|
||||
@ -297,6 +228,161 @@ const
|
||||
{$ENDIF}
|
||||
SListIndexOutOfBounds = 'Invalid stringlist index %d';
|
||||
|
||||
type
|
||||
|
||||
{ TSynEditUndoTxtInsert }
|
||||
|
||||
TSynEditUndoTxtInsert = class(TSynEditUndoItem)
|
||||
private
|
||||
FPosX, FPosY, FLen: Integer;
|
||||
public
|
||||
constructor Create(APosX, APosY, ALen: Integer);
|
||||
function PerformUndo(Caller: TObject): Boolean; override;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTxtDelete }
|
||||
|
||||
TSynEditUndoTxtDelete = class(TSynEditUndoItem)
|
||||
private
|
||||
FPosX, FPosY: Integer;
|
||||
FText: String;
|
||||
public
|
||||
constructor Create(APosX, APosY: Integer; AText: String);
|
||||
function PerformUndo(Caller: TObject): Boolean; override;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTxtLineBreak }
|
||||
|
||||
TSynEditUndoTxtLineBreak = class(TSynEditUndoItem)
|
||||
private
|
||||
FPosY: Integer;
|
||||
public
|
||||
constructor Create(APosY: Integer);
|
||||
function PerformUndo(Caller: TObject): Boolean; override;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTxtLineJoin }
|
||||
|
||||
TSynEditUndoTxtLineJoin = class(TSynEditUndoItem)
|
||||
private
|
||||
FPosX, FPosY: Integer;
|
||||
public
|
||||
constructor Create(APosX, APosY: Integer);
|
||||
function PerformUndo(Caller: TObject): Boolean; override;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTxtLinesIns }
|
||||
|
||||
TSynEditUndoTxtLinesIns = class(TSynEditUndoItem)
|
||||
private
|
||||
FPosY, FCount: Integer;
|
||||
public
|
||||
constructor Create(ALine, ACount: Integer);
|
||||
function PerformUndo(Caller: TObject): Boolean; override;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTxtLinesDel }
|
||||
|
||||
TSynEditUndoTxtLinesDel = class(TSynEditUndoItem)
|
||||
private
|
||||
FPosY, FCount: Integer;
|
||||
public
|
||||
constructor Create(ALine, ACount: Integer);
|
||||
function PerformUndo(Caller: TObject): Boolean; override;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTxtInsert }
|
||||
|
||||
constructor TSynEditUndoTxtInsert.Create(APosX, APosY, ALen: Integer);
|
||||
begin
|
||||
FPosX := APosX;
|
||||
FPosY := APosY;
|
||||
FLen := ALen;
|
||||
end;
|
||||
|
||||
function TSynEditUndoTxtInsert.PerformUndo(Caller: TObject): Boolean;
|
||||
begin
|
||||
Result := Caller is TSynEditStringList;
|
||||
if Result then
|
||||
TSynEditStringList(Caller).EditDelete(FPosX, FPosY, FLen);
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTxtDelete }
|
||||
|
||||
constructor TSynEditUndoTxtDelete.Create(APosX, APosY: Integer; AText: String);
|
||||
begin
|
||||
FPosX := APosX;
|
||||
FPosY := APosY;
|
||||
FText := AText;
|
||||
end;
|
||||
|
||||
function TSynEditUndoTxtDelete.PerformUndo(Caller: TObject): Boolean;
|
||||
begin
|
||||
Result := Caller is TSynEditStringList;
|
||||
if Result then
|
||||
TSynEditStringList(Caller).EditInsert(FPosX, FPosY, FText);
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTxtLineBreak }
|
||||
|
||||
constructor TSynEditUndoTxtLineBreak.Create(APosY: Integer);
|
||||
begin
|
||||
FPosY := APosY;
|
||||
end;
|
||||
|
||||
function TSynEditUndoTxtLineBreak.PerformUndo(Caller: TObject): Boolean;
|
||||
begin
|
||||
Result := Caller is TSynEditStringList;
|
||||
if Result then
|
||||
TSynEditStringList(Caller).EditLineJoin(FPosY)
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTxtLineJoin }
|
||||
|
||||
constructor TSynEditUndoTxtLineJoin.Create(APosX, APosY: Integer);
|
||||
begin
|
||||
FPosX := APosX;
|
||||
FPosY := APosY;
|
||||
end;
|
||||
|
||||
function TSynEditUndoTxtLineJoin.PerformUndo(Caller: TObject): Boolean;
|
||||
begin
|
||||
Result := Caller is TSynEditStringList;
|
||||
if Result then
|
||||
TSynEditStringList(Caller).EditLineBreak(FPosX, FPosY)
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTxtLinesIns }
|
||||
|
||||
constructor TSynEditUndoTxtLinesIns.Create(ALine, ACount: Integer);
|
||||
begin
|
||||
FPosY := ALine;
|
||||
FCount := ACount;
|
||||
end;
|
||||
|
||||
function TSynEditUndoTxtLinesIns.PerformUndo(Caller: TObject): Boolean;
|
||||
begin
|
||||
Result := Caller is TSynEditStringList;
|
||||
if Result then
|
||||
TSynEditStringList(Caller).UndoEditLinesDelete(FPosY, FCount)
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTxtLinesDel }
|
||||
|
||||
constructor TSynEditUndoTxtLinesDel.Create(ALine, ACount: Integer);
|
||||
begin
|
||||
FPosY := ALine;
|
||||
FCount := ACount;
|
||||
end;
|
||||
|
||||
function TSynEditUndoTxtLinesDel.PerformUndo(Caller: TObject): Boolean;
|
||||
begin
|
||||
Result := Caller is TSynEditStringList;
|
||||
if Result then
|
||||
TSynEditStringList(Caller).EditLinesInsert(FPosY, FCount)
|
||||
end;
|
||||
|
||||
|
||||
{ TSynEditStringList }
|
||||
|
||||
procedure ListIndexOutOfBounds(Index: integer);
|
||||
@ -490,6 +576,24 @@ begin
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
function TSynEditStringList.GetRedoList: TSynEditUndoList;
|
||||
begin
|
||||
Result := fRedoList;
|
||||
end;
|
||||
|
||||
function TSynEditStringList.GetUndoList: TSynEditUndoList;
|
||||
begin
|
||||
if FIsUndoing then
|
||||
Result := fRedoList
|
||||
else
|
||||
Result := fUndoList;
|
||||
end;
|
||||
|
||||
procedure TSynEditStringList.SetIsUndoing(const AValue: Boolean);
|
||||
begin
|
||||
FIsUndoing := AValue;
|
||||
end;
|
||||
|
||||
// Maps the Physical Width (ScreenCells) to each character
|
||||
// Multibyte Chars have thw width on the first byte, and a 0 Width for all other bytes
|
||||
function TSynEditStringList.GetPhysicalCharWidths(const Line: String; Index: Integer): TPhysicalCharWidths;
|
||||
@ -512,8 +616,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{end} //mh 2000-10-19
|
||||
|
||||
function TSynEditStringList.GetObject(Index: integer): TObject;
|
||||
begin
|
||||
if (Index >= 0) and (Index < Count) then
|
||||
@ -726,15 +828,11 @@ begin
|
||||
Ranges[Index] := ARange;
|
||||
end;
|
||||
|
||||
procedure TSynEditStringList.MarkModified(AFirst, ALast: Integer;
|
||||
AUndo: Boolean; AReason: TSynChangeReason);
|
||||
procedure TSynEditStringList.MarkModified(AFirst, ALast: Integer);
|
||||
var
|
||||
Index: Integer;
|
||||
begin
|
||||
// AUndo = True => this change is also pushed to the undo list, False => to the redo list
|
||||
// AReason - a reason of change
|
||||
|
||||
for Index := AFirst to ALast do
|
||||
for Index := AFirst - 1 to ALast - 1 do
|
||||
if (Index >= 0) and (Index < Count) then
|
||||
Flags[Index] := Flags[Index] + [sfModified] - [sfSaved];
|
||||
end;
|
||||
@ -799,257 +897,105 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoList }
|
||||
|
||||
constructor TSynEditUndoList.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
fItems := TList.Create;
|
||||
fMaxUndoActions := 1024;
|
||||
fNextChangeNumber := 1; //sbs 2000-11-19
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
fUnModifiedItem:=-1;
|
||||
{$ENDIF}
|
||||
end;
|
||||
|
||||
destructor TSynEditUndoList.Destroy;
|
||||
begin
|
||||
Clear;
|
||||
fItems.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.AddChange(AReason: TSynChangeReason; AStart,
|
||||
AEnd: TPoint; ChangeText: string; SelMode: TSynSelectionMode);
|
||||
procedure TSynEditStringList.EditInsert(LogX, LogY: Integer; AText: String);
|
||||
var
|
||||
NewItem: TSynEditUndoItem;
|
||||
s: string;
|
||||
begin
|
||||
if fLockCount = 0 then begin
|
||||
NewItem := TSynEditUndoItem.Create;
|
||||
try
|
||||
with NewItem do begin
|
||||
fChangeReason := AReason;
|
||||
fChangeSelMode := SelMode;
|
||||
fChangeStartPos := AStart;
|
||||
fChangeEndPos := AEnd;
|
||||
fChangeStr := ChangeText;
|
||||
{begin} //sbs 2000-11-19
|
||||
if fBlockChangeNumber <> 0 then
|
||||
fChangeNumber := fBlockChangeNumber
|
||||
else begin
|
||||
fChangeNumber := fNextChangeNumber;
|
||||
if fBlockCount = 0 then begin
|
||||
Inc(fNextChangeNumber);
|
||||
if fNextChangeNumber = 0 then
|
||||
Inc(fNextChangeNumber);
|
||||
end;
|
||||
end;
|
||||
{end} //sbs 2000-11-19
|
||||
end;
|
||||
(* DebugLn(['TSynEditUndoList.AddChange ChangeNumber=',NewItem.fChangeNumber,
|
||||
' Reason=', SynChangeReasonNames[AReason],' Astart=',dbgs(AStart),
|
||||
' AEnd=',dbgs(AEnd),' SelMode=',ord(SelMode)]); *)
|
||||
PushItem(NewItem);
|
||||
except
|
||||
NewItem.Free;
|
||||
raise;
|
||||
end;
|
||||
end;
|
||||
s := Strings[LogY - 1];
|
||||
if LogX - 1 > Length(s) then
|
||||
AText := StringOfChar(' ', LogX - 1 - Length(s)) + AText;
|
||||
Strings[LogY - 1] := copy(s,1, LogX - 1) + AText + copy(s, LogX, length(s));
|
||||
UndoList.AddChange(TSynEditUndoTxtInsert.Create(LogX, LogY, Length(AText)));
|
||||
MarkModified(LogY, LogY);
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.AppendToLastChange(AReason: TSynChangeReason; AStart,
|
||||
AEnd: TPoint; ChangeText: string; SelMode: TSynSelectionMode);
|
||||
function TSynEditStringList.EditDelete(LogX, LogY, ByteLen: Integer): String;
|
||||
var
|
||||
NewItem: TSynEditUndoItem;
|
||||
s: string;
|
||||
begin
|
||||
if (fLockCount = 0) and (PeekItem <> nil) then begin
|
||||
if (fItems.Count = fUnModifiedItem) then
|
||||
inc(fUnModifiedItem);
|
||||
NewItem := TSynEditUndoItem.Create;
|
||||
try
|
||||
with NewItem do begin
|
||||
fChangeReason := AReason;
|
||||
fChangeSelMode := SelMode;
|
||||
fChangeStartPos := AStart;
|
||||
fChangeEndPos := AEnd;
|
||||
fChangeStr := ChangeText;
|
||||
fChangeNumber := PeekItem.fChangeNumber;
|
||||
end;
|
||||
//PushItem(NewItem);
|
||||
fItems.Add(NewItem);
|
||||
EnsureMaxEntries;
|
||||
except
|
||||
NewItem.Free;
|
||||
raise;
|
||||
end;
|
||||
end;
|
||||
s := Strings[LogY - 1];
|
||||
Result := copy(s, LogX, ByteLen);
|
||||
Strings[LogY - 1] := copy(s,1, LogX - 1) + copy(s, LogX + ByteLen, length(s));
|
||||
UndoList.AddChange(TSynEditUndoTxtDelete.Create(LogX, LogY, Result));
|
||||
MarkModified(LogY, LogY);
|
||||
end;
|
||||
|
||||
{begin} //sbs 2000-11-19
|
||||
procedure TSynEditUndoList.BeginBlock;
|
||||
begin
|
||||
Inc(fBlockCount);
|
||||
fBlockChangeNumber := fNextChangeNumber;
|
||||
end;
|
||||
{end} //sbs 2000-11-19
|
||||
|
||||
procedure TSynEditUndoList.Clear;
|
||||
procedure TSynEditStringList.EditLineBreak(LogX, LogY: Integer);
|
||||
var
|
||||
i: integer;
|
||||
s: string;
|
||||
begin
|
||||
for i := 0 to fItems.Count - 1 do
|
||||
TSynEditUndoItem(fItems[i]).Free;
|
||||
fItems.Clear;
|
||||
fFullUndoImposible := FALSE; //mh 2000-10-03
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
fUnModifiedItem:=-1;
|
||||
{$ENDIF}
|
||||
s := Strings[LogY - 1];
|
||||
Strings[LogY - 1] := copy(s, 1, LogX - 1);
|
||||
Insert(LogY, copy(s, LogX, length(s)));
|
||||
UndoList.AddChange(TSynEditUndoTxtLineBreak.Create(LogY));
|
||||
MarkModified(LogY, LogY + 1);
|
||||
end;
|
||||
|
||||
{begin} //sbs 2000-11-19
|
||||
procedure TSynEditUndoList.EndBlock;
|
||||
begin
|
||||
if fBlockCount > 0 then begin
|
||||
Dec(fBlockCount);
|
||||
if fBlockCount = 0 then begin
|
||||
fBlockChangeNumber := 0;
|
||||
Inc(fNextChangeNumber);
|
||||
if fNextChangeNumber = 0 then
|
||||
Inc(fNextChangeNumber);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
{end} //sbs 2000-11-19
|
||||
|
||||
procedure TSynEditUndoList.EnsureMaxEntries;
|
||||
procedure TSynEditStringList.EditLineJoin(LogY: Integer; FillText: String = '');
|
||||
var
|
||||
Item: TSynEditUndoItem;
|
||||
t: string;
|
||||
begin
|
||||
if fItems.Count > fMaxUndoActions then begin //mh 2000-10-03
|
||||
fFullUndoImposible := TRUE; //mh 2000-10-03
|
||||
while fItems.Count > fMaxUndoActions do begin
|
||||
Item := TSynEditUndoItem(fItems[0]);
|
||||
Item.Free;
|
||||
fItems.Delete(0);
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
if fUnModifiedItem>=0 then dec(fUnModifiedItem);
|
||||
{$ENDIF}
|
||||
end;
|
||||
end;
|
||||
t := Strings[LogY - 1];
|
||||
if FillText <> '' then
|
||||
EditInsert(1 + Length(t), LogY, FillText);
|
||||
UndoList.AddChange(TSynEditUndoTxtLineJoin.Create(1 + Length(Strings[LogY-1]),
|
||||
LogY));
|
||||
Strings[LogY - 1] := t + FillText + Strings[LogY] ;
|
||||
Delete(LogY);
|
||||
MarkModified(LogY, LogY);
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.GetCanUndo: boolean;
|
||||
procedure TSynEditStringList.EditLinesInsert(LogY, ACount: Integer;
|
||||
AText: String = '');
|
||||
begin
|
||||
Result := fItems.Count > 0;
|
||||
InsertLines(LogY - 1, ACount);
|
||||
UndoList.AddChange(TSynEditUndoTxtLinesIns.Create(LogY, ACount));
|
||||
if AText <> '' then
|
||||
EditInsert(1, LogY, AText);
|
||||
MarkModified(LogY, LogY + ACount - 1);
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.GetItemCount: integer;
|
||||
begin
|
||||
Result := fItems.Count;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.Lock;
|
||||
begin
|
||||
Inc(fLockCount);
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.PeekItem: TSynEditUndoItem;
|
||||
procedure TSynEditStringList.EditLinesDelete(LogY, ACount: Integer);
|
||||
var
|
||||
iLast: integer;
|
||||
i: Integer;
|
||||
begin
|
||||
Result := nil;
|
||||
iLast := fItems.Count - 1;
|
||||
if iLast >= 0 then
|
||||
Result := TSynEditUndoItem(fItems[iLast]);
|
||||
for i := LogY to LogY + ACount - 1 do
|
||||
EditDelete(1, i, length(Strings[i-1]));
|
||||
DeleteLines(LogY - 1, ACount);
|
||||
UndoList.AddChange(TSynEditUndoTxtLinesDel.Create(LogY, ACount));
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.PopItem: TSynEditUndoItem;
|
||||
var
|
||||
iLast: integer;
|
||||
procedure TSynEditStringList.EditUndo(Item: TSynEditUndoItem);
|
||||
begin
|
||||
Result := nil;
|
||||
iLast := fItems.Count - 1;
|
||||
if iLast >= 0 then begin
|
||||
Result := TSynEditUndoItem(fItems[iLast]);
|
||||
fItems.Delete(iLast);
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
if fUnModifiedItem>fItems.Count then fUnModifiedItem:=-1;
|
||||
{$ENDIF}
|
||||
(*DebugLn(['TSynEditUndoList.PopItem=',Result.fChangeNumber,
|
||||
' Reason=', SynChangeReasonNames[Result.fChangeReason],' Astart=',dbgs(Result.fChangeStartPos),
|
||||
' AEnd=',dbgs(result.fChangeEndPos),' SelMode=',ord(result.fChangeSelMode )]);*)
|
||||
IsUndoing := True;
|
||||
try
|
||||
EditRedo(Item);
|
||||
finally
|
||||
IsUndoing := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.PushItem(Item: TSynEditUndoItem);
|
||||
procedure TSynEditStringList.UndoEditLinesDelete(LogY, ACount: Integer);
|
||||
begin
|
||||
if Assigned(Item) then begin
|
||||
fItems.Add(Item);
|
||||
EnsureMaxEntries;
|
||||
if Assigned(fOnAdded) then
|
||||
fOnAdded(Self);
|
||||
UndoList.AddChange(TSynEditUndoTxtLinesDel.Create(LogY, ACount));
|
||||
DeleteLines(LogY - 1, ACount);
|
||||
end;
|
||||
|
||||
procedure TSynEditStringList.EditRedo(Item: TSynEditUndoItem);
|
||||
begin
|
||||
Item.PerformUndo(self);
|
||||
end;
|
||||
|
||||
procedure TSynEditStringList.SendNotification(AReason: TSynEditNotifyReason; ASender: TSynEditStrings; aIndex, aCount: Integer);
|
||||
begin
|
||||
case AReason of
|
||||
senrLineChange:
|
||||
FLineChangeNotificationList.CallRangeNotifyEvents(ASender, aIndex, aCount);
|
||||
senrLineCount:
|
||||
FLineRangeNotificationList.CallRangeNotifyEvents(ASender, aIndex, aCount);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.SetMaxUndoActions(Value: integer);
|
||||
begin
|
||||
if Value < 0 then
|
||||
Value := 0;
|
||||
if Value <> fMaxUndoActions then begin
|
||||
fMaxUndoActions := Value;
|
||||
EnsureMaxEntries;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSynEditUndoList.Unlock;
|
||||
begin
|
||||
if fLockCount > 0 then
|
||||
Dec(fLockCount);
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.IsLocked: Boolean;
|
||||
begin
|
||||
Result := fLockCount > 0;
|
||||
end;
|
||||
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
procedure TSynEditUndoList.MarkTopAsUnmodified;
|
||||
begin
|
||||
fUnModifiedItem:=fItems.Count;
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.IsTopMarkedAsUnmodified: boolean;
|
||||
begin
|
||||
Result:=(fItems.Count=fUnModifiedItem);
|
||||
end;
|
||||
|
||||
function TSynEditUndoList.UnModifiedMarkerExists: boolean;
|
||||
begin
|
||||
Result:=fUnModifiedItem>=0;
|
||||
end;
|
||||
|
||||
{$ENDIF}
|
||||
|
||||
{ TSynEditUndoItem }
|
||||
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
function TSynEditUndoItem.ChangeStartPos: TPoint;
|
||||
begin
|
||||
if (fChangeStartPos.Y < fChangeEndPos.Y)
|
||||
or ((fChangeStartPos.Y = fChangeEndPos.Y) and (fChangeStartPos.X < fChangeEndPos.X))
|
||||
then result := fChangeStartPos
|
||||
else result := fChangeEndPos;
|
||||
end;
|
||||
|
||||
function TSynEditUndoItem.ChangeEndPos: TPoint;
|
||||
begin
|
||||
if (fChangeStartPos.Y < fChangeEndPos.Y)
|
||||
or ((fChangeStartPos.Y = fChangeEndPos.Y) and (fChangeStartPos.X < fChangeEndPos.X))
|
||||
then result := fChangeEndPos
|
||||
else result := fChangeStartPos;
|
||||
end;
|
||||
{$ENDIF}
|
||||
|
||||
{ TSynEditStringMemory }
|
||||
type
|
||||
PObject = ^TObject;
|
||||
|
@ -29,7 +29,7 @@ interface
|
||||
uses
|
||||
LCLProc,
|
||||
Classes, SysUtils, SynEditTypes, SynEditTextBase, SynEditTextBuffer,
|
||||
SynEditPointClasses;
|
||||
SynEditPointClasses, SynEditMiscProcs;
|
||||
|
||||
type
|
||||
|
||||
@ -42,7 +42,6 @@ type
|
||||
fCaret: TSynEditCaret;
|
||||
FIsTrimming: Boolean;
|
||||
FTrimType: TSynEditStringTrimmingType;
|
||||
fUndoList: TSynEditUndoList;
|
||||
fSpaces: String;
|
||||
fLineText: String;
|
||||
fLineIndex: Integer;
|
||||
@ -55,9 +54,15 @@ type
|
||||
procedure SetEnabled(const AValue : Boolean);
|
||||
procedure SetTrimType(const AValue: TSynEditStringTrimmingType);
|
||||
function TrimLine(const S : String; Index: Integer; RealUndo: Boolean = False) : String;
|
||||
procedure StoreSpacesForLine(const Index: Integer; const SpaceStr, LineStr: String);
|
||||
function Spaces(Index: Integer) : String;
|
||||
procedure DoLinesChanged(Index, N: integer);
|
||||
procedure TrimAfterLock;
|
||||
procedure EditInsertTrim(LogX, LogY: Integer; AText: String);
|
||||
function EditDeleteTrim(LogX, LogY, ByteLen: Integer): String;
|
||||
procedure EditMoveToTrim(LogY, Len: Integer);
|
||||
procedure EditMoveFromTrim(LogY, Len: Integer);
|
||||
procedure UpdateLineText(LogY: Integer);
|
||||
protected
|
||||
function GetExpandedString(Index: integer): string; override;
|
||||
function GetLengthOfLongestLine: integer; override;
|
||||
@ -83,16 +88,184 @@ type
|
||||
procedure Lock;
|
||||
procedure UnLock;
|
||||
procedure ForceTrim; // for redo; redo can not wait for UnLock
|
||||
procedure UndoRealSpaces(Item: TSynEditUndoItem);
|
||||
property Enabled : Boolean read fEnabled write SetEnabled;
|
||||
property UndoTrimmedSpaces: Boolean read FUndoTrimmedSpaces write FUndoTrimmedSpaces;
|
||||
property UndoList: TSynEditUndoList read fUndoList write fUndoList;
|
||||
|
||||
property IsTrimming: Boolean read FIsTrimming;
|
||||
property TrimType: TSynEditStringTrimmingType read FTrimType write SetTrimType;
|
||||
public
|
||||
procedure EditInsert(LogX, LogY: Integer; AText: String); override;
|
||||
Function EditDelete(LogX, LogY, ByteLen: Integer): String; override;
|
||||
procedure EditLineBreak(LogX, LogY: Integer); override;
|
||||
procedure EditLineJoin(LogY: Integer; FillText: String = ''); override;
|
||||
procedure EditLinesInsert(LogY, ACount: Integer; AText: String = ''); override;
|
||||
procedure EditLinesDelete(LogY, ACount: Integer); override;
|
||||
procedure EditUndo(Item: TSynEditUndoItem); override;
|
||||
procedure EditRedo(Item: TSynEditUndoItem); override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
type
|
||||
|
||||
{ TSynEditUndoTrimMoveTo }
|
||||
|
||||
TSynEditUndoTrimMoveTo = class(TSynEditUndoItem)
|
||||
private
|
||||
FPosY, FLen: Integer;
|
||||
public
|
||||
constructor Create(APosY, ALen: Integer);
|
||||
function PerformUndo(Caller: TObject): Boolean; override;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTrimMoveFrom }
|
||||
|
||||
TSynEditUndoTrimMoveFrom = class(TSynEditUndoItem)
|
||||
private
|
||||
FPosY, FLen: Integer;
|
||||
public
|
||||
constructor Create(APosY, ALen: Integer);
|
||||
function PerformUndo(Caller: TObject): Boolean; override;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTrimInsert }
|
||||
|
||||
TSynEditUndoTrimInsert = class(TSynEditUndoItem)
|
||||
private
|
||||
FPosX, FPosY, FLen: Integer;
|
||||
public
|
||||
constructor Create(APosX, APosY, ALen: Integer);
|
||||
function PerformUndo(Caller: TObject): Boolean; override;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTrimDelete }
|
||||
|
||||
TSynEditUndoTrimDelete = class(TSynEditUndoItem)
|
||||
private
|
||||
FPosX, FPosY: Integer;
|
||||
FText: String;
|
||||
public
|
||||
constructor Create(APosX, APosY: Integer; AText: String);
|
||||
function PerformUndo(Caller: TObject): Boolean; override;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTrimForget }
|
||||
|
||||
TSynEditUndoTrimForget = class(TSynEditUndoItem)
|
||||
private
|
||||
FPosY: Integer;
|
||||
FText: String;
|
||||
public
|
||||
constructor Create(APosY: Integer; AText: String);
|
||||
function PerformUndo(Caller: TObject): Boolean; override;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTrimMoveTo }
|
||||
|
||||
constructor TSynEditUndoTrimMoveTo.Create(APosY, ALen: Integer);
|
||||
begin
|
||||
FPosY := APosY;
|
||||
FLen := ALen;
|
||||
end;
|
||||
|
||||
function TSynEditUndoTrimMoveTo.PerformUndo(Caller: TObject): Boolean;
|
||||
begin
|
||||
Result := Caller is TSynEditStringTrimmingList;
|
||||
if Result then
|
||||
with TSynEditStringTrimmingList(Caller) do begin
|
||||
EditMoveFromTrim(FPosY, FLen);
|
||||
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
|
||||
FPosY - 1, 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTrimMoveFrom }
|
||||
|
||||
constructor TSynEditUndoTrimMoveFrom.Create(APosY, ALen: Integer);
|
||||
begin
|
||||
FPosY := APosY;
|
||||
FLen := ALen;
|
||||
end;
|
||||
|
||||
function TSynEditUndoTrimMoveFrom.PerformUndo(Caller: TObject): Boolean;
|
||||
begin
|
||||
Result := Caller is TSynEditStringTrimmingList;
|
||||
if Result then
|
||||
with TSynEditStringTrimmingList(Caller) do begin
|
||||
EditMoveToTrim(FPosY, FLen);
|
||||
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
|
||||
FPosY - 1, 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTrimInsert }
|
||||
|
||||
constructor TSynEditUndoTrimInsert.Create(APosX, APosY, ALen: Integer);
|
||||
begin
|
||||
FPosX := APosX;
|
||||
FPosY := APosY;
|
||||
FLen := ALen;
|
||||
end;
|
||||
|
||||
function TSynEditUndoTrimInsert.PerformUndo(Caller: TObject): Boolean;
|
||||
begin
|
||||
Result := Caller is TSynEditStringTrimmingList;
|
||||
if Result then
|
||||
with TSynEditStringTrimmingList(Caller) do begin
|
||||
EditDeleteTrim(FPosX, FPosY, FLen);
|
||||
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
|
||||
FPosY - 1, 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTrimDelete }
|
||||
|
||||
constructor TSynEditUndoTrimDelete.Create(APosX, APosY: Integer; AText: String);
|
||||
begin
|
||||
FPosX := APosX;
|
||||
FPosY := APosY;
|
||||
FText := AText;
|
||||
end;
|
||||
|
||||
function TSynEditUndoTrimDelete.PerformUndo(Caller: TObject): Boolean;
|
||||
begin
|
||||
Result := Caller is TSynEditStringTrimmingList;
|
||||
if Result then
|
||||
with TSynEditStringTrimmingList(Caller) do begin
|
||||
EditInsertTrim(FPosX, FPosY, FText);
|
||||
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
|
||||
FPosY - 1, 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TSynEditUndoTrimForget }
|
||||
|
||||
constructor TSynEditUndoTrimForget.Create(APosY: Integer; AText: String);
|
||||
begin
|
||||
FPosY := APosY;
|
||||
FText := AText;
|
||||
end;
|
||||
|
||||
function TSynEditUndoTrimForget.PerformUndo(Caller: TObject): Boolean;
|
||||
begin
|
||||
Result := Caller is TSynEditStringTrimmingList;
|
||||
if Result then
|
||||
with TSynEditStringTrimmingList(Caller) do begin
|
||||
UndoList.Lock;
|
||||
EditInsertTrim(1, FPosY, FText);
|
||||
UndoList.Unlock;
|
||||
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
|
||||
FPosY - 1, 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
function LastNoneSpacePos(const s: String): Integer;
|
||||
begin
|
||||
Result := length(s);
|
||||
while (Result > 0) and (s[Result] in [#9, ' ']) do dec(Result);
|
||||
end;
|
||||
|
||||
{ TSynEditStringTrimmingList }
|
||||
|
||||
@ -138,8 +311,7 @@ begin
|
||||
s := fSynStrings[fLineIndex];
|
||||
fSynStrings[fLineIndex] := s; // trigger OnPutted, so the line gets repainted
|
||||
if (fLineIndex <> TSynEditCaret(Sender).LinePos - 1) then begin
|
||||
fUndoList.AppendToLastChange(crTrimSpace, Point(1+length(s), fLineIndex+1),
|
||||
Point(1+length(s)+length(fSpaces), fLineIndex+1), fSpaces, smNormal);
|
||||
UndoList.AppendToLastChange(TSynEditUndoTrimForget.Create(FLineIndex+1, FSpaces));
|
||||
fSpaces := '';
|
||||
end else begin
|
||||
// same line, only right of caret
|
||||
@ -150,8 +322,7 @@ begin
|
||||
j := i - length(s) - 1;
|
||||
s := copy(FSpaces, j + 1, MaxInt);
|
||||
FSpaces := copy(FSpaces, 1, j);
|
||||
fUndoList.AppendToLastChange(crTrimSpace, Point(i, fLineIndex+1),
|
||||
Point(i + length(s), fLineIndex+1), s, smNormal);
|
||||
UndoList.AppendToLastChange(TSynEditUndoTrimForget.Create(FLineIndex+1, s));
|
||||
end;
|
||||
FIsTrimming := False;
|
||||
FLineEdited := False;
|
||||
@ -180,21 +351,6 @@ begin
|
||||
FTrimType := AValue;
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.UndoRealSpaces(Item: TSynEditUndoItem);
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if (not fEnabled) then exit;
|
||||
if length(fSynStrings.Strings[Item.fChangeStartPos.y-1]) + 1 <> Item.fChangeStartPos.x then
|
||||
exit;
|
||||
fSynStrings.Strings[Item.fChangeStartPos.y-1]
|
||||
:= copy(fSynStrings.Strings[Item.fChangeStartPos.y-1],
|
||||
1, Item.fChangeStartPos.x-1) + Item.fChangeStr;
|
||||
if (fLineIndex = Item.fChangeStartPos.y-1) then fSpaces := '';
|
||||
i := fLockList.IndexOfObject(TObject(Pointer(Item.fChangeStartPos.y-1)));
|
||||
if i >= 0 then fLockList.Delete(i);
|
||||
end;
|
||||
|
||||
function TSynEditStringTrimmingList.TrimLine(const S: String; Index: Integer;
|
||||
RealUndo: Boolean = False): String;
|
||||
var
|
||||
@ -205,35 +361,40 @@ begin
|
||||
if RealUndo then begin
|
||||
temp := fSynStrings.Strings[Index];
|
||||
l := length(temp);
|
||||
i:= l;
|
||||
while (i>0) and (temp[i] in [#9, ' ']) do dec(i);
|
||||
i := LastNoneSpacePos(temp);
|
||||
// Add RealSpaceUndo
|
||||
if i < l then
|
||||
fUndoList.AddChange(crTrimRealSpace, Point(i+1, Index+1),
|
||||
Point(l, Index+1), copy(temp, i+1, l-i), smNormal);
|
||||
EditInsertTrim(1, Index + 1,
|
||||
inherited EditDelete(1 + i, Index + 1, l - i));
|
||||
end;
|
||||
|
||||
l := length(s);
|
||||
i := l;
|
||||
while (i>0) and (s[i] in [#9, ' ']) do dec(i);
|
||||
i := LastNoneSpacePos(s);
|
||||
temp := copy(s, i+1, l-i);
|
||||
if i=l then
|
||||
result := s // No need to make a copy
|
||||
else
|
||||
result := copy(s, 1, i);
|
||||
|
||||
StoreSpacesForLine(Index, temp, Result);
|
||||
end ;
|
||||
|
||||
procedure TSynEditStringTrimmingList.StoreSpacesForLine(const Index: Integer; const SpaceStr, LineStr: String);
|
||||
var
|
||||
i: LongInt;
|
||||
begin
|
||||
if fLockCount > 0 then begin
|
||||
i := fLockList.IndexOfObject(TObject(pointer(Index)));
|
||||
if i < 0 then
|
||||
fLockList.AddObject(temp, TObject(pointer(Index)))
|
||||
fLockList.AddObject(SpaceStr, TObject(pointer(Index)))
|
||||
else
|
||||
fLockList[i] := temp;
|
||||
end
|
||||
else if (fLineIndex = Index) then begin
|
||||
fSpaces := temp;
|
||||
fLineText:=result;
|
||||
fLockList[i] := SpaceStr;
|
||||
end;
|
||||
end ;
|
||||
if (fLineIndex = Index) then begin
|
||||
fSpaces := SpaceStr;
|
||||
fLineText:= LineStr;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TSynEditStringTrimmingList.Spaces(Index : Integer) : String;
|
||||
var
|
||||
@ -267,7 +428,7 @@ begin
|
||||
j := Integer(Pointer(fLockList.Objects[i]));
|
||||
if (j >= Index) and (j < Index - N) then
|
||||
fLockList.Delete(i)
|
||||
else if j > Index then
|
||||
else if j >= Index then
|
||||
fLockList.Objects[i] := TObject(Pointer(j + N));
|
||||
end;
|
||||
end else begin
|
||||
@ -295,7 +456,7 @@ end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.TrimAfterLock;
|
||||
var
|
||||
i, index, llen, slen: Integer;
|
||||
i, index, slen: Integer;
|
||||
ltext: String;
|
||||
begin
|
||||
if (not fEnabled) then exit;
|
||||
@ -314,10 +475,8 @@ begin
|
||||
slen := length(fLockList[i]);
|
||||
if (slen > 0) and (index >= 0) and (index < fSynStrings.Count) then begin
|
||||
ltext := fSynStrings[index];
|
||||
llen := length(ltext);
|
||||
fSynStrings[index] := ltext; // trigger OnPutted, so the line gets repainted
|
||||
fUndoList.AppendToLastChange(crTrimSpace, Point(1+llen, index+1),
|
||||
Point(1+llen+slen, index+1), fLockList[i], smNormal);
|
||||
UndoList.AppendToLastChange(TSynEditUndoTrimForget.Create(Index+1, fLockList[i]));
|
||||
end;
|
||||
end;
|
||||
FIsTrimming := False;
|
||||
@ -451,5 +610,247 @@ begin
|
||||
fLineIndex := Index1;
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.EditInsertTrim(LogX, LogY: Integer;
|
||||
AText: String);
|
||||
var
|
||||
s: string;
|
||||
begin
|
||||
if AText = '' then
|
||||
exit;
|
||||
s := Spaces(LogY - 1);
|
||||
StoreSpacesForLine(LogY - 1,
|
||||
copy(s,1, LogX - 1) + AText + copy(s, LogX, length(s)),
|
||||
fSynStrings.Strings[LogY - 1]);
|
||||
UndoList.AddChange(TSynEditUndoTrimInsert.Create(LogX, LogY, Length(AText)));
|
||||
end;
|
||||
|
||||
function TSynEditStringTrimmingList.EditDeleteTrim(LogX, LogY, ByteLen:
|
||||
Integer): String;
|
||||
var
|
||||
s: string;
|
||||
begin
|
||||
if ByteLen <= 0 then
|
||||
exit('');
|
||||
s := Spaces(LogY - 1);
|
||||
Result := copy(s, LogX, ByteLen);
|
||||
StoreSpacesForLine(LogY - 1,
|
||||
copy(s,1, LogX - 1) + copy(s, LogX + ByteLen, length(s)),
|
||||
fSynStrings.Strings[LogY - 1]);
|
||||
UndoList.AddChange(TSynEditUndoTrimDelete.Create(LogX, LogY, Result));
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.EditMoveToTrim(LogY, Len: Integer);
|
||||
var
|
||||
t, s: String;
|
||||
begin
|
||||
if Len <= 0 then
|
||||
exit;
|
||||
t := fSynStrings[LogY - 1];
|
||||
s := copy(t, 1 + length(t) - Len, Len) + Spaces(LogY - 1);
|
||||
t := copy(t, 1, length(t) - Len);
|
||||
fSynStrings[LogY - 1] := t;
|
||||
StoreSpacesForLine(LogY - 1, s, t);
|
||||
UndoList.AddChange(TSynEditUndoTrimMoveTo.Create(LogY, Len));
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.EditMoveFromTrim(LogY, Len: Integer);
|
||||
var
|
||||
t, s: String;
|
||||
begin
|
||||
if Len <= 0 then
|
||||
exit;
|
||||
s := Spaces(LogY - 1);
|
||||
t := fSynStrings[LogY - 1] + copy(s, 1, Len);
|
||||
s := copy(s, 1 + Len, Len);
|
||||
fSynStrings[LogY - 1] := t;
|
||||
StoreSpacesForLine(LogY - 1, s, t);
|
||||
UndoList.AddChange(TSynEditUndoTrimMoveFrom.Create(LogY, Len));
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.UpdateLineText(LogY: Integer);
|
||||
begin
|
||||
if LogY - 1 = fLineIndex then
|
||||
fLineText := fSynStrings[LogY - 1];
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.EditInsert(LogX, LogY: Integer; AText: String);
|
||||
var
|
||||
t: String;
|
||||
Len, LenNS: Integer;
|
||||
IsSpaces: Boolean;
|
||||
begin
|
||||
if (not fEnabled) then begin
|
||||
fSynStrings.EditInsert(LogX, LogY, AText);
|
||||
exit;
|
||||
end;
|
||||
|
||||
t := Strings[LogY - 1]; // include trailing
|
||||
if LogX - 1 > Length(t) then begin
|
||||
AText := StringOfChar(' ', LogX - 1 - Length(t)) + AText;
|
||||
LogX := 1 + Length(t);
|
||||
end;
|
||||
IsSpaces := LastNoneSpacePos(AText) = 0;
|
||||
t := fSynStrings[LogY - 1];
|
||||
Len := length(t);
|
||||
LenNS := LastNoneSpacePos(t);
|
||||
if (LenNS < LogX - 1) and not IsSpaces then
|
||||
LenNs := LogX - 1;
|
||||
|
||||
// Trim any existing (commited/real) spaces // skip if we append none-spaces
|
||||
if (LenNS < Len) and (IsSpaces or (LogX <= len)) then
|
||||
begin
|
||||
EditMoveToTrim(LogY, Len - LenNS);
|
||||
Len := LenNS;
|
||||
end;
|
||||
|
||||
if LogX > len then begin
|
||||
if IsSpaces then begin
|
||||
EditInsertTrim(LogX - Len, LogY, AText);
|
||||
AText := '';
|
||||
end else begin
|
||||
// Get Fill Spaces
|
||||
EditMoveFromTrim(LogY, LogX - 1 - len);
|
||||
// Trim
|
||||
Len := length(AText);
|
||||
LenNS := LastNoneSpacePos(AText);
|
||||
if LenNS < Len then begin
|
||||
EditInsertTrim(1, LogY, copy(AText, 1 + LenNS, Len));
|
||||
AText := copy(AText, 1, LenNS);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
if AText <> '' then
|
||||
inherited EditInsert(LogX, LogY, AText)
|
||||
else
|
||||
SendNotification(senrLineChange, self, LogY - 1, 1);
|
||||
|
||||
// update spaces
|
||||
UpdateLineText(LogY);
|
||||
end;
|
||||
|
||||
Function TSynEditStringTrimmingList.EditDelete(LogX, LogY, ByteLen
|
||||
: Integer): String;
|
||||
var
|
||||
t: String;
|
||||
Len: Integer;
|
||||
begin
|
||||
if (not fEnabled) then begin
|
||||
fSynStrings.EditDelete(LogX, LogY, ByteLen);
|
||||
exit;
|
||||
end;
|
||||
|
||||
Result := '';
|
||||
t := fSynStrings[LogY - 1];
|
||||
Len := length(t);
|
||||
|
||||
// Delete uncommited spaces
|
||||
if LogX + ByteLen > Len + 1 then begin
|
||||
if LogX > Len + 1 then
|
||||
ByteLen := ByteLen - (LogX - (Len + 1));
|
||||
Result := EditDeleteTrim(max(LogX - Len, 1), LogY, LogX - 1 + ByteLen - Len);
|
||||
ByteLen := Len + 1 - LogX;
|
||||
end;
|
||||
|
||||
if ByteLen > 0 then
|
||||
Result := inherited EditDelete(LogX, LogY, ByteLen) + Result
|
||||
else
|
||||
SendNotification(senrLineChange, self, LogY - 1, 1);
|
||||
UpdateLineText(LogY);
|
||||
|
||||
// Trim any existing (commited/real) spaces
|
||||
t := fSynStrings[LogY - 1];
|
||||
EditMoveToTrim(LogY, length(t) - LastNoneSpacePos(t));
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.EditLineBreak(LogX, LogY: Integer);
|
||||
var
|
||||
s, t: string;
|
||||
begin
|
||||
if (not fEnabled) then begin
|
||||
fSynStrings.EditLineBreak(LogX, LogY);
|
||||
exit;
|
||||
end;
|
||||
|
||||
s := Spaces(LogY - 1);
|
||||
t := fSynStrings[LogY - 1];
|
||||
if LogX > length(t) then begin
|
||||
fSynStrings.EditLineBreak(1 + length(t), LogY);
|
||||
if s <> '' then
|
||||
s := EditDeleteTrim(LogX - length(t), LogY, length(s) - (LogX - 1 - length(t)));
|
||||
end
|
||||
else begin
|
||||
s := EditDeleteTrim(1, LogY, length(s));
|
||||
fSynStrings.EditLineBreak(LogX, LogY);
|
||||
end;
|
||||
DoLinesChanged(LogY, 1);
|
||||
UpdateLineText(LogY + 1);
|
||||
EditInsertTrim(1, LogY + 1, s);
|
||||
// Trim any existing (commited/real) spaces
|
||||
s := fSynStrings[LogY - 1];
|
||||
EditMoveToTrim(LogY, length(s) - LastNoneSpacePos(s));
|
||||
s := fSynStrings[LogY];
|
||||
EditMoveToTrim(LogY + 1, length(s) - LastNoneSpacePos(s));
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.EditLineJoin(LogY: Integer;
|
||||
FillText: String = '');
|
||||
var
|
||||
s: String;
|
||||
begin
|
||||
if (not fEnabled) then begin
|
||||
fSynStrings.EditLineJoin(LogY, FillText);
|
||||
exit;
|
||||
end;
|
||||
|
||||
EditMoveFromTrim(LogY, length(Spaces(LogY - 1)));
|
||||
|
||||
s := EditDeleteTrim(1, LogY + 1, length(Spaces(LogY))); // next line
|
||||
//Todo: if FillText isSpacesOnly AND NextLineIsSpacesOnly => add direct to trailing
|
||||
fSynStrings.EditLineJoin(LogY, FillText);
|
||||
DoLinesChanged(LogY - 1, -1);
|
||||
UpdateLineText(LogY);
|
||||
EditInsertTrim(1, LogY, s);
|
||||
|
||||
// Trim any existing (commited/real) spaces
|
||||
s := fSynStrings[LogY - 1];
|
||||
EditMoveToTrim(LogY, length(s) - LastNoneSpacePos(s));
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.EditLinesInsert(LogY, ACount: Integer;
|
||||
AText: String = '');
|
||||
var
|
||||
s: string;
|
||||
begin
|
||||
fSynStrings.EditLinesInsert(LogY, ACount, AText);
|
||||
s := fSynStrings[LogY - 1];
|
||||
EditMoveToTrim(LogY, length(s) - LastNoneSpacePos(s));
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.EditLinesDelete(LogY, ACount: Integer);
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
for i := LogY to LogY + ACount - 1 do
|
||||
EditMoveFromTrim(i, length(Spaces(i - 1)));
|
||||
fSynStrings.EditLinesDelete(LogY, ACount);
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.EditUndo(Item: TSynEditUndoItem);
|
||||
begin
|
||||
IsUndoing := True;
|
||||
try
|
||||
EditRedo(Item);
|
||||
finally
|
||||
IsUndoing := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.EditRedo(Item: TSynEditUndoItem);
|
||||
begin
|
||||
if not Item.PerformUndo(self) then
|
||||
inherited EditRedo(Item);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
@ -62,7 +62,7 @@ type
|
||||
// to be binary (clipboard) compatible with other (Delphi compiled) synedits
|
||||
// use {$PACKENUM 1}
|
||||
{$IFDEF SYN_LAZARUS}{$PACKENUM 1}{$ENDIF SYN_LAZARUS}
|
||||
TSynSelectionMode = (smNormal, smLine, smColumn);
|
||||
TSynSelectionMode = (smNormal, smLine, smColumn, smCurrent);
|
||||
{$IFDEF SYN_LAZARUS}{$PACKENUM 4}{$ENDIF SYN_LAZARUS}
|
||||
|
||||
TSynSearchOption = (ssoMatchCase, ssoWholeWord, ssoBackwards,
|
||||
|
Loading…
Reference in New Issue
Block a user