fix(plugins): gate on_load behind API v5

BREAKING CHANGE: plugins using on_load must target API v5 and declare api_version = 5.

Refs #901
This commit is contained in:
Hakan Seven committed 2026-08-29 15:57:26 +03:00
1 parent c527466791
commit 2842f6dc58
9 files changed
+43 -30

No files matched your search

+5 -7
View File
@@ -299,14 +299,12 @@ pub trait BuiltinPlugin: Send + Sync {
/// API major 4 or newer.
fn on_notification(&mut self, _command_id: Option<u64>, _notification: HostNotification) {}
/// Lifecycle callback invoked once after the plugin has been loaded and its
/// manifest/ribbon have been accepted by the host. The plugin receives the
/// active document tab's `HostApi` so it can set up worker threads or cache
/// `plugin_request_sender()` before any user command runs.
/// Lifecycle callback invoked once after the runner connects. The plugin
/// receives the active document tab's `HostApi` so it can set up worker
/// threads or cache `plugin_request_sender()` before any user command runs.
///
/// Added after API v4. The default implementation does nothing, so existing
/// plugins remain source-compatible. The runner only calls this for plugins
/// that report API major 4 or newer.
/// Added in API v5. The runner only calls this for plugins that report API
/// major 5 or newer.
fn on_load(&mut self, _host: &mut dyn HostApi) {}
}
+5 -5
View File
@@ -17,8 +17,9 @@
/// `document()` / `document_mut()` to local cached copies for out-of-process
/// plugins and appends `document_reader` / `document_view` at the end of the
/// vtable so API v2 plugins keep working. v4 adds full-duplex notifications on
/// a multiplexed socket while leaving the V2/V3 ABI and protocol untouched.
pub const API_VERSION: u32 = 4;
/// a multiplexed socket while leaving the V2/V3 ABI and protocol untouched. v5
/// adds the `BuiltinPlugin::on_load` lifecycle callback.
pub const API_VERSION: u32 = 5;
/// Oldest plugin API major the current host still loads. This keeps previously
/// compiled cdylibs usable as long as their vtable layout is a prefix of the
@@ -26,9 +27,8 @@ pub const API_VERSION: u32 = 4;
pub const API_VERSION_MIN_SUPPORTED: u32 = 2;
/// Environment variable that caps the API major accepted by the host at
/// runtime. Set to `3` to disable V4 plugins while keeping V2/V3 plugins
/// working; set to `2` for V2-only mode. The same mechanism will apply to
/// future API majors.
/// runtime. Set to `4` to disable V5 plugins, `3` to disable V4, or `2` for
/// V2-only mode.
pub const MAX_API_VERSION_ENV: &str = "OCS_PLUGIN_MAX_API_VERSION";
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+12 -6
View File
@@ -56,9 +56,9 @@ pub fn run(socket_name: &str, cdylib_path: &Path) -> Result<(), Box<dyn std::err
if version >= 4 {
let client = V4Client::connect_handshake(socket_name, &token)?;
// Give the plugin a chance to set up worker threads / cache the request
// sender before the host starts dispatching user commands.
invoke_on_load(&mut *plugin, &client, &interactive);
if version >= 5 {
invoke_on_load(&mut *plugin, &client, &interactive)?;
}
run_v4(&mut *plugin, &interactive, client)
} else {
let client = IpcClient::connect(socket_name)?;
@@ -70,11 +70,17 @@ pub fn run(socket_name: &str, cdylib_path: &Path) -> Result<(), Box<dyn std::err
}
}
fn invoke_on_load(plugin: &mut dyn BuiltinPlugin, client: &V4Client, interactive: &InteractiveRegistry) {
fn invoke_on_load(
plugin: &mut dyn BuiltinPlugin,
client: &V4Client,
interactive: &InteractiveRegistry,
) -> Result<(), Box<dyn std::error::Error>> {
let mut proxy = client.plugin_host_api(0, interactive.clone());
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
plugin.on_load(&mut proxy)
}));
}))
.map_err(|_| std::io::Error::other("plugin on_load() panicked"))?;
Ok(())
}
fn run_v3(
+1 -1
View File
@@ -147,7 +147,7 @@ mod tests {
value["ocs_plugin_api_version"].as_str(),
Some(env!("CARGO_PKG_VERSION"))
);
assert_eq!(value["api_version"].as_i64(), Some(4));
assert_eq!(value["api_version"].as_i64(), Some(5));
assert_eq!(value["api_version_min_supported"].as_i64(), Some(2));
// acadrust_version should have a patch component (e.g. "0.4.0").