Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Lua REPL handling of statements #4026

Merged
merged 6 commits into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/BizHawk.Client.Common/lua/ILuaLibraries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@ INamedLuaFunction CreateAndRegisterNamedFunction(

void SpawnAndSetFileThread(string pathToLoad, LuaFile lf);

void ExecuteString(string command);
/// <summary>
/// Executes Lua code. Automatically prepends <see langword="return"/> statement if possible.
/// </summary>
/// <returns>
/// Values returned by the Lua script, if any.
/// </returns>
object[] ExecuteString(string command);

(bool WaitForFrame, bool Terminated) ResumeScript(LuaFile lf);
}
Expand Down
20 changes: 8 additions & 12 deletions src/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1373,7 +1373,6 @@ private void InputBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
string consoleBeforeCall = OutputBox.Text;
var rawCommand = InputBox.Text;
InputBox.Clear();
InputBox.Refresh(); // if the command is something like `client.seekframe`, the Lua Console (and MainForm) will freeze until it finishes, so at least make it obvious that the Enter press was received
Expand All @@ -1388,18 +1387,15 @@ private void InputBox_KeyDown(object sender, KeyEventArgs e)

LuaSandbox.Sandbox(null, () =>
{
LuaImp.ExecuteString($"console.log({rawCommand})");
}, () =>
{
LuaSandbox.Sandbox(null, () =>
var prevMessageCount = _messageCount;
var results = LuaImp.ExecuteString(rawCommand);
// empty array if the command was e.g. a variable assignment or a loop without return statement
// "void" functions return a single null
// if output didn't change, Print will take care of writing out "(no return)"
if (results is not ([ ] or [ null ]) || _messageCount == prevMessageCount)
{
LuaImp.ExecuteString(rawCommand);

if (OutputBox.Text == consoleBeforeCall)
{
WriteLine("Command successfully executed");
}
});
LuaLibraries.Print(results);
}
});

_messageCount = 0;
Expand Down
25 changes: 23 additions & 2 deletions src/BizHawk.Client.EmuHawk/tools/Lua/LuaLibraries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,29 @@ public LuaThread SpawnCoroutine(string file)
public void SpawnAndSetFileThread(string pathToLoad, LuaFile lf)
=> lf.Thread = SpawnCoroutine(pathToLoad);

public void ExecuteString(string command)
=> _lua.DoString(command);
public object[] ExecuteString(string command)
{
const string ChunkName = "input"; // shows up in error messages

// Use LoadString to separate parsing and execution, to tell syntax errors and runtime errors apart
LuaFunction func;
try
{
// Adding a return is necessary to get out return values of functions and turn expressions ("1+1" etc.) into valid statements
func = _lua.LoadString($"return {command}", ChunkName);
}
catch (Exception)
{
// command may be a valid statement without the added "return"
// if previous attempt couldn't be parsed, run the raw command
return _lua.DoString(command, ChunkName);
}

using (func)
{
return func.Call();
}
}

public (bool WaitForFrame, bool Terminated) ResumeScript(LuaFile lf)
{
Expand Down