722 lines
17 KiB
ObjectPascal
722 lines
17 KiB
ObjectPascal
unit ATSynEdit_Finder;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
SysUtils, Classes, Dialogs, Forms,
|
|
Math,
|
|
RegExpr, //must be with {$define Unicode}
|
|
ATSynEdit,
|
|
ATSynEdit_Carets,
|
|
ATStringProc,
|
|
ATStringProc_TextBuffer;
|
|
|
|
type
|
|
TATFinderProgress = procedure(Sender: TObject; ACurPos, AMaxPos: integer;
|
|
var AContinue: boolean) of object;
|
|
TATFinderFound = procedure(Sender: TObject; APos1, APos2: TPoint) of object;
|
|
TATFinderConfirmReplace = procedure(Sender: TObject;
|
|
APos1, APos2: TPoint; AForMany: boolean;
|
|
var AConfirm, AContinue: boolean) of object;
|
|
|
|
type
|
|
{ TATTextFinder }
|
|
|
|
TATTextFinder = class
|
|
private
|
|
FMatchPos: integer;
|
|
FMatchLen: integer;
|
|
FStrFind: UnicodeString;
|
|
FStrReplace: UnicodeString;
|
|
FStrReplacement: UnicodeString;
|
|
FOnProgress: TATFinderProgress;
|
|
FOnBadRegex: TNotifyEvent;
|
|
function DoCountMatchesRegex(FromPos: integer; AWithEvent: boolean): integer;
|
|
function DoCountMatchesUsual(FromPos: integer; AWithEvent: boolean): Integer;
|
|
function DoFindMatchRegex(FromPos: integer; var MatchPos, MatchLen: integer): boolean;
|
|
function DoFindMatchUsual(FromPos: integer): Integer;
|
|
function IsMatchUsual(APos: integer): boolean;
|
|
procedure SetStrFind(const AValue: UnicodeString);
|
|
procedure SetStrReplace(const AValue: UnicodeString);
|
|
protected
|
|
procedure DoOnFound; virtual;
|
|
public
|
|
OptBack: boolean; //for non-regex
|
|
OptWords: boolean; //for non-regex
|
|
OptCase: boolean; //for regex and usual
|
|
OptRegex: boolean;
|
|
OptWrapped: boolean;
|
|
StrText: UnicodeString;
|
|
property StrFind: UnicodeString read FStrFind write SetStrFind;
|
|
property StrReplace: UnicodeString read FStrReplace write SetStrReplace;
|
|
property StrReplacement: UnicodeString read FStrReplacement; //for regex
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
function FindMatch(ANext: boolean; ASkipLen: integer; AStartPos: integer): boolean;
|
|
property MatchPos: integer read FMatchPos; //have meaning if FindMatch returned True
|
|
property MatchLen: integer read FMatchLen; //too
|
|
property OnProgress: TATFinderProgress read FOnProgress write FOnProgress;
|
|
property OnBadRegex: TNotifyEvent read FOnBadRegex write FOnBadRegex;
|
|
end;
|
|
|
|
type
|
|
{ TATEditorFinder }
|
|
|
|
TATEditorFinder = class(TATTextFinder)
|
|
private
|
|
FBuffer: TATStringBuffer;
|
|
FEditor: TATSynEdit;
|
|
FSkipLen: integer;
|
|
FOnFound: TATFinderFound;
|
|
FOnConfirmReplace: TATFinderConfirmReplace;
|
|
function DoFindOrReplace_Internal(ANext, AReplace, AForMany: boolean; out
|
|
AChanged: boolean; AStartPos: integer): boolean;
|
|
procedure DoFixCaretSelectionDirection;
|
|
procedure DoReplaceTextInEditor(P1, P2: TPoint);
|
|
function GetOffsetOfCaret: integer;
|
|
function GetOffsetStartPos: integer;
|
|
function GetRegexSkipIncrement: integer;
|
|
protected
|
|
procedure DoOnFound; override;
|
|
public
|
|
OptFromCaret: boolean;
|
|
OptConfirmReplace: boolean;
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure UpdateBuffer;
|
|
property Editor: TATSynEdit read FEditor write FEditor;
|
|
property OnFound: TATFinderFound read FOnFound write FOnFound;
|
|
property OnConfirmReplace: TATFinderConfirmReplace read FOnConfirmReplace write FOnConfirmReplace;
|
|
function DoFindOrReplace(ANext, AReplace, AForMany: boolean; out AChanged: boolean): boolean;
|
|
function DoReplaceSelectedMatch: boolean;
|
|
function DoCountAll(AWithEvent: boolean): integer;
|
|
function DoReplaceAll: integer;
|
|
function IsSelectionStartsAtFoundMatch: boolean;
|
|
end;
|
|
|
|
|
|
implementation
|
|
|
|
function IsWordChar(ch: Widechar): boolean;
|
|
begin
|
|
Result:= ATStringProc.IsCharWord(ch, '');
|
|
end;
|
|
|
|
function SRegexReplaceEscapedTabs(const AStr: string): string;
|
|
begin
|
|
Result:= AStr;
|
|
Result:= StringReplace(Result, '\\', #1, [rfReplaceAll]);
|
|
Result:= StringReplace(Result, '\t', #9, [rfReplaceAll]);
|
|
Result:= StringReplace(Result, #1, '\\', [rfReplaceAll]);
|
|
end;
|
|
|
|
|
|
function TATTextFinder.IsMatchUsual(APos: integer): boolean;
|
|
var
|
|
LenF, LastPos: integer;
|
|
begin
|
|
Result:= false;
|
|
if StrFind='' then exit;
|
|
if StrText='' then exit;
|
|
|
|
LenF:= Length(StrFind);
|
|
LastPos:= Length(StrText)-LenF+1;
|
|
|
|
if OptCase then
|
|
Result:= CompareMem(@StrFind[1], @StrText[APos], LenF*2)
|
|
else
|
|
Result:=
|
|
UnicodeLowerCase(StrFind) =
|
|
UnicodeLowerCase(Copy(StrText, APos, LenF));
|
|
|
|
if Result then
|
|
if OptWords then
|
|
Result:=
|
|
((APos <= 1) or (not IsWordChar(StrText[APos - 1]))) and
|
|
((APos >= LastPos) or (not IsWordChar(StrText[APos + LenF])));
|
|
end;
|
|
|
|
procedure TATTextFinder.SetStrFind(const AValue: UnicodeString);
|
|
begin
|
|
if FStrFind=AValue then Exit;
|
|
FStrFind:= AValue;
|
|
FMatchPos:= -1;
|
|
FMatchLen:= 0;
|
|
end;
|
|
|
|
procedure TATTextFinder.SetStrReplace(const AValue: UnicodeString);
|
|
begin
|
|
if FStrReplace=AValue then Exit;
|
|
FStrReplace:= AValue;
|
|
end;
|
|
|
|
procedure TATTextFinder.DoOnFound;
|
|
begin
|
|
//
|
|
end;
|
|
|
|
function TATTextFinder.DoFindMatchUsual(FromPos: integer): Integer;
|
|
var
|
|
LastPos, i: integer;
|
|
begin
|
|
Result:= 0;
|
|
if StrText='' then exit;
|
|
if StrFind='' then exit;
|
|
LastPos:= Length(StrText) - Length(StrFind) + 1;
|
|
|
|
if not OptBack then
|
|
for i:= FromPos to LastPos do
|
|
begin
|
|
if IsMatchUsual(i) then
|
|
begin
|
|
Result:= i;
|
|
Break
|
|
end;
|
|
end
|
|
else
|
|
for i:= FromPos downto 1 do
|
|
begin
|
|
if IsMatchUsual(i) then
|
|
begin
|
|
Result:= i;
|
|
Break
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TATTextFinder.DoFindMatchRegex(FromPos: integer; var MatchPos,
|
|
MatchLen: integer): boolean;
|
|
var
|
|
Obj: TRegExpr;
|
|
begin
|
|
Result:= false;
|
|
if StrText='' then exit;
|
|
if StrFind='' then exit;
|
|
|
|
Obj:= TRegExpr.Create;
|
|
try
|
|
Obj.ModifierS:= false; //don't catch all text by .*
|
|
Obj.ModifierM:= true; //allow to work with ^$
|
|
Obj.ModifierI:= not OptCase;
|
|
|
|
try
|
|
Obj.Expression:= StrFind;
|
|
Obj.InputString:= StrText;
|
|
Result:= Obj.ExecPos(FromPos);
|
|
except
|
|
if Assigned(FOnBadRegex) then
|
|
FOnBadRegex(Self);
|
|
Result:= false;
|
|
end;
|
|
|
|
if Result then
|
|
begin
|
|
MatchPos:= Obj.MatchPos[0];
|
|
MatchLen:= Obj.MatchLen[0];
|
|
if StrReplace<>'' then
|
|
FStrReplacement:= Obj.Replace(Obj.Match[0], SRegexReplaceEscapedTabs(StrReplace), true);
|
|
end;
|
|
finally
|
|
FreeAndNil(Obj);
|
|
end;
|
|
end;
|
|
|
|
function TATTextFinder.DoCountMatchesUsual(FromPos: integer; AWithEvent: boolean
|
|
): Integer;
|
|
var
|
|
LastPos, i: Integer;
|
|
Ok: boolean;
|
|
begin
|
|
Result:= 0;
|
|
if StrText='' then exit;
|
|
if StrFind='' then exit;
|
|
LastPos:= Length(StrText) - Length(StrFind) + 1;
|
|
|
|
for i:= FromPos to LastPos do
|
|
begin
|
|
if Application.Terminated then exit;
|
|
if IsMatchUsual(i) then
|
|
begin
|
|
Inc(Result);
|
|
if AWithEvent then
|
|
begin
|
|
FMatchPos:= i;
|
|
FMatchLen:= Length(StrFind);
|
|
DoOnFound;
|
|
end;
|
|
|
|
if Assigned(FOnProgress) then
|
|
begin
|
|
Ok:= true;
|
|
FOnProgress(Self, i, LastPos, Ok);
|
|
if not Ok then Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TATTextFinder.DoCountMatchesRegex(FromPos: integer; AWithEvent: boolean
|
|
): integer;
|
|
var
|
|
Obj: TRegExpr;
|
|
Ok: boolean;
|
|
begin
|
|
Result:= 0;
|
|
if StrFind='' then exit;
|
|
if StrText='' then exit;
|
|
|
|
Obj:= TRegExpr.Create;
|
|
try
|
|
Obj.ModifierS:= false;
|
|
Obj.ModifierM:= true;
|
|
Obj.ModifierI:= not OptCase;
|
|
|
|
try
|
|
Obj.Expression:= StrFind;
|
|
Obj.InputString:= StrText;
|
|
Ok:= Obj.ExecPos(FromPos);
|
|
except
|
|
if Assigned(FOnBadRegex) then
|
|
FOnBadRegex(Self);
|
|
Result:= 0;
|
|
Exit;
|
|
end;
|
|
|
|
if Ok then
|
|
begin
|
|
Inc(Result);
|
|
if AWithEvent then
|
|
begin
|
|
FMatchPos:= Obj.MatchPos[0];
|
|
FMatchLen:= Obj.MatchLen[0];
|
|
DoOnFound;
|
|
end;
|
|
|
|
while Obj.ExecNext do
|
|
begin
|
|
if Application.Terminated then exit;
|
|
Inc(Result);
|
|
if AWithEvent then
|
|
begin
|
|
FMatchPos:= Obj.MatchPos[0];
|
|
FMatchLen:= Obj.MatchLen[0];
|
|
DoOnFound;
|
|
end;
|
|
|
|
if Assigned(FOnProgress) then
|
|
begin
|
|
Ok:= true;
|
|
FOnProgress(Self, Obj.MatchPos[0], Length(StrText), Ok);
|
|
if not Ok then Break;
|
|
end;
|
|
end;
|
|
end;
|
|
finally
|
|
FreeAndNil(Obj);
|
|
end;
|
|
end;
|
|
|
|
procedure TATEditorFinder.UpdateBuffer;
|
|
var
|
|
Lens: TList;
|
|
i: integer;
|
|
begin
|
|
Lens:= TList.Create;
|
|
try
|
|
Lens.Clear;
|
|
for i:= 0 to FEditor.Strings.Count-1 do
|
|
Lens.Add(pointer(Length(FEditor.Strings.Lines[i])));
|
|
FBuffer.Setup(FEditor.Strings.TextString, Lens, 1);
|
|
finally
|
|
FreeAndNil(Lens);
|
|
end;
|
|
|
|
StrText:= FBuffer.FText;
|
|
end;
|
|
|
|
constructor TATEditorFinder.Create;
|
|
begin
|
|
inherited;
|
|
FEditor:= nil;
|
|
FBuffer:= TATStringBuffer.Create;
|
|
OptFromCaret:= false;
|
|
OptConfirmReplace:= false;
|
|
end;
|
|
|
|
destructor TATEditorFinder.Destroy;
|
|
begin
|
|
FEditor:= nil;
|
|
FreeAndNil(FBuffer);
|
|
inherited;
|
|
end;
|
|
|
|
function TATEditorFinder.GetOffsetOfCaret: integer;
|
|
var
|
|
Pnt: TPoint;
|
|
begin
|
|
with FEditor.Carets[0] do
|
|
begin
|
|
Pnt.X:= PosX;
|
|
Pnt.Y:= PosY;
|
|
end;
|
|
|
|
Result:= FBuffer.CaretToStr(Pnt);
|
|
Inc(Result); //was 0-based
|
|
|
|
//find-back must goto previous match
|
|
if OptBack then
|
|
Dec(Result, Length(StrFind));
|
|
|
|
if Result<1 then
|
|
Result:= 1;
|
|
end;
|
|
|
|
function TATEditorFinder.DoCountAll(AWithEvent: boolean): integer;
|
|
begin
|
|
UpdateBuffer;
|
|
if OptRegex then
|
|
Result:= DoCountMatchesRegex(1, AWithEvent)
|
|
else
|
|
Result:= DoCountMatchesUsual(1, AWithEvent);
|
|
end;
|
|
|
|
function TATEditorFinder.DoReplaceAll: integer;
|
|
var
|
|
Ok, Changed: boolean;
|
|
begin
|
|
Result:= 0;
|
|
if DoFindOrReplace(false, true, true, Changed) then
|
|
begin
|
|
if Changed then Inc(Result);
|
|
while DoFindOrReplace(true, true, true, Changed) do
|
|
begin
|
|
if Application.Terminated then exit;
|
|
if Changed then Inc(Result);
|
|
if Assigned(FOnProgress) then
|
|
begin
|
|
Ok:= true;
|
|
FOnProgress(Self, FMatchPos, Length(StrText), Ok);
|
|
if not Ok then Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TATEditorFinder.DoReplaceTextInEditor(P1, P2: TPoint);
|
|
var
|
|
Shift, PosAfter: TPoint;
|
|
Str: UnicodeString;
|
|
begin
|
|
if OptRegex then
|
|
Str:= StrReplacement
|
|
else
|
|
Str:= StrReplace;
|
|
|
|
FEditor.Strings.BeginUndoGroup;
|
|
FEditor.Strings.TextDeleteRange(P1.X, P1.Y, P2.X, P2.Y, Shift, PosAfter);
|
|
FEditor.Strings.TextInsert(P1.X, P1.Y, Str, false, Shift, PosAfter);
|
|
FEditor.Strings.EndUndoGroup;
|
|
|
|
//correct caret pos
|
|
//(e.g. replace "dddddd" to "--": move lefter)
|
|
if not OptBack then
|
|
FEditor.Carets[0].PosX:= P1.X+Length(Str);
|
|
end;
|
|
|
|
function TATEditorFinder.GetOffsetStartPos: integer;
|
|
begin
|
|
if OptFromCaret then
|
|
Result:= GetOffsetOfCaret
|
|
else
|
|
if OptRegex then
|
|
Result:= 1
|
|
else
|
|
if OptBack then
|
|
Result:= Length(StrText)
|
|
else
|
|
Result:= 1;
|
|
end;
|
|
|
|
procedure TATEditorFinder.DoFixCaretSelectionDirection;
|
|
var
|
|
Caret: TATCaretItem;
|
|
X1, Y1, X2, Y2: integer;
|
|
bSel: boolean;
|
|
begin
|
|
if FEditor.Carets.Count=0 then exit;
|
|
Caret:= FEditor.Carets[0];
|
|
Caret.GetRange(X1, Y1, X2, Y2, bSel);
|
|
if not bSel then exit;
|
|
|
|
if OptBack then
|
|
begin
|
|
Caret.PosX:= X1;
|
|
Caret.PosY:= Y1;
|
|
Caret.EndX:= X2;
|
|
Caret.EndY:= Y2;
|
|
end
|
|
else
|
|
begin
|
|
Caret.PosX:= X2;
|
|
Caret.PosY:= Y2;
|
|
Caret.EndX:= X1;
|
|
Caret.EndY:= Y1;
|
|
end;
|
|
end;
|
|
|
|
function TATEditorFinder.DoFindOrReplace(ANext, AReplace, AForMany: boolean;
|
|
out AChanged: boolean): boolean;
|
|
var
|
|
NStartPos: integer;
|
|
begin
|
|
Result:= false;
|
|
AChanged:= false;
|
|
|
|
if not Assigned(FEditor) then
|
|
begin
|
|
Showmessage('Finder.Editor not set');
|
|
Exit
|
|
end;
|
|
if StrFind='' then
|
|
begin
|
|
Showmessage('Finder.StrFind not set');
|
|
Exit
|
|
end;
|
|
if FEditor.Carets.Count=0 then
|
|
begin
|
|
Showmessage('Editor has not caret');
|
|
Exit
|
|
end;
|
|
|
|
if AReplace and FEditor.ModeReadOnly then exit;
|
|
if OptRegex then OptBack:= false;
|
|
|
|
DoFixCaretSelectionDirection;
|
|
|
|
NStartPos:= GetOffsetStartPos;
|
|
Result:= DoFindOrReplace_Internal(ANext, AReplace, AForMany, AChanged, NStartPos);
|
|
|
|
if not Result and OptWrapped then
|
|
if (not OptBack and (NStartPos>1)) or
|
|
(OptBack and (NStartPos<Length(StrText))) then
|
|
begin
|
|
//we must have AReplace=false
|
|
//(if not, need more actions: don't allow to replace in wrapped part if too big pos)
|
|
//
|
|
if DoFindOrReplace_Internal(ANext, false, AForMany, AChanged,
|
|
IfThen(not OptBack, 1, Length(StrText))) then
|
|
begin
|
|
Result:= (not OptBack and (MatchPos<NStartPos)) or
|
|
(OptBack and (MatchPos>NStartPos));
|
|
if not Result then
|
|
begin
|
|
FMatchPos:= -1;
|
|
FMatchLen:= 0;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TATEditorFinder.DoFindOrReplace_Internal(ANext, AReplace, AForMany: boolean;
|
|
out AChanged: boolean; AStartPos: integer): boolean;
|
|
//function usually called 1 time in outer func,
|
|
//or 1-2 times if OptWrap=true
|
|
var
|
|
P1, P2: TPoint;
|
|
ConfirmThis, ConfirmContinue: boolean;
|
|
begin
|
|
AChanged:= false;
|
|
Result:= FindMatch(ANext, FSkipLen, AStartPos);
|
|
FSkipLen:= FMatchLen;
|
|
|
|
if Result then
|
|
begin
|
|
P1:= FBuffer.StrToCaret(MatchPos-1);
|
|
P2:= FBuffer.StrToCaret(MatchPos-1+MatchLen);
|
|
FEditor.DoCaretSingle(P1.X, P1.Y);
|
|
|
|
if AReplace then
|
|
begin
|
|
ConfirmThis:= true;
|
|
ConfirmContinue:= true;
|
|
|
|
if OptConfirmReplace then
|
|
if Assigned(FOnConfirmReplace) then
|
|
FOnConfirmReplace(Self, P1, P2, AForMany, ConfirmThis, ConfirmContinue);
|
|
|
|
if not ConfirmContinue then
|
|
begin
|
|
Result:= false;
|
|
Exit;
|
|
end;
|
|
|
|
if ConfirmThis then
|
|
begin
|
|
DoReplaceTextInEditor(P1, P2);
|
|
UpdateBuffer;
|
|
|
|
if OptRegex then
|
|
FSkipLen:= Length(StrReplacement)+GetRegexSkipIncrement
|
|
else
|
|
FSkipLen:= Length(StrReplace);
|
|
AChanged:= true;
|
|
end;
|
|
end;
|
|
|
|
if AReplace then
|
|
//don't select
|
|
FEditor.DoCaretSingle(P1.X, P1.Y)
|
|
else
|
|
//select to right (find forward) or to left (find back)
|
|
if OptBack then
|
|
FEditor.DoCaretSingle(P1.X, P1.Y, P2.X, P2.Y, true)
|
|
else
|
|
FEditor.DoCaretSingle(P2.X, P2.Y, P1.X, P1.Y, true);
|
|
end;
|
|
end;
|
|
|
|
function TATEditorFinder.IsSelectionStartsAtFoundMatch: boolean;
|
|
var
|
|
Caret: TATCaretItem;
|
|
X1, Y1, X2, Y2: integer;
|
|
PosOfBegin, PosOfEnd: integer;
|
|
bSel: boolean;
|
|
begin
|
|
Result:= false;
|
|
if FEditor.Carets.Count=0 then exit;
|
|
Caret:= FEditor.Carets[0];
|
|
Caret.GetRange(X1, Y1, X2, Y2, bSel);
|
|
if not bSel then exit;
|
|
|
|
PosOfBegin:= FBuffer.CaretToStr(Point(X1, Y1))+1;
|
|
PosOfEnd:= FBuffer.CaretToStr(Point(X2, Y2))+1;
|
|
|
|
//allow to replace, also if selection=Strfind
|
|
Result:=
|
|
((PosOfBegin=FMatchPos) and (PosOfEnd=FMatchPos+FMatchLen)) or
|
|
((StrFind<>'') and (FEditor.TextSelected=StrFind));
|
|
end;
|
|
|
|
function TATEditorFinder.DoReplaceSelectedMatch: boolean;
|
|
var
|
|
Caret: TATCaretItem;
|
|
P1, P2: TPoint;
|
|
X1, Y1, X2, Y2: integer;
|
|
bSel: boolean;
|
|
begin
|
|
Result:= false;
|
|
if not IsSelectionStartsAtFoundMatch then
|
|
begin
|
|
//do Find-next (from caret)
|
|
DoFindOrReplace(false, false, false, bSel);
|
|
exit;
|
|
end;
|
|
|
|
Caret:= FEditor.Carets[0];
|
|
Caret.GetRange(X1, Y1, X2, Y2, bSel);
|
|
if not bSel then exit;
|
|
P1:= Point(X1, Y1);
|
|
P2:= Point(X2, Y2);
|
|
|
|
Caret.EndX:= -1;
|
|
Caret.EndY:= -1;
|
|
|
|
DoReplaceTextInEditor(P1, P2);
|
|
UpdateBuffer;
|
|
|
|
if OptRegex then
|
|
FSkipLen:= Length(StrReplacement)
|
|
else
|
|
FSkipLen:= Length(StrReplace);
|
|
Result:= true;
|
|
end;
|
|
|
|
|
|
constructor TATTextFinder.Create;
|
|
begin
|
|
StrText:= '';
|
|
FStrFind:= '';
|
|
FStrReplace:= '';
|
|
FStrReplacement:= '';
|
|
OptBack:= false;
|
|
OptCase:= false;
|
|
OptWords:= false;
|
|
OptRegex:= false;
|
|
FMatchPos:= -1;
|
|
FMatchLen:= 0;
|
|
end;
|
|
|
|
destructor TATTextFinder.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TATTextFinder.FindMatch(ANext: boolean; ASkipLen: integer; AStartPos: integer): boolean;
|
|
var
|
|
FromPos: integer;
|
|
begin
|
|
Result:= false;
|
|
if StrText='' then Exit;
|
|
if StrFind='' then Exit;
|
|
|
|
//regex code
|
|
if OptRegex then
|
|
begin
|
|
if not ANext then
|
|
FromPos:= AStartPos
|
|
else
|
|
FromPos:= FMatchPos+ASkipLen;
|
|
Result:= DoFindMatchRegex(FromPos, FMatchPos, FMatchLen);
|
|
if Result then DoOnFound;
|
|
Exit
|
|
end;
|
|
|
|
//usual code
|
|
if not ANext then
|
|
begin
|
|
FMatchPos:= AStartPos;
|
|
end
|
|
else
|
|
begin
|
|
if FMatchPos<=0 then
|
|
FMatchPos:= 1;
|
|
if not OptBack then
|
|
Inc(FMatchPos, ASkipLen)
|
|
else
|
|
Dec(FMatchPos, ASkipLen);
|
|
end;
|
|
|
|
FMatchPos:= DoFindMatchUsual(FMatchPos);
|
|
Result:= FMatchPos>0;
|
|
if Result then
|
|
begin
|
|
FMatchLen:= Length(StrFind);
|
|
DoOnFound;
|
|
end;
|
|
end;
|
|
|
|
procedure TATEditorFinder.DoOnFound;
|
|
var
|
|
P1, P2: TPoint;
|
|
begin
|
|
if Assigned(FOnFound) then
|
|
begin
|
|
P1:= FBuffer.StrToCaret(MatchPos-1);
|
|
P2:= FBuffer.StrToCaret(MatchPos-1+MatchLen);
|
|
FOnFound(Self, P1, P2);
|
|
end;
|
|
end;
|
|
|
|
function TATEditorFinder.GetRegexSkipIncrement: integer;
|
|
//this is to solve loop-forever if regex "$" replaced-all to eg "==="
|
|
//(need to skip one more char)
|
|
begin
|
|
Result:= 0;
|
|
if StrFind='$' then Result:= 1;
|
|
end;
|
|
|
|
end.
|