{------------------------------------------------------------------------------- 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, SynEditTextBase, SynEditPointClasses, SynEditMiscProcs; type TSynEditStringTrimmingType = (settLeaveLine, settEditLine, settMoveCaret, settIgnoreAll); { TSynEditStringTrimmingList } TSynEditStringTrimmingList = class(TSynEditStringsLinked) private fCaret: TSynEditCaret; FIsTrimming: Boolean; FTrimType: TSynEditStringTrimmingType; fSpaces: String; fLineText: String; fLineIndex: Integer; fEnabled: Boolean; FUndoTrimmedSpaces: Boolean; fLockCount: Integer; fLockList : TStringList; FLineEdited: Boolean; procedure DoCaretChanged(Sender : TObject); Procedure LinesChanged(Sender: TSynEditStrings; AIndex, ACount : 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 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; 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; 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; 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; 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 } constructor TSynEditStringTrimmingList.Create(ASynStringSource : TSynEditStrings; ACaret: TSynEditCaret); begin fCaret := ACaret; fCaret.AddChangeHandler(@DoCaretChanged); fLockList := TStringList.Create; fLineIndex:= -1; fSpaces := ''; fEnabled:=false; FUndoTrimmedSpaces := False; FIsTrimming := False; FLineEdited := False; FTrimType := settLeaveLine; Inherited Create(ASynStringSource); fSynStrings.AddChangeHandler(senrLineCount, {$IFDEF FPC}@{$ENDIF}LinesChanged); fSynStrings.AddChangeHandler(senrLineChange, {$IFDEF FPC}@{$ENDIF}LinesChanged); end; destructor TSynEditStringTrimmingList.Destroy; begin fSynStrings.RemoveChangeHandler(senrLineCount, {$IFDEF FPC}@{$ENDIF}LinesChanged); fSynStrings.RemoveChangeHandler(senrLineChange, {$IFDEF FPC}@{$ENDIF}LinesChanged); fCaret.RemoveChangeHandler(@DoCaretChanged); FreeAndNil(fLockList); inherited Destroy; 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 ((FTrimType in [settLeaveLine]) AND (fLineIndex = TSynEditCaret(Sender).LinePos - 1)) or ((FTrimType in [settEditLine]) and not FLineEdited) then begin if (fLineIndex <> TSynEditCaret(Sender).LinePos - 1) then fSpaces := ''; fLineIndex := TSynEditCaret(Sender).LinePos - 1; exit; end; FIsTrimming := True; SendNotification(senrLineChange, self, fLineIndex, 1); if (fLineIndex <> TSynEditCaret(Sender).LinePos - 1) or (FTrimType = settIgnoreAll) then begin UndoList.AppendToLastChange(TSynEditUndoTrimForget.Create(FLineIndex+1, FSpaces)); fSpaces := ''; 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); FSpaces := copy(FSpaces, 1, j); UndoList.AppendToLastChange(TSynEditUndoTrimForget.Create(FLineIndex+1, s)); end; FIsTrimming := False; FLineEdited := False; fLineIndex := TSynEditCaret(Sender).LinePos - 1; 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.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); 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 if fLockCount > 0 then begin i := fLockList.IndexOfObject(TObject(pointer(Index))); if i < 0 then fLockList.AddObject(SpaceStr, TObject(pointer(Index))) else fLockList[i] := 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.IndexOfObject(TObject(Pointer(Index))); if i < 0 then result := '' else result := fLockList[i]; exit; end; if Index <> fLineIndex then exit(''); if (fLineIndex < 0) or (fLineIndex >= fSynStrings.Count) or (fLineText <> fSynStrings[fLineIndex]) then begin fSpaces:=''; fLineText:=''; end; Result:= fSpaces; end; procedure TSynEditStringTrimmingList.DoLinesChanged(Index, N : integer); var i, j: Integer; begin if (not fEnabled) then exit; if fLockCount > 0 then begin for i := fLockList.Count-1 downto 0 do begin j := Integer(Pointer(fLockList.Objects[i])); if (j >= Index) and (j < Index - N) then fLockList.Delete(i) else if j >= Index then fLockList.Objects[i] := TObject(Pointer(j + N)); end; end else begin if (fLineIndex >= Index) and (fLineIndex < Index - N) then fLineIndex:=-1 else if fLineIndex > Index then inc(fLineIndex, N); end; end; procedure TSynEditStringTrimmingList.Lock; begin if (fLockCount = 0) and (fLineIndex >= 0) and Enabled then begin fLockList.AddObject(Spaces(fLineIndex), TObject(Pointer(fLineIndex))); FLineEdited := False; end; inc(fLockCount); end; procedure TSynEditStringTrimmingList.UnLock; begin dec(fLockCount); if (fLockCount = 0) then TrimAfterLock; end; procedure TSynEditStringTrimmingList.TrimAfterLock; var i, index, slen: Integer; ltext: String; begin if (not fEnabled) then exit; FIsTrimming := True; i := fLockList.IndexOfObject(TObject(Pointer(fLineIndex))); if i >= 0 then begin fSpaces:= fLockList[i]; if (fLineIndex >= 0) and (fLineIndex < fSynStrings.Count) then fLineText := fSynStrings[fLineIndex]; fLockList.Delete(i); DoCaretChanged(fCaret); end; FIsTrimming := True; for i := 0 to fLockList.Count-1 do begin index := Integer(Pointer(fLockList.Objects[i])); slen := length(fLockList[i]); if (slen > 0) and (index >= 0) and (index < fSynStrings.Count) then begin ltext := fSynStrings[index]; fSynStrings[index] := ltext; // trigger OnPutted, so the line gets repainted UndoList.AppendToLastChange(TSynEditUndoTrimForget.Create(Index+1, fLockList[i])); end; end; FIsTrimming := False; FLineEdited := False; fLockList.Clear; end; procedure TSynEditStringTrimmingList.ForceTrim; begin 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; DoLinesChanged(c, 1); Result := fSynStrings.Add(TrimLine(S, c)); end; procedure TSynEditStringTrimmingList.AddStrings(AStrings : TStrings); var i, c : Integer; begin c := fSynStrings.Count; DoLinesChanged(c, AStrings.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); DoLinesChanged(Index, -1); 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); DoLinesChanged(Index, -NumLines); end; procedure TSynEditStringTrimmingList.Insert(Index : integer; const S : string); begin FLineEdited := True; DoLinesChanged(Index, 1); fSynStrings.Insert(Index, TrimLine(S, Index)); end; procedure TSynEditStringTrimmingList.InsertLines(Index, NumLines : integer); begin FLineEdited := True; DoLinesChanged(Index, NumLines); fSynStrings.InsertLines(Index, NumLines); end; procedure TSynEditStringTrimmingList.InsertStrings(Index : integer; NewStrings : TStrings); var i : Integer; begin FLineEdited := True; DoLinesChanged(Index, NewStrings.Count); for i := 0 to NewStrings.Count-1 do NewStrings[i] := TrimLine(NewStrings[i], Index+i, True); fSynStrings.InsertStrings(Index, NewStrings); 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 = '') or (FTrimType = settIgnoreAll) 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) or (FTrimType = settIgnoreAll) 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.