mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-10-31 22:10:55 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			478 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			478 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| unit EMScriptMacro;
 | |
| 
 | |
| {$mode objfpc}{$H+}
 | |
| 
 | |
| interface
 | |
| 
 | |
| uses
 | |
|   Classes, SysUtils, SrcEditorIntf, IDEMsgIntf, LazIDEIntf, IDEOptionsIntf,
 | |
|   IDEExternToolIntf,
 | |
|   Controls, SynEdit, EMScriptClasses, EMSStrings, Laz2_XMLCfg, uPSRuntime,
 | |
|   uPSUtils, LazLoggerBase, LazFileUtils;
 | |
| 
 | |
| {$if defined(cpupowerpc)}
 | |
|   {$ifNdef darwin}  {$DEFINE PasScriptNotAvail } {$ifend}
 | |
|   {$ifNdef cpu32}  {$DEFINE PasScriptNotAvail } {$ifend}
 | |
| {$ifend}
 | |
| {$if defined(cpusparc) }  {$DEFINE PasScriptNotAvail } {$ifend}
 | |
| 
 | |
| const
 | |
|   EMSSupported = {$IFDEF PasScriptNotAvail} False {$ELSE} True {$ENDIF} ;
 | |
| 
 | |
| type
 | |
| 
 | |
|   { TEMSEditorMacro }
 | |
| 
 | |
|   TEMSEditorMacro = class(TEditorMacro)
 | |
|   private
 | |
|     FMacroName: String;
 | |
|     FSource: String;
 | |
|     FState: TEditorMacroState;
 | |
|     FHasError: Boolean;
 | |
|     FErrorMsg: String;
 | |
|     FKeyBinding: TEditorMacroKeyBinding;
 | |
|     FPrivateCompiler: TEMSPSPascalCompiler;
 | |
|     FPrivateExec: TEMSTPSExec;
 | |
|     function GetCompiler: TEMSPSPascalCompiler;
 | |
|     function GetExec: TEMSTPSExec;
 | |
|     procedure SetCompiler(AValue: TEMSPSPascalCompiler);
 | |
|     procedure SetExec(AValue: TEMSTPSExec);
 | |
|   protected
 | |
|     function  GetMacroName: String; override;
 | |
|     procedure SetMacroName(AValue: string); override;
 | |
|     function  GetState: TEditorMacroState; override;
 | |
|     function  GetErrorMsg: String; override;
 | |
|     function  GetKeyBinding: TEditorMacroKeyBinding; override;
 | |
| 
 | |
|     procedure DoRecordMacro({%H-}aEditor: TWinControl); override;
 | |
|     procedure DoPlaybackMacro(aEditor: TWinControl); override;
 | |
|     procedure DoStop; override;
 | |
|     procedure DoPause; override;
 | |
|     procedure DoResume; override;
 | |
| 
 | |
|     procedure Compile;
 | |
|     procedure FixBeginEnd;
 | |
|     property Compiler: TEMSPSPascalCompiler read GetCompiler write SetCompiler;
 | |
|     property Exec: TEMSTPSExec read GetExec write SetExec;
 | |
|   public
 | |
|     constructor Create({%H-}aOwner: TComponent); override;
 | |
|     destructor Destroy; override;
 | |
|     procedure AssignEventsFrom(AMacroRecorder: TEditorMacro); override;
 | |
| 
 | |
|     procedure Clear; override;
 | |
| 
 | |
|     function  GetAsSource: String; override;
 | |
|     procedure SetFromSource(const AText: String); override;
 | |
|     procedure WriteToXmlConf(AConf: TXMLConfig; const APath: String); override;
 | |
|     procedure ReadFromXmlConf(AConf: TXMLConfig; const APath: String); override;
 | |
| 
 | |
|     function  IsEmpty: Boolean; override;
 | |
|     function  IsInvalid: Boolean; override;
 | |
|     function  IsRecording({%H-}AnEditor: TWinControl): Boolean; override;
 | |
| 
 | |
|     property PrivateCompiler: TEMSPSPascalCompiler read FPrivateCompiler write FPrivateCompiler;
 | |
|     property PrivateExec: TEMSTPSExec read FPrivateExec write FPrivateExec;
 | |
|   end;
 | |
| 
 | |
|   { TEMSConfig }
 | |
| 
 | |
|   TEMSConfig = class(TAbstractIDEEnvironmentOptions)
 | |
|   private
 | |
|     FSelfTestActive: Boolean;
 | |
|     FSelfTestError: String;
 | |
|     FSelfTestFailed: Integer; // stores EMSVersion that failed
 | |
|   protected
 | |
|     function GetXmlConf(CleanOnError: Boolean = False): TRttiXMLConfig;
 | |
|   public
 | |
|     constructor Create;
 | |
|     procedure Init;
 | |
|     procedure Load;
 | |
|     procedure Save;
 | |
| 
 | |
|     class function GetGroupCaption: string; override;
 | |
|     class function GetInstance: TAbstractIDEOptions; override;
 | |
|   published
 | |
|     property SelfTestActive: Boolean read FSelfTestActive write FSelfTestActive;
 | |
|     property SelfTestFailed: Integer read FSelfTestFailed write FSelfTestFailed;
 | |
|     property SelfTestError: String read FSelfTestError write FSelfTestError;
 | |
|   end;
 | |
| 
 | |
| function GetEMSConf: TEMSConfig;
 | |
| 
 | |
| const
 | |
|   EMSVersion = 1;
 | |
| 
 | |
| implementation
 | |
| 
 | |
| var
 | |
|   GlobalCompiler: TEMSPSPascalCompiler;
 | |
|   GlobalExec: TEMSTPSExec;
 | |
|   ConfFile: TEMSConfig = nil;
 | |
|   ConfFileName: String = '';
 | |
| 
 | |
| const
 | |
|   DefaultConfFileName = 'editormacroscript.xml';
 | |
| 
 | |
| function GetConfFileName: String;
 | |
| begin
 | |
|   Result := ConfFileName;
 | |
|   if Result <> '' then
 | |
|     exit;
 | |
|   ConfFileName := AppendPathDelim(LazarusIDE.GetPrimaryConfigPath) + DefaultConfFileName;
 | |
|   LazarusIDE.CopySecondaryConfigFile(DefaultConfFileName);
 | |
|   Result := ConfFileName;
 | |
| end;
 | |
| 
 | |
| { Create global objects }
 | |
| 
 | |
| procedure CreateCompiler;
 | |
| begin
 | |
|   GlobalCompiler := TEMSPSPascalCompiler.Create;
 | |
| end;
 | |
| 
 | |
| procedure CreateExec;
 | |
| begin
 | |
|   GlobalExec := TEMSTPSExec.Create;
 | |
| end;
 | |
| 
 | |
| function GetEMSConf: TEMSConfig;
 | |
| begin
 | |
|   Result := ConfFile;
 | |
|   if Result <> nil then
 | |
|     exit;
 | |
| 
 | |
|   ConfFile := TEMSConfig.Create;
 | |
|   Result := ConfFile;
 | |
| end;
 | |
| 
 | |
| { TEMSConfig }
 | |
| 
 | |
| function TEMSConfig.GetXmlConf(CleanOnError: Boolean): TRttiXMLConfig;
 | |
| var
 | |
|   fn: String;
 | |
| begin
 | |
|   fn := GetConfFileName;
 | |
|   if (not FileExistsUTF8(fn)) then begin
 | |
|     Result := TRttiXMLConfig.CreateClean(fn)
 | |
|   end
 | |
|   else begin
 | |
|     if CleanOnError then
 | |
|       try
 | |
|         Result := TRttiXMLConfig.Create(fn);
 | |
|       except
 | |
|         Result := TRttiXMLConfig.CreateClean(fn)
 | |
|       end
 | |
|     else
 | |
|       Result := TRttiXMLConfig.Create(fn);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| constructor TEMSConfig.Create;
 | |
| begin
 | |
|   Init;
 | |
| end;
 | |
| 
 | |
| procedure TEMSConfig.Init;
 | |
| begin
 | |
|   FSelfTestActive := False;
 | |
|   SelfTestFailed := 0;
 | |
| end;
 | |
| 
 | |
| procedure TEMSConfig.Load;
 | |
| var
 | |
|   def: TEMSConfig;
 | |
|   cfg: TRttiXMLConfig;
 | |
| begin
 | |
|   cfg := GetXmlConf;
 | |
|   def := TEMSConfig.Create;
 | |
|   try
 | |
|     cfg.ReadObject('EMS/Settings/', Self, def);
 | |
|   finally
 | |
|     cfg.Free;
 | |
|     def.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TEMSConfig.Save;
 | |
| var
 | |
|   def: TEMSConfig;
 | |
|   cfg: TRttiXMLConfig;
 | |
| begin
 | |
|   cfg := GetXmlConf(True);
 | |
|   def := TEMSConfig.Create;
 | |
|   try
 | |
|     cfg.WriteObject('EMS/Settings/', Self, def);
 | |
|     cfg.Flush;
 | |
|   finally
 | |
|     cfg.Free;
 | |
|     def.Free;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| class function TEMSConfig.GetGroupCaption: string;
 | |
| begin
 | |
|   Result := EMSEditorMacroTitle;
 | |
| end;
 | |
| 
 | |
| class function TEMSConfig.GetInstance: TAbstractIDEOptions;
 | |
| begin
 | |
|   Result := GetEMSConf;
 | |
| end;
 | |
| 
 | |
| { TEMSEditorMacro }
 | |
| 
 | |
| function TEMSEditorMacro.GetCompiler: TEMSPSPascalCompiler;
 | |
| begin
 | |
|   Result := FPrivateCompiler;
 | |
|   if Result = nil then
 | |
|     Result := GlobalCompiler;
 | |
| end;
 | |
| 
 | |
| function TEMSEditorMacro.GetExec: TEMSTPSExec;
 | |
| begin
 | |
|   Result := FPrivateExec;
 | |
|   if Result = nil then
 | |
|     Result := GlobalExec;
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.SetCompiler(AValue: TEMSPSPascalCompiler);
 | |
| begin
 | |
|   FreeAndNil(FPrivateCompiler);
 | |
|   FPrivateCompiler := AValue;
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.SetExec(AValue: TEMSTPSExec);
 | |
| begin
 | |
|   FreeAndNil(FPrivateExec);
 | |
|   FPrivateExec := AValue;
 | |
| end;
 | |
| 
 | |
| function TEMSEditorMacro.GetMacroName: String;
 | |
| begin
 | |
|   Result := FMacroName;
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.SetMacroName(AValue: string);
 | |
| begin
 | |
|   FMacroName := AValue;
 | |
|   DoChanged;
 | |
| end;
 | |
| 
 | |
| function TEMSEditorMacro.GetState: TEditorMacroState;
 | |
| begin
 | |
|   Result := FState;
 | |
| end;
 | |
| 
 | |
| function TEMSEditorMacro.GetErrorMsg: String;
 | |
| begin
 | |
|   Result := FErrorMsg;
 | |
| end;
 | |
| 
 | |
| function TEMSEditorMacro.GetKeyBinding: TEditorMacroKeyBinding;
 | |
| begin
 | |
|   if FKeyBinding = nil then
 | |
|     FKeyBinding := GetDefaultKeyBinding;
 | |
|   Result := FKeyBinding;
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.DoRecordMacro(aEditor: TWinControl);
 | |
| begin
 | |
|   // Not supported
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.DoPlaybackMacro(aEditor: TWinControl);
 | |
| var
 | |
|   s, s2: tbtString;
 | |
|   ExObj: TObject;
 | |
|   i, x, y: Cardinal;
 | |
| begin
 | |
|   if IsEmpty or IsInvalid then exit;
 | |
| 
 | |
|   FState := emPlaying;
 | |
|   if Assigned(OnStateChange) then
 | |
|     OnStateChange(Self);
 | |
| 
 | |
|   try
 | |
|     Compile;
 | |
|     if IsInvalid then exit;
 | |
| 
 | |
|     Compiler.GetOutput({%H-}s);
 | |
|     if not Exec.LoadData(s) then // Load the data from the Data string.
 | |
|       exit;
 | |
|     Compiler.GetDebugOutput({%H-}s2);
 | |
|     Exec.LoadDebugData(s2);
 | |
| 
 | |
|     Exec.SynEdit := aEditor as TCustomSynEdit;
 | |
|     try
 | |
|       Exec.RunScript;
 | |
|     except
 | |
|       on e: Exception do
 | |
|         IDEMessagesWindow.AddCustomMessage(mluError,Format('%s: %s', [e.ClassName, e.Message]));
 | |
|     end;
 | |
|     if Exec.ExceptionCode <> erNoError then begin
 | |
|       ExObj := Exec.ExceptionObject;
 | |
|       if ExObj <> nil then
 | |
|         s := ExObj.ClassName
 | |
|       else
 | |
|         s := '<nil>';
 | |
|       s2 := '';
 | |
|       i := 0;
 | |
|       x := 0;
 | |
|       y := 0;
 | |
|       Exec.TranslatePositionEx(Exec.ExceptionProcNo, Exec.ExceptionPos, i, x, y, s2);
 | |
|       if IDEMessagesWindow <> nil then
 | |
|         IDEMessagesWindow.AddCustomMessage(mluError,Format('%s: "%s" at %d/%d', [s, Exec.ExceptionString, x,y]));
 | |
|     end;
 | |
| 
 | |
|   finally
 | |
|     FState := emStopped;
 | |
|     if Assigned(OnStateChange) then
 | |
|       OnStateChange(Self);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.DoStop;
 | |
| begin
 | |
| 
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.DoPause;
 | |
| begin
 | |
|   // Not supported
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.DoResume;
 | |
| begin
 | |
|   // Not supported
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.Compile;
 | |
| var
 | |
|   i: Integer;
 | |
| begin
 | |
|   FHasError := False;
 | |
|   FErrorMsg := '';
 | |
|   Compiler.Clear;
 | |
| 
 | |
| 
 | |
|   if not Compiler.Compile(FSource) then
 | |
|   begin
 | |
|     for i := 0 to Compiler.MsgCount -1 do
 | |
|       FErrorMsg := FErrorMsg + Compiler.Msg[i].MessageToString + LineEnding;
 | |
|     FHasError := True;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.FixBeginEnd;
 | |
| begin
 | |
|   if (not IsEmpty) and (not IsInvalid) then
 | |
|   begin
 | |
|     if (pos('begin', FSource) < 1) or (pos('end.', FSource) < 1) then
 | |
|       FSource := 'begin' + LineEnding + FSource + 'end.'+LineEnding;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| constructor TEMSEditorMacro.Create(aOwner: TComponent);
 | |
| begin
 | |
|   FState := emStopped;
 | |
| end;
 | |
| 
 | |
| destructor TEMSEditorMacro.Destroy;
 | |
| begin
 | |
|   inherited Destroy;
 | |
|   FreeAndNil(FKeyBinding);
 | |
|   FreeAndNil(FPrivateExec);
 | |
|   FreeAndNil(FPrivateCompiler);
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.AssignEventsFrom(AMacroRecorder: TEditorMacro);
 | |
| begin
 | |
|   FHasError := False;
 | |
|   if AMacroRecorder = nil then
 | |
|     Clear
 | |
|   else
 | |
|     FSource := AMacroRecorder.GetAsSource;
 | |
| 
 | |
|   if not(AMacroRecorder is TEMSEditorMacro) then
 | |
|     FixBeginEnd;
 | |
|   Compile;
 | |
|   DoChanged;
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.Clear;
 | |
| begin
 | |
|   FSource := '';
 | |
|   FHasError := False;
 | |
|   DoChanged;
 | |
| end;
 | |
| 
 | |
| function TEMSEditorMacro.GetAsSource: String;
 | |
| begin
 | |
|   Result := FSource;
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.SetFromSource(const AText: String);
 | |
| begin
 | |
|   FSource := AText;
 | |
|   Compile;
 | |
|   DoChanged;
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.WriteToXmlConf(AConf: TXMLConfig; const APath: String);
 | |
| begin
 | |
|   AConf.SetValue(APath + 'Name', MacroName);
 | |
|   AConf.SetValue(APath + 'Code/Value', GetAsSource);
 | |
| 
 | |
|   if (KeyBinding <> nil) then
 | |
|     KeyBinding.WriteToXmlConf(AConf, APath);
 | |
| end;
 | |
| 
 | |
| procedure TEMSEditorMacro.ReadFromXmlConf(AConf: TXMLConfig; const APath: String);
 | |
| var
 | |
|   s: String;
 | |
| begin
 | |
|   s := AConf.GetValue(APath + 'Code/Value', '');
 | |
|   FSource := s;
 | |
|   FixBeginEnd;
 | |
|   Compile;
 | |
|   s := AConf.GetValue(APath + 'Name', '');
 | |
|   if s <> '' then MacroName := s;
 | |
| 
 | |
|   if (not FHasError) and (IsEmpty) then begin
 | |
|     FHasError := True;
 | |
|     FErrorMsg := s;
 | |
|   end;
 | |
| 
 | |
|   if (KeyBinding <> nil) then
 | |
|     KeyBinding.ReadFromXmlConf(AConf, APath);
 | |
| 
 | |
|   DoChanged;
 | |
| end;
 | |
| 
 | |
| function TEMSEditorMacro.IsEmpty: Boolean;
 | |
| begin
 | |
|   Result := FSource = '';
 | |
| end;
 | |
| 
 | |
| function TEMSEditorMacro.IsInvalid: Boolean;
 | |
| begin
 | |
|   Result := FHasError;
 | |
| end;
 | |
| 
 | |
| function TEMSEditorMacro.IsRecording(AnEditor: TWinControl): Boolean;
 | |
| begin
 | |
|   Result := False;
 | |
| end;
 | |
| 
 | |
| initialization
 | |
|   CreateCompiler;
 | |
|   CreateExec;
 | |
| 
 | |
| finalization
 | |
|   FreeAndNil(GlobalExec);
 | |
|   FreeAndNil(GlobalCompiler);
 | |
|   FreeAndNil(ConfFile);
 | |
| 
 | |
| end.
 | |
| 
 | 
