mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 15:37:50 +02:00
582 lines
16 KiB
ObjectPascal
582 lines
16 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., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
Find all alternative declarations of an identifier.
|
|
|
|
TCarbonControl = class(TCarbonWidget)
|
|
procedure TCarbonControl.CreateWidget(const AParams: TCreateParams);
|
|
TCarbonCustomCheckBox = class(TCarbonControl)
|
|
TCarbonCheckBox = class(TCarbonCustomCheckBox)
|
|
procedure TCarbonCheckBox.CreateWidget(const AParams: TCreateParams);
|
|
|
|
}
|
|
unit FindOverloadsDlg;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, AVL_Tree, System.UITypes,
|
|
// LCL
|
|
Forms, StdCtrls, ButtonPanel, ComCtrls,
|
|
// LazUtils
|
|
LazFileUtils, LazLoggerBase,
|
|
// codetools
|
|
FindDeclarationTool, PascalParserTool, CodeTree, CodeCache, CodeToolManager,
|
|
FindOverloads,
|
|
// BuildIntf
|
|
ProjectIntf,
|
|
// IdeIntf
|
|
SrcEditorIntf, LazIDEIntf,
|
|
// IdeConfig
|
|
IDEProcs;
|
|
|
|
type
|
|
|
|
{ TFOWFile }
|
|
|
|
TFOWFile = class
|
|
private
|
|
FCode: TCodeBuffer;
|
|
FFilename: string;
|
|
FOnlyInterface: boolean;
|
|
FScanned: boolean;
|
|
procedure SetCode(const AValue: TCodeBuffer);
|
|
procedure SetScanned(const AValue: boolean);
|
|
public
|
|
constructor Create(const TheFilename: string);
|
|
destructor Destroy; override;
|
|
property Filename: string read FFilename;
|
|
property OnlyInterface: boolean read FOnlyInterface write FOnlyInterface;
|
|
property Scanned: boolean read FScanned write SetScanned;
|
|
property Code: TCodeBuffer read FCode write SetCode;
|
|
end;
|
|
|
|
TFindOverloadsScope = (
|
|
fosProject,
|
|
fosPackages,
|
|
fosOtherSources
|
|
);
|
|
TFindOverloadsScopes = set of TFindOverloadsScope;
|
|
|
|
TFOWStage = (
|
|
fowsStart,
|
|
fowsFinished
|
|
);
|
|
|
|
{ TFindOverloadsWorker }
|
|
|
|
TFindOverloadsWorker = class
|
|
private
|
|
FFiles: TAvlTree;
|
|
FScanFiles: TAvlTree;
|
|
FStagePosition: integer;
|
|
FStagePosMax: integer;
|
|
FStageTitle: string;
|
|
procedure CollectProjectFiles;
|
|
procedure CollectPackageFiles;
|
|
procedure CollectOtherSourceFiles;
|
|
procedure ScanSomeFiles;
|
|
procedure ScanFile(AFile: TFOWFile);
|
|
procedure CollectStartSource;
|
|
public
|
|
StartSourceScanned: boolean;
|
|
Scopes: TFindOverloadsScopes;
|
|
CompletedScopes: TFindOverloadsScopes;
|
|
Graph: TDeclarationOverloadsGraph;
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
procedure Work;
|
|
function Done: boolean;
|
|
procedure StopSearching;
|
|
function AddFileToScan(const Filename: string;
|
|
CheckExtension: boolean = true): TFOWFile;
|
|
function FindFile(const Filename: string): TFOWFile;
|
|
property Files: TAvlTree read FFiles; // tree of TFindOverloadsWorkerFile
|
|
property ScanFiles: TAvlTree read FScanFiles;// tree of TFindOverloadsWorkerFile
|
|
property StageTitle: string read FStageTitle write FStageTitle;
|
|
property StagePosition: integer read FStagePosition write FStagePosition;
|
|
property StagePosMax: integer read FStagePosMax write FStagePosMax;
|
|
end;
|
|
|
|
{ TFindOverloadsDialog }
|
|
|
|
TFindOverloadsDialog = class(TForm)
|
|
ButtonPanel1: TButtonPanel;
|
|
SearchAllCheckBox: TCheckBox;
|
|
CurGroupBox: TGroupBox;
|
|
ResultsProgressBar: TProgressBar;
|
|
ResultsGroupBox: TGroupBox;
|
|
CurTreeView: TTreeView;
|
|
ResultsTreeView: TTreeView;
|
|
procedure ButtonPanel1Click(Sender: TObject);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure FormDestroy(Sender: TObject);
|
|
procedure OnIdle(Sender: TObject; var Done: Boolean);
|
|
private
|
|
FIdleConnected: boolean;
|
|
fCurTreeViewComplete: boolean;
|
|
fWorker: TFindOverloadsWorker;
|
|
procedure SetIdleConnected(const AValue: boolean);
|
|
procedure UpdateProgress;
|
|
procedure StopWorking;
|
|
procedure UpdateCurTreeView;
|
|
public
|
|
property Worker: TFindOverloadsWorker read fWorker;
|
|
property IdleConnected: boolean read FIdleConnected write SetIdleConnected;
|
|
end;
|
|
|
|
function ShowFindOverloadsDialog: TModalResult;
|
|
function ShowFindOverloadsDialog(Code: TCodeBuffer; X, Y: integer): TModalResult;
|
|
|
|
function CompareFOWFiles(File1, File2: TFOWFile): integer;
|
|
function CompareFilenameWithFOWFile(FilenameAnsiString, FOWFile: Pointer): integer;
|
|
|
|
implementation
|
|
|
|
{$R *.lfm}
|
|
|
|
function ShowFindOverloadsDialog: TModalResult;
|
|
var
|
|
SrcEdit: TSourceEditorInterface;
|
|
Code: TCodeBuffer;
|
|
XY: TPoint;
|
|
begin
|
|
SrcEdit:=SourceEditorManagerIntf.ActiveEditor;
|
|
if SrcEdit=nil then
|
|
exit(mrCancel);
|
|
Code:=TCodeBuffer(SrcEdit.CodeToolsBuffer);
|
|
XY:=SrcEdit.CursorTextXY;
|
|
Result:=ShowFindOverloadsDialog(Code,XY.X,XY.Y);
|
|
end;
|
|
|
|
function ShowFindOverloadsDialog(Code: TCodeBuffer; X, Y: integer): TModalResult;
|
|
var
|
|
FindOverloadsDialog: TFindOverloadsDialog;
|
|
Graph: TDeclarationOverloadsGraph;
|
|
begin
|
|
if not LazarusIDE.BeginCodeTools then exit;
|
|
Graph:=nil;
|
|
FindOverloadsDialog:=nil;
|
|
CodeToolBoss.ActivateWriteLock;
|
|
try
|
|
if not CodeToolBoss.GatherOverloads(Code,X,Y,Graph) then begin
|
|
LazarusIDE.DoJumpToCodeToolBossError;
|
|
exit(mrCancel);
|
|
end;
|
|
//DebugLn(['ShowFindOverloadsDialog ',Graph.StartCode.Filename,' ',Graph.StartX,',',Graph.StartY]);
|
|
FindOverloadsDialog:=TFindOverloadsDialog.Create(nil);
|
|
FindOverloadsDialog.Worker.Graph:=Graph;
|
|
Result:=FindOverloadsDialog.ShowModal;
|
|
finally
|
|
CodeToolBoss.DeactivateWriteLock;
|
|
FindOverloadsDialog.Free;
|
|
Graph.Free;
|
|
end;
|
|
end;
|
|
|
|
function CompareFOWFiles(File1, File2: TFOWFile): integer;
|
|
begin
|
|
Result:=CompareFilenames(File1.Filename,File2.Filename);
|
|
end;
|
|
|
|
function CompareFilenameWithFOWFile(FilenameAnsiString, FOWFile: Pointer): integer;
|
|
begin
|
|
Result:=CompareFilenames(ansistring(FilenameAnsiString),TFOWFile(FOWFile).Filename);
|
|
end;
|
|
|
|
{ TFindOverloadsDialog }
|
|
|
|
procedure TFindOverloadsDialog.FormCreate(Sender: TObject);
|
|
begin
|
|
Caption:='Find overloads';
|
|
CurGroupBox.Caption:='Current identifier';
|
|
ResultsGroupBox.Caption:='Overloads';
|
|
SearchAllCheckBox.Caption:='Search in other sources too';
|
|
SearchAllCheckBox.ShowHint:=true;
|
|
SearchAllCheckBox.Hint:='Enable this to search in system sources too. For example the RTL and FCL sources. This can take some minutes on slow machines.';
|
|
|
|
ButtonPanel1.CancelButton.OnClick:=@ButtonPanel1Click;
|
|
|
|
fWorker:=TFindOverloadsWorker.Create;
|
|
IdleConnected:=true;
|
|
UpdateProgress;
|
|
end;
|
|
|
|
procedure TFindOverloadsDialog.ButtonPanel1Click(Sender: TObject);
|
|
begin
|
|
StopWorking;
|
|
end;
|
|
|
|
procedure TFindOverloadsDialog.FormDestroy(Sender: TObject);
|
|
begin
|
|
IdleConnected:=false;
|
|
FreeAndNil(fWorker);
|
|
end;
|
|
|
|
procedure TFindOverloadsDialog.OnIdle(Sender: TObject; var Done: Boolean);
|
|
begin
|
|
fWorker.Work;
|
|
Done:=fWorker.Done;
|
|
if Done then
|
|
IdleConnected:=false;
|
|
UpdateProgress;
|
|
if not fCurTreeViewComplete then
|
|
UpdateCurTreeView;
|
|
end;
|
|
|
|
procedure TFindOverloadsDialog.SetIdleConnected(const AValue: boolean);
|
|
begin
|
|
if FIdleConnected=AValue then exit;
|
|
FIdleConnected:=AValue;
|
|
if FIdleConnected then begin
|
|
ButtonPanel1.CancelButton.Enabled:=true;
|
|
ButtonPanel1.CloseButton.Enabled:=false;
|
|
Application.AddOnIdleHandler(@OnIdle)
|
|
end else begin
|
|
ButtonPanel1.CancelButton.Enabled:=false;
|
|
ButtonPanel1.CloseButton.Enabled:=true;
|
|
Application.RemoveOnIdleHandler(@OnIdle);
|
|
end;
|
|
end;
|
|
|
|
procedure TFindOverloadsDialog.UpdateProgress;
|
|
begin
|
|
if Worker.Done then
|
|
ResultsProgressBar.Visible:=false
|
|
else begin
|
|
ResultsProgressBar.Max:=Worker.StagePosMax;
|
|
ResultsProgressBar.Position:=Worker.StagePosition;
|
|
ResultsProgressBar.Visible:=true;
|
|
end;
|
|
end;
|
|
|
|
procedure TFindOverloadsDialog.StopWorking;
|
|
begin
|
|
IdleConnected:=false;
|
|
Worker.StopSearching;
|
|
end;
|
|
|
|
procedure TFindOverloadsDialog.UpdateCurTreeView;
|
|
var
|
|
s: String;
|
|
Node: TCodeTreeNode;
|
|
Tool: TFindDeclarationTool;
|
|
ParentNode: TCodeTreeNode;
|
|
begin
|
|
fCurTreeViewComplete:=true;
|
|
CurTreeView.BeginUpdate;
|
|
CurTreeView.Items.Clear;
|
|
Node:=Worker.Graph.StartCodeNode;
|
|
Tool:=Worker.Graph.StartTool;
|
|
if Node<>nil then begin
|
|
DebugLn(['TFindOverloadsDialog.UpdateCurTreeView ',Node.DescAsString,' ',dbgstr(copy(Tool.Src,Node.StartPos,20))]);
|
|
// unit name
|
|
s:=Tool.GetSourceName(false)+': ';
|
|
// keyword
|
|
case Node.Desc of
|
|
ctnEnumIdentifier: s:=s+'enum';
|
|
ctnVarDefinition: s:=s+'var';
|
|
ctnConstDefinition: s:=s+'const';
|
|
ctnTypeDefinition: s:=s+'type';
|
|
ctnGenericType: s:=s+'generic';
|
|
ctnProperty: s:=s+'property';
|
|
ctnProcedure: s:=s+'procedure';
|
|
ctnUseUnit: s:=s+'uses';
|
|
ctnUnit: s:=s+'unit';
|
|
ctnProgram: s:=s+'program';
|
|
ctnPackage: s:=s+'package';
|
|
ctnLibrary: s:=s+'library';
|
|
end;
|
|
s:=s+' ';
|
|
// context
|
|
if Node.Desc<>ctnEnumIdentifier then
|
|
begin
|
|
ParentNode:=Node.Parent;
|
|
while ParentNode<>nil do begin
|
|
case ParentNode.Desc of
|
|
ctnTypeDefinition,ctnGenericType:
|
|
s:=s+Tool.ExtractDefinitionName(Node)+'.';
|
|
end;
|
|
ParentNode:=ParentNode.Parent;
|
|
end;
|
|
end;
|
|
// name
|
|
case Node.Desc of
|
|
ctnEnumIdentifier, ctnTypeDefinition, ctnConstDefinition, ctnVarDefinition,
|
|
ctnGenericType:
|
|
s:=s+Tool.ExtractDefinitionName(Node);
|
|
ctnProperty:
|
|
s:=s+Tool.ExtractPropName(Node,false);
|
|
ctnProcedure:
|
|
s:=s+Tool.ExtractProcName(Node,[phpWithoutClassName,phpCommentsToSpace]);
|
|
ctnUseUnit:
|
|
s:=s+Tool.ExtractNode(Node,[phpCommentsToSpace]);
|
|
ctnUnit,ctnProgram,ctnPackage,ctnLibrary:
|
|
s:=s+Tool.GetSourceName(false);
|
|
end;
|
|
// add node
|
|
CurTreeView.Items.Add(nil,s);
|
|
end;
|
|
CurTreeView.EndUpdate;
|
|
end;
|
|
|
|
{ TFOWFile }
|
|
|
|
procedure TFOWFile.SetCode(const AValue: TCodeBuffer);
|
|
begin
|
|
if FCode=AValue then exit;
|
|
FCode:=AValue;
|
|
end;
|
|
|
|
procedure TFOWFile.SetScanned(const AValue: boolean);
|
|
begin
|
|
if FScanned=AValue then exit;
|
|
FScanned:=AValue;
|
|
end;
|
|
|
|
constructor TFOWFile.Create(const TheFilename: string);
|
|
begin
|
|
FFilename:=TheFilename;
|
|
FOnlyInterface:=true;
|
|
end;
|
|
|
|
destructor TFOWFile.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TFindOverloadsWorker }
|
|
|
|
procedure TFindOverloadsWorker.CollectProjectFiles;
|
|
var
|
|
AProject: TLazProject;
|
|
i: Integer;
|
|
ProjFile: TLazProjectFile;
|
|
begin
|
|
AProject:=LazarusIDE.ActiveProject;
|
|
if AProject<>nil then begin
|
|
for i:=0 to AProject.FileCount-1 do begin
|
|
ProjFile:=AProject.Files[i];
|
|
if ProjFile.IsPartOfProject then
|
|
AddFileToScan(ProjFile.Filename);
|
|
end;
|
|
end;
|
|
Include(CompletedScopes,fosProject);
|
|
end;
|
|
|
|
procedure TFindOverloadsWorker.CollectPackageFiles;
|
|
begin
|
|
|
|
Include(CompletedScopes,fosPackages);
|
|
end;
|
|
|
|
procedure TFindOverloadsWorker.CollectOtherSourceFiles;
|
|
begin
|
|
|
|
Include(CompletedScopes,fosOtherSources);
|
|
end;
|
|
|
|
procedure TFindOverloadsWorker.ScanSomeFiles;
|
|
const
|
|
MaxScanTime = 0.3/86400; // 0.3 seconds
|
|
var
|
|
StartTime: TDateTime;
|
|
CurFile: TFOWFile;
|
|
begin
|
|
StartTime:=Now;
|
|
while FScanFiles.Count>0 do begin
|
|
CurFile:=TFOWFile(FScanFiles.FindLowest.Data);
|
|
ScanFile(CurFile);
|
|
if Now-StartTime>=MaxScanTime then
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
procedure TFindOverloadsWorker.ScanFile(AFile: TFOWFile);
|
|
var
|
|
Tool: TCodeTool;
|
|
Filename: String;
|
|
MainFile: TFOWFile;
|
|
Code: TCodeBuffer;
|
|
begin
|
|
FScanFiles.Remove(AFile);
|
|
if AFile.Scanned then exit;
|
|
AFile.Scanned:=true;
|
|
//DebugLn(['TFindOverloadsWorker.ScanFile File=',AFile.Filename]);
|
|
// get codetool
|
|
Filename:=TrimFilename(AFile.Filename);
|
|
Code:=CodeToolBoss.LoadFile(Filename,true,false);
|
|
if Code=nil then begin
|
|
DebugLn(['TFindOverloadsWorker.ScanFile file not readable: ',Filename]);
|
|
exit;
|
|
end;
|
|
Tool:=TCodeTool(CodeToolBoss.GetCodeToolForSource(Code,true,false));
|
|
if Tool=nil then begin
|
|
DebugLn(['TFindOverloadsWorker.ScanFile file not in a unit: ',Filename]);
|
|
exit;
|
|
end;
|
|
// check if AFile is an include file
|
|
Filename:=Tool.MainFilename;
|
|
MainFile:=FindFile(Filename);
|
|
// get unit
|
|
if MainFile=nil then begin
|
|
MainFile:=TFOWFile.Create(Filename);
|
|
FFiles.Add(MainFile);
|
|
end;
|
|
if (MainFile<>AFile) and MainFile.Scanned then begin
|
|
//DebugLn(['TFindOverloadsWorker.ScanFile already scanned: ',Filename]);
|
|
exit;
|
|
end;
|
|
// scan unit
|
|
FScanFiles.Remove(MainFile);
|
|
MainFile.Scanned:=true;
|
|
if not AFile.OnlyInterface then
|
|
MainFile.OnlyInterface:=false;
|
|
//DebugLn(['TFindOverloadsWorker.ScanFile scanning: ',Tool.MainFilename]);
|
|
Graph.ScanToolForIdentifier(Tool,MainFile.OnlyInterface);
|
|
end;
|
|
|
|
procedure TFindOverloadsWorker.CollectStartSource;
|
|
var
|
|
Filename: String;
|
|
aFile: TFOWFile;
|
|
begin
|
|
Filename:=Graph.StartCode.Filename;
|
|
aFile:=FindFile(Filename);
|
|
if aFile=nil then begin
|
|
aFile:=TFOWFile.Create(Filename);
|
|
aFile.OnlyInterface:=false;
|
|
FFiles.Add(aFile);
|
|
FScanFiles.Add(aFile);
|
|
end;
|
|
end;
|
|
|
|
function TFindOverloadsWorker.AddFileToScan(const Filename: string;
|
|
CheckExtension: boolean): TFOWFile;
|
|
begin
|
|
if CheckExtension and (not FilenameIsPascalSource(Filename)) then
|
|
exit(nil);
|
|
Result:=FindFile(Filename);
|
|
if Result<>nil then exit;
|
|
Result:=TFOWFile.Create(Filename);
|
|
FFiles.Add(Result);
|
|
FScanFiles.Add(Result);
|
|
end;
|
|
|
|
function TFindOverloadsWorker.FindFile(const Filename: string): TFOWFile;
|
|
var
|
|
AVLNode: TAvlTreeNode;
|
|
begin
|
|
AVLNode:=FFiles.FindKey(Pointer(Filename),@CompareFilenameWithFOWFile);
|
|
if AVLNode<>nil then
|
|
Result:=TFOWFile(AVLNode.Data)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
constructor TFindOverloadsWorker.Create;
|
|
begin
|
|
Scopes:=[fosProject,fosPackages];
|
|
FFiles:=TAvlTree.Create(TListSortCompare(@CompareFOWFiles));
|
|
FScanFiles:=TAvlTree.Create(TListSortCompare(@CompareFOWFiles));
|
|
FStagePosMax:=100;
|
|
end;
|
|
|
|
destructor TFindOverloadsWorker.Destroy;
|
|
begin
|
|
Clear;
|
|
FreeAndNil(FFiles);
|
|
FreeAndNil(FScanFiles);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TFindOverloadsWorker.Clear;
|
|
begin
|
|
FFiles.FreeAndClear;
|
|
FScanFiles.Clear;
|
|
FStageTitle:='Finished';
|
|
FStagePosition:=0;
|
|
FStagePosMax:=100;
|
|
StartSourceScanned:=false;
|
|
end;
|
|
|
|
procedure TFindOverloadsWorker.Work;
|
|
begin
|
|
DebugLn(['TFindOverloadsWorker.Work START']);
|
|
if FScanFiles.Count>0 then begin
|
|
// scan files
|
|
ScanSomeFiles;
|
|
end
|
|
else if not StartSourceScanned then
|
|
begin
|
|
StageTitle:='Scanning start source ...';
|
|
StagePosition:=10;
|
|
StartSourceScanned:=true;
|
|
CollectStartSource;
|
|
end
|
|
else if (fosProject in Scopes) and not (fosProject in CompletedScopes) then
|
|
begin
|
|
// collect project files
|
|
StageTitle:='Scanning project ...';
|
|
StagePosition:=20;
|
|
CollectProjectFiles;
|
|
end
|
|
else if (fosPackages in Scopes) and not (fosPackages in CompletedScopes) then
|
|
begin
|
|
// collect package files
|
|
StageTitle:='Scanning packages ...';
|
|
StagePosition:=40;
|
|
CollectPackageFiles;
|
|
end
|
|
else if (fosOtherSources in Scopes) and not (fosOtherSources in CompletedScopes)
|
|
then begin
|
|
// collect other sources
|
|
StageTitle:='Scanning other sources ...';
|
|
StagePosition:=60;
|
|
CollectOtherSourceFiles;
|
|
end else begin
|
|
StageTitle:='Finished';
|
|
StagePosition:=StagePosMax;
|
|
Graph.ComputeShortestPaths;
|
|
end;
|
|
DebugLn(['TFindOverloadsWorker.Work END ',StageTitle,' ',StagePosition,'/',StagePosMax]);
|
|
end;
|
|
|
|
function TFindOverloadsWorker.Done: boolean;
|
|
begin
|
|
Result:=(Scopes-CompletedScopes=[]) and (FScanFiles.Count=0);
|
|
end;
|
|
|
|
procedure TFindOverloadsWorker.StopSearching;
|
|
begin
|
|
CompletedScopes:=Scopes;
|
|
FScanFiles.Clear;
|
|
Graph.ComputeShortestPaths;
|
|
end;
|
|
|
|
end.
|
|
|