diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 98909eb65..e4014c122 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -3233,6 +3233,11 @@ bool CommandController::registerPluginCommand(const QString &commandName) }; return true; } + +bool CommandController::unregisterPluginCommand(const QString &commandName) +{ + return this->commands_.erase(commandName) != 0; +} void CommandController::registerCommand(const QString &commandName, CommandFunctionVariants commandFunction) { diff --git a/src/controllers/commands/CommandController.hpp b/src/controllers/commands/CommandController.hpp index 85b4ddf58..3a8834415 100644 --- a/src/controllers/commands/CommandController.hpp +++ b/src/controllers/commands/CommandController.hpp @@ -44,6 +44,7 @@ public: std::unordered_map context = {}); bool registerPluginCommand(const QString &commandName); + bool unregisterPluginCommand(const QString &commandName); private: void load(Paths &paths); diff --git a/src/controllers/plugins/PluginController.cpp b/src/controllers/plugins/PluginController.cpp index c3d05dfc0..7a47b5087 100644 --- a/src/controllers/plugins/PluginController.cpp +++ b/src/controllers/plugins/PluginController.cpp @@ -34,41 +34,45 @@ void PluginController::initialize(Settings &settings, Paths &paths) { if (info.isDir()) { - // look for index.lua auto pluginDir = QDir(info.absoluteFilePath()); - auto index = QFileInfo(pluginDir.filePath("index.lua")); - 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())); + this->tryLoadFromDir(pluginDir); } } } +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) { @@ -78,13 +82,31 @@ void PluginController::load(QFileInfo index, QDir pluginDir, PluginMeta meta) this->loadChatterinoLib(l); auto pluginName = pluginDir.dirName(); - auto plugin = std::make_unique(pluginName, l, meta); + auto plugin = std::make_unique(pluginName, l, meta, pluginDir); this->plugins_.insert({pluginName, std::move(plugin)}); luaL_dofile(l, index.absoluteFilePath().toStdString().c_str()); 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) { for (const auto &[name, plugin] : this->plugins_) diff --git a/src/controllers/plugins/PluginController.hpp b/src/controllers/plugins/PluginController.hpp index 6aea67234..ec88da923 100644 --- a/src/controllers/plugins/PluginController.hpp +++ b/src/controllers/plugins/PluginController.hpp @@ -47,9 +47,12 @@ class Plugin public: QString codename; 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)) , meta(std::move(meta)) + , loadDirectory_(loadDirectory) , state_(state) { } @@ -71,6 +74,7 @@ public: } private: + QDir loadDirectory_; lua_State *state_; // maps command name -> function name @@ -112,9 +116,12 @@ public: return this->plugins_; } + bool reload(const QString &codename); + private: void load(QFileInfo index, QDir pluginDir, PluginMeta meta); void loadChatterinoLib(lua_State *l); + bool tryLoadFromDir(const QDir &pluginDir); std::map> plugins_; }; diff --git a/src/widgets/settingspages/PluginsPage.cpp b/src/widgets/settingspages/PluginsPage.cpp index a6ab81afc..4b69f8407 100644 --- a/src/widgets/settingspages/PluginsPage.cpp +++ b/src/widgets/settingspages/PluginsPage.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace chatterino { @@ -47,6 +48,12 @@ PluginsPage::PluginsPage() homepage->setOpenExternalLinks(true); pl->addRow("Homepage", homepage); + + auto *reload = new QPushButton("Reload"); + QObject::connect(reload, &QPushButton::pressed, [=]() { + getApp()->plugins->reload(codename); + }); + pl->addRow(reload); } } } // namespace chatterino