diff --git a/components/synedit/syneditpointclasses.pas b/components/synedit/syneditpointclasses.pas index 0522841e4e..5a5c616ea8 100644 --- a/components/synedit/syneditpointclasses.pas +++ b/components/synedit/syneditpointclasses.pas @@ -176,6 +176,7 @@ type FLinePos: Integer; // 1 based FCharPos: Integer; // 1 based FLastCharPos: Integer; // used by KeepCaretX + FBytePos, FBytePosOffset: Integer; // 1 based FOldLinePos: Integer; // 1 based FOldCharPos: Integer; // 1 based FAdjustToNextChar: Boolean; @@ -185,6 +186,7 @@ type FTouched: Boolean; procedure AdjustToChar; + procedure UpdateBytePos; function GetOldLineBytePos: TPoint; function GetOldLineCharPos: TPoint; procedure InternalSetLineCharPos(NewLine, NewCharPos: Integer; @@ -436,6 +438,8 @@ end; procedure TSynEditCaret.IncAutoMoveOnEdit; begin + if FAutoMoveOnEdit =0 then + UpdateBytePos;; inc(FAutoMoveOnEdit); end; @@ -511,6 +515,11 @@ begin end; end; +procedure TSynEditCaret.UpdateBytePos; +begin + FBytePos := FLines.LogPhysConvertor.PhysicalToLogical(FLinePos-1, FCharPos, FBytePosOffset); +end; + function TSynEditCaret.GetOldLineBytePos: TPoint; begin Result := FLines.PhysicalToLogicalPos(OldLineCharPos); @@ -589,6 +598,8 @@ begin AdjustToChar; if (not KeepLastCharPos) or (not FKeepCaretX) then FLastCharPos := FCharPos; + if FAutoMoveOnEdit <> 0 then + UpdateBytePos; end; finally Unlock; @@ -703,10 +714,17 @@ procedure TSynEditCaret.DoLinesEdited(Sender: TSynEditStrings; aLinePos, aBytePo end; end; +var + p: TPoint; begin if FAutoMoveOnEdit > 0 then begin IncForcePastEOL; - LineBytePos := AdjustPoint(LineBytePos); + p := AdjustPoint(Point(FBytePos, FLinePos)); + p.x := FLines.LogPhysConvertor.LogicalToPhysical(p.y-1, p.x, FBytePosOffset); + FBytePos := -1; + LineCharPos := p; + if FBytePos < 0 then + UpdateBytePos; DecForcePastEOL; end; end; diff --git a/components/synedit/synedittextbase.pas b/components/synedit/synedittextbase.pas index c281cb3a8b..92a9f3993c 100644 --- a/components/synedit/synedittextbase.pas +++ b/components/synedit/synedittextbase.pas @@ -242,6 +242,7 @@ type function PhysicalToLogicalPos(const p: TPoint): TPoint; function PhysicalToLogicalCol(const Line: string; Index, PhysicalPos: integer): integer; virtual; + property LogPhysConvertor :TSynLogicalPhysicalConvertor read FLogPhysConvertor; public procedure EditInsert(LogX, LogY: Integer; AText: String); virtual; abstract; function EditDelete(LogX, LogY, ByteLen: Integer): String; virtual; abstract; @@ -469,11 +470,13 @@ procedure TSynLogicalPhysicalConvertor.PrepareWidthsForLine(AIndex: Integer; var LineLen: Integer; Line: PChar; +//const dbg_cnt: integer = 0; 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)),')']); + //dbg_cnt := dbg_cnt + 1; exit; end; @@ -498,6 +501,7 @@ begin //else begin // debugln(['**************** COMPUTING widths: ', AIndex,' (',dbgs(Pointer(self)),') alloc=',FCurrentWidthsAlloc]); end; + //debugln(['**************** NEW for index:: ', AIndex,' (',dbgs(Pointer(self)),') after index: ', FCurrentLine, ' used ', dbg_cnt,' times // old-alloc=', FCurrentWidthsAlloc, ' new-len=',LineLen, ' viewchg:',dbgs(not(FViewChangeStamp=FLines.ViewChangeStamp)),' txtchg:',dbgs(not(FTextChangeStamp=FLines.TextChangeStamp))]); dbg_cnt := 0; FCurrentWidthsLen := LineLen; if LineLen > 0 then diff --git a/components/synedit/synedittexttrimmer.pas b/components/synedit/synedittexttrimmer.pas index 12b2a7e7fb..32242a5ba3 100644 --- a/components/synedit/synedittexttrimmer.pas +++ b/components/synedit/synedittexttrimmer.pas @@ -587,18 +587,23 @@ 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 + if fSpaces <> fLockList[i] then + IncViewChangeStamp; fSpaces:= fLockList[i]; if (fLineIndex >= 0) and (fLineIndex < fSynStrings.Count) then fLineText := fSynStrings[fLineIndex]; fLockList.Delete(i); DoCaretChanged(fCaret); - end; + end + else if fSpaces <> '' then + IncViewChangeStamp; FIsTrimming := True; BeginUpdate; + if fLockList.Count > 0 then + IncViewChangeStamp; try for i := 0 to fLockList.Count-1 do begin index := Integer(PtrUInt(Pointer(fLockList.Objects[i]))); diff --git a/components/synedit/test/testbase.pas b/components/synedit/test/testbase.pas index db9b5e6f91..cc44ee6bd0 100644 --- a/components/synedit/test/testbase.pas +++ b/components/synedit/test/testbase.pas @@ -7,7 +7,7 @@ interface uses Classes, SysUtils, Forms, fpcunit, SynEdit, LCLType, LCLProc, math, - SynEditTypes, Clipbrd; + SynEditTypes, SynEditPointClasses, Clipbrd; type @@ -24,6 +24,7 @@ type property ViewedTextBuffer; property TextBuffer; property TextView; // foldedview + property CaretObj: TSynEditCaret read GetCaretObj; end; { TTestBase } @@ -318,6 +319,7 @@ end; procedure TTestBase.TestFail(Name, Func, Expect, Got: String; Result: Boolean = False); begin if Result then exit; + DebugLn(DbgStr(SynEdit.Text)); if BaseTestName <> '' then Fail(Format('%s: %s (%s)%sExpected: %s%s Got: %s', [BaseTestName, Name, Func, LineEnding, Expect, LineEnding, Got])) else diff --git a/components/synedit/test/testbasicsynedit.pas b/components/synedit/test/testbasicsynedit.pas index 5da92e3333..b4b8486ae9 100644 --- a/components/synedit/test/testbasicsynedit.pas +++ b/components/synedit/test/testbasicsynedit.pas @@ -28,6 +28,7 @@ type procedure TestEditEmpty; procedure TestEditTabs; procedure TestEditPhysicalLogical; + procedure TestCaretAutoMove; end; implementation @@ -249,6 +250,65 @@ begin end; +procedure TTestBasicSynEdit.TestCaretAutoMove; + + procedure DoTest(name: string; y, x, insertY, insertX, InsertY2, InsertX2: integer; + txt: string; expY, expX: Integer); + begin + name := name + ' y='+inttostr(y)+' x='+inttostr(x); + if y > 0 then begin + ReCreateEdit; + SynEdit.TabWidth := 6; + SetLines(['x', 'abc', ' ääX', #9'mn', 'abc'#9'de', #9'Xää.']); + SynEdit.CaretXY := Point(x, y); + end; + + SynEdit.TextBetweenPointsEx[Point(insertX, insertY), point(insertX2, InsertY2), scamAdjust] + := txt; +debugln(dbgstr(SynEdit.Text)); + TestIsCaretPhys(name, expX, expY); + end; + +const + cr = LineEnding; +begin + + + DoTest('simple insert', 2,2, 2,1, 2,1, 'X', 2,3); + DoTest('simple insert CR', 2,2, 2,1, 2,1, 'X'+cr, 3,2); + DoTest('simple insert CR+', 2,2, 2,1, 2,1, cr+'X', 3,3); + DoTest('simple delete', 2,2, 2,1, 2,2, '', 2,1); + DoTest('simple delete CR', 2,2, 1,2, 2,1, '', 1,3); + DoTest('+simple delete CR', 2,2, 1,1, 2,1, '', 1,2); + + DoTest('simple insert (eol)', 2,4, 2,1, 2,1, 'X', 2,5); + DoTest('simple insert (past eol)', 2,7, 2,1, 2,1, 'X', 2,8); + + DoTest('insert with tab', 4,8, 4,1, 4,1, 'X', 4,8); + DoTest('insert with tab (cont)', -4,8, 4,2, 4,2, 'Y', 4,8); + DoTest('insert with tab (cont)', -4,8, 4,3, 4,3, 'abc', 4,8); + DoTest('insert with tab (cont)', -4,8, 4,6, 4,6, 'Z', 4,14); + DoTest('insert with tab (cont)', -4,8, 4,7, 4,7, '.', 4,14); + DoTest('delete with tab (cont)', -4,8, 4,1, 4,2, '', 4,14); + DoTest('delete with tab (cont)', -4,8, 4,1, 4,2, '', 4,8); + DoTest('delete with tab (cont)', -4,8, 4,1, 4,2, '', 4,8); + DoTest('delete with tab (cont)', -4,8, 4,1, 4,2, '', 4,8); + + + SynEdit.CaretObj.IncAutoMoveOnEdit; + DoTest('insert with tab (am-block)', 4,8, 4,1, 4,1, 'X', 4,8); + DoTest('insert with tab (am-block) (cont)', -4,8, 4,2, 4,2, 'Y', 4,8); + DoTest('insert with tab (am-block) (cont)', -4,8, 4,3, 4,3, 'abc', 4,8); + DoTest('insert with tab (am-block) (cont)', -4,8, 4,6, 4,6, 'Z', 4,14); + DoTest('insert with tab (am-block) (cont)', -4,8, 4,7, 4,7, '.', 4,14); + DoTest('delete with tab (cont)', -4,8, 4,1, 4,2, '', 4,14); + DoTest('delete with tab (cont)', -4,8, 4,1, 4,2, '', 4,8); + DoTest('delete with tab (cont)', -4,8, 4,1, 4,2, '', 4,8); + DoTest('delete with tab (cont)', -4,8, 4,1, 4,2, '', 4,8); + SynEdit.CaretObj.DecAutoMoveOnEdit; + +end; + initialization