unit TestNavigation; (* TODO: Word Left/Right: Need tests with fold, trim, selection *) {$mode objfpc}{$H+} interface uses SysUtils, testregistry, LCLProc, Forms, TestBase, SynEdit, SynEditKeyCmds, SynEditTypes; type { TTestSynNavigation } TTestSynNavigation = class(TTestBase) published procedure TestCaretHomeEnd; procedure TestCaretLeftRight; procedure TestCaretMoveLeftRightWord; //procedure TestCaretMoveLeftRightPartWord; end; implementation { TTestSynNavigation } procedure TTestSynNavigation.TestCaretHomeEnd; procedure DoInit1; begin ReCreateEdit; SynEdit.TabWidth := 6; SyneDit.Options := []; SyneDit.Options2 := []; SetLines(['', //1 '', 'test', // 3 '', '', ' ', 'test', // 3 #9#9#9#9#9, ' spaced', // 9 '', '', ' ', ' spaced', #9#9#9#9#9, #9'tabbed', // 15 '', '', ' ', #9'tabbed', #9#9#9#9#9, #9' tabbed spaced', // 21 '', '', ' ', #9' tabbed spaced', #9#9#9#9#9, ' ', // 27 (space only) '', '', ' ', #9' ', #9#9#9#9#9, ' spaced 9 for tab', // 33 #9#9#9#9#9, ' X ', // 35 ' X'#9, // 36 '' ]); end; procedure TestHome(Name:String; X, Y, ExpX1, ExpX2, ExpX3: Integer; ExpLTxTStartX: Integer = -1); begin SetCaretPhys(X, Y); synedit.CommandProcessor(ecLineStart, '', nil); TestIsCaretPhys(Name + '(1st home)', ExpX1,Y); synedit.CommandProcessor(ecLineStart, '', nil); TestIsCaretPhys(Name + '(2nd home)', ExpX2,Y); synedit.CommandProcessor(ecLineStart, '', nil); TestIsCaretPhys(Name + '(3rd home)', ExpX3,Y); if ExpLTxTStartX > 0 then begin SetCaretPhys(X, Y); synedit.CommandProcessor(ecLineTextStart, '', nil); TestIsCaretPhys(Name + '(1st line-text-start)', ExpLTxTStartX,Y); synedit.CommandProcessor(ecLineTextStart, '', nil); TestIsCaretPhys(Name + '(2nd line-text-start)', ExpLTxTStartX,Y); synedit.CommandProcessor(ecLineTextStart, '', nil); TestIsCaretPhys(Name + '(3rd line-text-start)', ExpLTxTStartX,Y); end; end; procedure TestEnd(Name:String; X, Y, ExpX1, ExpX2, ExpX3: Integer); begin SetCaretPhys(X, Y); synedit.CommandProcessor(ecLineEnd, '', nil); TestIsCaretPhys(Name + '(1st end)', ExpX1,Y); synedit.CommandProcessor(ecLineEnd, '', nil); TestIsCaretPhys(Name + '(2nd end)', ExpX2,Y); synedit.CommandProcessor(ecLineEnd, '', nil); TestIsCaretPhys(Name + '(3rd end)', ExpX3,Y); end; begin // None Smart-Home: // Caret goes x=1, then x=indend // Smart-Home: // Caret goes x=indent, then 1, IF AFTER indent, or at x=1 // Caret goes x=1, then x=indend, IF at x=indent, or inside indent // Both // Caret does not go past eol, unless explicitly enabled // if spaces/tab exist at start of otherwise empty line, caret will go to prev-line indent {%region} DoInit1; PushBaseName('no smart / no past-eol'); TestHome('empty 1st line', 1, 1, 1,1,1, 1); TestHome('empty 2nd line', 1, 2, 1,1,1, 1); TestHome('unindented line', 1, 3, 1,1,1, 1); TestHome('unindented line x-in-line', 4, 3, 1,1,1, 1); TestHome('1st line after unindendet', 1, 4, 1,1,1, 1); TestHome('2nd line after unindendet', 1, 5, 1,1,1, 1); TestHome('3rd #32 line after unindendet', 2, 6, 1,1,1, 1); TestHome('4th #9 line after unindendet', 2, 8, 1,1,1, 1); TestHome('space indented line', 1, 9, 4,1,4, 4); TestHome('space indented line x-after-indent', 4, 9, 1,4,1, 4); // go to indent, after absolute home TestHome('space indented line x-in-indent', 3, 9, 1,4,1, 4); TestHome('space indented line x-in-line', 6, 9, 1,4,1, 4); TestHome('1st after space indented line', 1,10, 1,1,1, 1); TestHome('2nd after space indented line', 1,11, 1,1,1, 1); TestHome('3rd #32 after space indented line', 2,12, 1,4,1, 1); TestHome('4th #9 after space indented line', 2,14, 1,4,1, 1); TestHome('#9 after long space indented line', 2,34, 1,9,1, 1); SyneDit.Options2 := SyneDit.Options2 + [eoCaretSkipTab]; TestHome('4th #9 after space indented line [skipT]', 7,14, 1,1,1, 1); TestHome('#9 after long space indented line [skipT]', 1,34, 7,1,7, 1); TestHome('#9 after long space indented line [skipT]', 7,34, 1,7,1, 1); TestHome('#9 after long space indented line [skipT]', 13,34, 1,7,1, 1); SyneDit.Options2 := SyneDit.Options2 - [eoCaretSkipTab]; TestHome('tab indented line', 1,15, 7,1,7, 7); TestHome('tab indented line x-after-indent', 7,15, 1,7,1, 7); TestHome('tab indented line x-in-indent', 5,15, 1,7,1, 7); TestHome('tab indented line x-in-line', 9,15, 1,7,1, 7); TestHome('1st after tab indented line', 1,16, 1,1,1, 1); TestHome('2nd after tab indented line', 1,17, 1,1,1, 1); TestHome('3rd #32 after tab indented line', 2,18, 1,7,1, 1); TestHome('4th #9 after tab indented line', 2,20, 1,7,1, 1); {%endregion} {%region} DoInit1; SyneDit.Options := [eoScrollPastEol]; PopPushBaseName('no smart / past-eol'); TestHome('empty 1st line', 1, 1, 1,1,1, 1); TestHome('empty 2nd line', 1, 2, 1,1,1, 1); TestHome('unindented line', 1, 3, 1,1,1, 1); TestHome('unindented line x-in-line', 4, 3, 1,1,1, 1); TestHome('1st line after unindendet', 1, 4, 1,1,1, 1); TestHome('2nd line after unindendet', 1, 5, 1,1,1, 1); TestHome('3rd #32 line after unindendet', 2, 6, 1,1,1, 1); TestHome('4th #9 line after unindendet', 2, 8, 1,1,1, 1); TestHome('space indented line', 1, 9, 4,1,4, 4); TestHome('space indented line x-after-indent', 4, 9, 1,4,1, 4); // go to indent, after absolute home TestHome('space indented line x-in-indent', 3, 9, 1,4,1, 4); TestHome('space indented line x-in-line', 6, 9, 1,4,1, 4); TestHome('1st after space indented line', 1,10, 4,1,4, 1); TestHome('1st after space indented line (x)', 2,10, 1,4,1, 1); TestHome('2nd after space indented line', 1,11, 4,1,4, 1); TestHome('3rd #32 after space indented line', 2,12, 1,4,1, 1); TestHome('4th #9 after space indented line', 2,14, 1,4,1, 1); TestHome('#9 after long space indented line', 2,34, 1,9,1, 1); SyneDit.Options2 := SyneDit.Options2 + [eoCaretSkipTab]; TestHome('4th #9 after space indented line [skipT]', 7,14, 1,1,1, 1); TestHome('#9 after long space indented line [skipT]', 1,34, 7,1,7, 1); TestHome('#9 after long space indented line [skipT]', 7,34, 1,7,1, 1); TestHome('#9 after long space indented line [skipT]', 13,34, 1,7,1, 1); SyneDit.Options2 := SyneDit.Options2 - [eoCaretSkipTab]; TestHome('tab indented line', 1,15, 7,1,7, 7); TestHome('tab indented line x-after-indent', 7,15, 1,7,1, 7); TestHome('tab indented line x-in-indent', 5,15, 1,7,1, 7); TestHome('tab indented line x-in-line', 9,15, 1,7,1, 7); TestHome('1st after tab indented line', 1,16, 7,1,7, 1); TestHome('1st after tab indented line(x)', 2,16, 1,7,1, 1); TestHome('2nd after tab indented line', 1,17, 7,1,7, 1); TestHome('3rd #32 after tab indented line', 2,18, 1,7,1, 1); TestHome('4th #9 after tab indented line', 2,20, 1,7,1, 1); {%endregion} {%region} DoInit1; SyneDit.Options := [eoEnhanceHomeKey]; PopPushBaseName('smart home / no past-eol'); TestHome('empty 1st line', 1, 1, 1,1,1, 1); TestHome('empty 2nd line', 1, 2, 1,1,1, 1); TestHome('unindented line', 1, 3, 1,1,1, 1); TestHome('unindented line x-in-line', 4, 3, 1,1,1, 1); TestHome('1st line after unindendet', 1, 4, 1,1,1, 1); TestHome('2nd line after unindendet', 1, 5, 1,1,1, 1); TestHome('3rd #32 line after unindendet', 2, 6, 1,1,1, 1); TestHome('4th #9 line after unindendet', 2, 8, 1,1,1, 1); TestHome('space indented line', 1, 9, 4,1,4, 4); // go to absolut home (x-=1), after indent TestHome('space indented line x-after-indent', 4, 9, 1,4,1, 4); TestHome('space indented line x-in-indent', 3, 9, 1,4,1, 4); TestHome('space indented line x-in-line', 6, 9, 4,1,4, 4); TestHome('1st after space indented line', 1,10, 1,1,1, 1); TestHome('2nd after space indented line', 1,11, 1,1,1, 1); TestHome('3rd #32 after space indented line', 2,12, 1,4,1, 1); TestHome('4th #9 after space indented line', 2,14, 1,4,1, 1); TestHome('#9 after long space indented line', 2,34, 1,9,1, 1); SyneDit.Options2 := SyneDit.Options2 + [eoCaretSkipTab]; TestHome('4th #9 after space indented line [skipT]', 7,14, 1,1,1, 1); TestHome('#9 after long space indented line [skipT]', 1,34, 7,1,7, 1); TestHome('#9 after long space indented line [skipT]', 7,34, 1,7,1, 1); TestHome('#9 after long space indented line [skipT]', 13,34, 7,1,7, 1); SyneDit.Options2 := SyneDit.Options2 - [eoCaretSkipTab]; TestHome('tab indented line', 1,15, 7,1,7, 7); TestHome('tab indented line x-after-indent', 7,15, 1,7,1, 7); TestHome('tab indented line x-in-indent', 5,15, 1,7,1, 7); TestHome('tab indented line x-in-line', 9,15, 7,1,7, 7); TestHome('1st after tab indented line', 1,16, 1,1,1, 1); TestHome('2nd after tab indented line', 1,17, 1,1,1, 1); TestHome('3rd #32 after tab indented line', 2,18, 1,7,1, 1); TestHome('3rd #32 after tab indented line', 11,18, 7,1,7, 1); TestHome('4th #9 after tab indented line', 2,20, 1,7,1, 1); {%endregion} {%region} DoInit1; SyneDit.Options := [eoEnhanceHomeKey, eoScrollPastEol]; PopPushBaseName('smart home / past-eol'); TestHome('empty 1st line', 1, 1, 1,1,1, 1); TestHome('empty 2nd line', 1, 2, 1,1,1, 1); TestHome('unindented line', 1, 3, 1,1,1, 1); TestHome('unindented line x-in-line', 4, 3, 1,1,1, 1); TestHome('1st line after unindendet', 1, 4, 1,1,1, 1); TestHome('2nd line after unindendet', 1, 5, 1,1,1, 1); TestHome('3rd #32 line after unindendet', 2, 6, 1,1,1, 1); TestHome('4th #9 line after unindendet', 2, 8, 1,1,1, 1); TestHome('space indented line', 1, 9, 4,1,4, 4); // go to absolut home (x-=1), after indent TestHome('space indented line x-after-indent', 4, 9, 1,4,1, 4); TestHome('space indented line x-in-indent', 3, 9, 1,4,1, 4); TestHome('space indented line x-in-line', 6, 9, 4,1,4, 4); TestHome('1st after space indented line', 1,10, 4,1,4, 1); TestHome('2nd after space indented line', 1,11, 4,1,4, 1); TestHome('3rd #32 after space indented line', 2,12, 1,4,1, 1); TestHome('4th #9 after space indented line', 2,14, 1,4,1, 1); TestHome('#9 after long space indented line', 2,34, 1,9,1, 1); SyneDit.Options2 := SyneDit.Options2 + [eoCaretSkipTab]; TestHome('4th #9 after space indented line [skipT]', 7,14, 1,1,1, 1); TestHome('#9 after long space indented line [skipT]', 7,34, 1,7,1, 1); SyneDit.Options2 := SyneDit.Options2 - [eoCaretSkipTab]; TestHome('tab indented line', 1,15, 7,1,7, 7); TestHome('tab indented line x-after-indent', 7,15, 1,7,1, 7); TestHome('tab indented line x-in-indent', 5,15, 1,7,1, 7); TestHome('tab indented line x-in-line', 9,15, 7,1,7, 7); TestHome('1st after tab indented line', 1,16, 7,1,7, 1); TestHome('1st after tab indented line (x)', 2,16, 1,7,1, 1); TestHome('2nd after tab indented line', 1,17, 7,1,7, 1); TestHome('3rd #32 after tab indented line', 2,18, 1,7,1, 1); TestHome('3rd #32 after tab indented line', 11,18, 7,1,7, 1); TestHome('4th #9 after tab indented line', 2,20, 1,7,1, 1); {%endregion} PopPushBaseName('NO smart end/ NO past-eol'); DoInit1; TestEnd ('empty 1st line', 1,1, 1,1,1); TestEnd ('end', 1,35, 6,3,6); TestEnd ('end tab', 1,36, 7,3,7); PopPushBaseName('smart end/ NO past-eol'); DoInit1; SyneDit.Options2 := [eoEnhanceEndKey]; TestEnd ('end', 1,35, 3,6,3); TestEnd ('end tab', 1,36, 3,7,3); //SyneDit.Options := [eoScrollPastEol, eoEnhanceHomeKey]; //SyneDit.Options2 := [eoEnhanceEndKey]; end; procedure TTestSynNavigation.TestCaretLeftRight; procedure SetTextLeftRight; begin ReCreateEdit; SetLines([ 'ああああああああああああああああああああああ', // 1: Double width at odd pos (start at 1, then every 2) ' ああああああああああああああああああああああ', // 2: Double width at even pos '', // 3: empty #9#9#9#9#9#9#9#9, // 4: tabs 'abc'#9'def', // 5: text+tabs 'abc def üüü xyz', // 6: utf8 multibyte 'abc def ü'#9'üü xyz', // 7: utf8 multibyte + tabs 'abc あdefあ123', // 8: Double withs 'abc あdef'#9'あ'#9'123', // 9: Double withs + tabs 'Aa'#$CC#$81'B', // 10: a-accent, in 2 (combining) codepoints) '' ]); end; procedure TestLeftRight(AName: String; ALine: Integer; AStartPhysX: Integer; ACommand:TSynEditorCommand; ExpPhysX, ExpLogX: Integer; ExpRepeatPhysX: Integer = -1; ExpRepeatLogX: Integer = -1; DoReverse: Boolean = True; DoKeepXDWidth: Boolean = True; ASelectXEnd: Integer = -1; ExpCaretY: integer = -1); var Name2: String; TestLogFirst: Boolean; procedure DoCmd; begin if ASelectXEnd < 0 then SetCaretPhys(AStartPhysX, ALine) else SetCaretAndSelPhys(ASelectXEnd, ALine, AStartPhysX, ALine); SynEdit.ExecuteCommand(ACommand, '', nil); end; procedure TestCmd; begin DoCmd; if not TestLogFirst then TestIsCaretPhys(Name2+'[1]', ExpPhysX, ExpCaretY); TestIsCaret(Name2+'[1]', ExpLogX, ExpCaretY); TestIsCaretPhys(Name2+'[1]', ExpPhysX, ExpCaretY); end; procedure DoCmdRepeat; begin if ExpRepeatPhysX < 0 then exit; SynEdit.ExecuteCommand(ACommand, '', nil); end; procedure TestCmdRepeat; begin if ExpRepeatPhysX < 0 then exit; DoCmdRepeat; if not TestLogFirst then TestIsCaretPhys(Name2+'[2]', ExpRepeatPhysX, ExpCaretY); if ExpRepeatLogX > 0 then TestIsCaret(Name2+'[2]', ExpRepeatLogX, ExpCaretY); TestIsCaretPhys(Name2+'[2]', ExpRepeatPhysX, ExpCaretY); end; procedure DoCmdReverse(AfterRepeat: Boolean = false); begin if not DoReverse then exit; if AfterRepeat and (ExpRepeatPhysX < 0) then exit; // was never repeated if ACommand = ecLeft then SynEdit.ExecuteCommand(ecRight, '', nil) else SynEdit.ExecuteCommand(ecLeft, '', nil); end; procedure TestCmdReverse(AfterRepeat: Boolean = false); begin if not DoReverse then exit; if AfterRepeat and (ExpRepeatPhysX < 0) then exit; // was never repeated DoCmdReverse(AfterRepeat); if AfterRepeat then begin if not TestLogFirst then TestIsCaretPhys(Name2+'[R,'+dbgs(AfterRepeat)+']', ExpPhysX, ExpCaretY); TestIsCaret(Name2, ExpLogX, ExpCaretY); TestIsCaretPhys(Name2+'[R,'+dbgs(AfterRepeat)+']', ExpPhysX, ExpCaretY); end else begin TestIsCaretPhys(Name2+'[R,'+dbgs(AfterRepeat)+']', AStartPhysX, ALine); end; end; procedure DoKeepXOnEmpty; begin if (ExpPhysX > 1) and not(eoScrollPastEol in SynEdit.Options) then begin SynEdit.CaretObj.LinePos := 3; // empty line SynEdit.CaretObj.LinePos := ExpCaretY; end; end; procedure TestKeepXOnEmpty(TestBetween: Boolean = False); begin if (ExpPhysX > 1) and not(eoScrollPastEol in SynEdit.Options) then begin SynEdit.CaretObj.LinePos := 3; // empty line if TestBetween then AssertEquals(Name2+'empty line at pos 1', 1, SynEdit.CaretObj.CharPos); SynEdit.CaretObj.LinePos := ExpCaretY; if eoKeepCaretX in SynEdit.Options then AssertEquals(Name2+'after empty line at pos 1', ExpPhysX, SynEdit.CaretObj.CharPos) else AssertEquals(Name2+'after empty line at pos 1', 1, SynEdit.CaretObj.CharPos); end; end; procedure DoKeepXOnDWidth; begin if (ExpPhysX > 1) and DoKeepXDWidth then begin if (ExpPhysX and 1) = 0 then SynEdit.CaretObj.LinePos := 1 // double width odd else SynEdit.CaretObj.LinePos := 2; // double width even SynEdit.CaretObj.LinePos := ExpCaretY; end; end; procedure TestKeepXOnDWidth(TestBetween: Boolean = False); begin if (ExpPhysX > 1) and DoKeepXDWidth then begin if (ExpPhysX and 1) = 0 then SynEdit.CaretObj.LinePos := 1 // double width odd else SynEdit.CaretObj.LinePos := 2; // double width even if TestBetween then AssertFalse(Name2+'after dwidth line at pos 1', ExpPhysX = SynEdit.CaretObj.CharPos); SynEdit.CaretObj.LinePos := ExpCaretY; AssertTrue(Name2+'after dwidth line at pos 1', (ExpPhysX = SynEdit.CaretObj.CharPos) = (eoKeepCaretX in SynEdit.Options)); end; end; var s: string; begin if ExpCaretY < 0 then ExpCaretY := ALine; EditorCommandToIdent(ACommand, s{%H-}); AName := Format('%s (%s Y=%d, X=%d) ', [AName, s, ALine, AStartPhysX]); {%region NO eoKeepCaretX} SynEdit.Options := SynEdit.Options - [eoKeepCaretX]; Name2 := AName + 'NO eoKeepCaretX: '; TestLogFirst := False; TestCmd; TestCmdReverse; TestCmd; TestCmdRepeat; TestCmdReverse(True); TestCmdReverse; DoCmd; TestCmdReverse; DoCmd; DoCmdRepeat; TestCmdReverse(True); TestCmdReverse; TestLogFirst := True; TestCmd; TestCmdReverse; TestCmd; TestCmdRepeat; TestCmdReverse(True); TestCmdReverse; TestCmd; TestKeepXOnEmpty(False); TestCmd; TestKeepXOnEmpty(True); TestCmd; TestKeepXOnDWidth(False); TestCmd; TestKeepXOnDWidth(True); {%endregion NO eoKeepCaretX} {%region WITH eoKeepCaretX} SynEdit.Options := SynEdit.Options + [eoKeepCaretX]; Name2 := Name2 + 'WITH eoKeepCaretX: '; TestLogFirst := False; TestCmd; TestCmdReverse; TestCmd; TestCmdRepeat; TestCmdReverse(True); TestCmdReverse; DoCmd; TestCmdReverse; DoCmd; DoCmdRepeat; TestCmdReverse(True); TestCmdReverse; TestLogFirst := True; TestCmd; TestCmdReverse; TestCmd; TestCmdRepeat; TestCmdReverse(True); TestCmdReverse; TestCmd; TestKeepXOnEmpty(False); if ASelectXEnd < 0 then TestCmdReverse; TestCmd; TestKeepXOnEmpty(True); if ASelectXEnd < 0 then TestCmdReverse; TestCmd; TestKeepXOnEmpty(False); TestCmdRepeat; TestCmd; TestKeepXOnDWidth(False); if ASelectXEnd < 0 then TestCmdReverse; TestCmd; TestKeepXOnDWidth(True); if ASelectXEnd < 0 then TestCmdReverse; TestCmd; TestKeepXOnDWidth(False); TestCmdRepeat; {%endregion WITH eoKeepCaretX} end; begin // TODO: // EOL/BOL at first/last (visible) line (with folding) // Multybyte char at EOL SetTextLeftRight; {%region ecRight} PushBaseName('No opts'); SynEdit.TabWidth := 6; SynEdit.MaxLeftChar := 200; SynEdit.Options := []; SynEdit.Options2 := []; TestLeftRight('Simple right', 5, 2, ecRight, 3, 3, 4, 4); TestLeftRight('After tab right', 5, 7, ecRight, 8, 6, 9, 7); TestLeftRight('Before tab right', 5, 4, ecRight, 5, 4, 6, 4); TestLeftRight('Mid tab right', 5, 5, ecRight, 6, 4, 7, 5); TestLeftRight('tab to tab right', 4, 6, ecRight, 7, 2, 8, 2); TestLeftRight('Umlaut right', 6, 9, ecRight, 10,11, 11,13); TestLeftRight('Double W right', 8, 5, ecRight, 7, 8, 8, 9); TestLeftRight('combining right', 10, 2, ecRight, 3, 5, 4, 6); TestLeftRight('at EOL right', 5, 10, ecRight, 1, 1, 2, 2, True, True, -1 , 6); PopPushBaseName('eoScrollPastEol'); SynEdit.Options := [eoScrollPastEol]; TestLeftRight('at EOL right', 5, 10, ecRight, 11, 9, 12,10); TestLeftRight('at Max-left right', 5,199, ecRight, 200,198, 200,198, False, False); PopPushBaseName('eoCaretSkipTab'); SynEdit.Options := []; SynEdit.Options2 := [eoCaretSkipTab]; TestLeftRight('Simple right', 5, 2, ecRight, 3, 3, 4, 4); TestLeftRight('After tab right', 5, 7, ecRight, 8, 6, 9, 7); TestLeftRight('Before tab right', 5, 4, ecRight, 7, 5, 8, 6); TestLeftRight('tab to tab right', 4, 7, ecRight, 13, 3, 19, 4); PopPushBaseName('eoCaretSkipsSelection'); SynEdit.Options2 := [eoCaretSkipsSelection]; TestLeftRight('Selected right', 5, 2, ecRight, 8, 6, -1, -1, True, False, 8); {%region ecRight} {%region ecLeft} SynEdit.Options := []; SynEdit.Options2 := []; TestLeftRight('Simple left', 5, 3, ecLeft, 2, 2, 1, 1); TestLeftRight('After tab left', 5, 7, ecLeft, 6, 4, 5, 4); TestLeftRight('Before tab left', 5, 4, ecLeft, 3, 3, 2, 2); TestLeftRight('Mid tab left', 5, 5, ecLeft, 4, 4, 3, 3); TestLeftRight('tab to tab left', 4, 8, ecLeft, 7, 2, 6, 1); TestLeftRight('Umlaut left', 6, 10, ecLeft, 9, 9, 8, 8); TestLeftRight('Double W left', 8, 7, ecLeft, 5, 5, 4, 4); TestLeftRight('combining left', 10, 3, ecLeft, 2, 2, 1, 1); TestLeftRight('at BOL left', 6, 1, ecLeft, 10, 8, 9, 7, True, True, -1 , 5); PopPushBaseName('eoScrollPastEol'); SynEdit.Options := [eoScrollPastEol]; TestLeftRight('at BOL left', 6, 1, ecLeft, 1, 1, 1, 1, False, False); PopPushBaseName('eoCaretSkipTab'); SynEdit.Options := []; SynEdit.Options2 := [eoCaretSkipTab]; TestLeftRight('Simple left', 5, 3, ecLeft, 2, 2, 1, 1); TestLeftRight('After tab left', 5, 7, ecLeft, 4, 4, 3, 3); TestLeftRight('tab to tab left', 4, 13, ecLeft, 7, 2, 1, 1); PopPushBaseName('eoCaretSkipsSelection'); SynEdit.Options2 := [eoCaretSkipsSelection]; TestLeftRight('Selected left', 5, 8, ecLeft, 3, 3, -1, -1, True, False, 3); {%endregion ecLeft} PopBaseName; end; procedure TTestSynNavigation.TestCaretMoveLeftRightWord; procedure DoInit; begin ReCreateEdit; SynEdit.TabWidth := 7; // 1 6 11 14 SetLines(['Some text to test', //1 // 1 5 9 'Foo bar abc', // 2 // 8 14 19 24 #9'Other line with tab', // 3 // 1 8 11 15 'tab'#9'in the middle', // 4 // 1 9 12 16 23 28 'tab'#9' in the middle with space', // 5 // 1 9 13 16 'umlaute äää in text', // 6 'normal line', // 4 14 21 28 32 ' untrimmed spaces around line ', // 8 'normal line', // 8 15 22 26 #9'tab'#9'only'#9'line'#9, // 10 // 1 8 'normal line', '', // 12 (empty) 'normal line', ' ', // space only empty line // 14 'normal line', '', 'A B', // 17 'normal line', // 18 '' ]); end; procedure TestWordLeft(Name:String; X, Y, ExpX1, ExpY1: Integer; ExpX2: Integer = -1; ExpY2: Integer = -1); begin SetCaretPhys(X,Y); SynEdit.CommandProcessor(ecWordLeft, '', nil); TestIsCaretPhys(Name + '(1st WordLeft)', ExpX1, ExpY1); if ExpY2 > 0 then begin SynEdit.CommandProcessor(ecWordLeft, '', nil); TestIsCaretPhys(Name + '(2nd WordLeft)', ExpX2, ExpY2); end; end; procedure TestWordRight(Name:String; X, Y, ExpX1, ExpY1: Integer; ExpX2: Integer = -1; ExpY2: Integer = -1); begin SetCaretPhys(X,Y); SynEdit.CommandProcessor(ecWordRight, '', nil); TestIsCaretPhys(Name + '(1st WordRight)', ExpX1, ExpY1); if ExpY2 > 0 then begin SynEdit.CommandProcessor(ecWordRight, '', nil); TestIsCaretPhys(Name + '(2nd WordRight)', ExpX2, ExpY2); end; end; begin DoInit; {%region word left} TestWordLeft('simple "te|st"', 16, 1, 14, 1, 11, 1); TestWordLeft('simple EOW "test|"', 18, 1, 14, 1, 11, 1); TestWordLeft('simple BOW "|test"', 14, 1, 11, 1, 6, 1); TestWordLeft('simple > BOT "So|me"', 3, 1, 1, 1, 1, 1); TestWordLeft('simple > prev-line "F|oo"', 2, 2, 1, 2, 18, 1); TestWordLeft('simple > prev-line "|Foo"', 1, 2, 18, 1, 14, 1); TestWordLeft('tab "wi|th"', 21, 3, 19, 3, 14, 3); TestWordLeft('tab EOW "with|"', 23, 3, 19, 3, 14, 3); TestWordLeft('tab BOW "|with"', 19, 3, 14, 3, 8, 3); TestWordLeft('tab > prev-line "O|ther"', 9, 3, 8, 3, 12, 2); TestWordLeft('M-tab "i|n"', 9, 4, 8, 4, 1, 4); TestWordLeft('M-tab > prev-line-tab "ta|b"', 3, 4, 1, 4, 27, 3); TestWordLeft('M-S-tab "i|n"', 10, 5, 9, 5, 1, 5); TestWordLeft('M-S-EOW tab "in|"', 11, 5, 9, 5, 1, 5); TestWordLeft('M-S-BOW tab "|in"', 9, 5, 1, 5, 21, 4); TestWordLeft('M-S-tab "#9| in"', 8, 5, 1, 5); TestWordLeft('Umlaut "ää|ä"', 11, 6, 9, 6, 1, 6); TestWordLeft('Umlaut EOW "äää|"', 12, 6, 9, 6, 1, 6); TestWordLeft('Umlaut BOW "|äää"', 9, 6, 1, 6, 33, 5); TestWordLeft('Umlaut "i|n"', 14, 6, 13, 6, 9, 6); TestWordLeft('After Umlaut > prev line', 1, 7, 20, 6, 16, 6); TestWordLeft('untrimmed "un|trimmed"', 6, 8, 4, 8, 12, 7); TestWordLeft('After untrimmed > prev', 1, 9, 35, 8, 28, 8); TestWordLeft('untrimmed tab "t|ab"', 9,10, 8,10, 12, 9); TestWordLeft('After untrimmed tab > prev', 1,11, 29,10, 22,10); TestWordLeft('After empty > prev', 1,13, 1,12); TestWordLeft('After space empty > prev', 1,15, 6,14); TestWordLeft('single char at eol "A B|"', 4,17, 3,17, 1,17); {%endregion} {%region word right} TestWordRight('simple "te|xt"', 8, 1, 11, 1, 14, 1); TestWordRight('simple EOW "text|"', 10, 1, 11, 1, 14, 1); TestWordRight('simple BOW "|text"', 6, 1, 11, 1, 14, 1); TestWordRight('simple EOT "li|ne"', 10,18, 12,18, 12,18); TestWordRight('simple > EOL, next line "te|st"', 16, 1, 18, 1, 1, 2); TestWordRight('tab "li|ne"', 16, 3, 19, 3, 24, 3); TestWordRight('tab EOW "line|"', 18, 3, 19, 3, 24, 3); TestWordRight('tab BOW "|line"', 14, 3, 19, 3, 24, 3); TestWordRight('tab > EOL, next-line', 25, 3, 27, 3, 1, 4); TestWordRight('M-tab "t|ab"', 2, 4, 8, 4, 11, 4); TestWordRight('M-tab "tab|"', 4, 4, 8, 4, 11, 4); TestWordRight('M-tab > EOL, next-line-tab', 17, 4, 21, 4, 1, 5); TestWordRight('M-S-tab BOW "t|ab"', 2, 5, 9, 5, 12, 5); TestWordRight('M-S-tab EOW "tab|"', 4, 5, 9, 5, 12, 5); TestWordRight('M-S-tab BOW "|tab"', 1, 5, 9, 5, 12, 5); TestWordRight('M-S-tab "tab#9| "', 5, 5, 9, 5, 12, 5); TestWordRight('Umlaut "ää|ä"', 11, 6, 13, 6, 16, 6); TestWordRight('Umlaut EOW "äää|"', 12, 6, 13, 6, 16, 6); TestWordRight('Umlaut BOW "|äää"', 9, 6, 13, 6, 16, 6); TestWordRight('Umlaut "um|laute"', 2, 6, 9, 6, 13, 6); TestWordRight('After Umlaut > EOL, next line', 18, 6, 20, 6, 1, 7); TestWordRight('Before untrimmed > next', 12, 7, 4, 8, 14, 8); TestWordRight('untrimmed > EOL, next', 30, 8, 35, 8, 1, 9); TestWordRight('Before untrimmed tab > next', 12, 9, 8,10, 15,10); TestWordRight('untrimmed tab > EOL, next', 24,10, 29,10, 1,11); TestWordRight('Before empty > next', 12,11, 1,12); TestWordRight('Before space empty > next', 12,13, 1,14, 6, 14); {%endregion} end; initialization RegisterTest(TTestSynNavigation); end.