IDE: find/rename identifier: search in used but not listed units too

git-svn-id: trunk@57727 -
This commit is contained in:
mattias 2018-04-26 17:37:14 +00:00
parent 9f7177574f
commit 7dd4ef464c
5 changed files with 417 additions and 240 deletions

View File

@ -100,6 +100,7 @@ type
TUsesGraph = class
private
FFiles: TAVLTree; // tree of TUGUnit sorted for Filename
FIgnoreFiles: TAVLTree; // tree of TUGUnit sorted for Filename
FQueuedFiles: TAVLTree; // tree of TUGUnit sorted for Filename
FTargetAll: boolean;
FTargetFiles: TAVLTree; // tree of TUGUnit sorted for Filename
@ -122,6 +123,7 @@ type
procedure AddStartUnit(ExpFilename: string);
procedure AddTargetUnit(ExpFilename: string);
procedure AddIgnoreUnit(ExpFilename: string);
procedure AddSystemUnitAsTarget;
function Parse(IgnoreErrors: boolean; out Completed: boolean;
StopAfterMs: integer = -1): boolean;
@ -134,6 +136,7 @@ type
function InsertMissingLinks(UGUnitList: TFPList): boolean;
property FilesTree: TAVLTree read FFiles; // tree of TUGUnit sorted for Filename (all parsed)
property IgnoreFilesTree: TAVLTree read FIgnoreFiles; // tree of TUGUnit sorted for Filename
property QueuedFilesTree: TAVLTree read FQueuedFiles; // tree of TUGUnit sorted for Filename
property TargetFilesTree: TAVLTree read FTargetFiles; // tree of TUGUnit sorted for Filename
property TargetAll: boolean read FTargetAll write FTargetAll;
@ -245,6 +248,7 @@ begin
FUnitClass:=TUGUnit;
FUsesClass:=TUGUses;
FFiles:=TAVLTree.Create(@CompareUGUnitFilenames);
FIgnoreFiles:=TAVLTree.Create(@CompareUGUnitFilenames);
FQueuedFiles:=TAVLTree.Create(@CompareUGUnitFilenames);
FTargetFiles:=TAVLTree.Create(@CompareUGUnitFilenames);
end;
@ -252,6 +256,7 @@ end;
destructor TUsesGraph.Destroy;
begin
Clear;
FreeAndNil(FIgnoreFiles);
FreeAndNil(FQueuedFiles);
FreeAndNil(FTargetFiles);
FreeAndNil(FFiles);
@ -333,16 +338,29 @@ begin
end;
procedure TUsesGraph.AddTargetUnit(ExpFilename: string);
var
NewUnit: TUGUnit;
begin
if ExpFilename='' then exit;
if FQueuedFiles.FindKey(PChar(ExpFilename),@CompareFilenameAndUGUnit)<>nil then
exit; // already a start file
// add to FFiles and FTargetFiles
//debugln(['TUsesGraph.AddTargetUnit ',ExpFilename]);
FTargetFiles.Add(GetUnit(ExpFilename,true));
NewUnit:=GetUnit(ExpFilename,true);
if FTargetFiles.Find(NewUnit)=nil then
FTargetFiles.Add(NewUnit);
FTargetDirsValid:=false;
end;
procedure TUsesGraph.AddIgnoreUnit(ExpFilename: string);
var
NewUnit: TUGUnit;
begin
NewUnit:=GetUnit(ExpFilename,true);
if FIgnoreFiles.Find(NewUnit)=nil then
FIgnoreFiles.Add(NewUnit);
end;
procedure TUsesGraph.AddSystemUnitAsTarget;
begin
AddTargetUnit(DirectoryCachePool.FindUnitInUnitSet('','system'));
@ -389,6 +407,7 @@ function TUsesGraph.Parse(IgnoreErrors: boolean; out Completed: boolean;
ImplementationUsesSection: TStrings;
begin
Result:=false;
//debugln(['ParseUnit ',CurUnit.Filename,' ',Pos('tcfiler',CurUnit.Filename)]);
Include(CurUnit.Flags,ugufLoadError);
// load file
Abort:=false;
@ -461,6 +480,7 @@ begin
CurUnit:=TUGUnit(AVLNode.Data);
FQueuedFiles.Delete(AVLNode);
Include(CurUnit.Flags,ugufReached);
if FIgnoreFiles.Find(CurUnit)<>nil then continue;
//debugln(['TUsesGraph.Parse Unit=',CurUnit.Filename,' UnitCanFindTarget=',UnitCanFindTarget(CurUnit.Filename)]);
if UnitCanFindTarget(CurUnit.Filename) then begin
ParseUnit(CurUnit);
@ -527,7 +547,7 @@ begin
end;
function TUsesGraph.UnitCanFindTarget(ExpFilename: string): boolean;
// returns true if units search path allows finding a target unit
// returns true if ExpFilename can find one of the targets via the search paths
var
BaseDir: String;
SrcPath: String;
@ -561,8 +581,6 @@ var
AVLNode: TAVLTreeNode;
CurUnit: TUGUnit;
Dir: String;
p: Integer;
TargetDir: String;
begin
if FTargetFiles.Count=0 then exit(TargetAll);
@ -585,7 +603,7 @@ begin
// in virtual directory
if (FTargetDirs='') or (FTargetDirs[1]<>';') then
FTargetDirs:=';'+FTargetDirs;
end else if not FileIsInPath(Dir,FTargetDirs) then begin
end else if FindPathInSearchPath(Dir,FTargetDirs)<1 then begin
// normal source directory
if FTargetDirs='' then
FTargetDirs:=Dir
@ -598,15 +616,9 @@ begin
Result:=true;
if TargetAll then exit;
if (ExpDir='') and (FTargetDirs[1]=';') then exit;
p:=1;
repeat
TargetDir:=GetNextDelimitedItem(FTargetDirs,';',p);
if TargetDir<>'' then begin
if CompareFilenames(TargetDir,ExpDir)=0 then exit;
end;
until p>length(FTargetDirs);
Result:=false;
if (ExpDir='') and (FTargetDirs[1]=';') then
exit; // virtual directory
Result:=FindPathInSearchPath(ExpDir,FTargetDirs)>0;
end;
function TUsesGraph.FindShortestPath(StartUnit, EndUnit: TUGUnit): TFPList;

View File

@ -250,6 +250,13 @@ type
);
TPkgIntfRequiredFlags = set of TPkgIntfRequiredFlag;
TPkgIntfGatherUnitType = (
piguListed, // unit is in list of given Owner, i.e. in lpi, lpk file, this may contain platform specific units
piguUsed, // unit is used directly or indirectly by the start module and no currently open package/project is associated with it
piguAllUsed // as pigyUsed, except even units associated with another package/project are returned
);
TPkgIntfGatherUnitTypes = set of TPkgIntfGatherUnitType;
{ TPackageEditingInterface }
TPackageEditingInterface = class(TComponent)
@ -273,6 +280,7 @@ type
function GetOwnersOfUnit(const UnitFilename: string): TFPList; virtual; abstract;
procedure ExtendOwnerListWithUsedByOwners(OwnerList: TFPList); virtual; abstract;
function GetSourceFilesOfOwners(OwnerList: TFPList): TStrings; virtual; abstract;
function GetUnitsOfOwners(OwnerList: TFPList; Flags: TPkgIntfGatherUnitTypes): TStrings; virtual; abstract;
function GetPossibleOwnersOfUnit(const UnitFilename: string;
Flags: TPkgIntfOwnerSearchFlags): TFPList; virtual; abstract;
function GetPackageOfSourceEditor(out APackage: TIDEPackage; ASrcEdit: TObject): TLazPackageFile; virtual; abstract;

View File

@ -36,12 +36,13 @@ uses
LCLProc, Forms, Controls, Dialogs, StdCtrls, ExtCtrls, ComCtrls, ButtonPanel,
LclIntf,
// CodeTools
FileProcs, CTUnitGraph, CodeTree, CodeCache, CodeToolManager,
FileProcs, CTUnitGraph, CodeTree, CodeCache, CodeToolManager, BasicCodeTools,
// LazUtils
LazFileUtils, LazFileCache, laz2_DOM, AvgLvlTree,
// IDE
LazarusIDEStrConsts, IDEProcs, IDEWindowIntf, MiscOptions, DialogProcs,
LazIDEIntf, IDEDialogs, InputHistory, SearchResultView, CodeHelp;
LazIDEIntf, IDEDialogs, SrcEditorIntf, PackageIntf, InputHistory,
SearchResultView, CodeHelp, TransferMacros;
type
@ -95,6 +96,10 @@ function ShowFindRenameIdentifierDialog(const Filename: string;
AllowRename: boolean; // allow user to disable/enable rename
SetRenameActive: boolean; // check rename
Options: TFindRenameIdentifierOptions): TModalResult;
function DoFindRenameIdentifier(
AllowRename: boolean; // allow user to disable/enable rename
SetRenameActive: boolean; // check rename
Options: TFindRenameIdentifierOptions): TModalResult;
function GatherIdentifierReferences(Files: TStringList;
DeclarationCode: TCodeBuffer; const DeclarationCaretXY: TPoint;
SearchInComments: boolean;
@ -166,6 +171,256 @@ begin
end;
end;
function DoFindRenameIdentifier(AllowRename: boolean; SetRenameActive: boolean;
Options: TFindRenameIdentifierOptions): TModalResult;
// TODO: replace Files: TStringsList with a AVL tree
function AddExtraFiles(Files: TStrings): TModalResult;
var
i: Integer;
CurFileMask: string;
FileInfo: TSearchRec;
CurDirectory: String;
CurFilename: String;
OnlyPascalSources: Boolean;
begin
Result:=mrCancel;
if (Options.ExtraFiles<>nil) then begin
for i:=0 to Options.ExtraFiles.Count-1 do begin
CurFileMask:=Options.ExtraFiles[i];
if not GlobalMacroList.SubstituteStr(CurFileMask) then exit;
CurFileMask:=ChompPathDelim(CurFileMask);
if not FilenameIsAbsolute(CurFileMask) then begin
if LazarusIDE.ActiveProject.IsVirtual then continue;
CurFileMask:=AppendPathDelim(LazarusIDE.ActiveProject.Directory+CurFileMask);
end;
CurFileMask:=TrimFilename(CurFileMask);
OnlyPascalSources:=false;
if DirPathExistsCached(CurFileMask) then begin
// a whole directory
OnlyPascalSources:=true;
CurFileMask:=AppendPathDelim(CurFileMask)+AllFilesMask;
end else if FileExistsCached(CurFileMask) then begin
// single file
Files.Add(CurFileMask);
continue;
end else begin
// a mask
end;
if FindFirstUTF8(CurFileMask,faAnyFile,FileInfo)=0
then begin
CurDirectory:=AppendPathDelim(ExtractFilePath(CurFileMask));
repeat
// check if special file
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='')
then
continue;
if OnlyPascalSources and not FilenameIsPascalSource(FileInfo.Name)
then
continue;
CurFilename:=CurDirectory+FileInfo.Name;
//debugln(['AddExtraFiles ',CurFilename]);
if FileIsText(CurFilename) then
Files.Add(CurFilename);
until FindNextUTF8(FileInfo)<>0;
end;
FindCloseUTF8(FileInfo);
end;
end;
Result:=mrOk;
end;
var
StartSrcEdit: TSourceEditorInterface;
DeclCode, StartSrcCode: TCodeBuffer;
DeclX, DeclY, DeclTopLine, StartTopLine, i: integer;
LogCaretXY, DeclarationCaretXY: TPoint;
OwnerList: TFPList;
ExtraFiles: TStrings;
Files: TStringList;
Identifier: string;
PascalReferences: TAVLTree;
ListOfLazFPDocNode: TFPList;
CurUnitname: String;
OldChange, Completed: Boolean;
Graph: TUsesGraph;
Node: TAVLTreeNode;
UGUnit: TUGUnit;
begin
Result:=mrCancel;
if not LazarusIDE.BeginCodeTools then exit(mrCancel);
StartSrcEdit:=SourceEditorManagerIntf.ActiveEditor;
StartSrcCode:=TCodeBuffer(StartSrcEdit.CodeToolsBuffer);
StartTopLine:=StartSrcEdit.TopLine;
// find the main declaration
LogCaretXY:=StartSrcEdit.CursorTextXY;
if not CodeToolBoss.FindMainDeclaration(StartSrcCode,
LogCaretXY.X,LogCaretXY.Y,
DeclCode,DeclX,DeclY,DeclTopLine) then
begin
LazarusIDE.DoJumpToCodeToolBossError;
exit(mrCancel);
end;
DeclarationCaretXY:=Point(DeclX,DeclY);
Result:=LazarusIDE.DoOpenFileAndJumpToPos(DeclCode.Filename, DeclarationCaretXY,
DeclTopLine,-1,-1,[ofOnlyIfExists,ofRegularFile,ofDoNotLoadResource]);
if Result<>mrOk then
exit;
CodeToolBoss.GetIdentifierAt(DeclCode,DeclarationCaretXY.X,DeclarationCaretXY.Y,Identifier);
CurUnitname:=ExtractFileNameOnly(DeclCode.Filename);
//debugln('TMainIDE.DoFindRenameIdentifier A DeclarationCaretXY=x=',dbgs(DeclarationCaretXY.X),' y=',dbgs(DeclarationCaretXY.Y));
Files:=nil;
OwnerList:=nil;
PascalReferences:=nil;
ListOfLazFPDocNode:=nil;
try
// let user choose the search scope
Result:=ShowFindRenameIdentifierDialog(DeclCode.Filename,DeclarationCaretXY,
AllowRename,SetRenameActive,nil);
if Result<>mrOk then begin
debugln('Error: (lazarus) DoFindRenameIdentifier failed: user cancelled dialog');
exit;
end;
// create the file list
Files:=TStringList.Create;
Files.Add(DeclCode.Filename);
if CompareFilenames(DeclCode.Filename,StartSrcCode.Filename)<>0 then
Files.Add(DeclCode.Filename);
Options:=MiscellaneousOptions.FindRenameIdentifierOptions;
// add packages, projects
case Options.Scope of
frProject:
begin
OwnerList:=TFPList.Create;
OwnerList.Add(LazarusIDE.ActiveProject);
end;
frOwnerProjectPackage,frAllOpenProjectsAndPackages:
begin
OwnerList:=PackageEditingInterface.GetOwnersOfUnit(StartSrcCode.Filename);
if (OwnerList<>nil)
and (Options.Scope=frAllOpenProjectsAndPackages) then begin
PackageEditingInterface.ExtendOwnerListWithUsedByOwners(OwnerList);
ReverseList(OwnerList);
end;
end;
end;
// get source files of packages and projects
if OwnerList<>nil then begin
// start in all listed files of the package(s)
ExtraFiles:=PackageEditingInterface.GetSourceFilesOfOwners(OwnerList);
if ExtraFiles<>nil then
begin
// parse all used units
Graph:=CodeToolBoss.CreateUsesGraph;
try
for i:=0 to ExtraFiles.Count-1 do
Graph.AddStartUnit(ExtraFiles[i]);
Graph.AddTargetUnit(DeclCode.Filename);
Graph.Parse(true,Completed);
Node:=Graph.FilesTree.FindLowest;
while Node<>nil do begin
UGUnit:=TUGUnit(Node.Data);
Files.Add(UGUnit.Filename);
Node:=Node.Successor;
end;
finally
ExtraFiles.Free;
Graph.Free;
end;
end;
end;
//debugln(['DoFindRenameIdentifier ',Files.Text]);
// add user defined extra files
Result:=AddExtraFiles(Files);
if Result<>mrOk then begin
debugln('Error: (lazarus) DoFindRenameIdentifier unable to add user defined extra files');
exit;
end;
// search pascal source references
Result:=GatherIdentifierReferences(Files,DeclCode,
DeclarationCaretXY,Options.SearchInComments,PascalReferences);
if CodeToolBoss.ErrorMessage<>'' then
LazarusIDE.DoJumpToCodeToolBossError;
if Result<>mrOk then begin
debugln('Error: (lazarus) DoFindRenameIdentifier GatherIdentifierReferences failed');
exit;
end;
{$IFDEF EnableFPDocRename}
// search fpdoc references
Result:=GatherFPDocReferencesForPascalFiles(Files,DeclarationUnitInfo.Source,
DeclarationCaretXY,ListOfLazFPDocNode);
if Result<>mrOk then begin
debugln('Error: (lazarus) DoFindRenameIdentifier GatherFPDocReferences failed');
exit;
end;
{$ENDIF}
// ToDo: search lfm source references
// ToDo: search i18n references
// ToDo: designer references
// rename identifier
if Options.Rename then begin
if CompareIdentifiers(PChar(Identifier),PChar(CurUnitName))=0 then
begin
IDEMessageDialog(srkmecRenameIdentifier,
lisTheIdentifierIsAUnitPleaseUseTheFileSaveAsFunction,
mtInformation,[mbCancel],'');
exit(mrCancel);
end;
OldChange:=LazarusIDE.OpenEditorsOnCodeToolChange;
LazarusIDE.OpenEditorsOnCodeToolChange:=true;
try
if not CodeToolBoss.RenameIdentifier(PascalReferences,
Identifier,Options.RenameTo, DeclCode, @DeclarationCaretXY)
then begin
LazarusIDE.DoJumpToCodeToolBossError;
debugln('Error: (lazarus) DoFindRenameIdentifier unable to commit');
Result:=mrCancel;
exit;
end;
finally
LazarusIDE.OpenEditorsOnCodeToolChange:=OldChange;
end;
if Options.RenameShowResult then
Result:=ShowIdentifierReferences(DeclCode,
DeclarationCaretXY,PascalReferences);
end;
// show result
Result:=mrOk;
if (not Options.Rename) or (not SetRenameActive) then begin
Result:=ShowIdentifierReferences(DeclCode,
DeclarationCaretXY,PascalReferences);
if Result<>mrOk then exit;
end;
finally
Files.Free;
OwnerList.Free;
CodeToolBoss.FreeTreeOfPCodeXYPosition(PascalReferences);
FreeListObjects(ListOfLazFPDocNode,true);
// jump back in source editor
Result:=LazarusIDE.DoOpenFileAndJumpToPos(StartSrcCode.Filename, LogCaretXY,
StartTopLine,-1,-1,[ofOnlyIfExists,ofRegularFile,ofDoNotLoadResource]);
end;
end;
function GatherIdentifierReferences(Files: TStringList;
DeclarationCode: TCodeBuffer; const DeclarationCaretXY: TPoint;
SearchInComments: boolean;

View File

@ -9967,229 +9967,8 @@ begin
end;
function TMainIDE.DoFindRenameIdentifier(Rename: boolean): TModalResult;
var
Options: TFindRenameIdentifierOptions;
// TODO: replace Files: TStringsList with a AVL tree
function AddExtraFiles(Files: TStrings): TModalResult;
var
i: Integer;
CurFileMask: string;
FileInfo: TSearchRec;
CurDirectory: String;
CurFilename: String;
OnlyPascalSources: Boolean;
begin
Result:=mrCancel;
if (Options.ExtraFiles<>nil) then begin
for i:=0 to Options.ExtraFiles.Count-1 do begin
CurFileMask:=Options.ExtraFiles[i];
if not GlobalMacroList.SubstituteStr(CurFileMask) then exit;
CurFileMask:=ChompPathDelim(CurFileMask);
if not FilenameIsAbsolute(CurFileMask) then begin
if Project1.IsVirtual then continue;
CurFileMask:=AppendPathDelim(Project1.Directory+CurFileMask);
end;
CurFileMask:=TrimFilename(CurFileMask);
OnlyPascalSources:=false;
if DirPathExistsCached(CurFileMask) then begin
// a whole directory
OnlyPascalSources:=true;
CurFileMask:=AppendPathDelim(CurFileMask)+GetAllFilesMask;
end else if FileExistsCached(CurFileMask) then begin
// single file
Files.Add(CurFileMask);
continue;
end else begin
// a mask
end;
if FindFirstUTF8(CurFileMask,faAnyFile,FileInfo)=0
then begin
CurDirectory:=AppendPathDelim(ExtractFilePath(CurFileMask));
repeat
// check if special file
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='')
then
continue;
if OnlyPascalSources and not FilenameIsPascalSource(FileInfo.Name)
then
continue;
CurFilename:=CurDirectory+FileInfo.Name;
//debugln(['AddExtraFiles ',CurFilename]);
if FileIsText(CurFilename) then
Files.Add(CurFilename);
until FindNextUTF8(FileInfo)<>0;
end;
FindCloseUTF8(FileInfo);
end;
end;
Result:=mrOk;
end;
var
TargetSrcEdit, DeclarationSrcEdit: TSourceEditor;
TargetUnitInfo, DeclarationUnitInfo: TUnitInfo;
NewSource: TCodeBuffer;
NewX, NewY, NewTopLine: integer;
LogCaretXY, DeclarationCaretXY: TPoint;
OwnerList: TFPList;
ExtraFiles: TStrings;
Files: TStringList;
Identifier: string;
PascalReferences: TAVLTree;
ListOfLazFPDocNode: TFPList;
CurUnitname: String;
OldChange: Boolean;
begin
Result:=mrCancel;
TargetSrcEdit:=nil;
if not BeginCodeTool(TargetSrcEdit,TargetUnitInfo,[]) then exit;
// find the main declaration
LogCaretXY:=TargetSrcEdit.EditorComponent.LogicalCaretXY;
if not CodeToolBoss.FindMainDeclaration(TargetUnitInfo.Source,
LogCaretXY.X,LogCaretXY.Y,
NewSource,NewX,NewY,NewTopLine) then
begin
DoJumpToCodeToolBossError;
exit;
end;
DoJumpToCodePosition(TargetSrcEdit, TargetUnitInfo,
NewSource, NewX, NewY, NewTopLine, NewTopLine, NewTopLine, [jfFocusEditor]);
CodeToolBoss.GetIdentifierAt(NewSource,NewX,NewY,Identifier);
CurUnitname:=ExtractFileNameOnly(NewSource.Filename);
GetCurrentUnit(DeclarationSrcEdit,DeclarationUnitInfo);
DeclarationCaretXY:=DeclarationSrcEdit.EditorComponent.LogicalCaretXY;
//debugln('TMainIDE.DoFindRenameIdentifier A DeclarationCaretXY=x=',dbgs(DeclarationCaretXY.X),' y=',dbgs(DeclarationCaretXY.Y));
Files:=nil;
OwnerList:=nil;
PascalReferences:=nil;
ListOfLazFPDocNode:=nil;
try
// let user choose the search scope
Result:=ShowFindRenameIdentifierDialog(DeclarationUnitInfo.Source.Filename,
DeclarationCaretXY,true,Rename,nil);
if Result<>mrOk then begin
debugln('Error: (lazarus) TMainIDE.DoFindRenameIdentifier failed: user cancelled dialog');
exit;
end;
// create the file list
Files:=TStringList.Create;
Files.Add(TargetUnitInfo.Filename);
if CompareFilenames(DeclarationUnitInfo.Filename,TargetUnitInfo.Filename)<>0 then
Files.Add(DeclarationUnitInfo.Filename);
Options:=MiscellaneousOptions.FindRenameIdentifierOptions;
// add packages, projects
case Options.Scope of
frProject:
begin
OwnerList:=TFPList.Create;
OwnerList.Add(Project1);
end;
frOwnerProjectPackage,frAllOpenProjectsAndPackages:
begin
OwnerList:=PkgBoss.GetOwnersOfUnit(TargetUnitInfo.Filename);
if (OwnerList<>nil)
and (Options.Scope=frAllOpenProjectsAndPackages) then begin
PkgBoss.ExtendOwnerListWithUsedByOwners(OwnerList);
ReverseList(OwnerList);
end;
end;
end;
// get source files of packages and projects
if OwnerList<>nil then begin
ExtraFiles:=PkgBoss.GetSourceFilesOfOwners(OwnerList);
try
if ExtraFiles<>nil then
Files.AddStrings(ExtraFiles);
finally
ExtraFiles.Free;
end;
end;
// add user defined extra files
Result:=AddExtraFiles(Files);
if Result<>mrOk then begin
debugln('Error: (lazarus) TMainIDE.DoFindRenameIdentifier unable to add user defined extra files');
exit;
end;
// search pascal source references
Result:=GatherIdentifierReferences(Files,DeclarationUnitInfo.Source,
DeclarationCaretXY,Options.SearchInComments,PascalReferences);
if CodeToolBoss.ErrorMessage<>'' then
DoJumpToCodeToolBossError;
if Result<>mrOk then begin
debugln('Error: (lazarus) TMainIDE.DoFindRenameIdentifier GatherIdentifierReferences failed');
exit;
end;
{$IFDEF EnableFPDocRename}
// search fpdoc references
Result:=GatherFPDocReferencesForPascalFiles(Files,DeclarationUnitInfo.Source,
DeclarationCaretXY,ListOfLazFPDocNode);
if Result<>mrOk then begin
debugln('Error: (lazarus) TMainIDE.DoFindRenameIdentifier GatherFPDocReferences failed');
exit;
end;
{$ENDIF}
// ToDo: search lfm source references
// ToDo: search i18n references
// ToDo: designer references
// rename identifier
if Options.Rename then begin
if CompareIdentifiers(PChar(Identifier),PChar(CurUnitName))=0 then
begin
IDEMessageDialog(srkmecRenameIdentifier,
lisTheIdentifierIsAUnitPleaseUseTheFileSaveAsFunction,
mtInformation,[mbCancel],'');
exit(mrCancel);
end;
OldChange:=OpenEditorsOnCodeToolChange;
OpenEditorsOnCodeToolChange:=true;
try
if not CodeToolBoss.RenameIdentifier(PascalReferences,
Identifier,Options.RenameTo, DeclarationUnitInfo.Source, @DeclarationCaretXY)
then begin
DoJumpToCodeToolBossError;
debugln('Error: (lazarus) TMainIDE.DoFindRenameIdentifier unable to commit');
Result:=mrCancel;
exit;
end;
finally
OpenEditorsOnCodeToolChange:=OldChange;
end;
if Options.RenameShowResult then
Result:=ShowIdentifierReferences(DeclarationUnitInfo.Source,
DeclarationCaretXY,PascalReferences);
end;
// show result
if (not Options.Rename) or (not Rename) then begin
Result:=ShowIdentifierReferences(DeclarationUnitInfo.Source,
DeclarationCaretXY,PascalReferences);
if Result<>mrOk then exit;
end;
finally
Files.Free;
OwnerList.Free;
CodeToolBoss.FreeTreeOfPCodeXYPosition(PascalReferences);
FreeListObjects(ListOfLazFPDocNode,true);
// jump back in source editor
DoJumpToCodePosition(TargetSrcEdit, TargetUnitInfo,
TargetUnitInfo.Source, LogCaretXY.X, LogCaretXY.Y, -1, -1, -1, [jfFocusEditor]);
end;
Result:=FindRenameIdentifier.DoFindRenameIdentifier(true,Rename,nil);
end;
function TMainIDE.DoFindUsedUnitReferences: boolean;

View File

@ -51,10 +51,10 @@ uses
LCLProc, Forms, Controls, Dialogs, Menus, ComCtrls, LResources,
// LazUtils
LazUTF8, Laz2_XMLCfg, lazutf8classes, LazFileUtils, LazFileCache, StringHashList,
Translations, AvgLvlTree,
Translations, AvgLvlTree, Laz_AVL_Tree,
// Codetools
CodeToolsConfig, CodeToolManager, CodeCache, BasicCodeTools,
FileProcs, CodeTree,
FileProcs, CodeTree, CTUnitGraph,
// IDE Interface
NewItemIntf, ProjPackIntf, ProjectIntf, PackageIntf, PackageDependencyIntf, PackageLinkIntf,
CompOptsIntf, MenuIntf, IDEWindowIntf, IDEExternToolIntf, MacroIntf, LazIDEIntf,
@ -230,6 +230,7 @@ type
function GetOwnersOfUnit(const UnitFilename: string): TFPList; override;
procedure ExtendOwnerListWithUsedByOwners(OwnerList: TFPList); override;
function GetSourceFilesOfOwners(OwnerList: TFPList): TStrings; override;
function GetUnitsOfOwners(OwnerList: TFPList; Flags: TPkgIntfGatherUnitTypes): TStrings; override;
function GetPossibleOwnersOfUnit(const UnitFilename: string;
Flags: TPkgIntfOwnerSearchFlags): TFPList; override;
function GetPackageOfCurrentSourceEditor(out APackage: TIDEPackage): TPkgFile;
@ -4702,6 +4703,128 @@ begin
end;
end;
function TPkgManager.GetUnitsOfOwners(OwnerList: TFPList;
Flags: TPkgIntfGatherUnitTypes): TStrings;
var
Units: TFilenameToPointerTree;
Graph: TUsesGraph;
procedure AddUnit(ExpFilename: string);
begin
if not FileExistsCached(ExpFilename) then exit;
if Units.Contains(ExpFilename) then exit;
Units[ExpFilename]:=nil;
end;
procedure AddStartModule(ExpFilename: string);
begin
AddUnit(ExpFilename);
Graph.AddStartUnit(ExpFilename);
end;
var
i, j: Integer;
CurOwner: TObject;
CurProject: TProject;
CurPackage: TLazPackage;
ProjFile: TLazProjectFile;
PkgFile: TPkgFile;
Completed: boolean;
Node: TAVLTreeNode;
UGUnit: TUGUnit;
begin
debugln(['TPkgManager.GetUnitsOfOwners piguListed=',piguListed in Flags,' piguUsed=',piguUsed in Flags,' piguAllUsed=',piguAllUsed in Flags]);
Result:=TStringListUTF8.Create;
if (OwnerList=nil) or (OwnerList.Count=0) then exit;
Units:=TFilenameToPointerTree.Create(false);
Graph:=TUsesGraph.Create;
try
for i:=0 to OwnerList.Count-1 do
begin
CurOwner:=TObject(OwnerList[i]);
if CurOwner is TProject then
begin
CurProject:=TProject(CurOwner);
if (pfMainUnitIsPascalSource in CurProject.Flags)
and (CurProject.MainUnitInfo<>nil) then
AddStartModule(CurProject.MainUnitInfo.GetFullFilename);
if piguListed in Flags then
begin
for j:=0 to CurProject.FileCount-1 do
begin
ProjFile:=CurProject.Files[j];
if not FilenameIsPascalUnit(ProjFile.Filename) then continue;
AddStartModule(ProjFile.GetFullFilename);
end;
end;
end else if CurOwner is TLazPackage then
begin
CurPackage:=TLazPackage(CurOwner);
if piguListed in Flags then
begin
for j:=0 to CurPackage.FileCount-1 do
begin
PkgFile:=CurPackage.Files[j];
if not (PkgFile.FileType in PkgFileRealUnitTypes) then continue;
AddStartModule(PkgFile.GetFullFilename);
end;
end;
end;
end;
if Units.Count=0 then
begin
debugln(['TPkgManager.GetUnitsOfOwners no start modules END']);
exit; // no start modules
end;
if [piguUsed,piguAllUsed]*Flags<>[] then
begin
// parse units recursively
Graph.AddSystemUnitAsTarget;
if piguAllUsed in Flags then
begin
// gather all used units
end else if piguUsed in Flags then
begin
// ignore units of other packages
for i:=0 to PackageGraph.Count-1 do
begin
CurPackage:=PackageGraph[i];
if OwnerList.IndexOf(CurPackage)>=0 then continue;
for j:=0 to CurPackage.FileCount-1 do
begin
PkgFile:=CurPackage.Files[j];
if not (PkgFile.FileType in PkgFileRealUnitTypes) then continue;
Graph.AddIgnoreUnit(PkgFile.GetFullFilename);
end;
end;
end;
// parse
Graph.Parse(true,Completed);
if Completed then ;
// add parsed units
Node:=Graph.FilesTree.FindLowest;
while Node<>nil do
begin
UGUnit:=TUGUnit(Node.Data);
if Graph.IgnoreFilesTree.Find(UGUnit)=nil then
Units[UGUnit.Filename]:=nil;
Node:=Node.Successor;
end;
end;
Units.GetNames(Result);
finally
Graph.Free;
Units.Free;
end;
end;
function TPkgManager.GetPossibleOwnersOfUnit(const UnitFilename: string;
Flags: TPkgIntfOwnerSearchFlags): TFPList;
var