plugins/console: Fixed tab completion after linenoise update.

* Can now cycle through candidates by repeatedly pushing Tab.
* Also cleaned up Lua thread context object a little, and made it
  possible to pass any Lua object as a status value.
This commit is contained in:
Vas Crabb 2023-03-08 03:18:21 +11:00
parent 3f99355acc
commit 6dea565343
3 changed files with 109 additions and 88 deletions

View File

@ -43,8 +43,11 @@ function console.startplugin()
local ln = require('linenoise')
ln.setcompletion(
function(c, str)
status = str
yield()
ln.addcompletion(c, str)
for k, v in pairs(status) do
ln.addcompletion(c, v)
end
end)
local ret = ln.linenoise('$PROMPT')
if ret == nil then
@ -191,11 +194,8 @@ function console.startplugin()
return curstring, strs, expr:match("([%.:%w%(%)%[%]_]-)([:%.%[%(])" .. word .. "$")
end
local function get_completions(line, endpos)
local function get_completions(line)
matches = {}
local endstr = line:sub(endpos + 1, -1)
line = line:sub(1, endpos)
endstr = endstr or ""
local start, word = line:match("^(.*[ \t\n\"\\'><=;:%+%-%*/%%^~#{}%(%)%[%].,])(.-)$")
if not start then
start = ""
@ -206,16 +206,18 @@ function console.startplugin()
local str, strs, expr, sep = simplify_expression(line, word)
contextual_list(expr, sep, str, word, strs)
if #matches > 1 then
print("\n")
for k, v in pairs(matches) do
print(v)
end
return "\x01" .. "-1"
if #matches == 0 then
return { line }
elseif #matches == 1 then
return start .. matches[1] .. endstr .. "\x01" .. (#start + #matches[1])
return { start .. matches[1] }
end
return "\x01" .. "-1"
print("")
result = { }
for k, v in pairs(matches) do
print(v)
table.insert(result, start .. v)
end
return result
end
emu.register_start(function()
@ -249,7 +251,7 @@ function console.startplugin()
-- ln.refresh() FIXME: how to replicate this now that the API has been removed?
end
if conth.yield then
conth:continue(get_completions(conth.result:match("([^\x01]*)\x01(.*)")))
conth:continue(get_completions(conth.result))
return
elseif conth.busy then
return

View File

@ -31,7 +31,9 @@
#include "corestr.h"
#include "notifier.h"
#include <condition_variable>
#include <cstring>
#include <mutex>
#include <thread>
@ -58,6 +60,91 @@ struct lua_engine::devenum
namespace {
struct thread_context
{
private:
sol::object m_result;
std::mutex m_guard;
std::condition_variable m_sync;
public:
bool m_busy = false;
bool m_yield = false;
bool start(char const *scr)
{
std::unique_lock<std::mutex> lock(m_guard);
if (m_busy)
return false;
std::string script(scr);
std::thread th(
[this, script = std::string(scr)] ()
{
sol::state thstate;
thstate.open_libraries();
thstate["package"]["preload"]["zlib"] = &luaopen_zlib;
thstate["package"]["preload"]["lfs"] = &luaopen_lfs;
thstate["package"]["preload"]["linenoise"] = &luaopen_linenoise;
sol::load_result res = thstate.load(script);
if (res.valid())
{
sol::protected_function func = res.get<sol::protected_function>();
thstate.set_function(
"yield",
[this, &thstate]()
{
std::unique_lock<std::mutex> lock(m_guard);
m_result = thstate["status"];
m_yield = true;
m_sync.wait(lock);
m_yield = false;
thstate["status"] = m_result;
});
auto ret = func();
if (ret.valid())
{
m_result = ret.get<sol::object>();
}
else
{
sol::error err = ret;
osd_printf_error("[LUA ERROR] in thread: %s\n", err.what());
}
}
else
{
sol::error err = res;
osd_printf_error("[LUA ERROR] when loading script for thread: %s\n", err.what());
}
m_busy = false; // FIXME: shouldn't do this when not holding the lock
});
m_busy = true;
m_yield = false;
th.detach();
return true;
}
void resume(sol::object val)
{
std::unique_lock<std::mutex> lock(m_guard);
if (m_yield)
{
m_result = val;
m_sync.notify_all();
}
}
sol::object result()
{
std::unique_lock<std::mutex> lock(m_guard);
if (m_busy && !m_yield)
return sol::lua_nil;
else
return m_result;
}
};
struct image_interface_formats
{
image_interface_formats(device_image_interface &i) : image(i) { }
@ -883,70 +970,12 @@ void lua_engine::initialize()
* thread.yield - check if thread is yielded
*/
auto thread_type = emu.new_usertype<context>("thread", sol::call_constructor, sol::constructors<sol::types<>>());
thread_type.set("start", [](context &ctx, const char *scr) {
std::string script(scr);
if (ctx.busy)
return false;
std::thread th([&ctx, script]() {
sol::state thstate;
thstate.open_libraries();
thstate["package"]["preload"]["zlib"] = &luaopen_zlib;
thstate["package"]["preload"]["lfs"] = &luaopen_lfs;
thstate["package"]["preload"]["linenoise"] = &luaopen_linenoise;
sol::load_result res = thstate.load(script);
if(res.valid())
{
sol::protected_function func = res.get<sol::protected_function>();
thstate["yield"] = [&ctx, &thstate]() {
std::mutex m;
std::unique_lock<std::mutex> lock(m);
ctx.result = thstate["status"];
ctx.yield = true;
ctx.sync.wait(lock);
ctx.yield = false;
thstate["status"] = ctx.result;
};
auto ret = func();
if (ret.valid())
{
const char *tmp = ret.get<const char *>();
if (tmp != nullptr)
ctx.result = tmp;
else
osd_printf_error("[LUA ERROR] in thread: return value must be string\n");
}
else
{
sol::error err = ret;
osd_printf_error("[LUA ERROR] in thread: %s\n", err.what());
}
}
else
{
sol::error err = res;
osd_printf_error("[LUA ERROR] when loading script for thread: %s\n", err.what());
}
ctx.busy = false;
});
ctx.busy = true;
ctx.yield = false;
th.detach();
return true;
});
thread_type.set("continue", [](context &ctx, const char *val) {
if (!ctx.yield)
return;
ctx.result = val;
ctx.sync.notify_all();
});
thread_type.set("result", sol::property([](context &ctx) -> std::string {
if (ctx.busy && !ctx.yield)
return "";
return ctx.result;
}));
thread_type.set("busy", sol::readonly(&context::busy));
thread_type.set("yield", sol::readonly(&context::yield));
auto thread_type = emu.new_usertype<thread_context>("thread", sol::call_constructor, sol::constructors<sol::types<>>());
thread_type.set_function("start", &thread_context::start);
thread_type.set_function("continue", &thread_context::resume);
thread_type["result"] = sol::property(&thread_context::result);
thread_type["busy"] = sol::readonly(&thread_context::m_busy);
thread_type["yield"] = sol::readonly(&thread_context::m_yield);
/* save_item library

View File

@ -12,7 +12,6 @@
#pragma once
#include <condition_variable>
#include <functional>
#include <map>
#include <memory>
@ -161,15 +160,6 @@ private:
unsigned int stride;
};
struct context
{
context() { busy = false; yield = false; }
std::string result;
std::condition_variable sync;
bool busy;
bool yield;
};
// internal state
lua_State *m_lua_state;
std::unique_ptr<sol::state_view> m_sol_state;