diff --git a/docs/wip-plugins.md b/docs/wip-plugins.md index 59827f528..56648edac 100644 --- a/docs/wip-plugins.md +++ b/docs/wip-plugins.md @@ -55,7 +55,30 @@ The official manual for them is available [here](https://www.lua.org/manual/5.4/ ### Chatterino API -All Chatterino functions are exposed in a global table called `c2`. The following functions are available +All Chatterino functions are exposed in a global table called `c2`. The following members are available: + +#### `log(level, args...)` + +Writes a message to the Chatterino log. The `level` argument should be a +`LogLevel` member. All `args` should be convertible to a string with +`tostring()`. + +Example: +``` +c2.log(c2.LogLevel.Warning, "Hello, this should show up in the Chatterino log by default") + +c2.log(c2.LogLevel.Debug, "Hello world") +-- Equivalent to doing qCDebug(chatterinoLua) << "[pluginDirectory:Plugin Name]" << "Hello, world"; from C++ +``` + +#### `LogLevel` enum + +This table describes log levels available to Lua Plugins. The values behind the names may change, do not count on them. It has the following keys: + +- `Debug` +- `Info` +- `Warning` +- `Critical` #### `register_command(name, handler)` @@ -139,3 +162,7 @@ execfile("./stuff.lua") -- executes Plugins/name/stuff.lua execfile("../stuff.lua") -- tries to load Plugins/stuff.lua and errors execfile("luac.out") -- tried to load Plugins/name/luac.out and errors because it contains non-utf8 data ``` + +#### `print(Args...)` + +The `print` global function is equivalent to calling `c2.log(c2.LogLevel.Debug, Args...)` diff --git a/src/controllers/plugins/LuaApi.cpp b/src/controllers/plugins/LuaApi.cpp index 0f2aaf079..979939665 100644 --- a/src/controllers/plugins/LuaApi.cpp +++ b/src/controllers/plugins/LuaApi.cpp @@ -15,6 +15,7 @@ # include "lualib.h" # include +# include # include // NOLINTBEGIN(*vararg) @@ -102,6 +103,63 @@ int c2_system_msg(lua_State *L) return 1; } +namespace { + void logHelper(lua_State *L, Plugin *pl, QDebug stream, int argc) + { + stream.noquote(); + stream << "[" + pl->codename + ":" + pl->meta.name + "]"; + for (int i = 1; i <= argc; i++) + { + stream << lua::toString(L, i); + } + lua_pop(L, argc); + } + +} // namespace + +int c2_log(lua_State *L) +{ + auto *pl = getApp()->plugins->getPluginByStatePtr(L); + if (pl == nullptr) + { + luaL_error(L, "print: internal error: no plugin?"); + return 0; + } + auto logc = lua_gettop(L) - 1; + // this is the expansion of qCDebug() macro + auto temp = + (QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, + QT_MESSAGELOG_FUNC, chatterinoLua().categoryName())); + + LogLevel lvl{}; + if (!lua::pop(L, &lvl, 1)) + { + luaL_error(L, "Invalid log level, use one from c2.LogLevel."); + return 0; + } + QDebug stream{(QString *)nullptr}; + switch (lvl) + { + case LogLevel::Debug: + stream = temp.debug(); + break; + case LogLevel::Critical: + stream = temp.critical(); + break; + case LogLevel::Info: + stream = temp.info(); + break; + case LogLevel::Warning: + stream = temp.warning(); + break; + default: + assert(false && "if this happens magic_enum must have failed us"); + break; + } + logHelper(L, pl, stream, logc); + return 0; +} + int g_load(lua_State *L) { auto countArgs = lua_gettop(L); @@ -207,6 +265,24 @@ int g_dofile(lua_State *L) return lua_gettop(L); } +int g_print(lua_State *L) +{ + auto *pl = getApp()->plugins->getPluginByStatePtr(L); + if (pl == nullptr) + { + luaL_error(L, "print: internal error: no plugin?"); + return 0; + } + auto argc = lua_gettop(L); + // this is the expansion of qCDebug() macro + auto stream = + (QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, + QT_MESSAGELOG_FUNC, chatterinoLua().categoryName()) + .debug()); + logHelper(L, pl, stream, argc); + return 0; +} + } // namespace chatterino::lua::api // NOLINTEND(*vararg) #endif diff --git a/src/controllers/plugins/LuaApi.hpp b/src/controllers/plugins/LuaApi.hpp index c4d5af45d..d058d6f47 100644 --- a/src/controllers/plugins/LuaApi.hpp +++ b/src/controllers/plugins/LuaApi.hpp @@ -9,11 +9,16 @@ namespace chatterino::lua::api { int c2_register_command(lua_State *L); int c2_send_msg(lua_State *L); int c2_system_msg(lua_State *L); +int c2_log(lua_State *L); int g_load(lua_State *L); int g_dofile(lua_State *L); +int g_print(lua_State *L); // NOLINTEND(readability-identifier-naming) +// Represents "calls" to qCDebug, qCInfo ... +enum LogLevel { Debug, Info, Warning, Critical }; + } // namespace chatterino::lua::api #endif diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index cf61a4195..a20c7e7c0 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -147,15 +147,20 @@ void PluginController::openLibrariesFor(lua_State *L, {"system_msg", lua::api::c2_system_msg}, {"register_command", lua::api::c2_register_command}, {"send_msg", lua::api::c2_send_msg}, + {"log", lua::api::c2_log}, {nullptr, nullptr}, }; lua_pushglobaltable(L); auto global = lua_gettop(L); - // count of elements in C2LIB - 1 (to account for terminator) - lua::pushEmptyTable(L, 3); + // count of elements in C2LIB + LogLevel + auto c2libIdx = lua::pushEmptyTable(L, 5); luaL_setfuncs(L, c2Lib, 0); + + lua::pushEnumTable(L); + lua_setfield(L, c2libIdx, "LogLevel"); + lua_setfield(L, global, "c2"); // ban functions @@ -177,6 +182,8 @@ void PluginController::openLibrariesFor(lua_State *L, // chatterino dofile is way more similar to require() than dofile() {"execfile", lua::api::g_dofile}, + + {"print", lua::api::g_print}, {nullptr, nullptr}, }; luaL_setfuncs(L, replacementFuncs, 0);