diff --git a/src/plugins/lua/luaengine.cpp b/src/plugins/lua/luaengine.cpp index be589a3b146..10d75d0979a 100644 --- a/src/plugins/lua/luaengine.cpp +++ b/src/plugins/lua/luaengine.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -20,6 +21,25 @@ using namespace Utils; namespace Lua { +class LuaInterfaceImpl : public Utils::LuaInterface +{ + LuaEngine *m_engine; + +public: + LuaInterfaceImpl(LuaEngine *engine) + : m_engine(engine) + { + Utils::setLuaInterface(this); + } + ~LuaInterfaceImpl() override { Utils::setLuaInterface(nullptr); } + + expected_str> runScript( + const QString &script, const QString &name) override + { + return m_engine->runScript(script, name); + } +}; + class LuaEnginePrivate { public: @@ -29,6 +49,8 @@ public: QList> m_autoProviders; QMap> m_hooks; + + std::unique_ptr m_luaInterface; }; static LuaEngine *s_instance = nullptr; @@ -43,6 +65,7 @@ LuaEngine::LuaEngine() : d(new LuaEnginePrivate()) { s_instance = this; + d->m_luaInterface.reset(new LuaInterfaceImpl(this)); } LuaEngine::~LuaEngine() @@ -50,6 +73,70 @@ LuaEngine::~LuaEngine() s_instance = nullptr; } +class LuaStateImpl : public Utils::LuaState +{ +public: + sol::state lua; +}; + +// Runs the gives script in a new Lua state. The returned Object manages the lifetime of the state. +std::unique_ptr LuaEngine::runScript(const QString &script, const QString &name) +{ + std::unique_ptr opaque = std::make_unique(); + + opaque->lua.open_libraries( + sol::lib::base, + sol::lib::bit32, + sol::lib::coroutine, + sol::lib::debug, + sol::lib::io, + sol::lib::math, + sol::lib::os, + sol::lib::package, + sol::lib::string, + sol::lib::table, + sol::lib::utf8); + + opaque->lua["print"] = [prefix = name, printToOutputPane = true](sol::variadic_args va) { + const QString msg = variadicToStringList(va).join("\t"); + + qDebug().noquote() << "[" << prefix << "]" << msg; + if (printToOutputPane) { + static const QString p + = ansiColoredText("[" + prefix + "]", creatorTheme()->color(Theme::Token_Text_Muted)); + Core::MessageManager::writeSilently(QString("%1 %2").arg(p, msg)); + } + }; + + opaque->lua.new_usertype( + "PluginSpec", sol::no_constructor, "name", sol::property([](ScriptPluginSpec &self) { + return self.name; + })); + + opaque->lua["PluginSpec"] = ScriptPluginSpec{name, {}, std::make_unique()}; + + for (const auto &[name, func] : d->m_providers.asKeyValueRange()) { + opaque->lua["package"]["preload"][name.toStdString()] = + [func = func](const sol::this_state &s) { return func(s); }; + } + + for (const auto &func : d->m_autoProviders) + func(opaque->lua); + + auto result + = opaque->lua + .safe_script(script.toStdString(), sol::script_pass_on_error, name.toStdString()); + + if (!result.valid()) { + sol::error err = result; + qWarning() << "Failed to run script" << name << ":" << QString::fromUtf8(err.what()); + Core::MessageManager::writeFlashing( + tr("Failed to run script %1: %2").arg(name, QString::fromUtf8(err.what()))); + } + + return opaque; +} + void LuaEngine::registerProvider(const QString &packageName, const PackageProvider &provider) { QTC_ASSERT(!instance().d->m_providers.contains(packageName), return); diff --git a/src/plugins/lua/luaengine.h b/src/plugins/lua/luaengine.h index ef51bbc9b0a..688c728808e 100644 --- a/src/plugins/lua/luaengine.h +++ b/src/plugins/lua/luaengine.h @@ -10,6 +10,7 @@ #include #include +#include #include @@ -108,6 +109,9 @@ public: return {}; } + // Runs the given script in a new Lua state. The returned Object manages the lifetime of the state. + std::unique_ptr runScript(const QString &script, const QString &name); + protected: Utils::expected_str connectHooks( sol::state_view lua, const sol::table &table, const QString &path);