mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-07 12:12:40 +02:00
412 lines
14 KiB
ObjectPascal
412 lines
14 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
Options dialog and methods for finding and renaming identifier references.
|
|
}
|
|
unit FindRenameIdentifier;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, LCLProc, LResources, Forms, Controls, Graphics, Dialogs,
|
|
StdCtrls, Buttons, ExtCtrls,
|
|
AVL_Tree, CodeAtom, CodeCache,
|
|
CodeToolManager,
|
|
LazarusIDEStrConsts, IDEProcs, IDEWindowIntf, MiscOptions, DialogProcs,
|
|
InputHistory, SearchResultView;
|
|
|
|
type
|
|
TFindRenameIdentifierDialog = class(TForm)
|
|
CancelButton: TButton;
|
|
CurrentGroupBox: TGroupBox;
|
|
CurrentListBox: TListBox;
|
|
ExtraFilesEdit: TEdit;
|
|
ExtraFilesGroupBox: TGroupBox;
|
|
FindOrRenameButton: TButton;
|
|
NewEdit: TEdit;
|
|
NewGroupBox: TGroupBox;
|
|
RenameCheckBox: TCheckBox;
|
|
ScopeCommentsCheckBox: TCheckBox;
|
|
ScopeGroupBox: TGroupBox;
|
|
ScopeRadioGroup: TRadioGroup;
|
|
procedure FindOrRenameButtonClick(Sender: TObject);
|
|
procedure FindRenameIdentifierDialogClose(Sender: TObject;
|
|
var CloseAction: TCloseAction);
|
|
procedure FindRenameIdentifierDialogCreate(Sender: TObject);
|
|
procedure RenameCheckBoxChange(Sender: TObject);
|
|
private
|
|
FAllowRename: boolean;
|
|
FIdentifierFilename: string;
|
|
FIdentifierPosition: TPoint;
|
|
procedure SetAllowRename(const AValue: boolean);
|
|
procedure UpdateRename;
|
|
public
|
|
procedure LoadFromConfig;
|
|
procedure SaveToConfig;
|
|
procedure LoadFromOptions(Options: TFindRenameIdentifierOptions);
|
|
procedure SaveToOptions(Options: TFindRenameIdentifierOptions);
|
|
procedure SetIdentifier(const NewIdentifierFilename: string;
|
|
const NewIdentifierPosition: TPoint);
|
|
property IdentifierFilename: string read FIdentifierFilename;
|
|
property IdentifierPosition: TPoint read FIdentifierPosition;
|
|
property AllowRename: boolean read FAllowRename write SetAllowRename;
|
|
end;
|
|
|
|
|
|
function ShowFindRenameIdentifierDialog(const Filename: string;
|
|
const Position: TPoint; AllowRename, SetRenameActive: boolean;
|
|
Options: TFindRenameIdentifierOptions): TModalResult;
|
|
function GatherIdentifierReferences(Files: TStringList;
|
|
DeclarationCode: TCodeBuffer; const DeclarationCaretXY: TPoint;
|
|
SearchInComments: boolean;
|
|
var TreeOfPCodeXYPosition: TAVLTree): TModalResult;
|
|
function ShowIdentifierReferences(
|
|
DeclarationCode: TCodeBuffer; const DeclarationCaretXY: TPoint;
|
|
TreeOfPCodeXYPosition: TAVLTree): TModalResult;
|
|
procedure AddReferencesToResultView(DeclarationCode: TCodeBuffer;
|
|
const DeclarationCaretXY: TPoint; TargetCode: TCodeBuffer;
|
|
TreeOfPCodeXYPosition: TAVLTree; ClearItems: boolean; SearchPageIndex: integer);
|
|
|
|
|
|
implementation
|
|
|
|
|
|
function ShowFindRenameIdentifierDialog(const Filename: string;
|
|
const Position: TPoint; AllowRename, SetRenameActive: boolean;
|
|
Options: TFindRenameIdentifierOptions): TModalResult;
|
|
var
|
|
FindRenameIdentifierDialog: TFindRenameIdentifierDialog;
|
|
begin
|
|
FindRenameIdentifierDialog:=TFindRenameIdentifierDialog.Create(nil);
|
|
try
|
|
FindRenameIdentifierDialog.LoadFromConfig;
|
|
FindRenameIdentifierDialog.SetIdentifier(Filename,Position);
|
|
FindRenameIdentifierDialog.AllowRename:=AllowRename;
|
|
if SetRenameActive and AllowRename then
|
|
FindRenameIdentifierDialog.RenameCheckBox.Checked:=true;
|
|
Result:=FindRenameIdentifierDialog.ShowModal;
|
|
if Result=mrOk then
|
|
if Options<>nil then
|
|
FindRenameIdentifierDialog.SaveToOptions(Options);
|
|
finally
|
|
FindRenameIdentifierDialog.Free;
|
|
end;
|
|
end;
|
|
|
|
function GatherIdentifierReferences(Files: TStringList;
|
|
DeclarationCode: TCodeBuffer; const DeclarationCaretXY: TPoint;
|
|
SearchInComments: boolean;
|
|
var TreeOfPCodeXYPosition: TAVLTree): TModalResult;
|
|
var
|
|
i: Integer;
|
|
LoadResult: TModalResult;
|
|
Code: TCodeBuffer;
|
|
ListOfPCodeXYPosition: TFPList;
|
|
begin
|
|
Result:=mrCancel;
|
|
ListOfPCodeXYPosition:=nil;
|
|
TreeOfPCodeXYPosition:=nil;
|
|
try
|
|
// sort files
|
|
Files.Sort;
|
|
// remove doubles
|
|
i:=0;
|
|
while i<=Files.Count-2 do begin
|
|
while (i<=Files.Count-2) and (CompareFilenames(Files[i],Files[i+1])=0) do
|
|
begin
|
|
Files.Delete(i+1);
|
|
end;
|
|
inc(i);
|
|
end;
|
|
|
|
// search in every file
|
|
for i:=0 to Files.Count-1 do begin
|
|
LoadResult:=
|
|
LoadCodeBuffer(Code,Files[i],[lbfCheckIfText,lbfUpdateFromDisk]);
|
|
if LoadResult=mrAbort then begin
|
|
debugln('GatherIdentifierReferences unable to load "',Code.Filename,'"');
|
|
exit;
|
|
end;
|
|
if LoadResult<>mrOk then continue;
|
|
|
|
// search references
|
|
CodeToolBoss.FreeListOfPCodeXYPosition(ListOfPCodeXYPosition);
|
|
if not CodeToolBoss.FindReferences(
|
|
DeclarationCode,DeclarationCaretXY.X,DeclarationCaretXY.Y,
|
|
Code, not SearchInComments, ListOfPCodeXYPosition) then
|
|
begin
|
|
debugln('GatherIdentifierReferences unable to FindReferences in "',Code.Filename,'"');
|
|
Result:=mrAbort;
|
|
exit;
|
|
end;
|
|
//debugln('GatherIdentifierReferences FindReferences in "',Code.Filename,'" ',dbgs(ListOfPCodeXYPosition<>nil));
|
|
|
|
// add to tree
|
|
if ListOfPCodeXYPosition<>nil then begin
|
|
if TreeOfPCodeXYPosition=nil then
|
|
TreeOfPCodeXYPosition:=CodeToolBoss.CreateTreeOfPCodeXYPosition;
|
|
CodeToolBoss.AddListToTreeOfPCodeXYPosition(ListOfPCodeXYPosition,
|
|
TreeOfPCodeXYPosition,true,false);
|
|
end;
|
|
end;
|
|
|
|
Result:=mrOk;
|
|
finally
|
|
CodeToolBoss.FreeListOfPCodeXYPosition(ListOfPCodeXYPosition);
|
|
if Result<>mrOk then
|
|
CodeToolBoss.FreeTreeOfPCodeXYPosition(TreeOfPCodeXYPosition);
|
|
end;
|
|
end;
|
|
|
|
function ShowIdentifierReferences(
|
|
DeclarationCode: TCodeBuffer; const DeclarationCaretXY: TPoint;
|
|
TreeOfPCodeXYPosition: TAVLTree): TModalResult;
|
|
var
|
|
Identifier: string;
|
|
OldSearchPageIndex: LongInt;
|
|
SearchPageIndex: LongInt;
|
|
begin
|
|
Result:=mrCancel;
|
|
SearchPageIndex:=-1;
|
|
try
|
|
// show result
|
|
CodeToolBoss.GetIdentifierAt(DeclarationCode,
|
|
DeclarationCaretXY.X,DeclarationCaretXY.Y,Identifier);
|
|
// create a search result page
|
|
SearchPageIndex:=SearchResultsView.AddSearch(
|
|
'References of '+Identifier,
|
|
Identifier,
|
|
'',
|
|
ExtractFilePath(DeclarationCode.Filename),
|
|
'*.pas;*.pp;*.p;*.inc',
|
|
[fifWholeWord,fifSearchDirectories]);
|
|
if SearchPageIndex<0 then exit;
|
|
|
|
// list results
|
|
SearchResultsView.BeginUpdate(SearchPageIndex);
|
|
AddReferencesToResultView(DeclarationCode,DeclarationCaretXY,
|
|
DeclarationCode,TreeOfPCodeXYPosition,false,SearchPageIndex);
|
|
OldSearchPageIndex:=SearchPageIndex;
|
|
SearchPageIndex:=-1;
|
|
SearchResultsView.EndUpdate(OldSearchPageIndex);
|
|
SearchResultsView.ShowOnTop;
|
|
finally
|
|
if SearchPageIndex>=0 then
|
|
SearchResultsView.EndUpdate(SearchPageIndex);
|
|
end;
|
|
end;
|
|
|
|
procedure AddReferencesToResultView(DeclarationCode: TCodeBuffer;
|
|
const DeclarationCaretXY: TPoint; TargetCode: TCodeBuffer;
|
|
TreeOfPCodeXYPosition: TAVLTree; ClearItems: boolean;
|
|
SearchPageIndex: integer);
|
|
var
|
|
Identifier: string;
|
|
CodePos: PCodeXYPosition;
|
|
CurLine: String;
|
|
TrimmedLine: String;
|
|
TrimCnt: Integer;
|
|
ANode: TAVLTreeNode;
|
|
begin
|
|
CodeToolBoss.GetIdentifierAt(DeclarationCode,
|
|
DeclarationCaretXY.X,DeclarationCaretXY.Y,Identifier);
|
|
|
|
SearchResultsView.BeginUpdate(SearchPageIndex);
|
|
if ClearItems then
|
|
SearchResultsView.Items[SearchPageIndex].Clear;
|
|
if (TreeOfPCodeXYPosition<>nil) then begin
|
|
ANode:=TreeOfPCodeXYPosition.FindHighest;
|
|
while ANode<>nil do begin
|
|
CodePos:=PCodeXYPosition(ANode.Data);
|
|
CurLine:=TrimRight(CodePos^.Code.GetLine(CodePos^.Y-1));
|
|
TrimmedLine:=Trim(CurLine);
|
|
TrimCnt:=length(CurLine)-length(TrimmedLine);
|
|
//debugln('ShowReferences x=',dbgs(CodePos^.x),' y=',dbgs(CodePos^.y),' ',CurLine);
|
|
SearchResultsView.AddMatch(SearchPageIndex,
|
|
CodePos^.Code.Filename,
|
|
Point(CodePos^.X,CodePos^.Y),
|
|
Point(CodePos^.X+length(Identifier),CodePos^.Y),
|
|
TrimmedLine,
|
|
CodePos^.X-TrimCnt, length(Identifier));
|
|
ANode:=TreeOfPCodeXYPosition.FindPrecessor(ANode);
|
|
end;
|
|
end;
|
|
SearchResultsView.EndUpdate(SearchPageIndex);
|
|
end;
|
|
|
|
{ TFindRenameIdentifierDialog }
|
|
|
|
procedure TFindRenameIdentifierDialog.FindRenameIdentifierDialogCreate(
|
|
Sender: TObject);
|
|
begin
|
|
IDEDialogLayoutList.ApplyLayout(Self,450,460);
|
|
|
|
Caption:=lisFRIFindOrRenameIdentifier;
|
|
CancelButton.Caption:=dlgCancel;
|
|
CurrentGroupBox.Caption:=lisCodeToolsOptsIdentifier;
|
|
ExtraFilesGroupBox.Caption:=lisFRIAdditionalFilesToSearchEGPathPasPath2Pp;
|
|
FindOrRenameButton.Caption:=lisFRIFindReferences;
|
|
NewGroupBox.Caption:=lisFRIRenameTo;
|
|
RenameCheckBox.Caption:=lisFRIRename;
|
|
ScopeCommentsCheckBox.Caption:=lisFRISearchInCommentsToo;
|
|
ScopeGroupBox.Caption:=lisFRISearchWhere;
|
|
ScopeRadioGroup.Caption:=dlgScope;
|
|
ScopeRadioGroup.Items[0]:=lisFRIinCurrentUnit;
|
|
ScopeRadioGroup.Items[1]:=lisFRIinMainProject;
|
|
ScopeRadioGroup.Items[2]:=lisFRIinProjectPackageOwningCurrentUnit;
|
|
ScopeRadioGroup.Items[3]:=lisFRIinAllOpenPackagesAndProjects;
|
|
|
|
LoadFromConfig;
|
|
end;
|
|
|
|
procedure TFindRenameIdentifierDialog.RenameCheckBoxChange(Sender: TObject);
|
|
begin
|
|
UpdateRename;
|
|
end;
|
|
|
|
procedure TFindRenameIdentifierDialog.UpdateRename;
|
|
begin
|
|
RenameCheckBox.Enabled:=AllowRename;
|
|
NewEdit.Enabled:=RenameCheckBox.Checked and RenameCheckBox.Enabled;
|
|
if NewEdit.Enabled then
|
|
FindOrRenameButton.Caption:=lisFRIRenameAllReferences
|
|
else
|
|
FindOrRenameButton.Caption:=lisFRIFindReferences;
|
|
end;
|
|
|
|
procedure TFindRenameIdentifierDialog.SetAllowRename(const AValue: boolean);
|
|
begin
|
|
if FAllowRename=AValue then exit;
|
|
FAllowRename:=AValue;
|
|
UpdateRename;
|
|
end;
|
|
|
|
procedure TFindRenameIdentifierDialog.FindOrRenameButtonClick(Sender: TObject);
|
|
var
|
|
NewIdentifier: String;
|
|
begin
|
|
NewIdentifier:=NewEdit.Text;
|
|
if (NewIdentifier='') or (not IsValidIdent(NewIdentifier)) then begin
|
|
MessageDlg(lisFRIInvalidIdentifier,
|
|
Format(lisSVUOisNotAValidIdentifier, ['"', NewIdentifier, '"']), mtError,
|
|
[mbCancel], 0);
|
|
exit;
|
|
end;
|
|
ModalResult:=mrOk;
|
|
end;
|
|
|
|
procedure TFindRenameIdentifierDialog.FindRenameIdentifierDialogClose(
|
|
Sender: TObject; var CloseAction: TCloseAction);
|
|
begin
|
|
SaveToConfig;
|
|
IDEDialogLayoutList.SaveLayout(Self);
|
|
end;
|
|
|
|
procedure TFindRenameIdentifierDialog.LoadFromConfig;
|
|
begin
|
|
LoadFromOptions(MiscellaneousOptions.FindRenameIdentifierOptions);
|
|
end;
|
|
|
|
procedure TFindRenameIdentifierDialog.SaveToConfig;
|
|
begin
|
|
SaveToOptions(MiscellaneousOptions.FindRenameIdentifierOptions);
|
|
end;
|
|
|
|
procedure TFindRenameIdentifierDialog.LoadFromOptions(
|
|
Options: TFindRenameIdentifierOptions);
|
|
begin
|
|
RenameCheckBox.Checked:=Options.Rename;
|
|
ExtraFilesEdit.Text:=StringListToText(Options.ExtraFiles,';',true);
|
|
NewEdit.Text:=Options.RenameTo;
|
|
ScopeCommentsCheckBox.Checked:=Options.SearchInComments;
|
|
case Options.Scope of
|
|
frCurrentUnit: ScopeRadioGroup.ItemIndex:=0;
|
|
frProject: ScopeRadioGroup.ItemIndex:=1;
|
|
frOwnerProjectPackage: ScopeRadioGroup.ItemIndex:=2;
|
|
else
|
|
ScopeRadioGroup.ItemIndex:=3;
|
|
end;
|
|
UpdateRename;
|
|
end;
|
|
|
|
procedure TFindRenameIdentifierDialog.SaveToOptions(
|
|
Options: TFindRenameIdentifierOptions);
|
|
begin
|
|
Options.Rename:=RenameCheckBox.Checked;
|
|
SplitString(ExtraFilesEdit.Text,';',Options.ExtraFiles,true);
|
|
Options.RenameTo:=NewEdit.Text;
|
|
Options.SearchInComments:=ScopeCommentsCheckBox.Checked;
|
|
case ScopeRadioGroup.ItemIndex of
|
|
0: Options.Scope:=frCurrentUnit;
|
|
1: Options.Scope:=frProject;
|
|
2: Options.Scope:=frOwnerProjectPackage;
|
|
else Options.Scope:=frAllOpenProjectsAndPackages;
|
|
end;
|
|
end;
|
|
|
|
procedure TFindRenameIdentifierDialog.SetIdentifier(
|
|
const NewIdentifierFilename: string; const NewIdentifierPosition: TPoint);
|
|
var
|
|
s: String;
|
|
ACodeBuffer: TCodeBuffer;
|
|
ListOfCodeBuffer: TFPList;
|
|
i: Integer;
|
|
CurCode: TCodeBuffer;
|
|
NewIdentifier: String;
|
|
begin
|
|
FIdentifierFilename:=NewIdentifierFilename;
|
|
FIdentifierPosition:=NewIdentifierPosition;
|
|
CurrentListBox.Items.Clear;
|
|
s:=IdentifierFilename
|
|
+'('+IntToStr(IdentifierPosition.Y)+','+IntToStr(IdentifierPosition.X)+')';
|
|
CurrentListBox.Items.Add(s);
|
|
LoadCodeBuffer(ACodeBuffer,IdentifierFileName,[lbfCheckIfText]);
|
|
if ACodeBuffer<>nil then begin
|
|
CodeToolBoss.GetIncludeCodeChain(ACodeBuffer,true,ListOfCodeBuffer);
|
|
if ListOfCodeBuffer<>nil then begin
|
|
for i:=0 to ListOfCodeBuffer.Count-1 do begin
|
|
CurCode:=TCodeBuffer(ListOfCodeBuffer[i]);
|
|
if CurCode=ACodeBuffer then break;
|
|
s:=CurCode.Filename;
|
|
CurrentListBox.Items.Insert(0,s);
|
|
end;
|
|
ListOfCodeBuffer.Free;
|
|
end;
|
|
if CodeToolBoss.GetIdentifierAt(ACodeBuffer,
|
|
NewIdentifierPosition.X,NewIdentifierPosition.Y,NewIdentifier) then
|
|
begin
|
|
CurrentGroupBox.Caption:=Format(lisFRIIdentifier, [NewIdentifier]);
|
|
NewEdit.Text:=NewIdentifier;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
initialization
|
|
{$I findrenameidentifier.lrs}
|
|
|
|
end.
|
|
|
|
|