dev: simplify protocol def
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <expected>
|
||||
|
||||
#include "PluginProtocol.h"
|
||||
#include "Async/Async.h"
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
@@ -9,33 +10,22 @@
|
||||
|
||||
// clang-format off
|
||||
/// Run `python scripts/plugin-def.py update` to update the hash.
|
||||
#define CLICE_PLUGIN_DEF_HASH "sha256:e35ff1adfbc385f7bae605e82ca4e7e70cfbf0f933bb8d57169d0d29abe5b6f7"
|
||||
#define CLICE_PLUGIN_DEF_HASH "sha256:c46f7edfda0455327c65d40b9315ad5dc39153326c8cc63f1d8de2e2d0e7735a"
|
||||
// clang-format on
|
||||
|
||||
namespace clice {
|
||||
|
||||
/// The hash of the definitions exposed to server plugins.
|
||||
constexpr std::string_view plugin_definition_hash = CLICE_PLUGIN_DEF_HASH;
|
||||
|
||||
class Server;
|
||||
|
||||
#define CLICE_PLUGIN_PROTOCOL
|
||||
|
||||
#include "PluginDef.h"
|
||||
#undef CLICE_PLUGIN_PROTOCOL
|
||||
|
||||
struct ServerPluginBuilder;
|
||||
|
||||
/// A loaded server plugin.
|
||||
///
|
||||
/// An instance of this class wraps a loaded server plugin and gives access to its interface.
|
||||
class Plugin {
|
||||
public:
|
||||
struct Self;
|
||||
|
||||
Plugin(Self* self) : self(self) {}
|
||||
|
||||
Self* operator->() {
|
||||
return self;
|
||||
}
|
||||
|
||||
public:
|
||||
/// Attempts to load a server plugin from a given file.
|
||||
///
|
||||
@@ -56,21 +46,17 @@ public:
|
||||
/// Registers the server callbacks for the loaded plugin.
|
||||
void register_server_callbacks(ServerPluginBuilder& builder) const;
|
||||
|
||||
public:
|
||||
struct Self;
|
||||
|
||||
Plugin(Self* self) : self(self) {}
|
||||
|
||||
Self* operator->() {
|
||||
return self;
|
||||
}
|
||||
|
||||
protected:
|
||||
Self* self;
|
||||
};
|
||||
|
||||
struct ServerPluginBuilder {
|
||||
public:
|
||||
ServerPluginBuilder(ServerRef server_ref) : server_ref(server_ref) {}
|
||||
|
||||
#define CliceServerPluginAPI(METHOD, ...) void METHOD(void* plugin_data, __VA_ARGS__)
|
||||
|
||||
#include "PluginDef.h"
|
||||
#undef CliceServerPluginAPI
|
||||
|
||||
protected:
|
||||
ServerRef server_ref;
|
||||
};
|
||||
|
||||
} // namespace clice
|
||||
|
||||
@@ -10,14 +10,23 @@
|
||||
#error "CLICE_PLUGIN_API_VERSION must be 1, but got " CLICE_PLUGIN_API_VERSION
|
||||
#endif
|
||||
|
||||
/// Defines the library APIs that loads a plugin.
|
||||
#ifdef CLICE_PLUGIN_PROTOCOL
|
||||
#include <cstdint>
|
||||
|
||||
#include "Async/Async.h"
|
||||
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
class Server;
|
||||
|
||||
struct ServerPluginBuilder;
|
||||
/// Defines the library APIs that loads a plugin.
|
||||
extern "C" {
|
||||
/// A C-compatible struct that contains information about the plugin.
|
||||
struct PluginInfo {
|
||||
/// The clice API version of the plugin.
|
||||
std::uint32_t api_version;
|
||||
uint32_t api_version;
|
||||
/// The name of the plugin.
|
||||
const char* name;
|
||||
/// The version of the plugin.
|
||||
@@ -61,23 +70,43 @@ public:
|
||||
protected:
|
||||
Self* self;
|
||||
};
|
||||
#endif
|
||||
|
||||
/// Defines the library APIs to register callbacks for a plugin.
|
||||
#ifdef CliceServerPluginAPI
|
||||
CliceServerPluginAPI(get_server_ref, ServerRef& server);
|
||||
using lifecycle_hook_t = async::Task<> (*)(ServerRef server, void* plugin_data);
|
||||
struct ServerPluginBuilder {
|
||||
public:
|
||||
ServerPluginBuilder(ServerRef server_ref) : server_ref(server_ref) {}
|
||||
|
||||
CliceServerPluginAPI(on_initialize, lifecycle_hook_t callback);
|
||||
CliceServerPluginAPI(on_initialized, lifecycle_hook_t callback);
|
||||
CliceServerPluginAPI(on_shutdown, lifecycle_hook_t callback);
|
||||
CliceServerPluginAPI(on_exit, lifecycle_hook_t callback);
|
||||
CliceServerPluginAPI(on_did_change_configuration, lifecycle_hook_t callback);
|
||||
using command_handler_t =
|
||||
async::Task<llvm::json::Value> (*)(ServerRef server,
|
||||
void* plugin_data,
|
||||
llvm::ArrayRef<llvm::StringRef> arguments);
|
||||
CliceServerPluginAPI(register_commmand_handler,
|
||||
llvm::StringRef command,
|
||||
command_handler_t callback);
|
||||
#endif
|
||||
/// Gets a reference to the server.
|
||||
auto get_server_ref() const -> ServerRef {
|
||||
return server_ref;
|
||||
}
|
||||
|
||||
#define CliceServerPluginAPI(METHOD, ...) void METHOD(void* plugin_data, __VA_ARGS__)
|
||||
|
||||
using lifecycle_hook_t = async::Task<> (*)(ServerRef server, void* plugin_data);
|
||||
|
||||
/// Registers a callback to be called when the server is initialized.
|
||||
CliceServerPluginAPI(on_initialize, lifecycle_hook_t callback);
|
||||
/// Registers a callback to be called when the server is initialized.
|
||||
CliceServerPluginAPI(on_initialized, lifecycle_hook_t callback);
|
||||
/// Registers a callback to be called when the server is shutdown.
|
||||
CliceServerPluginAPI(on_shutdown, lifecycle_hook_t callback);
|
||||
/// Registers a callback to be called when the server is exiting.
|
||||
CliceServerPluginAPI(on_exit, lifecycle_hook_t callback);
|
||||
/// Registers a callback to be called when the server's configuration is changed.
|
||||
CliceServerPluginAPI(on_did_change_configuration, lifecycle_hook_t callback);
|
||||
using command_handler_t =
|
||||
async::Task<llvm::json::Value> (*)(ServerRef server,
|
||||
void* plugin_data,
|
||||
llvm::ArrayRef<llvm::StringRef> arguments);
|
||||
/// Registers a callback to be called when a command is received from the LSP client.
|
||||
CliceServerPluginAPI(register_commmand_handler,
|
||||
llvm::StringRef command,
|
||||
command_handler_t callback);
|
||||
#undef CliceServerPluginAPI
|
||||
|
||||
protected:
|
||||
ServerRef server_ref;
|
||||
};
|
||||
|
||||
} // namespace clice
|
||||
3
include/Server/Utility.h
Normal file
3
include/Server/Utility.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define bail(...) std::unexpected(std::format(__VA_ARGS__))
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Server/Plugin.h"
|
||||
|
||||
#include "Implement.h"
|
||||
#include "Server/Utility.h"
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/Support/DynamicLibrary.h"
|
||||
@@ -28,44 +29,62 @@ std::expected<Plugin, std::string> Plugin::load(const std::string& file_path) {
|
||||
std::string err;
|
||||
auto library = llvm::sys::DynamicLibrary::getPermanentLibrary(file_path.c_str(), &err);
|
||||
if(!library.isValid()) {
|
||||
return std::unexpected("Could not load library '" + file_path + "': " + err);
|
||||
return bail("Could not load library '{}': {}", file_path, err);
|
||||
}
|
||||
|
||||
Plugin P{
|
||||
/// We currently never destroy plugins, so this is not a memory leak.
|
||||
new Self{file_path, library}
|
||||
};
|
||||
|
||||
/// `clice_get_server_plugin_info` should be resolved to the definition from the plugin
|
||||
/// we are currently loading.
|
||||
intptr_t get_details_fn = (intptr_t)library.getAddressOfSymbol("clice_get_server_plugin_info");
|
||||
|
||||
if(!get_details_fn) {
|
||||
/// If the symbol isn't found, this is probably a legacy plugin, which is an
|
||||
/// error.
|
||||
return std::unexpected("Plugin entry point not found in '" + file_path +
|
||||
"'. Is this a clice server plugin?");
|
||||
return bail(
|
||||
"The symbol `clice_get_server_plugin_info` is not found in '{}'. Is this a clice server plugin?",
|
||||
file_path);
|
||||
}
|
||||
|
||||
auto info = reinterpret_cast<decltype(clice_get_server_plugin_info)*>(get_details_fn)();
|
||||
|
||||
/// First, we check whether the plugin is compatible with the clice plugin API.
|
||||
if(info.api_version != CLICE_PLUGIN_API_VERSION) {
|
||||
return std::unexpected("Wrong API version on plugin '" + file_path + "'. Got version " +
|
||||
std::to_string(info.api_version) + ", supported version is " +
|
||||
std::to_string(CLICE_PLUGIN_API_VERSION) + ".");
|
||||
return bail("Wrong API version on plugin '{}'. Got version {}. Supported version is {}.",
|
||||
file_path,
|
||||
info.api_version,
|
||||
CLICE_PLUGIN_API_VERSION);
|
||||
}
|
||||
|
||||
/// Then, we safely get definition hash from the plugin, and check if it is consistent with
|
||||
/// the expected hash. This ensures that the plugin has consistent declarations with the server.
|
||||
/// the expected hash. This ensures that the plugin has consistent declarations with the
|
||||
/// server.
|
||||
std::string definition_hash = info.definition_hash;
|
||||
if(definition_hash != CLICE_PLUGIN_DEF_HASH) {
|
||||
return std::unexpected("Wrong definition hash on plugin '" + file_path + "'. Got '" +
|
||||
definition_hash + "', expected '" + CLICE_PLUGIN_DEF_HASH + "'.");
|
||||
if(plugin_definition_hash.size() != definition_hash.size()) {
|
||||
return bail("Wrong definition hash size on plugin '{}'. Got {}, expected {} ({}).",
|
||||
file_path,
|
||||
definition_hash.size(),
|
||||
plugin_definition_hash.size(),
|
||||
plugin_definition_hash);
|
||||
}
|
||||
|
||||
/// If there is any non-printable character in the definition hash, this is likely a bug in the
|
||||
/// plugin. We cannot even print the `definition_hash` in this case.
|
||||
if(std::ranges::any_of(definition_hash, [](char c) { return !std::isprint(c); })) {
|
||||
return bail("Corrupt definition hash on plugin '{}'. This is likely a bug in the plugin.",
|
||||
file_path);
|
||||
}
|
||||
|
||||
if(definition_hash != CLICE_PLUGIN_DEF_HASH) {
|
||||
return bail("Wrong definition hash on plugin '{}'. Got '{}', expected '{}'.",
|
||||
file_path,
|
||||
definition_hash,
|
||||
CLICE_PLUGIN_DEF_HASH);
|
||||
}
|
||||
|
||||
/// A plugin must implement the `register_server_callbacks` function.
|
||||
if(!info.register_server_callbacks) {
|
||||
return std::unexpected("Empty `register_server_callbacks` function in plugin '" +
|
||||
file_path + "'.");
|
||||
return bail("Empty `register_server_callbacks` function in plugin '{}'.", file_path);
|
||||
}
|
||||
|
||||
P->name = info.name;
|
||||
@@ -91,10 +110,6 @@ using command_handler_t =
|
||||
async::Task<llvm::json::Value> (*)(ServerRef server,
|
||||
const llvm::ArrayRef<llvm::StringRef>& arguments);
|
||||
|
||||
void ServerPluginBuilder::get_server_ref(void* plugin_data, ServerRef& server) {
|
||||
server = server_ref;
|
||||
}
|
||||
|
||||
void ServerPluginBuilder::on_initialize(void* plugin_data, lifecycle_hook_t callback) {
|
||||
auto server = server_ref;
|
||||
server_ref.server().initialize_hooks.push_back(
|
||||
|
||||
Reference in New Issue
Block a user