feat: add REPL for plugins (#6120)
This can be enabled by setting the `.plugins.repl.enabled` setting to true
Close Chatterino and make a backup of your settings.json before attempting any modifications.
```json5
{
"plugins": {
"repl": {
"enabled": true,
},
},
}
```
This commit is contained in:
@@ -31,19 +31,6 @@
|
||||
namespace {
|
||||
using namespace chatterino;
|
||||
|
||||
void logHelper(lua_State *L, Plugin *pl, QDebug stream,
|
||||
const sol::variadic_args &args)
|
||||
{
|
||||
stream.noquote();
|
||||
stream << "[" + pl->id + ":" + pl->meta.name + "]";
|
||||
for (const auto &arg : args)
|
||||
{
|
||||
stream << lua::toString(L, arg.stack_index());
|
||||
// Remove this from our stack
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
QDebug qdebugStreamForLogLevel(lua::api::LogLevel lvl)
|
||||
{
|
||||
auto base =
|
||||
@@ -101,7 +88,7 @@ void c2_log(ThisPluginState L, LogLevel lvl, sol::variadic_args args)
|
||||
lua::StackGuard guard(L);
|
||||
{
|
||||
QDebug stream = qdebugStreamForLogLevel(lvl);
|
||||
logHelper(L, L.plugin(), stream, args);
|
||||
L.plugin()->log(L.state(), lvl, std::move(stream), args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +248,7 @@ void g_print(ThisPluginState L, sol::variadic_args args)
|
||||
(QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE,
|
||||
QT_MESSAGELOG_FUNC, chatterinoLua().categoryName())
|
||||
.debug());
|
||||
logHelper(L, L.plugin(), stream, args);
|
||||
L.plugin()->log(L.state(), LogLevel::Info, std::move(stream), args);
|
||||
}
|
||||
|
||||
void package_loadlib(sol::variadic_args args)
|
||||
|
||||
@@ -218,6 +218,8 @@ std::unordered_set<QString> Plugin::listRegisteredCommands()
|
||||
|
||||
Plugin::~Plugin()
|
||||
{
|
||||
this->onUnloaded();
|
||||
|
||||
for (auto *timer : this->activeTimeouts)
|
||||
{
|
||||
QObject::disconnect(timer, nullptr, nullptr, nullptr);
|
||||
@@ -293,6 +295,35 @@ bool Plugin::hasHTTPPermissionFor(const QUrl &url)
|
||||
});
|
||||
}
|
||||
|
||||
void Plugin::log(lua_State *L, lua::api::LogLevel level, QDebug stream,
|
||||
const sol::variadic_args &args)
|
||||
{
|
||||
stream.noquote();
|
||||
stream << "[" + this->id + ":" + this->meta.name + "]";
|
||||
QString fullMessage;
|
||||
for (const auto &arg : args)
|
||||
{
|
||||
auto s = lua::toString(L, arg.stack_index());
|
||||
stream << s;
|
||||
|
||||
if (!fullMessage.isEmpty())
|
||||
{
|
||||
fullMessage.append(' ');
|
||||
}
|
||||
fullMessage.append(s);
|
||||
|
||||
// Remove this from our stack
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
this->onLog(level, fullMessage);
|
||||
}
|
||||
|
||||
sol::state_view Plugin::state()
|
||||
{
|
||||
return {this->state_};
|
||||
}
|
||||
|
||||
bool Plugin::hasNetworkPermission() const
|
||||
{
|
||||
return std::ranges::any_of(this->meta.permissions, [](const auto &p) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
# include "controllers/plugins/LuaUtilities.hpp"
|
||||
# include "controllers/plugins/PluginPermission.hpp"
|
||||
|
||||
# include <boost/signals2/signal.hpp>
|
||||
# include <QDir>
|
||||
# include <QString>
|
||||
# include <QUrl>
|
||||
@@ -21,6 +22,10 @@
|
||||
struct lua_State;
|
||||
class QTimer;
|
||||
|
||||
namespace chatterino::lua::api {
|
||||
enum class LogLevel;
|
||||
} // namespace chatterino::lua::api
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
struct PluginMeta {
|
||||
@@ -137,12 +142,20 @@ public:
|
||||
bool hasHTTPPermissionFor(const QUrl &url);
|
||||
bool hasNetworkPermission() const;
|
||||
|
||||
void log(lua_State *L, lua::api::LogLevel level, QDebug stream,
|
||||
const sol::variadic_args &args);
|
||||
|
||||
sol::state_view state();
|
||||
|
||||
std::map<lua::api::EventType, sol::protected_function> callbacks;
|
||||
|
||||
// In-flight HTTP Requests
|
||||
// This is a lifetime hack to ensure they get deleted with the plugin. This relies on the Plugin getting deleted on reload!
|
||||
std::vector<std::shared_ptr<lua::api::HTTPRequest>> httpRequests;
|
||||
|
||||
boost::signals2::signal<void()> onUnloaded;
|
||||
boost::signals2::signal<void(lua::api::LogLevel, const QString &)> onLog;
|
||||
|
||||
private:
|
||||
QDir loadDirectory_;
|
||||
lua_State *state_;
|
||||
|
||||
@@ -278,6 +278,8 @@ void PluginController::load(const QFileInfo &index, const QDir &pluginDir,
|
||||
}
|
||||
temp->dataDirectory().mkpath(".");
|
||||
|
||||
// make sure we capture log messages during load
|
||||
this->onPluginLoaded(temp);
|
||||
qCDebug(chatterinoLua) << "Running lua file:" << index;
|
||||
int err = luaL_dofile(l, index.absoluteFilePath().toStdString().c_str());
|
||||
if (err != 0)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
# include "controllers/commands/CommandContext.hpp"
|
||||
# include "controllers/plugins/Plugin.hpp"
|
||||
|
||||
# include <boost/signals2/signal.hpp>
|
||||
# include <QDir>
|
||||
# include <QFileInfo>
|
||||
# include <QJsonArray>
|
||||
@@ -63,6 +64,8 @@ public:
|
||||
|
||||
WebSocketPool &webSocketPool();
|
||||
|
||||
boost::signals2::signal<void(Plugin *)> onPluginLoaded;
|
||||
|
||||
private:
|
||||
void loadPlugins();
|
||||
void load(const QFileInfo &index, const QDir &pluginDir,
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
|
||||
# include "Application.hpp"
|
||||
# include "common/QLogging.hpp"
|
||||
# include "controllers/plugins/LuaAPI.hpp"
|
||||
# include "controllers/plugins/PluginController.hpp"
|
||||
|
||||
# include <QObject>
|
||||
# include <QStringBuilder>
|
||||
# include <sol/thread.hpp>
|
||||
|
||||
namespace chatterino::lua {
|
||||
@@ -27,9 +29,10 @@ Plugin *ThisPluginState::plugin()
|
||||
|
||||
void logError(Plugin *plugin, QStringView context, const QString &msg)
|
||||
{
|
||||
QString fullMessage = context % u" - " % msg;
|
||||
qCWarning(chatterinoLua).noquote()
|
||||
<< "[" + plugin->id + ":" + plugin->meta.name + "]" << context << "-"
|
||||
<< msg;
|
||||
<< "[" + plugin->id + ":" + plugin->meta.name + "]" << fullMessage;
|
||||
plugin->onLog(api::LogLevel::Warning, fullMessage);
|
||||
}
|
||||
|
||||
} // namespace chatterino::lua
|
||||
|
||||
Reference in New Issue
Block a user