Add support for reloading plugins

This commit is contained in:
Mm2PL 2023-01-30 23:55:16 +01:00
parent 03de6869c4
commit 56c7e78f48
No known key found for this signature in database
GPG key ID: 94AC9B80EFA15ED9
5 changed files with 75 additions and 33 deletions

View file

@ -3233,6 +3233,11 @@ bool CommandController::registerPluginCommand(const QString &commandName)
}; };
return true; return true;
} }
bool CommandController::unregisterPluginCommand(const QString &commandName)
{
return this->commands_.erase(commandName) != 0;
}
void CommandController::registerCommand(const QString &commandName, void CommandController::registerCommand(const QString &commandName,
CommandFunctionVariants commandFunction) CommandFunctionVariants commandFunction)
{ {

View file

@ -44,6 +44,7 @@ public:
std::unordered_map<QString, QString> context = {}); std::unordered_map<QString, QString> context = {});
bool registerPluginCommand(const QString &commandName); bool registerPluginCommand(const QString &commandName);
bool unregisterPluginCommand(const QString &commandName);
private: private:
void load(Paths &paths); void load(Paths &paths);

View file

@ -34,41 +34,45 @@ void PluginController::initialize(Settings &settings, Paths &paths)
{ {
if (info.isDir()) if (info.isDir())
{ {
// look for index.lua
auto pluginDir = QDir(info.absoluteFilePath()); auto pluginDir = QDir(info.absoluteFilePath());
auto index = QFileInfo(pluginDir.filePath("index.lua")); this->tryLoadFromDir(pluginDir);
qCDebug(chatterinoLua)
<< "trying" << info << "," << index << "at" << pluginDir;
if (!index.exists())
{
qCDebug(chatterinoLua)
<< "Missing index.lua in plugin directory" << pluginDir;
continue;
}
qCDebug(chatterinoLua)
<< "found index.lua, now looking for info.json!";
auto info = QFileInfo(pluginDir.filePath("info.json"));
if (!info.exists())
{
qCDebug(chatterinoLua)
<< "Missing info.json in plugin directory" << pluginDir;
continue;
}
QFile infoFile(info.absoluteFilePath());
infoFile.open(QIODevice::ReadOnly);
auto everything = infoFile.readAll();
auto doc = QJsonDocument::fromJson(everything);
if (!doc.isObject())
{
qCDebug(chatterinoLua)
<< "info.json root is not an object" << pluginDir;
continue;
}
this->load(index, pluginDir, PluginMeta(doc.object()));
} }
} }
} }
bool PluginController::tryLoadFromDir(const QDir &pluginDir)
{
// look for index.lua
auto index = QFileInfo(pluginDir.filePath("index.lua"));
qCDebug(chatterinoLua) << "looking for index.lua and info.json in"
<< pluginDir.path();
if (!index.exists())
{
qCDebug(chatterinoLua)
<< "Missing index.lua in plugin directory" << pluginDir;
return false;
}
qCDebug(chatterinoLua) << "found index.lua, now looking for info.json!";
auto infojson = QFileInfo(pluginDir.filePath("info.json"));
if (!infojson.exists())
{
qCDebug(chatterinoLua)
<< "Missing info.json in plugin directory" << pluginDir;
return false;
}
QFile infoFile(infojson.absoluteFilePath());
infoFile.open(QIODevice::ReadOnly);
auto everything = infoFile.readAll();
auto doc = QJsonDocument::fromJson(everything);
if (!doc.isObject())
{
qCDebug(chatterinoLua)
<< "info.json root is not an object" << pluginDir;
return false;
}
this->load(index, pluginDir, PluginMeta(doc.object()));
return true;
}
void PluginController::load(QFileInfo index, QDir pluginDir, PluginMeta meta) void PluginController::load(QFileInfo index, QDir pluginDir, PluginMeta meta)
{ {
@ -78,13 +82,31 @@ void PluginController::load(QFileInfo index, QDir pluginDir, PluginMeta meta)
this->loadChatterinoLib(l); this->loadChatterinoLib(l);
auto pluginName = pluginDir.dirName(); auto pluginName = pluginDir.dirName();
auto plugin = std::make_unique<Plugin>(pluginName, l, meta); auto plugin = std::make_unique<Plugin>(pluginName, l, meta, pluginDir);
this->plugins_.insert({pluginName, std::move(plugin)}); this->plugins_.insert({pluginName, std::move(plugin)});
luaL_dofile(l, index.absoluteFilePath().toStdString().c_str()); luaL_dofile(l, index.absoluteFilePath().toStdString().c_str());
qCInfo(chatterinoLua) << "Loaded" << pluginName << "plugin from" << index; qCInfo(chatterinoLua) << "Loaded" << pluginName << "plugin from" << index;
} }
bool PluginController::reload(const QString &codename)
{
auto it = this->plugins_.find(codename);
if (it == this->plugins_.end())
{
return false;
}
lua_close(it->second->state_);
for (const auto &[cmd, _] : it->second->ownedCommands)
{
getApp()->commands->unregisterPluginCommand(cmd);
}
QDir loadDir = it->second->loadDirectory_;
this->plugins_.erase(codename);
this->tryLoadFromDir(loadDir);
return true;
}
void PluginController::callEvery(const QString &functionName) void PluginController::callEvery(const QString &functionName)
{ {
for (const auto &[name, plugin] : this->plugins_) for (const auto &[name, plugin] : this->plugins_)

View file

@ -47,9 +47,12 @@ class Plugin
public: public:
QString codename; QString codename;
PluginMeta meta; PluginMeta meta;
Plugin(QString codename, lua_State *state, PluginMeta meta)
Plugin(QString codename, lua_State *state, PluginMeta meta,
const QDir &loadDirectory)
: codename(std::move(codename)) : codename(std::move(codename))
, meta(std::move(meta)) , meta(std::move(meta))
, loadDirectory_(loadDirectory)
, state_(state) , state_(state)
{ {
} }
@ -71,6 +74,7 @@ public:
} }
private: private:
QDir loadDirectory_;
lua_State *state_; lua_State *state_;
// maps command name -> function name // maps command name -> function name
@ -112,9 +116,12 @@ public:
return this->plugins_; return this->plugins_;
} }
bool reload(const QString &codename);
private: private:
void load(QFileInfo index, QDir pluginDir, PluginMeta meta); void load(QFileInfo index, QDir pluginDir, PluginMeta meta);
void loadChatterinoLib(lua_State *l); void loadChatterinoLib(lua_State *l);
bool tryLoadFromDir(const QDir &pluginDir);
std::map<QString, std::unique_ptr<Plugin>> plugins_; std::map<QString, std::unique_ptr<Plugin>> plugins_;
}; };

View file

@ -10,6 +10,7 @@
#include <QFormLayout> #include <QFormLayout>
#include <QGroupBox> #include <QGroupBox>
#include <QLabel> #include <QLabel>
#include <QPushButton>
namespace chatterino { namespace chatterino {
@ -47,6 +48,12 @@ PluginsPage::PluginsPage()
homepage->setOpenExternalLinks(true); homepage->setOpenExternalLinks(true);
pl->addRow("Homepage", homepage); pl->addRow("Homepage", homepage);
auto *reload = new QPushButton("Reload");
QObject::connect(reload, &QPushButton::pressed, [=]() {
getApp()->plugins->reload(codename);
});
pl->addRow(reload);
} }
} }
} // namespace chatterino } // namespace chatterino