diff --git a/components/synedit/syncompletion.pas b/components/synedit/syncompletion.pas index f83a779235..424b0469ef 100644 --- a/components/synedit/syncompletion.pas +++ b/components/synedit/syncompletion.pas @@ -1592,6 +1592,7 @@ begin if F.CurrentEditor is TCustomSynEdit then with TCustomSynEdit(F.CurrentEditor) do begin BeginUndoBlock; + BeginUpdate; LogCaret:=PhysicalToLogicalPos(CaretXY); NewBlockBegin:=LogCaret; CurLine:=Lines[NewBlockBegin.Y - 1]; @@ -1626,7 +1627,8 @@ begin TextBetweenPointsEx[NewBlockBegin, NewBlockEnd, scamEnd] := ItemList[Position]; TCustomSynEdit(F.CurrentEditor).SetFocus; end; - end; + end; + EndUpdate; EndUndoBlock; end; end; diff --git a/components/synedit/synedit.pp b/components/synedit/synedit.pp index 6d0e347f96..436833e0de 100644 --- a/components/synedit/synedit.pp +++ b/components/synedit/synedit.pp @@ -597,6 +597,8 @@ type procedure DestroyMarkList; procedure RemoveHandlers(ALines: TSynEditStrings = nil); procedure ExtraLineCharsChanged(Sender: TObject); + procedure InternalBeginUndoBlock(aList: TSynEditUndoList = nil); // includes paintlock + procedure InternalEndUndoBlock(aList: TSynEditUndoList = nil); protected procedure CreateHandle; override; procedure CreateParams(var Params: TCreateParams); override; @@ -738,7 +740,7 @@ type procedure AddKey(Command: TSynEditorCommand; Key1: word; SS1: TShiftState; Key2: word; SS2: TShiftState); procedure AfterLoadFromFile; - procedure BeginUndoBlock(aList: TSynEditUndoList = nil); + procedure BeginUndoBlock; procedure BeginUpdate; function CaretXPix: Integer; function CaretYPix: Integer; @@ -755,7 +757,7 @@ type destructor Destroy; override; procedure DoCopyToClipboard(SText: string; FoldInfo: String = ''); procedure DragDrop(Source: TObject; X, Y: Integer); override; - procedure EndUndoBlock(aList: TSynEditUndoList = nil); + procedure EndUndoBlock; procedure EndUpdate; procedure EnsureCursorPosVisible; {$IFDEF SYN_COMPILER_4_UP} @@ -4009,7 +4011,7 @@ var PasteAction: TSynCopyPasteAction; begin Result := False; - BeginUndoBlock; + InternalBeginUndoBlock; try PTxt := ClipHelper.TextP; PMode := ClipHelper.SelectionMode; @@ -4050,7 +4052,7 @@ begin end; end; finally - EndUndoBlock; + InternalEndUndoBlock; end; end; @@ -4296,11 +4298,11 @@ end; procedure TCustomSynEdit.SetSelTextExternal(const Value: string); begin // undo entry added - BeginUndoBlock; + InternalBeginUndoBlock; try FBlockSelection.SelText := Value; finally - EndUndoBlock; + InternalEndUndoBlock; end; end; @@ -5016,7 +5018,7 @@ begin FTheLinesView.IsRedoing := True; Item := Group.Pop; if Item <> nil then begin - BeginUndoBlock; + InternalBeginUndoBlock; fUndoList.CurrentGroup.Reason := Group.Reason; fUndoList.IsInsideRedo := True; try @@ -5025,7 +5027,7 @@ begin Item := Group.Pop; until (Item = nil); finally - EndUndoBlock; + InternalEndUndoBlock; end; end; FTheLinesView.IsRedoing := False; @@ -5125,7 +5127,7 @@ begin FTheLinesView.IsUndoing := True; Item := Group.Pop; if Item <> nil then begin - BeginUndoBlock(fRedoList); + InternalBeginUndoBlock(fRedoList); fRedoList.CurrentGroup.Reason := Group.Reason; fUndoList.Lock; try @@ -5137,7 +5139,7 @@ begin // Todo: Decide what do to, If there are any trimable spaces. FTrimmedLinesView.ForceTrim; fUndoList.UnLock; - EndUndoBlock(fRedoList); + InternalEndUndoBlock(fRedoList); end; end; FTheLinesView.IsUndoing := False; @@ -5490,21 +5492,21 @@ end; procedure TCustomSynEdit.SetTextBetweenPoints(aStartPoint, aEndPoint: TPoint; const AValue: String); begin - BeginUndoBlock; + InternalBeginUndoBlock; try FInternalBlockSelection.SelectionMode := smNormal; FInternalBlockSelection.StartLineBytePos := aStartPoint; FInternalBlockSelection.EndLineBytePos := aEndPoint; FInternalBlockSelection.SelText := AValue; finally - EndUndoBlock; + InternalEndUndoBlock; end; end; procedure TCustomSynEdit.SetTextBetweenPointsEx(aStartPoint, aEndPoint: TPoint; aCaretMode: TSynCaretAdjustMode; const AValue: String); begin - BeginUndoBlock; + InternalBeginUndoBlock; try if aCaretMode = scamAdjust then FCaret.IncAutoMoveOnEdit; @@ -5519,7 +5521,7 @@ begin finally if aCaretMode = scamAdjust then FCaret.DecAutoMoveOnEdit; - EndUndoBlock; + InternalEndUndoBlock; end; end; @@ -5666,7 +5668,7 @@ begin or ((NewCaret.Y = BB.Y) and (NewCaret.X < BB.X)); end; if DoDrop then begin - BeginUndoBlock; //mh 2000-11-20 + InternalBeginUndoBlock; //mh 2000-11-20 try DragDropText := TCustomSynEdit(Source).SelText; BlockSel := TCustomSynEdit(Source).FBlockSelection; @@ -5714,7 +5716,7 @@ begin BlockEnd := {$IFDEF SYN_LAZARUS}PhysicalToLogicalPos(CaretXY) {$ELSE}CaretXY{$ENDIF}; finally - EndUndoBlock; + InternalEndUndoBlock; end; end; finally @@ -5996,7 +5998,7 @@ begin DoOnProcessCommand(Command, AChar, Data); if Command <> ecNone then begin try - BeginUndoBlock; + InternalBeginUndoBlock; FBeautifyStartLineIdx := -1; FBeautifyEndLineIdx := -1; if assigned(FBeautifier) then begin @@ -6024,7 +6026,7 @@ begin FBeautifyStartLineIdx+1, FBeautifyEndLineIdx+1); end; finally - EndUndoBlock; + InternalEndUndoBlock; end; end; end; @@ -6629,10 +6631,10 @@ end; procedure TCustomSynEdit.ClearAll; begin {$IFDEF SYN_LAZARUS} - BeginUndoBlock; + InternalBeginUndoBlock; SelectAll; SelText:=''; - EndUndoBlock; + InternalEndUndoBlock; {$ELSE} Lines.Clear; {$ENDIF} @@ -6654,8 +6656,7 @@ begin fBlockSelection.ActiveSelectionMode := Value; end; -{begin} //sbs 2000-11-19 -procedure TCustomSynEdit.BeginUndoBlock(aList: TSynEditUndoList = nil); +procedure TCustomSynEdit.InternalBeginUndoBlock(aList: TSynEditUndoList); begin if aList = nil then aList := fUndoList; aList.OnNeedCaretUndo := {$IFDEF FPC}@{$ENDIF}GetCaretUndo; @@ -6663,15 +6664,8 @@ begin IncPaintLock; FFoldedLinesView.Lock; end; -{end} //sbs 2000-11-19 -procedure TCustomSynEdit.BeginUpdate; -begin - IncPaintLock; -end; - -{begin} //sbs 2000-11-19 -procedure TCustomSynEdit.EndUndoBlock(aList: TSynEditUndoList = nil); +procedure TCustomSynEdit.InternalEndUndoBlock(aList: TSynEditUndoList); begin if aList = nil then aList := fUndoList; // Write all trimming info to the end of the undo block, @@ -6682,7 +6676,28 @@ begin DecPaintLock; aList.EndBlock; // Todo: Doing this after DecPaintLock, can cause duplicate calls to StatusChanged(scModified) end; -{end} //sbs 2000-11-19 + +procedure TCustomSynEdit.BeginUndoBlock; +begin + fUndoList.OnNeedCaretUndo := {$IFDEF FPC}@{$ENDIF}GetCaretUndo; + fUndoList.BeginBlock; + ////FFoldedLinesView.Lock; + //FTrimmedLinesView.Lock; +end; + +procedure TCustomSynEdit.BeginUpdate; +begin + IncPaintLock; +end; + +procedure TCustomSynEdit.EndUndoBlock; +begin + // Write all trimming info to the end of the undo block, + // so it will be undone first, and other UndoItems do see the expected spaces + //FTrimmedLinesView.UnLock; + ////FFoldedLinesView.UnLock; + fUndoList.EndBlock; +end; procedure TCustomSynEdit.EndUpdate; begin @@ -7565,7 +7580,7 @@ begin exit; end; - BeginUndoBlock; + InternalBeginUndoBlock; try i := 0; OldCaretX := CaretX; @@ -7609,7 +7624,7 @@ begin CaretX := OldCaretX + i; //debugln('TCustomSynEdit.DoTabKey StartOfBlock=',dbgs(StartOfBlock),' fBlockEnd=',dbgs(fBlockEnd),' Spaces="',Spaces,'"'); finally - EndUndoBlock; + InternalEndUndoBlock; end; EnsureCursorPosVisible; end; diff --git a/components/synedit/test/testtrimspace.pas b/components/synedit/test/testtrimspace.pas index f63805005f..b1ae3a82cb 100644 --- a/components/synedit/test/testtrimspace.pas +++ b/components/synedit/test/testtrimspace.pas @@ -17,7 +17,7 @@ or between two empty lines uses Classes, SysUtils, Forms, testregistry, TestBase, LCLProc, LCLType, - SynEdit, SynEditKeyCmds; + SynEdit, SynEditKeyCmds, SynEditTextTrimmer; type @@ -43,6 +43,7 @@ type procedure TrimUndoRedo; procedure TrimUndoRedoEc; procedure NoTrimUndoRedo; + procedure TestInUndoBlock; end; implementation @@ -63,14 +64,14 @@ begin AssertEquals(FTName+' ('+ASubName+') Caret X', AExpX, SynEdit.CaretX); AssertEquals(FTName+' ('+ASubName+') Caret Y', AExpY, SynEdit.CaretY); end; -debugln(['done ',ASubName]); +//debugln(['done ',ASubName]); end; procedure TTestTrimSpace.test_start(AName, Txt: String); begin FTName := AName; SynEdit.Text := Txt; -debugln(['----- START ',AName]); +//debugln(['----- START ',AName]); end; @@ -525,6 +526,74 @@ begin end; +procedure TTestTrimSpace.TestInUndoBlock; +type TUpdateMode = (umNone, umOuter, umInner); +var UpdateMode: TUpdateMode; + + procedure BeginUndoBlock; + begin + if UpdateMode = umOuter then SynEdit.BeginUpdate; + SynEdit.BeginUndoBlock; + if UpdateMode = umInner then SynEdit.BeginUpdate; + end; + + procedure EndUndoBlock; + begin + if UpdateMode = umInner then SynEdit.EndUpdate; + SynEdit.EndUndoBlock; + if UpdateMode = umOuter then SynEdit.EndUpdate; + end; + + procedure DoTestInUndoBlock; + begin + ReCreateEdit; + SynEdit.Options := [eoTrimTrailingSpaces, eoAutoIndent, eoScrollPastEol]; // eoGroupUndo + SynEdit.TrimSpaceType := settLeaveLine; + SetLines(['abc d', 'mno', 'xyz', '']); + SetCaret(1,1); + // need to add space later, so it is regocnized as trailing + + BeginUndoBlock; + SynEdit.TextBetweenPointsEx[point(5,1), point(6,1), scamEnd] := ''; // delete d + SynEdit.TextBetweenPointsEx[point(4,2), point(4,2), scamEnd] := ' '; // add space + EndUndoBlock; + TestIsFullText ('modified after block', ['abc', 'mno ', 'xyz', '']); + TestIsCaret('modified after block', 5,2); + + SynEdit.Undo; + TestIsFullText ('Undone', ['abc d', 'mno', 'xyz', '']); + TestIsCaret('UnDone', 1,1); + + SynEdit.Redo; + TestIsFullText ('Redone', ['abc', 'mno ', 'xyz', '']); + TestIsCaret('Redone', 5,2); + + SynEdit.Undo; + TestIsFullText ('Undone 2', ['abc d', 'mno', 'xyz', '']); + TestIsCaret('UnDone 2', 1,1); + + SynEdit.Redo; + TestIsFullText ('Redone 2', ['abc', 'mno ', 'xyz', '']); + TestIsCaret('Redone 2', 5,2); + + end; + +begin + UpdateMode := umNone; + PushBaseName('Without BeginUpdate'); + DoTestInUndoBlock; + + UpdateMode := umInner; + PushBaseName('With BeginUpdate inside'); + DoTestInUndoBlock; + + UpdateMode := umOuter; + PushBaseName('With BeginUpdate outside'); + DoTestInUndoBlock; + + PopBaseName; +end; + initialization diff --git a/ide/sourceeditor.pp b/ide/sourceeditor.pp index 574147d528..7d5c66f680 100644 --- a/ide/sourceeditor.pp +++ b/ide/sourceeditor.pp @@ -2170,6 +2170,7 @@ begin CodeToolsInSync:=not NeedsUpdateCodeBuffer; if SrcLogEntry<>nil then begin SynEditor.BeginUndoBlock; + SynEditor.BeginUpdate; SynEditor.TemplateEdit.IncExternalEditLock; SynEditor.SyncroEdit.IncExternalEditLock; try @@ -2202,6 +2203,7 @@ begin finally SynEditor.SyncroEdit.DecExternalEditLock; SynEditor.TemplateEdit.DecExternalEditLock; + SynEditor.EndUpdate; SynEditor.EndUndoBlock; end; end else begin @@ -3384,7 +3386,6 @@ var P: TPoint; begin if ReadOnly then exit; - FEditor.BeginUpdate; FEditor.BeginUndoBlock; if not EditorComponent.SelAvail then begin P.Y := FEditor.CaretY; @@ -3398,9 +3399,9 @@ begin i:=EditorOpts.HighlighterList.FindByHighlighter(FEditor.Highlighter); if i>=0 then IsPascal := EditorOpts.HighlighterList[i].DefaultCommentType <> comtCPP; + // will show modal dialog - must not be in Editor.BeginUpdate block, or painting will not work FEditor.SelText:=AddConditional(EditorComponent.SelText,IsPascal); FEditor.EndUndoBlock; - FEditor.EndUpdate; end; procedure TSourceEditor.SortSelection; @@ -3884,9 +3885,11 @@ begin // user typed 'begin' if not LazarusIDE.SaveSourceEditorChangesToCodeCache(self) then exit; FEditor.BeginUndoBlock; + FEditor.BeginUpdate; try if not CodeToolBoss.CompleteBlock(CodeBuffer,p.X,p.Y,true) then exit; finally + FEditor.EndUpdate; FEditor.EndUndoBlock; end; end; @@ -3901,6 +3904,7 @@ begin if not LazarusIDE.SaveSourceEditorChangesToCodeCache(self) then exit; XY:=FEditor.LogicalCaretXY; FEditor.BeginUndoBlock; + FEditor.BeginUpdate; try if not CodeToolBoss.CompleteBlock(CodeBuffer,XY.X,XY.Y,false, NewCode,NewX,NewY,NewTopLine) then exit; @@ -3913,6 +3917,7 @@ begin FEditor.LogicalCaretXY:=XY; end; finally + FEditor.EndUpdate; FEditor.EndUndoBlock; end; end;