lazarus/components/synedit/synedittexttrimmer.pas
2015-03-24 17:50:59 +00:00

1386 lines
43 KiB
ObjectPascal

{-------------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.
Alternatively, the contents of this file may be used under the terms of the
GNU General Public License Version 2 or later (the "GPL"), in which case
the provisions of the GPL are applicable instead of those above.
If you wish to allow use of your version of this file only under the terms
of the GPL and not to allow others to use your version of this file
under the MPL, indicate your decision by deleting the provisions above and
replace them with the notice and other provisions required by the GPL.
If you do not delete the provisions above, a recipient may use your version
of this file under either the MPL or the GPL.
-------------------------------------------------------------------------------}
unit SynEditTextTrimmer;
{$I synedit.inc}
interface
uses
LCLProc,
Classes, SysUtils, LazSynEditText, SynEditTextBase, SynEditTypes, SynEditHighlighter,
SynEditPointClasses, SynEditMiscProcs;
type
TSynEditStringTrimmingType = (settLeaveLine, settEditLine, settMoveCaret,
settIgnoreAll);
TSynEditStringTrimmingList = class;
{ TLazSynDisplayTrim }
TLazSynDisplayTrim = class(TLazSynDisplayViewEx)
private
FTrimer: TSynEditStringTrimmingList;
FTempLineStringForPChar: String;
FAtLineStart: Boolean;
public
constructor Create(ATrimer: TSynEditStringTrimmingList);
procedure FinishHighlighterTokens; override;
procedure SetHighlighterTokensLine(ALine: TLineIdx; out ARealLine: TLineIdx); override;
function GetNextHighlighterToken(out ATokenInfo: TLazSynDisplayTokenInfo): Boolean; override;
end;
TSynEditTrimSpaceListEntry = record
LineIndex: Integer;
TrimmedSpaces: String;
end;
{ TSynEditTrimSpaceList }
TSynEditTrimSpaceList = object
private
FCount: Integer;
protected
Procedure Grow;
public
Entries: array of TSynEditTrimSpaceListEntry;
property Count: Integer read FCount;
procedure Clear;
procedure Add(ALineIdx: Integer; ASpaces: String);
procedure Delete(AEntryIdx: Integer);
function IndexOf(ALineIdx: Integer): Integer;
end;
{ TSynEditStringTrimmingList }
TSynEditStringTrimmingList = class(TSynEditStringsLinked)
private
fCaret: TSynEditCaret;
FIsTrimming: Boolean;
FTrimType: TSynEditStringTrimmingType;
fSpaces: String;
fLineText: String;
fLineIndex: Integer;
fEnabled: Boolean;
FUndoTrimmedSpaces: Boolean;
fLockCount: Integer;
fLockList : TSynEditTrimSpaceList;
FLineEdited: Boolean;
FTempLineStringForPChar: String; // experimental; used by GetPChar;
FViewChangeStamp: int64;
FDisplayView: TLazSynDisplayTrim;
procedure MaybeAddUndoForget(APosY: Integer; AText: String);
procedure DoCaretChanged(Sender : TObject);
procedure ListCleared(Sender: TObject);
Procedure LinesChanged(Sender: TSynEditStrings; AIndex, ACount : Integer);
Procedure LineCountChanged(Sender: TSynEditStrings; AIndex, ACount : Integer);
procedure DoLinesChanged(Index, N: integer);
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 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);
procedure IncViewChangeStamp;
protected
function GetViewChangeStamp: int64; override;
function GetExpandedString(Index: integer): string; override;
function GetLengthOfLongestLine: integer; override;
function Get(Index: integer): string; override;
function GetObject(Index: integer): TObject; override;
procedure Put(Index: integer; const S: string); override;
procedure PutObject(Index: integer; AObject: TObject); override;
function GetPCharSpaces(ALineIndex: Integer; out ALen: Integer): PChar; // experimental
function GetDisplayView: TLazSynDisplayView; override;
public
constructor Create(ASynStringSource: TSynEditStrings; ACaret: TSynEditCaret);
destructor Destroy; override;
function Add(const S: string): integer; override;
procedure AddStrings(AStrings: TStrings); override;
procedure Clear; override;
procedure Delete(Index: integer); override;
procedure DeleteLines(Index, NumLines: integer); override;
procedure Insert(Index: integer; const S: string); override;
procedure InsertLines(Index, NumLines: integer); override;
procedure InsertStrings(Index: integer; NewStrings: TStrings); override;
function GetPChar(ALineIndex: Integer; out ALen: Integer): PChar; override; // experimental
procedure Exchange(Index1, Index2: integer); override;
property LengthOfLongestLine: integer read GetLengthOfLongestLine;
public
procedure Lock;
procedure UnLock;
procedure ForceTrim; // for redo; redo can not wait for UnLock
property Enabled : Boolean read fEnabled write SetEnabled;
property UndoTrimmedSpaces: Boolean read FUndoTrimmedSpaces write FUndoTrimmedSpaces; // deprecated 'not implemented';
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;
function EditReplace(LogX, LogY, ByteLen: Integer; AText: String): 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
{off $Define SynTrimUndoDebug}
{off $Define SynTrimDebug}
{$IFDEF SynUndoDebug}
{$Define SynUndoDebugItems}
{$Define SynTrimUndoDebug}
{$ENDIF}
type
{ TSynEditUndoTrimMoveTo }
TSynEditUndoTrimMoveTo = class(TSynEditUndoItem)
private
FPosY, FLen: Integer;
protected
function DebugString: String; override;
public
constructor Create(APosY, ALen: Integer);
function PerformUndo(Caller: TObject): Boolean; override;
end;
{ TSynEditUndoTrimMoveFrom }
TSynEditUndoTrimMoveFrom = class(TSynEditUndoItem)
private
FPosY, FLen: Integer;
protected
function DebugString: String; override;
public
constructor Create(APosY, ALen: Integer);
function PerformUndo(Caller: TObject): Boolean; override;
end;
{ TSynEditUndoTrimInsert }
TSynEditUndoTrimInsert = class(TSynEditUndoItem)
private
FPosX, FPosY, FLen: Integer;
protected
function DebugString: String; override;
public
constructor Create(APosX, APosY, ALen: Integer);
function PerformUndo(Caller: TObject): Boolean; override;
end;
{ TSynEditUndoTrimDelete }
TSynEditUndoTrimDelete = class(TSynEditUndoItem)
private
FPosX, FPosY: Integer;
FText: String;
protected
function DebugString: String; override;
public
constructor Create(APosX, APosY: Integer; AText: String);
function PerformUndo(Caller: TObject): Boolean; override;
end;
{ TSynEditUndoTrimForget }
TSynEditUndoTrimForget = class(TSynEditUndoItem)
private
FPosY: Integer;
FText: String;
protected
function DebugString: String; override;
public
constructor Create(APosY: Integer; AText: String);
function PerformUndo(Caller: TObject): Boolean; override;
end;
{ TSynEditTrimSpaceList }
procedure TSynEditTrimSpaceList.Grow;
var
l: Integer;
begin
l := Length(Entries);
if l < 16
then l := 32
else l := l * 2;
SetLength(Entries, l);
end;
procedure TSynEditTrimSpaceList.Clear;
begin
SetLength(Entries, 0);
FCount := 0;
end;
procedure TSynEditTrimSpaceList.Add(ALineIdx: Integer; ASpaces: String);
var
l, h, m: Integer;
begin
if FCount = Length(Entries) then
Grow;
l := 0;
h := FCount - 1;
while h > l do begin
m := (h + l) div 2;
if ALineIdx <= Entries[m].LineIndex
then h := m
else l := m + 1;
end;
if (FCount > 0) and (ALineIdx >= Entries[l].LineIndex) then
inc(l);
if l < FCount then begin
Entries[FCount].TrimmedSpaces := '';
Move(Entries[l], Entries[l+1], (FCount-l)*SizeOf(Entries[0]));
Pointer(Entries[l].TrimmedSpaces) := nil;
end;
Entries[l].LineIndex := ALineIdx;
Entries[l].TrimmedSpaces := ASpaces;
inc(FCount);
end;
procedure TSynEditTrimSpaceList.Delete(AEntryIdx: Integer);
begin
Assert((AEntryIdx >= 0) and (AEntryIdx < FCount), 'TSynEditTrimSpaceList.Delete index');
Entries[AEntryIdx].TrimmedSpaces := '';
dec(FCount);
if AEntryIdx < FCount then begin
Move(Entries[AEntryIdx+1], Entries[AEntryIdx], (FCount-AEntryIdx)*SizeOf(Entries[0]));
Pointer(Entries[FCount].TrimmedSpaces) := nil;
end;
end;
function TSynEditTrimSpaceList.IndexOf(ALineIdx: Integer): Integer;
var
l, h, m: Integer;
begin
if FCount <= 0 then
exit(-1);
l := 0;
h := FCount - 1;
while h > l do begin
m := (h + l) div 2;
if ALineIdx <= Entries[m].LineIndex
then h := m
else l := m + 1;
end;
if ALineIdx = Entries[l].LineIndex
then Result := l
else Result := -1;
end;
{ TLazSynDisplayTrim }
constructor TLazSynDisplayTrim.Create(ATrimer: TSynEditStringTrimmingList);
begin
inherited Create;
FTrimer := ATrimer;
end;
procedure TLazSynDisplayTrim.FinishHighlighterTokens;
begin
inherited FinishHighlighterTokens;
FTempLineStringForPChar := '';
end;
procedure TLazSynDisplayTrim.SetHighlighterTokensLine(ALine: TLineIdx; out ARealLine: TLineIdx);
begin
CurrentTokenLine := ALine;
FAtLineStart := True;
inherited SetHighlighterTokensLine(ALine, ARealLine);
end;
function TLazSynDisplayTrim.GetNextHighlighterToken(out ATokenInfo: TLazSynDisplayTokenInfo): Boolean;
begin
Result := False;
if not Initialized then exit;
if (CurrentTokenHighlighter = nil) and (FTrimer.Spaces(CurrentTokenLine) <> '') then begin
Result := FAtLineStart;
if not Result then exit;
FTempLineStringForPChar := FTrimer[CurrentTokenLine];
ATokenInfo.TokenStart := PChar(FTempLineStringForPChar);
ATokenInfo.TokenLength := length(FTempLineStringForPChar);
ATokenInfo.TokenAttr := nil;
FAtLineStart := False;
exit;
end;
// highlighter currently includes trimed spaces
Result := inherited GetNextHighlighterToken(ATokenInfo);
end;
{ TSynEditUndoTrimMoveTo }
function TSynEditUndoTrimMoveTo.DebugString: String;
begin
Result := 'FPosY='+IntToStr(FPosY)+' FLen='+IntToStr(FLen);
end;
constructor TSynEditUndoTrimMoveTo.Create(APosY, ALen: Integer);
begin
FPosY := APosY;
FLen := ALen;
{$IFDEF SynTrimUndoDebug}debugln(['--- Trimmer Undo Insert ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
end;
function TSynEditUndoTrimMoveTo.PerformUndo(Caller: TObject): Boolean;
begin
Result := Caller is TSynEditStringTrimmingList;
if Result then begin
{$IFDEF SynTrimUndoDebug}debugln(['--- Trimmer Undo Perform ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
with TSynEditStringTrimmingList(Caller) do begin
EditMoveFromTrim(FPosY, FLen);
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
FPosY - 1, 1);
end;
end;
end;
{ TSynEditUndoTrimMoveFrom }
function TSynEditUndoTrimMoveFrom.DebugString: String;
begin
Result := 'FPosY='+IntToStr(FPosY)+' FLen='+IntToStr(FLen);
end;
constructor TSynEditUndoTrimMoveFrom.Create(APosY, ALen: Integer);
begin
FPosY := APosY;
FLen := ALen;
{$IFDEF SynTrimUndoDebug}debugln(['--- Trimmer Undo Insert ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
end;
function TSynEditUndoTrimMoveFrom.PerformUndo(Caller: TObject): Boolean;
begin
Result := Caller is TSynEditStringTrimmingList;
if Result then begin
{$IFDEF SynTrimUndoDebug}debugln(['--- Trimmer Undo Perform ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
with TSynEditStringTrimmingList(Caller) do begin
EditMoveToTrim(FPosY, FLen);
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
FPosY - 1, 1);
end;
end;
end;
{ TSynEditUndoTrimInsert }
function TSynEditUndoTrimInsert.DebugString: String;
begin
Result := 'FPosY='+IntToStr(FPosY)+' FPosX='+IntToStr(FPosX)+' FLen='+IntToStr(FLen);
end;
constructor TSynEditUndoTrimInsert.Create(APosX, APosY, ALen: Integer);
begin
FPosX := APosX;
FPosY := APosY;
FLen := ALen;
{$IFDEF SynTrimUndoDebug}debugln(['--- Trimmer Undo Insert ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
end;
function TSynEditUndoTrimInsert.PerformUndo(Caller: TObject): Boolean;
begin
Result := Caller is TSynEditStringTrimmingList;
if Result then begin
{$IFDEF SynTrimUndoDebug}debugln(['--- Trimmer Undo Perform ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
with TSynEditStringTrimmingList(Caller) do begin
EditDeleteTrim(FPosX, FPosY, FLen);
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
FPosY - 1, 1);
SendNotification(senrEditAction, TSynEditStringTrimmingList(Caller),
FPosY, 0, length(fSynStrings[FPosY-1]) + FPosX - 1, -FLen, '');
end;
end;
end;
{ TSynEditUndoTrimDelete }
function TSynEditUndoTrimDelete.DebugString: String;
begin
Result := 'FPosY='+IntToStr(FPosY)+' FPosX='+IntToStr(FPosX)+' FText="'+FText+'"';
end;
constructor TSynEditUndoTrimDelete.Create(APosX, APosY: Integer; AText: String);
begin
FPosX := APosX;
FPosY := APosY;
FText := AText;
{$IFDEF SynTrimUndoDebug}debugln(['--- Trimmer Undo Insert ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
end;
function TSynEditUndoTrimDelete.PerformUndo(Caller: TObject): Boolean;
begin
Result := Caller is TSynEditStringTrimmingList;
if Result then begin
{$IFDEF SynTrimUndoDebug}debugln(['--- Trimmer Undo Perform ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
with TSynEditStringTrimmingList(Caller) do begin
EditInsertTrim(FPosX, FPosY, FText);
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
FPosY - 1, 1);
SendNotification(senrEditAction, TSynEditStringTrimmingList(Caller),
FPosY, 0, length(fSynStrings[FPosY-1]) + FPosX - 1, length(FText), FText);
end;
end;
end;
{ TSynEditUndoTrimForget }
function TSynEditUndoTrimForget.DebugString: String;
begin
Result := 'FPosY='+IntToStr(FPosY)+' FText="'+FText+'"';
end;
constructor TSynEditUndoTrimForget.Create(APosY: Integer; AText: String);
begin
FPosY := APosY;
FText := AText;
{$IFDEF SynTrimUndoDebug}debugln(['--- Trimmer Undo Insert ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
end;
function TSynEditUndoTrimForget.PerformUndo(Caller: TObject): Boolean;
begin
Result := Caller is TSynEditStringTrimmingList;
if Result then begin
{$IFDEF SynTrimUndoDebug}debugln(['--- Trimmer Undo Perform ',DbgSName(self), ' ', dbgs(Self), ' - ', DebugString]);{$ENDIF}
with TSynEditStringTrimmingList(Caller) do begin
CurUndoList.Lock;
EditInsertTrim(1, FPosY, FText);
CurUndoList.Unlock;
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
FPosY - 1, 1);
SendNotification(senrEditAction, TSynEditStringTrimmingList(Caller),
FPosY, 0, length(fSynStrings[FPosY-1]), length(FText), FText);
end;
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 }
constructor TSynEditStringTrimmingList.Create(ASynStringSource : TSynEditStrings; ACaret: TSynEditCaret);
begin
fCaret := ACaret;
fCaret.AddChangeHandler(@DoCaretChanged);
//fLockList := TSynEditTrimSpaceList.Create;
fLockList.Clear;
FDisplayView := TLazSynDisplayTrim.Create(Self);
FDisplayView.NextView := ASynStringSource.DisplayView;
fLineIndex:= -1;
fSpaces := '';
fEnabled:=false;
FUndoTrimmedSpaces := False;
FIsTrimming := False;
FLineEdited := False;
FTrimType := settLeaveLine;
Inherited Create(ASynStringSource);
fSynStrings.AddChangeHandler(senrLineCount, @LineCountChanged);
fSynStrings.AddChangeHandler(senrLineChange, @LinesChanged);
fSynStrings.AddNotifyHandler(senrCleared, @ListCleared);
end;
destructor TSynEditStringTrimmingList.Destroy;
begin
fSynStrings.RemoveChangeHandler(senrLineCount, @LineCountChanged);
fSynStrings.RemoveChangeHandler(senrLineChange, @LinesChanged);
fSynStrings.RemoveNotifyHandler(senrCleared, @ListCleared);
fCaret.RemoveChangeHandler(@DoCaretChanged);
FreeAndNil(FDisplayView);
//FreeAndNil(fLockList);
inherited Destroy;
end;
procedure TSynEditStringTrimmingList.MaybeAddUndoForget(APosY: Integer; AText: String);
var
L: TSynEditUndoItem;
begin
if (FTrimType = settIgnoreAll) then begin
L := CurUndoList.GetLastChange;
if (L <> nil) and (L is TSynEditUndoTrimInsert) and
(TSynEditUndoTrimInsert(L).FPosY = APosY)
then begin
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- MaybeAddUndoForget - removing last undo']);{$ENDIF}
CurUndoList.PopLastChange.Free;
exit;
end;
end;
CurUndoList.AppendToLastChange(TSynEditUndoTrimForget.Create(APosY, AText));
end;
procedure TSynEditStringTrimmingList.DoCaretChanged(Sender : TObject);
var
s: String;
i, j: Integer;
begin
if (not fEnabled) then exit;
if (fLockCount > 0) or (length(fSpaces) = 0) or
(fLineIndex < 0) or (fLineIndex >= fSynStrings.Count) or
( (fLineIndex = TSynEditCaret(Sender).LinePos - 1) and
( (FTrimType in [settLeaveLine]) or
((FTrimType in [settEditLine]) and not FLineEdited) ))
then begin
if (fLineIndex <> TSynEditCaret(Sender).LinePos - 1) then begin
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- CaretChnaged - Clearing 1 ', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), 'newCaretYPos=',TSynEditCaret(Sender).LinePos]);{$ENDIF}
if fSpaces <> '' then IncViewChangeStamp;
fLineIndex := TSynEditCaret(Sender).LinePos - 1;
fSpaces := '';
end;
exit;
end;
FIsTrimming := True;
IncViewChangeStamp;
if (fLineIndex <> TSynEditCaret(Sender).LinePos - 1) or
(FTrimType = settIgnoreAll) then
begin
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- CaretChnaged - Trimming,clear 1 ', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), 'newCaretYPos=',TSynEditCaret(Sender).LinePos]);{$ENDIF}
MaybeAddUndoForget(FLineIndex+1, FSpaces);
i := length(FSpaces);
fSpaces := '';
TSynEditCaret(Sender).InvalidateBytePos; // tabs at EOL may now be spaces
SendNotification(senrLineChange, self, fLineIndex, 1);
SendNotification(senrEditAction, self, FLineIndex+1, 0,
1+length(fSynStrings[FLineIndex]), -i, '');
end else begin
// same line, only right of caret
s := fSynStrings[fLineIndex];
i := TSynEditCaret(Sender).BytePos;
if i <= length(s) + 1 then
j := 0
else
j := i - length(s) - 1;
s := copy(FSpaces, j + 1, MaxInt);
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- CarteChnaged - Trimming,part to ',length(s),' ', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), 'newCaretYPos=',TSynEditCaret(Sender).LinePos]);{$ENDIF}
FSpaces := copy(FSpaces, 1, j);
i := length(s);
MaybeAddUndoForget(FLineIndex+1, s);
SendNotification(senrLineChange, self, fLineIndex, 1);
SendNotification(senrEditAction, self, FLineIndex+1, 0,
1+length(fSynStrings[FLineIndex]) + length(FSpaces), -i, '');
end;
FIsTrimming := False;
FLineEdited := False;
fLineIndex := TSynEditCaret(Sender).LinePos - 1;
end;
procedure TSynEditStringTrimmingList.ListCleared(Sender: TObject);
begin
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- LIST CLEARED ', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces) ]);{$ENDIF}
if fSpaces <> '' then IncViewChangeStamp;
fLockList.Clear;
fLineIndex:= -1;
fSpaces := '';
end;
procedure TSynEditStringTrimmingList.LinesChanged(Sender: TSynEditStrings; AIndex, ACount: Integer);
begin
if FIsTrimming then
exit;
FLineEdited := true;
if fLockCount = 0 then
DoCaretChanged(fCaret);
end;
procedure TSynEditStringTrimmingList.LineCountChanged(Sender: TSynEditStrings;
AIndex, ACount: Integer);
begin
DoLinesChanged(AIndex, ACount);
LinesChanged(Sender, AIndex, ACount);
end;
procedure TSynEditStringTrimmingList.DoLinesChanged(Index, N : integer);
var
i, j: Integer;
begin
if (not fEnabled) then exit;
IncViewChangeStamp;
if fLockCount > 0 then begin
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- Lines Changed (ins/del) locked ', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces) ]);{$ENDIF}
for i := fLockList.Count-1 downto 0 do begin
j := fLockList.Entries[i].LineIndex;
if (j >= Index) and (j < Index - N) then
fLockList.Delete(i)
else if j >= Index then
fLockList.Entries[i].LineIndex := j + N;
end;
end else begin
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- Lines Changed (ins/del) not locked ', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces) ]);{$ENDIF}
if (fLineIndex >= Index) and (fLineIndex < Index - N) then
fLineIndex:=-1
else if fLineIndex > Index then
inc(fLineIndex, N);
end;
end;
procedure TSynEditStringTrimmingList.SetEnabled(const AValue : Boolean);
begin
if fEnabled = AValue then exit;
fEnabled:=AValue;
fLockList.Clear;
fLockCount:=0;
FSpaces := '';
FLineIndex := -1;
FLockList.Clear;
FIsTrimming := True;
FLineEdited := False;
if fEnabled and (fLineIndex >= 0) and (fLineIndex < fSynStrings.Count) then
fSynStrings[fLineIndex] := TrimLine(fSynStrings[fLineIndex], fLineIndex);
FIsTrimming := False;
end;
procedure TSynEditStringTrimmingList.SetTrimType(const AValue: TSynEditStringTrimmingType);
begin
if FTrimType = AValue then exit;
FTrimType := AValue;
end;
function TSynEditStringTrimmingList.TrimLine(const S: String; Index: Integer;
RealUndo: Boolean = False): String;
var
l, i:integer;
temp: String;
begin
if (not fEnabled) then exit(s);
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- TrimLine ', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), ' RealUndo=', RealUndo ]);{$ENDIF}
if RealUndo then begin
temp := fSynStrings.Strings[Index];
l := length(temp);
i := LastNoneSpacePos(temp);
// Add RealSpaceUndo
if i < l then
EditInsertTrim(1, Index + 1,
inherited EditDelete(1 + i, Index + 1, l - i));
end;
l := length(s);
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
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- StoreSpacesforLine ', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), ' Index=', Index, ' Spacestr=',length(SpaceStr), ' LineStr=',length(LineStr), ' fLockCount=',fLockCount]);{$ENDIF}
if fLockCount > 0 then begin
i := fLockList.IndexOf(Index);
if i < 0 then
fLockList.Add(Index, SpaceStr)
else
fLockList.Entries[i].TrimmedSpaces := SpaceStr;
end;
if (fLineIndex = Index) then begin
fSpaces := SpaceStr;
fLineText:= LineStr;
end;
end;
function TSynEditStringTrimmingList.Spaces(Index : Integer) : String;
var
i : Integer;
begin
if (not fEnabled) then exit('');
if fLockCount > 0 then begin
i := fLockList.IndexOf(Index);
if i < 0 then
result := ''
else
result := fLockList.Entries[i].TrimmedSpaces;
//{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- Spaces (for line / locked)', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), ' Index=', Index, ' Result=',length(Result)]);{$ENDIF}
exit;
end;
if Index <> fLineIndex then exit('');
if (fLineIndex < 0) or (fLineIndex >= fSynStrings.Count)
or (fLineText <> fSynStrings[fLineIndex]) then begin
if fSpaces <> '' then IncViewChangeStamp;
fSpaces:='';
fLineText:='';
end;
Result:= fSpaces;
{$IFDEF SynTrimDebug}if length(Result) > 0 then debugln(['--- Trimmer -- Spaces (for line / not locked)', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), ' Index=', Index, ' Result=',length(Result)]);{$ENDIF}
end;
procedure TSynEditStringTrimmingList.Lock;
begin
if (fLockCount = 0) and (fLineIndex >= 0) and Enabled then begin
fLockList.Add(fLineIndex, Spaces(fLineIndex));
FLineEdited := False;
end;
inc(fLockCount);
end;
procedure TSynEditStringTrimmingList.UnLock;
begin
dec(fLockCount);
if (fLockCount = 0) then TrimAfterLock;
if (FTrimType = settIgnoreAll) then DoCaretChanged(fCaret);
end;
procedure TSynEditStringTrimmingList.TrimAfterLock;
var
i, index, slen: Integer;
ltext: String;
begin
if (not fEnabled) then exit;
FIsTrimming := True;
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- TrimAfterLock', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), ' Index=', Index, ' LockList=',fLockList.CommaText]);{$ENDIF}
i := fLockList.IndexOf(fLineIndex);
if i >= 0 then begin
if fSpaces <> fLockList.Entries[i].TrimmedSpaces then
IncViewChangeStamp;
fSpaces:= fLockList.Entries[i].TrimmedSpaces;
if (fLineIndex >= 0) and (fLineIndex < fSynStrings.Count) then
fLineText := fSynStrings[fLineIndex];
fLockList.Delete(i);
DoCaretChanged(fCaret);
end
else if fSpaces <> '' then
IncViewChangeStamp;
FIsTrimming := True;
BeginUpdate;
if fLockList.Count > 0 then
IncViewChangeStamp;
try
for i := 0 to fLockList.Count-1 do begin
index := fLockList.Entries[i].LineIndex;
slen := length(fLockList.Entries[i].TrimmedSpaces);
if (slen > 0) and (index >= 0) and (index < fSynStrings.Count) then begin
ltext := fSynStrings[index];
// TODO: Avoid triggering the highlighter
fSynStrings[index] := ltext; // trigger OnPutted, so the line gets repainted
MaybeAddUndoForget(Index+1, fLockList.Entries[i].TrimmedSpaces);
end;
end;
finally
EndUpdate;
FIsTrimming := False;
end;
FLineEdited := False;
fLockList.Clear;
end;
procedure TSynEditStringTrimmingList.ForceTrim;
begin
FlushNotificationCache;
DoCaretChanged(fCaret); // Caret May be locked
TrimAfterLock;
end;
// Lines
function TSynEditStringTrimmingList.GetExpandedString(Index : integer) : string;
begin
Result:= fSynStrings.ExpandedStrings[Index] + Spaces(Index);
end;
function TSynEditStringTrimmingList.GetLengthOfLongestLine : integer;
var
i: Integer;
begin
Result:= fSynStrings.LengthOfLongestLine;
if (fLineIndex >= 0) and (fLineIndex < Count) then begin
i:= length(ExpandedStrings[fLineIndex]);
if (i > Result) then Result := i;
end;
end;
function TSynEditStringTrimmingList.Get(Index : integer) : string;
begin
Result:= fSynStrings.Strings[Index] + Spaces(Index);
end;
function TSynEditStringTrimmingList.GetObject(Index : integer) : TObject;
begin
Result:= fSynStrings.Objects[Index];
end;
procedure TSynEditStringTrimmingList.Put(Index : integer; const S : string);
begin
FLineEdited := True;
fSynStrings.Strings[Index]:= TrimLine(S, Index, True);
end;
procedure TSynEditStringTrimmingList.PutObject(Index : integer; AObject : TObject);
begin
FLineEdited := True;
fSynStrings.Objects[Index]:= AObject;
end;
function TSynEditStringTrimmingList.Add(const S : string) : integer;
var
c : Integer;
begin
FLineEdited := True;
c := fSynStrings.Count;
Result := fSynStrings.Add(TrimLine(S, c));
end;
procedure TSynEditStringTrimmingList.AddStrings(AStrings : TStrings);
var
i, c : Integer;
begin
c := fSynStrings.Count;
for i := 0 to AStrings.Count-1 do
AStrings[i] := TrimLine(AStrings[i], c + i);
fSynStrings.AddStrings(AStrings);
end;
procedure TSynEditStringTrimmingList.Clear;
begin
fSynStrings.Clear;
fLineIndex:=-1;
end;
procedure TSynEditStringTrimmingList.Delete(Index : integer);
begin
FLineEdited := True;
TrimLine('', Index, True);
fSynStrings.Delete(Index);
end;
procedure TSynEditStringTrimmingList.DeleteLines(Index, NumLines : integer);
var
i: Integer;
begin
FLineEdited := True;
for i := 0 to NumLines-1 do
TrimLine('', Index+i, True);
fSynStrings.DeleteLines(Index, NumLines);
end;
procedure TSynEditStringTrimmingList.Insert(Index : integer; const S : string);
begin
FLineEdited := True;
fSynStrings.Insert(Index, TrimLine(S, Index));
end;
procedure TSynEditStringTrimmingList.InsertLines(Index, NumLines : integer);
begin
FLineEdited := True;
fSynStrings.InsertLines(Index, NumLines);
end;
procedure TSynEditStringTrimmingList.InsertStrings(Index : integer; NewStrings : TStrings);
var
i : Integer;
begin
FLineEdited := True;
for i := 0 to NewStrings.Count-1 do
NewStrings[i] := TrimLine(NewStrings[i], Index+i, True);
fSynStrings.InsertStrings(Index, NewStrings);
end;
function TSynEditStringTrimmingList.GetPCharSpaces(ALineIndex: Integer; out
ALen: Integer): PChar;
begin
FTempLineStringForPChar := Get(ALineIndex);
ALen := length(FTempLineStringForPChar);
Result := PChar(FTempLineStringForPChar);
end;
function TSynEditStringTrimmingList.GetDisplayView: TLazSynDisplayView;
begin
Result := FDisplayView;
end;
function TSynEditStringTrimmingList.GetPChar(ALineIndex: Integer; out ALen: Integer): PChar;
begin
Result := inherited GetPChar(ALineIndex, ALen);
// check if we need to apend spaces
if (not fEnabled) then exit;
if (fLockCount = 0) and (fLineIndex <> ALineIndex) then exit;
if (fLockCount > 0) and (fLockList.IndexOf(ALineIndex) < 0) then exit;
Result:= GetPCharSpaces(ALineIndex, ALen);
end;
procedure TSynEditStringTrimmingList.Exchange(Index1, Index2 : integer);
begin
FLineEdited := True;
fSynStrings.Exchange(Index1, Index2);
if fLineIndex = Index1 then
fLineIndex := Index2
else if fLineIndex = Index2 then
fLineIndex := Index1;
end;
procedure TSynEditStringTrimmingList.EditInsertTrim(LogX, LogY: Integer;
AText: String);
var
s: string;
begin
if (AText = '') then
exit;
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- EditInsertTrim', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), ' X=', LogX, ' Y=',LogY, ' text=',length(AText)]);{$ENDIF}
s := Spaces(LogY - 1);
StoreSpacesForLine(LogY - 1,
copy(s,1, LogX - 1) + AText + copy(s, LogX, length(s)),
fSynStrings.Strings[LogY - 1]);
CurUndoList.AddChange(TSynEditUndoTrimInsert.Create(LogX, LogY, Length(AText)));
IncViewChangeStamp;
end;
function TSynEditStringTrimmingList.EditDeleteTrim(LogX, LogY, ByteLen:
Integer): String;
var
s: string;
begin
if (ByteLen <= 0) then
exit('');
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- EditDeleteTrim()', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), ' X=', LogX, ' Y=',LogY, ' ByteLen=',ByteLen]);{$ENDIF}
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]);
if Result <> '' then
CurUndoList.AddChange(TSynEditUndoTrimDelete.Create(LogX, LogY, Result));
IncViewChangeStamp;
end;
procedure TSynEditStringTrimmingList.EditMoveToTrim(LogY, Len: Integer);
var
t, s: String;
begin
if Len <= 0 then
exit;
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- EditMoveToTrim()', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), ' Y=',LogY, ' len=',Len]);{$ENDIF}
t := fSynStrings[LogY - 1];
s := copy(t, 1 + length(t) - Len, Len) + Spaces(LogY - 1);
t := copy(t, 1, length(t) - Len);
StoreSpacesForLine(LogY - 1, s, t);
fSynStrings[LogY - 1] := t;
CurUndoList.AddChange(TSynEditUndoTrimMoveTo.Create(LogY, Len));
IncViewChangeStamp;
end;
procedure TSynEditStringTrimmingList.EditMoveFromTrim(LogY, Len: Integer);
var
t, s: String;
begin
if Len <= 0 then
exit;
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- EditMoveFromTrim()', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), ' Y=',LogY, ' len=',Len]);{$ENDIF}
s := Spaces(LogY - 1);
t := fSynStrings[LogY - 1] + copy(s, 1, Len);
s := copy(s, 1 + Len, length(s));
StoreSpacesForLine(LogY - 1, s, t);
fSynStrings[LogY - 1] := t;
CurUndoList.AddChange(TSynEditUndoTrimMoveFrom.Create(LogY, Len));
IncViewChangeStamp;
end;
procedure TSynEditStringTrimmingList.UpdateLineText(LogY: Integer);
begin
if LogY - 1 = fLineIndex then
fLineText := fSynStrings[LogY - 1];
end;
procedure TSynEditStringTrimmingList.IncViewChangeStamp;
begin
{$PUSH}{$Q-}{$R-}
FViewChangeStamp := FViewChangeStamp + 1;
{$POP}
end;
function TSynEditStringTrimmingList.GetViewChangeStamp: int64;
begin
Result := inherited GetViewChangeStamp;
{$PUSH}{$Q-}{$R-}
Result := Result + FViewChangeStamp;
{$POP}
end;
procedure TSynEditStringTrimmingList.EditInsert(LogX, LogY: Integer; AText: String);
var
t: String;
Len, LenNS, SaveLogX: Integer;
IsSpaces: Boolean;
SaveText: String;
begin
if (not fEnabled) then begin
fSynStrings.EditInsert(LogX, LogY, AText);
exit;
end;
t := fSynStrings[LogY - 1];
Len := length(t);
if ( (LogX <= Len) and not(t[Len] in [#9, #32]) ) or
( AText = '') or
( (LogX <= Len+1) and not(AText[Length(AText)] in [#9, #32]) )
then begin
fSynStrings.EditInsert(LogX, LogY, AText);
exit;
end;
IncIsInEditAction;
if Count = 0 then fSynStrings.Add('');
FlushNotificationCache;
IgnoreSendNotification(senrEditAction, True);
SaveText := AText;
SaveLogX := LogX;
Len := Length(t) + Length(Spaces(LogY-1));
if LogX - 1 > Len then begin
AText := StringOfChar(' ', LogX - 1 - Len) + AText;
LogX := 1 + Len;
end;
IsSpaces := LastNoneSpacePos(AText) = 0;
Len := length(t);
LenNS := LastNoneSpacePos(t);
if (LenNS < LogX - 1) and not IsSpaces then
LenNs := LogX - 1;
// Trim any existing (committed/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);
IgnoreSendNotification(senrEditAction, False);
SendNotification(senrEditAction, self, LogY, 0, SaveLogX, length(SaveText), SaveText);
DecIsInEditAction;
end;
function TSynEditStringTrimmingList.EditDelete(LogX, LogY, ByteLen: Integer): String;
var
t: String;
Len: Integer;
SaveByteLen: LongInt;
begin
Result := '';
if (not fEnabled) or (ByteLen <= 0) then begin
fSynStrings.EditDelete(LogX, LogY, ByteLen);
exit;
end;
t := fSynStrings[LogY - 1];
Len := length(t);
if (LogX + ByteLen <= Len) and not(t[Len] in [#9, #32]) then begin
fSynStrings.EditDelete(LogX, LogY, ByteLen);
exit;
end;
IncIsInEditAction;
FlushNotificationCache;
SaveByteLen := ByteLen;
IgnoreSendNotification(senrEditAction, True);
// Delete uncommited spaces (could also be ByteLen too big, due to past EOL)
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
begin
SendNotification(senrLineChange, self, LogY - 1, 1);
end;
UpdateLineText(LogY);
// Trim any existing (committed/real) spaces
t := fSynStrings[LogY - 1];
EditMoveToTrim(LogY, length(t) - LastNoneSpacePos(t));
IgnoreSendNotification(senrEditAction, False);
SendNotification(senrEditAction, self, LogY, 0, LogX, -SaveByteLen, '');
DecIsInEditAction;
end;
function TSynEditStringTrimmingList.EditReplace(LogX, LogY, ByteLen: Integer;
AText: String): String;
var
t: String;
SaveByteLen: LongInt;
Len, LenNS, SaveLogX: Integer;
IsSpaces: Boolean;
SaveText: String;
begin
if (not fEnabled) then begin
Result := inherited EditReplace(LogX, LogY, ByteLen, AText);
exit;
end;
if (Count = 0) or (ByteLen <= 0)
then begin
Result := '';
EditInsert(LogX, LogY, AText);
exit;
end;
t := fSynStrings[LogY - 1];
Len := length(t);
if ( (LogX + ByteLen <= Len) and not(t[Len] in [#9, #32]) ) or
( AText = '') or
( (LogX + ByteLen <= Len+1) and not(AText[Length(AText)] in [#9, #32]) )
then begin
Result := inherited EditReplace(LogX, LogY, ByteLen, AText);
exit;
end;
IncIsInEditAction;
FlushNotificationCache;
IgnoreSendNotification(senrEditAction, True);
SaveByteLen := ByteLen;
SaveText := AText;
SaveLogX := LogX;
Result := '';
// Delete uncommited spaces (could also be ByteLen too big, due to past EOL)
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
begin
SendNotification(senrLineChange, self, LogY - 1, 1);
end;
//// Trim any existing (committed/real) spaces
//t := fSynStrings[LogY - 1];
//EditMoveToTrim(LogY, length(t) - LastNoneSpacePos(t));
// Insert
t := fSynStrings[LogY - 1];
Len := Length(t) + Length(Spaces(LogY-1));
if LogX - 1 > Len then begin
AText := StringOfChar(' ', LogX - 1 - Len) + AText;
LogX := 1 + Len;
end;
IsSpaces := LastNoneSpacePos(AText) = 0;
Len := length(t);
LenNS := LastNoneSpacePos(t);
if (LenNS < LogX - 1) and not IsSpaces then
LenNs := LogX - 1;
// Trim any existing (committed/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);
IgnoreSendNotification(senrEditAction, False);
SendNotification(senrEditAction, self, LogY, 0, LogX, -SaveByteLen, '');
SendNotification(senrEditAction, self, LogY, 0, SaveLogX, length(SaveText), SaveText);
DecIsInEditAction;
end;
procedure TSynEditStringTrimmingList.EditLineBreak(LogX, LogY: Integer);
var
s, t: string;
begin
if (not fEnabled) then begin
fSynStrings.EditLineBreak(LogX, LogY);
exit;
end;
IncIsInEditAction;
FlushNotificationCache;
IgnoreSendNotification(senrEditAction, True);
s := Spaces(LogY - 1);
t := fSynStrings[LogY - 1];
if LogX > length(t) then begin
fSynStrings.EditLineBreak(1 + length(t), LogY);
FlushNotificationCache; // senrEditaction is ignored, so we need to flush by hand
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);
FlushNotificationCache; // senrEditaction is ignored, so we need to flush by hand
end;
UpdateLineText(LogY + 1);
EditInsertTrim(1, LogY + 1, s);
// Trim any existing (committed/real) spaces
s := fSynStrings[LogY - 1];
EditMoveToTrim(LogY, length(s) - LastNoneSpacePos(s));
s := fSynStrings[LogY];
EditMoveToTrim(LogY + 1, length(s) - LastNoneSpacePos(s));
IgnoreSendNotification(senrEditAction, False);
SendNotification(senrEditAction, self, LogY, 1, LogX, 0, '');
DecIsInEditAction;
end;
procedure TSynEditStringTrimmingList.EditLineJoin(LogY: Integer;
FillText: String = '');
var
s: String;
begin
if (not fEnabled) then begin
fSynStrings.EditLineJoin(LogY, FillText);
exit;
end;
IncIsInEditAction;
FlushNotificationCache;
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);
FlushNotificationCache; // senrEditaction is ignored, so we need to flush by hand
UpdateLineText(LogY);
EditInsertTrim(1, LogY, s);
// Trim any existing (committed/real) spaces
s := fSynStrings[LogY - 1];
EditMoveToTrim(LogY, length(s) - LastNoneSpacePos(s));
DecIsInEditAction;
end;
procedure TSynEditStringTrimmingList.EditLinesInsert(LogY, ACount: Integer;
AText: String = '');
var
s: string;
begin
IncIsInEditAction;
FlushNotificationCache;
fSynStrings.EditLinesInsert(LogY, ACount, AText);
s := fSynStrings[LogY - 1];
EditMoveToTrim(LogY, length(s) - LastNoneSpacePos(s));
DecIsInEditAction;
end;
procedure TSynEditStringTrimmingList.EditLinesDelete(LogY, ACount: Integer);
var
i: Integer;
begin
IncIsInEditAction;
FlushNotificationCache;
for i := LogY to LogY + ACount - 1 do
EditMoveFromTrim(i, length(Spaces(i - 1)));
fSynStrings.EditLinesDelete(LogY, ACount);
DecIsInEditAction;
end;
procedure TSynEditStringTrimmingList.EditUndo(Item: TSynEditUndoItem);
begin
EditRedo(Item);
end;
procedure TSynEditStringTrimmingList.EditRedo(Item: TSynEditUndoItem);
begin
IncIsInEditAction; // all undo calls edit actions
if not Item.PerformUndo(self) then
inherited EditRedo(Item);
DecIsInEditAction;
end;
end.