#pragma once // Step 161: Plugin loader #include #include #include #include #include #include "PluginAPI.h" #ifdef _WIN32 #include #else #include #endif struct PluginRegistry { std::vector concepts; std::vector generators; std::vector grammars; std::vector annotations; std::vector panels; std::vector commands; std::vector> keybindings; void clear() { concepts.clear(); generators.clear(); grammars.clear(); annotations.clear(); panels.clear(); commands.clear(); keybindings.clear(); } }; struct PluginHandle { std::string path; PluginDescriptor descriptor; void* handle = nullptr; PluginShutdownFn shutdown = nullptr; }; class PluginLoader { public: bool loadFromDirectory(const std::string& dir, std::string& error) { namespace fs = std::filesystem; std::error_code ec; if (!fs::exists(dir, ec)) { error = "Directory not found: " + dir; return false; } bool any = false; for (const auto& entry : fs::directory_iterator(dir)) { if (!entry.is_regular_file()) continue; auto path = entry.path(); #ifdef _WIN32 if (path.extension() != ".dll") continue; #elif __APPLE__ if (path.extension() != ".dylib") continue; #else if (path.extension() != ".so") continue; #endif std::string err; if (!loadPlugin(path.string(), err)) { error = err; return false; } any = true; } if (!any) { error = "No plugin libraries found in: " + dir; } return any; } bool loadPlugin(const std::string& path, std::string& error) { #ifdef _WIN32 HMODULE lib = LoadLibraryA(path.c_str()); if (!lib) { error = "LoadLibrary failed: " + path; return false; } auto init = reinterpret_cast( GetProcAddress(lib, "WhetstonePluginInit")); auto shutdown = reinterpret_cast( GetProcAddress(lib, "WhetstonePluginShutdown")); #else void* lib = dlopen(path.c_str(), RTLD_LAZY); if (!lib) { error = std::string("dlopen failed: ") + dlerror(); return false; } auto init = reinterpret_cast( dlsym(lib, "WhetstonePluginInit")); auto shutdown = reinterpret_cast( dlsym(lib, "WhetstonePluginShutdown")); #endif if (!init) { error = "Missing WhetstonePluginInit in: " + path; unloadLibrary(lib); return false; } PluginDescriptor desc; if (!invokeInit(init, desc)) { error = "Plugin init failed: " + path; unloadLibrary(lib); return false; } PluginHandle handle; handle.path = path; handle.descriptor = desc; handle.handle = lib; handle.shutdown = shutdown; plugins_.push_back(std::move(handle)); return true; } bool loadFromEntryPoint(PluginInitFn init, const std::string& pathLabel, std::string& error) { PluginDescriptor desc; if (!invokeInit(init, desc)) { error = "Plugin init failed: " + pathLabel; return false; } PluginHandle handle; handle.path = pathLabel; handle.descriptor = desc; plugins_.push_back(std::move(handle)); return true; } void unloadAll() { for (auto& plugin : plugins_) { if (plugin.shutdown) plugin.shutdown(); if (plugin.handle) { unloadLibrary(plugin.handle); } } plugins_.clear(); registry_.clear(); } const std::vector& plugins() const { return plugins_; } const PluginRegistry& registry() const { return registry_; } private: bool invokeInit(PluginInitFn init, PluginDescriptor& desc) { PluginHost host; host.ctx = this; host.registerConcept = [](void* ctx, const char* name) { static_cast(ctx)->registry_.concepts.emplace_back(name ? name : ""); }; host.registerGenerator = [](void* ctx, const char* language) { static_cast(ctx)->registry_.generators.emplace_back(language ? language : ""); }; host.registerGrammar = [](void* ctx, const char* language) { static_cast(ctx)->registry_.grammars.emplace_back(language ? language : ""); }; host.registerAnnotation = [](void* ctx, const char* name) { static_cast(ctx)->registry_.annotations.emplace_back(name ? name : ""); }; host.registerPanel = [](void* ctx, const char* name) { static_cast(ctx)->registry_.panels.emplace_back(name ? name : ""); }; host.registerCommand = [](void* ctx, const char* commandId) { static_cast(ctx)->registry_.commands.emplace_back(commandId ? commandId : ""); }; host.registerKeybinding = [](void* ctx, const char* commandId, const char* keybinding) { static_cast(ctx)->registry_.keybindings.emplace_back( commandId ? commandId : "", keybinding ? keybinding : ""); }; return init(&host, &desc); } static void unloadLibrary(void* handle) { #ifdef _WIN32 FreeLibrary(reinterpret_cast(handle)); #else dlclose(handle); #endif } PluginRegistry registry_; std::vector plugins_; };