1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-10 20:42:21 +02:00

Embedded console / terminal support

This commit is contained in:
Brian Fiete 2024-07-23 07:56:23 +02:00
parent 60817eec48
commit 20a8e3327c
28 changed files with 2317 additions and 690 deletions

View file

@ -138,6 +138,14 @@ namespace IDE.Debugger
Allocations = 2
}
public enum OpenFileFlags
{
None,
RedirectStdInput = 1,
RedirectStdOutput = 2,
RedirectStdError = 4
}
public List<Breakpoint> mBreakpointList = new List<Breakpoint>();
public Dictionary<String, StepFilter> mStepFilterList = new Dictionary<String, StepFilter>();
@ -157,7 +165,7 @@ namespace IDE.Debugger
static extern bool Debugger_OpenMiniDump(char8* filename);
[CallingConvention(.Stdcall),CLink]
static extern bool Debugger_OpenFile(char8* launchPath, char8* targetPath, char8* args, char8* workingDir, void* envBlockPtr, int32 envBlockLen, bool hotSwapEnabled);
static extern bool Debugger_OpenFile(char8* launchPath, char8* targetPath, char8* args, char8* workingDir, void* envBlockPtr, int32 envBlockLen, bool hotSwapEnabled, OpenFileFlags openFileFlags);
[CallingConvention(.Stdcall),CLink]
static extern bool Debugger_ComptimeAttach(void* bfCompiler);
@ -165,6 +173,9 @@ namespace IDE.Debugger
[CallingConvention(.Stdcall),CLink]
static extern bool Debugger_Attach(int32 processId, AttachFlags attachFlags);
[CallingConvention(.Stdcall),CLink]
public static extern void Debugger_GetStdHandles(Platform.BfpFile** outStdIn, Platform.BfpFile** outStdOut, Platform.BfpFile** outStdErr);
[CallingConvention(.Stdcall),CLink]
static extern void Debugger_Run();
@ -473,7 +484,7 @@ namespace IDE.Debugger
Debugger_FullReportMemory();
}
public bool OpenFile(String launchPath, String targetPath, String args, String workingDir, Span<char8> envBlock, bool isCompiled, bool hotSwapEnabled)
public bool OpenFile(String launchPath, String targetPath, String args, String workingDir, Span<char8> envBlock, bool isCompiled, bool hotSwapEnabled, OpenFileFlags openFileFlags)
{
DeleteAndNullify!(mRunningPath);
mRunningPath = new String(launchPath);
@ -481,7 +492,7 @@ namespace IDE.Debugger
mIsComptimeDebug = false;
mIsRunningCompiled = isCompiled;
mIsRunningWithHotSwap = hotSwapEnabled;
return Debugger_OpenFile(launchPath, targetPath, args, workingDir, envBlock.Ptr, (int32)envBlock.Length, hotSwapEnabled);
return Debugger_OpenFile(launchPath, targetPath, args, workingDir, envBlock.Ptr, (int32)envBlock.Length, hotSwapEnabled, openFileFlags);
}
public bool ComptimeAttach(BfCompiler compiler)
@ -1175,6 +1186,11 @@ namespace IDE.Debugger
return Debugger_Attach(process.Id, attachFlags);
}
public void GetStdHandles(Platform.BfpFile** outStdIn, Platform.BfpFile** outStdOut, Platform.BfpFile** outStdErr)
{
Debugger_GetStdHandles(outStdIn, outStdOut, outStdErr);
}
public DbgProfiler StartProfiling(int threadId, String desc, int sampleRate)
{
DbgProfiler profiler = new DbgProfiler(Debugger_StartProfiling(threadId, desc, (.)sampleRate));

View file

@ -345,6 +345,9 @@ namespace IDE
bool mProfileCompile = false;
ProfileInstance mProfileCompileProfileId;
Monitor mDebugOutputMonitor = new .() ~ delete _;
String mDebugOutput = new .() ~ delete _;
#if !CLI
public IPCHelper mIPCHelper ~ delete _;
public bool mIPCHadFocus;
@ -3779,7 +3782,7 @@ namespace IDE
}
// Always write to STDOUT even if we're running as a GUI, allowing cases like RunAndWait to pass us a stdout handle
Console.Error.WriteLine("ERROR: {0}", text);
Console.Error.WriteLine("ERROR: {0}", text).IgnoreError();
#if CLI
mFailed = true;
@ -3806,7 +3809,7 @@ namespace IDE
mFailed = true;
OutputLineSmart("ERROR: {0}", text);
Console.Error.WriteLine("ERROR: {0}", text);
Console.Error.WriteLine("ERROR: {0}", text).IgnoreError();
return null;
}
@ -8250,8 +8253,11 @@ namespace IDE
NOP!();
}
mConsolePanel.SysKeyDown(evt);
//mTerminalPanel.SysKeyDown(evt);
if (!evt.mKeyFlags.HeldKeys.HasFlag(.Alt))
{
mConsolePanel.SysKeyDown(evt);
mTerminalPanel.SysKeyDown(evt);
}
if (evt.mHandled)
return;
@ -8426,8 +8432,8 @@ namespace IDE
void SysKeyUp(KeyCode keyCode)
{
//mTerminalPanel.SysKeyUp(keyCode);
mConsolePanel.SysKeyUp(keyCode);
mTerminalPanel.SysKeyUp(keyCode);
}
void ShowOpenFileInSolutionDialog()
@ -8786,8 +8792,10 @@ namespace IDE
return;
StreamReader streamReader = scope StreamReader(fileStream, null, false, 4096);
int count = 0;
while (true)
{
count++;
var buffer = scope String();
if (streamReader.ReadLine(buffer) case .Err)
break;
@ -8816,6 +8824,60 @@ namespace IDE
}
}
void ReadDebugOutputThread(Object obj)
{
FileStream fileStream = (.)obj;
int count = 0;
Loop: while (true)
{
uint8[4096] data = ?;
switch (fileStream.TryRead(data, -1))
{
case .Ok(let len):
if (len == 0)
break Loop;
using (mDebugOutputMonitor.Enter())
{
for (int i < len)
mDebugOutput.Append((char8)data[i]);
}
case .Err:
break Loop;
}
/*var buffer = scope String();
if (streamReader.Read(buffer) case .Err)
break;
using (mDebugOutputMonitor.Enter())
mDebugOutput.Add(new String(buffer));*/
count++;
}
delete fileStream;
}
/*static void ReadDebugErrorThread(Object obj)
{
ExecutionInstance executionInstance = (ExecutionInstance)obj;
FileStream fileStream = scope FileStream();
if (executionInstance.mProcess.AttachStandardError(fileStream) case .Err)
return;
StreamReader streamReader = scope StreamReader(fileStream, null, false, 4096);
while (true)
{
var buffer = scope String();
if (streamReader.ReadLine(buffer) case .Err)
break;
using (IDEApp.sApp.mMonitor.Enter())
executionInstance.mDeferredOutput.Add(new String(buffer));
}
}*/
public enum RunFlags
{
None,
@ -11943,8 +12005,26 @@ namespace IDE
return true;
}
if (!mDebugger.OpenFile(launchPath, targetPath, arguments, workingDir, envBlock, wasCompiled, workspaceOptions.mAllowHotSwapping))
if (mSettings.mDebugConsoleKind == .Embedded)
{
ShowConsole();
mConsolePanel.Attach();
}
if (mSettings.mDebugConsoleKind == .RedirectToImmediate)
{
ShowImmediatePanel();
}
DebugManager.OpenFileFlags openFileFlags = .None;
if ((mSettings.mDebugConsoleKind == .RedirectToImmediate) || (mSettings.mDebugConsoleKind == .RedirectToOutput))
openFileFlags |= .RedirectStdOutput | .RedirectStdError;
if (!mDebugger.OpenFile(launchPath, targetPath, arguments, workingDir, envBlock, wasCompiled, workspaceOptions.mAllowHotSwapping, openFileFlags))
{
if (!mSettings.mAlwaysEnableConsole)
mConsolePanel.Detach();
DeleteAndNullify!(mCompileAndRunStopwatch);
return false;
}
@ -12301,8 +12381,10 @@ namespace IDE
mOutputPanel = new OutputPanel(true);
mOutputPanel.mAutoDelete = false;
mTerminalPanel = new TerminalPanel();
mTerminalPanel .Init();
mTerminalPanel.mAutoDelete = false;
mConsolePanel = new ConsolePanel();
mConsolePanel.Init();
mConsolePanel.mAutoDelete = false;
mImmediatePanel = new ImmediatePanel();
mImmediatePanel.mAutoDelete = false;
@ -12963,6 +13045,37 @@ namespace IDE
{
mDebugger.Update();
Platform.BfpFile* stdOut = null;
Platform.BfpFile* stdError = null;
mDebugger.GetStdHandles(null, &stdOut, &stdError);
if (stdOut != null)
{
FileStream fileStream = new FileStream();
fileStream.Attach(stdOut);
Thread thread = new Thread(new => ReadDebugOutputThread);
thread.Start(fileStream, true);
}
if (stdError != null)
{
FileStream fileStream = new FileStream();
fileStream.Attach(stdError);
Thread thread = new Thread(new => ReadDebugOutputThread);
thread.Start(fileStream, true);
}
using (mDebugOutputMonitor.Enter())
{
if (!mDebugOutput.IsEmpty)
{
mDebugOutput.Replace("\r", "");
if (mSettings.mDebugConsoleKind == .RedirectToOutput)
mOutputPanel.Write(mDebugOutput);
if (mSettings.mDebugConsoleKind == .RedirectToImmediate)
mImmediatePanel.Write(mDebugOutput);
mDebugOutput.Clear();
}
}
runState = mDebugger.GetRunState();
mDebuggerPerformingTask = (runState == .DebugEval) || (runState == .DebugEval_Done) || (runState == .SearchingSymSrv);
@ -13514,6 +13627,8 @@ namespace IDE
var disassemblyPanel = TryGetDisassemblyPanel(false);
if (disassemblyPanel != null)
disassemblyPanel.Disable();
if (!mSettings.mAlwaysEnableConsole)
mConsolePanel.Detach();
mDebugger.DisposeNativeBreakpoints();
mDebugger.Detach();
mDebugger.mIsRunning = false;
@ -14539,6 +14654,16 @@ namespace IDE
RefreshRate = 60;
}
if (mTerminalPanel != null)
{
// Detach terminal if the panel is closed
var terminalTabButton = GetTab(mTerminalPanel);
if (terminalTabButton == null)
{
mTerminalPanel.Detach();
}
}
bool hasFocus = false;
for (let window in mWindows)
{
@ -14622,6 +14747,15 @@ namespace IDE
if (mScriptManager != null)
mScriptManager.Update();
if (mConsolePanel != null)
{
if ((mSettings.mAlwaysEnableConsole) ||
((mSettings.mDebugConsoleKind == .Embedded) && (mDebugger.mIsRunning)))
mConsolePanel.Attach();
else
mConsolePanel.Detach();
}
if (mTestManager != null)
{
mTestManager.Update();

View file

@ -898,8 +898,10 @@ namespace IDE
Add("Show Output", "Ctrl+Alt+O");
Add("Show Profiler", "Ctrl+Alt+P");
Add("Show QuickWatch", "Shift+Alt+W");
Add("Show Threads", "Ctrl+Alt+T");
Add("Show Threads", "Ctrl+Alt+H");
Add("Show Watches", "Ctrl+Alt+W");
Add("Show Console", "Ctrl+Alt+N");
Add("Show Terminal", "Ctrl+Alt+T");
Add("Show Workspace Explorer", "Ctrl+Alt+S");
Add("Start Debugging", "F5");
Add("Start Without Debugging", "Ctrl+F5");
@ -1097,6 +1099,14 @@ namespace IDE
public bool mDependencies;
}
public enum ConsoleKind
{
Native,
Embedded,
RedirectToOutput,
RedirectToImmediate,
}
public bool mLoadedSettings;
public String mSettingFileText ~ delete _;
public DateTime mSettingFileDateTime;
@ -1110,6 +1120,8 @@ namespace IDE
public RecentFiles mRecentFiles = new RecentFiles() ~ delete _;
public String mWakaTimeKey = new .() ~ delete _;
public String mWindowsTerminal = new .("Powershell") ~ delete _;
public ConsoleKind mDebugConsoleKind;
public bool mAlwaysEnableConsole;
public String mEmscriptenPath = new .() ~ delete _;
public bool mEnableDevMode;
public TutorialsFinished mTutorialsFinished = .();
@ -1167,8 +1179,12 @@ namespace IDE
mDebuggerSettings.Serialize(sd);
using (sd.CreateObject("VisualStudio"))
mVSSettings.Serialize(sd);
using (sd.CreateObject("Terminal"))
using (sd.CreateObject("Console"))
{
sd.Add("WindowsTerminal", mWindowsTerminal);
sd.Add("DebugConsole", mDebugConsoleKind);
sd.Add("AlwaysEnableConsole", mAlwaysEnableConsole);
}
using (sd.CreateObject("Wasm"))
sd.Add("EmscriptenPath", mEmscriptenPath);
@ -1258,8 +1274,12 @@ namespace IDE
mDebuggerSettings.Deserialize(sd);
using (sd.Open("VisualStudio"))
mVSSettings.Deserialize(sd);
using (sd.Open("Terminal"))
using (sd.Open("Console"))
{
sd.Get("WindowsTerminal", mWindowsTerminal);
mDebugConsoleKind = sd.GetEnum<ConsoleKind>("DebugConsole", .Native);
mAlwaysEnableConsole = sd.GetBool("AlwaysEnableConsole");
}
using (sd.Open("Wasm"))
sd.Get("EmscriptenPath", mEmscriptenPath);

View file

@ -614,7 +614,7 @@ namespace IDE
var envBlock = scope List<char8>();
Environment.EncodeEnvironmentVariables(envVars, envBlock);
if (!gApp.mDebugger.OpenFile(curProjectInfo.mTestExePath, curProjectInfo.mTestExePath, mTestInstance.mArgs, mTestInstance.mWorkingDir, envBlock, true, false))
if (!gApp.mDebugger.OpenFile(curProjectInfo.mTestExePath, curProjectInfo.mTestExePath, mTestInstance.mArgs, mTestInstance.mWorkingDir, envBlock, true, false, .None))
{
QueueOutputLine("ERROR: Failed debug '{0}'", curProjectInfo.mTestExePath);
TestFailed();

File diff suppressed because it is too large Load diff

View file

@ -332,7 +332,7 @@ namespace IDE.ui
var envBlock = scope List<char8>();
Environment.EncodeEnvironmentVariables(envVars, envBlock);
if (!gApp.mDebugger.OpenFile(targetPath, targetPath, arguments, workingDir, envBlock, false, false))
if (!gApp.mDebugger.OpenFile(targetPath, targetPath, arguments, workingDir, envBlock, false, false, .None))
{
gApp.Fail(scope String()..AppendF("Unable to open executable for debugging: {0}", targetPath));
return;

View file

@ -2991,7 +2991,7 @@ namespace IDE.ui
}
});
item = folderItem.AddItem("Terminal");
item = folderItem.AddItem("External Terminal");
item.mOnMenuItemSelected.Add(new (menu) =>
{
let projectItem = GetSelectedProjectItem();
@ -3023,6 +3023,34 @@ namespace IDE.ui
process.Start(psi).IgnoreError();
}
});
item = folderItem.AddItem("Embedded Terminal");
item.mOnMenuItemSelected.Add(new (menu) =>
{
let projectItem = GetSelectedProjectItem();
String path = scope String();
if (projectItem == null)
{
path.Set(gApp.mWorkspace.mDir);
}
else if (let projectFolder = projectItem as ProjectFolder)
{
if (projectFolder.mParentFolder == null)
{
path.Set(projectFolder.mProject.mProjectDir);
}
else
projectFolder.GetFullImportPath(path);
}
else
projectItem.mParentFolder.GetFullImportPath(path);
if (!path.IsWhiteSpace)
{
gApp.ShowTerminal();
gApp.mTerminalPanel.OpenDirectory(path);
}
});
}
if (projectItem == null)

View file

@ -62,7 +62,7 @@ namespace IDE.ui
AddCategoryItem(root, "Compiler");
AddCategoryItem(root, "Debugger");
AddCategoryItem(root, "Visual Studio");
AddCategoryItem(root, "Terminal");
AddCategoryItem(root, "Console");
AddCategoryItem(root, "Wasm");
if (!gApp.mSettings.mVSSettings.IsConfigured())
@ -170,7 +170,7 @@ namespace IDE.ui
category.Open(true, true);
}
void PopulateTerminalOptions()
void PopulateConsoleOptions()
{
mCurPropertiesTarget = gApp.mSettings;
@ -179,6 +179,8 @@ namespace IDE.ui
category.mIsBold = true;
category.mTextColor = Color.Mult(DarkTheme.COLOR_TEXT, cHeaderColor);
AddPropertiesItem(category, "Windows Terminal", "mWindowsTerminal");
AddPropertiesItem(category, "Debug Console", "mDebugConsoleKind");
AddPropertiesItem(category, "Always Enable Console", "mAlwaysEnableConsole");
category.Open(true, true);
}
@ -438,7 +440,7 @@ namespace IDE.ui
case .VisualStudio:
PopulateVSOptions();
case .Terminal:
PopulateTerminalOptions();
PopulateConsoleOptions();
case .Wasm:
PopulateWasmOptions();
default:

View file

@ -10,15 +10,51 @@ using Beefy.widgets;
using Beefy.events;
using System.Diagnostics;
using Beefy.utils;
using IDE.util;
namespace IDE.ui;
class TerminalPanel : Panel
class TerminalPanel : ConsolePanel
{
public override void Serialize(StructuredData data)
{
base.Serialize(data);
data.Add("Type", "TerminalPanel");
}
public override void Init()
{
var consoleProvider = new BeefConConsoleProvider();
consoleProvider.mBeefConExePath = new $"{gApp.mInstallDir}/BeefCon.exe";
consoleProvider.mTerminalExe = new .(gApp.mSettings.mWindowsTerminal);
mConsoleProvider = consoleProvider;
}
public override void AddedToParent()
{
var consoleProvider = (BeefConConsoleProvider)mConsoleProvider;
consoleProvider.mTerminalExe.Set(gApp.mSettings.mWindowsTerminal);
consoleProvider.mWorkingDir.Set(gApp.mWorkspace.mDir);
mConsoleProvider.Attach();
}
public override void RemovedFromParent(Widget previousParent, WidgetWindow window)
{
}
public override void Update()
{
base.Update();
}
public void OpenDirectory(StringView path)
{
var consoleProvider = (BeefConConsoleProvider)mConsoleProvider;
consoleProvider.mWorkingDir.Set(path);
consoleProvider.Detach();
consoleProvider.Attach();
}
}

File diff suppressed because it is too large Load diff