lazarus/components/fpcunit/ide/regtestinsight.pas

333 lines
9.5 KiB
ObjectPascal

unit RegTestInsight;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
LazLoggerBase, Forms, Controls,
IDECommands, MenuIntf, IDEWindowIntf, LazIDEIntf, IDEMsgIntf,
IDEOptEditorIntf, IDEOptionsIntf, IDEExternToolIntf,
Projectintf,
CodeTree, CodeCache, CodeToolManager, FindDeclarationTool,
frmTestInsight, TestInsightServer, StrTestCaseOpts,
TestInsightController, fraTestInsightOpts;
Type
{ TLazTestInsightForm }
TLazTestInsightForm = class(TTestInsightForm)
private
Public
procedure ShowMessage(aType: TInsightMessageType; Const Msg : String); override;
Function GetTestProject : String; override;
procedure RunTestProject(aExecutable : string; SendNamesOnly : Boolean); override;
procedure NavigateTo(const aClass, aMethod, aUnit, aLocationFile: String; aLocationLine: Integer); override;
procedure ConfigureServer(aServer: TTestInsightServer); override;
Function AutoFetchTests: Boolean; override;
function ShowRefreshTestproject: Boolean; override;
end;
procedure register;
implementation
const
TestInsightFormName = 'TestInsightForm';
var
TestInsightOptionsFrameID: integer = 1001;
Type
{ TFormFreeNotifier }
TFormFreeNotifier = Class(TComponent)
Public
Type
TFreeNotificationProc = Procedure(aForm : TCustomForm);
Private
FOnNotify : TFreeNotificationProc;
FForm : TCustomForm;
private
procedure SetForm(AValue: TCustomForm);
Protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
function DoProjectChanged(Sender: TObject; {%H-}AProject: TLazProject): TModalResult;
Public
Constructor CustomCreate(aOwner : TComponent; aOnNotify : TFreeNotificationProc);
Property Form : TCustomForm Read FForm Write SetForm;
end;
var
FreeNotifier : TFormFreeNotifier;
TestInsightForm: TTestInsightForm;
Function ShowFileAt(aLocationFile : String; aLocationLine : integer) : Boolean;
var
F : TLazProjectFile;
begin
F:=LazarusIDE.ActiveProject.FindFile(aLocationFile,[]);
Result:=Assigned(F);
if Result then
Result:=(mrOK=LazarusIDE.DoOpenFileAndJumpToPos(F.GetFullFilename,Point(aLocationLine-1,0),0,0,0,0,0,[ofOnlyIfExists]));
end;
Function ShowMethod(aClass,aMethod, aUnit : String) : Boolean;
var
F : TLazProjectFile;
Tool : TCodeTool;
ClassTool : TFindDeclarationTool;
C : TCodeBuffer;
ClassNode,NewNode,Node : TCodeTreeNode;
NewTopLine : Integer;
Params: TFindDeclarationParams;
LProj : TLazProject;
Ctx : TFindContext;
caret : TCodeXYPosition;
begin
Result:=False;
LProj:=LazarusIDE.ActiveProject;
if (aUnit<>'') then
begin
F:=LProj.FindFile(aunit+'.pp',[pfsfOnlyProjectFiles]);
if Not Assigned(F) then
F:=LProj.FindFile(aunit+'.pas',[pfsfOnlyProjectFiles]);
if Not Assigned(F) then
F:=LProj.FindFile(aunit+'.p',[pfsfOnlyProjectFiles]);
// Writeln('Found unit "',aunit,'" : ',Assigned(F));
end;
// Search in unit, if available
Tool:=Nil;
Node:=Nil;
if Assigned(F) then
begin
C:=CodeToolBoss.LoadFile(F.GetFullFilename,False,false);
if CodetoolBoss.Explore(C,Tool,False,false) then
Node:=Tool.FindImplementationNode;
end;
// If not found
if not Assigned(Node) then
begin
C:=CodeToolBoss.LoadFile(LProj.MainFile.GetFullFilename,False,false);
if CodetoolBoss.Explore(C,Tool,False,false) then
Node:=Tool.FindMainBeginEndNode;
end;
// Writeln('Node found for unit "',aunit,'" : ',Assigned(Node));
if Node=nil then
exit;
Params:=TFindDeclarationParams.Create;
Params.ContextNode:=Node;
Params.Flags:=[fdfSearchInParentNodes];
Params.SetIdentifier(Tool,PChar(aClass),nil);
if not Tool.FindIdentifierInContext(Params) then
begin
// Writeln('Class not found for unit "',aunit,'", identifier ',aClass);
exit;
end;
NewNode:=Params.NewNode;
ClassTool:=Params.NewCodeTool;
ClassNode:=NewNode.FirstChild;
if (ClassNode=nil)
or (NewNode.Desc<>ctnTypeDefinition)
or (ClassNode.Desc<>ctnClass) then
begin
// Writeln('Class identifier in unit "',aunit,'", is not a class identifier ',aClass);
Exit;
end;
Ctx:=ClassTool.FindClassMember(ClassNode,aMethod,true);
if not Assigned(Ctx.Node) then
begin
// Writeln('Method ',aMethod,' for class ',aClass,' not found in unit "',aunit);
exit;
end;
Ctx.Tool.CleanPosToCaretAndTopLine(Ctx.Node.StartPos,Caret,NewTopLine);
Result:=(mrOK=LazarusIDE.DoOpenFileAndJumpToPos(Caret.Code.Filename,Point(Caret.X,Caret.Y),NewTopLine,-1,-1,[ofRegularFile]));
end;
Var
TestInsightWindowCreator: TIDEWindowCreator;
ViewTestInsightWindowCommand: TIDECommand;
Controller : TTestInsightController;
procedure ShowTestInsightWindow(Sender: TObject);
var
F : TCustomForm;
begin
if TestInsightForm=nil then
begin
F:=IDEWindowCreators.ShowForm(TestInsightWindowCreator.FormName,true);
FreeNotifier.Form:=F;
end
else
IDEWindowCreators.ShowForm(TestInsightForm,true);
end;
procedure CreateTestInsightWindow(Sender: TObject; aFormName: string;
var AForm: TCustomForm; DoDisableAutoSizing: boolean);
begin
if not SameText(aFormName,TestInsightFormName) then
begin
debugln(['ERROR: TestInsightFormName : there is already a form with this name']);
exit;
end;
IDEWindowCreators.CreateForm(AForm,TLazTestInsightForm,DoDisableAutoSizing,
LazarusIDE.OwningComponent);
AForm.Name:=aFormName;
TestInsightForm:=TTestInsightForm(AForm);
end;
procedure ClearTestInsightForm(aForm: TCustomForm);
begin
if aForm=TestInsightForm then
TestInsightForm:=nil;
end;
procedure register;
var
CmdCatView: TIDECommandCategory;
begin
Controller:=TTestInsightController.Create;
Controller.Options.LoadFromFile(TestInsightConfig);
FreeNotifier:=TFormFreeNotifier.CustomCreate(Nil,@ClearTestInsightForm);
CmdCatView:=IDECommandList.FindCategoryByName(CommandCategoryViewName);
if CmdCatView=nil then
raise Exception.Create('simplewebsrv: command category '+CommandCategoryViewName+' not found');
// Windows - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TestInsightWindowCreator:=IDEWindowCreators.Add(TestInsightFormName,
@CreateTestInsightWindow,nil,'20%','20%','+50%','+20%');
// Windows - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ViewTestInsightWindowCommand:=RegisterIDECommand(CmdCatView, 'ViewTestInsightWindow',
rsTestInsightTitle, CleanIDEShortCut, CleanIDEShortCut, nil, @ ShowTestInsightWindow);
RegisterIDEMenuCommand(itmViewMainWindows, 'ViewTestInsightWindow',
rsTestInsightTitle, nil, nil, ViewTestInsightWindowCommand);
// Options Frame - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TestInsightOptionsFrameID:=RegisterIDEOptionsEditor(GroupEnvironment,TTestInsightOptionsFrame,TestInsightOptionsFrameID)^.Index;
LazarusIDE.AddHandlerOnProjectOpened(@FreeNotifier.DoProjectChanged);
end;
{ TFormFreeNotifier }
function TFormFreeNotifier.DoProjectChanged(Sender: TObject; AProject: TLazProject): TModalResult;
begin
Result:=mrOK;
if Assigned(FForm) and (FForm is TLazTestInsightForm) then
TLazTestInsightForm(FForm).MaybeStartTestProject;
end;
procedure TFormFreeNotifier.SetForm(AValue: TCustomForm);
begin
if FForm=AValue then Exit;
if Assigned(FForm) then FForm.RemoveFreeNotification(Self);
FForm:=AValue;
if Assigned(FForm) then FForm.FreeNotification(Self);
end;
procedure TFormFreeNotifier.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation=opRemove) and (aComponent=FForm) then
begin
FOnNotify(FForm);
FForm:=Nil;
end;
end;
constructor TFormFreeNotifier.CustomCreate(aOwner: TComponent; aOnNotify: TFreeNotificationProc);
begin
Inherited Create(aOwner);
FForm:=Nil;
FOnNotify:=aOnNotify;
end;
{ TLazTestInsightForm }
procedure TLazTestInsightForm.ShowMessage(aType: TInsightMessageType; const Msg: String);
Const
MLU : Array[TInsightMessageType] of TMessageLineUrgency = (TMessageLineUrgency.mluImportant,TMessageLineUrgency.mluError);
begin
if Assigned(IDEMessagesWindow) then
IDEMessagesWindow.AddCustomMessage(MLU[aType],Msg,'',0,0,rsTestInsightTitle)
end;
function TLazTestInsightForm.GetTestProject: String;
begin
// When the IDE restores windows, the ActiveProject is not yet there, so we need to check.
if Assigned(LazarusIDE)
and Assigned(LazarusIDE.ActiveProject)
and assigned(LazarusIDE.ActiveProject.LazCompilerOptions) then
Result:=LazarusIDE.ActiveProject.LazCompilerOptions.CreateTargetFilename;
end;
procedure TLazTestInsightForm.RunTestProject(aExecutable : string; SendNamesOnly: Boolean);
begin
// Probably the controller should do this.
inherited RunTestProject(aExecutable,SendNamesOnly);
end;
procedure TLazTestInsightForm.ConfigureServer(aServer: TTestInsightServer);
begin
aServer.Port:=Controller.Options.Port;
aServer.BasePath:=Controller.Options.BasePath;
end;
function TLazTestInsightForm.AutoFetchTests: Boolean;
begin
Result:=Controller.Options.AutoFetchTests
end;
procedure TLazTestInsightForm.NavigateTo(const aClass,aMethod, aUnit, aLocationFile: String; aLocationLine: Integer);
var
NavOK : Boolean;
begin
NavOK:=False;
if (aLocationFile<>'') then
NavOK:=ShowFileAt(aLocationFile,aLocationLine);
if not NavOK then
NavOK:=ShowMethod(aClass,aMethod,aUnit);
if not NavOK then
ShowMessage(imtError,Format('Failed to navigate to test %s.%s in unit %s',[aClass,aMethod,aUnit]));
end;
function TLazTestInsightForm.ShowRefreshTestproject: Boolean;
begin
Result:=True;
end;
finalization
FreeAndNil(Controller);
FreeAndNil(FreeNotifier);
end.