From 43f2ee3e1137c58ce7bb026ab309d5a7e2b1ee10 Mon Sep 17 00:00:00 2001 From: Basile Burg Date: Mon, 3 Nov 2014 08:13:23 +0100 Subject: [PATCH] added input to stdin support to runnable modules --- lazproj/coedit.lpi | 15 +++++++- src/ce_main.lfm | 42 +++++++++++++++++++++ src/ce_main.pas | 90 ++++++++++++++++++++++++++++++++++---------- src/ce_procinput.lfm | 59 +++++++++++++++++++++++++++++ src/ce_procinput.pas | 71 ++++++++++++++++++++++++++++++++++ 5 files changed, 257 insertions(+), 20 deletions(-) create mode 100644 src/ce_procinput.lfm create mode 100644 src/ce_procinput.pas diff --git a/lazproj/coedit.lpi b/lazproj/coedit.lpi index 66d0ec21..382f8dfe 100644 --- a/lazproj/coedit.lpi +++ b/lazproj/coedit.lpi @@ -52,6 +52,11 @@ + + + + + @@ -135,7 +140,7 @@ - + @@ -304,6 +309,14 @@ + + + + + + + + diff --git a/src/ce_main.lfm b/src/ce_main.lfm index e253d632..12ecd70f 100644 --- a/src/ce_main.lfm +++ b/src/ce_main.lfm @@ -1829,6 +1829,42 @@ object CEMainForm: TCEMainForm Caption = 'Windows' object mnuLayout: TMenuItem Caption = 'Layout' + Bitmap.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000064000000640000000000000000000000FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00BE7D + 558FBD7C54B5BC7B53EEBA7953FFB67751FFB47651FFB17450FFAD724FFFAA71 + 4FFFA86F4EFFA76E4DFFA66E4DFEA46E4DF1A66E4DC4FFFFFF00FFFFFF00C080 + 55DEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA66E4DEDFFFFFF00FFFFFF00C282 + 58FEFFFFFFFFAF672CFFAD642BFFAD642BFFFFFFFFFF56AB5DFF51A657FF4CA1 + 52FF479C4CFF449849FF409445FFFFFFFFFFA76F4EFEFFFFFF00FFFFFF00C486 + 5AFFFFFFFFFFAF672CFFCD9F74FFAD642BFFFFFFFFFF4B9F50FF70C97BFF70C9 + 7BFF6FC87AFF6EC778FF3E9343FFFFFFFFFFA8714FFFFFFFFF00FFFFFF00C586 + 5BFFFFFFFFFFAF672CFFCDA075FFAD642BFFFFFFFFFF4EA354FF73CA7DFF72CA + 7DFF71C97CFF6FC87AFF419546FFFFFFFFFFAC7250FFFFFFFF00FFFFFF00C788 + 5AFFFFFFFFFFB46E33FFD0A57EFFB26C2EFFFFFFFFFF50A556FF74CB7EFF73CB + 7DFF72CA7CFF70C97BFF449949FFFFFFFFFFB27651FFFFFFFF00FFFFFF00C88A + 5BFFFFFFFFFFB77640FFD1A683FFB67338FFFFFFFFFF52A859FF73CB7DFF73CB + 7DFF72CA7CFF70C97BFF479C4DFFFFFFFFFFB67853FFFFFFFF00FFFFFF00CA8C + 5CFFFFFFFFFFBC7F54FFBA7D4CFFBA7C4AFFFFFFFFFF469B4CFF53A859FF51A6 + 57FF4FA455FF4DA253FF4A9F50FFFFFFFFFFB97C54FFFFFFFF00FFFFFF00CB8E + 5DFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBC7E55FFFFFFFF00FFFFFF00CC90 + 5EFAFFFFFFFFEDC49BFFEDC49BFFEDC49BFFEDC49BFFEDC49BFFEDC49BFFEDC4 + 9BFFEDC49BFFEDC49BFFEDC49BFFFFFFFFFFBE8157FFFFFFFF00FFFFFF00CD91 + 5FF0FFFFFFFFEDC49CFFF4DAC1FFF4DAC1FFF4DBC2FFF4DBC2FFF4DBC2FFF4DB + C2FFF4DBC2FFF4DBC2FFEDC49BFFFFFFFFFFC18458FEFFFFFF00FFFFFF00CE93 + 5FD8FFFFFFFFEDC49CFFEDC49CFFEDC49CFFEDC49BFFEDC49BFFEDC49BFFEDC4 + 9BFFEDC49BFFEDC49BFFEDC49BFFFFFFFFFFC68C61F8FFFFFF00FFFFFF00CE93 + 5F9BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD6AA89FFFFFFFF00FFFFFF00CE93 + 5F71CE935F90CE935FCCCE935FFFCD935FFECD925FFFCC905FFFCD9261FFCD93 + 63FFCB9161FFCB8E5FEFCA9264CBD8AE8BFFD7AC8BFFFFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 + } ImageIndex = 31 end end @@ -3149,4 +3185,10 @@ object CEMainForm: TCEMainForm OnShowHint = ApplicationProperties1ShowHint left = 96 end + object OutputTimer: TIdleTimer + Interval = 100 + OnTimer = OutputTimerTimer + left = 128 + top = 1 + end end diff --git a/src/ce_main.pas b/src/ce_main.pas index 35667f18..d942be9a 100644 --- a/src/ce_main.pas +++ b/src/ce_main.pas @@ -5,13 +5,13 @@ unit ce_main; interface uses - Classes, SysUtils, FileUtil, SynEditKeyCmds, SynHighlighterLFM, Forms, + Classes, SysUtils, FileUtil, SynEditKeyCmds, SynHighlighterLFM, Forms, asyncprocess, AnchorDocking, AnchorDockStorage, AnchorDockOptionsDlg, Controls, Graphics, Dialogs, Menus, ActnList, ExtCtrls, process, XMLPropStorage, ComCtrls, dynlibs, ce_common, ce_dmdwrap, ce_project, ce_dcd, ce_plugin, ce_synmemo, ce_widget, ce_messages, ce_interfaces, ce_editor, ce_projinspect, ce_projconf, ce_search, ce_staticexplorer, ce_miniexplorer, ce_libman, ce_libmaneditor, ce_customtools, - ce_observer, ce_writableComponent, ce_toolseditor; + ce_observer, ce_writableComponent, ce_toolseditor, ce_procinput; type @@ -60,6 +60,7 @@ type actEdUnIndent: TAction; Actions: TActionList; ApplicationProperties1: TApplicationProperties; + OutputTimer: TIdleTimer; imgList: TImageList; mainMenu: TMainMenu; MenuItem1: TMenuItem; @@ -170,6 +171,7 @@ type var CanShow: Boolean; var HintInfo: THintInfo); procedure FormCloseQuery(Sender: TObject; var CanClose: boolean); procedure FormDropFiles(Sender: TObject; const FileNames: array of String); + procedure OutputTimerTimer(Sender: TObject); private fDoc: TCESynMemo; @@ -189,8 +191,11 @@ type fProjMru: TMruFileList; fFileMru: TMruFileList; fLibMan: TLibraryManager; + fPrInpWidg: TCEProcInputWidget; fTools: TCETools; + fRunProc: TProcess;// TAsyncProcess; + // ICEMultiDocObserver procedure docNew(const aDoc: TCESynMemo); procedure docClosing(const aDoc: TCESynMemo); @@ -224,12 +229,14 @@ type procedure LoadDocking; procedure SaveDocking; procedure KillPlugs; + procedure FreeRunnableProc; // widget interfaces subroutines procedure checkWidgetActions(const aWidget: TCEWidget); procedure widgetShowFromAction(sender: TObject); // run & exec sub routines + procedure asyncprocOutput(sender: TObject); procedure ProcessOutputToMsg(const aProcess: TProcess;aCtxt: TMessageContext = mcUnknown); procedure compileAndRunFile(const edIndex: NativeInt; const runArgs: string = ''); procedure compileProject(const aProject: TCEProject); @@ -457,6 +464,7 @@ begin fExplWidg := TCEMiniExplorerWidget.create(self); fLibMWidg := TCELibManEditorWidget.create(self); fTlsEdWidg:= TCEToolsEditorWidget.create(self); + fPrInpWidg:= TCEProcInputWidget.create(self); fWidgList.addWidget(@fMesgWidg); fWidgList.addWidget(@fEditWidg); @@ -467,6 +475,7 @@ begin fWidgList.addWidget(@fExplWidg); fWidgList.addWidget(@fLibMWidg); fWidgList.addWidget(@fTlsEdWidg); + fWidgList.addWidget(@fPrInpWidg); for widg in fWidgList do begin @@ -661,6 +670,21 @@ begin fPlugList.Free; end; +procedure TCEMainForm.FreeRunnableProc; +var + fname: string; +begin + if fRunProc = nil then + exit; + // + fname := fRunProc.Executable; + fRunProc.Terminate(0); + fRunProc.Free; + fRunProc := nil; + if fileExists(fname) then + sysutils.DeleteFile(fname); +end; + destructor TCEMainForm.destroy; begin SaveSettings; @@ -671,6 +695,7 @@ begin fProjMru.Free; fFileMru.Free; fProject.Free; + FreeRunnableProc; // EntitiesConnector.removeObserver(self); inherited; @@ -1177,21 +1202,25 @@ var dt: PMessageItemData; i: NativeInt; msg: string; + hasRead: boolean; begin If not (poUsePipes in aProcess.Options) then exit; // readCnt := 0; + readSz := 0; + hasRead := false; ioBuffSz := aProcess.PipeBufferSize; str := TMemorystream.Create; lns := TStringList.Create; - readSz := 0; try - repeat - str.SetSize(readSz + ioBuffSz); + while aProcess.Output.NumBytesAvailable <> 0 do + begin + hasRead := true; + str.Size := str.Size + ioBuffSz; readCnt := aProcess.Output.Read((str.Memory + readSz)^, ioBuffSz); - Inc(readSz, readCnt); - until readCnt = 0; - Str.SetSize(readSz); + readSz += readCnt; + end; + str.Size := readSz; lns.LoadFromStream(Str); for i:= 0 to lns.Count-1 do begin msg := lns.Strings[i]; @@ -1209,19 +1238,36 @@ begin finally str.Free; lns.Free; - fMesgWidg.scrollToBack; + if hasRead then + fMesgWidg.scrollToBack; end; end; +procedure TCEMainForm.OutputTimerTimer(Sender: TObject); +begin + if fRunProc <> nil then + ProcessOutputToMsg(fRunProc, mcEditor); +end; + +procedure TCEMainForm.asyncprocOutput(sender: TObject); +begin + //ProcessOutputToMsg(TAsyncProcess(sender), mcEditor); +end; + procedure TCEMainForm.compileAndRunFile(const edIndex: NativeInt; const runArgs: string = ''); var editor: TCESynMemo; dmdproc: TProcess; - runproc: TProcess; + //runproc: TProcess; fname: string; begin + + FreeRunnableProc; + fRunProc := TProcess.Create(nil); + //fRunProc := TAsyncProcess.Create(nil); + //fRunProc.OnReadData := @asyncprocOutput; + dmdproc := TProcess.Create(nil); - runproc := TProcess.Create(nil); editor := fEditWidg.editor[edIndex]; try @@ -1250,13 +1296,17 @@ begin begin ProcessOutputToMsg(dmdproc, mcEditor); fMesgWidg.addCeInf(editor.fileName + ' successfully compiled', mcEditor ); - runproc.Options := [poStderrToOutPut, poUsePipes]; - runproc.CurrentDirectory := extractFilePath(runProc.Executable); - runproc.Parameters.DelimitedText := expandSymbolicString(runArgs); - runproc.Executable := fname + exeExt; - runproc.Execute; - repeat ProcessOutputToMsg(runproc, mcEditor) until not runproc.Running; - sysutils.DeleteFile(fname + exeExt); + fRunProc.Options := fRunProc.Options + [poStderrToOutPut, poUsePipes]; + fRunProc.CurrentDirectory := extractFilePath(fRunProc.Executable); + fRunProc.Parameters.DelimitedText := expandSymbolicString(runArgs); + fRunProc.Executable := fname + exeExt; + fPrInpWidg.process := fRunProc; + fRunProc.Execute; + + //ProcessOutputToMsg(fRunProc, mcEditor); + + //repeat ProcessOutputToMsg(runproc, mcEditor) until not runproc.Running; + //sysutils.DeleteFile(fname + exeExt); sysutils.DeleteFile(fname + objExt); end else begin @@ -1264,9 +1314,11 @@ begin fMesgWidg.addCeErr(editor.fileName + ' has not been compiled', mcEditor ); end; + //fPrInpWidg.process := nil; + finally dmdproc.Free; - runproc.Free; + //runproc.Free; end; end; diff --git a/src/ce_procinput.lfm b/src/ce_procinput.lfm new file mode 100644 index 00000000..69eeb5db --- /dev/null +++ b/src/ce_procinput.lfm @@ -0,0 +1,59 @@ +inherited CEProcInputWidget: TCEProcInputWidget + Left = 1427 + Height = 61 + Top = 335 + Width = 481 + Caption = 'Process input' + ClientHeight = 61 + ClientWidth = 481 + inherited Back: TPanel + Height = 61 + Width = 481 + ClientHeight = 61 + ClientWidth = 481 + inherited Content: TPanel + Height = 61 + Width = 481 + ClientHeight = 61 + ClientWidth = 481 + object txtInp: TEdit[0] + Left = 4 + Height = 27 + Top = 30 + Width = 394 + Align = alClient + BorderSpacing.Left = 4 + BorderSpacing.Top = 2 + BorderSpacing.Right = 4 + BorderSpacing.Bottom = 4 + OnKeyPress = txtInpKeyPress + TabOrder = 0 + end + object btnSend: TButton[1] + Left = 402 + Height = 27 + Top = 30 + Width = 75 + Align = alRight + BorderSpacing.Top = 2 + BorderSpacing.Right = 4 + BorderSpacing.Bottom = 4 + Caption = 'Send' + OnClick = btnSendClick + TabOrder = 1 + end + object txtExeName: TStaticText[2] + Left = 4 + Height = 22 + Top = 4 + Width = 473 + Align = alTop + BorderSpacing.Around = 4 + BorderStyle = sbsSunken + Caption = 'no process' + TabOrder = 2 + Transparent = False + end + end + end +end diff --git a/src/ce_procinput.pas b/src/ce_procinput.pas new file mode 100644 index 00000000..1723dbca --- /dev/null +++ b/src/ce_procinput.pas @@ -0,0 +1,71 @@ +unit ce_procinput; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls, + Menus, StdCtrls, ce_widget, process, ce_common; + +type + + { TCEProcInputWidget } + TCEProcInputWidget = class(TCEWidget) + btnSend: TButton; + txtInp: TEdit; + txtExeName: TStaticText; + procedure btnSendClick(Sender: TObject); + procedure txtInpKeyPress(Sender: TObject; var Key: char); + private + fProc: TProcess; + procedure sendInput; + procedure setProc(const aValue: TProcess); + public + property process: TProcess read fProc write setProc; + end; + +implementation +{$R *.lfm} + +procedure TCEProcInputWidget.setProc(const aValue: TProcess); +begin + txtExeName.Caption := 'no process'; + fProc := nil; + if aValue = nil then + exit; + if not (poUsePipes in aValue.Options) then + exit; + fProc := aValue; + txtExeName.Caption := shortenPath(fProc.Executable); +end; + +procedure TCEProcInputWidget.sendInput; +var + inp: string; +begin + inp := txtInp.Text + lineEnding; + fProc.Input.Write(inp[1], length(inp)); + txtInp.Text := ''; +end; + +procedure TCEProcInputWidget.txtInpKeyPress(Sender: TObject; var Key: char); +begin + if fProc = nil then + exit; + if key <> lineEnding[1] then + exit; + sendInput; +end; + +procedure TCEProcInputWidget.btnSendClick(Sender: TObject); +begin + if fProc = nil then + exit; + sendInput; +end; + + + +end. +