SynEdit: speed up large operations (search replace)

git-svn-id: trunk@43760 -
This commit is contained in:
martin 2014-01-18 01:19:11 +00:00
parent 309c217c7f
commit 3d9de1aad4
5 changed files with 166 additions and 52 deletions

View File

@ -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 = '');

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;