diff --git a/components/synedit/lazsynedittext.pas b/components/synedit/lazsynedittext.pas index d8be51bf7f..73277e3992 100644 --- a/components/synedit/lazsynedittext.pas +++ b/components/synedit/lazsynedittext.pas @@ -39,6 +39,8 @@ uses type TSynEditStrings = class; + TStringListLinesModifiedEvent = procedure(Sender: TSynEditStrings; + aIndex, aNewCount, aOldCount: Integer) of object; TStringListLineCountEvent = procedure(Sender: TSynEditStrings; aIndex, aCount: Integer) of object; TStringListLineEditEvent = procedure(Sender: TSynEditStrings; @@ -48,6 +50,7 @@ type TSynEditNotifyReason = ( // TStringListLineCountEvent senrLineCount, // Lines Inserted or Deleted (if not empty, they will trigger senrLineChange too) senrLineChange, // Lines modified (also triggered by senrEditAction) + senrLinesModified, // Send once in "EndUpdate". Modified, inserted or deleted senrHighlightChanged, // used by Highlighter (invalidate and fold checks needed) // TStringListLineEditEvent senrEditAction, // EditInsert, EditDelete, EditLineBreak, ... @@ -289,6 +292,8 @@ type procedure AddGenericHandler(AReason: TSynEditNotifyReason; AHandler: TMethod); virtual; abstract; + procedure AddModifiedHandler(AReason: TSynEditNotifyReason; + AHandler: TStringListLinesModifiedEvent); procedure AddChangeHandler(AReason: TSynEditNotifyReason; AHandler: TStringListLineCountEvent); procedure AddNotifyHandler(AReason: TSynEditNotifyReason; @@ -296,6 +301,8 @@ type procedure RemoveGenericHandler(AReason: TSynEditNotifyReason; AHandler: TMethod); virtual; abstract; + procedure RemoveModifiedHandler(AReason: TSynEditNotifyReason; + AHandler: TStringListLinesModifiedEvent); procedure RemoveChangeHandler(AReason: TSynEditNotifyReason; AHandler: TStringListLineCountEvent); procedure RemoveNotifyHandler(AReason: TSynEditNotifyReason; @@ -304,10 +311,11 @@ type procedure AddEditHandler(AHandler: TStringListLineEditEvent); procedure RemoveEditHandler(AHandler: TStringListLineEditEvent); procedure SendHighlightChanged(aIndex, aCount: Integer); override; + procedure SendNotification(AReason: TSynEditNotifyReason; + ASender: TSynEditStrings; aIndex, aCount: Integer); virtual; abstract; procedure SendNotification(AReason: TSynEditNotifyReason; ASender: TSynEditStrings; aIndex, aCount: Integer; - aBytePos: Integer = -1; aLen: Integer = 0; - aTxt: String = ''); virtual; abstract; + aBytePos: Integer; aLen: Integer; aTxt: String); virtual; abstract; procedure SendNotification(AReason: TSynEditNotifyReason; ASender: TObject); virtual; abstract; procedure FlushNotificationCache; virtual; abstract; @@ -431,10 +439,11 @@ type AHandler: TMethod); override; procedure RemoveGenericHandler(AReason: TSynEditNotifyReason; AHandler: TMethod); override; + procedure SendNotification(AReason: TSynEditNotifyReason; + ASender: TSynEditStrings; aIndex, aCount: Integer); override; procedure SendNotification(AReason: TSynEditNotifyReason; ASender: TSynEditStrings; aIndex, aCount: Integer; - aBytePos: Integer = -1; aLen: Integer = 0; - aTxt: String = ''); override; + aBytePos: Integer; aLen: Integer; aTxt: String); override; procedure SendNotification(AReason: TSynEditNotifyReason; ASender: TObject); override; procedure FlushNotificationCache; override; @@ -967,6 +976,13 @@ begin Result := (FSenderUpdateCount > 0) or (UpdateCount > 0); end; +procedure TSynEditStrings.AddModifiedHandler(AReason: TSynEditNotifyReason; + AHandler: TStringListLinesModifiedEvent); +begin + assert(AReason in [senrLinesModified], 'AddModifiedHandler'); + AddGenericHandler(AReason, TMethod(AHandler)); +end; + procedure TSynEditStrings.AddChangeHandler(AReason: TSynEditNotifyReason; AHandler: TStringListLineCountEvent); begin @@ -979,6 +995,13 @@ begin AddGenericHandler(AReason, TMethod(AHandler)); end; +procedure TSynEditStrings.RemoveModifiedHandler(AReason: TSynEditNotifyReason; + AHandler: TStringListLinesModifiedEvent); +begin + assert(AReason in [senrLinesModified], 'RemoveModifiedHandler'); + RemoveGenericHandler(AReason, TMethod(AHandler)); +end; + procedure TSynEditStrings.RemoveChangeHandler(AReason: TSynEditNotifyReason; AHandler: TStringListLineCountEvent); begin @@ -1415,6 +1438,12 @@ begin fSynStrings.EditRedo(Item); end; +procedure TSynEditStringsLinked.SendNotification(AReason: TSynEditNotifyReason; + ASender: TSynEditStrings; aIndex, aCount: Integer); +begin + fSynStrings.SendNotification(AReason, ASender, aIndex, aCount); +end; + procedure TSynEditStringsLinked.SendNotification(AReason: TSynEditNotifyReason; ASender: TSynEditStrings; aIndex, aCount: Integer; aBytePos: Integer = -1; aLen: Integer = 0; aTxt: String = ''); diff --git a/components/synedit/synedit.pp b/components/synedit/synedit.pp index b7e14ee9f3..43665a2cb2 100644 --- a/components/synedit/synedit.pp +++ b/components/synedit/synedit.pp @@ -6307,7 +6307,7 @@ begin begin FCaret.LinePos := FFoldedLinesView.ViewPosToTextIndex(FFoldedLinesView.Count)+1; end; - + // goto special line / column position ecGotoXY, ecSelGotoXY: if Assigned(Data) then begin @@ -8087,11 +8087,11 @@ begin begin // not at start of line -> jump to start of line NewPos.X := 1; - end else + end else begin // calculate line start position FirstNonBlank := -1; - if CaretY <= FTheLinesView.Count then + if CaretY <= FTheLinesView.Count then begin s := FTheLinesView[CaretXY.Y - 1]; @@ -8109,7 +8109,7 @@ begin // this line is not blank if FirstNonBlank < 1 then FirstNonBlank := 1; LineStart := LogicalToPhysicalPos(Point(FirstNonBlank, CaretY)).x; - end else + end else begin // this line is blank // -> use automatic line indent @@ -8905,7 +8905,7 @@ begin fOnChange(Self); end; -procedure TCustomSynEdit.ModifiedChanged(Sender: TObject); +procedure TCustomSynEdit.ModifiedChanged(Sender: TObject); begin StatusChanged([scModified]); end; diff --git a/components/synedit/syneditmarkupctrlmouselink.pp b/components/synedit/syneditmarkupctrlmouselink.pp index 37baff9d95..8b83127dc0 100644 --- a/components/synedit/syneditmarkupctrlmouselink.pp +++ b/components/synedit/syneditmarkupctrlmouselink.pp @@ -46,7 +46,7 @@ type FLastMouseCaretLogical: TPoint; function GetIsMouseOverLink: Boolean; procedure SetLastMouseCaret(const AValue: TPoint); - Procedure LinesChanged(Sender: TSynEditStrings; AIndex, ACount : Integer); + Procedure LinesChanged(Sender: TSynEditStrings; AIndex, ANewCount, AOldCount : Integer); function IsCtrlMouseShiftState(AShift: TShiftState; OnlyShowLink: Boolean): Boolean; procedure InternalUpdateCtrlMouse; protected @@ -104,8 +104,8 @@ begin Result := FCtrlLinkable and (FCtrlMouseLine >= 0); end; -procedure TSynEditMarkupCtrlMouseLink.LinesChanged(Sender: TSynEditStrings; AIndex, - ACount: Integer); +procedure TSynEditMarkupCtrlMouseLink.LinesChanged(Sender: TSynEditStrings; AIndex, ANewCount, + AOldCount: Integer); begin If LastMouseCaret.Y < 0 then exit; LastMouseCaret := Point(-1, -1); @@ -218,8 +218,7 @@ end; destructor TSynEditMarkupCtrlMouseLink.Destroy; begin if Lines <> nil then begin; - Lines.RemoveChangeHandler(senrLineCount, @LinesChanged); - Lines.RemoveChangeHandler(senrLineChange, @LinesChanged); + Lines.RemoveModifiedHandler(senrLinesModified, @LinesChanged); end; inherited Destroy; end; @@ -228,8 +227,7 @@ procedure TSynEditMarkupCtrlMouseLink.SetLines(const AValue: TSynEditStrings); begin inherited SetLines(AValue); if Lines <> nil then begin; - Lines.AddChangeHandler(senrLineCount, @LinesChanged); - Lines.AddChangeHandler(senrLineChange, @LinesChanged); + Lines.AddModifiedHandler(senrLinesModified, @LinesChanged); end; end; diff --git a/components/synedit/synedittextbuffer.pp b/components/synedit/synedittextbuffer.pp index 80e5ab3c89..c5b683f60e 100644 --- a/components/synedit/synedittextbuffer.pp +++ b/components/synedit/synedittextbuffer.pp @@ -63,6 +63,13 @@ type TStringListIndexEvent = procedure(Index: Integer) of object; + { TLinesModifiedNotificationList } + + TLinesModifiedNotificationList = Class(TSynMethodList) + public + Procedure CallRangeNotifyEvents(Sender: TSynEditStrings; aIndex, aNewCount, aOldCount: Integer); + end; + { TLineRangeNotificationList } TLineRangeNotificationList = Class(TSynMethodList) @@ -140,6 +147,7 @@ type FCachedNotify: Boolean; FCachedNotifyStart, FCachedNotifyCount: Integer; FCachedNotifySender: TSynEditStrings; + FModifiedNotifyStart, FModifiedNotifyNewCount, FModifiedNotifyOldCount: Integer; FIsInEditAction: Integer; FIgnoreSendNotification: array [TSynEditNotifyReason] of Integer; @@ -214,9 +222,11 @@ type AHandler: TMethod); override; procedure RemoveGenericHandler(AReason: TSynEditNotifyReason; AHandler: TMethod); override; + procedure SendNotification(AReason: TSynEditNotifyReason; + ASender: TSynEditStrings; aIndex, aCount: Integer); override; procedure SendNotification(AReason: TSynEditNotifyReason; ASender: TSynEditStrings; aIndex, aCount: Integer; - aBytePos: Integer = -1; aLen: Integer = 0; aTxt: String = ''); override; + aBytePos: Integer; aLen: Integer; aTxt: String); override; procedure SendNotification(AReason: TSynEditNotifyReason; ASender: TObject); override; procedure FlushNotificationCache; override; @@ -226,7 +236,7 @@ type property AttachedSynEdits[Index: Integer]: TSynEditBase read GetAttachedSynEdits; procedure CopyHanlders(OtherLines: TSynEditStringList; AOwner: TObject = nil); procedure RemoveHanlders(AOwner: TObject); - procedure SendCachedNotify; // ToDO: review caghing versus changes to topline and other values + procedure SendCachedNotify; // ToDO: review caching versus changes to topline and other values public property DosFileFormat: boolean read fDosFileFormat write fDosFileFormat; property LengthOfLongestLine: integer read GetLengthOfLongestLine; @@ -567,6 +577,8 @@ begin do case r of senrLineCount, senrLineChange, senrHighlightChanged: FNotifyLists[r] := TLineRangeNotificationList.Create; + senrLinesModified: + FNotifyLists[r] := TLinesModifiedNotificationList.Create; senrEditAction: FNotifyLists[r] := TLineEditNotificationList.Create; else @@ -666,7 +678,7 @@ begin EndUpdate; end; -procedure TSynEditStringList.DeleteLines(Index, NumLines: Integer); +procedure TSynEditStringList.DeleteLines(Index, NumLines: integer); begin if NumLines > 0 then begin // Ensure correct index, so DeleteLines will not throw exception @@ -1213,9 +1225,17 @@ begin SendNotification(senrIncPaintLock, Sender); // DoIncPaintLock SendNotification(senrAfterIncPaintLock, Sender); FCachedNotify := False; + FModifiedNotifyStart := -1; + FModifiedNotifyOldCount := 0; + FModifiedNotifyNewCount := 0; end else begin if FCachedNotify then SendCachedNotify; + assert( (FModifiedNotifyOldCount >= 0) and (FModifiedNotifyNewCount >= 0), 'FModifiedNotify___Count >= 0'); + if (FModifiedNotifyOldCount > 0) or (FModifiedNotifyNewCount > 0) then + TLinesModifiedNotificationList(FNotifyLists[senrLinesModified]) + .CallRangeNotifyEvents(Self, FModifiedNotifyStart, FModifiedNotifyNewCount, FModifiedNotifyOldCount); + // Above notifications must be before senrDecPaintLock is sent FIsInDecPaintLock := True; try SendNotification(senrBeforeDecPaintLock, Sender); @@ -1392,12 +1412,56 @@ begin end; procedure TSynEditStringList.SendNotification(AReason: TSynEditNotifyReason; - ASender: TSynEditStrings; aIndex, aCount: Integer; - aBytePos: Integer = -1; aLen: Integer = 0; aTxt: String = ''); + ASender: TSynEditStrings; aIndex, aCount: Integer); +var + i, oldcount, overlap: Integer; begin + assert(AReason in [senrLineChange, senrLineCount, senrLinesModified, senrHighlightChanged], 'Correct SendNotification'); if FIgnoreSendNotification[AReason] > 0 then exit; - if IsUpdating then begin; + if IsUpdating and (AReason in [senrLineChange, senrLineCount]) then begin + // senrLinesModified + assert( (FModifiedNotifyOldCount >= 0) and (FModifiedNotifyNewCount >= 0), 'FModifiedNotify___Count >= 0'); + assert(aIndex >= 0, 'SendNotification index'); + if (FModifiedNotifyOldCount = 0) and (FModifiedNotifyNewCount = 0) then + FModifiedNotifyStart := aIndex; + + if aIndex < FModifiedNotifyStart then begin + i := FModifiedNotifyStart - aIndex; + FModifiedNotifyStart := aIndex; + FModifiedNotifyNewCount := FModifiedNotifyNewCount + i; + FModifiedNotifyOldCount := FModifiedNotifyOldCount + i; + end; + + if AReason = senrLineCount then begin + if aCount < 0 then begin + oldcount := -aCount; + if (aIndex < FModifiedNotifyStart + FModifiedNotifyNewCount) then begin + overlap := (FModifiedNotifyStart + FModifiedNotifyNewCount) - aIndex; + if overlap > oldcount then overlap := oldcount; + FModifiedNotifyNewCount := FModifiedNotifyNewCount - overlap; + oldcount := oldcount - overlap; + end; + FModifiedNotifyOldCount := FModifiedNotifyOldCount + oldcount; + oldcount := 0; + end + else begin + FModifiedNotifyNewCount := FModifiedNotifyNewCount + aCount; + oldcount := aCount; // because already added to newcount + end; + end + else + if AReason = senrLineChange then begin + oldcount := aCount; + end; + + if aIndex + oldcount > FModifiedNotifyStart + FModifiedNotifyNewCount then begin + i := (aIndex + oldcount) - (FModifiedNotifyStart + FModifiedNotifyNewCount); + FModifiedNotifyNewCount := FModifiedNotifyNewCount + i; + FModifiedNotifyOldCount := FModifiedNotifyOldCount + i; + end; + + // CacheNotify if AReason = senrLineCount then begin // maybe cache and combine if not FCachedNotify then begin @@ -1418,7 +1482,8 @@ begin FCachedNotify := False; exit; end; - end; + end + else if FCachedNotify and (AReason = senrLineChange) and (ASender = FCachedNotifySender) and (FCachedNotifyCount > 0) and (aIndex >= FCachedNotifyStart) and @@ -1428,19 +1493,35 @@ begin if FCachedNotify then SendCachedNotify; + end + else begin + case AReason of + senrLineChange: + TLinesModifiedNotificationList(FNotifyLists[senrLinesModified]) + .CallRangeNotifyEvents(ASender, aIndex, aCount, aCount); + senrLineCount: + TLinesModifiedNotificationList(FNotifyLists[senrLinesModified]) + .CallRangeNotifyEvents(ASender, aIndex, aCount, 0); + end; end; - case AReason of - senrLineChange, senrLineCount, senrHighlightChanged: - TLineRangeNotificationList(FNotifyLists[AReason]) - .CallRangeNotifyEvents(ASender, aIndex, aCount); - senrEditAction: - // aindex is mis-named (linepos) for edit action - TLineEditNotificationList(FNotifyLists[AReason]) - .CallRangeNotifyEvents(ASender, aIndex, aBytePos, aLen, aCount, aTxt); - else - raise Exception.Create('Invalid'); - end; + TLineRangeNotificationList(FNotifyLists[AReason]) + .CallRangeNotifyEvents(ASender, aIndex, aCount); +end; + +procedure TSynEditStringList.SendNotification(AReason: TSynEditNotifyReason; + ASender: TSynEditStrings; aIndex, aCount: Integer; + aBytePos: Integer; aLen: Integer; aTxt: String); +begin + assert(AReason in [senrEditAction], 'Correct SendNotification'); + if FIgnoreSendNotification[AReason] > 0 then exit; + + if FCachedNotify then + SendCachedNotify; + + // aindex is mis-named (linepos) for edit action + TLineEditNotificationList(FNotifyLists[AReason]) + .CallRangeNotifyEvents(ASender, aIndex, aBytePos, aLen, aCount, aTxt); end; procedure TSynEditStringList.SendNotification(AReason: TSynEditNotifyReason; @@ -1448,7 +1529,7 @@ procedure TSynEditStringList.SendNotification(AReason: TSynEditNotifyReason; begin if FCachedNotify then SendCachedNotify; - if AReason in [senrLineChange, senrLineCount, senrHighlightChanged, senrEditAction] then + if AReason in [senrLineChange, senrLineCount, senrLinesModified, senrHighlightChanged, senrEditAction] then raise Exception.Create('Invalid'); FNotifyLists[AReason].CallNotifyEvents(ASender); end; @@ -1599,6 +1680,18 @@ begin end; end; +{ TLinesModifiedNotificationList } + +procedure TLinesModifiedNotificationList.CallRangeNotifyEvents(Sender: TSynEditStrings; aIndex, + aNewCount, aOldCount: Integer); +var + i: LongInt; +begin + i:=Count; + while NextDownIndex(i) do + TStringListLinesModifiedEvent(Items[i])(Sender, aIndex, aNewCount, aOldCount); +end; + { TLineRangeNotificationList } procedure TLineRangeNotificationList.CallRangeNotifyEvents(Sender: TSynEditStrings; diff --git a/components/synedit/syngutterlineoverview.pp b/components/synedit/syngutterlineoverview.pp index 67d66f0567..6ba198761a 100644 --- a/components/synedit/syngutterlineoverview.pp +++ b/components/synedit/syngutterlineoverview.pp @@ -229,8 +229,7 @@ type procedure ReCalc; override; procedure BufferChanged(Sender: TObject); - procedure LineChanged(Sender: TSynEditStrings; AIndex, ACount: Integer); - procedure LineCountChanged(Sender: TSynEditStrings; AIndex, ACount: Integer); + procedure LineModified(Sender: TSynEditStrings; aIndex, aNewCount, aOldCount: Integer); procedure SynStatusChanged(Sender: TObject; Changes: TSynStatusChanges); public constructor Create(AOwner: TComponent); override; @@ -1070,28 +1069,24 @@ end; procedure TSynGutterLOvProviderModifiedLines.BufferChanged(Sender: TObject); begin TSynEditStringList(Sender).RemoveHanlders(self); - TSynEditStringList(TextBuffer).AddGenericHandler(senrLineChange, TMethod(@LineChanged)); - TSynEditStringList(TextBuffer).AddGenericHandler(senrLineCount, TMethod(@LineCountChanged)); + TSynEditStringList(TextBuffer).AddModifiedHandler(senrLinesModified, @LineModified); TSynEditStringList(TextBuffer).AddGenericHandler(senrTextBufferChanged, TMethod(@BufferChanged)); end; -procedure TSynGutterLOvProviderModifiedLines.LineChanged(Sender: TSynEditStrings; - AIndex, ACount: Integer); +procedure TSynGutterLOvProviderModifiedLines.LineModified(Sender: TSynEditStrings; aIndex, + aNewCount, aOldCount: Integer); begin if (FFirstTextLineChanged < 0) or (AIndex + 1 < FFirstTextLineChanged) then FFirstTextLineChanged := AIndex + 1; + + if aOldCount = aNewCount then + aIndex := aIndex + aOldCount + else + aIndex := TextBuffer.Count; if (FLastTextLineChanged <> 0) and (AIndex + 1 > FLastTextLineChanged) then FLastTextLineChanged := AIndex + 1; - InvalidateTextLines(FFirstTextLineChanged, FLastTextLineChanged); -end; -procedure TSynGutterLOvProviderModifiedLines.LineCountChanged(Sender: TSynEditStrings; AIndex, - ACount: Integer); -begin - if (FFirstTextLineChanged < 0) or (AIndex + 1 < FFirstTextLineChanged) then - FFirstTextLineChanged := AIndex + 1; - FLastTextLineChanged := 0; // open end - InvalidateTextLines(FFirstTextLineChanged, TextBuffer.Count); + InvalidateTextLines(FFirstTextLineChanged, FLastTextLineChanged); end; procedure TSynGutterLOvProviderModifiedLines.SynStatusChanged(Sender: TObject; @@ -1107,8 +1102,7 @@ end; constructor TSynGutterLOvProviderModifiedLines.Create(AOwner: TComponent); begin inherited Create(AOwner); - TSynEditStringList(TextBuffer).AddGenericHandler(senrLineChange, TMethod(@LineChanged)); - TSynEditStringList(TextBuffer).AddGenericHandler(senrLineCount, TMethod(@LineCountChanged)); + TSynEditStringList(TextBuffer).AddModifiedHandler(senrLinesModified, @LineModified); TSynEditStringList(TextBuffer).AddGenericHandler(senrTextBufferChanged, TMethod(@BufferChanged)); TCustomSynEdit(SynEdit).RegisterStatusChangedHandler(@SynStatusChanged, [scModified]); FFirstTextLineChanged := -1;