SynEdit: Cache char-width info (Logical<>Physical) / Cleanup

git-svn-id: trunk@30074 -
This commit is contained in:
martin 2011-03-29 23:44:25 +00:00
parent c3ace57233
commit 86a71bfe1e
8 changed files with 342 additions and 38 deletions

View File

@ -1593,7 +1593,7 @@ begin
with TCustomSynEdit(F.CurrentEditor) do begin
BeginUndoBlock;
BeginUpdate;
LogCaret:=PhysicalToLogicalPos(CaretXY);
LogCaret := LogicalCaretXY;
NewBlockBegin:=LogCaret;
CurLine:=Lines[NewBlockBegin.Y - 1];
while (NewBlockBegin.X>1) and (NewBlockBegin.X-1<=length(CurLine))

View File

@ -344,9 +344,9 @@ type
FBlockIndent: integer;
FCaret: TSynEditCaret;
FInternalCaret: TSynEditCaret;
FScreenCaret: TSynEditScreenCaret;
FInternalBlockSelection: TSynEditSelection;
FOnChangeUpdating: TChangeUpdatingEvent;
FScreenCaret: TSynEditScreenCaret;
FMouseSelectionMode: TSynSelectionMode;
fCtrlMouseActive: boolean; // deprecated since 0.9.29
fMarkupManager : TSynEditMarkupManager;
@ -866,6 +866,7 @@ type
property CaretX: Integer read GetCaretX write SetCaretX;
property CaretY: Integer read GetCaretY write SetCaretY;
property CaretXY: TPoint read GetCaretXY write SetCaretXY;// screen position
property LogicalCaretXY: TPoint read GetLogicalCaretXY write SetLogicalCaretXY;
property CharsInWindow: Integer read fCharsInWindow;
property CharWidth: integer read fCharWidth;
property Color default clWhite;
@ -873,7 +874,6 @@ type
{$IFDEF SYN_LAZARUS}
property CtrlMouseActive: boolean read fCtrlMouseActive; deprecated; // deprecated in 0.9.29
{$ENDIF}
property LogicalCaretXY: TPoint read GetLogicalCaretXY write SetLogicalCaretXY;
{$IFDEF SYN_LAZARUS}
property SelStart: Integer read GetSelStart write SetSelStart;
property SelEnd: Integer read GetSelEnd write SetSelEnd;
@ -2169,12 +2169,13 @@ end;
function TCustomSynEdit.GetLogicalCaretXY: TPoint;
begin
Result:=PhysicalToLogicalPos(CaretXY);
Result := FCaret.LineBytePos;
end;
procedure TCustomSynEdit.SetLogicalCaretXY(const NewLogCaretXY: TPoint);
begin
CaretXY:=LogicalToPhysicalPos(NewLogCaretXY);
FCaret.ChangeOnTouch;
FCaret.LineBytePos := NewLogCaretXY;
end;
procedure TCustomSynEdit.SetBeautifier(NewBeautifier: TSynCustomBeautifier);
@ -2935,7 +2936,7 @@ begin
Inc(X, CharsInWindow);
FCaret.LineCharPos := Point(X, C.Y);
if (not(sfIsDragging in fStateFlags)) then
SetBlockEnd(PhysicalToLogicalPos(CaretXY));
SetBlockEnd(LogicalCaretXY);
end;
if fScrollDeltaY <> 0 then begin
if GetKeyState(VK_SHIFT) < 0 then
@ -2947,7 +2948,7 @@ begin
else Y := TopLine; // scrolling up
FCaret.LineCharPos := Point(C.X, Y);
if (not(sfIsDragging in fStateFlags)) then
SetBlockEnd(PhysicalToLogicalPos(CaretXY));
SetBlockEnd(LogicalCaretXY);
end;
finally
DoDecPaintLock(Self);
@ -3003,8 +3004,8 @@ begin
if sfWaitForDragging in fStateFlags then
begin
ComputeCaret(X, Y);
SetBlockBegin(PhysicalToLogicalPos(CaretXY));
SetBlockEnd(PhysicalToLogicalPos(CaretXY));
SetBlockBegin(LogicalCaretXY);
SetBlockEnd(LogicalCaretXY);
Exclude(fStateFlags, sfWaitForDragging);
end;
@ -4117,7 +4118,7 @@ end;
procedure TCustomSynEdit.SelectWord;
begin
SetWordBlock(PhysicalToLogicalPos(CaretXY));
SetWordBlock(LogicalCaretXY);
end;
procedure TCustomSynEdit.SelectLine(WithLeadSpaces: Boolean = True);
@ -4153,12 +4154,12 @@ end;
function TCustomSynEdit.GetCaretX : Integer;
begin
Result:= fCaret.CharPos;
Result:= FCaret.CharPos;
end;
function TCustomSynEdit.GetCaretY : Integer;
begin
Result:= fCaret.LinePos;
Result:= FCaret.LinePos;
end;
function TCustomSynEdit.GetCaretUndo: TSynEditUndoItem;
@ -4188,7 +4189,7 @@ end;
function TCustomSynEdit.GetCaretXY: TPoint;
begin
Result := Point(CaretX, CaretY);
Result := FCaret.LineCharPos;
end;
function TCustomSynEdit.GetFoldedCodeColor: TSynSelectedColor;
@ -4994,7 +4995,7 @@ begin
FBlockSelection.EndLineBytePos := Point(x2, MinMax(Value.y, 1, FTheLinesView.Count));
end;
FBlockSelection.ActiveSelectionMode := smNormal;
CaretXY := FTheLinesView.LogicalToPhysicalPos(FBlockSelection.EndLineBytePos);
LogicalCaretXY := FBlockSelection.EndLineBytePos;
//DebugLn(' FFF2 ',Value.X,',',Value.Y,' BlockBegin=',BlockBegin.X,',',BlockBegin.Y,' BlockEnd=',BlockEnd.X,',',BlockEnd.Y);
DoDecPaintLock(Self);
end;
@ -5748,10 +5749,8 @@ begin
FCaret.DecForcePastEOL;
end;
FCaret.LineCharPos := NewCaret;
BlockBegin := {$IFDEF SYN_LAZARUS}PhysicalToLogicalPos(NewCaret)
{$ELSE}NewCaret{$ENDIF};
BlockEnd := {$IFDEF SYN_LAZARUS}PhysicalToLogicalPos(CaretXY)
{$ELSE}CaretXY{$ENDIF};
BlockBegin := PhysicalToLogicalPos(NewCaret);
BlockEnd := LogicalCaretXY;
finally
InternalEndUndoBlock;
end;
@ -6211,9 +6210,9 @@ begin
CaretNew := PrevWordPos;
if FFoldedLinesView.FoldedAtTextIndex[CaretNew.Y - 1] then begin
CY := FindNextUnfoldedLine(CaretNew.Y, False);
CaretNew := LogicalToPhysicalPos(Point(1 + Length(FTheLinesView[CY-1]), CY));
CaretNew := Point(1 + Length(FTheLinesView[CY-1]), CY);
end;
FCaret.LineCharPos := CaretNew;
FCaret.LineBytePos := CaretNew;
end;
ecWordRight, ecSelWordRight, ecColSelWordRight:
begin
@ -6235,7 +6234,7 @@ begin
else begin
Temp := LineText;
Len := Length(Temp);
LogCaretXY:=PhysicalToLogicalPos(CaretXY);
LogCaretXY:=LogicalCaretXY;
Caret := CaretXY;
//debugln('ecDeleteLastChar B Temp="',DbgStr(Temp),'" CaretX=',dbgs(CaretX),' LogCaretXY=',dbgs(LogCaretXY));
if LogCaretXY.X > Len +1
@ -6275,7 +6274,7 @@ begin
else begin
Temp := LineText;
Len := Length(Temp);
LogCaretXY:=PhysicalToLogicalPos(CaretXY);
LogCaretXY:=LogicalCaretXY;
if LogCaretXY.X <= Len then
begin
// delete char
@ -6306,7 +6305,7 @@ begin
WP := Point(Len + 1, CaretY);
if (WP.X <> CaretX) or (WP.Y <> CaretY) then begin
FInternalBlockSelection.StartLineBytePos := PhysicalToLogicalPos(WP);
FInternalBlockSelection.EndLineBytePos := PhysicalToLogicalPos(CaretXY);
FInternalBlockSelection.EndLineBytePos := LogicalCaretXY;
FInternalBlockSelection.ActiveSelectionMode := smNormal;
FInternalBlockSelection.SetSelTextPrimitive(smNormal, nil);
if Helper <> '' then
@ -6322,7 +6321,7 @@ begin
WP := Point(1, CaretY);
if (WP.X <> CaretX) or (WP.Y <> CaretY) then begin
FInternalBlockSelection.StartLineBytePos := PhysicalToLogicalPos(WP);
FInternalBlockSelection.EndLineBytePos := PhysicalToLogicalPos(CaretXY);
FInternalBlockSelection.EndLineBytePos := LogicalCaretXY;
FInternalBlockSelection.ActiveSelectionMode := smNormal;
FInternalBlockSelection.SetSelTextPrimitive(smNormal, nil);
CaretXY := WP;
@ -6348,7 +6347,7 @@ begin
if SelAvail and (not FBlockSelection.Persistent) and (eoOverwriteBlock in fOptions2) then
SetSelTextExternal('');
Temp := LineText;
LogCaretXY:=PhysicalToLogicalPos(CaretXY);
LogCaretXY:=LogicalCaretXY;
Len := Length(Temp);
if LogCaretXY.X > Len + 1 then
LogCaretXY.X := Len + 1;
@ -6424,7 +6423,7 @@ begin
// Insert a linebreak, but do not apply any other functionality (such as indent)
if SelAvail and (not FBlockSelection.Persistent) and (eoOverwriteBlock in fOptions2) then
SetSelTextExternal('');
LogCaretXY:=PhysicalToLogicalPos(CaretXY);
LogCaretXY:=LogicalCaretXY;
FTheLinesView.EditLineBreak(LogCaretXY.X, LogCaretXY.Y);
CaretXY := Point(1, CaretY + 1);
EnsureCursorPosVisible;
@ -6611,7 +6610,7 @@ begin
if fInserting then
Helper := '';
fUndoList.AddChange(crInsert, StartOfBlock,
PhysicalToLogicalPos(CaretXY),
LogicalCaretXY,
Helper, smNormal);
if CaretX >= LeftChar + fCharsInWindow then
LeftChar := LeftChar + min(25, fCharsInWindow - 1);
@ -8266,8 +8265,9 @@ var
end;
inc(EndPt.X);
SetCaretAndSelection(CaretXY, StartPt, EndPt);
end else if MoveCaret then
CaretXY := LogicalToPhysicalPos(Result)
end
else if MoveCaret then
LogicalCaretXY := Result;
end;
procedure DoFindMatchingQuote(q: char);
@ -8543,7 +8543,7 @@ var
end;
begin
LogCaret:=PhysicalToLogicalPos(CaretXY);
LogCaret:=LogicalCaretXY;
CX := LogCaret.X;
CY := LogCaret.Y;
// valid line?
@ -8596,7 +8596,7 @@ var
LogCaret: TPoint;
DelSpaces : Boolean;
begin
LogCaret:=PhysicalToLogicalPos(CaretXY);
LogCaret:=LogicalCaretXY;
CX := LogCaret.X;
CY := LogCaret.Y;
// valid line?

View File

@ -716,7 +716,7 @@ end;
constructor TSynEditSelection.Create(ALines : TSynEditStrings; aActOnLineChanges: Boolean);
begin
Inherited Create(ALines);
FInternalCaret := TSynEditCaret.Create;
FInternalCaret := TSynEditCaret.Create; // TODO: does not need FLines.AddEditHandler
FInternalCaret.Lines := FLines;
FActiveSelectionMode := smNormal;

View File

@ -129,6 +129,30 @@ type
read GetStorageMems write SetStorageMems; default;
end;
{ TSynLogicalPhysicalConvertor }
const
SYN_LP_MIN_ALLOC = 1024; // Keep at least n*SizeOf(TPhysicalCharWidths) allocated
type
TSynLogicalPhysicalConvertor = class
private
FLines: TSynEditStrings;
FCurrentWidths: TPhysicalCharWidths;
FCurrentWidthsLen, FCurrentWidthsAlloc: Integer;
FCurrentLine: Integer;
FTextChangeStamp, FViewChangeStamp: Int64;
// TODOtab-width
procedure PrepareWidthsForLine(AIndex: Integer; AForce: Boolean = False);
public
constructor Create(ALines: TSynEditStrings);
destructor Destroy; override;
// Line is 0-based // Column is 1-based
function PhysicalToLogical(AIndex, AColumn: Integer): Integer;
function PhysicalToLogical(AIndex, AColumn: Integer; out AColOffset: Integer): Integer;
// ACharPos 1=before 1st char
function LogicalToPhysical(AIndex, ABytePos: Integer): Integer;
function LogicalToPhysical(AIndex, ABytePos: Integer; var AColOffset: Integer): Integer;
end;
{ TSynEditStringsBase }
TSynEditStringsBase = class(TStrings)
@ -147,6 +171,7 @@ type
TSynEditStrings = class(TSynEditStringsBase)
private
FSenderUpdateCount: Integer;
FLogPhysConvertor :TSynLogicalPhysicalConvertor;
protected
FIsUtf8: Boolean;
function GetIsUtf8 : Boolean; virtual;
@ -155,6 +180,8 @@ type
function GetExpandedString(Index: integer): string; virtual; abstract;
function GetLengthOfLongestLine: integer; virtual; abstract;
procedure SetTextStr(const Value: string); override;
function GetTextChangeStamp: int64; virtual; abstract;
function GetViewChangeStamp: int64; virtual;
function GetUndoList: TSynEditUndoList; virtual; abstract;
function GetRedoList: TSynEditUndoList; virtual; abstract;
@ -172,6 +199,7 @@ type
procedure DoGetPhysicalCharWidths(Line: PChar; LineLen, Index: Integer; PWidths: PPhysicalCharWidth); virtual; abstract;
public
constructor Create;
destructor Destroy; override;
procedure BeginUpdate(Sender: TObject); overload;
procedure EndUpdate(Sender: TObject); overload;
function IsUpdating: Boolean;
@ -229,6 +257,8 @@ type
property IsUndoing: Boolean read GetIsUndoing write SetIsUndoing;
property IsRedoing: Boolean read GetIsRedoing write SetIsRedoing;
public
property TextChangeStamp: int64 read GetTextChangeStamp;
property ViewChangeStamp: int64 read GetViewChangeStamp; // tabs-size, trailing-spaces, ...
property ExpandedStrings[Index: integer]: string read GetExpandedString;
property LengthOfLongestLine: integer read GetLengthOfLongestLine;
property IsUtf8: Boolean read GetIsUtf8 write SetIsUtf8;
@ -242,6 +272,8 @@ type
function GetIsUtf8 : Boolean; override;
procedure SetIsUtf8(const AValue : Boolean); override;
function GetTextChangeStamp: int64; override;
function GetViewChangeStamp: int64; override;
function GetRange(Index: Pointer): TSynManagedStorageMem; override;
procedure PutRange(Index: Pointer; const ARange: TSynManagedStorageMem); override;
@ -430,6 +462,130 @@ begin
raise ESynEditStorageMem.CreateFmt(SListIndexOutOfBounds, [Index]);
end;
{ TSynLogicalPhysicalConvertor }
procedure TSynLogicalPhysicalConvertor.PrepareWidthsForLine(AIndex: Integer;
AForce: Boolean);
var
LineLen: Integer;
Line: PChar;
begin
if (not AForce) and (FCurrentLine = AIndex) and
(FLines.TextChangeStamp = FTextChangeStamp) and (FLines.ViewChangeStamp = FViewChangeStamp)
then begin
//debugln(['**************** RE-USING widths: ', AIndex,' (',dbgs(Pointer(self)),')']);
exit;
end;
if (AIndex < 0) or (AIndex >= FLines.Count) then begin
FCurrentWidthsLen := 0;
FViewChangeStamp := FLines.ViewChangeStamp;
FTextChangeStamp := FLines.TextChangeStamp;
exit;
end;
Line := FLines.GetPChar(AIndex, LineLen);
if LineLen > FCurrentWidthsAlloc then begin
//debugln(['**************** COMPUTING widths (grow): ', AIndex,' (',dbgs(Pointer(self)),') old-alloc=', FCurrentWidthsAlloc, ' new-len=',LineLen]);
SetLength(FCurrentWidths, LineLen * SizeOf(TPhysicalCharWidth));
FCurrentWidthsAlloc := LineLen;
end
else if FCurrentWidthsAlloc > Max(Max(LineLen, FCurrentWidthsLen)*4, SYN_LP_MIN_ALLOC) then begin
//debugln(['**************** COMPUTING widths (shrink): ', AIndex,' (',dbgs(Pointer(self)),') old-alloc=', FCurrentWidthsAlloc, ' new-len=',LineLen]);
FCurrentWidthsAlloc := Max(Max(LineLen, FCurrentWidthsLen), SYN_LP_MIN_ALLOC) ;
SetLength(FCurrentWidths, FCurrentWidthsAlloc * SizeOf(TPhysicalCharWidth));
//end
//else begin
// debugln(['**************** COMPUTING widths: ', AIndex,' (',dbgs(Pointer(self)),') alloc=',FCurrentWidthsAlloc]);
end;
FCurrentWidthsLen := LineLen;
if LineLen > 0 then
FLines.DoGetPhysicalCharWidths(Line, LineLen, AIndex, @FCurrentWidths[0]);
FViewChangeStamp := FLines.ViewChangeStamp;
FTextChangeStamp := FLines.TextChangeStamp;
FCurrentLine := AIndex;
end;
constructor TSynLogicalPhysicalConvertor.Create(ALines: TSynEditStrings);
begin
FLines := ALines;
FCurrentLine := -1;
FCurrentWidths := nil;
FCurrentWidthsLen := 0;
FCurrentWidthsAlloc := 0;
end;
destructor TSynLogicalPhysicalConvertor.Destroy;
begin
SetLength(FCurrentWidths, 0);
inherited Destroy;
end;
function TSynLogicalPhysicalConvertor.PhysicalToLogical(AIndex,
AColumn: Integer): Integer;
var
ColOffs: Integer;
begin
Result := PhysicalToLogical(AIndex, AColumn, ColOffs);
end;
function TSynLogicalPhysicalConvertor.PhysicalToLogical(AIndex, AColumn: Integer;
out AColOffset: Integer): Integer;
var
BytePos, ScreenPos, ScreenPosOld: integer;
begin
PrepareWidthsForLine(AIndex);
ScreenPos := 1;
BytePos := 0;
while BytePos < FCurrentWidthsLen do begin
ScreenPosOld := ScreenPos;
ScreenPos := ScreenPos + FCurrentWidths[BytePos];
inc(BytePos);
if ScreenPos > AColumn then begin
AColOffset := AColumn - ScreenPosOld;
exit(BytePos);
end;
end;
// Column at/past end of line
AColOffset := 0;
Result := BytePos + 1 + AColumn - ScreenPos;
end;
function TSynLogicalPhysicalConvertor.LogicalToPhysical(AIndex,
ABytePos: Integer): Integer;
var
ColOffs: Integer;
begin
ColOffs := 0;
Result := LogicalToPhysical(AIndex, ABytePos, ColOffs);
end;
function TSynLogicalPhysicalConvertor.LogicalToPhysical(AIndex, ABytePos: Integer;
var AColOffset: Integer): Integer;
var
i: integer;
CharWidths: TPhysicalCharWidths;
begin
PrepareWidthsForLine(AIndex);
dec(ABytePos);
if ABytePos >= FCurrentWidthsLen then begin
Result := 1 + ABytePos - FCurrentWidthsLen;
ABytePos := FCurrentWidthsLen;
AColOffset := 0;
end
else begin
AColOffset := Min(AColOffset, FCurrentWidths[ABytePos]-1);
Result := 1 + AColOffset;
end;
for i := 0 to ABytePos - 1 do
Result := Result + FCurrentWidths[i];
end;
{ TSynEditStringsBase }
function TSynEditStringsBase.GetPChar(ALineIndex: Integer): PChar;
@ -443,10 +599,17 @@ end;
constructor TSynEditStrings.Create;
begin
FLogPhysConvertor := TSynLogicalPhysicalConvertor.Create(self);
inherited Create;
IsUtf8 := True;
end;
destructor TSynEditStrings.Destroy;
begin
FreeAndNil(FLogPhysConvertor);
inherited Destroy;
end;
procedure TSynEditStrings.BeginUpdate(Sender: TObject);
begin
if FSenderUpdateCount = 0 then
@ -579,6 +742,11 @@ begin
end;
end;
function TSynEditStrings.GetViewChangeStamp: int64;
begin
Result := 0;
end;
procedure TSynEditStrings.SetUpdateState(Updating: Boolean);
begin
// Update/check "FSenderUpdateCount", to avoid extra locking/unlocking
@ -591,8 +759,9 @@ end;
function TSynEditStrings.LogicalToPhysicalPos(const p : TPoint) : TPoint;
begin
Result := p;
if Result.Y - 1 < Count then
Result.X:=LogicalToPhysicalCol(self[Result.Y - 1], Result.Y, Result.X);
Result.X := FLogPhysConvertor.LogicalToPhysical(p.y - 1, p.x);
// if Result.Y - 1 < Count then
// Result.X:=LogicalToPhysicalCol(self[Result.Y - 1], Result.Y, Result.X);
end;
function TSynEditStrings.LogicalToPhysicalCol(const Line : String;
@ -619,8 +788,9 @@ end;
function TSynEditStrings.PhysicalToLogicalPos(const p : TPoint) : TPoint;
begin
Result := p;
if (Result.Y>=1) and (Result.Y <= Count) then
Result.X:=PhysicalToLogicalCol(self[Result.Y - 1], Result.Y - 1, Result.X);
Result.X := FLogPhysConvertor.PhysicalToLogical(p.y - 1, p.x);
// if (Result.Y>=1) and (Result.Y <= Count) then
// Result.X:=PhysicalToLogicalCol(self[Result.Y - 1], Result.Y - 1, Result.X);
end;
function TSynEditStrings.PhysicalToLogicalCol(const Line : string;
@ -728,6 +898,16 @@ begin
FSynStrings.IsUtf8 := AValue;
end;
function TSynEditStringsLinked.GetTextChangeStamp: int64;
begin
Result := fSynStrings.GetTextChangeStamp;
end;
function TSynEditStringsLinked.GetViewChangeStamp: int64;
begin
Result := fSynStrings.GetViewChangeStamp;
end;
//Ranges
function TSynEditStringsLinked.GetRange(Index: Pointer): TSynManagedStorageMem;
begin

View File

@ -136,6 +136,7 @@ type
protected
function GetExpandedString(Index: integer): string; override;
function GetLengthOfLongestLine: integer; override;
function GetTextChangeStamp: int64; override;
function GetRedoList: TSynEditUndoList; override;
function GetUndoList: TSynEditUndoList; override;
@ -201,7 +202,6 @@ type
property Flags[Index: Integer]: TSynEditStringFlags read GetFlags
write SetFlags;
property Modified: Boolean read FModified write SetModified;
property TextChangeStamp: int64 read FTextChangeStamp;
public
property UndoList: TSynEditUndoList read GetUndoList write fUndoList;
property RedoList: TSynEditUndoList read GetRedoList write fRedoList;
@ -642,6 +642,11 @@ begin
Result := 0;
end;
function TSynEditStringList.GetTextChangeStamp: int64;
begin
Result := FTextChangeStamp;
end;
function TSynEditStringList.GetRedoList: TSynEditUndoList;
begin
Result := fRedoList;

View File

@ -58,11 +58,13 @@ type
FTabData: TSynEditStringTabData;
FLastLineHasTab: Boolean; // Last line, parsed by GetPhysicalCharWidths
FLastLinePhysLen: Integer;
FViewChangeStamp: int64;
procedure TextBufferChanged(Sender: TObject);
procedure LineCountChanged(Sender: TSynEditStrings; AIndex, ACount : Integer);
function ExpandedString(Index: integer): string;
function ExpandedStringLength(Index: integer): Integer;
protected
function GetViewChangeStamp: int64; override;
function GetTabWidth : integer;
procedure SetTabWidth(const AValue : integer);
function GetExpandedString(Index: integer): string; override;
@ -170,6 +172,10 @@ var
begin
if FTabWidth = AValue then exit;
{$PUSH}{$Q-}{$R-}
FViewChangeStamp := FViewChangeStamp + 1;
{$POP}
FTabWidth := AValue;
FIndexOfLongestLine := -1;
for i := 0 to Count - 1 do
@ -177,6 +183,14 @@ begin
FTabData[i] := LINE_LEN_UNKNOWN;
end;
function TSynEditStringTabExpander.GetViewChangeStamp: int64;
begin
Result := inherited GetViewChangeStamp;
{$PUSH}{$Q-}{$R-}
Result := Result + FViewChangeStamp;
{$POP}
end;
procedure TSynEditStringTabExpander.TextBufferChanged(Sender: TObject);
var
Data: TSynEditStringTabData;

View File

@ -52,6 +52,7 @@ type
fLockList : TStringList;
FLineEdited: Boolean;
FTempLineStringForPChar: String; // experimental; used by GetPChar;
FViewChangeStamp: int64;
procedure DoCaretChanged(Sender : TObject);
procedure ListCleared(Sender: TObject);
Procedure LinesChanged(Sender: TSynEditStrings; AIndex, ACount : Integer);
@ -68,7 +69,9 @@ type
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;
@ -378,6 +381,7 @@ begin
then begin
if (fLineIndex <> TSynEditCaret(Sender).LinePos - 1) then begin
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- CarteChnaged - Clearing 1 ', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), 'newCaretYPos=',TSynEditCaret(Sender).LinePos]);{$ENDIF}
if fSpaces <> '' then IncViewChangeStamp;
fLineIndex := TSynEditCaret(Sender).LinePos - 1;
fSpaces := '';
end;
@ -385,7 +389,7 @@ begin
end;
FIsTrimming := True;
SendNotification(senrLineChange, self, fLineIndex, 1);
IncViewChangeStamp;
if (fLineIndex <> TSynEditCaret(Sender).LinePos - 1) or
(FTrimType = settIgnoreAll) then
begin
@ -393,6 +397,7 @@ begin
CurUndoList.AppendToLastChange(TSynEditUndoTrimForget.Create(FLineIndex+1, FSpaces));
i := length(FSpaces);
fSpaces := '';
SendNotification(senrLineChange, self, fLineIndex, 1);
SendNotification(senrEditAction, self, FLineIndex+1, 0,
1+length(fSynStrings[FLineIndex]), -i, '');
end else begin
@ -408,6 +413,7 @@ begin
FSpaces := copy(FSpaces, 1, j);
i := length(s);
CurUndoList.AppendToLastChange(TSynEditUndoTrimForget.Create(FLineIndex+1, s));
SendNotification(senrLineChange, self, fLineIndex, 1);
SendNotification(senrEditAction, self, FLineIndex+1, 0,
1+length(fSynStrings[FLineIndex]) + length(FSpaces), -i, '');
end;
@ -419,6 +425,7 @@ 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 := '';
@ -445,6 +452,7 @@ 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
@ -549,6 +557,7 @@ begin
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;
@ -578,6 +587,7 @@ var
begin
if (not fEnabled) then exit;
FIsTrimming := True;
IncViewChangeStamp;
{$IFDEF SynTrimDebug}debugln(['--- Trimmer -- TrimAfterLock', ' fLineIndex=', fLineIndex, ' fSpaces=',length(fSpaces), ' Index=', Index, ' LockList=',fLockList.CommaText]);{$ENDIF}
i := fLockList.IndexOfObject(TObject(Pointer(PtrUInt(fLineIndex))));
if i >= 0 then begin
@ -760,6 +770,7 @@ begin
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:
@ -777,6 +788,7 @@ begin
fSynStrings.Strings[LogY - 1]);
if Result <> '' then
CurUndoList.AddChange(TSynEditUndoTrimDelete.Create(LogX, LogY, Result));
IncViewChangeStamp;
end;
procedure TSynEditStringTrimmingList.EditMoveToTrim(LogY, Len: Integer);
@ -792,6 +804,7 @@ begin
StoreSpacesForLine(LogY - 1, s, t);
fSynStrings[LogY - 1] := t;
CurUndoList.AddChange(TSynEditUndoTrimMoveTo.Create(LogY, Len));
IncViewChangeStamp;
end;
procedure TSynEditStringTrimmingList.EditMoveFromTrim(LogY, Len: Integer);
@ -807,6 +820,7 @@ begin
StoreSpacesForLine(LogY - 1, s, t);
fSynStrings[LogY - 1] := t;
CurUndoList.AddChange(TSynEditUndoTrimMoveFrom.Create(LogY, Len));
IncViewChangeStamp;
end;
procedure TSynEditStringTrimmingList.UpdateLineText(LogY: Integer);
@ -815,6 +829,21 @@ begin
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;

View File

@ -27,6 +27,7 @@ type
published
procedure TestEditEmpty;
procedure TestEditTabs;
procedure TestEditPhysicalLogical;
end;
implementation
@ -173,6 +174,81 @@ begin
end;
procedure TTestBasicSynEdit.TestEditPhysicalLogical;
procedure TestPhysLog(name: string; y, x, expX: integer);
var gotX: Integer;
begin
name := name + ' y='+inttostr(y)+' x='+inttostr(x);
gotX := SynEdit.PhysicalToLogicalPos(Point(x, y)).x;
AssertEquals(name+' PhysicalToLogicalPos', expX, gotX);
gotX := SynEdit.PhysicalToLogicalCol(SynEdit.Lines[y-1], y-1, x);
AssertEquals(name+' PhysicalToLogicalCol', expX, gotX);
end;
procedure TestLogPhys(name: string; y, x, expX: integer);
var gotX: Integer;
begin
name := name + ' y='+inttostr(y)+' x='+inttostr(x);
gotX := SynEdit.LogicalToPhysicalPos(Point(x, y)).x;
AssertEquals(name+' LogicalToPhysicalPos', expX, gotX);
gotX := SynEdit.LogicalToPhysicalCol(SynEdit.Lines[y-1], y-1, x);
AssertEquals(name+' LogicalToPhysicalCol', expX, gotX);
end;
begin
ReCreateEdit;
SynEdit.TabWidth := 6;
SetLines(['abc', ' ääX', #9'mn', 'abc'#9'de', #9'Xää.']);
TestLogPhys('simple line (abc)', 1, 1, 1);
TestLogPhys('simple line (abc)', 1, 2, 2);
TestLogPhys('simple line (abc)', 1, 4, 4);
TestLogPhys('simple line (abc)', 1, 5, 5);
TestLogPhys('simple line (abc)', 1, 6, 6);
TestLogPhys('line with 2byte-char', 2, 1, 1);
TestLogPhys('line with 2byte-char', 2, 2, 2);
TestLogPhys('line with 2byte-char', 2, 4, 3); // after ae
TestLogPhys('line with 2byte-char', 2, 6, 4);
TestLogPhys('line with 2byte-char', 2, 7, 5);
TestLogPhys('line with 2byte-char', 2, 8, 6);
TestLogPhys('line with 2byte-char', 2, 11, 9);
TestLogPhys('line with tab (start)', 3, 1, 1);
TestLogPhys('line with tab (start)', 3, 2, 7);
TestLogPhys('line with tab (middle)', 4, 3, 3);
TestLogPhys('line with tab (middle)', 4, 4, 4); // before tab
TestLogPhys('line with tab (middle)', 4, 5, 7); // after tab
TestLogPhys('line with tab (middle)', 4, 6, 8);
TestLogPhys('line with tab (middle)', 4, 9, 11);
TestLogPhys('line with tab (start) + 2bc', 5, 1, 1);
TestLogPhys('line with tab (start) + 2bc', 5, 2, 7);
TestLogPhys('line with tab (start) + 2bc', 5, 3, 8);
TestLogPhys('line with tab (start) + 2bc', 5, 5, 9);
TestPhysLog('simple line (abc)', 1, 1, 1);
TestPhysLog('simple line (abc)', 1, 2, 2);
TestPhysLog('simple line (abc)', 1, 4, 4);
TestPhysLog('simple line (abc)', 1, 5, 5);
TestPhysLog('simple line (abc)', 1, 6, 6);
TestPhysLog('line with 2byte-char', 2, 1, 1);
TestPhysLog('line with 2byte-char', 2, 2, 2);
TestPhysLog('line with 2byte-char', 2, 3, 4);
TestPhysLog('line with 2byte-char', 2, 4, 6);
TestPhysLog('line with 2byte-char', 2, 5, 7);
TestPhysLog('line with 2byte-char', 2, 6, 8);
TestPhysLog('line with 2byte-char', 2, 7, 9);
TestPhysLog('line with tab (start)', 3, 1, 1);
TestPhysLog('line with tab (start)', 3, 2, 1);
TestPhysLog('line with tab (start)', 3, 5, 1);
TestPhysLog('line with tab (start)', 3, 6, 1);
TestPhysLog('line with tab (start)', 3, 7, 2);
TestPhysLog('line with tab (start)', 3, 8, 3);
TestPhysLog('line with tab (start)', 3, 9, 4);
TestPhysLog('line with tab (start)', 3, 11, 6);
end;
initialization