mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 16:08:05 +00:00
Plugin API (breaking; the plugin system is unreleased): - Remove the runtime `contributions` interface (register-command, register-language, register-theme). Commands, languages and themes are declared in plugin.toml only, so everything a plugin contributes goes through the install preview and consent. The registry drops runtime scope, `contribute` and `dispose_runtime`; inline `definition` in language and theme declarations is gone. - `completion-item.kind` is a string (the 25 LSP kinds; unknown kinds show no icon), like the other open-ended kinds. - `lifecycle.activate` and `commands.run` return `types.error` instead of a string. The host maps it to the matching error kind, and only `failed` counts against the plugin: cancelling, a missing grant or an unknown command does not. The SDK's `Plugin` returns `Result<_, Error>`; plain messages convert to `Error::Failed`. Runtime: - Editor providers of several plugins are called at the same time (`Actor::submit` + `Pending::wait`): the wait is the slowest plugin's, not the sum, so each gets the full edit-path budget. Versioning and tooling: - Components are checked at install and by `openidea-plugin check` for the plugin API they were built for; another major or a newer minor is refused with what to do. - Tests: the SDK's WIT matches the host's, a simulated next-minor host links every shipped component, every host function has an SDK wrapper, released APIs only grow (no snapshot until the first release). - plugins/build.sh builds every checked-in component, records a source fingerprint next to it, and `--check` fails on stale components; CI runs it plus fmt, clippy (host and wasm) and tests for the SDK, the official plugins and the fixtures. - The API evolution rules and the checklist for a new host function are in plugins/README.md; the design notes under docs/ are removed.
54 lines
2.3 KiB
Rust
54 lines
2.3 KiB
Rust
//! Every function the plugin API imports is reachable through the SDK: a new
|
|
//! WIT function without a wrapper in `src/` (and so in the mock host, which
|
|
//! mirrors the wrappers) fails here instead of going unnoticed by plugin
|
|
//! authors. The check is textual: free functions must be called as
|
|
//! `wit::<interface>::<function>(`, resource methods as `.<method>(`.
|
|
|
|
use std::path::Path;
|
|
use wit_parser::{FunctionKind, Resolve, WorldItem};
|
|
|
|
fn sdk_source() -> String {
|
|
let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
|
|
let mut out = String::new();
|
|
for entry in std::fs::read_dir(dir).unwrap() {
|
|
let path = entry.unwrap().path();
|
|
if path.extension().is_some_and(|e| e == "rs") {
|
|
out.push_str(&std::fs::read_to_string(path).unwrap());
|
|
}
|
|
}
|
|
out
|
|
}
|
|
|
|
#[test]
|
|
fn every_imported_function_has_an_sdk_wrapper() {
|
|
let mut resolve = Resolve::default();
|
|
let (pkg, _) = resolve.push_dir(Path::new(env!("CARGO_MANIFEST_DIR")).join("wit")).unwrap();
|
|
let source = sdk_source();
|
|
let world = resolve.packages[pkg].worlds["plugin"];
|
|
let mut missing = Vec::new();
|
|
let mut checked = 0;
|
|
for item in resolve.worlds[world].imports.values() {
|
|
let WorldItem::Interface { id, .. } = item else { continue };
|
|
let iface = &resolve.interfaces[*id];
|
|
if iface.package != Some(pkg) {
|
|
continue; // WASI
|
|
}
|
|
let module = iface.name.as_deref().unwrap().replace('-', "_");
|
|
for f in iface.functions.values() {
|
|
let name = f.item_name().replace('-', "_");
|
|
let call = match f.kind {
|
|
FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => format!("wit::{module}::{name}("),
|
|
FunctionKind::Constructor(_) => "::new(".to_owned(),
|
|
FunctionKind::Static(_) | FunctionKind::AsyncStatic(_) => format!("::{name}("),
|
|
_ => format!(".{name}("),
|
|
};
|
|
checked += 1;
|
|
if !source.contains(&call) {
|
|
missing.push(format!("{}.{} (expected `{call}`)", iface.name.as_deref().unwrap(), f.name));
|
|
}
|
|
}
|
|
}
|
|
assert!(checked > 30, "only {checked} functions found: is the world still called `plugin`?");
|
|
assert!(missing.is_empty(), "plugin API functions without an SDK wrapper:\n {}", missing.join("\n "));
|
|
}
|