SynEdit: Rewrite of Caret-class, introduction of simplified base class for faster block(selection) replace operation. Related to secondary report of issue #0023556

git-svn-id: trunk@39806 -
This commit is contained in:
martin 2013-01-08 17:33:10 +00:00
parent 009fe39d7a
commit 0d31a09f02
5 changed files with 1411 additions and 446 deletions

View File

@ -9,27 +9,24 @@ interface
uses
SynBeautifier, SynCompletion, SynEdit, SynEditAutoComplete, SynEditExport,
SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
SynEditHighlighterXMLBase, SynEditKeyCmds, LazSynEditMouseCmdsTypes,
SynHighlighterPo, SynEditLines, SynEditMarks, SynEditMarkup,
SynEditMarkupBracket, SynEditMarkupCtrlMouseLink, SynEditMarkupHighAll,
SynEditMarkupSelection, SynEditMarkupSpecialLine, SynEditMarkupWordGroup,
SynEditMiscClasses, SynEditMiscProcs, SynEditMouseCmds, SynEditPlugins,
SynEditPointClasses, SynEditRegexSearch, SynEditSearch, SynEditStrConst,
SynEditTextBase, SynEditTextBuffer, SynEditTextBidiChars,
SynEditTextTabExpander, SynEditTextTrimmer, SynEditTypes, SynExportHTML,
SynGutter, SynGutterBase, SynGutterChanges, SynGutterCodeFolding,
SynGutterLineNumber, SynGutterLineOverview, SynGutterMarks,
SynHighlighterAny, SynHighlighterCpp, SynHighlighterCss, SynHighlighterDiff,
SynHighlighterHashEntries, SynHighlighterHTML, SynHighlighterJava,
SynHighlighterJScript, SynHighlighterLFM, SynHighlighterMulti,
SynHighlighterPas, SynHighlighterPerl, SynHighlighterPHP,
SynHighlighterPosition, SynHighlighterPython, SynHighlighterSQL,
SynHighlighterTeX, synhighlighterunixshellscript, SynHighlighterVB,
SynHighlighterXML, SynMacroRecorder, SynMemo, SynPluginSyncroEdit,
SynPluginSyncronizedEditBase, SynPluginTemplateEdit, LazSynEditText,
LazSynTextArea, SynRegExpr, SynTextDrawer, SynEditMarkupGutterMark,
SynHighlighterBat, SynHighlighterIni, SynEditMarkupSpecialChar,
SynEditTextDoubleWidthChars, LazarusPackageIntf;
SynEditHighlighterXMLBase, SynEditKeyCmds, LazSynEditMouseCmdsTypes, SynHighlighterPo,
SynEditLines, SynEditMarks, SynEditMarkup, SynEditMarkupBracket,
SynEditMarkupCtrlMouseLink, SynEditMarkupHighAll, SynEditMarkupSelection,
SynEditMarkupSpecialLine, SynEditMarkupWordGroup, SynEditMiscClasses, SynEditMiscProcs,
SynEditMouseCmds, SynEditPlugins, SynEditPointClasses, SynEditRegexSearch, SynEditSearch,
SynEditStrConst, SynEditTextBase, SynEditTextBuffer, SynEditTextBidiChars,
SynEditTextTabExpander, SynEditTextTrimmer, SynEditTypes, SynExportHTML, SynGutter,
SynGutterBase, SynGutterChanges, SynGutterCodeFolding, SynGutterLineNumber,
SynGutterLineOverview, SynGutterMarks, SynHighlighterAny, SynHighlighterCpp,
SynHighlighterCss, SynHighlighterDiff, SynHighlighterHashEntries, SynHighlighterHTML,
SynHighlighterJava, SynHighlighterJScript, SynHighlighterLFM, SynHighlighterMulti,
SynHighlighterPas, SynHighlighterPerl, SynHighlighterPHP, SynHighlighterPosition,
SynHighlighterPython, SynHighlighterSQL, SynHighlighterTeX, synhighlighterunixshellscript,
SynHighlighterVB, SynHighlighterXML, SynMacroRecorder, SynMemo, SynPluginSyncroEdit,
SynPluginSyncronizedEditBase, SynPluginTemplateEdit, LazSynEditText, LazSynTextArea,
SynRegExpr, SynTextDrawer, SynEditMarkupGutterMark, SynHighlighterBat, SynHighlighterIni,
SynEditMarkupSpecialChar, SynEditTextDoubleWidthChars, SynEditTextSystemCharWidth,
LazarusPackageIntf;
implementation

View File

@ -84,6 +84,10 @@ type
TSynLogPhysFlag = (lpfAdjustToCharBegin, lpfAdjustToNextChar);
TSynLogPhysFlags = set of TSynLogPhysFlag;
const
cspDefault = cspFollowLtr;
cslDefault = cslFollowLtr;
(** LRL // L=Ltr-Char / R=RTl-Char
* LogToPhys
@ -123,37 +127,50 @@ type
(3, cspRight) => 3 // looking to the right, using 2nd L = Log 3
*)
type
TSynLogicalPhysicalConvertor = class
private
FLastLogicalResultPos: Integer;
FLastPhysicalResultPos: Integer;
FAdjustedLogToPhysOrigin: Integer;
FAdjustedPhysToLogOrigin: Integer;
FLines: TSynEditStrings;
FCurrentWidths: TPhysicalCharWidths;
FCurrentWidthsLen, FCurrentWidthsAlloc: Integer;
FCurrentLine: Integer;
FTextChangeStamp, FViewChangeStamp: Int64;
FUnAdjustedPhysToLogColOffs: Integer;
FUnAdjustedPhysToLogResult: Integer;
// TODOtab-width
function GetCurrentLine: Integer;
function GetCurrentWidths: PPhysicalCharWidth;
procedure PrepareWidthsForLine(AIndex: Integer; AForce: Boolean = False);
protected
procedure SetWidthsForLine(AIndex: Integer; ANewWidths: TPhysicalCharWidths);
public
constructor Create(ALines: TSynEditStrings);
destructor Destroy; override;
property CurrentLine: Integer read GetCurrentLine;
property CurrentWidths: PPhysicalCharWidth read GetCurrentWidths;
property CurrentWidthsCount: Integer read FCurrentWidthsLen;
public
// Line is 0-based // Column is 1-based
function PhysicalToLogical(AIndex, AColumn: Integer): Integer;
function PhysicalToLogical(AIndex, AColumn: Integer; out AColOffset: Integer;
ACharSide: TSynPhysCharSide= cspFollowLtr//;
{AFlags: TSynLogPhysFlags = []}): Integer;
ACharSide: TSynPhysCharSide= cspDefault;
AFlags: TSynLogPhysFlags = []): Integer;
// ACharPos 1=before 1st char
function LogicalToPhysical(AIndex, ABytePos: Integer): Integer;
function LogicalToPhysical(AIndex, ABytePos: Integer; var AColOffset: Integer;
ACharSide: TSynLogCharSide = cslFollowLtr;
ACharSide: TSynLogCharSide = cslDefault;
AFlags: TSynLogPhysFlags = []): Integer;
// properties set, if lpfAdjustTo... is used
property LastLogicalResultPos: Integer read FLastLogicalResultPos;
//property LastLogicalResultColOffs: Integer read FLastLogicalResultColOffs;
property LastPhysicalResultPos: Integer read FLastPhysicalResultPos;
// By LogToPhys
property AdjustedLogToPhysOrigin: Integer read FAdjustedLogToPhysOrigin;
// By PhysToLog
property AdjustedPhysToLogOrigin: Integer read FAdjustedPhysToLogOrigin;
property UnAdjustedPhysToLogResult: Integer read FUnAdjustedPhysToLogResult;
property UnAdjustedPhysToLogColOffs: Integer read FUnAdjustedPhysToLogColOffs;
end;
(*
@ -589,6 +606,20 @@ begin
FCurrentLine := AIndex;
end;
function TSynLogicalPhysicalConvertor.GetCurrentWidths: PPhysicalCharWidth;
begin
if FCurrentWidthsLen > 0
then Result := @FCurrentWidths[0]
else Result := nil;
end;
function TSynLogicalPhysicalConvertor.GetCurrentLine: Integer;
begin
if (FLines.TextChangeStamp = FTextChangeStamp) and (FLines.ViewChangeStamp = FViewChangeStamp)
then Result := FCurrentLine
else Result := -1;
end;
procedure TSynLogicalPhysicalConvertor.SetWidthsForLine(AIndex: Integer;
ANewWidths: TPhysicalCharWidths);
begin
@ -625,11 +656,24 @@ begin
end;
function TSynLogicalPhysicalConvertor.PhysicalToLogical(AIndex, AColumn: Integer; out
AColOffset: Integer; ACharSide: TSynPhysCharSide{; AFlags: TSynLogPhysFlags}): Integer;
AColOffset: Integer; ACharSide: TSynPhysCharSide; AFlags: TSynLogPhysFlags): Integer;
var
BytePos, ScreenPos, ScreenPosOld: integer;
RtlPos, RtlScreen: Integer;
begin
FAdjustedPhysToLogOrigin := AColumn;
FUnAdjustedPhysToLogResult := AColumn;
FUnAdjustedPhysToLogColOffs := 0;
AColOffset := 0;
{$IFnDEF WithOutSynBiDi}
if (AColumn = 0) or ((AColumn = 1) and (ACharSide in [cspLeft, cspFollowLtr])) then
exit(AColumn);
{$ELSE}
if (AColumn = 0) or (AColumn = 1) then
exit(AColumn);
{$ENDIF}
PrepareWidthsForLine(AIndex);
ScreenPos := 1;
@ -644,6 +688,7 @@ begin
// currently Ltr
if (ScreenPos = AColumn) and (ACharSide in [cspLeft, cspFollowLtr]) then begin
AColOffset := 0;
FUnAdjustedPhysToLogResult := BytePos+1;
exit(BytePos+1);
end;
@ -667,6 +712,7 @@ begin
ScreenPos := ScreenPos + RtlScreen;
if (ScreenPos = AColumn) and (ACharSide in [cspLeft, cspFollowRtl]) then begin
AColOffset := 0;
FUnAdjustedPhysToLogResult := BytePos+1;
exit(BytePos+1);
end
else
@ -684,6 +730,21 @@ begin
if (ScreenPos < AColumn) then begin
AColOffset := ScreenPosOld - AColumn;
FUnAdjustedPhysToLogResult := BytePos;
FUnAdjustedPhysToLogColOffs := AColOffset;
if (AColOffset <> 0) then begin
if lpfAdjustToCharBegin in AFlags then begin
FAdjustedPhysToLogOrigin := ScreenPosOld;
AColOffset := 0;
end
else if lpfAdjustToNextChar in AFlags then begin
FAdjustedPhysToLogOrigin := ScreenPos;
AColOffset := 0;
while (BytePos < FCurrentWidthsLen) and ((FCurrentWidths[BytePos] and PCWMask) = 0) do
inc(BytePos);
inc(BytePos);
end;
end;
exit(BytePos);
end;
@ -691,6 +752,7 @@ begin
if (ScreenPos = AColumn) then begin
AColOffset := 0;
FUnAdjustedPhysToLogResult := BytePos+1;
exit(BytePos+1);
end;
@ -712,6 +774,21 @@ begin
if ScreenPos > AColumn then begin
AColOffset := AColumn - ScreenPosOld;
FUnAdjustedPhysToLogResult := BytePos;
FUnAdjustedPhysToLogColOffs := AColOffset;
if (AColOffset <> 0) then begin
if lpfAdjustToCharBegin in AFlags then begin
FAdjustedPhysToLogOrigin := ScreenPosOld;
AColOffset := 0;
end
else if lpfAdjustToNextChar in AFlags then begin
FAdjustedPhysToLogOrigin := ScreenPos;
AColOffset := 0;
while (BytePos < FCurrentWidthsLen) and ((FCurrentWidths[BytePos] and PCWMask) = 0) do
inc(BytePos);
inc(BytePos);
end;
end;
exit(BytePos);
end;
end;
@ -722,6 +799,21 @@ begin
inc(BytePos);
if ScreenPos > AColumn then begin
AColOffset := AColumn - ScreenPosOld;
FUnAdjustedPhysToLogResult := BytePos;
FUnAdjustedPhysToLogColOffs := AColOffset;
if (AColOffset <> 0) then begin
if lpfAdjustToCharBegin in AFlags then begin
FAdjustedPhysToLogOrigin := ScreenPosOld;
AColOffset := 0;
end
else if lpfAdjustToNextChar in AFlags then begin
FAdjustedPhysToLogOrigin := ScreenPos;
AColOffset := 0;
while (BytePos < FCurrentWidthsLen) and ((FCurrentWidths[BytePos] and PCWMask) = 0) do
inc(BytePos);
inc(BytePos);
end;
end;
exit(BytePos);
end;
end;
@ -730,6 +822,7 @@ begin
// Column at/past end of line
AColOffset := 0;
Result := BytePos + 1 + AColumn - ScreenPos;
FUnAdjustedPhysToLogResult := Result;
end;
function TSynLogicalPhysicalConvertor.LogicalToPhysical(AIndex,
@ -747,8 +840,7 @@ var
i: integer;
RtlLen: Integer;
begin
FLastLogicalResultPos := ABytePos;
FLastPhysicalResultPos := ABytePos;
FAdjustedLogToPhysOrigin := ABytePos;
{$IFDEF AssertSynMemIndex}
if (ABytePos <= 0) then
raise Exception.Create(Format('Bad Bytpos for PhystoLogical BytePos=%d ColOffs= %d idx= %d',[ABytePos, AColOffset, AIndex]));
@ -776,7 +868,7 @@ begin
while (ABytePos < FCurrentWidthsLen) and ((FCurrentWidths[ABytePos] and PCWMask) = 0) do
inc(ABytePos);
end;
FLastLogicalResultPos := ABytePos+1;
FAdjustedLogToPhysOrigin := ABytePos+1;
assert((FCurrentWidths[ABytePos] and PCWMask) <> 0, 'LogicalToPhysical at char');
end;
if ABytePos < FCurrentWidthsLen then
@ -834,7 +926,6 @@ begin
then
Result := Result + RtlLen;
{$ENDIF}
FLastPhysicalResultPos := Result;
end;
{ TSynEditStrings }

View File

@ -7076,6 +7076,8 @@ var
nAction: TSynReplaceAction;
CurReplace: string;
ptFoundStart, ptFoundEnd: TPoint;
ptFoundStartSel, ptFoundEndSel: TPoint;
function InValidSearchRange(First, Last: integer): boolean;
begin
@ -7089,6 +7091,16 @@ var
end;
end;
procedure SetFoundCaretAndSel;
begin
if ptFoundStartSel.y < 0 then
exit;
BlockBegin := ptFoundStartSel;
if bBackward then LogicalCaretXY := BlockBegin;
BlockEnd := ptFoundEndSel;
if not bBackward then LogicalCaretXY := ptFoundEndSel;
end;
begin
Result := 0;
// can't search for or replace an empty string
@ -7139,6 +7151,7 @@ begin
// search while the current search position is inside of the search range
IncPaintLock;
try
ptFoundStartSel.y := -1;
//DebugLn(['TCustomSynEdit.SearchReplace ptStart=',dbgs(ptStart),' ptEnd=',dbgs(ptEnd),' ASearch="',dbgstr(ASearch),'" AReplace="',dbgstr(AReplace),'"']);
while fTSearch.FindNextOne(FTheLinesView,ptStart,ptEnd,ptFoundStart,ptFoundEnd, True) do
begin
@ -7152,10 +7165,9 @@ begin
Inc(Result);
// Select the text, so the user can see it in the OnReplaceText event
// handler or as the search result.
BlockBegin := ptFoundStart;
if bBackward then LogicalCaretXY := BlockBegin;
BlockEnd := ptFoundEnd;
if not bBackward then LogicalCaretXY := ptFoundEnd;
ptFoundStartSel := ptFoundStart;
ptFoundEndSel := ptFoundEnd;
//SetFoundCaretAndSel;
// If it's a 'search' only we can leave the procedure now.
if not (bReplace or bReplaceAll) then exit;
// Prompt and replace or replace all. If user chooses to replace
@ -7164,6 +7176,7 @@ begin
if ssoRegExpr in AOptions then
CurReplace:=fTSearch.RegExprReplace;
if bPrompt and Assigned(fOnReplaceText) then begin
SetFoundCaretAndSel;
EnsureCursorPosVisible;
try
DecPaintLock;
@ -7184,7 +7197,8 @@ begin
end;
// replace text
//DebugLn(['TCustomSynEdit.SearchReplace OldSel="',dbgstr(SelText),'"']);
SetSelTextExternal(CurReplace);
//SetSelTextExternal(CurReplace);
SetTextBetweenPoints(ptFoundStart, ptFoundEnd, CurReplace, [setSelect], scamIgnore);
//DebugLn(['TCustomSynEdit.SearchReplace NewSel="',dbgstr(SelText),'"']);
// adjust positions
ptEnd:=AdjustPositionAfterReplace(ptEnd,ptFoundStart,ptFoundEnd,
@ -7212,6 +7226,7 @@ begin
//DebugLn(['TCustomSynEdit.SearchReplace FIND NEXT ptStart=',dbgs(ptStart),' ptEnd=',dbgs(ptEnd)]);
end;
finally
SetFoundCaretAndSel;
DecPaintLock;
end;
end;

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ interface
uses
Classes, SysUtils, testregistry, LCLProc, LCLType, Forms, TestBase,
SynEdit, SynEditTextTrimmer, SynEditKeyCmds, LazSynEditText;
SynEdit, SynEditTextTrimmer, SynEditKeyCmds, LazSynEditText, SynEditPointClasses;
type
@ -23,19 +23,26 @@ type
TrimType: TSynEditStringTrimmingType;
TrimEnabled: Boolean;
protected
function TestMaxLeftProc: Integer;
procedure ReCreateEdit; reintroduce;
published
procedure TestEditEmpty;
procedure TestEditTabs;
procedure TestEditEcChar;
procedure TestPhysicalLogical;
procedure TestPhysicalLogical; // TODO adjust to char tests
procedure TestLogicalAdjust;
procedure TestCaretObject;
procedure TestCaretAutoMove;
procedure TestCaretDeleteWord_LastWord;
end;
implementation
function TTestBasicSynEdit.TestMaxLeftProc: Integer;
begin
Result := 6000;
end;
procedure TTestBasicSynEdit.ReCreateEdit;
begin
inherited ReCreateEdit;
@ -452,7 +459,7 @@ procedure TTestBasicSynEdit.TestPhysicalLogical;
expXcsRtl: integer = -1; expColCsRtl: integer = -1);
var
gotX, gotCol: Integer;
expDef, expColDef: Integer;
expDef: Integer;
begin
name := name + ' y='+inttostr(y)+' x='+inttostr(x);
@ -857,6 +864,538 @@ begin
end;
procedure TTestBasicSynEdit.TestCaretObject;
var
TestCaret, TestCaret2: TSynEditCaret;
TestOrder: Integer;
UseAdjustToNextChar, UseIncAdjustToNextChar: Boolean;
UseAllowPastEOL, UseIncAllowPastEOL: Boolean;
UseKeepCaretX: Boolean;
UseLock: Boolean;
UseChangeOnTouch: Boolean;
UseIncAutoMoveOnEdit: Boolean;
UseSkipTabs: Boolean;
UseMaxLeft: Boolean;
procedure CheckPhys(AName: String; ExpY, ExpX: Integer);
procedure CheckEach;
begin
AssertEquals(AName + 'Phys.Y', ExpY, TestCaret.LinePos);
AssertEquals(AName + 'Phys.X', ExpX, TestCaret.CharPos);
end;
procedure CheckPoint;
begin
AssertEquals(AName + 'Phys.XY.Y', ExpY, TestCaret.LineCharPos.Y);
AssertEquals(AName + 'Phys.XY.X', ExpX, TestCaret.LineCharPos.x);
end;
begin
if ExpX <= 0 then exit;
AName := BaseTestName + ' ' + AName;
if (TestOrder and 1) = 0 then begin
CheckEach;
CheckPoint;
end else begin
CheckPoint;
CheckEach;
end;
end;
procedure CheckIsAtChar(AName: String; ExpY, ExpX: Integer);
begin
if ExpX <= 0 then exit;
AName := BaseTestName + ' ' + AName;
if (TestOrder and 1) = 1 then exit; // Only one order
AssertEquals(AName + 'IsAtLineChar', True, TestCaret.IsAtLineChar(point(ExpX, ExpY)));
AssertEquals(AName + 'NOT IsAtLineChar', False, TestCaret.IsAtLineChar(point(ExpX+1, ExpY)));
end;
procedure CheckLog(AName: String; ExpY, ExpX, ExpOffs: Integer);
procedure CheckEach;
begin
AssertEquals(AName + 'Log.Y', ExpY, TestCaret.LinePos);
AssertEquals(AName + 'Log.X', ExpX, TestCaret.BytePos);
AssertEquals(AName + 'Log.Offs', ExpOffs, TestCaret.BytePosOffset);
end;
procedure CheckPoint;
begin
AssertEquals(AName + 'Log.XY.Y', ExpY, TestCaret.LineBytePos.y);
AssertEquals(AName + 'Log.XY.X', ExpX, TestCaret.LineBytePos.x);
end;
begin
if ExpX <= 0 then exit;
AName := BaseTestName + ' ' + AName;
AName := BaseTestName + ' ' + AName;
if (TestOrder and 1) = 0 then begin
CheckEach;
CheckPoint;
end else begin
CheckPoint;
CheckEach;
end;
end;
procedure CheckIsAtByte(AName: String; ExpY, ExpX, ExpOffs: Integer);
begin
if ExpX <= 0 then exit;
AName := BaseTestName + ' ' + AName;
if (TestOrder and 1) = 1 then exit; // Only one order
AssertEquals(AName + 'IsAtLineChar', True, TestCaret.IsAtLineByte(point(ExpX, ExpY), ExpOffs));
AssertEquals(AName + 'NOT IsAtLineByte', False, TestCaret.IsAtLineByte(point(ExpX+1, ExpY), ExpOffs));
if ExpOffs = 0 then begin
AssertEquals(AName + 'IsAtLineChar', True, TestCaret.IsAtLineByte(point(ExpX, ExpY)));
AssertEquals(AName + 'NOT IsAtLineByte', False, TestCaret.IsAtLineByte(point(ExpX+1, ExpY)));
AssertEquals(AName + 'NOT IsAtLineByte', False, TestCaret.IsAtLineByte(point(ExpX, ExpY), 1));
end;
end;
procedure CheckLogPhys(AName: String; ExpY, ExpX, ExpLogX, ExpOffs: Integer);
procedure CheckAtPos;
procedure CheckAtPosChar;
begin
TestCaret2.LineBytePos := point(1, 1);
TestCaret2.LineCharPos := point(ExpX, ExpY);
AssertTrue(AName + 'IsAtPos(Char)', TestCaret.IsAtPos(TestCaret2));
TestCaret2.LineCharPos := point(1, ExpY-1);
AssertFalse(AName + 'not IsAtPos(Char)', TestCaret.IsAtPos(TestCaret2));
end;
procedure CheckAtPosByte;
begin
TestCaret2.LineCharPos := point(1, 1);
TestCaret2.LineBytePos := point(ExpLogX, ExpY);
//TestCaret2.BytePosOffset := ExpOffs;
if ExpOffs = 0 then // TODO
AssertTrue(AName + 'IsAtPos(Byte)', TestCaret.IsAtPos(TestCaret2));
TestCaret2.LineBytePos := point(1, ExpY-1);
AssertFalse(AName + 'not IsAtPos(Byte)', TestCaret.IsAtPos(TestCaret2));
end;
begin
if ((TestOrder and 1) = 0) and (ExpX > 0) then CheckAtPosChar;
if (ExpLogX > 0) then CheckAtPosByte;
if ((TestOrder and 1) = 1) and (ExpX > 0) then CheckAtPosChar;
end;
procedure CheckPos;
begin
if (TestOrder and 2) = 0 then begin
if ExpX > 0 then CheckPhys(AName, ExpY, ExpX);
if ExpLogX > 0 then CheckLog (AName, ExpY, ExpLogX, ExpOffs);
end else begin
if ExpLogX > 0 then CheckLog (AName, ExpY, ExpLogX, ExpOffs);
if ExpX > 0 then CheckPhys(AName, ExpY, ExpX);
end;
end;
begin
if (TestOrder and 8) = 8 then
CheckAtPos;
if (TestOrder and 4) = 4 then
CheckPos;
if (TestOrder and 2) = 2 then begin
CheckIsAtChar(AName, ExpY, ExpX);
CheckIsAtByte(AName, ExpY, ExpLogX, ExpOffs);
end else begin
CheckIsAtByte(AName, ExpY, ExpLogX, ExpOffs);
CheckIsAtChar(AName, ExpY, ExpX);
end;
if (TestOrder and 4) = 0 then
CheckPos;
if (TestOrder and 8) = 0 then
CheckAtPos;
end;
Procedure DoOneTest(AName: String; AY, AX, ALogX, ALogOffs: Integer;
ExpY, ExpX, ExpLogX, ExpLogOffs: Integer;
// X,LogX,LogOffs, [X,LogX,LogOffs]
AMoveHorizNext: array of integer; AMoveHorizPrev: array of integer;
ExpMoveHorizFalse: Boolean = False;
ALineForKeepX: Integer = -1; ExpNotKeptX: Integer = -1; ExpNotKeptLogX: Integer = -1
);
procedure DoOneSetLine(Y: Integer);
begin
if UseLock then TestCaret.Lock;
if UseChangeOnTouch then TestCaret.ChangeOnTouch;
TestCaret.LinePos := Y;
if UseLock then TestCaret.Unlock;
end;
procedure DoOneSetChar(Y, X: Integer; ChangeToLine: Integer = -1);
begin
if UseLock then TestCaret.Lock;
if UseChangeOnTouch then TestCaret.ChangeOnTouch;
TestCaret.LineCharPos := point(X, Y);
if ChangeToLine > 0 then TestCaret.LinePos := ChangeToLine;
if UseLock then TestCaret.Unlock;
end;
procedure DoOneSetByte(Y, X, {%H-}O: Integer; ChangeToLine: Integer = -1);
begin
if UseLock then TestCaret.Lock;
if UseChangeOnTouch then TestCaret.ChangeOnTouch;
TestCaret.LineBytePos := point(X, Y);
//TestCaret.BytePosOffset := O;
if ChangeToLine > 0 then TestCaret.LinePos := ChangeToLine;
if UseLock then TestCaret.Unlock;
end;
procedure DoOneMoveHoriz(C: Integer);
begin
if UseLock then TestCaret.Lock;
if UseChangeOnTouch then TestCaret.ChangeOnTouch;
AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(C));
if UseLock then TestCaret.Unlock;
end;
procedure DoOneMoveHorizFromChar(C, Y, X: Integer);
begin
if UseLock then TestCaret.Lock;
if UseChangeOnTouch then TestCaret.ChangeOnTouch;
TestCaret.LineCharPos := point(X, Y);
AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(C));
if UseLock then TestCaret.Unlock;
end;
procedure DoOneMoveHorizFromByte(C, Y, X, {%H-}O: Integer);
begin
if UseLock then TestCaret.Lock;
if UseChangeOnTouch then TestCaret.ChangeOnTouch;
TestCaret.LineBytePos := point(X, Y);
//TestCaret.BytePosOffset := O;
AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(C));
if UseLock then TestCaret.Unlock;
end;
begin
DoOneSetChar(AY, AX);
CheckLogPhys(AName + ' from CharPos', ExpY, ExpX, ExpLogX, ExpLogOffs);
// KeepX
if (ALineForKeepX > 0) and UseKeepCaretX then begin
if (TestOrder and 4) = 4 then begin
TestCaret.LineCharPos := point(1, 1);
if (TestOrder and 6) = 4 then
TestCaret.LineCharPos := point(AX, AY);
end;
if (TestOrder and 6) = 6
then DoOneSetChar(AY, AX, ALineForKeepX)
else DoOneSetLine(ALineForKeepX);
CheckLogPhys(AName + ' from CharPos', ALineForKeepX, ExpNotKeptX, ExpNotKeptLogX, 0);
if ExpNotKeptX < 0 then AssertFalse(AName + '(char) keepx moved (c)', ExpX = TestCaret.CharPos);
if ExpNotKeptLogX < 0 then AssertFalse(AName + '(char) keepx moved (b)', ExpLogX = TestCaret.BytePos);
DoOneSetLine(AY);
CheckLogPhys(AName + ' from CharPos', ExpY, ExpX, ExpLogX, ExpLogOffs);
end;
if length(AMoveHorizNext) >= 3 then begin
DoOneMoveHorizFromChar(1, AY, AX);
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizNext[0], AMoveHorizNext[1], AMoveHorizNext[2]);
if not ExpMoveHorizFalse then begin
DoOneMoveHoriz(-1);
CheckLogPhys(AName + ' from CharPos', ExpY, ExpX, ExpLogX, ExpLogOffs);
end;
end;
if length(AMoveHorizNext) >= 6 then begin
TestCaret.LineCharPos := point(AX, AY);
if UseLock then TestCaret.Lock;
if UseChangeOnTouch then TestCaret.ChangeOnTouch;
AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(1));
AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(1));
if UseLock then TestCaret.Unlock;
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizNext[3], AMoveHorizNext[4], AMoveHorizNext[5]);
if not ExpMoveHorizFalse then begin
DoOneMoveHoriz(-1);
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizNext[0], AMoveHorizNext[1], AMoveHorizNext[2]);
DoOneMoveHoriz(-1);
CheckLogPhys(AName + ' from CharPos', ExpY, ExpX, ExpLogX, ExpLogOffs);
TestCaret.LineCharPos := point(AX, AY);
DoOneMoveHoriz(2);
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizNext[3], AMoveHorizNext[4], AMoveHorizNext[5]);
end;
end;
if length(AMoveHorizPrev) >= 3 then begin
DoOneMoveHorizFromChar(-1, AY, AX);
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizPrev[0], AMoveHorizPrev[1], AMoveHorizPrev[2]);
if not ExpMoveHorizFalse then begin
DoOneMoveHoriz(1);
CheckLogPhys(AName + ' from CharPos', ExpY, ExpX, ExpLogX, ExpLogOffs);
end;
end;
if length(AMoveHorizPrev) >= 6 then begin
TestCaret.LineCharPos := point(AX, AY);
if UseLock then TestCaret.Lock;
if UseChangeOnTouch then TestCaret.ChangeOnTouch;
AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(-1));
AssertEquals(AName + 'MoveHoriz is '+dbgs(ExpMoveHorizFalse), not ExpMoveHorizFalse, TestCaret.MoveHoriz(-1));
if UseLock then TestCaret.Unlock;
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizPrev[3], AMoveHorizPrev[4], AMoveHorizPrev[5]);
if not ExpMoveHorizFalse then begin
DoOneMoveHoriz(1);
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizPrev[0], AMoveHorizPrev[1], AMoveHorizPrev[2]);
DoOneMoveHoriz(1);
CheckLogPhys(AName + ' from CharPos', ExpY, ExpX, ExpLogX, ExpLogOffs);
TestCaret.LineCharPos := point(AX, AY);
DoOneMoveHoriz(-2);
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizPrev[3], AMoveHorizPrev[4], AMoveHorizPrev[5]);
end;
end;
// Logical
if ALogOffs <> 0 then exit; // TODO;
if ALogX > 0 then begin
DoOneSetByte(AY, ALogX, ALogOffs);
CheckLogPhys(AName + ' from BytePos', ExpY, ExpX, ExpLogX, ExpLogOffs);
// KeepX
if (ALineForKeepX > 0) and UseKeepCaretX then begin
if (TestOrder and 4) = 4 then begin
TestCaret.LineBytePos := point(1, 1);
if (TestOrder and 6) = 4 then
TestCaret.LineBytePos := point(ALogX, AY);
end;
if (TestOrder and 6) = 6
then DoOneSetByte(AY, ALogX, ALogOffs, ALineForKeepX)
else DoOneSetLine(ALineForKeepX);
CheckLogPhys(AName + ' from CharPos', ALineForKeepX, ExpNotKeptX, ExpNotKeptLogX, 0);
if ExpNotKeptX < 0 then AssertFalse(AName + '(char) keepx moved (c)', ExpX = TestCaret.CharPos);
if ExpNotKeptLogX < 0 then AssertFalse(AName + '(char) keepx moved (b)', ExpLogX = TestCaret.BytePos);
DoOneSetLine(AY);
CheckLogPhys(AName + ' from CharPos', ExpY, ExpX, ExpLogX, ExpLogOffs);
end;
if length(AMoveHorizNext) >= 3 then begin
DoOneMoveHorizFromByte(1, AY, ALogX, ALogOffs);
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizNext[0], AMoveHorizNext[1], AMoveHorizNext[2]);
if not ExpMoveHorizFalse then begin
DoOneMoveHoriz(-1);
CheckLogPhys(AName + ' from CharPos', ExpY, ExpX, ExpLogX, ExpLogOffs);
end;
end;
if length(AMoveHorizNext) >= 6 then begin
TestCaret.LineBytePos := point(ALogX, AY);
if UseLock then TestCaret.Lock;
if UseChangeOnTouch then TestCaret.ChangeOnTouch;
TestCaret.MoveHoriz(1);
TestCaret.MoveHoriz(1);
if UseLock then TestCaret.Unlock;
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizNext[3], AMoveHorizNext[4], AMoveHorizNext[5]);
if not ExpMoveHorizFalse then begin
DoOneMoveHoriz(-1);
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizNext[0], AMoveHorizNext[1], AMoveHorizNext[2]);
DoOneMoveHoriz(-1);
CheckLogPhys(AName + ' from CharPos', ExpY, ExpX, ExpLogX, ExpLogOffs);
TestCaret.LineBytePos := point(ALogX, AY);
DoOneMoveHoriz(2);
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizNext[3], AMoveHorizNext[4], AMoveHorizNext[5]);
end;
end;
if length(AMoveHorizPrev) >= 3 then begin
DoOneMoveHorizFromByte(-1, AY, ALogX, ALogOffs);
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizPrev[0], AMoveHorizPrev[1], AMoveHorizPrev[2]);
if not ExpMoveHorizFalse then begin
DoOneMoveHoriz(1);
CheckLogPhys(AName + ' from CharPos', ExpY, ExpX, ExpLogX, ExpLogOffs);
end;
end;
if length(AMoveHorizPrev) >= 6 then begin
TestCaret.LineBytePos := point(ALogX, AY);
if UseLock then TestCaret.Lock;
if UseChangeOnTouch then TestCaret.ChangeOnTouch;
TestCaret.MoveHoriz(-1);
TestCaret.MoveHoriz(-1);
if UseLock then TestCaret.Unlock;
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizPrev[3], AMoveHorizPrev[4], AMoveHorizPrev[5]);
if not ExpMoveHorizFalse then begin
DoOneMoveHoriz(1);
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizPrev[0], AMoveHorizPrev[1], AMoveHorizPrev[2]);
DoOneMoveHoriz(1);
CheckLogPhys(AName + ' from CharPos', ExpY, ExpX, ExpLogX, ExpLogOffs);
TestCaret.LineBytePos := point(ALogX, AY);
DoOneMoveHoriz(-2);
CheckLogPhys(AName + ' from CharPos', ExpY, AMoveHorizPrev[3], AMoveHorizPrev[4], AMoveHorizPrev[5]);
end;
end;
end;
end;
procedure DoTests;
begin
if (TestOrder >= 4) and (UseIncAllowPastEOL or UseIncAdjustToNextChar) then
exit;
ReCreateEdit;
SynEdit.TabWidth := 6;
SetLines(['x',
StringOfChar('x', 40),
' äääbc', // EOL = 7, 10
'X嗚呼あ嗚呼嗚呼あ嗚呼嗚呼あ嗚呼嗚呼あ',
'嗚呼嗚呼あ嗚呼嗚呼あ嗚呼嗚呼あ嗚呼嗚呼あ',
' '#9#9#9'mn',
'',
'X ab',
'']);
try
PushBaseName(Format('UseLock=%s, AdjustToNextChar=%s, IncAdjustToNextChar=%s,'+
' AllowPastEOL=%s, IncAllowPastEOL=%s, KeepCaretX=%s,' +
' ChangeOnTouch=%s, IncAutoMoveOnEdit=%s, SkipTabs=%s' +
' MaxLeft=%s',
[dbgs(UseLock), dbgs(UseAdjustToNextChar), dbgs(UseIncAdjustToNextChar),
dbgs(UseAllowPastEOL), dbgs(UseIncAllowPastEOL),
dbgs(UseKeepCaretX), dbgs(UseChangeOnTouch),
dbgs(UseIncAutoMoveOnEdit), dbgs(UseSkipTabs), dbgs(UseMaxLeft)
]));
//debugln(BaseTestName);
TestCaret := TSynEditCaret.Create;
TestCaret.Lines := SynEdit.ViewedTextBuffer;
TestCaret.AdjustToNextChar := UseAdjustToNextChar;
if UseIncAdjustToNextChar then TestCaret.IncForceAdjustToNextChar;
TestCaret.AllowPastEOL := UseAllowPastEOL;
if UseIncAllowPastEOL then TestCaret.IncForcePastEOL;
TestCaret.KeepCaretX := UseKeepCaretX;
if UseIncAutoMoveOnEdit then TestCaret.IncAutoMoveOnEdit;
TestCaret.SkipTabs := UseSkipTabs;
if UseMaxLeft then
TestCaret.MaxLeftChar := @TestMaxLeftProc; // 6000
TestCaret2 := TSynEditCaret.Create;
TestCaret2.Lines := SynEdit.ViewedTextBuffer;
if UseAdjustToNextChar or UseIncAdjustToNextChar
then DoOneTest('Basic', 2, 3, 3, 0, 2, 3, 3, 0,
[4,4,0, 5,5,0], [2,2,0, 1,1,0], False, // MoveHoriz
4, 4, 5 // KeepX
)
else DoOneTest('Basic', 2, 3, 3, 0, 2, 3, 3, 0,
[4,4,0, 5,5,0], [2,2,0, 1,1,0], False, // MoveHoriz
4, 2, 2 // KeepX
);
// past EOL
if UseAllowPastEOL or UseIncAllowPastEOL
then DoOneTest('past EOL', 8, 9, 9, 0, 8, 9, 9, 0,
[10,10,0], [8,8,0])
else DoOneTest('past EOL', 8, 9, 9, 0, 8, 5, 5, 0,
[5,5,0, 5,5,0],[], True);
// BOL
DoOneTest('at BOL', 8, 1, 1, 0, 8, 1, 1, 0,
[],[1,1,0, 1,1,0], True);
// one past EOL
if UseAllowPastEOL or UseIncAllowPastEOL
then DoOneTest('one past EOL', 3, 8,11, 0, 3, 8,11, 0,
[ 9,12,0, 10,13,0], [7,10,0, 6,9,0])
else DoOneTest('one past EOL', 3, 8,11, 0, 3, 7,10, 0,
[7,10,0, 7,10,0], [], True);
// MaxLeftChar 6000 (5999 char / EOL = 6000)
if UseAllowPastEOL or UseIncAllowPastEOL
then if UseMaxLeft
then DoOneTest('past EOL', 3, 6001, 6004, 0, 3, 6000, 6003, 0,
[6000, 6003, 0], [], True)
else DoOneTest('past EOL', 3, 6001, 6004, 0, 3, 6001, 6004, 0,
[], [])
else DoOneTest('past EOL', 3, 6001, 6004, 0, 3, 7,10, 0,
[],[]
);
// ' äääbc'
if UseAdjustToNextChar or UseIncAdjustToNextChar
then DoOneTest('LogPhys', 3, 4, 6, 0, 3, 4, 6, 0,
[5,8,0, 6,9,0], [3,4,0, 2,2,0], False,
5, 5, 7
)
else DoOneTest('LogPhys', 3, 4, 6, 0, 3, 4, 6, 0,
[5,8,0, 6,9,0], [3,4,0, 2,2,0], False,
5, 3, 4
);
// 'X嗚呼あ' // skip "from byte"
if UseAdjustToNextChar or UseIncAdjustToNextChar
then DoOneTest('Mid Dbl-Width', 4, 3, -3, 0, 4, 4, 5, 0,
[6,8,0], [2,2,0], False,
5, 5, 7
)
else DoOneTest('Mid Dbl-Width', 4, 3, -3, 0, 4, 2, 2, 0,
[4,5,0], [1,1,0], False,
5, 1, 1
);
// ' '#9#9#9'mn' // skip "from byte" TODO
if UseSkipTabs
then if UseAdjustToNextChar or UseIncAdjustToNextChar
then DoOneTest('Mid Tab', 6, 8, -3, 1, 6,13, 4, 0,
[19,5,0, 20,6,0], [7,3,0, 2,2,0], False
//7,1,1
)
else if UseAllowPastEOL or UseIncAllowPastEOL
then DoOneTest('Mid Tab', 6, 8, -3, 1, 6, 7, 3, 0,
[13,4,0, 19,5,0], [2,2,0, 1,1,0], False
)
else DoOneTest('Mid Tab', 6, 8, -3, 1, 6, 7, 3, 0,
[13,4,0, 19,5,0], [2,2,0, 1,1,0], False,
7,1,1
)
else if UseAllowPastEOL or UseIncAllowPastEOL
then DoOneTest('Mid Tab', 6, 8, -3, 1, 6, 8, 3, 1,
[9,3,2, 10,3,3], [7,3,0, 6,2,4], False
)
else DoOneTest('Mid Tab', 6, 8, -3, 1, 6, 8, 3, 1,
[9,3,2, 10,3,3], [7,3,0, 6,2,4], False,
7,1,1
);
finally
PopBaseName;
FreeAndNil(TestCaret);
FreeAndNil(TestCaret2);
end;
end;
begin
for TestOrder := 0 to 11 do // CheckAtPos (8) only runs first 4
for UseLock := low(Boolean) to high(Boolean) do
for UseAdjustToNextChar := low(Boolean) to high(Boolean) do
for UseIncAdjustToNextChar := low(Boolean) to high(Boolean) do
for UseAllowPastEOL := low(Boolean) to high(Boolean) do
for UseIncAllowPastEOL := low(Boolean) to high(Boolean) do
for UseKeepCaretX := low(Boolean) to high(Boolean) do
for UseChangeOnTouch := low(Boolean) to high(Boolean) do
for UseIncAutoMoveOnEdit := low(Boolean) to high(Boolean) do
for UseSkipTabs := low(Boolean) to high(Boolean) do
for UseMaxLeft := low(Boolean) to high(Boolean) do
// OldPos, MoveHoriz
// IsAtPos /
DoTests;
end;
procedure TTestBasicSynEdit.TestCaretAutoMove;