From 78e0c7a23e1cb6f61a767c1982af88a8fd20f30f Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Tue, 3 Nov 2026 14:46:46 +0800 Subject: [PATCH] feat(plugins): finalize plugin API before release, concurrent providers 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. --- .github/workflows/ci.yml | 33 + Cargo.lock | 2 +- crates/ide-dap/src/tests.rs | 2 +- crates/ide-extension/src/index.rs | 2 +- crates/ide-extension/src/lib.rs | 5 +- crates/ide-extension/src/package.rs | 2 +- crates/ide-extension/src/registry.rs | 95 +-- crates/ide-extension/src/ui.rs | 4 +- crates/ide-host/benches/plugins.rs | 3 +- .../ide-host/src/bin/openidea-plugin/main.rs | 15 +- crates/ide-host/src/ops/lsp.rs | 79 ++- crates/ide-host/src/plugins/api.rs | 27 +- crates/ide-host/src/plugins/install.rs | 2 +- crates/ide-host/src/plugins/market.rs | 2 +- crates/ide-host/src/plugins/mod.rs | 42 +- crates/ide-host/src/plugins/points.rs | 2 - crates/ide-host/src/plugins/providers.rs | 116 ++-- crates/ide-host/src/plugins/remote.rs | 3 +- crates/ide-host/src/plugins/remote_host.rs | 2 +- crates/ide-host/src/plugins/run_detectors.rs | 3 +- crates/ide-host/src/plugins/theme.rs | 16 +- crates/ide-host/src/plugins/ui_points.rs | 2 +- crates/ide-host/src/plugins/views.rs | 2 +- crates/ide-host/tests/distribution.rs | 2 +- crates/ide-host/tests/plugins.rs | 130 ++-- crates/ide-host/tests/remote_plugins.rs | 6 +- crates/ide-language/src/lib.rs | 2 +- crates/ide-language/src/plugin_point.rs | 33 +- crates/ide-language/src/syntax/external.rs | 2 +- crates/ide-plugin/Cargo.toml | 2 +- crates/ide-plugin/src/actor.rs | 52 +- crates/ide-plugin/src/lib.rs | 5 +- crates/ide-plugin/src/runtime.rs | 244 ++++--- crates/ide-plugin/src/types.rs | 3 +- crates/ide-plugin/src/wit_host.rs | 55 +- crates/ide-plugin/tests/api_compat.rs | 270 ++++++++ .../tests/fixtures/misbehave/dist/plugin.wasm | Bin 84266 -> 87772 bytes .../misbehave/dist/plugin.wasm.sources | 1 + .../tests/fixtures/misbehave/plugin.toml | 32 + .../tests/fixtures/misbehave/src/lib.rs | 107 +++- .../tests/fixtures/p0-probe/wit/probe.wit | 2 +- crates/ide-plugin/tests/p0_probe.rs | 2 +- crates/ide-run/src/external.rs | 4 +- docs/plugin_p0.md | 159 ----- docs/plugin_plan.md | 601 ------------------ perf/budgets.json | 2 +- plugins/README.md | 147 +++-- plugins/build.sh | 89 ++- plugins/hello/dist/plugin.wasm | Bin 132571 -> 132268 bytes plugins/hello/dist/plugin.wasm.sources | 1 + plugins/hello/plugin.toml | 2 +- plugins/hello/src/lib.rs | 8 +- .../official/ai-completion/dist/plugin.wasm | Bin 165922 -> 166971 bytes .../ai-completion/dist/plugin.wasm.sources | 1 + plugins/official/ai-completion/src/lib.rs | 4 +- plugins/official/checkstyle/dist/plugin.wasm | Bin 157899 -> 159016 bytes .../checkstyle/dist/plugin.wasm.sources | 1 + plugins/official/checkstyle/src/lib.rs | 4 +- plugins/official/csv-editor/dist/plugin.wasm | Bin 130639 -> 130963 bytes .../csv-editor/dist/plugin.wasm.sources | 1 + plugins/official/csv-editor/src/lib.rs | 4 +- plugins/official/git-toolbox/dist/plugin.wasm | Bin 107752 -> 108855 bytes .../git-toolbox/dist/plugin.wasm.sources | 1 + plugins/official/git-toolbox/src/lib.rs | 8 +- .../official/key-promoter/dist/plugin.wasm | Bin 99579 -> 98959 bytes .../key-promoter/dist/plugin.wasm.sources | 1 + plugins/official/key-promoter/src/lib.rs | 13 +- plugins/official/kubernetes/dist/plugin.wasm | Bin 210999 -> 209585 bytes .../kubernetes/dist/plugin.wasm.sources | 1 + plugins/official/kubernetes/src/lib.rs | 4 +- plugins/official/mybatis/dist/plugin.wasm | Bin 158332 -> 159800 bytes .../official/mybatis/dist/plugin.wasm.sources | 1 + plugins/official/mybatis/src/lib.rs | 6 +- plugins/official/solidity/dist/plugin.wasm | Bin 105969 -> 107192 bytes .../solidity/dist/plugin.wasm.sources | 1 + plugins/official/solidity/src/lib.rs | 8 +- .../official/string-tools/dist/plugin.wasm | Bin 165373 -> 164796 bytes .../string-tools/dist/plugin.wasm.sources | 1 + plugins/official/string-tools/src/lib.rs | 10 +- sdk/rust/openidea-plugin/Cargo.lock | 1 + sdk/rust/openidea-plugin/Cargo.toml | 4 + sdk/rust/openidea-plugin/src/host.rs | 28 - sdk/rust/openidea-plugin/src/lib.rs | 50 +- sdk/rust/openidea-plugin/src/mock.rs | 56 -- sdk/rust/openidea-plugin/src/providers.rs | 50 +- sdk/rust/openidea-plugin/src/syntax.rs | 2 +- sdk/rust/openidea-plugin/src/text.rs | 32 +- sdk/rust/openidea-plugin/src/types.rs | 13 + sdk/rust/openidea-plugin/tests/api_frozen.rs | 180 ++++++ .../openidea-plugin/tests/wit_coverage.rs | 53 ++ sdk/rust/openidea-plugin/wit/exports.wit | 15 +- sdk/rust/openidea-plugin/wit/host.wit | 13 - sdk/rust/openidea-plugin/wit/plugin.wit | 7 +- sdk/rust/openidea-plugin/wit/providers.wit | 26 +- sdk/rust/openidea-plugin/wit/views.wit | 2 +- src-tauri/src/remote_dev.rs | 6 +- src/features/plugins/consent.tsx | 2 +- src/features/plugins/projection.ts | 2 +- src/features/plugins/uiPoints.tsx | 2 +- src/features/plugins/views/PluginView.tsx | 2 +- wit/exports.wit | 15 +- wit/host.wit | 13 - wit/plugin.wit | 7 +- wit/providers.wit | 26 +- wit/views.wit | 2 +- 105 files changed, 1521 insertions(+), 1582 deletions(-) create mode 100644 crates/ide-plugin/tests/api_compat.rs create mode 100644 crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm.sources delete mode 100644 docs/plugin_p0.md delete mode 100644 docs/plugin_plan.md create mode 100644 plugins/hello/dist/plugin.wasm.sources create mode 100644 plugins/official/ai-completion/dist/plugin.wasm.sources create mode 100644 plugins/official/checkstyle/dist/plugin.wasm.sources create mode 100644 plugins/official/csv-editor/dist/plugin.wasm.sources create mode 100644 plugins/official/git-toolbox/dist/plugin.wasm.sources create mode 100644 plugins/official/key-promoter/dist/plugin.wasm.sources create mode 100644 plugins/official/kubernetes/dist/plugin.wasm.sources create mode 100644 plugins/official/mybatis/dist/plugin.wasm.sources create mode 100644 plugins/official/solidity/dist/plugin.wasm.sources create mode 100644 plugins/official/string-tools/dist/plugin.wasm.sources create mode 100644 sdk/rust/openidea-plugin/tests/api_frozen.rs create mode 100644 sdk/rust/openidea-plugin/tests/wit_coverage.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a1236d6a..eca369c93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,6 +71,39 @@ jobs: - run: cargo build --locked -p ide-host --bin openidea-server --target x86_64-unknown-linux-musl - run: target/x86_64-unknown-linux-musl/debug/openidea-server --version + # The plugin SDK, the official plugins and the test fixtures build in their + # own workspaces (the root excludes them), so the jobs above don't see them. + plugins: + name: Plugins (SDK, official, fixtures) + runs-on: ubuntu-22.04 + env: + CARGO_TARGET_DIR: ${{ github.workspace }}/target-plugins + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + with: { targets: wasm32-wasip2, components: "clippy, rustfmt" } + - uses: Swatinem/rust-cache@v2 + with: + key: plugins + cache-directories: target-plugins + save-if: ${{ github.ref == 'refs/heads/main' }} + - name: Format, lint (host and wasm) and test each crate + shell: bash + run: | + set -euo pipefail + for dir in sdk/rust/openidea-plugin plugins/*/ plugins/official/*/ crates/ide-plugin/tests/fixtures/misbehave; do + [ -f "$dir/Cargo.toml" ] || continue + echo "::group::$dir" + cargo fmt --check --manifest-path "$dir/Cargo.toml" + cargo clippy --locked --all-targets --manifest-path "$dir/Cargo.toml" -- -D warnings + cargo clippy --locked --target wasm32-wasip2 --manifest-path "$dir/Cargo.toml" -- -D warnings + cargo test --locked --manifest-path "$dir/Cargo.toml" + echo "::endgroup::" + done + # The host's tests run the checked-in components: they must be built from these sources. + - name: Checked-in components are up to date + run: plugins/build.sh --check + deny: name: Cargo deny runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index ac0c58f70..37a6cf588 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4451,13 +4451,13 @@ dependencies = [ "ide-core", "ide-extension", "parking_lot", + "semver", "serde", "serde_json", "specta", "tempfile", "thiserror 2.0.21", "tokio", - "toml 0.9.12+spec-1.1.0", "tracing", "wasmtime", "wasmtime-wasi", diff --git a/crates/ide-dap/src/tests.rs b/crates/ide-dap/src/tests.rs index 181386a5f..f108c0fbc 100644 --- a/crates/ide-dap/src/tests.rs +++ b/crates/ide-dap/src/tests.rs @@ -1149,7 +1149,7 @@ async fn java_stack_frames_without_breakpoints_normalize_windows_paths() { } } -/// Plan P3: a built-in language definition (GDScript) shipped by a plugin +/// A built-in language definition (GDScript) shipped by a plugin /// instead: its debugger starts the program approved for the plugin's /// `process:godot` grant, and refuses once that file changed. #[cfg(unix)] diff --git a/crates/ide-extension/src/index.rs b/crates/ide-extension/src/index.rs index 2bb05d450..cadc0ea99 100644 --- a/crates/ide-extension/src/index.rs +++ b/crates/ide-extension/src/index.rs @@ -1,4 +1,4 @@ -//! The contribution index (docs/plugin_plan.md 4.5): validated UI +//! The contribution index: validated UI //! declarations of installed packages, kept on disk so start-up does not //! validate them again (sanitising thousands of icon SVGs, say). //! diff --git a/crates/ide-extension/src/lib.rs b/crates/ide-extension/src/lib.rs index 6595166a0..9089067ad 100644 --- a/crates/ide-extension/src/lib.rs +++ b/crates/ide-extension/src/lib.rs @@ -1,5 +1,4 @@ -//! The plugin system's kernel, independent of the Wasm runtime -//! (`docs/plugin_plan.md` 3–4): +//! The plugin system's kernel, independent of the Wasm runtime: //! //! - [`Manifest`]: `plugin.toml` (0.2 format; 0.1 still parses); //! - [`Capability`]: what a plugin may do, with its [`Risk`] level; @@ -32,6 +31,6 @@ pub use manifest::{ engine_compatible, validate_item_id, validate_plugin_id, validate_point_id, }; pub use point::{Commit, Disposable, ExtensionPoint, MAX_PACKAGE_FILE, Package, Placement, Prepared, decode}; -pub use registry::{ContributionInfo, ExtensionRegistry, PreparedSet, Scope}; +pub use registry::{ContributionInfo, ExtensionRegistry, PreparedSet}; pub use ui::{UiContribution, UiDecl, UiPoint, UiStore, to_json}; pub use when::validate_when; diff --git a/crates/ide-extension/src/package.rs b/crates/ide-extension/src/package.rs index ee8c8642e..5822a405e 100644 --- a/crates/ide-extension/src/package.rs +++ b/crates/ide-extension/src/package.rs @@ -1,4 +1,4 @@ -//! Plugin distribution (docs/plugin_plan.md 4.7): the package file, its +//! Plugin distribution: the package file, its //! signature, and the static marketplace index. //! //! - **Package** (`-.oiplugin`): a zip of the manifest, the files diff --git a/crates/ide-extension/src/registry.rs b/crates/ide-extension/src/registry.rs index 172b580d7..e8630fd85 100644 --- a/crates/ide-extension/src/registry.rs +++ b/crates/ide-extension/src/registry.rs @@ -9,14 +9,6 @@ use std::collections::BTreeMap; use std::path::PathBuf; use std::sync::Arc; -/// Manifest contributions are `Static` (re-committed on every start); `Runtime` -/// ones are registered by the plugin's code and withdrawn when it stops. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Scope { - Static, - Runtime, -} - /// A contribution as the plugin details page lists it. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] #[serde(rename_all = "camelCase")] @@ -28,9 +20,7 @@ pub struct ContributionInfo { struct Entry { point: &'static str, key: Option, - decl: toml::Value, label: String, - scope: Scope, disposable: Disposable, } @@ -43,7 +33,7 @@ pub struct PreparedSet { pub files: Vec, /// Declarations for extension points this host does not know (skipped). pub warnings: Vec, - items: Vec<(&'static str, toml::Value, Prepared)>, + items: Vec<(&'static str, Prepared)>, } impl PreparedSet { @@ -122,7 +112,7 @@ impl ExtensionRegistry { }; for (i, decl) in pkg.manifest.declarations(point_id).into_iter().enumerate() { let prepared = point - .prepare(pkg, decl.clone()) + .prepare(pkg, decl) .map_err(|e| format!("contributes.{point_id}[{i}]: {}", strip_prefix(&e, point_id)))?; for cap in &prepared.capabilities { if !set.capabilities.contains(cap) { @@ -134,7 +124,7 @@ impl ExtensionRegistry { set.files.push(f.clone()); } } - set.items.push((point.id(), decl, prepared)); + set.items.push((point.id(), prepared)); } } Ok(set) @@ -142,12 +132,12 @@ impl ExtensionRegistry { /// Makes a prepared set take effect, all or nothing: if one commit fails, /// the ones before it are disposed in reverse order. - pub fn commit(&self, set: PreparedSet, scope: Scope) -> Result<(), String> { + pub fn commit(&self, set: PreparedSet) -> Result<(), String> { let mut committed: Vec = Vec::with_capacity(set.items.len()); - for (point, decl, prepared) in set.items { + for (point, prepared) in set.items { let Prepared { key, label, commit, .. } = prepared; match commit() { - Ok(disposable) => committed.push(Entry { point, key, decl, label, scope, disposable }), + Ok(disposable) => committed.push(Entry { point, key, label, disposable }), Err(e) => { tracing::error!(plugin = %set.plugin, "contributes.{point}: commit failed: {e}"); while let Some(entry) = committed.pop() { @@ -177,31 +167,6 @@ impl ExtensionRegistry { Ok(()) } - /// Registers one declaration at runtime (a plugin's `register-command`). - /// Returns `false` when an identical contribution with the same key exists. - pub fn contribute(&self, pkg: &Package, point_id: &str, decl: toml::Value) -> Result { - let point = self.point(point_id).ok_or_else(|| format!("unknown extension point {point_id:?}"))?; - let prepared = point.prepare(pkg, decl.clone())?; - if let Some(key) = &prepared.key { - let entries = self.entries.lock(); - let same = entries.get(pkg.id()).is_some_and(|list| { - list.iter().any(|e| e.point == point.id() && e.key.as_ref() == Some(key) && e.decl == decl) - }); - if same { - return Ok(false); - } - } - let set = PreparedSet { - plugin: pkg.id().to_owned(), - capabilities: prepared.capabilities.clone(), - files: Vec::new(), - warnings: Vec::new(), - items: vec![(point.id(), decl, prepared)], - }; - self.commit(set, Scope::Runtime)?; - Ok(true) - } - /// Withdraws everything `plugin` contributed, newest first. pub fn dispose_plugin(&self, plugin: &str) -> bool { let list = self.entries.lock().remove(plugin).unwrap_or_default(); @@ -212,22 +177,6 @@ impl ExtensionRegistry { any } - /// Withdraws what `plugin`'s code registered, keeping its manifest contributions. - pub fn dispose_runtime(&self, plugin: &str) -> bool { - let removed: Vec = { - let mut entries = self.entries.lock(); - let Some(list) = entries.get_mut(plugin) else { return false }; - let (runtime, keep): (Vec, Vec) = list.drain(..).partition(|e| e.scope == Scope::Runtime); - *list = keep; - runtime - }; - let any = !removed.is_empty(); - for entry in removed.into_iter().rev() { - entry.disposable.dispose(); - } - any - } - pub fn contributions(&self, plugin: &str) -> Vec { self.entries .lock() @@ -243,14 +192,6 @@ impl ExtensionRegistry { self.entries.lock().get(plugin).map_or(0, |list| list.iter().filter(|e| e.point == point).count()) } - /// Whether `plugin` has a contribution with `key` at `point`. - pub fn has_key(&self, plugin: &str, point: &str, key: &str) -> bool { - self.entries - .lock() - .get(plugin) - .is_some_and(|list| list.iter().any(|e| e.point == point && e.key.as_deref() == Some(key))) - } - /// Plugins with at least one contribution. pub fn plugins(&self) -> Vec { self.entries.lock().iter().filter(|(_, l)| !l.is_empty()).map(|(id, _)| id.clone()).collect() @@ -332,7 +273,7 @@ mod tests { assert_eq!(set.len(), 3); assert_eq!(set.files, [PathBuf::from("x.json")]); assert!(live.lock().is_empty(), "prepare has no side effects"); - reg.commit(set, Scope::Static).unwrap(); + reg.commit(set).unwrap(); assert_eq!(*live.lock(), ["c", "a", "b"]); assert_eq!(reg.count("p", "things"), 2); assert_eq!(reg.contributions("p").len(), 3); @@ -344,7 +285,7 @@ mod tests { fn a_failed_commit_rolls_back() { let (reg, live) = setup(); let p = pkg("[[contributes.things]]\nname = \"a\"\n[[contributes.things]]\nname = \"b\"\nfail = true"); - let err = reg.commit(reg.prepare(&p).unwrap(), Scope::Static).unwrap_err(); + let err = reg.commit(reg.prepare(&p).unwrap()).unwrap_err(); assert!(err.contains("boom")); assert!(live.lock().is_empty(), "a was undone"); assert!(reg.contributions("p").is_empty()); @@ -361,24 +302,4 @@ mod tests { assert_eq!(set.warnings.len(), 1); assert!(set.warnings[0].contains("futureThing")); } - - #[test] - fn runtime_contributions_replace_by_key_and_dispose_separately() { - let (reg, live) = setup(); - let p = pkg("[[contributes.things]]\nname = \"static\""); - reg.commit(reg.prepare(&p).unwrap(), Scope::Static).unwrap(); - let decl = |name: &str| { - let mut t = toml::Table::new(); - t.insert("name".into(), name.into()); - t.insert("key".into(), true.into()); - toml::Value::Table(t) - }; - assert!(reg.contribute(&p, "things", decl("r1")).unwrap()); - assert!(!reg.contribute(&p, "things", decl("r1")).unwrap(), "identical: no change"); - assert!(reg.contribute(&p, "things", decl("r2")).unwrap()); - assert_eq!(*live.lock(), ["static", "r2"], "same key replaced r1"); - assert!(reg.contribute(&p, "nope", decl("x")).is_err()); - assert!(reg.dispose_runtime("p")); - assert_eq!(*live.lock(), ["static"]); - } } diff --git a/crates/ide-extension/src/ui.rs b/crates/ide-extension/src/ui.rs index e12bfceec..9a59029b2 100644 --- a/crates/ide-extension/src/ui.rs +++ b/crates/ide-extension/src/ui.rs @@ -216,7 +216,7 @@ pub fn to_json(v: &toml::Value) -> serde_json::Value { mod tests { use super::*; use crate::manifest::Manifest; - use crate::registry::{ExtensionRegistry, Scope}; + use crate::registry::ExtensionRegistry; #[test] fn ui_point_projects_into_the_store() { @@ -233,7 +233,7 @@ mod tests { [[contributes.badges]]\ntext = \"hi\""; let pkg = Package::new(Manifest::parse(src).unwrap(), PathBuf::from("/x")); let rev = store.revision(); - reg.commit(reg.prepare(&pkg).unwrap(), Scope::Static).unwrap(); + reg.commit(reg.prepare(&pkg).unwrap()).unwrap(); assert!(store.revision() > rev); let items = store.items("badges", Some("p")); assert_eq!(items.len(), 1); diff --git a/crates/ide-host/benches/plugins.rs b/crates/ide-host/benches/plugins.rs index 491fc974a..c0a5782f4 100644 --- a/crates/ide-host/benches/plugins.rs +++ b/crates/ide-host/benches/plugins.rs @@ -1,5 +1,4 @@ -//! `cargo bench -p ide-host`: the plugin runtime budgets of -//! docs/plugin_plan.md 4.5, checked by `pnpm perf:rust` against +//! `cargo bench -p ide-host`: the plugin runtime budgets, checked by `pnpm perf:rust` against //! `perf/budgets.json` › rust (`plugins/…`). //! //! - `load_50`: start-up load of 50 installed plugins (20 with code, 10 diff --git a/crates/ide-host/src/bin/openidea-plugin/main.rs b/crates/ide-host/src/bin/openidea-plugin/main.rs index d55fb96ed..99b736308 100644 --- a/crates/ide-host/src/bin/openidea-plugin/main.rs +++ b/crates/ide-host/src/bin/openidea-plugin/main.rs @@ -338,19 +338,10 @@ fn check(dir: &Path) -> Result<(), String> { if let Some(wasm) = &manifest.wasm { let engine = PluginEngine::new()?; let component = engine.compile(&dir.join(wasm))?; - let foreign: Vec = engine - .imports(&component) - .into_iter() - .filter(|i| !(i.starts_with("openidea:plugin/") || i.starts_with("wasi:"))) - .collect(); - if !foreign.is_empty() { - return Err(format!( - "the component imports interfaces the IDE does not provide: {}", - foreign.join(", ") - )); - } + let built_for = engine.check_component(&component)?; let size = std::fs::metadata(dir.join(wasm)).map(|m| m.len()).unwrap_or(0); - println!(" code: {} ({})", wasm.display(), human_size(size)); + let api = built_for.map(|v| format!(", plugin API {}.{}", v.major, v.minor)).unwrap_or_default(); + println!(" code: {} ({}{api})", wasm.display(), human_size(size)); } else { println!(" code: none (contributions only)"); } diff --git a/crates/ide-host/src/ops/lsp.rs b/crates/ide-host/src/ops/lsp.rs index 984466712..4e19ba134 100644 --- a/crates/ide-host/src/ops/lsp.rs +++ b/crates/ide-host/src/ops/lsp.rs @@ -79,28 +79,40 @@ async fn plugin_call(f: impl FnOnce() -> T + Send + 'static) /// Items from plugins carry no server (resolve and commands don't apply). const PLUGIN_SERVER: ServerId = ServerId(0); +/// A plugin's `completion-item.kind` (wit/providers.wit lists them). +fn plugin_completion_kind(kind: &str) -> Option { + Some(match kind { + "text" => CompletionKind::Text, + "method" => CompletionKind::Method, + "function" => CompletionKind::Function, + "constructor" => CompletionKind::Constructor, + "field" => CompletionKind::Field, + "variable" => CompletionKind::Variable, + "class" => CompletionKind::Class, + "interface" => CompletionKind::Interface, + "module" => CompletionKind::Module, + "property" => CompletionKind::Property, + "unit" => CompletionKind::Unit, + "value" => CompletionKind::Value, + "enum" => CompletionKind::Enum, + "keyword" => CompletionKind::Keyword, + "snippet" => CompletionKind::Snippet, + "color" => CompletionKind::Color, + "file" => CompletionKind::File, + "reference" => CompletionKind::Reference, + "folder" => CompletionKind::Folder, + "enum-member" => CompletionKind::EnumMember, + "constant" => CompletionKind::Constant, + "struct" => CompletionKind::Struct, + "event" => CompletionKind::Event, + "operator" => CompletionKind::Operator, + "type-parameter" => CompletionKind::TypeParameter, + _ => return None, + }) +} + fn plugin_completion(item: ide_plugin::CompletionItem) -> CompletionItem { - let kind = item.kind.as_deref().and_then(|k| { - Some(match k { - "text" => CompletionKind::Text, - "method" => CompletionKind::Method, - "function" => CompletionKind::Function, - "constructor" => CompletionKind::Constructor, - "field" => CompletionKind::Field, - "variable" => CompletionKind::Variable, - "class" => CompletionKind::Class, - "interface" => CompletionKind::Interface, - "module" => CompletionKind::Module, - "property" => CompletionKind::Property, - "keyword" => CompletionKind::Keyword, - "snippet" => CompletionKind::Snippet, - "value" => CompletionKind::Value, - "constant" => CompletionKind::Constant, - "file" => CompletionKind::File, - "reference" => CompletionKind::Reference, - _ => return None, - }) - }); + let kind = item.kind.as_deref().and_then(plugin_completion_kind); CompletionItem { label: item.label, label_detail: None, @@ -715,3 +727,28 @@ pub(crate) fn register(d: &mut Dispatcher) { lsp_execute_command(&c.host, &c.window, c.arg("doc")?, c.arg("command")?).await }); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn every_completion_kind_the_plugin_api_documents_is_understood() { + let wit = include_str!("../../../../wit/providers.wit"); + let doc: String = wit + .lines() + .map(|l| l.trim().trim_start_matches("///").trim()) + .skip_while(|l| !l.starts_with("Icon and sort group:")) + .take_while(|l| !l.starts_with("kind:")) + .collect::>() + .join(" "); + let list = doc.strip_prefix("Icon and sort group:").unwrap().split(". Unknown").next().unwrap(); + let list = list.replace(" or ", ", "); + let kinds: Vec<&str> = list.split(',').map(str::trim).filter(|k| !k.is_empty()).collect(); + assert_eq!(kinds.len(), 25, "{kinds:?}"); + for k in kinds { + assert!(plugin_completion_kind(k).is_some(), "{k:?}"); + } + assert_eq!(plugin_completion_kind("nonsense"), None); + } +} diff --git a/crates/ide-host/src/plugins/api.rs b/crates/ide-host/src/plugins/api.rs index da79e228c..22726195d 100644 --- a/crates/ide-host/src/plugins/api.rs +++ b/crates/ide-host/src/plugins/api.rs @@ -1,9 +1,9 @@ //! [`HostApi`] for one plugin's code: what its imports reach in the IDE. //! Capabilities were checked by the runtime before any of this runs. -use super::{COMMANDS, MAX_COMMANDS, PluginEvent, PluginEventSink}; +use super::{PluginEvent, PluginEventSink}; use ide_core::{CancellationToken, TextEdit}; -use ide_extension::{ExtensionRegistry, Package}; +use ide_extension::Package; use ide_language::ProgramPin; use ide_plugin::{ EventKind, HostApi, HostError, HttpRequest, HttpResponse, NotificationLevel, ProcessOutput, RepositoryStatus, @@ -143,7 +143,6 @@ struct TaskEntry { /// State shared by the host and every plugin's [`PluginApi`]. pub(super) struct Shared { pub sink: Arc, - pub registry: Arc, pub workspace: RwLock>>, /// `/.data`: one directory per plugin id. pub data_dir: PathBuf, @@ -163,7 +162,6 @@ pub(super) struct Shared { impl Shared { pub fn new( sink: Arc, - registry: Arc, data_dir: PathBuf, pins: Arc>>, ui: Arc, @@ -171,7 +169,6 @@ impl Shared { Arc::new(Self { ui, sink, - registry, workspace: RwLock::new(None), secrets: RwLock::new(Arc::new(FileSecretStore { dir: data_dir.clone() })), pins, @@ -320,18 +317,6 @@ impl HostApi for PluginApi { .emit(None, PluginEvent::StatusItemChanged { plugin_id: self.id().to_owned(), id: id.to_owned(), item }); } - fn contribute(&self, point: &str, decl: toml::Value) -> Result { - if point == COMMANDS { - let key = decl.get("id").and_then(|v| v.as_str()).unwrap_or_default(); - if self.shared.registry.count(self.id(), COMMANDS) >= MAX_COMMANDS - && !self.shared.registry.has_key(self.id(), COMMANDS, key) - { - return Err(format!("too many commands (max {MAX_COMMANDS})")); - } - } - self.shared.registry.contribute(&self.pkg, point, decl) - } - fn projects(&self) -> Vec { self.shared.workspace().map(|w| w.projects()).unwrap_or_default() } @@ -754,13 +739,7 @@ mod tests { ) .unwrap(); let sink: Arc = Arc::new(|_: Option<&str>, _: PluginEvent| {}); - let shared = Shared::new( - sink, - Arc::new(ExtensionRegistry::new()), - dir.join(".data"), - pins, - ide_extension::UiStore::new(), - ); + let shared = Shared::new(sink, dir.join(".data"), pins, ide_extension::UiStore::new()); PluginApi { pkg: Package::new(manifest, dir.to_path_buf()), shared } } diff --git a/crates/ide-host/src/plugins/install.rs b/crates/ide-host/src/plugins/install.rs index 1409b5f81..5056300a9 100644 --- a/crates/ide-host/src/plugins/install.rs +++ b/crates/ide-host/src/plugins/install.rs @@ -1,5 +1,5 @@ //! Copying a package into the plugins directory without ever leaving the -//! plugin missing (docs/plugin_plan.md 4.7). Installed plugins are versioned: +//! plugin missing. Installed plugins are versioned: //! `///` plus an `active` pointer file (and `previous`, //! the version to roll back to). A new version is written to a staging //! directory, renamed into place, and only then made active by replacing the diff --git a/crates/ide-host/src/plugins/market.rs b/crates/ide-host/src/plugins/market.rs index 8e34c2b63..771ac3597 100644 --- a/crates/ide-host/src/plugins/market.rs +++ b/crates/ide-host/src/plugins/market.rs @@ -1,4 +1,4 @@ -//! The marketplace (docs/plugin_plan.md 4.7): a static index (`index.json` + +//! The marketplace: a static index (`index.json` + //! packages, served from any URL or folder) lists publishers and their keys, //! plugins, and revocations. The host keeps the last index it read //! (`market-index.json`), checks signatures against it, and disables what it diff --git a/crates/ide-host/src/plugins/mod.rs b/crates/ide-host/src/plugins/mod.rs index 4ca93b2fb..745be9baf 100644 --- a/crates/ide-host/src/plugins/mod.rs +++ b/crates/ide-host/src/plugins/mod.rs @@ -1,4 +1,4 @@ -//! The plugin host (`docs/plugin_plan.md` 3): assembles the extension +//! The plugin host: assembles the extension //! registry (`ide-extension`), the grant ledger and the Wasm runtime //! (`ide-plugin`). It runs where the project is — in the desktop app for local //! projects, in `openidea-server` for remote ones; the desktop shell only adapts @@ -32,7 +32,7 @@ pub mod views; pub use api::{FileSecretStore, HostWorkspace, PluginWorkspace, SecretStore}; pub use icons::{map_asset as icon_theme_map_asset, sanitize_svg, svg_prefix as icon_theme_svg_prefix}; pub use market::{INDEX_FILE as MARKET_INDEX_FILE, MarketPlugin}; -pub use points::{COMMANDS, CommandDecl, MAX_COMMANDS, register_builtin_points}; +pub use points::{COMMANDS, CommandDecl, register_builtin_points}; pub(crate) use providers::editor_providers as editor_providers_for; pub use providers::{ProviderKind, set_editor_providers}; pub use theme::{ThemeDef, ThemeKind}; @@ -42,7 +42,7 @@ use ide_core::{IdeError, IdeResult, LanguageId}; use ide_extension::ledger::{self, BinaryGrant, Decision, Ledger, Request, Subject}; use ide_extension::{ ActivationEvent, Capability, CapabilityInfo, ContributionIndex, ExtensionRegistry, MANIFEST_FILE, Manifest, - Package, Scope, UiContribution, UiStore, + Package, UiContribution, UiStore, }; use ide_language::plugin_point::PluginLanguages; use ide_language::{LanguageConfig, ProgramPin}; @@ -192,8 +192,6 @@ struct Batches { pending: u32, /// Kinds of the batch being handled right now. running: Option>, - /// A finished batch changed contributions: the next flush announces it. - changed: bool, } impl LoadedPlugin { @@ -352,8 +350,7 @@ impl PluginHost { languages.set_process_policy(Arc::new(move |plugin, cap| { policy_pins.read().get(&(plugin.to_owned(), cap.to_string())).cloned() })); - let shared = - api::Shared::new(sink.clone(), registry.clone(), plugins_dir.join(".data"), pins.clone(), ui.clone()); + let shared = api::Shared::new(sink.clone(), plugins_dir.join(".data"), pins.clone(), ui.clone()); let market = RwLock::new(Self::load_market_index(&plugins_dir)); let host = Self { market, @@ -997,7 +994,7 @@ impl PluginHost { self.declared_view(id, view)?; let view = view.to_owned(); self.code_call(id, invocation, "handling the view's event", move |rt, inv| { - rt.view_event(&view, event.clone(), inv).map(|fx| ((), fx)) + rt.view_event(&view, event.clone(), inv) }) } @@ -1016,7 +1013,7 @@ impl PluginHost { id: &str, invocation: Invocation, what: &str, - f: impl Fn(&mut Runtime, Invocation) -> Result<(R, ide_plugin::CallEffects), CallError> + Clone + Send + 'static, + f: impl Fn(&mut Runtime, Invocation) -> Result + Clone + Send + 'static, ) -> IdeResult { let plugin = self.get(id)?; let client = invocation.client.clone(); @@ -1061,16 +1058,17 @@ impl PluginHost { let result = actor.call(None, move |rt| call(rt, inv)); if !matches!(result, Err(ActorError::Stopped | ActorError::SoftTimeout | ActorError::Cancelled)) { let crashed = matches!(result, Err(ActorError::Call(CallError::Trap(_)))); - self.record_call(&plugin, started.elapsed(), result.is_ok(), !crashed); + // Cancelling or a missing grant is not the plugin failing. + let ok = match &result { + Ok(_) => true, + Err(ActorError::Call(CallError::Guest(e))) => !e.is_failure(), + Err(_) => false, + }; + self.record_call(&plugin, started.elapsed(), ok, !crashed); } match result { - Ok((value, fx)) => { - if fx.contributions_changed { - self.changed(); - } - return Ok(value); - } - Err(ActorError::Call(CallError::Guest(e))) => return Err(IdeError::internal(e)), + Ok(value) => return Ok(value), + Err(ActorError::Call(CallError::Guest(e))) => return Err(e.into()), Err(ActorError::Call(CallError::Trap(e))) => { self.reap(&plugin, &actor, client.as_deref()); return Err(IdeError::internal(format!("plugin {id:?} {e}"))); @@ -1274,10 +1272,9 @@ impl PluginHost { for ((id, client), events) in due { changed |= self.deliver(&id, client, events); } - // Batches that finished since: contributions they changed, crashes. + // Batches that crashed since. let plugins: Vec<_> = self.plugins.read().values().cloned().collect(); for p in plugins { - changed |= std::mem::take(&mut p.batches.lock().changed); let dead = p.runtime.lock().clone().filter(|a| !a.is_alive()); if let Some(actor) = dead { self.reap(&p, &actor, None); @@ -1354,7 +1351,6 @@ impl PluginHost { let mut batches = finish.lock(); batches.pending = batches.pending.saturating_sub(1); batches.running = None; - batches.changed |= result.is_ok_and(|fx| fx.contributions_changed); }, ); activated @@ -1590,7 +1586,9 @@ impl PluginHost { ))); } // Precompiled on the way: the installed copy has the same bytes. - self.engine.compile_cached(&src.join(wasm), &self.component_cache()).map_err(IdeError::invalid)?; + let component = + self.engine.compile_cached(&src.join(wasm), &self.component_cache()).map_err(IdeError::invalid)?; + self.engine.check_component(&component).map_err(IdeError::invalid)?; } Ok((pkg, set)) } @@ -1689,7 +1687,7 @@ impl PluginHost { return; } }; - if let Err(e) = self.registry.commit(set, Scope::Static) { + if let Err(e) = self.registry.commit(set) { return self.set_failed(plugin, e); } { diff --git a/crates/ide-host/src/plugins/points.rs b/crates/ide-host/src/plugins/points.rs index f6f32f9e3..9874bd400 100644 --- a/crates/ide-host/src/plugins/points.rs +++ b/crates/ide-host/src/plugins/points.rs @@ -10,8 +10,6 @@ use serde::{Deserialize, Serialize}; use std::sync::Arc; pub const COMMANDS: &str = "commands"; -/// Most commands one plugin may register. -pub const MAX_COMMANDS: usize = 256; const MAX_TITLE: usize = 256; /// `[[contributes.commands]]`: an action `plugin..` in Search diff --git a/crates/ide-host/src/plugins/providers.rs b/crates/ide-host/src/plugins/providers.rs index aa457b54c..d60b806a2 100644 --- a/crates/ide-host/src/plugins/providers.rs +++ b/crates/ide-host/src/plugins/providers.rs @@ -1,4 +1,4 @@ -//! Editor providers of plugins (docs/plugin_plan.md P6): completion, inline +//! Editor providers of plugins: completion, inline //! completion, code actions, go to definition and gutter line markers, for //! the languages a plugin declares. The host calls only plugins whose //! declaration matches, so the others stay unloaded. @@ -160,8 +160,8 @@ pub(super) fn secret(_pkg: &Package, decl: toml::Value) -> Result( + /// Edit-path calls (soft / hard deadline) to every usable plugin that + /// declares `kind` for `language`, in plugin order, leaving out those that + /// failed or were late. Each plugin runs on its own thread, so the calls + /// run at the same time: the wait is the slowest plugin's, not the sum. + fn edit_calls( &self, - plugin: &Arc, - f: impl FnOnce(&mut Runtime, Duration) -> Result + Send + 'static, - ) -> Option { - let (actor, activated) = self.ensure_active(plugin, None).ok()?; - if activated { - self.changed(); - } - let started = Instant::now(); - let hard = self.limits.edit_hard; - let result = actor.call(Some(self.limits.edit_soft), move |rt| f(rt, hard)); - self.finish_provider_call(plugin, &actor, started, result) + kind: ProviderKind, + language: &str, + f: impl FnOnce(&mut Runtime, Duration) -> Result + Clone + Send + 'static, + ) -> Vec<(Arc, R)> { + let (soft, hard) = (self.limits.edit_soft, self.limits.edit_hard); + let queued: Vec<_> = self + .provider_plugins(kind, language) + .into_iter() + .filter_map(|plugin| { + let (actor, activated) = self.ensure_active(&plugin, None).ok()?; + if activated { + self.changed(); + } + let f = f.clone(); + let started = Instant::now(); + let call = actor.submit(Some(soft), move |rt| f(rt, hard)); + Some((plugin, actor, started, call)) + }) + .collect(); + queued + .into_iter() + .filter_map(|(plugin, actor, started, call)| { + let result = call.and_then(Pending::wait); + self.finish_provider_call(&plugin, &actor, started, result).map(|r| (plugin, r)) + }) + .collect() } pub(super) fn finish_provider_call( @@ -233,7 +251,7 @@ impl PluginHost { } Err(ActorError::Cancelled) => None, Err(ActorError::Call(CallError::Guest(e))) => { - self.record_call(plugin, elapsed, false, true); + self.record_call(plugin, elapsed, !e.is_failure(), true); tracing::debug!(plugin = %plugin.id(), "provider: {e}"); None } @@ -251,16 +269,13 @@ impl PluginHost { at: ide_core::Pos, trigger: Option<&str>, ) -> Vec<(String, CompletionItem)> { - let mut out = Vec::new(); - for p in self.provider_plugins(ProviderKind::Completion, &doc.language) { - let (d, t) = (doc.clone(), trigger.map(str::to_owned)); - if let Some(items) = - self.edit_call(&p, move |rt, timeout| rt.provide_completion(&d, at, t.as_deref(), timeout)) - { - out.extend(items.into_iter().map(|i| (p.id().to_owned(), i))); - } - } - out + let (d, t) = (Arc::new(doc.clone()), trigger.map(str::to_owned)); + self.edit_calls(ProviderKind::Completion, &doc.language, move |rt, timeout| { + rt.provide_completion(&d, at, t.as_deref(), timeout) + }) + .into_iter() + .flat_map(|(p, items)| items.into_iter().map(move |i| (p.id().to_owned(), i))) + .collect() } pub fn provide_code_actions( @@ -268,38 +283,33 @@ impl PluginHost { doc: &ProviderDocument, selection: ide_core::Range, ) -> Vec<(String, CodeAction)> { - let mut out = Vec::new(); - for p in self.provider_plugins(ProviderKind::CodeActions, &doc.language) { - let d = doc.clone(); - if let Some(actions) = - self.edit_call(&p, move |rt, timeout| rt.provide_code_actions(&d, selection, timeout)) - { - out.extend(actions.into_iter().map(|a| (p.id().to_owned(), a))); - } - } - out + let d = Arc::new(doc.clone()); + self.edit_calls(ProviderKind::CodeActions, &doc.language, move |rt, timeout| { + rt.provide_code_actions(&d, selection, timeout) + }) + .into_iter() + .flat_map(|(p, actions)| actions.into_iter().map(move |a| (p.id().to_owned(), a))) + .collect() } pub fn provide_definition(&self, doc: &ProviderDocument, at: ide_core::Pos) -> Vec { - let mut out = Vec::new(); - for p in self.provider_plugins(ProviderKind::Definition, &doc.language) { - let d = doc.clone(); - if let Some(locations) = self.edit_call(&p, move |rt, timeout| rt.provide_definition(&d, at, timeout)) { - out.extend(locations); - } - } - out + let d = Arc::new(doc.clone()); + self.edit_calls(ProviderKind::Definition, &doc.language, move |rt, timeout| { + rt.provide_definition(&d, at, timeout) + }) + .into_iter() + .flat_map(|(_, locations)| locations) + .collect() } pub fn provide_line_markers(&self, doc: &ProviderDocument) -> Vec<(String, LineMarker)> { - let mut out = Vec::new(); - for p in self.provider_plugins(ProviderKind::LineMarkers, &doc.language) { - let d = doc.clone(); - if let Some(markers) = self.edit_call(&p, move |rt, timeout| rt.provide_line_markers(&d, timeout)) { - out.extend(markers.into_iter().map(|m| (p.id().to_owned(), m))); - } - } - out + let d = Arc::new(doc.clone()); + self.edit_calls(ProviderKind::LineMarkers, &doc.language, move |rt, timeout| { + rt.provide_line_markers(&d, timeout) + }) + .into_iter() + .flat_map(|(p, markers)| markers.into_iter().map(move |m| (p.id().to_owned(), m))) + .collect() } /// Ghost text from the first plugin that has some. Not bound by the diff --git a/crates/ide-host/src/plugins/remote.rs b/crates/ide-host/src/plugins/remote.rs index b1d8ffa21..e3666cf47 100644 --- a/crates/ide-host/src/plugins/remote.rs +++ b/crates/ide-host/src/plugins/remote.rs @@ -1,5 +1,4 @@ -//! Plugin languages of a remote project (docs/plugin_plan.md 3, placement -//! rules): `Workspace` contributions take effect where the project is, so the +//! Plugin languages of a remote project: `Workspace` contributions take effect where the project is, so the //! desktop pushes its plugins' language definitions to `openidea-server` //! (`plugin_sync_languages`) together with the `process:` programs the user //! granted. The server resolves each granted program on its own `PATH`, pins diff --git a/crates/ide-host/src/plugins/remote_host.rs b/crates/ide-host/src/plugins/remote_host.rs index 86986e57a..2514ee809 100644 --- a/crates/ide-host/src/plugins/remote_host.rs +++ b/crates/ide-host/src/plugins/remote_host.rs @@ -1,4 +1,4 @@ -//! Code plugins of remote projects (docs/plugin_plan.md 3, placement rule 1): +//! Code plugins of remote projects: //! Wasm runs where the project is, so `openidea-server` hosts the desktop's //! code plugins and they read, search, run processes and publish diagnostics //! on the remote machine. diff --git a/crates/ide-host/src/plugins/run_detectors.rs b/crates/ide-host/src/plugins/run_detectors.rs index 0fdb92c91..c48b001e6 100644 --- a/crates/ide-host/src/plugins/run_detectors.rs +++ b/crates/ide-host/src/plugins/run_detectors.rs @@ -1,6 +1,5 @@ //! The `runDetectors` extension point: run configurations a plugin offers in -//! projects that contain a marker file (docs/plugin_plan.md P3, "open -//! `ide-run` detection"). +//! projects that contain a marker file. //! //! ```toml //! [[contributes.runDetectors]] diff --git a/crates/ide-host/src/plugins/theme.rs b/crates/ide-host/src/plugins/theme.rs index 2de8eabdb..e4f38ff51 100644 --- a/crates/ide-host/src/plugins/theme.rs +++ b/crates/ide-host/src/plugins/theme.rs @@ -96,11 +96,9 @@ impl ThemesPoint { #[derive(Deserialize)] #[serde(rename_all = "kebab-case", deny_unknown_fields)] struct Decl { - path: Option, + path: PathBuf, #[serde(default)] editor_scheme: Option, - /// The theme JSON inline (runtime `register-theme`). - definition: Option, } impl ExtensionPoint for ThemesPoint { @@ -114,15 +112,9 @@ impl ExtensionPoint for ThemesPoint { fn prepare(&self, pkg: &Package, decl: toml::Value) -> Result { let decl: Decl = decode(POINT_ID, decl)?; - let mut files = Vec::new(); - let mut theme = match (&decl.path, &decl.definition) { - (Some(path), None) => { - files.push(path.clone()); - ThemeDef::parse(&pkg.read_text(path)?).map_err(|e| format!("{}: {e}", path.display()))? - } - (None, Some(def)) => ThemeDef::parse(def)?, - _ => return Err("give either `path` or `definition`".into()), - }; + let path = &decl.path; + let mut theme = ThemeDef::parse(&pkg.read_text(path)?).map_err(|e| format!("{}: {e}", path.display()))?; + let mut files = vec![path.clone()]; if let Some(scheme) = &decl.editor_scheme { files.push(scheme.clone()); theme.merge_editor_scheme(&pkg.read_text(scheme)?).map_err(|e| format!("{}: {e}", scheme.display()))?; diff --git a/crates/ide-host/src/plugins/ui_points.rs b/crates/ide-host/src/plugins/ui_points.rs index 538e402e4..88d3c89c6 100644 --- a/crates/ide-host/src/plugins/ui_points.rs +++ b/crates/ide-host/src/plugins/ui_points.rs @@ -1,4 +1,4 @@ -//! Declarative UI contributions (docs/plugin_plan.md P7): menus, key +//! Declarative UI contributions: menus, key //! bindings, status bar items and settings (L0, shown before any code runs), //! and the views and custom editors whose content plugin code renders (L2). //! Everything is validated here; the client only projects the JSON. diff --git a/crates/ide-host/src/plugins/views.rs b/crates/ide-host/src/plugins/views.rs index e096ddb30..fd4ed77cf 100644 --- a/crates/ide-host/src/plugins/views.rs +++ b/crates/ide-host/src/plugins/views.rs @@ -1,4 +1,4 @@ -//! Declarative views and custom editors (docs/plugin_plan.md P7) as the +//! Declarative views and custom editors as the //! window commands `plugin_view_render` / `plugin_view_event` serve them — //! the desktop for local projects, `openidea-server` for remote ones. diff --git a/crates/ide-host/tests/distribution.rs b/crates/ide-host/tests/distribution.rs index f7a1ac64a..8be3f21aa 100644 --- a/crates/ide-host/tests/distribution.rs +++ b/crates/ide-host/tests/distribution.rs @@ -1,4 +1,4 @@ -//! Plan P8: versioned installs with automatic rollback, signed packages from +//! Versioned installs with automatic rollback, signed packages from //! a static marketplace index, and revocation. use ide_extension::package::{self, Index, IndexEntry, KeyEntry, Publisher, PublisherKey, Revocation}; diff --git a/crates/ide-host/tests/plugins.rs b/crates/ide-host/tests/plugins.rs index 80611eb51..0dcaf7b4e 100644 --- a/crates/ide-host/tests/plugins.rs +++ b/crates/ide-host/tests/plugins.rs @@ -6,6 +6,7 @@ //! `plugins/build.sh crates/ide-plugin/tests/fixtures/misbehave` after //! changing their source or `wit/`. +use ide_core::error::ErrorKind; use ide_host::plugins::*; use ide_plugin::{EditorContext, HostError, Invocation, Limits}; use parking_lot::Mutex; @@ -292,14 +293,17 @@ fn hello_plugin_commands() { // Without an editor it fails cleanly; guest errors don't disable the plugin. assert!(f.run("hello", "word-count", None).unwrap_err().contains("no file is open")); - assert!(f.run_in(&editor, "hello", "nope").unwrap_err().contains("unknown command")); + let inv = Invocation { editor: Some(editor.clone()), ..f.inv() }; + let err = f.host.run_command("hello", "nope", None, inv).unwrap_err(); + assert_eq!(err.kind, ErrorKind::NotFound, "{err:?}"); assert_eq!(f.status("hello"), PluginStatus::Active); let stats = f.host.info_of("hello").unwrap().stats; assert_eq!(stats.activations, 1); - assert!(stats.calls >= 4 && stats.errors >= 2, "{stats:?}"); + // "no file is open" is the plugin failing; an unknown command is not. + assert!(stats.calls >= 4 && stats.errors == 1, "{stats:?}"); } -/// Plan P5: edits name the document version they were computed for. +/// Edits name the document version they were computed for. #[test] fn edits_are_refused_when_the_document_changed() { use ide_plugin::{HostApi, Runtime, RuntimeSetup}; @@ -344,20 +348,26 @@ fn read_file_is_confined_to_the_project() { assert_eq!(f.status("misbehave"), PluginStatus::Active); } -/// Plan P5: a missing capability is an error the plugin can handle, not a trap. +/// A missing capability is an error the plugin can handle, not a trap. #[test] fn ungranted_capabilities_are_denied_not_trapped() { let f = fixture(Limits::default()); f.install(&misbehave_pkg()); let editor = f.workspace.open("src/notes.txt", &[]); - let err = f.run_in(&editor, "misbehave", "insert").unwrap_err(); - assert!(err.contains("Denied") && err.contains("editor.write"), "{err}"); + let inv = Invocation { editor: Some(editor.clone()), ..f.inv() }; + let err = f.host.run_command("misbehave", "insert", None, inv).unwrap_err(); + assert_eq!(err.kind, ErrorKind::PermissionDenied, "{err:?}"); + assert!(err.message.contains("editor.write"), "{err:?}"); let info = f.host.info_of("misbehave").unwrap(); assert_eq!((info.status, info.stats.crashes), (PluginStatus::Active, 0), "the instance lives on"); + assert_eq!(info.stats.errors, 0, "a missing grant is not the plugin failing"); + let err = f.host.run_command("misbehave", "cancel", None, f.inv()).unwrap_err(); + assert_eq!(err.kind, ErrorKind::Cancelled, "{err:?}"); + assert_eq!(f.host.info_of("misbehave").unwrap().stats.errors, 0, "nor is the user cancelling"); assert_eq!(f.workspace.text("src/notes.txt"), "one two three\nfour five\n"); } -/// Plan P4: a trap drops the instance, which restarts on next use after a +/// A trap drops the instance, which restarts on next use after a /// backoff that doubles; only consecutive crashes stop the plugin. #[test] fn crashes_restart_with_backoff_until_the_circuit_opens() { @@ -457,7 +467,7 @@ fn memory_limit_traps() { assert_eq!(f.host.info_of("misbehave").unwrap().stats.crashes, 1); } -/// Plan P4: a running call can be interrupted from another thread; the +/// A running call can be interrupted from another thread; the /// instance is rebuilt on next use without counting as a crash. #[test] fn running_calls_can_be_cancelled() { @@ -493,7 +503,7 @@ fn misbehave_with_language(dir: &Path) -> PathBuf { pkg } -/// Plan P4: edit-path calls stop waiting at the soft deadline (the result is +/// Edit-path calls stop waiting at the soft deadline (the result is /// dropped, the instance kept) and are interrupted at the hard one. #[test] fn edit_path_calls_have_soft_and_hard_deadlines() { @@ -537,7 +547,56 @@ fn edit_path_calls_have_soft_and_hard_deadlines() { assert_eq!(host.info_of("misbehave").unwrap().stats.activations, 2); } -/// Plan P4: idle instances are reclaimed; the plugin activates again on its +/// Editor providers of several plugins are asked at the same time: the wait +/// is the slowest plugin's, not the sum, so each has the whole budget. +#[test] +fn providers_of_several_plugins_run_concurrently() { + let tmp = tempfile::tempdir().unwrap(); + let limits = Limits { + edit_soft: Duration::from_millis(300), + edit_hard: Duration::from_secs(2), + fuel: u64::MAX, + ..Limits::default() + }; + let host = + Arc::new(PluginHost::new(tmp.path().join("plugins"), Arc::new(|_: Option<&str>, _| {}), limits).unwrap()); + host.load_all(); + host.startup_finished(); + for id in ["slow-a", "slow-b"] { + let pkg = tmp.path().join(id); + copy_dir(&misbehave_pkg(), &pkg); + std::fs::write( + pkg.join("plugin.toml"), + format!( + "id = \"{id}\"\nname = \"{id}\"\nversion = \"0.0.1\"\nengines.openidea = \"^1.0\"\n\ + wasm = \"dist/plugin.wasm\"\nactivation = [\"on-language:text\"]\n\ + [[contributes.providers]]\nkinds = [\"completion\"]\nlanguages = [\"text\"]\n" + ), + ) + .unwrap(); + host.install_from_dir(&pkg, &Consent::all()).unwrap(); + } + let doc = |text: &str| ide_plugin::ProviderDocument { + path: "/p/a.txt".into(), + language: "text".into(), + version: 1, + text: text.into(), + }; + let at = ide_core::Pos::new(0, 0); + // Activation is not what is measured. + assert_eq!(host.provide_completion(&doc("fast"), at, None).len(), 2); + + let started = Instant::now(); + let items = host.provide_completion(&doc("sleep:200"), at, None); + let elapsed = started.elapsed(); + let mut from: Vec<&str> = items.iter().map(|(p, _)| p.as_str()).collect(); + from.sort(); + assert_eq!(from, ["slow-a", "slow-b"], "both within the budget"); + assert!(elapsed < Duration::from_millis(350), "one after the other would take 400ms: {elapsed:?}"); + assert_eq!(host.info_of("slow-b").unwrap().stats.slow_calls, 0); +} + +/// Idle instances are reclaimed; the plugin activates again on its /// next use, and `on-schedule` activations don't keep it alive. #[test] fn idle_instances_are_reclaimed_and_schedules_reactivate() { @@ -553,7 +612,7 @@ fn idle_instances_are_reclaimed_and_schedules_reactivate() { assert_eq!(f.host.instances(), 0); let info = f.host.info_of("misbehave").unwrap(); assert_eq!((info.status, info.stats.reclaims), (PluginStatus::Inactive, 1)); - assert_eq!(f.command_titles("misbehave").len(), 9, "runtime contributions stay"); + assert_eq!(f.command_titles("misbehave").len(), 8, "contributions stay"); assert_eq!(f.run("misbehave", "echo", Some("y")).unwrap().as_deref(), Some("y")); assert_eq!(f.host.info_of("misbehave").unwrap().stats.activations, 2); @@ -575,27 +634,6 @@ fn idle_instances_are_reclaimed_and_schedules_reactivate() { assert_eq!(f.status("hello"), PluginStatus::Inactive, "reclaimed again"); } -#[test] -fn runtime_language_and_theme_registration() { - let f = fixture(Limits::default()); - f.install(&misbehave_pkg()); - let lang = "id = \"zz\"\nname = \"ZZ\"\nextensions = [\"zz\"]"; - f.run("misbehave", "language", Some(lang)).unwrap(); - assert!(f.run("misbehave", "language", Some("not toml [")).unwrap_err().contains("invalid language")); - assert_eq!(f.host.languages().iter().map(|l| l.id.to_string()).collect::>(), ["zz"]); - assert_eq!(f.host.languages()[0].extensions, ["zz"]); - - let theme = r##"{"id":"t1","name":"T1","kind":"light","tokens":{"--bg-app":"#fff"}}"##; - f.run("misbehave", "theme", Some(theme)).unwrap(); - assert!( - f.run("misbehave", "theme", Some(r#"{"id":"t2","name":"x","kind":"dark","tokens":{"--a":"url(x)"}}"#)).is_err() - ); - assert_eq!(f.theme_kinds("misbehave"), [("t1".to_owned(), ThemeKind::Light)]); - let active = f.host.set_theme(Some(("misbehave", "t1"))).unwrap().unwrap(); - assert_eq!(active.theme.tokens["--bg-app"], "#fff"); - assert!(f.host.set_theme(Some(("misbehave", "nope"))).is_err()); -} - #[test] fn contribution_only_plugins() { let f = fixture(Limits::default()); @@ -953,7 +991,7 @@ fn replaced_binaries_are_refused() { assert!(err.message.contains("confirm"), "{}", err.message); } -/// Plan P3: a declaration-only language plugin reaches detection and LSP of +/// A declaration-only language plugin reaches detection and LSP of /// every project; its server starts only while `process:` is granted, from /// the approved file. #[cfg(unix)] @@ -1005,7 +1043,7 @@ fn plugin_languages_join_the_registry_and_start_only_granted_servers() { assert_eq!(project.detect(Path::new("/p/a.toy"), None), LanguageId::plain_text()); } -/// Plan P3: a plugin provides the syntax of a language the IDE has no grammar +/// A plugin provides the syntax of a language the IDE has no grammar /// for (the official Solidity plugin, built with the SDK), activated on first use. #[test] fn plugin_syntax_for_languages_without_a_grammar() { @@ -1050,7 +1088,7 @@ fn plugin_syntax_for_languages_without_a_grammar() { ide_language::set_external_syntax(None); } -/// Plan P3: plugins add run configurations to `ide-run`'s detection; the +/// Plugins add run configurations to `ide-run`'s detection; the /// command runs only while granted, from the approved file. #[cfg(unix)] #[test] @@ -1102,7 +1140,7 @@ fn run_detectors_offer_granted_commands_in_marked_projects() { assert!(detection.launch(config).unwrap_err().contains("no longer enabled")); } -/// Plan P4: compiled components are cached across runs (keyed by module and +/// Compiled components are cached across runs (keyed by module and /// engine), so a cold activation only deserializes. #[test] fn precompiled_components_make_cold_activation_cheap() { @@ -1261,7 +1299,7 @@ fn official_material_icons_install() { ); } -/// Plan P1: an icon theme with thousands of SVGs (sanitising, package size, start-up). +/// An icon theme with thousands of SVGs (sanitising, package size, start-up). #[test] fn thousands_of_icons_install_and_reload_within_budget() { const ICONS: usize = 3000; @@ -1374,7 +1412,7 @@ fn official(name: &str) -> PathBuf { repo().join("plugins/official").join(name) } -/// Plan P5, String Tools: 26 commands show before the code loads; one command +/// String Tools: 26 commands show before the code loads; one command /// edits every selection as one versioned change. #[test] fn official_string_tools() { @@ -1395,7 +1433,7 @@ fn official_string_tools() { assert_eq!(f.workspace.document(&editor.path).unwrap().0, before + 1, "both selections in one change"); } -/// Plan P5, Key Promoter: action events are batched; an idle plugin is +/// Key Promoter: action events are batched; an idle plugin is /// woken once per batch, and its counts persist in storage. #[test] fn official_key_promoter() { @@ -1456,7 +1494,7 @@ fn git(dir: &Path, args: &[&str]) { assert!(out.status.success(), "git {args:?}: {}", String::from_utf8_lossy(&out.stderr)); } -/// Plan P5 acceptance: Git Toolbox's scheduled fetch still runs after the +/// Git Toolbox's scheduled fetch still runs after the /// instance was reclaimed while idle, and the status bar follows. #[test] fn official_git_toolbox_fetches_on_schedule_after_idle_reclaim() { @@ -1574,7 +1612,7 @@ fn http_server( (port, seen) } -/// Plan P6, AI Completion: typing on cancels the previous request (the fetch +/// AI Completion: typing on cancels the previous request (the fetch /// returns `cancelled`, the instance stays); the plugin's own work stays /// within the edit-path budget. #[test] @@ -1629,7 +1667,7 @@ fn official_ai_completion_cancels_superseded_requests() { assert!(times[9] < Duration::from_millis(500), "{times:?}"); } -/// Plan P6, Checkstyle: a save runs the approved `checkstyle` and publishes +/// Checkstyle: a save runs the approved `checkstyle` and publishes /// diagnostics; quick fixes come through the code-action provider; saving /// again cancels the check still running. #[cfg(unix)] @@ -1695,7 +1733,7 @@ fn official_checkstyle_checks_on_save() { assert_eq!(info.stats.crashes, 0); } -/// Plan P6, MyBatis Navigator: interface ↔ XML through the providers, on a +/// MyBatis Navigator: interface ↔ XML through the providers, on a /// real project tree (`find-files`), within the edit-path budget. #[test] fn official_mybatis_navigates_between_interface_and_xml() { @@ -1749,7 +1787,7 @@ fn official_mybatis_navigates_between_interface_and_xml() { assert_eq!(f.host.info_of("openidea.mybatis").unwrap().stats.slow_calls, 0); } -/// Plan P7, CSV Editor: a custom editor renders the document as a table (the +/// CSV Editor: a custom editor renders the document as a table (the /// rows asked for), and edits the document itself — versioned, so a text edit /// made meanwhile is never overwritten. #[test] @@ -1784,7 +1822,7 @@ fn official_csv_editor_edits_the_shared_document() { assert_eq!(err.kind, ide_core::error::ErrorKind::NotFound); } -/// Plan P7 acceptance: the sample plugin makes a tool window with a tree view. +/// The sample plugin makes a tool window with a tree view. /// Its menus, shortcut and status bar item are declarations, listed before /// any code runs; the tree is rendered by the code, which loads on first show. #[test] @@ -1826,7 +1864,7 @@ fn sample_plugin_tool_window_with_a_tree() { } } -/// Plan P7, Kubernetes: the cluster tree through `kubectl` (pinned), logs in +/// Kubernetes: the cluster tree through `kubectl` (pinned), logs in /// the terminal of the invoking window. #[test] fn official_kubernetes_browses_the_cluster_and_opens_logs() { diff --git a/crates/ide-host/tests/remote_plugins.rs b/crates/ide-host/tests/remote_plugins.rs index 0b5ac11ce..953a68934 100644 --- a/crates/ide-host/tests/remote_plugins.rs +++ b/crates/ide-host/tests/remote_plugins.rs @@ -1,5 +1,5 @@ -//! Plan P6 remote acceptance: code plugins of a remote project run on the -//! server (docs/plugin_plan.md 3). The desktop's plugin host syncs its live +//! Code plugins of a remote project run on the +//! server. The desktop's plugin host syncs its live //! code plugins over the remote protocol; Checkstyle then checks the file on //! the server's disk when it is saved, and secrets come from the desktop. @@ -207,7 +207,7 @@ async fn remote_projects_run_the_desktops_code_plugins_on_the_server() { plugins.host().shutdown(); } -/// Plan P7 remote acceptance: CSV Editor and Kubernetes in a remote project. +/// CSV Editor and Kubernetes in a remote project. /// The table edits the document on the server; the cluster is the server's /// `kubectl`'s, and its log terminal opens in the remote window. #[tokio::test(flavor = "multi_thread", worker_threads = 4)] diff --git a/crates/ide-language/src/lib.rs b/crates/ide-language/src/lib.rs index ca544edfc..cef38d97f 100644 --- a/crates/ide-language/src/lib.rs +++ b/crates/ide-language/src/lib.rs @@ -169,7 +169,7 @@ pub struct DebuggerConfig { pub pinned: Option, } -/// The program a plugin's `process:` grant approved (docs/plugin_plan.md 4.2): +/// The program a plugin's `process:` grant approved: /// a plugin language's server or debug adapter starts from this exact path, /// and only while the file still has the approved content. Set by the plugin /// host, never read from a definition. diff --git a/crates/ide-language/src/plugin_point.rs b/crates/ide-language/src/plugin_point.rs index 1aa4cbe56..b6558ff12 100644 --- a/crates/ide-language/src/plugin_point.rs +++ b/crates/ide-language/src/plugin_point.rs @@ -128,9 +128,7 @@ impl LanguagesPoint { #[serde(deny_unknown_fields)] struct Decl { /// A `languages/.toml`-style file in the package. - path: Option, - /// The same, inline (runtime `register-language`). - definition: Option, + path: PathBuf, } /// Parses and checks one language definition. @@ -179,18 +177,8 @@ impl ExtensionPoint for LanguagesPoint { fn prepare(&self, pkg: &Package, decl: toml::Value) -> Result { let decl: Decl = decode(POINT_ID, decl)?; - let (src, file) = match (decl.path, decl.definition) { - (Some(path), None) => { - let src = pkg.read_text(&path)?; - (src, Some(path)) - } - (None, Some(def)) => (def, None), - _ => return Err("give either `path` or `definition`".into()), - }; - let cfg = parse_definition(&src).map_err(|e| match &file { - Some(f) => format!("{}: {e}", f.display()), - None => e, - })?; + let src = pkg.read_text(&decl.path)?; + let cfg = parse_definition(&src).map_err(|e| format!("{}: {e}", decl.path.display()))?; let caps = required_capabilities(&cfg); let (store, plugin, key, label) = ( self.store.clone(), @@ -198,10 +186,7 @@ impl ExtensionPoint for LanguagesPoint { cfg.id.to_string(), format!("{} (.{})", cfg.name, cfg.extensions.join(", .")), ); - let mut prepared = Prepared::new(label, move || Ok(store.add(plugin, cfg))).key(key); - if let Some(f) = file { - prepared = prepared.file(f); - } + let mut prepared = Prepared::new(label, move || Ok(store.add(plugin, cfg))).key(key).file(decl.path); for cap in caps { prepared = prepared.capability(cap); } @@ -212,7 +197,7 @@ impl ExtensionPoint for LanguagesPoint { #[cfg(test)] mod tests { use super::*; - use ide_extension::{ExtensionRegistry, Manifest, Scope}; + use ide_extension::{ExtensionRegistry, Manifest}; #[test] fn languages_point_registers_and_requires_process_capabilities() { @@ -233,16 +218,12 @@ mod tests { let set = reg.prepare(&pkg).unwrap(); assert_eq!(set.capabilities, [Capability::Process("solc".into())]); assert_eq!(set.files, [PathBuf::from("l/sol.toml")]); - reg.commit(set, Scope::Static).unwrap(); + reg.commit(set).unwrap(); assert_eq!(store.all()[0].id.as_str(), "solidity"); reg.dispose_plugin("p"); assert!(store.all().is_empty()); - let bad = |def: &str| { - let mut t = toml::Table::new(); - t.insert("definition".into(), def.into()); - reg.contribute(&pkg, POINT_ID, toml::Value::Table(t)).unwrap_err() - }; + let bad = |def: &str| parse_definition(def).unwrap_err(); assert!(bad("not toml [").contains("invalid language")); assert!(bad("id = \"\"\nname = \"x\"").contains("must not be empty")); assert!(bad("id = \"a b\"\nname = \"x\"").contains("invalid language id")); diff --git a/crates/ide-language/src/syntax/external.rs b/crates/ide-language/src/syntax/external.rs index 3d91a14a8..6399a2f01 100644 --- a/crates/ide-language/src/syntax/external.rs +++ b/crates/ide-language/src/syntax/external.rs @@ -1,5 +1,5 @@ //! Syntax from outside this crate for languages without a built-in grammar: a -//! plugin's `syntax-provider` (docs/plugin_p0.md 4.1). Providers return flat +//! plugin's `syntax-provider`. Providers return flat //! results in UTF-8 byte offsets; they are validated and converted here into //! the same [`OutlineItem`], [`FoldRange`] and highlight data the grammars //! produce, so the editor can't tell the difference. Results are memoized per diff --git a/crates/ide-plugin/Cargo.toml b/crates/ide-plugin/Cargo.toml index 0cb208221..0c3bf091b 100644 --- a/crates/ide-plugin/Cargo.toml +++ b/crates/ide-plugin/Cargo.toml @@ -12,10 +12,10 @@ blake3.workspace = true serde.workspace = true serde_json.workspace = true specta.workspace = true -toml.workspace = true thiserror.workspace = true tracing.workspace = true parking_lot.workspace = true +semver = "1" wasmtime = { version = "49.0.4", default-features = false, features = ["runtime", "cranelift", "component-model", "std", "parallel-compilation", "async"] } wasmtime-wasi = { version = "49.0.4", default-features = false, features = ["p2"] } diff --git a/crates/ide-plugin/src/actor.rs b/crates/ide-plugin/src/actor.rs index cf716d043..9897aa34e 100644 --- a/crates/ide-plugin/src/actor.rs +++ b/crates/ide-plugin/src/actor.rs @@ -1,4 +1,4 @@ -//! One thread per active plugin owning its [`Runtime`] (docs/plugin_plan.md 4.5). +//! One thread per active plugin owning its [`Runtime`]. //! //! Callers queue jobs and wait on a reply channel instead of holding a lock //! across the call, so an edit-path caller can stop waiting at its soft @@ -33,6 +33,32 @@ pub enum ActorError { Cancelled, } +/// A call queued with [`Actor::submit`]. +pub struct Pending { + result: mpsc::Receiver>, + give_up: Option, + alive: Arc, +} + +impl Pending { + /// Waits for the outcome, until the call's soft deadline if it has one. + pub fn wait(self) -> Result { + let outcome = match self.give_up { + Some(t) => match self.result.recv_timeout(t.saturating_duration_since(Instant::now())) { + Ok(r) => r, + Err(mpsc::RecvTimeoutError::Timeout) => return Err(ActorError::SoftTimeout), + Err(mpsc::RecvTimeoutError::Disconnected) => { + // Skipped as too late, or the actor ended. + let alive = self.alive.load(Ordering::Acquire); + return Err(if alive { ActorError::SoftTimeout } else { ActorError::Stopped }); + } + }, + None => self.result.recv().map_err(|_| ActorError::Stopped)?, + }; + outcome.map_err(ActorError::Call) + } +} + pub struct Actor { tx: Mutex>>, thread: Mutex>>, @@ -97,6 +123,17 @@ impl Actor { wait: Option, f: impl FnOnce(&mut Runtime) -> Result + Send + 'static, ) -> Result { + self.submit(wait, f)?.wait() + } + + /// Queues `f` like [`call`](Self::call) and returns at once, so a caller + /// can start calls on several plugins and then wait for all of them: the + /// wait is the slowest one's, not the sum. + pub fn submit( + &self, + wait: Option, + f: impl FnOnce(&mut Runtime) -> Result + Send + 'static, + ) -> Result, ActorError> { let give_up = wait.map(|w| Instant::now() + w); let (reply, result) = mpsc::sync_channel(1); let (crash, alive) = (self.crash.clone(), self.alive.clone()); @@ -119,18 +156,7 @@ impl Actor { let Some(tx) = tx.as_ref() else { return Err(ActorError::Stopped) }; tx.send(job).map_err(|_| ActorError::Stopped)?; } - let outcome = match give_up { - Some(t) => match result.recv_timeout(t.saturating_duration_since(Instant::now())) { - Ok(r) => r, - Err(mpsc::RecvTimeoutError::Timeout) => return Err(ActorError::SoftTimeout), - Err(mpsc::RecvTimeoutError::Disconnected) => { - // Skipped as too late, or the actor ended. - return Err(if self.is_alive() { ActorError::SoftTimeout } else { ActorError::Stopped }); - } - }, - None => result.recv().map_err(|_| ActorError::Stopped)?, - }; - outcome.map_err(ActorError::Call) + Ok(Pending { result, give_up, alive: self.alive.clone() }) } /// Like [`call`](Self::call) without a soft deadline, but `cancel` (the diff --git a/crates/ide-plugin/src/lib.rs b/crates/ide-plugin/src/lib.rs index bebcad554..fd27cb249 100644 --- a/crates/ide-plugin/src/lib.rs +++ b/crates/ide-plugin/src/lib.rs @@ -14,9 +14,10 @@ mod sandbox; mod types; mod wit_host; -pub use actor::{Actor, ActorError}; +pub use actor::{Actor, ActorError, Pending}; pub use runtime::{ - API_VERSION, CANCELLED, CallCancel, CallEffects, CallError, HostApi, PluginEngine, Runtime, RuntimeSetup, + API_VERSION, CANCELLED, CallCancel, CallError, GuestError, HostApi, PluginEngine, Runtime, RuntimeSetup, check_api, + component_api, host_api, }; pub use sandbox::{MAX_READ_BYTES, read_text, resolve_in_root}; pub use types::*; diff --git a/crates/ide-plugin/src/runtime.rs b/crates/ide-plugin/src/runtime.rs index 8a8d10c4b..f491af3c8 100644 --- a/crates/ide-plugin/src/runtime.rs +++ b/crates/ide-plugin/src/runtime.rs @@ -31,7 +31,7 @@ use syntax_bindings::exports::openidea::plugin::syntax_provider as wit_syntax; use views_bindings::exports::openidea::plugin::view_provider as wit_view; use views_bindings::openidea::plugin::view_types as wit_vt; -/// The API version of the world above. +/// The API version of the world above, `major.minor` of [`host_api`]. pub const API_VERSION: &str = "1.0"; const EPOCH_TICK: Duration = Duration::from_millis(10); pub(crate) const MAX_TEXT: usize = 1 << 20; @@ -41,16 +41,12 @@ const STDERR_CAPACITY: usize = 256 << 10; pub const CANCELLED: &str = "was cancelled"; /// What plugin code may ask of the IDE, per plugin. Implemented by the host, -/// which routes notifications and status items to windows, edits to open -/// documents and runtime registrations through the extension registry. -/// Capabilities are already checked when these are called. +/// which routes notifications and status items to windows and edits to open +/// documents. Capabilities are already checked when these are called. pub trait HostApi: Send + Sync { fn notify(&self, client: Option<&str>, level: NotificationLevel, title: String, body: String); /// Sets (`Some`) or removes the plugin's status bar item `id`. fn status_item(&self, id: &str, item: Option); - /// Registers a declaration with an extension point at runtime - /// (`commands`, `themes`, `languages`). Returns whether anything changed. - fn contribute(&self, point: &str, decl: toml::Value) -> Result; /// Roots of the open projects. fn projects(&self) -> Vec; /// Unsaved text of an open editor's file. @@ -151,8 +147,7 @@ impl PluginEngine { Component::new(&self.engine, bytes).map_err(|e| format!("not a valid plugin component: {e}")) } - /// Compiles `wasm`, or loads it precompiled from `cache_dir` (docs/plugin_plan.md - /// 4.5): the key is the module's hash plus the engine's compatibility hash, + /// Compiles `wasm`, or loads it precompiled from `cache_dir`: the key is the module's hash plus the engine's compatibility hash, /// so an engine upgrade simply misses and recompiles. Cache failures only /// cost the compilation. pub fn compile_cached(&self, wasm: &Path, cache_dir: &Path) -> Result { @@ -200,12 +195,77 @@ impl PluginEngine { &self.engine } - /// Interfaces a component imports (`openidea:plugin/host@0.1.0`, `wasi:…`). - pub fn imports(&self, component: &Component) -> Vec { - component.component_type().imports(&self.engine).map(|(name, _)| name.to_owned()).collect() + /// Rejects a component this host can't run, with a message that says what + /// to do: one importing interfaces outside the plugin API and WASI, or one + /// built for a plugin API this host doesn't implement (another major, or a + /// newer minor). Returns the API version the component was built for + /// (`None`: it uses no plugin interface). + pub fn check_component(&self, component: &Component) -> Result, String> { + let ty = component.component_type(); + let imports: Vec = ty.imports(&self.engine).map(|(name, _)| name.to_owned()).collect(); + let foreign: Vec<&str> = imports + .iter() + .map(String::as_str) + .filter(|i| !(i.starts_with(PLUGIN_PACKAGE_PREFIX) || i.starts_with("wasi:"))) + .collect(); + if !foreign.is_empty() { + return Err(format!("the component imports interfaces the IDE does not provide: {}", foreign.join(", "))); + } + let exports = ty.exports(&self.engine).map(|(name, _)| name.to_owned()).collect::>(); + let built_for = component_api(imports.iter().chain(&exports).map(String::as_str))?; + if let Some(v) = &built_for { + check_api(v, &host_api())?; + } + Ok(built_for) } } +/// Interface names of the plugin API start with this (`openidea:plugin/log@1.0.0`). +const PLUGIN_PACKAGE_PREFIX: &str = "openidea:plugin/"; + +/// The plugin API this host implements: the version of the `wit/` package. +pub fn host_api() -> semver::Version { + semver::Version::parse(ide_extension::ENGINE_VERSION).expect("ENGINE_VERSION is a semantic version") +} + +/// The plugin API version a component was built for: the highest version on +/// its `openidea:plugin/*` imports and exports. An unversioned plugin +/// interface is an error, since nothing could tell compatible hosts apart. +pub fn component_api<'a>(names: impl IntoIterator) -> Result, String> { + let mut highest: Option = None; + for name in names { + let Some(rest) = name.strip_prefix(PLUGIN_PACKAGE_PREFIX) else { continue }; + let Some((_, version)) = rest.split_once('@') else { + return Err(format!("the component uses the unversioned plugin interface {name:?}")); + }; + let version = + semver::Version::parse(version).map_err(|e| format!("bad plugin API version in {name:?}: {e}"))?; + if highest.as_ref().is_none_or(|h| version > *h) { + highest = Some(version); + } + } + Ok(highest) +} + +/// A component built for `built_for` runs on a host implementing `host` when +/// the major versions match and the host's minor is at least as new (1.x only +/// adds; patches change no interface; 0.x makes no promise, so there every +/// minor is its own API). +pub fn check_api(built_for: &semver::Version, host: &semver::Version) -> Result<(), String> { + let (b, h) = ((built_for.major, built_for.minor), (host.major, host.minor)); + let same_line = b.0 == h.0 && (h.0 > 0 || b.1 == h.1); + if same_line && b <= h { + return Ok(()); + } + let newer = b > h; + let (b, h) = (format!("{}.{}", b.0, b.1), format!("{}.{}", h.0, h.1)); + Err(if newer { + format!("the component was built for plugin API {b}, newer than this IDE's {h}: update OpenIDEA") + } else { + format!("the component was built for plugin API {b}; this IDE runs {h}: rebuild it with the {h} SDK") + }) +} + fn store_precompiled(component: &Component, file: &Path) { let Ok(bytes) = component.serialize() else { return }; if let Some(dir) = file.parent() { @@ -217,13 +277,6 @@ fn store_precompiled(component: &Component, file: &Path) { } } -/// Side effects of one call the host acts on afterwards. -#[derive(Default, Debug)] -pub struct CallEffects { - /// A runtime registration changed contributions. - pub contributions_changed: bool, -} - /// Interrupts the running call: the epoch callback traps on `flag`, host /// operations (a fetch) watch `token`. Reset when a call starts. #[derive(Default)] @@ -275,7 +328,6 @@ pub struct StoreData { pub(crate) granted: Vec, pub(crate) host: Arc, pub(crate) invocation: Invocation, - pub(crate) effects: CallEffects, /// Tasks begun by the running call and not ended yet. pub(crate) open_tasks: Vec, pub(crate) last_report: Option, @@ -312,10 +364,76 @@ pub(crate) fn clip(mut s: String, max: usize) -> String { /// trapped / ran out of budget (the instance is poisoned and must be dropped). #[derive(Debug)] pub enum CallError { - Guest(String), + /// The plugin returned an error. + Guest(GuestError), + /// The instance trapped (panic, fuel, memory, timeout) and is unusable. Trap(String), } +impl CallError { + fn missing_export(interface: &str) -> Self { + Self::Guest(GuestError::Failed(format!("the plugin does not export {interface}"))) + } +} + +impl std::fmt::Display for CallError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Guest(e) => e.fmt(f), + Self::Trap(e) => f.write_str(e), + } + } +} + +/// An error a plugin returned (the API's `error` variant). +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum GuestError { + /// A capability it needs is not granted. + Denied(String), + Stale, + NotFound(String), + Invalid(String), + /// The user cancelled, or the plugin is being stopped. + Cancelled, + Failed(String), +} + +impl GuestError { + /// Whether this is the plugin failing, as opposed to an outcome the + /// user or the host caused (cancelling, a missing grant, bad input). + pub fn is_failure(&self) -> bool { + matches!(self, Self::Failed(_)) + } +} + +impl std::fmt::Display for GuestError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Denied(c) => write!(f, "permission {c} was not granted"), + Self::Stale => f.write_str("the document changed meanwhile"), + Self::NotFound(m) => write!(f, "not found: {m}"), + Self::Invalid(m) => write!(f, "invalid: {m}"), + Self::Cancelled => f.write_str("cancelled"), + Self::Failed(m) => f.write_str(m), + } + } +} + +impl From for ide_core::IdeError { + fn from(e: GuestError) -> Self { + use ide_core::error::ErrorKind; + let kind = match &e { + GuestError::Denied(_) => ErrorKind::PermissionDenied, + GuestError::Stale => ErrorKind::OutOfSync, + GuestError::NotFound(_) => ErrorKind::NotFound, + GuestError::Invalid(_) => ErrorKind::InvalidInput, + GuestError::Cancelled => ErrorKind::Cancelled, + GuestError::Failed(_) => ErrorKind::Internal, + }; + ide_core::IdeError::new(kind, e.to_string()) + } +} + pub struct RuntimeSetup { pub plugin_id: String, pub version: String, @@ -397,7 +515,6 @@ impl Runtime { granted: setup.granted, host: setup.host, invocation: Invocation::default(), - effects: CallEffects::default(), open_tasks: Vec::new(), last_report: None, }; @@ -512,10 +629,9 @@ impl Runtime { f: impl FnOnce(&wit_syntax::Guest, &mut Store) -> wasmtime::Result, ) -> Result { if self.exports.syntax.is_none() { - return Err(CallError::Guest("the plugin does not provide syntax".into())); + return Err(CallError::missing_export("syntax-provider")); } self.call(Invocation::default(), timeout, |exports, store| f(exports.syntax.as_ref().expect("checked"), store)) - .map(|(r, _)| r) } /// The component exports `providers`. @@ -529,10 +645,9 @@ impl Runtime { f: impl FnOnce(&wit_prov::Guest, &mut Store) -> wasmtime::Result, ) -> Result { if self.exports.providers.is_none() { - return Err(CallError::Guest("the plugin does not export providers".into())); + return Err(CallError::missing_export("providers")); } self.call(Invocation::default(), timeout, |e, store| f(e.providers.as_ref().expect("checked"), store)) - .map(|(r, _)| r) } pub fn provide_completion( @@ -550,7 +665,7 @@ impl Runtime { .map(|i| CompletionItem { label: clip(i.label, MAX_TITLE), detail: i.detail.map(|d| clip(d, MAX_TITLE)), - kind: i.kind.map(|k| completion_kind(k).to_owned()), + kind: i.kind.map(|k| clip(k, 32)), insert_text: clip(i.insert_text, 64 << 10), replace: i.replace.map(range_from_wit), sort_text: i.sort_text.map(|t| clip(t, 64)), @@ -634,7 +749,7 @@ impl Runtime { .collect()) } - pub fn activate(&mut self) -> Result { + pub fn activate(&mut self) -> Result<(), CallError> { let data = self.store.data(); let ctx = wit_types::Context { plugin_id: data.plugin_id.clone(), @@ -645,7 +760,7 @@ impl Runtime { self.call(Invocation::default(), self.limits.timeout, |e, store| { e.plugin.openidea_plugin_lifecycle().call_activate(store, &ctx) }) - .and_then(|(r, fx)| r.map(|()| fx).map_err(CallError::Guest)) + .and_then(|r| r.map_err(|e| CallError::Guest(e.into()))) } pub fn run_command( @@ -653,8 +768,8 @@ impl Runtime { id: &str, arg: Option<&str>, invocation: Invocation, - ) -> Result<(Option, CallEffects), CallError> { - let (result, fx) = self.call(invocation, self.limits.timeout, |e, store| { + ) -> Result, CallError> { + let result = self.call(invocation, self.limits.timeout, |e, store| { let ctx = store.data_mut().new_invocation()?; let r = e.plugin.openidea_plugin_commands().call_run( &mut *store, @@ -665,7 +780,7 @@ impl Runtime { store.data_mut().drop_invocation(ctx); r })?; - result.map(|v| (v, fx)).map_err(CallError::Guest) + result.map_err(|e| CallError::Guest(e.into())) } /// The component exports `view-provider`. @@ -679,12 +794,12 @@ impl Runtime { view: &str, viewport: Option, invocation: Invocation, - ) -> Result<(View, CallEffects), CallError> { + ) -> Result { if self.exports.views.is_none() { - return Err(CallError::Guest("the plugin does not export view-provider".into())); + return Err(CallError::missing_export("view-provider")); } let viewport = viewport.map(|v| wit_vt::Viewport { first_row: v.first_row, rows: v.rows.min(MAX_TABLE_ROWS) }); - let (result, fx) = self.call(invocation, self.limits.timeout, |e, store| { + let result = self.call(invocation, self.limits.timeout, |e, store| { let ctx = store.data_mut().new_invocation()?; let views = e.views.as_ref().expect("checked"); let r = @@ -693,38 +808,29 @@ impl Runtime { r })?; match result { - Ok(v) => Ok((view_from_wit(v), fx)), - Err(e) => Err(CallError::Guest(crate::wit_host::error_text(e))), + Ok(v) => Ok(view_from_wit(v)), + Err(e) => Err(CallError::Guest(e.into())), } } /// A user action in a view or custom editor. - pub fn view_event( - &mut self, - view: &str, - event: ViewEvent, - invocation: Invocation, - ) -> Result { + pub fn view_event(&mut self, view: &str, event: ViewEvent, invocation: Invocation) -> Result<(), CallError> { if self.exports.views.is_none() { - return Err(CallError::Guest("the plugin does not export view-provider".into())); + return Err(CallError::missing_export("view-provider")); } let event = event_to_wit_view(event); - let (result, fx) = self.call(invocation, self.limits.timeout, |e, store| { + let result = self.call(invocation, self.limits.timeout, |e, store| { let ctx = store.data_mut().new_invocation()?; let views = e.views.as_ref().expect("checked"); let r = views.call_handle(&mut *store, view, &event, wasmtime::component::Resource::new_borrow(ctx.rep())); store.data_mut().drop_invocation(ctx); r })?; - result.map(|()| fx).map_err(|e| CallError::Guest(crate::wit_host::error_text(e))) + result.map_err(|e| CallError::Guest(e.into())) } /// Delivers a batch of events (from the window in `invocation`). - pub fn handle_events( - &mut self, - events: Vec, - invocation: Invocation, - ) -> Result { + pub fn handle_events(&mut self, events: Vec, invocation: Invocation) -> Result<(), CallError> { let events: Vec = events.into_iter().take(MAX_BATCH).map(event_to_wit).collect(); self.call(invocation, self.limits.timeout, |e, store| { let ctx = store.data_mut().new_invocation()?; @@ -736,16 +842,13 @@ impl Runtime { store.data_mut().drop_invocation(ctx); r }) - .map(|((), fx)| fx) } /// Best effort; errors are logged. pub fn deactivate(&mut self) { - if let Err(CallError::Trap(e) | CallError::Guest(e)) = - self.call(Invocation::default(), self.limits.timeout, |e, store| { - e.plugin.openidea_plugin_lifecycle().call_deactivate(store) - }) - { + if let Err(e) = self.call(Invocation::default(), self.limits.timeout, |e, store| { + e.plugin.openidea_plugin_lifecycle().call_deactivate(store) + }) { tracing::warn!(plugin = %self.store.data().plugin_id, "deactivate failed: {e}"); } } @@ -761,7 +864,7 @@ impl Runtime { invocation: Invocation, timeout: Duration, f: impl FnOnce(&Exports, &mut Store) -> wasmtime::Result, - ) -> Result<(R, CallEffects), CallError> { + ) -> Result { arm(&mut self.store, &self.limits, timeout).map_err(|e| CallError::Trap(e.to_string()))?; self.store.data_mut().invocation = invocation; let result = f(&self.exports, &mut self.store); @@ -771,13 +874,12 @@ impl Runtime { for id in std::mem::take(&mut data.open_tasks) { data.host.task_end(id); } - let effects = std::mem::take(&mut data.effects); let stderr = data.drain_stderr(); if !stderr.trim().is_empty() { tracing::info!(plugin = %data.plugin_id, "stderr: {}", stderr.trim_end()); } match result { - Ok(r) => Ok((r, effects)), + Ok(r) => Ok(r), Err(e) => { let mut msg = describe_trap(&e, &self.limits); // Typically the panic message: "panicked at src/lib.rs:1:2:\nboom". @@ -824,28 +926,6 @@ fn doc_to_wit(d: &ProviderDocument) -> wit_prov::Document { } } -fn completion_kind(k: wit_prov::CompletionKind) -> &'static str { - use wit_prov::CompletionKind as K; - match k { - K::Text => "text", - K::Method => "method", - K::Function => "function", - K::Constructor => "constructor", - K::Field => "field", - K::Variable => "variable", - K::Class => "class", - K::Interface => "interface", - K::Module => "module", - K::Property => "property", - K::Keyword => "keyword", - K::Snippet => "snippet", - K::Value => "value", - K::Constant => "constant", - K::File => "file", - K::Reference => "reference", - } -} - /// Resets the budgets for the next call: fuel, deadline, cancellation. fn arm(store: &mut Store, limits: &Limits, timeout: Duration) -> wasmtime::Result<()> { store.set_fuel(limits.fuel)?; diff --git a/crates/ide-plugin/src/types.rs b/crates/ide-plugin/src/types.rs index e9074a700..9b4dfed07 100644 --- a/crates/ide-plugin/src/types.rs +++ b/crates/ide-plugin/src/types.rs @@ -173,8 +173,7 @@ pub struct RepositoryStatus { pub changed_files: u32, } -/// Resource limits and supervision policy for plugin code -/// (docs/plugin_plan.md 4.5). +/// Resource limits and supervision policy for plugin code. #[derive(Clone, Copy, Debug)] pub struct Limits { /// Wasm fuel (≈ instructions) per host → plugin call. diff --git a/crates/ide-plugin/src/wit_host.rs b/crates/ide-plugin/src/wit_host.rs index 5de21e760..265f992a0 100644 --- a/crates/ide-plugin/src/wit_host.rs +++ b/crates/ide-plugin/src/wit_host.rs @@ -33,7 +33,6 @@ pub(crate) mod syntax_bindings { with: { "openidea:plugin/types": super::bindings::openidea::plugin::types, "openidea:plugin/log": super::bindings::openidea::plugin::log, - "openidea:plugin/contributions": super::bindings::openidea::plugin::contributions, "openidea:plugin/ui": super::bindings::openidea::plugin::ui, "openidea:plugin/storage": super::bindings::openidea::plugin::storage, "openidea:plugin/workspace": super::bindings::openidea::plugin::workspace, @@ -61,7 +60,6 @@ pub(crate) mod providers_bindings { with: { "openidea:plugin/types": super::bindings::openidea::plugin::types, "openidea:plugin/log": super::bindings::openidea::plugin::log, - "openidea:plugin/contributions": super::bindings::openidea::plugin::contributions, "openidea:plugin/ui": super::bindings::openidea::plugin::ui, "openidea:plugin/storage": super::bindings::openidea::plugin::storage, "openidea:plugin/workspace": super::bindings::openidea::plugin::workspace, @@ -89,7 +87,6 @@ pub(crate) mod views_bindings { with: { "openidea:plugin/types": super::bindings::openidea::plugin::types, "openidea:plugin/log": super::bindings::openidea::plugin::log, - "openidea:plugin/contributions": super::bindings::openidea::plugin::contributions, "openidea:plugin/ui": super::bindings::openidea::plugin::ui, "openidea:plugin/storage": super::bindings::openidea::plugin::storage, "openidea:plugin/workspace": super::bindings::openidea::plugin::workspace, @@ -109,8 +106,8 @@ pub(crate) mod views_bindings { } use bindings::openidea::plugin::{ - contributions, diagnostics, editor, events, invocation, log, net, process, secrets, settings, storage, tasks, - types as wit, ui, vcs, views, workspace, + diagnostics, editor, events, invocation, log, net, process, secrets, settings, storage, tasks, types as wit, ui, + vcs, views, workspace, }; /// A project handle: its root. @@ -150,14 +147,17 @@ pub(crate) fn to_wit(e: HostError) -> wit::Error { } /// A guest's `error` as text (`denied: net:example.com`, `stale`, …). -pub(crate) fn error_text(e: wit::Error) -> String { - match e { - wit::Error::Denied(c) => format!("permission {c} was not granted"), - wit::Error::Stale => "the document changed meanwhile".into(), - wit::Error::NotFound(m) => format!("not found: {m}"), - wit::Error::Invalid(m) => format!("invalid: {m}"), - wit::Error::Cancelled => "cancelled".into(), - wit::Error::Failed(m) => m, +impl From for crate::runtime::GuestError { + fn from(e: wit::Error) -> Self { + let clip = |m: String| clip(m, crate::runtime::MAX_TITLE * 16); + match e { + wit::Error::Denied(c) => Self::Denied(clip(c)), + wit::Error::Stale => Self::Stale, + wit::Error::NotFound(m) => Self::NotFound(clip(m)), + wit::Error::Invalid(m) => Self::Invalid(clip(m)), + wit::Error::Cancelled => Self::Cancelled, + wit::Error::Failed(m) => Self::Failed(clip(m)), + } } } @@ -172,10 +172,6 @@ fn range_to_wit(r: &ide_core::Range) -> wit::Range { } } -fn table(entries: &[(&str, toml::Value)]) -> toml::Value { - toml::Value::Table(entries.iter().map(|(k, v)| ((*k).to_owned(), v.clone())).collect()) -} - impl StoreData { fn require(&self, cap: Capability) -> Result<(), wit::Error> { if self.granted.iter().any(|g| g.covers(&cap)) { Ok(()) } else { Err(wit::Error::Denied(cap.to_string())) } @@ -185,12 +181,6 @@ impl StoreData { self.invocation.client.as_deref() } - fn contribute(&mut self, point: &str, decl: toml::Value) -> Result<(), wit::Error> { - let changed = self.host.contribute(point, decl).map_err(wit::Error::Invalid)?; - self.effects.contributions_changed |= changed; - Ok(()) - } - fn push(&mut self, value: T) -> wasmtime::Result> { Ok(self.table.push(value)?) } @@ -221,25 +211,6 @@ impl log::Host for StoreData { } } -impl contributions::Host for StoreData { - fn register_command(&mut self, id: String, title: String, category: Option) -> Fallible<()> { - let max = crate::runtime::MAX_TITLE; - let mut entries = vec![("id", toml::Value::String(id)), ("title", toml::Value::String(clip(title, max)))]; - if let Some(c) = category.map(|c| clip(c, max)).filter(|c| !c.is_empty()) { - entries.push(("category", toml::Value::String(c))); - } - Ok(self.contribute("commands", table(&entries))) - } - - fn register_language(&mut self, definition: String) -> Fallible<()> { - Ok(self.contribute("languages", table(&[("definition", toml::Value::String(definition))]))) - } - - fn register_theme(&mut self, definition: String) -> Fallible<()> { - Ok(self.contribute("themes", table(&[("definition", toml::Value::String(definition))]))) - } -} - impl ui::Host for StoreData { fn show_notification( &mut self, diff --git a/crates/ide-plugin/tests/api_compat.rs b/crates/ide-plugin/tests/api_compat.rs new file mode 100644 index 000000000..f7dad376a --- /dev/null +++ b/crates/ide-plugin/tests/api_compat.rs @@ -0,0 +1,270 @@ +//! Plugin API versioning (plugins/README.md, "Evolving the plugin API"). +//! +//! - The `wit/` package version, `ENGINE_VERSION` and `API_VERSION` agree. +//! - The version check accepts components the host can run and explains the rest. +//! - Components built for 1.0 link against a host of a later 1.x minor that +//! adds functions and interfaces (wasmtime resolves `@1.0.0` names to a +//! semver-compatible definition), and the host finds their exports under +//! the newer name; a 2.0 host does not link them. The newer host is +//! simulated with a linker that stubs every plugin import the component has +//! at the newer version, so no copy of the WIT is needed. +//! +//! Components: the checked-in `dist/plugin.wasm` of `plugins/hello`, the +//! official plugins (`plugins/build.sh` rebuilds them). + +use ide_plugin::{API_VERSION, PluginEngine, check_api, component_api, host_api}; +use std::path::{Path, PathBuf}; +use wasmtime::component::types::ComponentItem; +use wasmtime::component::{Component, Linker, ResourceTable, ResourceType}; +use wasmtime::{Config, Engine}; +use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView}; + +fn repo() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")).join("../..") +} + +/// Every component the repository ships: `hello` and the official plugins with code. +fn components() -> Vec { + let mut out = vec![repo().join("plugins/hello/dist/plugin.wasm")]; + let official = repo().join("plugins/official"); + let mut dirs: Vec<_> = std::fs::read_dir(&official).unwrap().map(|e| e.unwrap().path()).collect(); + dirs.sort(); + out.extend(dirs.into_iter().map(|d| d.join("dist/plugin.wasm")).filter(|p| p.is_file())); + assert!(out.len() >= 10, "expected hello and the official plugins, found {out:?}"); + out +} + +fn v(s: &str) -> semver::Version { + semver::Version::parse(s).unwrap() +} + +#[test] +fn wit_package_engine_and_api_versions_agree() { + let host = host_api(); + assert_eq!(API_VERSION, format!("{}.{}", host.major, host.minor)); + let want = format!("package openidea:plugin@{host};"); + let mut files = 0; + for entry in std::fs::read_dir(repo().join("wit")).unwrap() { + let path = entry.unwrap().path(); + if path.extension().is_some_and(|e| e == "wit") { + let src = std::fs::read_to_string(&path).unwrap(); + assert!(src.lines().any(|l| l.trim() == want), "{} does not declare `{want}`", path.display()); + files += 1; + } + } + assert!(files > 1); +} + +/// The SDK ships its own copy of `wit/` (it builds outside this repository); +/// it must be the API this host implements. +#[test] +fn the_sdk_carries_the_hosts_wit() { + let read = |dir: PathBuf| { + let mut files: Vec<(String, String)> = std::fs::read_dir(&dir) + .unwrap() + .map(|e| e.unwrap().path()) + .filter(|p| p.extension().is_some_and(|e| e == "wit")) + .map(|p| (p.file_name().unwrap().to_string_lossy().into_owned(), std::fs::read_to_string(&p).unwrap())) + .collect(); + files.sort(); + files + }; + let host = read(repo().join("wit")); + let sdk = read(repo().join("sdk/rust/openidea-plugin/wit")); + let names = |f: &[(String, String)]| f.iter().map(|(n, _)| n.clone()).collect::>(); + assert_eq!(names(&sdk), names(&host), "sdk/rust/openidea-plugin/wit has other files than wit/"); + for ((name, a), (_, b)) in host.iter().zip(&sdk) { + assert!(a == b, "sdk/rust/openidea-plugin/wit/{name} differs from wit/{name}: copy wit/ into the SDK"); + } +} + +#[test] +fn the_version_check_runs_compatible_components_and_explains_the_rest() { + let host = v("1.3.0"); + for ok in ["1.0.0", "1.3.0", "1.3.7", "1.0.0-rc.1"] { + assert_eq!(check_api(&v(ok), &host), Ok(()), "{ok}"); + } + let newer = check_api(&v("1.4.0"), &host).unwrap_err(); + assert!(newer.contains("1.4") && newer.contains("update OpenIDEA"), "{newer}"); + assert!(check_api(&v("2.0.0"), &host).unwrap_err().contains("update OpenIDEA")); + let older = check_api(&v("0.2.0"), &host).unwrap_err(); + assert!(older.contains("rebuild it with the 1.3 SDK"), "{older}"); + // 0.x: every minor is its own API. + assert_eq!(check_api(&v("0.2.0"), &v("0.2.5")), Ok(())); + assert!(check_api(&v("0.1.0"), &v("0.2.0")).is_err()); + assert!(check_api(&v("0.3.0"), &v("0.2.0")).is_err()); +} + +#[test] +fn a_components_api_is_the_highest_plugin_interface_version() { + let names = ["wasi:io/poll@0.2.6", "openidea:plugin/log@1.0.0", "openidea:plugin/views@1.2.0"]; + assert_eq!(component_api(names), Ok(Some(v("1.2.0")))); + assert_eq!(component_api(["wasi:cli/stdout@0.2.6"]), Ok(None)); + assert!(component_api(["openidea:plugin/log"]).unwrap_err().contains("unversioned")); +} + +#[test] +fn shipped_components_are_built_for_this_api() { + let engine = PluginEngine::new().unwrap(); + let host = host_api(); + for wasm in components() { + let component = engine.compile(&wasm).unwrap(); + let built_for = engine.check_component(&component).unwrap_or_else(|e| panic!("{}: {e}", wasm.display())); + let built_for = built_for.unwrap_or_else(|| panic!("{} uses no plugin interface", wasm.display())); + assert_eq!((built_for.major, built_for.minor), (host.major, host.minor), "{}", wasm.display()); + } +} + +// --- a later host, simulated ------------------------------------------------------------- + +struct Data { + wasi: WasiCtx, + table: ResourceTable, +} + +impl WasiView for Data { + fn ctx(&mut self) -> WasiCtxView<'_> { + WasiCtxView { ctx: &mut self.wasi, table: &mut self.table } + } +} + +/// Distinct host types for the stubbed resources (a component may import several). +struct Stub; + +fn stub_resource(n: usize) -> ResourceType { + macro_rules! pick { ($($i:literal)*) => { match n { $($i => ResourceType::host::>(),)* _ => panic!("more than 16 resources") } } } + pick!(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) +} + +enum Host<'a> { + /// Defines everything the component imports, plus a function and an + /// interface the component doesn't know. + Superset, + /// Leaves out one function, so linking must fail. + Without(&'a str), +} + +/// A linker with WASI and every `openidea:plugin/*` interface `component` +/// imports, defined at `version` with functions that are never called. +fn host_at(engine: &Engine, component: &Component, version: &str, host: Host<'_>) -> Linker { + let mut linker = Linker::new(engine); + wasmtime_wasi::p2::add_to_linker_sync(&mut linker).unwrap(); + // The component's resource → its stub. A resource re-exported through + // `use` (`invocation` uses `editor.text-editor`) is the same type, so it + // must get the same stub. + let mut resources: Vec<(ResourceType, ResourceType)> = Vec::new(); + for (name, item) in component.component_type().imports(engine) { + let Some(rest) = name.strip_prefix("openidea:plugin/") else { continue }; + let iface = rest.split_once('@').map_or(rest, |(i, _)| i); + let ComponentItem::ComponentInstance(instance) = item.ty else { panic!("{name} is not an instance") }; + let mut defined = linker.instance(&format!("openidea:plugin/{iface}@{version}")).unwrap(); + for (export, item) in instance.exports(engine) { + match item.ty { + ComponentItem::ComponentFunc(_) if !matches!(host, Host::Without(f) if f == export) => { + defined.func_new(export, |_, _, _, _| Err(wasmtime::Error::msg("stub"))).unwrap(); + } + ComponentItem::Resource(ty) => { + let stub = match resources.iter().find(|(r, _)| *r == ty) { + Some((_, stub)) => *stub, + None => { + let stub = stub_resource(resources.len()); + resources.push((ty, stub)); + stub + } + }; + defined.resource(export, stub, |_, _| Ok(())).unwrap(); + } + _ => {} + } + } + if matches!(host, Host::Superset) { + defined.func_new("added-in-a-later-minor", |_, _, _, _| Ok(())).unwrap(); + } + } + if matches!(host, Host::Superset) { + let mut added = linker.instance(&format!("openidea:plugin/added-interface@{version}")).unwrap(); + added.func_new("call", |_, _, _, _| Ok(())).unwrap(); + } + linker +} + +fn engine() -> Engine { + let mut config = Config::new(); + config.wasm_component_model(true); + Engine::new(&config).unwrap() +} + +/// The next minor of the API this host implements. +fn next_minor() -> String { + let host = host_api(); + format!("{}.{}.0", host.major, host.minor + 1) +} + +#[test] +fn components_link_against_a_later_minor_that_adds_functions_and_interfaces() { + let engine = engine(); + let next = next_minor(); + for wasm in components() { + let component = Component::from_file(&engine, &wasm).unwrap(); + let linker = host_at(&engine, &component, &next, Host::Superset); + linker.instantiate_pre(&component).unwrap_or_else(|e| panic!("{}: {e:?}", wasm.display())); + + let exports: Vec = component + .component_type() + .exports(&engine) + .map(|(n, _)| n.to_owned()) + .filter(|n| n.starts_with("openidea:plugin/")) + .collect(); + assert!(!exports.is_empty(), "{} exports no plugin interface", wasm.display()); + for name in exports { + let iface = name.split_once('@').unwrap().0; + let newer = format!("{iface}@{next}"); + assert!( + component.get_export_index(None, &newer).is_some(), + "{}: a {next} host can't find export {name}", + wasm.display() + ); + } + } +} + +#[test] +fn components_do_not_link_against_another_major() { + let engine = engine(); + let next_major = format!("{}.0.0", host_api().major + 1); + let wasm = repo().join("plugins/hello/dist/plugin.wasm"); + let component = Component::from_file(&engine, &wasm).unwrap(); + let linker = host_at(&engine, &component, &next_major, Host::Superset); + assert!(linker.instantiate_pre(&component).is_err()); + let iface = component + .component_type() + .exports(&engine) + .map(|(n, _)| n.to_owned()) + .find(|n| n.starts_with("openidea:plugin/")) + .unwrap(); + let newer = format!("{}@{next_major}", iface.split_once('@').unwrap().0); + assert!(component.get_export_index(None, &newer).is_none()); +} + +#[test] +fn a_later_minor_that_drops_a_function_does_not_link() { + // Keeps the test above honest: the stub host really is type-checked. + let engine = engine(); + let wasm = repo().join("plugins/hello/dist/plugin.wasm"); + let component = Component::from_file(&engine, &wasm).unwrap(); + let (iface, func) = component + .component_type() + .imports(&engine) + .filter(|(n, _)| n.starts_with("openidea:plugin/")) + .find_map(|(n, item)| match item.ty { + ComponentItem::ComponentInstance(i) => i + .exports(&engine) + .find(|(_, it)| matches!(it.ty, ComponentItem::ComponentFunc(_))) + .map(|(f, _)| (n.to_owned(), f.to_owned())), + _ => None, + }) + .expect("hello imports a plugin function"); + let linker = host_at(&engine, &component, &next_minor(), Host::Without(&func)); + let err = linker.instantiate_pre(&component).err().unwrap_or_else(|| panic!("{iface} without {func} linked")); + assert!(format!("{err:?}").contains(&func), "{err:?}"); +} diff --git a/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm b/crates/ide-plugin/tests/fixtures/misbehave/dist/plugin.wasm index 8ef7fdba7517d04e911946a1245265f37763423d..d6018357a9baf8dc67ee202600c21e69cd5da317 100644 GIT binary patch delta 28720 zcmeHwdtg+>_5Yo@`@XxIoA>5L?rsuDfItEvA;=>)iilbTU-bl7QH%8x*Up zh~Q|~swh^fw7yVmeYQqL>jNtaRa#N8R=?U>rD|W_`u+9yIWu=(0c-pH_0L~mc3x-B zoH=vm%$alV&i?3H_x^j_VQtaCijc-Mui&gbv?=_fihl0uTQs&7XK9DqrTLck zE?zX)+1sOQzD*Au5e~RC@8aH`!HzY9x@KI+kLASd2O@7V0(m9-vGWNpP>YLI0lD=lztic5mX|IyPB! zyWV1cajd)B8}NjfuDQkI?nY*akKIdIP@LvDi-p7ko?0{PDMBAUcSnDJZ$E+k&eP0t zMASQ%nLE0@9#)y_$wPa8sCU(1->Sj-fx-TcMJv3MSi74j%@Z4av%UEWMy&D85G8)g zTjo<+;+ zJA2TJcP4B15c+blBXEYd!czbsQ*1O!*eFqNob9blmx%F3lX%mp%Z;)Zu6EHBF-_Ch zedxG2INEf@@uw|x$1jnwE=~_tEO1jHN_{fM2a7K>C`~~4LpDgUlcl^rl}Etdhf7q|me5(EFMPp{Q8~L9^CezBf)!qy zdph&Tl<3JF!rXn58)N>&%dHqkZC((`wPh^-S|cD%%Da;V61SAIDp8T2k2!73ua3XL ze#L_L)02O0ov~2R752GYzK~D%_&hm5Q_pp|T)HnWFFzFWd0b#BgTQeYV@#v8yMXz) z{PQr~RS*s{1~l{+_Ar0YyodsIC1 zsEWUMRmEHOIpsq;HN*R7Uqr+UT3A(LUJaY9Yt_w(`8BMx5({W`=aP>8fwsm84HFuw z7WXdcs9&_0R_=gS(-C zZmVHdVnz$*RhyaEQp3i@wOQkP`#O3s`bE?Fmakgc*)wq@iCNre8~fGZsiCxgC2%~0 zMaMLL73xEoHTNs2&tluL^&`4SY;0zubnUnX(O5WI+*@Si9Uol0=)BGa&}++~y|tUz zv=pbKd~7}7Cd7~PL#^1%uqT{x)|t!u2UcIO=E84Wy!bop8n%(e=AU%(Df7-<3U-ud{tv#lL26u>CA>@K*g-SO|t+GjAIjiVtZrxo+(crBL3(SlrU;HCL4JHu^^y zEu>j&c8FP8Ee|3Nazj<3nuXn1=$cD6WNpY5Or~^zsWKj9PKp^hGUYQrp)TXb=~}9j z*&&%u=3QYob8#%{FGs9MQGJZSY~0P6KThP=a#sw*Yn0NW+}5JHPUGU1ZpYn&T`_`4 z)+@3wIvTdmDK(uqm{D8H%VWt_k*FJ3Jrb+i^4P8~M+*MMybd~F%;&ewuBc{tqyB3n zg>lIejMk1&q2G>o#rz=*-)l#fQyYhA2D)HfF^}b%L$xmZANbQ&5`^2K5JLlmZZQeL z2x685;0}PX1=hd__J~s>W7sF+;>c)+65@hg(1rPz6x-!p;GjxID4D?yyE%!wt)HCX(kg1$ho8%if&Dj|$Q7$DQ zri)E?7FT8SniD3jFezT^<|pEjV#}xCj0eP#k@|B}oCu~z;Tt*Hf^?lZvM!S5gp~%9 zd_{l5ktq28>*6EuSrnNRFV*sFhthp{x*lhj@Oae_{xtib z)*bUy%QSDR!lFaj@AeE2Nmhq6RTA}@w^9v6P1V_K8uWqXw&Rvw7WE0cWNaKzGeo?R_&X40}>4w6AeZwEaJzfFI;{*_(7_Ma&P_gcJG`J z#@((F8;E=`!USG$+EcI1n;nvbof7jpRTzX*6$^xz9Ve!N{wRUP`6*E&7zWjZu0f(} z$iUVx8xsNOrl>dQP3dRiK)rTKOoukjmD<#HgX|oG^dPX=98t)ybwokKcF9Q5|Ka|Z z?n+J1L=CplrynqyAg`)__o+v2q6wOavA(xY)_6dj#hUq1h%i&=yXsOJ67doe*UXQ1 zB=FCd4kJ(F!pNZrn)xvx$ZPweI${IjLp+bVC2#d%JAP~^PP3`o|6nWaA<}D@C^x!} z>0MEzEWOL-Xh3@oGPb_b_N|EpZ1DNOjByIyY zx;F14Uc}t9Lm)LN(3ZOyqUa&fbJmMu6maZ)j#y>saa%DBZJ;O3?Sw@xi6+wYBqk)i zw95Fr7Ou&O4IAeD1gC*2OE(`-=@ja%_YrFQHAtiV3H4!rn!fVH|R)SdYQuLk4-?Tw?n`b^q*opL0FolTb z5zDY*4A~ewv|N_YjvpN}%3^`Ijs49s$`mS7$rUz56h%{$;w&qOO7dS?{Jm-McdH3; zDe*^tB>r59Kh26{XUrKC(T8!x)i9e#!ccTJ2`{ZpU&i<`a2h`vHc=^NvjGAUFn=ru z>cvn(IL4ZnQ8wsT{kZ{|wW8Nity;HO(~>{5rsGmvpgiFp` zk|r-`N|FSg&I!4LSgqc6&Bi~6r?APMOglSD5|I=d15Bf=vgH_*>tb$4DiD&Sf zl&*q-8c^({l7M7aCSKJ`lkEO5Y-2~0y%+i%|tCicSrQ@^1c`dYXT+dw9QE*t&0FzZ=`=#Ywm!R&(ls8Em=(M>|( zPC>_0BXI>?sZv=899-dujKC2g9Qq;9x`MVwKwAXOv!QJl5e<%Y%gxbN3`LJ_g`;)^ zj#}VwFwpJ^`=_fKYyn?%r)`c?9rM_CJ8KAV4=RQC5P@pqKtmPVF6_~z;iSf6;7|3C zcx6t3cR#@xE5-fW3dDp>KC!-G^yJS$ofc(SN*p%6#mb_Lgice2Nfb#g5lBjy@wton zMB`pbAY4(eXrYcLbcV^OLl#`4o``cpXGm_Axei!ypGuc`(bW}s< zA#ys?Z);zWSz)~V~oK_6Z2^Rk%fL+1@`OP37Wwe>doC}0Np=mG~ z0G8YVmcq?A)Kw&+bu(b0E~q=YNZOy+{N%U+DR-QQ#bb4q>;dsc-5ldhMe$~arj3`C zh*QUpW1ow)<0t0HrWTiqhWa)?6;F+yI_gj})#aF}E|{qzQa^*;x8t<>%NcuA?4NLL zzB5(HEf389M68{7F8i@KFtHGyf1Wr6pW_>>*keQrCw^qDXiHDc5lKMf;|>_BOCMK~UI669GTt%X<@hGt5$Bk#OpNgDGiHe7*R>!h(Q4q!TncbD8&v zbz7#!-}EqF(q12iz3x*O|B1cscNpQ%ve$VVdhptJ{qje@)J{Z4FvOjr;Bpw0)HA8* zAW5b-8iYv%3ZxexW=M0yNA~q}+EZyG^I1V`8iW#3LdYynG-&&x0lm!%!f2=SbjS)) z6b6*v@ zf4E4?>{Y?f~X z|CxtuSW@$3oK%Asry^OM+*Y@%BMFrI=Z@l2*@cvxlxCk)f;8%_~!^5^sCIv*X1=w86wl zAu%Wq77dfqVX6Hm41GgBMa>FZP^ohFvAxbQK8Gqp#I;&P;99lHSt=?=1oaV7xdJOh z3wfRuYA3s_uLWI(tQ^uKp|}mRcd7yyj|ixsejM@GDOY-o)oOaPrdv5s?3&iBX@D!} zib7Y}7W{C)hU19kFN@(kPL>%<+Vhy~ltzHr>aaS9R`5odJBcZA(vDV?S)n>UJ!(R+ zLC|xYAKedRaO}KHGPF(y2gxgM**Luau>gE2AzT z^V zK@ANFCA7D6h4{KHZ@kOt_<}CmO?>d%7Riy{zMRbsaXCc4?OPr5*_Fql9A0Mz6h(93 z%JSPo^+$&|>}^ovQ|)|qgHv@tLn3W2Z@p(2+74~N08)Bzam)_s!FcMlaR>kdfIRN% zw80X?iOZ4~mJS+qV!uudhKqq%jV%)-IwW7Bymqm`aLlmVjsfWb8(a>cy#sMEL6F}B zAi*R^jsgLl7byg`cO`K?DA^8gLV$8cR?a7LKE;J39~D0&V~CTIESUo@K?(5^2SPl~ z2oN)M@Q;`&pHBpcl~~BcN=_9AG#ob%VWls{O5zk5ZO*!JIGL{JE6Jk3DXQD@W4_6} z!-1B#YQ!zLgCGyY6OaebVmA)X^aLI-m?kjwRJc_+<4%F{C`jD)xT9k*g=AZ38y{NQ zxfp6Y3@6sx7=QM*56Iu<}Rdc5`IWOh<3bh9=p+*EJ<$}ftL)z>zpH=$+==s1q z6brX&CsQYJ6Lw$tqL^vroIKzN4>|ddFkd}*Gi@NL7IqT;{zdF}L>A=^`HHL{)ddqQc%f7LVWTI3M8QyH-JNdGu*-;0i z=q`2C@k)7r|}ed6nF<8vH+3sx|w_m{=?nd1u|py?&gjbk9-M6%^Y z@yD5E71zB;L&1V|21gZCVrlkuFN(TZWzko*Q{EnjabXA2?Cr_=-7;bB5FN9snzqW! zJ?+-F+WEQ`Jp#ZlZ8)54`1q{u`0!=2JDqkTo;-7oFsl4&OZX>;nt)?s1hP#GzcQ-) z%BYgL0vtA^P34z16}D%};s~LPD!($SWX?iimQm&Z5~IqW-u65r4Z^RW9cFIye@*=V z8|`E@z|8n(#CyMz@ji#~zHG+JCn4oHe@Sar<_eILjQ1*IR_1(hluBzBj(9TGWX?jA z#dzcdcReI;|;4s+bu%)u!$W%$P1>;lJhgA63F%+$P>QYCOCrzS(vB zr6DnM^HuC6@x<&`g3ePAhI!sDZa(_xxm#bL`HxA96(R?n0JQB_ueU{ zo&GW3xFuDxZkssojF0&aRkG&HCG2$;)Sy- zke+qU*qm=&Do00>1i^nuM9)z*Qvnq{r$}r#XChKRJ7+!(F8ACkDSC78IG-~{jfNQX zplz2Ou=H;89bqo02(6VIuwTak063>DXhLtj3#R$DN?xMq{spHQf7~Lw_S+wC5qS$w zJ^u@Kxp;YLgndiA+!+B5 zwyZe%>USiaZrWi}j#rCza4cn%92Bj~*0PVqW6Sx^RS zRfj|AGC!v#8cY)sH#wMtPD$1PK%H)GF>B+dhqbT$D2gU4u)AynpkXzhen=ktM zE2pJ~uVwNq$pk`5{U_M~wDxz>Jg5*mSB_PDn2_YbgaOsltE!DB+0%@So=j(UQp|Sx z+2>FP6PSo6r7z!&!EF4Yr$T(#?{ro!ruX}e)EFJ^pF41cQWximxr0(d*9?v)f!e-` zB=l#46^*gC}8fiO7cp29=?Sa*3b=085vMf#W*4LuOLAko((I9E80Gw)A(-`59%ls`dOVZ8jwGfVPh=MVr8wYqAJj0nr*1}yvt`mqh zyf+{NrvO={-0yIdbYa-`6Bsoe*kmP6+FGgX5r`Qep;JreWh%DIvsbiBu- zV7xs9+)1zeu-b+k-KyAYuO(jtPWMnsaMonbxq8Ztu^C;MluYcPiXI>{yt#tot<6B0 zbWvd>C^M-HR*NdL@mz>Xhg@^ZWSN@RRNLf}q*Oy$klBi!Hp(snaD)co&X%qVBW^=< zrI=ud0rG-D7*p(&rW%MMN)P2KxpW9zL6NY5RRM>+$12|kaW3F!x!qZuBM+M8Z)W#W<8cl@@Rm?sLIr{cDy9B zNAz7##CPuzS6&b`?zx}zgZ-Iiej$ExNv+s_L1F&;s;nH`KA>#9p9|NTC2YO;XwB&G z+N-Grw&Y=KE?S3+uv0>iy&kk20T=)Iwlv28Nse zN8EYg1AO}f;;e5pFZc&70d#{WUrkB#1q=AU<-0j}frP|PiXOA~Ni^iQ36O^;DW(UK z#Ze!Wh_5|9Kg6ARIIu??{6+)cw@1XjxfBEY&Ns{0)#8qCey#X($6U3aOH#et1+^#w z7c~_)?*-jM3g@>;Q|zRRI{469am$)<;g_wTv@qF!C@tupCDSGPS34Cam z`1Inj#%0R$u$h5}V=kF!ekh5R$5vnmf6l)o%6IJ%-@YVfJTC2X`wcZob@ML;!(rx_ zm#Ns+&|=8t4DCUA05E?~?9-BN9cHhSd$s)%!Txm@O<~d>VXj*%8sam8>t#bMQys&- zFkYWATvvLykH#;BT{mNB9!&oYLrwgpJ!0oj9l!Je@yXCbH3x_y`C4&$ddv@Hp?svy z<;mv7wRew}&$h8%u;^0{&uzRIgLh<-mX;0Nn=d_;f4E0{e(7j-r6{}XD7Hf^ysTi_ z7Acps@p!A&(MTyt`wzV_IMVIoNxFTgO5A%{J*}BHFPp_T?h>Qc9pk(5CCQH8i=K5Y zY=gLC-KbE8;|BBd%DVjNTNFP>)42}g!6@25p=|IQm@XBCOnEMn}GYKW}KrVcXIim0f#|km!n;Ce+ zJX!Ins&JY6#k1eCVY~XSs6F<_L|VuJf!a7MkA43VkH2w66^;P6Tw&$QX{PtqeYkoChSp=7FMfUYW**kEcFSO?t{Ll2nGrX!2kjlP`F9luoD86apn^R#N;g z-yv3BIhKEShuD1O6r7m%TzO3X2Q)HUgDW2WaWZp06cfJvF#q%ScYN^eCs-`y%;WZz z;4Gfa%E?4xo0`=f;`yt-9{qU7FzLz~ls}v`+rX+z!M4}adB0=*1ay4e`bm8IBjUOB z$M_yna^iO^NS~N|b@8-eRfuglW#C^!v*{T4!1$um3?#c~zBfA6V%^nqm91JTKD@dh zX|F~$oPe2JxxrA5Si4iyI`ba5-!ymS~1L zI-9ppjx=fM4b}XEJ)-S~5~#^DZ?HqZUO$XU#VFxx`a*2Gq0TFJHf(nLZkQ7K@1*T5 zYfHa3g|>YMldX1Jlj?J0B`y;<(|ao?7#k##^Q+^OCiF)zVTFO`G%WL;5%OsLpK#5bNx*V za@0HJ48|EwHRdxyzqyKkuuIh5yf_W_{hOy#*@2razW#o}H&25;n6Y_1{}votvv&t~ zs?OCk+|qi9{(|@ouhMMMZlaz*!j6Kd{Fb_j zk#dxqjokohY`pn}g-=6}8ywCQuvZ;`o#b^S2$0Urcd2=l{1V3h;469f8Unp_M*p1A z9F&_sly_0ecu_^tXft0E{PrXG=X=Dg+e;}r>-M_*PbEq8q|3p9^2F`asy~;gV&D`6 z0yN3@)gU0~J`MP%+m}|Q`aE2hq>0gOd~1yQyM1dFMbB+Ln>{CLesCgtUaa~-J$pgi z{)6M#i{g_X6qY?MhbSM&fhih%sWBJcl!bd|kEpz(lszt{-BIcPIYHsg)W^hmcg&sr zQ_5j$@C4~yVe?ta(piiimzc5=93w@e+g2AoYP-zm2nLVjTzIyIEp)v3s5p4Xd3ew5 z;2jaseJ8zh_rp75_}q18mGf;KBLH5QS4!m|li!v}7fOwYM#O?U^uh(=7!{7mqGwwO=EOzYVj-#6HHCGfxNF;3DB0JxHM46)=!cgw zAvXT-BzB#6_lMK0>m?$eW_~xBNF)` zTkeaMNn1&F2hQOuMtI#rK18PPT)lbUvXdSmM9EdGx}v8Xn4y%jh7=ABM}peVYUZ1Y z#+w+v)@Hs%8JZWG2zv&0+Qjw~;OTyU`#0E!I~Lu44n;GBqh%w@H{G^TQo`AN+!a9;B?Zbp9aT{K@pl)>LO;WSaG_i^hiv_!qB>IS*Cwt$W4t zht8*H??cB^6nl6>NHvV%Ekzq(KYjQ#swjM93Ps01(nQgvkIaiD@#A}lU!!GV(X*=z zWeu>;ADKbWlOH{Uq79Eu3aCane)z?xmx{!JM{B7j@K||q5}hkXZ(TUd*}8YfjK>T- zqu48sef(sKZhib2iq8LOCq=LP^cIR%{;ZIqD}UBa(Vu=+Ls9t?brj8cqH+8euPeTj zaqM7D7Ww20E2=Oi2kh|W`6tdTP15wRF2AgqVj|Gm!;Mv{x?X!6E|--d9INP`d?z|Z zzPNt4nf+eu8jcXl-yfcx8s~PM-s?#Nwm(@7z@_qY{gX!!`14PWLD3(dtiWf^QNM&Ap)P**E~?o1Y$HW~d3H5Ly+3cE zXxqy;mKc~t02&#=O<8f^z-{DvR;tbPJZDc%DwW!c#8h< z!hVY0d{IJ%cI~0uZ+1y`7riu#a$kRGX<@R%2wYpKk%>JoO-j`iWYv`ZVkI@b`WMer zbo$E@`;{+0M!DI$B^_&aOFI6%domS{d*yj{Q2gnYlPELy)fh!9UX{%@zq*rhi}%d% zIg`)C#DI&7TyJoKxrf2t7ro^!zhVNcxjNpsC3kk=$zkEN5ThGM3 z`n4M^r%wolPnvide6Da$rMb}XdSkM&OI)%$BG$fs4k118dMia``<|ody?y84$f7L11$*<09_>837BGVAgR##LWs8gsNY0jc}&99{{`Tnn`q(+_1`j39Sf%?1X zjk75F)f;jKw(gg#+`IqHoK%lk#LWDI7=E)2oM&%I&Y$|0guDDL$@1ggmWu5=Z_C0H ze^V}Ad;8*4x7o~Ic0e-w)&pP8>|Y*`%x-z-eWI-LHxAYtDfj!|lvCt?SIWlpcfU!w zN8XjP@$tK_fN8_OJ(B%NxZb-K>BM`lu+K#2`{&~Gx%V##UVSp`Yx=IM@#vf#C;iuI z=JVl(hZT!@mWXFQnCTwo)2v~^K00#8f)7LPX+6Cb?p&Z-U98Rli*6`D30(lw+kakf z?~1`?C_Qgg=kg^~eO`ZWcSlcm$AuU4_AlAd^huL{N9SK}%41sMe*)~b`0&rs>9CQf zY1)4}%yyev0C-PEC`3r+&qmtsV*U9vTw9GQ3kGJF7tE8C_LgZfM%*jQj@V zsqrX;bAVtj;%}m(T*Q?K$&M_fqX;pC%?8or9PfQ|AWq1W!cMeA0AumY9;GG%a9~ z<{C|;rHP-4^s{Jp3gQzHiinD2!Pk%uA^g>qm>6cI@m8cWIUNGhe^F4~q zN1*IK6ypCjqSXbab_v1;gj*5rMffSg%LoS$K0^2tg1gYvOoUQ|F$j$aGZBtOI16C~ z!Z!;c8=H{WjIa%1JHjIfI}u(+*pKi5!siH{B2x<^lq1w4v>+Uda4Nz=gcS&D5H3Zy z79oM~mBhyXEo%Ss#K!+EYX9@Zp8vmQ?L26{=@7&j2qdWVH%mpS&o(Dzi+BFkl30|> z79agFPhCJ&>0Zd`j;WCsP~qw*Z6S+mdJZW%e9siCj&4hG$xDvHVec_HiAWxs9`{vu zuITIS9~@v>;64_{LwP!-(c(9pG|NbHO6N!z@)X*7F4K(MR31kRS1vqOg z+(+i%@r-EerM5GjN*`7F(aiSe;tRww6-cFwR4SNCg(#Ky+cB&$&UUeXnIZhl9F39c zV~zAd%8yM3b66Af5SyZbQKV{E3zd-;Vy#eoys<%kHpN`}$Zo>@Q$A zXc}Q+IKJ5H_4xxvFrx1K!oRtO++nUErci-3Gb`x~uxV?&XWl>sUVES<*4MsB`%kS9fR6k}*bS z&p=22V0~Nydiy*2mM>b|QR^Q7(e+d|CbXn?@v0RaJ%jQ_xiS4b*(AF~Uv=slIx-;! z#`TTwcew{U2bXsYsIL5d{T=7yinop>akm7i@2&yqbWzfUuIZh)pl*zJu(x;lU}xWe zXK+#f(vHE2{knSq$-xWtj-DkKN<0JI7xw#HJ&RU!q=u5{u3{6}q{Q7-Y}84fqj4#f zmcH*!OI|Cd2ReIJr!K~GxjR;4P?$hD-}r3QwM-D7&h%D(I%E2GXR$JDLSjp(3}4!O zFSlCE%&gO^HEoAiN_+Wx;#%^!a5|x%l%1{7Zp!f7%Aj$D)o?o}a;F`c9o)?t&1oFI4 z@{&NH3lpCFL~l6@u^g4&NNH1E9msNVZD56!m*+VKI+W#mR&=cB?Y~gNSTpt=x&AE3 zQGV7HX+AH3LCQU&kkGL@OLJtIpUO&5#;^hl69+1oI~34$%~h#D87;}ud>>|5Go12j zq~c;g(CmN68R&N>`-^&%1H4B+HGNvd1v>Xi=Gx=ES|DSis1k z(mEJlE-*CO!8sawKukg_s@)nwFQJV2xm&EX_|eRnOsjk1k=|V1J+_UfNYHj_o(p8h|BKuG z(EkzgHogR2kuE%hh(oRel(aNBv^384MtI)@hZg@p$8sF7u@_fq&uX3(lBBP^JTU9( zLtH+YukL0%gpy1+NyH?Fh{xP8_06eS@%F7f`yz(svHNZb?>pfJ2=vMj2qGxGY)GDhSJufk!?L1fhg0;GP_By z`g#}uD|9XFE#ZARN}2efOae-obo3jVN&GC5t>c6B=dbEnoN<3C%k`Fk|A!|iP{p*0 z%x;|}Jg{mhtYuoAx|ZiPd3U~AF|uQ=ATWccn#t}`DDuqtm&)FWDQKYwWC>nJbBM7ETb6ju1uNpWX&5R zt@#)T&!#(L)#_T6H^}>{mA>$6H3XbRYzFWE8*NO+@hYB_V;!4;v-Gl$O7Q=sZ^QO# zBbL!&SCJ8hU6EQj<23K_Iu1~^BbPuuRzSRGN&R6ZZbEtq)Mqb&dZUnOIU~h>qW0y| zJHe1DM_t&PA+e2`Hx2=-%NDT7usbw=TBw>d5~?ZLLREi=P&FHL+|HDv7BCBYYSxt2 zkC@W8I);J5{*)$(Nbg!qXY5GY ztBz2zHy*Zw#Fa6s0p?B7?blgHgk@$`Q9V zkG!JVU0tA&KsL7VwC_c2{n{?gvq~C!sVknzj!R$aY&A+VWvXMQw0?~cO+E~wUud3Q ziAdf7O-M#uxE&lf%|UX+iG-x-Fi3VMktBwi*#s!XY%Fw>cVc^#xdYQ3R5Rv3^hA_x z#tKTDQ_V^`%kne#4VtwH+wZY}! zX`sV+K3z86;AzuNaS1)~S`9l|hiXY!V^|>=={<5&_{g%r939Fg!=7_~g+SK|645a% ze;hVIo^2aR2o$@#VItC_h;(b-#DdYR6sP8IjAm8S{YEiQ`=LO*>!;OO1un2zOef{IJ0q2;^1gjR5~iTAf$G|#p%fgm2yBuQw}qVW;KRL zgFYo|0-O;vC%!g@)r^T~-g13eOBWjhhU>g@$pA6^mzoYI+!*aO&2xriJ>BX~@;6@H zOrBH6!d6xPsvarGwlf3mUw1ty{m&uHg2Umj9iGE@Y?FAWmb@UftRl|6ZoUjV?ID(E zlG_Zz-NoEscZx(8Q3$>Vp!#z%1r$^#G=Ac)$UR#5WR2e zvX(aPkq*g$eHt<%o))8yX;q1`*{rI< zjfboMm=>n3ytfBH_2iIcpW=DlQQA?71NHcQ4ZP);=&eV;EOFZ;c9h9ONf+n@a)IiJ zx9izV?rJCKX^Dm+<}=;dAfCj^3GCo!)=NA zO>9b{K7wy7;d#LE@9?P$1JAS)r$#+%^@`+s8&F z;#1gIqY@r^`2zvgNG3-mlUYo0wJL+FtXi^>)kv1`(V2rDlR4;GHRw7yAU0OfIc_AK z<44lT>LqOxCj1*No{7r8bK;l#cN+eEzfGh1t&901kse}6PWrbTFq4(@<=ByEGwrvg zuN-^Yq?@9~eh9uaMDthDmtm?5v?%&UjifIVW@^$33Gmho8-!0IqoST4F~X$qvgy)L z;4?BS$V%WdGd5s0Oa7nj}!E9_0RlGf8dvMeCaHRYlki;h~>`eE)E-kMpal%x{ zGyObG;=O6Ct?+P;X*c<$o!_Ei4T&K*r4w7=l1@BX#>&SBR1j(^#=wdgyF(IhM>*<*Z_QzH7yz&Ypt6`JHQ4 z_0?l*7$|hhG~4TSkJ8FU+J|JSIzQ$jo$t826ux&n1d@Z@GnM6w8SbFC%}qnYkLN^r zs~0Zt^5RzCVVKa0U*0(azr6D?yz8w7dDjbBYvMLUyfs9g`O@On@L@R7nnPo(;H@Tk z+(+@&TxZM%%S-v=jl}tLScHb`FDn@N^CW3QH<3G&6;u^|l_gv>8FEyUsGPyVytsza zJW@|T`%;-Gp2@<> z*D<1NN2LtHN}x#EdKMWaQIE5)|Iw>YNo;6iwXWFUh4msgR9UR29~0vVC;gt5yOQb} z_3|=L@)@N!>p^1`)tk5r#xEhqyURxh3Q?APTtv?S$-kNAzR3JDlVY{#aX zH)`Y~#7b(3Yaa}MqVq^LPA-;sEcJB0N_m{B(=4w^`I@FAA3!%NhO}gupHtPN=hlp( zY00TMB6Dh{tF|*z%$ym7%PIW>XtS~z+BRZv_Q>&^m2EbjZ8}zDUq}D>z5OdXmaxAw zms^`KQmtj~ECu+Xs``Otohzj8RoZ^6DY^JP@E|z`A)la@{~JfK9In^Pbsw0G$GWaW X>1@^%`~M0w>$kuDvkFwcQu2QR=SeJA delta 25147 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 String { - format!("{e:?}") +fn failed(message: &str) -> types::Error { + types::Error::Failed(message.into()) } impl lifecycle::Guest for Misbehave { - fn activate(ctx: types::Context) -> Result<(), String> { + fn activate(ctx: types::Context) -> Result<(), types::Error> { // A deliberately broken build (rollback tests): `version = "…-broken"`. if ctx.plugin_version.ends_with("-broken") { - return Err("this build is broken".into()); - } - for id in ["echo", "panic", "spin", "alloc", "read", "root", "insert", "language", "theme"] { - contributions::register_command(id, id, None).map_err(err)?; + return Err(failed("this build is broken")); } Ok(()) } @@ -31,7 +38,7 @@ impl lifecycle::Guest for Misbehave { } impl commands::Guest for Misbehave { - fn run(id: String, ctx: &Invocation, arg: Option) -> Result, String> { + fn run(id: String, ctx: &Invocation, arg: Option) -> Result, types::Error> { match id.as_str() { "echo" => Ok(arg), "panic" => panic!("boom"), @@ -49,18 +56,18 @@ impl commands::Guest for Misbehave { } } "read" => { - let project = ctx.project().ok_or("no project is open")?; - project.read_file(&arg.unwrap_or_default()).map(Some).map_err(err) + let project = ctx.project().ok_or_else(|| failed("no project is open"))?; + project.read_file(&arg.unwrap_or_default()).map(Some) } - "root" => ctx.project().map(|p| p.root().map_err(err)).transpose(), + "root" => ctx.project().map(|p| p.root()).transpose(), // `editor.write` is not granted: an error, not a trap. "insert" => { - let editor = ctx.editor().ok_or("no editor")?; - editor.apply_edits(0, &[]).map(|_| None).map_err(err) + let editor = ctx.editor().ok_or_else(|| failed("no editor"))?; + editor.apply_edits(0, &[]).map(|_| None) } - "language" => contributions::register_language(&arg.unwrap_or_default()).map(|_| None).map_err(err), - "theme" => contributions::register_theme(&arg.unwrap_or_default()).map(|_| None).map_err(err), - other => Err(format!("unknown command: {other}")), + // The user gave up: not the plugin failing. + "cancel" => Err(types::Error::Cancelled), + other => Err(types::Error::NotFound(format!("command {other}"))), } } } @@ -70,22 +77,26 @@ impl event_handler::Guest for Misbehave { } /// Edit-path budgets: the text says how to behave. `sleep:` busy-waits -/// that long, `spin` never returns, `panic` traps; anything else is one -/// keyword token over the whole text. +/// that long, `spin` never returns, `panic` traps. +fn behave(text: &str) { + if let Some(ms) = text.strip_prefix("sleep:").and_then(|n| n.trim().parse::().ok()) { + let until = std::time::Instant::now() + std::time::Duration::from_millis(ms); + while std::time::Instant::now() < until { + std::hint::black_box(0); + } + } else if text == "spin" { + loop { + std::hint::black_box(0); + } + } else if text == "panic" { + panic!("boom"); + } +} + +/// One keyword token over the whole text, after [`behave`]. impl syntax_provider::Guest for Misbehave { fn highlights(_language: String, text: String) -> Vec { - if let Some(ms) = text.strip_prefix("sleep:").and_then(|n| n.trim().parse::().ok()) { - let until = std::time::Instant::now() + std::time::Duration::from_millis(ms); - while std::time::Instant::now() < until { - std::hint::black_box(0); - } - } else if text == "spin" { - loop { - std::hint::black_box(0); - } - } else if text == "panic" { - panic!("highlight boom"); - } + behave(&text); vec![Token { start: 0, end: text.len() as u32, kind: "keyword".into() }] } @@ -98,4 +109,36 @@ impl syntax_provider::Guest for Misbehave { } } +/// Completion: one item, labelled with the document's path, after [`behave`]. +impl providers::Guest for Misbehave { + fn completion(doc: Document, _at: types::Position, _trigger: Option) -> Vec { + behave(&doc.text); + vec![CompletionItem { + label: doc.path.clone(), + detail: None, + kind: Some("keyword".into()), + insert_text: doc.path, + replace: None, + sort_text: None, + documentation: None, + }] + } + + fn inline_completion(_doc: Document, _at: types::Position) -> Option { + None + } + + fn code_actions(_doc: Document, _selection: types::Range) -> Vec { + Vec::new() + } + + fn definition(_doc: Document, _at: types::Position) -> Vec { + Vec::new() + } + + fn line_markers(_doc: Document) -> Vec { + Vec::new() + } +} + export!(Misbehave); diff --git a/crates/ide-plugin/tests/fixtures/p0-probe/wit/probe.wit b/crates/ide-plugin/tests/fixtures/p0-probe/wit/probe.wit index d3cbece56..b1ff2c415 100644 --- a/crates/ide-plugin/tests/fixtures/p0-probe/wit/probe.wit +++ b/crates/ide-plugin/tests/fixtures/p0-probe/wit/probe.wit @@ -1,4 +1,4 @@ -/// P0 probe (docs/plugin_p0.md): optional exports, async calls and +/// Runtime probe: optional exports, async calls and /// cancellation, and a WASI p2 task model built on `wasi:io/poll`. /// Test fixture only; not part of the plugin API. package openidea:probe@0.1.0; diff --git a/crates/ide-plugin/tests/p0_probe.rs b/crates/ide-plugin/tests/p0_probe.rs index 11026d12b..d9295b802 100644 --- a/crates/ide-plugin/tests/p0_probe.rs +++ b/crates/ide-plugin/tests/p0_probe.rs @@ -1,4 +1,4 @@ -//! P0 experiments (docs/plugin_p0.md), kept as regression tests: they pin the +//! Runtime experiments, kept as regression tests: they pin the //! wasmtime behaviour the plugin runtime is designed around. //! //! Components: `tests/fixtures/p0-probe/dist/{min,full}.wasm`, rebuilt with diff --git a/crates/ide-run/src/external.rs b/crates/ide-run/src/external.rs index 0be3405d2..340a714cb 100644 --- a/crates/ide-run/src/external.rs +++ b/crates/ide-run/src/external.rs @@ -1,5 +1,5 @@ -//! Run configurations detected outside this crate: plugins' `runDetectors` -//! (docs/plugin_plan.md P3). Their ids start with [`EXTERNAL_PREFIX`]; before +//! Run configurations detected outside this crate: plugins' `runDetectors`. +//! Their ids start with [`EXTERNAL_PREFIX`]; before //! one is started the provider names the program to run (the file approved //! for the plugin's `process:` capability) or refuses. diff --git a/docs/plugin_p0.md b/docs/plugin_p0.md deleted file mode 100644 index 4c374dd71..000000000 --- a/docs/plugin_p0.md +++ /dev/null @@ -1,159 +0,0 @@ -# 插件系统 P0 验证结论 - -对应 `docs/plugin_plan.md` 第 5 节 P0。实验代码保留为回归测试: -`crates/ide-plugin/tests/p0_probe.rs`,探针组件在 `crates/ide-plugin/tests/fixtures/p0-probe/` -(`build.sh` 生成 `dist/min.wasm` 与 `dist/full.wasm`)。环境:wasmtime 49.0.5、wasmtime-wasi 49(p2)、 -wit-bindgen 0.63、`wasm32-wasip2`,Apple Silicon,debug 构建(release 数字括注)。 - -## 1. 可选导出能否按接口绑定 —— 可以 - -测试:`optional_exports_bind_per_interface`。 - -- `Component::get_export_index(None, "openidea:probe/code-actions@0.1.0")` 在**编译后、实例化前**就能判断 - 组件有没有导出某个接口。安装时据此算出插件实现了哪些 Provider,写进贡献索引,启动时不需要实例化。 -- `bindgen!` 生成的整 world 绑定(`ProbeHostPre::new`)要求所有导出都在,缺一个就失败; - 但每个导出接口各有一个 `GuestIndices::new(&instance_pre)`,缺失时返回 `Err`,存在时正常 `load` 和调用。 -- 结论:**不需要退回“基础 world + 多个组合 world”**。宿主用一个包含全部可选导出的 world 生成绑定, - 只用到每个接口的 `GuestIndices`,不用整 world 的 `*Pre`。插件作者侧:SDK 为每个 Provider 提供 - feature 或宏,只导出实现了的接口(wit-bindgen 按 world 生成,SDK 内置几个常用组合 world 即可, - 宿主不关心组合方式)。 - -对设计的影响:无,4.3 节的“可选导出”成立。 - -## 2. async 调用、取消与实例复用 - -测试:`spinning_guest_yields_to_the_executor`、`cancelled_instance_cannot_be_reused_but_reinstantiates_fast`、 -`async_host_import_does_not_block`。 - -- wasmtime 49 里 `Config::async_support` 已废弃且无效果,async 能力由 `async` cargo feature 和调用方式决定。 -- `store.epoch_deadline_async_yield_and_update(1)` + `call_async`:死循环的 guest 每个 epoch tick 让出一次, - **单线程**执行器上的其他任务照常推进。长调用不再独占线程。 -- async 宿主导入(`imports: { "...sleep-ms": async }`)等待期间不阻塞线程。 -- guest 返回 `Err`(不是 trap)后实例照常可用。 -- **取消(丢弃 future)后实例不能复用**:下一次调用返回 `Trap::CannotEnterComponent`。 - 无论取消发生在 guest 计算中还是 async 宿主导入的等待中,结果相同。原因:wasmtime 在 fiber 被 - dispose 时把错误传回 `catch_traps`,组件 store 被标记为 `trapped`(`may_enter() == false`)。 - 这是 wasmtime 的有意行为,不能绕开。 -- **重建很便宜**:从已有的 `InstancePre` 新建 store 并实例化,加上第一次调用,约 **50µs**(release 约 40µs)。 - 代价在于丢失实例内存里的状态,而不是时间。 - -结论与对 4.5 节的修订: - -1. 取消 = 丢弃 future + 丢弃 store;下次调用前从 `InstancePre` 重建。不需要冷编译。 -2. 两段式超时仍然保留,但理由从“避免冷启动”改为“保留实例状态”: - - **软超时**(编辑路径 50ms):调用方不再等待、结果丢弃,调用在 actor 内继续执行完,实例和状态保留; - 同一插件的后续调用排队,新请求到来时旧请求如仍在排队则直接丢弃(不进入 guest)。 - - **硬超时**(编辑路径 500ms,命令 5s):丢弃 future,重建实例,计入熔断计数。 -3. 用户主动取消(例如 AI Completion 的旧请求)优先走**协作式取消**(见第 3 节),实例不受影响; - 协作式取消在宽限期内没有返回时,再升级为硬取消。 -4. 插件需要跨调用保留的状态一律走 `storage`;实例内存只当缓存。SDK 文档写明这一点。 -5. fuel 保留作为死循环兜底;与 epoch 让出同时启用没有冲突。 - -## 3. WASI p2 上的流式与任务模型 —— 可行,不依赖 p3 - -测试:`pollable_task_streams_and_cancels_cooperatively`。 - -- 宿主定义 `task` 资源,方法 `next-ready() -> pollable` 返回 `wasi:io/poll.pollable`(宿主用 - `wasmtime_wasi::p2::subscribe` 包装自定义 `Pollable`)。guest 调 `pollable.block()` 时,wasmtime 在 - async 宿主里等待该 future,执行器线程不被占用。 -- guest 循环“等待 → 取数据 → `report` 进度 → 检查 `is-cancelled`”,宿主推送数据并在最后设置取消标志; - guest 正常返回,**实例保持可用**。 -- 流式输出(例如 AI Completion 逐块返回、Kubernetes 日志)用同一模式的反方向:宿主提供 - `output-stream` 风格的资源(`write(chunk)` 是 async 宿主导入),或者 guest 通过 `task.emit(chunk)` 推送; - 两者都只需要 p2 资源 + pollable。 -- 结论:4.3 节 `tasks` 接口定为: - ```wit - resource task { - report: func(done: u32, total: option); - is-cancelled: func() -> bool; - /// 取消或有新输入时就绪,供 guest 在等待时让出。 - cancelled: func() -> pollable; - } - ``` - `net` 的流式响应体直接复用 `wasi:io/streams.input-stream`,不自造类型。 - -## 4. `syntax`、`index` 接口 - -依据:`ide-syntax`(`StructureService`:`node_at`、`syntax_errors`、`locals`、`identifiers_named`、 -`structural_ranges`,全部基于 `ide-language` 的 `SyntaxCache` 增量树,0 基行、UTF-16 列)和 `ide-index` -(`ProjectIndex`:`find_files`、`find_symbols`、`symbols_named`,后台 worker + redb 持久化)。 -Solidity 与 MyBatis Navigator 两个原型需要的能力如下,接口按它们的最小需求定稿。 - -### 4.1 `syntax` - -两个方向,分开设计: - -- **读宿主的树(import)**:适用于宿主有 grammar 的语言(MyBatis Navigator 读 Java、XML)。 - ```wit - interface syntax { - resource tree { - /// 0 基行、UTF-16 列;返回包含该位置的最内层命名节点。 - node-at: func(pos: position) -> option; - /// tree-sitter 查询语法;只返回被捕获的节点,最多 `limit` 个。 - query: func(source: string, range: option, limit: u32) -> result, error>; - errors: func(limit: u32) -> list; - } - record node { kind: string, range: range, text: string, parent-kind: option } - record capture { name: string, node: node } - /// 打开的文档用增量缓存树;未打开的文件现场解析(受 `project.read` 约束)。 - parse: func(path: string) -> result; - } - ``` - 不暴露节点句柄的游走 API(`parent`/`children` 逐个跨边界调用代价高);查询一次返回结果列表。 -- **插件提供语法树(export,P3)**:宿主没有 grammar 的语言(Solidity)。宿主不加载插件提供的原生 - grammar;插件在 Wasm 里自带解析器,导出 `syntax-provider`,按需返回**扁平结果**而不是树: - ```wit - interface syntax-provider { - highlights: func(text: string) -> list; // range + 语义类别(映射到 --syn-*) - outline: func(text: string) -> list; - folding: func(text: string) -> list; - } - ``` - 宿主把结果转换成现有的 `OutlineItem`、`FoldRange`、`SyntaxHighlights`,编辑器侧无感知。 - 这条路径在编辑路径上,适用 4.5 节的编辑路径预算。 - 原因:tree-sitter 的 Wasm grammar 需要 `tree-sitter` 的 wasm 特性和额外的 wasmtime 实例, - 且 grammar 不能表达大纲和折叠;扁平结果把实现方式留给插件。 - -### 4.2 `index` - -- **查宿主的符号索引(import)**: - ```wit - interface index { - find-symbols: func(query: string, types-only: bool, limit: u32) -> list; - symbols-named: func(name: string, kind: option, limit: u32) -> list; - find-files: func(query: string, limit: u32) -> list; - /// 插件自定义索引的查询,见下。 - lookup: func(index: string, key: string, limit: u32) -> list; - } - record entry { key: string, value: string, path: string, range: range } - ``` -- **插件自定义索引(export + 声明)**:MyBatis Navigator 要“Mapper 接口方法 ↔ XML 语句 id”的跨文件映射, - 宿主的符号索引不认识 XML 语句。做法: - ```toml - [[contributes.indexes]] - id = "statements" - files = ["**/*Mapper.xml", "**/*Mapper.java"] - version = 1 # 改变索引格式时递增,宿主据此重建 - ``` - 插件导出 `indexer.index-file(path, text) -> list`;宿主在**后台**(复用 `ide-index` 的 worker - 调度和 redb 存储,按插件 id + 索引 id 分表)对匹配文件调用,文件变化时增量更新,插件禁用或卸载时删表。 - 插件运行时只能 `lookup` 自己的索引。索引任务是后台任务,适用后台预算并上报进度。 - -对设计的影响:4.3 节的 `syntax`、`index` 按上面两节定稿;新增 `contributes.indexes` 扩展点(P6)。 - -## 5. 文档修正 - -`plugins/README.md` 第 4 节原文暗示插件语言里的 `language-servers`、`debugger` 会生效。实际上插件语言 -只经 `languages` 命令进入前端的语言列表(文件检测、注释、缩进、借用的 CodeMirror 模式),不进入后端 -`LanguageRegistry`,因此 LSP、DAP、后端语言检测都不生效。README 已改为如实说明,P3 完成后再恢复。 - -## 6. 对计划的修订汇总 - -| 位置 | 原设计 | 修订 | -| -------------------- | ------------------------ | ------------------------------------------------------------ | -| 4.3 可选导出 | 待验证,备选组合 world | 按接口 `GuestIndices` 绑定,安装时用 `get_export_index` 记录 | -| 4.5 超时 | 两段式用于避免冷启动 | 重建约 50µs;两段式用于保留实例状态;用户取消优先协作式 | -| 4.3 `tasks` | `report`、`is-cancelled` | 增加 `cancelled() -> pollable` | -| 4.3 `syntax`/`index` | 待定 | 见第 4 节 | - -以上修订已同步到 `docs/plugin_plan.md`。 diff --git a/docs/plugin_plan.md b/docs/plugin_plan.md deleted file mode 100644 index 180b40f5c..000000000 --- a/docs/plugin_plan.md +++ /dev/null @@ -1,601 +0,0 @@ -# 插件系统重构计划 - -状态:P0–P8 完成(P4 release 实测在预算内),WIT 冻结为 `openidea:plugin@1.0.0`。涉及 `crates/ide-plugin`、`crates/ide-host`、`wit/`、 -`src-tauri/src/commands/plugin.rs`、`src/features/plugins`、`src/workbench/contributions.ts`。 - -## 1. 目标 - -- 新增贡献类型时不改内核:领域 crate 或前端 feature 注册一个扩展点即可。 -- 插件能做出实质功能:Provider、事件、长任务、视图、自定义编辑器、外部工具链。 -- 第三方代码默认隔离,能力需声明并由用户逐个插件授权。 -- 已安装插件不拖慢启动:没有被触发的插件不实例化 Wasm。 -- 本地和远程项目使用同一套宿主,从 P3 起每阶段都在远程项目上验收。 -- 用 10 个官方插件验证设计,只走公开 SDK。 - -### 非目标 - -- 不把第一方 feature 改造成插件。它们和插件共用注册表即可。 -- 不兼容 VS Code 插件。 -- API 1.0 不支持插件间的代码依赖,只能通过命令 id 协作。 -- 不提供服务端市场,先用静态索引。 - -## 2. 现状 - -### 已有,可直接复用 - -| 能力 | 位置 | 本计划中的用途 | -| ----------------- | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | -| 项目信任 | `crates/ide-host/src/trust.rs`;`create_host` 通过 `set_project_policy` 接入,未受信项目走 `restrict_project` | 项目插件加载的前置门槛 | -| when 上下文求值 | `src/platform/context.ts` | 菜单、键位、状态栏的 `when` 条件,不另造表达式语言 | -| 后台符号索引 | `crates/ide-index` | `index` 接口在其上开放,不从零设计 | -| epoch + fuel 限额 | `crates/ide-plugin/src/runtime.rs` | 保留作为兜底,调用模型改为 async(见 4.5) | -| 性能预算 | `perf/budgets.json` | 新增插件相关指标 | - -### 问题 - -| 问题 | 证据 | 后果 | -| ---------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | -| 贡献类型写死 | 新增一类要改 `Contributions`、`Registrations`、`PluginInfo`、`CatalogPlugin`、`static_contributions`、`files()`、`bridge.ts` | 扩展成本线性增长 | -| 内部扩展点没开放 | 前端有 48 个 `register*` 函数,插件只能贡献命令、语言、主题 | 插件做不出实质功能 | -| 插件语言是半成品 | `PluginHost::languages()` 只被 `commands/app.rs` 的 `languages` 命令使用,没有进入 `LanguageRegistry`;语法树只能来自内置 grammar | `plugins/README.md` 写的 `language-servers`、`debugger` 实际不生效 | -| 只有拉模型 | guest 只导出 `activate`、`deactivate`、`run-command`,同步调用,单次上限 5s,超时即 trap | 无事件、无 Provider、无长任务、无取消 | -| 启动全量激活 | `load_all` 逐个编译并激活,没有预编译缓存 | 插件越多启动越慢 | -| 信任只有项目一级 | 受信项目里 `.openidea/plugins/*` 全部自动加载,无逐插件确认;首次迁移时最近项目被整体标为受信;已安装插件声明即授权 | 开放进程或网络后,打开一个受信仓库就会执行其中任意插件 | -| 宿主位置不对 | `PluginHost` 在 `src-tauri`,`ide-host` 和 `openidea-server` 都没有 | 远程项目无法在项目所在机器运行插件 | -| 安装非原子 | `lib.rs` 安装时先 `remove_dir_all` 旧目录再 `rename` | 中途失败插件丢失;无版本回滚 | -| 分发不完整 | 无打包格式、签名、市场 | 无法形成生态 | -| 编辑无版本校验 | `insert-text`、`replace-selection` 是发完不管的事件 | 存在竞态,插件也拿不到结果 | - -## 3. 目标架构 - -```text - 主 WebView(只跑第一方代码) - 贡献投影层:已校验的声明 → 现有 register*(不会失败) - 声明式视图渲染器(src/ui)| 沙箱 iframe(逃生口) - │ 快照/增量 + 视图树 ▲ 用户事件 -────────┼────────────────────────────┼────────── 本地 IPC 或远程协议 - ide-host(本地与 openidea-server 共用,运行在项目所在机器) - ExtensionRegistry:扩展点 + 按插件记账 - CapabilityBroker:授权账本 + 项目信任 - ActivationManager:激活事件 + 空闲回收 - WasmRuntime:每插件一个 actor,async 调用 + 分级预算 - │ 只通过能力接口 - project / editor / syntax / index / lsp / net / process / storage - │ 需要本机的能力(secrets、通知、剪贴板) - 本地代理(客户端侧) -``` - -依赖方向:`ide-core` ← `ide-extension`(manifest、注册表、记账、能力模型,不依赖 wasmtime)← -各领域 crate(实现扩展点)与 `ide-plugin`(Wasm 运行时)← `ide-host`(组装)。 - -### 放置规则 - -现在就定下来,不等到远程阶段: - -1. **Wasm 一律运行在项目所在机器**(本地项目在本机,远程项目在 `openidea-server`)。 -2. **UI 只做声明投影**:插件不直接接触 WebView,视图树和用户事件通过协议往返。 -3. **`Placement` 属于扩展点,不属于插件**:`Ui` 类声明(菜单、键位、主题、图标)投影到客户端; - `Workspace` 类声明(语言、LSP、运行配置)在宿主侧生效。 -4. **只有本机才有的能力走本地代理**:`secrets` 存在客户端的系统钥匙串,宿主按需转发; - `net` 从项目所在机器发出(与 LSP 一致),在授权页上注明。 -5. 编辑路径 Provider 在远程模式下的预算另计网络往返,单独设指标。 - -## 4. 关键设计 - -### 4.1 扩展点 - -```rust -pub trait ExtensionPoint: Send + Sync { - fn id(&self) -> &'static str; // "languages" - fn placement(&self) -> Placement; // Ui | Workspace - /// 只解析和校验,无副作用;同时算出这条声明需要的能力。 - /// Ui 类扩展点也在这里做完全部校验(JSON schema),前端投影不允许失败。 - fn prepare(&self, pkg: &Package, decl: toml::Value) -> Result; -} - -pub struct Prepared { - pub capabilities: Vec, // 带 LSP 的语言 → process: - pub commit: Box Result + Send>, -} -``` - -- 原子生效:全部 `prepare` 成功才开始 `commit`;任一 `commit` 失败,把已 commit 的 `Disposable` 逆序撤销。 -- 前后端一致:所有校验都在 Rust 侧完成,前端只做投影。投影抛错时记为缺陷,不做部分生效。 -- 干净卸载:贡献记在所属插件名下,禁用或卸载时统一 dispose。 -- 前向兼容:不认识的 `contributes.` 告警并跳过,不让整包失败。 -- 前端投影:`defineUiPoint("menus", decl => registerMenuGroup(...))`,复用现有注册函数; - `when` 条件交给 `src/platform/context.ts` 求值。 -- `LanguageRegistry` 改为“内置 + 插件覆盖层”的可变结构;内置 `languages/*.toml` 走同一扩展点。 - -### 4.2 能力分级 - -按实现形式分: - -| 级别 | 形式 | 用途 | -| ----------- | --------------------------- | --------------------------------------------------------- | -| L0 声明 | 纯数据 | 主题、图标主题、语言、片段、键位、模板、设置 schema、i18n | -| L1 逻辑 | Wasm 组件 | 命令、Provider、事件订阅 | -| L2 视图 | 声明式视图树;必要时 iframe | 工具窗、面板、自定义编辑器 | -| L3 外部进程 | LSP、DAP、CLI | 语言工具链 | - -按风险分(决定授权页怎么展示): - -| 风险 | 能力 | 授权方式 | -| ---- | -------------------------------------------------------------------- | -------------------------------------------- | -| 低 | `project.read`、`editor.read`、`storage`、事件订阅(不含按键) | 安装页列出,一次确认 | -| 中 | `editor.write`、`vcs.write`、`net:`、`secrets`、按键与输入事件 | 安装页单独列出并说明用途 | -| 高 | `process:`、`fs.read:<项目外路径>` | 单独弹窗,明确提示“等同于在本机执行任意代码” | - -`process:` 说明: - -- 它是完全的代码执行权限,参数无法有效限制(例如 `git -c core.sshCommand=...`),不把它包装成细粒度权限。 -- 二进制在授权时按绝对路径解析并写入账本;运行时路径或文件哈希变化要重新确认。不在运行时按 PATH 查找, - 防止仓库在 PATH 前面放同名程序冒充。 -- 声明也算能力:带 `language-servers.command` 的语言包同样需要 `process:` 授权,否则语言注册修好后就成了沙箱绕过口。 - -`net:` 由宿主的 HTTP 客户端实现,每一跳重定向都校验主机;不向插件暴露原始 socket。 - -默认用声明式视图树(tree、list、form、toolbar),由 `src/ui` 渲染,自动跟随主题和无障碍规范。 -iframe 只用于富内容:严格 CSP、仅 `postMessage`、不暴露 Tauri API。第三方代码不进主 WebView。 - -### 4.3 WIT API - -开发期版本号用 `openidea:plugin@0.2.0`,**P8 远程验收通过后才定为 1.0**(已于 P8 冻结为 `1.0.0`)。 -视图和远程两块最可能逼出破坏性改动,提前承诺 1.0 没有意义。 -目前只有样例和主题包,0.1 不写适配层;0.2.x 期间允许破坏性修改;1.0 之后 minor 只做增量,major 由宿主保留旧版适配。 - -```wit -package openidea:plugin@0.2.0; - -interface workspace { resource project { root: func() -> string; - read-file: func(path: string) -> result; } } -interface editor { resource text-editor { - apply-edits: func(version: u64, edits: list) -> result<_, error>; } } -interface syntax { resource tree { node-at, query, errors } parse: func(path) -> result; } -interface index { find-symbols, symbols-named, find-files, lookup /* 插件自定义索引 */ } -interface events { subscribe: func(kind: event-kind) -> subscription; } -interface tasks { resource task { report: func(done: u32, total: option); - is-cancelled: func() -> bool; - cancelled: func() -> pollable; } } -// 另有 ui、vcs、net、process、storage、secrets - -interface commands { run: func(id: string, ctx: borrow, arg: option) - -> result, error>; } -// 可选导出:code-actions、completion、inline-completion、run-detector、view-provider、 -// syntax-provider、indexer … -``` - -`syntax`、`index`、`tasks` 的定稿与理由见 `docs/plugin_p0.md` 第 3、4 节。 - -- 窗口和项目作为句柄传入,去掉隐式的 `Invocation` 调用态。 -- P5 实施的偏差:`events.subscribe` 不返回 `subscription` 资源,订阅记在宿主、会话内有效,因为实例会被 - 空闲回收而订阅要继续生效(`on-event:` 声明同样效果);`tasks.task` 暂无 `cancelled() -> pollable`, - 调用是同步的线程 actor,`is-cancelled` 轮询即可,等 P6 流式接口一起加;guest 侧语法提供者用只导出 - `syntax-provider` 的 `syntax` world 与 `plugin` 合并(wit-bindgen 对同一导出接口会生成重名宏)。 -- P6 实施的偏差:Provider 不靠探测可选导出,而由 manifest `[[contributes.providers]]` 声明种类和语言, - 宿主只调用声明匹配的插件(未匹配的不加载);导出放在 `providers` 接口、`plugin-providers` world。 - `net.fetch` 一次返回完整响应(≤ 8 MiB),没有流式 body;内联补全的取消由宿主中断调用完成 - (`call_cancellable`,宽限 250ms),仍不需要 `pollable`。密钥不经 guest:请求头写 `{{secret:}}`, - 宿主发出前替换(这就是“本地代理”的做法);远程项目里由服务器按需向桌面端请求。 -- 权限失败返回 `error::denied`,不再 trap,插件可以降级运行。 -- 安装时读取组件 import 列表并与 manifest 比对,使用了未声明接口的拒绝安装。 -- 编辑带文档版本号,版本不符返回 `error::stale`,由插件决定重试。 -- 坐标继续遵守 0 基行号和 UTF-16 列。 - -### 4.4 Manifest - -```toml -id = "acme.rust-tools" # 带发布者命名空间 -version = "1.2.0" -engines.openidea = "^0.2" -wasm = "plugin.wasm" -activation = ["on-language:rust", "on-command:acme.check"] - -[capabilities] -required = ["project.read", "editor.write"] -optional = ["net:crates.io", "process:cargo"] - -[[contributes.commands]] -id = "check" -title = "%check.title%" - -[[contributes.menus]] -menu = "editor.context" -command = "check" -when = "editorLang == rust" # 由 src/platform/context.ts 求值 - -[[contributes.languages]] -path = "languages/rust.toml" - -[contributes.settings] -schema = "settings.schema.json" # 自动生成设置页并接入设置搜索 - -[[contributes.views]] -id = "crates" -location = "toolWindow.right" -``` - -菜单、键位、状态栏只靠声明显示,用户首次触发时才激活插件。 -激活事件:`on-command`、`on-language`、`on-file`、`on-view`、`on-schedule`、`on-startup-finished`。 -`optional` 能力在用户拒绝后插件照常加载,调用时返回 `error::denied`。 - -### 4.5 运行时 - -- 启动只读缓存的贡献索引,不实例化 Wasm。 -- 用 `Component::serialize` 把编译结果落盘,以 wasm 哈希加引擎版本作为键;引擎升级后后台重新编译。 -- ~~调用改为 async~~(P4 实施时改为线程 actor,见下一条):async 方案要把宿主绑定全部改成 async, - 而一个实例同一时刻只执行一个调用,换来的只是少几个线程。 -- 每个活动插件一个 actor 线程独占 `Store`,调用方把任务投进邮箱、在回复通道上等待,不再跨调用持锁; - 软超时由调用方停止等待实现,硬超时和取消由 store 的 `epoch_deadline_callback`(每个 epoch tick 检查 - 截止时间和取消标志)中断。fuel 保留作为死循环兜底。空闲回收后线程随之结束。 -- 超时与实例复用(P0 已验证):丢弃 future 后实例被 wasmtime 标记为不可再进入,必须重建;从 `InstancePre` - 重建约 50µs。编辑路径 Provider 用“软超时丢结果、硬超时才重建”的两段式,目的是保留实例状态; - 用户主动取消优先走协作式取消(`task.is-cancelled` / `cancelled()`),宽限期后再硬取消。 -- 分级预算:编辑路径 Provider 软超时 50ms(丢弃结果),硬超时 500ms(重建实例);命令 5s; - 后台任务时限更长,但必须上报进度。 -- 故障恢复:指数退避重启,连续失败才熔断,不再一次 trap 就停用整个会话。 -- 空闲后销毁实例,持久状态通过 `storage` 接口保存;`on-schedule` 到点时重新激活,不阻止回收。 -- 每插件统计激活耗时、调用延迟、错误数,在设置页显示,并写入 `perf/budgets.json`。 - -性能预算(P4 已用 release 实测确认,数字见 P4 完成情况): - -| 指标 | 初值 | -| ------------------------------------------- | ------ | -| 安装 50 个插件时 `startup.readyMs` 增量 | ≤ 20ms | -| 启动阶段实例化的 Wasm 数 | 0 | -| 有预编译缓存时单插件冷激活 | ≤ 30ms | -| 编辑路径 Provider p95(本地) | ≤ 50ms | -| 编辑路径 Provider p95(远程,扣除网络 RTT) | ≤ 50ms | -| 已回收插件的常驻内存 | 0 实例 | - -### 4.6 信任与授权 - -- 授权账本:记录插件 id、版本、包哈希、已授权能力,以及高风险能力对应的二进制绝对路径和文件哈希。 -- 安装时展示能力清单,确认结果写入账本;更新后能力有增加时要重新确认,能力不变时静默升级。 -- 项目插件 `.openidea/plugins/*`:项目受信只是前提,每个插件还要单独确认后才加载。 - 首次迁移标为受信的项目也遵守这一条。 -- 开发插件:“链接开发插件”按**目录路径**授权,不按包哈希授权,避免每次构建都弹窗;能力有增加时仍然重新确认。 -- 已安装插件“声明即授权”的旧行为,在账本上线时一次性迁移:已有插件只自动授予低风险能力,其余首次使用时再确认。 - -### 4.7 分发 - -- 包格式:单文件 zip,内含 manifest、wasm、资源、签名。 -- 签名:Ed25519。信任根是官方索引里登记的发布者公钥;发布者的 id 前缀(`acme.`)与公钥绑定; - 发布者自行轮换密钥,旧密钥签发的版本继续有效,直到被吊销。 -- 未签名包:只能通过“从文件安装”或“链接开发插件”安装,安装页显示明确警告。 -- 吊销:静态索引附带吊销和下架列表,宿主每天检查一次;命中的插件禁用并通知用户。 -- 目录:`plugins///` 加 active 指针,支持原子升级和回滚;数据放 `data//`。 -- 市场:静态索引(Git 仓库 + JSON + CDN)。 - -### 4.8 SDK 与工具 - -“只走公开 SDK”是验收前提,所以 SDK 本身就是交付物: - -- `openidea-plugin` Rust crate:封装 wit-bindgen 生成的绑定,提供命令、Provider、视图的辅助类型。 -- 脚手架:`new`(模板)、`build`(组件化并校验 import 与 manifest 一致)、`link`(链接开发插件)、`pack`(打包与签名)。 -- mock host:插件作者在 `cargo test` 里不启动 IDE 就能测命令和 Provider。 -- 官方插件全部使用这套工具构建,宿主测试也通过 mock host 覆盖。 - -### 4.9 否决的方案 - -| 方案 | 否决原因 | -| ------------------------------------ | -------------------------------------------------- | -| JS 扩展宿主进程 | 没有真正的沙箱,要多维护一个 Node 进程,API 面巨大 | -| 第三方 JS 直接进主 WebView | 能调用全部 Tauri IPC,完全绕过权限 | -| Rust `dylib` 原生插件 | ABI 不稳定,没有隔离 | -| 第一方 feature 全部插件化 | 大量重写,换不来用户价值 | -| 通用 JSON-RPC `handle(method, json)` | 丢掉类型安全和 WIT 契约 | -| 一开始就定为 API 1.0 | 视图和远程没验证,大概率要破坏性修改 | -| `process:` 运行时按 PATH 解析 | 仓库可以在 PATH 前面放同名程序冒充 | - -代价:Wasm 生态较小。可提供 TS SDK(`jco componentize`)缓解,但单包体积会明显增大。 - -## 5. 实施阶段 - -每阶段都可以单独发布。从 P3 起,每阶段的验收同时在本地和远程项目上运行。 - -### P0 验证 - -- [x] 验证 wasmtime 49 能否按接口绑定可选导出;不行就退回“基础 world + 多个组合 world”(可以,按接口绑定) -- [x] 验证 async 调用:epoch 让出、丢弃 future 取消、**取消或超时后实例能否复用**(不能复用,重建约 50µs) -- [x] 验证 WASI p2 上的流式和任务模型(`pollable` + `task` 资源),API 不依赖 p3 -- [x] Solidity、MyBatis Navigator 最小原型,基于 `ide-syntax` 和 `ide-index` 定稿 `syntax`、`index` 接口 -- [x] 修正 `plugins/README.md` 中不生效的语言能力说明 - -结论见 `docs/plugin_p0.md`。 - -验收:四个验证项都有书面结论;结论推翻了第 4 节设计的,先改文档再进入 P1。 - -### P1 内核 - -- [x] 新增 `ide-extension`:manifest、`ExtensionPoint`、注册表、记账、commit 失败回滚 -- [x] 迁移命令、主题、语言到扩展点 -- [x] 前端贡献投影层,`when` 接入 `src/platform/context.ts` -- [x] 宿主移入 `ide-host`,`src-tauri` 只保留适配 -- [x] 安装改为先写临时目录、再原子替换,不先删旧版 -- [x] SDK crate、`new`/`build`/`link` 脚手架、mock host -- [x] 官方插件:Material Icons - -验收:现有 `crates/ide-plugin/tests/host.rs` 场景全部通过;新增 `iconThemes` 不改内核; -Material Icons 用 SDK 脚手架构建。 - -完成情况:`host.rs` 迁为 `crates/ide-host/tests/plugins.rs`,原场景全部保留并通过,`hello` 改用 SDK -重新构建;`iconThemes` 只在 `ide-host/src/plugins/{icons,points}.rs` 注册,`ide-extension` 未改。 -SDK 在 `sdk/rust/openidea-plugin`(`Plugin` trait、`register_plugin!`、原生目标上的 `mock::MockHost`); -脚手架是 `ide-host` 的第二个二进制 `openidea-plugin`(`new --template command|theme|icon-theme|language`、 -`build`、`check`、`link`/`unlink`/`links`),`check` 走与安装相同的校验并拒绝导入 WIT 以外接口的组件。 -Material Icons 在 `plugins/official/material-icons`,由 `new --template icon-theme` 生成, -106 个原创图标,文件夹图标同样生效(有角色的目录保留内置图标)。规模测试:3000 个 SVG 的主题 -安装约 1.2s、启动加载约 0.28s(release;debug 为 1.7s / 0.6s),启动时逐个重新净化,P4 的贡献索引缓存应消除这部分。 - -### P2 授权 - -- [x] 授权账本、安装确认页、能力变更时重新确认 -- [x] 项目插件逐个确认;开发插件按路径授权 -- [x] 已安装插件的授权迁移 -- [x] 风险分级展示;`process:` 绝对路径解析和哈希校验 - -验收:打开受信仓库时,未确认的项目插件一律不执行;能力增加时要求重新确认; -替换授权过的二进制后,调用被拒绝并提示重新确认。 - -完成情况:账本在 `ide-extension/src/ledger.rs`(`grants.json`,按已安装 id 或目录路径记账);三条验收 -分别由 `plugins.rs` 的 `untrusted_or_unconfirmed_project_plugins_never_run`、 -`new_capabilities_require_consent_again`、`replaced_binaries_are_refused` 覆盖。0.1 授权只自动迁移低风险能力, -其余在首次调用时弹出确认(`ConsentRequested`)。语言声明推导出的 `process:` 视为可选,拒绝后语言照常可用。 - -### P3 语言全链路 - -- [x] `LanguageRegistry` 覆盖层,插件语言进入语言检测和 LSP -- [x] LSP、DAP 声明,接入 `process:` 授权 -- [x] 插件提供语法树 -- [x] 开放 `ide-run` 的运行配置检测 -- [x] 官方插件:Solidity - -验收:纯声明插件能启动语言服务器,本地和远程都可用;拒绝 `process:` 后语言高亮照常、LSP 不启动; -把一个内置语言定义迁成插件,跑通 DAP。 - -完成情况: - -- **覆盖层**:`LanguageRegistry::attach_plugins` 挂上 `PluginLanguages`,按 revision 惰性重建,项目 fork 自动跟随; - 内置和用户定义在 id、扩展名、文件名上都优先,插件不能抢占。插件语言变化时发 `LanguagesChanged`, - 宿主重新检测已打开文档、重配 LSP,前端刷新语言表。 -- **`process:` 授权**:服务器和调试器只在授权后出现在有效配置里,并带 `ProgramPin`(授权时的绝对路径 - - blake3);`ide-lsp`、`ide-dap` 启动前校验,文件被替换即拒绝(`PermissionDenied`)。用户在 - Settings › Language Servers 自己改了命令则不再受插件授权约束。 -- **远程**:客户端把插件语言和已授权程序名经 `plugin_sync_languages` 推给 `openidea-server`,服务器在本机 - `PATH` 上解析并钉住(`plugin-pins.json`),撤销授权即删除钉住;连接建立和语言/授权变化时同步。 -- **语法树**:WIT 新增可选导出 `syntax-provider`(`plugin-syntax` world,扁平 token/symbol/fold,UTF-8 字节偏移); - 运行时按接口绑定,旧组件不受影响;`ide-language` 的 `ExternalSyntax` 在没有内置 grammar 时接管, - 结果按文档版本缓存;SDK 提供 `SyntaxProvider` 和 `register_plugin!(T, syntax)`。远程项目暂不运行插件 Wasm(P4/P5)。 -- **运行配置**:`ide-run` 的 `ExternalDetection` 钩子 + `runDetectors` 扩展点(标记文件 → 命令), - 命令同样是 `process:` 能力,启动前校验钉住的程序。 -- **Solidity**:`plugins/official/solidity`,SDK 构建,手写词法器提供高亮、结构视图、折叠;声明 - Nomic Foundation 语言服务器和 `solc --lsp`(回退),Foundry / Hardhat 运行配置。 -- **DAP 迁移**:`ide-dap` 测试把内置 `gdscript.toml` 作为插件语言加载(无内置定义),经钉住的程序跑通调试会话, - 替换程序后拒绝启动。 - -### P4 运行时 - -- [x] 贡献索引与惰性激活 -- [x] 预编译缓存 -- [x] ~~async 调用~~线程 actor、分级预算、熔断、空闲回收 -- [x] 每插件统计;4.5 节的预算写入 `perf/budgets.json` - -验收:安装 50 个插件时达到 4.5 节预算,启动不实例化 Wasm。 - -完成情况: - -- **贡献索引**:`ide-extension::ContributionIndex`,已安装副本(有 `.package-hash`)的 UI 声明校验结果按 - 包哈希 + 扩展点 + 声明 + IDE 版本落盘(`plugins/.cache/contributions`),启动直接复用,不再逐个净化 SVG; - 链接和项目插件不进索引。后台预热时清理不再使用的条目。 -- **预编译缓存**:`PluginEngine::compile_cached`,键为模块 blake3 + `precompile_compatibility_hash` - (`plugins/.cache/components`),安装校验时顺带写入,启动完成后后台补齐并清理。 -- **运行时**:`ide-plugin::Actor`(见 4.5),`Limits` 增加 `edit_soft`/`edit_hard`/`restart_backoff`/ - `max_failures`/`idle_timeout`。trap 后丢弃实例,下次使用时按 1s、2s… 退避重建,连续 3 次才标记 Failed; - 正常返回即清零。用户取消(`PluginHost::cancel`)不计入熔断。`PluginHost::tick`(桌面端每 5s) - 负责回收空闲 5 分钟的实例、上报无人等待的崩溃、触发到期的 `on-schedule`(激活后照样会被回收)。 - “从 InstancePre 重建”改为从预编译缓存重新实例化,冷激活同样很便宜,回收后不必常驻已编译组件。 -- **统计**:激活次数与耗时、调用数、错误数、最慢与近 128 次 p95、崩溃、编辑路径超时、回收次数, - 设置页显示。`crates/ide-host/benches/plugins.rs` 测 50 个插件启动加载、冷激活、编辑路径调用, - 预算在 `perf/budgets.json` › rust › `plugins/*`,`pnpm perf:rust` 检查。 -- **实测**(2026-11-01,release bench profile,Apple Silicon,`cargo bench -p ide-host --bench plugins`, - criterion 均值及 95% 区间): - - | bench id | 实测 | 预算 (`perf/budgets.json`) | 占比 | - | ---------------------------------------------- | ---------------------- | -------------------------- | ---- | - | `plugins/load_50`(50 插件建宿主 + 加载) | 16.93ms(16.51–17.49) | 20ms | 85% | - | `plugins/cold_activation`(预编译,激活+调用) | 0.313ms(0.293–0.337) | 30ms | 1% | - | `plugins/syntax_highlights_2k_lines` | 1.69ms(1.67–1.73) | 50ms | 3% | - - 50 插件为 20×hello、10×solidity、10×material-icons、10×纯声明插件;启动不实例化 Wasm 由 bench 内断言保证。 - 三项均在预算内,验收通过。预算值不收紧:`load_50` 已占 85%,另两项是面向用户的体验上限而非回归门限, - 余量留给更慢的机器和远程场景。`load_50` 量的是插件宿主加载耗时,作为 `startup.readyMs` 增量的上界近似; - 语法高亮测的是整篇 2k 行单次调用,比编辑路径的单次 Provider 调用更重。 - -### P5 事件与任务 - -- [x] WIT 拆分、资源句柄、事件、任务、存储、VCS -- [x] 带版本校验的编辑接口 -- [x] `hello` 迁移到 0.2 API -- [x] 官方插件:String Tools、Key Promoter、Git Toolbox - -验收:三个插件只用公开 SDK 完成;Git Toolbox 的定时 fetch 在空闲回收后仍能按时触发。 - -完成情况: - -- **WIT 0.2**(`wit/`,按接口分文件):`types`(`error` 变体)、`log`、`contributions`、`ui`(通知、状态栏项)、 - `storage`、`workspace`(`project` 资源、`projects()`)、`editor`(`text-editor` 资源)、`invocation`、`vcs`、 - `events`、`tasks`;导出 `lifecycle`、`commands`(`run(id, borrow, arg)`)、`event-handler`, - 可选 `syntax-provider`。缺能力一律返回 `error::denied`,不再 trap。0.1 组件不再运行(无适配层,见 4.3)。 -- **编辑**:`apply-edits(version, edits)` 在宿主经 `Documents::apply_edits_at` 于文档锁内比对版本, - 不符返回 `stale`;成功即一次 `DocPatched`,编辑器里是一步撤销。命令调用时前端带上焦点编辑器的路径、 - 语言和选区(UTF-16 列)。 -- **事件**:前端按插件关心的种类(`plugin_event_kinds`)上报打开/保存文件、焦点编辑器、动作(带来源和快捷键)、 - 仓库变化;宿主按插件和窗口排队,运行中的插件即时投递,空闲插件攒 2s 再唤醒。订阅经 `subscribe` 或 - manifest `on-event:`,整个会话有效、跨空闲回收。`on-schedule` 到点投递 `scheduled`。 -- **任务**:`tasks.begin` 进入前端后台任务列表(可取消,取消同时取消宿主侧的 fetch),打开任务的调用改用 - 60s 后台预算;任务不跨调用。 -- **存储**:`plugins/.data//storage.json`,1 MiB,卸载时删除。**VCS**:`ide-vcs` 读状态,fetch 仅在受信项目。 -- **SDK**:`Invocation`/`Project`/`TextEditor`/`Task` 句柄、`transform_selections`、`text` 坐标工具; - mock host 有带版本的编辑器、存储、订阅、任务、VCS。脚手架模板随之更新。 -- **官方插件**(`plugins/official/`):String Tools 26 条命令分 4 组、多光标一步撤销;Key Promoter 由 - `on-event:action-performed` 激活、计数存 storage;Git Toolbox `on-schedule:300` 抓取所有项目、状态栏显示 - 分支和 ↑↓。宿主端到端测试 `official_*`,验收由 `official_git_toolbox_fetches_on_schedule_after_idle_reclaim` - (本地 bare 远端、回收后 `tick` 到点、状态栏变为 `main ↓1`)覆盖。 - -### P6 Provider 与网络 - -- [x] 通用 Provider:诊断、快速修复、补全、内联补全、导航、行标记 -- [x] 网络、密钥(经本地代理)、一次性进程 -- [x] 官方插件:Checkstyle、AI Completion、MyBatis Navigator - -验收:三个插件只用公开 SDK 完成;AI Completion 在连续输入时旧请求能被取消,达到编辑路径预算; -Checkstyle 在远程项目上读到的是远端文件。 - -完成情况: - -- **WIT**:新增 `io.wit`(`diagnostics.publish`、`net.fetch`、`secrets.is-set`、`process.run`)、 - `providers.wit`(`completion`、`inline-completion`、`code-actions`、`definition`、`line-markers`), - `workspace.project.find-files`(`ignore` + glob,遵守 `.gitignore`)。偏差见 4.3。 -- **Provider**:manifest `[[contributes.providers]] kinds/languages` 声明;宿主 `provide_*` 分发, - 结果并入现有 LSP 操作(补全列表、⌥⏎ 快速修复、⌘B、内联补全),新增 `plugin_line_markers` 操作和编辑器 - 行标记栏(有标记时才出现)。编辑路径走 50ms 软 / 500ms 硬预算;内联补全不受 50ms 限制,但请求被取代即中断。 -- **网络**:共享 reqwest 客户端;仅 https(`localhost` 可 http)、按 `net:` 授权、拒绝跨主机重定向、 - 8 MiB 上限、随调用取消。**密钥**:`[[contributes.secrets]]` 在 设置 › 插件 中录入,存 macOS 钥匙串 - (其他平台为 0600 文件);guest 读不到值。**进程**:只运行 `process:` 授权时固定的程序(路径 + 哈希), - 工作目录为受信项目根,4 MiB 输出,取消或超时即杀。**诊断**:按插件和文件替换,进 Problems 视图。 -- **远程**(放置规则 1、4):`openidea-server` 内置插件宿主(`plugins::remote_host`)。桌面端在连接时和插件 - 变化后(800ms 防抖)同步已启用的代码插件:先发摘要和授权,再只上传服务器缺少的包;服务器按桌面端授权 - 记录,`process:` 在服务器 `PATH` 上解析并固定。窗口的运行时命令(运行命令、事件、状态栏项、任务、行标记) - 与桌面命令同名,远程窗口自动路由到服务器;插件事件以 `plugin` 通知回到该窗口;密钥由服务器以 - `plugin_secret` 请求向桌面端按需索取。项目插件(`.openidea/plugins`)暂不在远程运行,服务器侧 storage - 与桌面端不同步。 -- **官方插件**:AI Completion(Ollama / OpenAI,密钥只发往 api.openai.com);Checkstyle(保存时检查, - 新保存取消旧任务,三种快速修复,项目级配置 `checkstyle.xml` / `config/checkstyle/checkstyle.xml` 优先); - MyBatis Navigator(接口 ↔ XML 跳转、行标记、XML 中 statement id 补全)。 -- **验收**:`official_ai_completion_cancels_superseded_requests`(连续输入时旧请求在宽限内被中断,结果在编辑 - 路径预算内);`official_checkstyle_checks_on_save`;`official_mybatis_navigates_between_interface_and_xml` - (p95 远低于 50ms);远程验收由 `remote_projects_run_the_desktops_code_plugins_on_the_server` 覆盖: - 经远程协议同步 Checkstyle 与 AI Completion,保存只存在于服务器上的文件,Checkstyle 在服务器上运行并读到该 - 文件,诊断经协议回到客户端;密钥向客户端索取;桌面端停用后服务器卸载。 - -### P7 UI 贡献 - -- [x] 菜单、键位、状态栏、设置 schema -- [x] 声明式视图、自定义编辑器 -- [x] 最后做 iframe -- [x] 官方插件:Kubernetes、CSV Editor - -验收:示例插件能做出带树视图的工具窗;CSV Editor 与文本视图双向同步并共享撤销; -两个插件在远程项目上都可用。 - -完成情况: - -- **L0 声明**(`plugins::ui_points`,全部在 Rust 侧校验,前端只投影):`menus`(编辑器、项目视图、编辑器标签 - 的上下文菜单,以及原生 Tools 菜单的动态槽 `pluginTools`)、`keymaps`(插件默认键位排在预设和用户覆盖之下, - 已被占用的按键丢弃;必须带 Ctrl/Alt/Meta 或功能键)、`statusBar`(声明即显示,代码用同 id 的 - `set-status-item` 更新)、`settings`(JSON schema 子集:boolean/string/number/integer、enum、默认值;生成 - 设置页、进设置搜索、项目可覆盖;值存在 `Settings.plugins[]`,插件经 `settings.get` 读取,类型不符回落到默认值)。 - 菜单、键位、状态栏只能引用本插件声明过的命令。 -- **L2 视图**(`wit/views.wit`):视图是数据——工具栏、body(tree、list、form、table、message、html)、状态行; - 可选导出 `view-provider.render/handle`,用户操作以 `view-event` 回传,代码用 `views.refresh` 请求重绘。 - WIT 不能递归,树是带 `parent` 的扁平列表,`expandable` 的节点展开时才取子节点;表格只取可见行(`viewport`), - 大文件不整体传输。`views` 成为工具窗,显示即激活(`on-view`)。 -- **自定义编辑器**:`customEditors` 按文件名模式接管打开(`option` 只出现在 Open With);编辑器标签挂到该文件的 - 共享文档上(`attachDocument`),插件经带版本的 `apply-edits` 改文档,所以文本标签同步显示、撤销是同一条 - 历史(有文本标签时记在它那里,否则记在自定义编辑器自己的历史),保存、脏标记、关闭提示都走文档; - “Open as Text” 打开文本编辑器。 -- **iframe**:`html` body 放进 `sandbox="allow-scripts"`(不透明源)的 `srcdoc`,CSP 只允许内联脚本、样式和 - `data:` 图片,无网络、无 IDE API;只经 `openidea.postMessage/onMessage` 与插件交换消息(`view-event::message`、 - `views.post-message`)。边界:声明式 body 能表达的一律用声明式,html 只用于富内容。 -- **终端**:`views.open-terminal(title, program, args)` 在调用窗口打开终端标签运行 `process:` 固定的程序, - 长连接流(`kubectl logs -f`)和交互会话(`kubectl exec -it`)都走它,不另加流式进程接口。 -- **远程**:`plugin_view_render`、`plugin_view_event` 与桌面命令同名,远程窗口路由到服务器;`viewChanged`、 - `viewMessage`、`openTerminal` 经 `plugin` 通知回到窗口,终端在服务器上运行。 -- **官方插件**:CSV Editor(RFC 4180、引号与换行、短记录补齐、插入/删除行、表头与分隔符设置、按版本缓存解析); - Kubernetes(命名空间 → Pods/Deployments/Services 逐级加载、状态摘要、日志和 shell 在终端、context/ - 命名空间/日志行数/shell 设置、Tools 菜单刷新)。`hello` 增加带树视图的工具窗和菜单、键位、状态栏声明。 -- **验收**:`sample_plugin_tool_window_with_a_tree`(声明在代码加载前就列出,显示即激活,展开触发 refresh, - 停用后声明全部撤销);`official_csv_editor_edits_the_shared_document`;`attachment.test.ts` 的共享撤销用例 - (无文本标签与有文本标签两种情况);`official_kubernetes_browses_the_cluster_and_opens_logs`;远程验收 - `remote_projects_get_plugin_views_and_custom_editors`(表格编辑的是服务器上的文档,集群用服务器的 kubectl, - 日志终端开在远程窗口)。 - -### P8 分发与 1.0 - -- [x] 包格式、签名、发布者公钥登记、吊销列表 -- [x] 版本目录、原子升级、回滚 -- [x] 静态市场索引与市场页 -- [x] 冻结 WIT,发布 `openidea:plugin@1.0.0` - -验收:升级失败能自动回滚;命中吊销列表的插件被禁用;10 个官方插件只改 `engines.openidea` 和 WIT 包版本即可切到 1.0。 - -完成情况: - -- **包与签名**(`ide_extension::package`):`-.oiplugin` 是 zip(manifest、声明引用的文件、组件、 - 许可证文件、可选的 `SIGNATURE.json`);解包拒绝越界路径、链接和超限内容。签名是 Ed25519,签的是 - `openidea-plugin/1\n\n\n<摘要>`,摘要覆盖 manifest 与包内文件。`openidea-plugin keygen`、 - `pack --key --publisher`、`index`(由一批包生成索引,保留已有吊销和旧公钥)。 -- **信任与吊销**:静态索引(`index.json`,格式 1)登记发布者及其公钥(信任根;发布者 `acme` 只能发布 - `acme.*`)、插件条目(含摘要)和吊销(某些版本或全部版本;公钥可标为吊销)。签名不成立(被改过、 - 不是该发布者的公钥、越权前缀、公钥已吊销)的包拒绝安装;无签名包从磁盘安装时提示。宿主保存最近一次读到 - 的索引(`market-index.json`),启动时、每次刷新(桌面端每天一次)都重新评估:命中的插件停用并给出原因、 - 通知用户,`set_enabled` 无法绕过;解除吊销后自动恢复。 -- **版本目录与回滚**(`plugins::install`):`///` 加 `active`、`previous` 指针,新版本 - 写完后才原子替换指针;更新后**新版本代码首次启动失败**(实例化失败、`activate` 出错或 trap)时自动回滚到 - 被替换的版本、删掉失败的版本、恢复旧的授权记录并通知用户;加载即失败的更新在安装时就回滚。设置页可手动 - “回滚到 …”。旧的平铺安装在下次更新时迁入版本目录;中断的迁移由 `recover` 复原。 -- **市场页**:设置 › 插件 › Available 填入索引 URL(`https://…/index.json` 或本地路径)后列出市场插件, - 安装/更新时下载、对照索引条目核对摘要和发布者签名,再走同一个授权安装页;已安装插件显示“由 … 签名 / - 未签名”。没有配置市场时仍显示本机可用的包。 -- **冻结 WIT 1.0**:包名改为 `openidea:plugin@1.0.0`,宿主 `ENGINE_VERSION`/`API_VERSION` 为 1.0;0.x 组件 - 不再运行(与 4.3 的约定一致:1.0 之后 minor 只做增量,major 由宿主保留旧版适配)。 -- **验收**:`a_failing_update_rolls_back_automatically`(`2.0.0-broken` 首次启动失败 → 回到 1.0.0、失败版本 - 删除、用户收到通知、之后正常;成功的更新可手动回滚;重启后仍是生效版本); - `signed_marketplace_plugins_install_and_revocations_disable_them`(签名安装、篡改与越权签名被拒、版本吊销 - 与公钥吊销都会停用且重启后仍生效、解除后恢复);`installs_from_before_versioning_keep_working_and_move_on_update`; - 10 个官方插件的源码未改,只把 `engines.openidea` 改为 `^1.0`、用 1.0 的 SDK 重新构建,`openidea-plugin - check` 全部通过,宿主与远程测试全部通过。 - -## 6. 官方验证插件 - -第二列是功能原型,不进入产品名、代码和提交信息;不移植原插件的代码。 - -| # | 官方插件 | 功能原型 | 级别 | 主要检验 | 阶段 | -| --- | ----------------- | ---------------------------------- | ------- | -------------------------------------------------------------------------------- | ---------------- | -| 1 | Material Icons | Atom Material Icons | L0 | 新扩展点只需一处注册;上千个 SVG 的净化、包体积、首屏加载;SDK 脚手架 | P1 | -| 2 | Solidity | Solidity | L0 + L3 | 声明式语言包走通 LSP;`process:` 授权;开放 `detect_*`;插件语法树 | P0 原型,P3 完成 | -| 3 | String Tools | String Manipulation | L1 | 大量命令声明和菜单分组;未激活也能显示;多光标编辑合并撤销;SDK 体验 | P5 | -| 4 | Key Promoter | Key Promoter X | L1 | 高频事件批量投递,避免频繁唤醒;按键事件属于中风险授权;持久化统计 | P5 | -| 5 | Git Toolbox | GitToolBox(不含内置已有的 blame) | L1 + UI | 定时 fetch 与空闲回收的配合;VCS 读写权限;状态栏刷新 | P5 | -| 6 | Checkstyle | CheckStyle-IDEA | L1 + L3 | 一次性进程;诊断和快速修复;保存时触发并取消旧任务;项目级设置;远程 | P6 | -| 7 | AI Completion | GitHub Copilot | L1 | 按键级延迟和取消;流式网络;密钥经本地代理;内联补全从 LSP 专用改为通用 Provider | P6 | -| 8 | MyBatis Navigator | MyBatisX | L1 | Mapper 与 XML 互跳、行标记、基于 `ide-index` 的跨文件索引、SQL 补全 | P0 原型,P6 完成 | -| 9 | Kubernetes | Kubernetes | L2 + L3 | 声明式树视图的能力上限;日志长连接流;`kubectl exec` 接内嵌终端;项目外访问授权 | P7 | -| 10 | CSV Editor | CSV Editor | L2 | 自定义编辑器双向同步、共享撤销、大文件;划定声明式视图与 iframe 的边界 | P7 | - -候补:Grep Console(运行控制台过滤,首选)、Translation(简单网络场景)、PlantUML(预览 + 外部渲染)。 -DAP 不在这 10 个之内:P3 把一个内置语言定义迁成插件来测。 - -### 验收规则 - -1. 只用公开 SDK:代码放在 `plugins/official/`,不依赖内部 crate,宿主里没有按插件 id 判断的逻辑。 -2. 缺 API 就加通用扩展点,并记录有没有第二个使用者;只有一个使用者的 API 要重新审视。 -3. 10 个全部安装后,启动不实例化 Wasm,指标符合 4.5 节预算。 -4. 自动化测试覆盖禁用和卸载:菜单、视图、语言、进程都清理干净。 -5. 每个插件都跑 panic、超时、拒绝授权三类测试:IDE 不受影响,插件能降级运行。 -6. 从 P3 起,每个插件都在远程项目上跑一遍。 - -## 7. 风险与待定项 - -| 项 | 影响 | 处理 | -| ------------------------------------------------------------ | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -| `syntax`、`index` 接口 | 决定语言类和框架类插件能否成立 | 已在 P0 定稿(`docs/plugin_p0.md` 第 4 节),P3、P6 实现时验证 | -| 取消或超时后实例能否复用 | 决定编辑路径 Provider 的成本 | P0 结论:不能复用,重建约 50µs;用两段式超时保留状态 | -| 可选导出的绑定方式 | 影响 Provider 的接入形式 | P0 结论:按接口 `GuestIndices` 绑定 | -| 流式和任务模型 | AI Completion、Kubernetes 依赖 | P0 结论:p2 资源 + `pollable` 可行 | -| 远程模式下编辑路径的延迟 | 补全类 Provider 在远程项目上可能不可用 | P4 起单独设远程指标;不达标时允许 Provider 声明“仅本地” | -| `on-schedule` 与空闲回收的关系 | Git Toolbox 依赖 | P4 实现,P5 用 Git Toolbox 验证 | -| 项目外文件访问:`fs.read:` 还是只给 `process:` | 影响能力模型 | 已定(P7):Kubernetes 只要 `process:kubectl`,kubeconfig 由 kubectl 自己读,插件不读项目外文件;`fs.read` 仍保留为高风险能力,暂无使用者 | -| 发布者密钥丢失或泄露 | 影响已发布版本的可信度 | 吊销列表 + 发布者重新登记,P8 写成流程文档 | -| Wasm 单包体积(尤其 TS SDK) | 影响分发和内存 | P4 纳入性能预算观测 | diff --git a/perf/budgets.json b/perf/budgets.json index 21ad2c6e5..f831d10c2 100644 --- a/perf/budgets.json +++ b/perf/budgets.json @@ -1,5 +1,5 @@ { - "$comment": "Upper bounds, lower is better. ui: `.` from `pnpm perf` (medians of the measured runs; enforced at CPU 1x only). Latencies are input → start of the first frame showing the result, so ~16.7 ms is 'next frame'. rust: criterion bench id → mean in microseconds, from `pnpm perf:rust` (`plugins/…`: the runtime budgets of docs/plugin_plan.md 4.5).", + "$comment": "Upper bounds, lower is better. ui: `.` from `pnpm perf` (medians of the measured runs; enforced at CPU 1x only). Latencies are input → start of the first frame showing the result, so ~16.7 ms is 'next frame'. rust: criterion bench id → mean in microseconds, from `pnpm perf:rust` (`plugins/…`: the plugin runtime budgets).", "regressionTolerance": 0.2, "regressionFloor": 1, "ui": { diff --git a/plugins/README.md b/plugins/README.md index 4765e4212..fc4258823 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -30,8 +30,7 @@ sdk/rust/openidea-plugin/ # the Rust SDK (host API, Plugin trait, mock host) ``` The plugin API is defined in [`wit/`](../wit/) (`openidea:plugin@1.0.0`, world -`plugin`); the design and roadmap are in -[`docs/plugin_plan.md`](../docs/plugin_plan.md). +`plugin`). ## 1. Quick start @@ -207,13 +206,13 @@ panic = "abort" `src/lib.rs`: ```rust -use openidea_plugin::{Invocation, NotificationLevel, Plugin, host, register_plugin}; +use openidea_plugin::{Error, Invocation, NotificationLevel, Plugin, host, register_plugin}; #[derive(Default)] struct WordTools; impl Plugin for WordTools { - fn run_command(&mut self, id: &str, ctx: &Invocation, _arg: Option<&str>) -> Result, String> { + fn run_command(&mut self, id: &str, ctx: &Invocation, _arg: Option<&str>) -> Result, Error> { let editor = ctx.editor().ok_or("no file is open")?; match id { "count" => { @@ -226,7 +225,7 @@ impl Plugin for WordTools { editor.transform_selections(|s| s.to_uppercase())?; Ok(None) } - _ => Err(format!("unknown command {id}")), + _ => Err(Error::NotFound(format!("command {id}"))), } } } @@ -261,31 +260,30 @@ then run `openidea-plugin check`. ### The API -| Rust SDK | WIT | Notes | -| ------------------------------------------------------------------------ | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Plugin::run_command(id, ctx, arg)` | export `commands.run` | `ctx` is the window: `ctx.project()`, `ctx.editor()`. `Err(msg)` shows _" failed: msg"_; the plugin keeps running. | -| `Plugin::handle_events(events, ctx)` | export `event-handler.handle` | Batches of subscribed events, oldest first, per window. | -| `Plugin::activate` / `deactivate` | export `lifecycle` | Per instance: after a crash or idle reclaim a new one is activated. | -| `TextEditor::document()` → `{version, text}` | `editor.text-editor.document` | `selections()` are those when the command ran (0-based lines, UTF-16 columns). | -| `TextEditor::apply_edits(version, edits)` | `editor.text-editor.apply-edits` | One undoable change, only if the document is still at `version` — else `Error::Stale` and nothing changes. `transform_selections` retries once. | -| `Project::root()`, `read_file(path)` | `workspace.project` | Absolute or project-relative; `..`/symlinks escaping the root are rejected; UTF-8 only, ≤ 16 MiB; unsaved editor contents win. | -| `host::projects()` | `workspace.projects` | Every open project (for scheduled work without a window). | -| `Project::vcs_status()`, `vcs_fetch()` | `vcs` | Fetch runs git, so only in trusted projects; call it inside a task. | -| `host::subscribe(kind)` | `events.subscribe` | For the session, across idle reclaim (an event activates the plugin). Or declare `on-event:<kind>`. | -| `host::begin_task(title, cancellable)` → `Task` | `tasks.begin` | Shown with the background tasks; `report`, `is_cancelled`; the call may run 60 s instead of 5 s while it is open; dropping it ends it. | -| `host::storage::{get, set, delete, keys}` | `storage` | Survives restarts; the instance's memory does not. Deleted on uninstall. | -| `host::set_status_item(id, item)`, `remove_status_item` | `ui` | `text`, `tooltip`, `command` (one of yours, run on click); at most 8; kept while the plugin is idle. | -| `host::notify(level, title, body)` | `ui.show-notification` | Balloon in the window the command or events came from (every window otherwise). | -| `host::log(level, msg)` | `log` | IDE log, tagged with the plugin id. `eprintln!` output is logged too and shown when the plugin crashes. | -| `host::register_command`, `register_language`, `register_theme` | `contributions` | Run-time contributions; declaring them in `plugin.toml` is better (they show before the code loads). Max 256 commands. | -| `Project::find_files(glob, limit)` | `workspace.project.find-files` | Project-relative glob (`**/*Mapper.xml`); honours `.gitignore`; at most `limit` paths. | -| `host::publish_diagnostics(path, items)` | `diagnostics.publish` | Replaces this plugin's problems for the file (editor squiggles and the Problems view); an empty list clears them. | -| `host::fetch(request)` → `{status, headers, body}` | `net.fetch` | See _Network_. | -| `Project::run_process(program, args, stdin)` → `{code, stdout, stderr}` | `process.run` | Only the pinned program of a `process:` grant; runs in the project root of a trusted project; 4 MiB of output; killed when the call is cancelled or times out. | -| `host::refresh_view(view)` | `views.refresh` | The view (or custom editors) render again; call it when the content changed. | -| `host::post_view_message(view, text)` | `views.post-message` | To the script of the view's `html` body. | -| `host::open_terminal(title, program, args)` | `views.open-terminal` | A terminal tab in the invoking window running the approved `process:<program>` in the project root: output that keeps coming, interactive sessions. | -| `host::setting(key)`, `setting_bool`, `setting_string`, `setting_number` | `settings.get` | The user's value (project over global) or the schema's default. | +| Rust SDK | WIT | Notes | +| ------------------------------------------------------------------------ | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Plugin::run_command(id, ctx, arg)` | export `commands.run` | `ctx` is the window: `ctx.project()`, `ctx.editor()`. An error shows _"<title> failed: …"_; the plugin keeps running. `"msg".into()` is `Error::Failed`; `Cancelled` and `Denied` don't count as the plugin failing. | +| `Plugin::handle_events(events, ctx)` | export `event-handler.handle` | Batches of subscribed events, oldest first, per window. | +| `Plugin::activate` / `deactivate` | export `lifecycle` | Per instance: after a crash or idle reclaim a new one is activated. | +| `TextEditor::document()` → `{version, text}` | `editor.text-editor.document` | `selections()` are those when the command ran (0-based lines, UTF-16 columns). | +| `TextEditor::apply_edits(version, edits)` | `editor.text-editor.apply-edits` | One undoable change, only if the document is still at `version` — else `Error::Stale` and nothing changes. `transform_selections` retries once. | +| `Project::root()`, `read_file(path)` | `workspace.project` | Absolute or project-relative; `..`/symlinks escaping the root are rejected; UTF-8 only, ≤ 16 MiB; unsaved editor contents win. | +| `host::projects()` | `workspace.projects` | Every open project (for scheduled work without a window). | +| `Project::vcs_status()`, `vcs_fetch()` | `vcs` | Fetch runs git, so only in trusted projects; call it inside a task. | +| `host::subscribe(kind)` | `events.subscribe` | For the session, across idle reclaim (an event activates the plugin). Or declare `on-event:<kind>`. | +| `host::begin_task(title, cancellable)` → `Task` | `tasks.begin` | Shown with the background tasks; `report`, `is_cancelled`; the call may run 60 s instead of 5 s while it is open; dropping it ends it. | +| `host::storage::{get, set, delete, keys}` | `storage` | Survives restarts; the instance's memory does not. Deleted on uninstall. | +| `host::set_status_item(id, item)`, `remove_status_item` | `ui` | `text`, `tooltip`, `command` (one of yours, run on click); at most 8; kept while the plugin is idle. | +| `host::notify(level, title, body)` | `ui.show-notification` | Balloon in the window the command or events came from (every window otherwise). | +| `host::log(level, msg)` | `log` | IDE log, tagged with the plugin id. `eprintln!` output is logged too and shown when the plugin crashes. | +| `Project::find_files(glob, limit)` | `workspace.project.find-files` | Project-relative glob (`**/*Mapper.xml`); honours `.gitignore`; at most `limit` paths. | +| `host::publish_diagnostics(path, items)` | `diagnostics.publish` | Replaces this plugin's problems for the file (editor squiggles and the Problems view); an empty list clears them. | +| `host::fetch(request)` → `{status, headers, body}` | `net.fetch` | See _Network_. | +| `Project::run_process(program, args, stdin)` → `{code, stdout, stderr}` | `process.run` | Only the pinned program of a `process:` grant; runs in the project root of a trusted project; 4 MiB of output; killed when the call is cancelled or times out. | +| `host::refresh_view(view)` | `views.refresh` | The view (or custom editors) render again; call it when the content changed. | +| `host::post_view_message(view, text)` | `views.post-message` | To the script of the view's `html` body. | +| `host::open_terminal(title, program, args)` | `views.open-terminal` | A terminal tab in the invoking window running the approved `process:<program>` in the project root: output that keeps coming, interactive sessions. | +| `host::setting(key)`, `setting_bool`, `setting_string`, `setting_number` | `settings.get` | The user's value (project over global) or the schema's default. | `run-command` receives an optional string argument: `executeAction("plugin.hello.insert-date", "arg")` from another feature, or nothing when run from Search Everywhere / a shortcut. @@ -321,9 +319,17 @@ Then `register_plugin!(MyPlugin, providers)` and implement `inline_completion`, `code_actions`, `definition`, `line_markers`. Each gets the document (path, language, version, text). Results join the language servers' (completion lists, ⌥⏎ quick fixes, ⌘B, gutter icons; a code action -carries edits for any files, applied as one change). Editor providers run -under the edit-path budget: 50 ms, after which the result is dropped, and an -interruption at 500 ms. Inline completion is exempt from the 50 ms (it may +carries edits for any files, applied as one change). A completion item's +`kind` (`CompletionKind` in the SDK, a string in the WIT) picks its icon. +Editor providers run under the edit-path budget: 50 ms, after which the +result is dropped, and an interruption at 500 ms. Every plugin providing a +kind is asked at the same time, so each has the whole budget. + +Providers run in the plugin's one instance, with its commands, events and +views: they see the same memory (Checkstyle answers quick fixes from the +results of its last check) but also wait behind them. Keep commands and event +handlers short where you can; a provider call queued behind a long one is +dropped at 50 ms like a slow one. Inline completion is exempt from the 50 ms (it may need the network) but is cancelled as soon as the user types on: check `Task::is_cancelled` or just let the host interrupt the call. @@ -627,12 +633,77 @@ and the publisher's key, then shows the install page. ## 9. Testing the host The host's integration tests (`crates/ide-host/tests/plugins.rs`) load the -checked-in `hello` and test-fixture components. Rebuild them after changing the -SDK or the WIT file: +checked-in components of `hello`, the official plugins and the test fixtures. +Rebuild them after changing a plugin, the SDK or the WIT files: ```sh -cargo run -p ide-host --bin openidea-plugin -- build plugins/hello -cargo run -p ide-host --bin openidea-plugin -- build plugins/official/solidity -plugins/build.sh crates/ide-plugin/tests/fixtures/misbehave +plugins/build.sh # all of them +plugins/build.sh official/solidity # one +plugins/build.sh --check # what CI runs: nothing stale, everything builds cargo test -p ide-host --test plugins ``` + +Next to each `dist/plugin.wasm`, `dist/plugin.wasm.sources` fingerprints the +sources it was built from (the plugin, the SDK and `wit/`); builds are not +byte-reproducible across machines, so `--check` compares fingerprints. + +The SDK carries a copy of `wit/` in `sdk/rust/openidea-plugin/wit`; keep them +identical (a test in `crates/ide-plugin/tests/api_compat.rs` checks). Changing +the API itself follows _Evolving the plugin API_ below. + +## 10. Evolving the plugin API + +Until the plugin system's first release the API may still change freely. From +then on wasmtime decides what stays compatible: it resolves interface names +by semver (a component importing `openidea:plugin/x@1.0.0` links against a +host defining `@1.1.0`, and the host finds its exports the same way), but +function types must match exactly — a record with another field count, or a +variant or enum with another case count, doesn't link. So a released 1.x +only grows: + +1. **Released types and signatures never change**, whichever way the data + flows: no new record fields, variant or enum cases, renames or parameters. + Extend with new types and new functions (or interfaces). `types.error` is + used everywhere and is no exception; new situations use `failed(string)` + or `invalid(string)`. Sets that keep growing (completion, token, symbol and + fold kinds, icons) are strings for this reason. +2. **Host interfaces (imports) may gain functions and interfaces**: older + components don't import them. +3. **Plugin interfaces (exports) never gain functions** once released: the + host would not find them in older components. New capabilities get new + interfaces (e.g. a `hover-provider`) in new optional worlds. +4. **The host knows each component's API version** (the highest version of + its `openidea:plugin/*` imports and exports, `PluginEngine::check_component`). + Installing and `openidea-plugin check` refuse another major or a newer + minor with a message that says to update the IDE or rebuild with the SDK. +5. **One version number**: the `wit/` package version, `ENGINE_VERSION` and + `API_VERSION` agree, and the SDK's `wit/` copy is identical to `wit/`. + +Checked automatically: + +- SDK `tests/api_frozen.rs`: `tests/api/<version>/` holds the WIT of each + released version; every interface, type, function and world import or + export in it must still exist unchanged in `wit/` (rules 1–3). Copy `wit/` + there when releasing; there is no snapshot before the first release. +- `crates/ide-plugin/tests/api_compat.rs`: the versions agree, the SDK's copy + matches, the version check (rules 4, 5); a simulated next-minor host (every + import stubbed at the newer version, plus extra functions and an extra + interface) links every shipped component and finds its exports; another + major doesn't link; a host missing one function doesn't link. + +A new host function touches: + +| Where | Checked by | +| --------------------------------------------------------- | ---------------------------------------------- | +| `wit/*.wit`, copied to `sdk/rust/openidea-plugin/wit` | `api_compat.rs::the_sdk_carries_the_hosts_wit` | +| `crates/ide-plugin/src/wit_host.rs`: binding, capability | the compiler (bindgen's traits) | +| `crates/ide-plugin/src/runtime.rs`: `HostApi` method | the compiler | +| `crates/ide-host/src/plugins/api.rs`: implementation | the compiler (desktop and `openidea-server`) | +| SDK `src/host.rs`: the wasm and the mock `imp` | the compiler (CI builds both targets) | +| SDK public wrapper | SDK `tests/wit_coverage.rs` | +| Capability: `ide-extension/src/capability.rs`, consent UI | review; a test that a missing grant is denied | +| The API table above | review | + +Then run `plugins/build.sh`; CI's `plugins` job runs `plugins/build.sh --check` +plus fmt, clippy (host and wasm) and the tests of the SDK, the official +plugins and the fixtures. diff --git a/plugins/build.sh b/plugins/build.sh index 2b30f45c0..0c2639015 100755 --- a/plugins/build.sh +++ b/plugins/build.sh @@ -1,30 +1,95 @@ #!/usr/bin/env bash -# Builds Rust plugins into WebAssembly components (wasm32-wasip2 emits a -# component directly; no cargo-component / wasm-tools needed). +# Builds the repository's Rust plugins into WebAssembly components +# (wasm32-wasip2 emits a component directly; no cargo-component / wasm-tools). # -# plugins/build.sh # all plugins with a Cargo.toml -# plugins/build.sh hello # one plugin +# plugins/build.sh # every component the repo checks in +# plugins/build.sh hello # one plugin (a name under plugins/, or a path) +# plugins/build.sh --check [...] # change nothing; fail if a checked-in component +# # is stale or a plugin doesn't build (CI) # -# Output: <plugin>/dist/plugin.wasm (referenced by `wasm = ...` in plugin.toml). +# Output: <plugin>/dist/plugin.wasm (referenced by `wasm = ...` in plugin.toml) +# and <plugin>/dist/plugin.wasm.sources, a fingerprint of the sources it was +# built from: the plugin (minus target/ and dist/), the SDK and wit/. Builds +# are not byte-reproducible across machines, so --check compares fingerprints, +# not bytes. CARGO_TARGET_DIR, when set, is shared by all plugins. set -euo pipefail here="$(cd "$(dirname "$0")" && pwd)" +repo="$(cd "$here/.." && pwd)" rustup target list --installed | grep -qx wasm32-wasip2 || rustup target add wasm32-wasip2 +check=0 +if [ "${1:-}" = "--check" ]; then + check=1 + shift +fi + +sha256() { if command -v sha256sum >/dev/null; then sha256sum; else shasum -a 256; fi; } + +# Lists "<path> <sha256>" for the files git sees (tracked, or new and not +# ignored) under the given paths of directory $1, minus build output and Finder +# metadata; so a +# checkout fingerprints the same as the working tree that committed it. +list() { + local dir="$1" + shift + (cd "$dir" && git ls-files --cached --others --exclude-standard -- "$@") | + { grep -Ev '(^|/)(dist|target)/|(^|/)\.DS_Store$' || true; } | LC_ALL=C sort -u | while read -r f; do + if [ -f "$dir/$f" ]; then printf '%s %s\n' "$f" "$(sha256 <"$dir/$f" | cut -d' ' -f1)"; fi + done +} + +# Prints the fingerprint of everything plugin $1 is built from. +fingerprint() { + { list "$1" .; list "$repo" sdk/rust/openidea-plugin/Cargo.toml sdk/rust/openidea-plugin/src sdk/rust/openidea-plugin/wit wit; } | + sha256 | cut -d' ' -f1 +} + +failed=0 build() { local dir="$1" - local name + local name target out name="$(sed -n 's/^name *= *"\(.*\)"/\1/p' "$dir/Cargo.toml" | head -1)" - echo "building $name ($dir)" - CARGO_TARGET_DIR="$dir/target" cargo build --manifest-path "$dir/Cargo.toml" --release --target wasm32-wasip2 + target="${CARGO_TARGET_DIR:-$dir/target}" + out="$target/wasm32-wasip2/release/${name//-/_}.wasm" + local rel="${dir#"$repo"/}" + if [ "$check" = 1 ]; then + if ! CARGO_TARGET_DIR="$target" cargo build -q --locked --manifest-path "$dir/Cargo.toml" --release --target wasm32-wasip2; then + echo "FAIL $rel: does not build" + failed=1 + return + fi + local want have + want="$(fingerprint "$dir")" + have="$(cat "$dir/dist/plugin.wasm.sources" 2>/dev/null || true)" + if [ "$want" != "$have" ]; then + echo "STALE $rel: dist/plugin.wasm was not built from the current sources; run plugins/build.sh $rel" + failed=1 + else + echo "ok $rel" + fi + return + fi + echo "building $name ($rel)" + CARGO_TARGET_DIR="$target" cargo build --manifest-path "$dir/Cargo.toml" --release --target wasm32-wasip2 mkdir -p "$dir/dist" - cp "$dir/target/wasm32-wasip2/release/${name//-/_}.wasm" "$dir/dist/plugin.wasm" - echo " -> $dir/dist/plugin.wasm ($(wc -c < "$dir/dist/plugin.wasm" | tr -d " ") bytes)" + cp "$out" "$dir/dist/plugin.wasm" + fingerprint "$dir" >"$dir/dist/plugin.wasm.sources" + echo " -> $rel/dist/plugin.wasm ($(wc -c <"$dir/dist/plugin.wasm" | tr -d " ") bytes)" } if [ $# -gt 0 ]; then for p in "$@"; do - case "$p" in /*) build "$p" ;; *) build "$here/$p" ;; esac + if [ -d "$here/$p" ]; then build "$here/$p" + elif [ -d "$repo/$p" ]; then build "$(cd "$repo/$p" && pwd)" + elif [ -d "$p" ]; then build "$(cd "$p" && pwd)" + else echo "no plugin at $p" >&2; exit 2 + fi done else - for manifest in "$here"/*/Cargo.toml; do build "$(dirname "$manifest")"; done + # Every checked-in component built from a plugin.toml + Cargo.toml directory. + for manifest in "$here"/*/Cargo.toml "$here"/official/*/Cargo.toml "$repo"/crates/ide-plugin/tests/fixtures/*/Cargo.toml; do + dir="$(dirname "$manifest")" + [ -f "$dir/plugin.toml" ] && build "$dir" + done fi +exit "$failed" diff --git a/plugins/hello/dist/plugin.wasm b/plugins/hello/dist/plugin.wasm index 5570638caa5ca1daf6ad28ae8ea44d7bd3d3f346..b0c8caeecc4070cc047258c9eb6b267b6a9daf8d 100644 GIT binary patch delta 22006 zcmcJ13w+Pj|Nr}S-k)tBJ{xbD*=+cHJ{vZSVVgU9lRHsL^-XfgH>TW@u59E^xg5M@ zgmjTqn3_T@Nfeo)D9SBVgzgvW|9qYE{_Mi~{=Sd@|M%Y>=YG!Xyw2-(Ugw<m_Qh=P zx|!bf{VR(NKjaUn?29nBnOo$&F|i&|+3S@vVp0>Rval#z2(JhimWZk#D9j))g&`4g zUrcp*CMGeag115i(=?-qycNhBCHTiH^6OTQq?&^Ug_&ej3ICRYIb`SN78<@B!&cGh zO|o}vQqE7Ob=|r%I^F7~XWjZEZn+{4JmnT<oYkExab>PskgIj2rFx#wg*~Tqy3Fk~ z#Z9Ylr>)NyW-43?pVz(4x}%ED>2hxF4%J?y;!jk3(C|h4E>q)LiYX+I*GQ+Saz$L4 zGx7%z%9w_Hb8|WcofW@REpOu32Syq*sJ`R>gA#p-!v>AK``)3(OiFg>1SQK2HKLt) zKTr*iF&lm6dqkrAD9&rnK>_W4r~Z#rF~@i?a>$^;!|xh9bkM!`jv8WY5f$DeGHmh< z++-Gtz2;=frN^oNB-%+A>3h0BPlzSvuQW-_6<fp#agr8^jUrzx7MsLt!V#~F0`ass zMK6nW;!UwqEE7vbo;XcUiS^=Dv01z!7Kr)cYkJRoR;&`w3>2%yY_UX~pcln+;(4(~ ztQ9Ya4dNxSP`o0R3n|_f--_?VIdNWmFD{4~rZg9ri_H1v+vZ#5Q)Yp=+1z68GhZ-w znO~TP%+Jlk<|pQ7=6>@-v*L%B!nWl96(1#{H!ds8HL~qL4ZmT}`JD4sUViObuTsvI zrK%+!4d)K22IukIT*L4sPRd0ZH0)1xk7np`x%?h8^AwDF4E%7pFxKlH$>_1HOpp9t z{=bQyl)Zw<8S9oPF!lXpFZrw-Fl4{=l<JX;9&=n&^4A8xFSCkuW#AXD;*iFmDKOYt zevet2MvUX0@_Uq8#gQ^S@~hXm!$bM<{*-0(hP3KWrS)=g{drU<>our_eCGzWX_owV zgQnv2Q*vp8wlq&3YLF0rX>x9^5oP#IyATZd0t64`56j98U%`;K8m7=|@@&K5RSv4w zFt=BjbyyBgZQSshTJGfoz}ReS9rF)aZ?^5RE$g7%mRd&~oFspl)7(t3*2%OdYG8(z zjnXRUnGL|KwJcbww7k(Wx+GgQ-h}u<<KMh9T&}FXkS4&hi%tJ8c_w=`ORu?1Gw{vQ z(q^yGGdwAuZZ^#~pBH0C+8*l(**LwP*mFqUmOfgp=;W2(b*U=f>{O-ix-vkp&PZ!c zX@Eh~o}$IKCMdC0_ug8%NAZ-*3&Zz^CA(w<yT8Iyi!71lk4P2+!6HKol3ldCkQJ$> z^$GWgB%eQmyWE{AqPctM7n6<8t|Z9t$oR|@lOj#oB{Q*-@JHCkjBLcw+14BKq0D-f z4ZpX!@cIpZh33MD6mn-~fHvo!&9nt=m-SoDr<di{mbDOn(Q+JJk$qaFTQ4*I0l|}3 z^!u!L<ib{WRQh!)Bc|b7bLEv*Em}V}#jQ%tw&wB}+fcZ2G|cbuo7M-s2zQJY0)-z0 zVRY++^qfq=jXR2EQ|Ax6gCJs|G03Xg$Ou{3x>kIIjTxsfK{af|$=Mw+z4ds$w@ovu zv`X>Bop`S7*0xClbWJgbX;OGHg$EQ7|1iJY*0vVq$Rln0#4HUh5meP{g!T&Aq1~)< zq-R0<`91y@1x6leKSs>?LiWyTR&%?eNx1FL_J=DDoAx(P7lI*nxb?1FkrfBy@J3c` zn1aJuchL^n($1pa<s)`#lqO-gy^BCYxyind=E`7)7EP~#==E2y-gBqHYB3Kc6ZQo? zjR(`r=@1W~YdSQlUk0ce!)738`YU#h5|M__=a2G-LkO%{3p#dgx1|Jm(>|{S+zScb zuR%vzueu$8qn_5ZkA~Vd$SEB&X{+4QG4<wY3wg=t!i!N72QF%&Kt=S7Z<Q>;6(2<~ zSAHPUA^Kqpw#iyA+gzU=HjAZKKHE8iHp|aDw=Tn5e-~vLx^<Zh)4L(Px@#Yj{#RN3 zuddy5G*MwCc?Fey5HwGA&~O*=|G&DgnzQV5z#;O##lcsC@&UyI!}>UXe77)yoq4SL z1c==q-Q%lkix6(7g_K)m56gx<YDR{n%3?{qrAJeDr|_aQug4u_n}6-m__|Pkz|z)% z<dq&ZqyFT5sfN!lV1RbX!9AN**~~S}`9dQ*3He<4()9z%Fgd!DVshI~-DyiHCjWoh z1ZI%4vM<HR^v-oNq?T#5qiQ1xypx5+QM9#NQPKn#T*j#EVL7*Rb-AWz|1xW++)J&Y zMXw$udlH<H|9MZcg}eE@t#z}CwOj7!RVTVUTPCmc8eC@9>>FIlWi^YUVsEH;Q~Vzg zkm0@SwJC2NR!GBFpD4#t+qB5C=MeAIOpqX)8tlb$$eJUE_wJvzS?}s^lrw1S)q}QT zQ2x)o?>4KRT~Y{ZVrTfn>|L(ok$-RB9#sF3E-lmG+%=W&93H><9pOEqN&JRfd+Vx< zxtfM)P@QF;^90t_{_X|s2l~-to!dj^-u9aJvUd5F$+oxO<O;j6Sr_v2ZogM}&*|nj zWaa)%;T(17Ul;N4{<k20yZ<u8{qLOVJ?m0Jp1-qJ)#XpA1qLwDZ|(wRtpQEYwa<W< zQf}C&0eu5nmxQZG9@qK^SL#qNPYy_S$8ah7u09nOYvHs$k{9kuqmSg%cXf4pnCPMF z`~x4NebPTLLsnjWojf+MiL5=atGs<s6V%Qg^st;YsEYjdndY+R;0!t_ClB6Ef6Goo z5@X8X19#k$?KiBy<-{SWW$h%^1`9<lo}}?dB|L`xO6&VVgfLXYbFJt<4=M*z5*t9h zRKxmcQf>-N$z&q&KSPxVmu1|bM-q?B1+D?%^OIj(V^n><_Wuo{^3%IZJJ2+=x=V4a zTn2{r^_N*^iA651mJ5g0DaHEjLleb}L-Neff6<fq9sZq2Q8Sb<`#rWl+bWR5hxL)$ z?^$|H?VY8j-8XCs!?<Pmt@OTpdH86TK_Omv>>|=u*YJJEbW3ClY}jcdf*xgz4Y-PM z#Uj~b%oNE;C{y^g+64rP@Vc`zMG{hrJ(AKG<})ZXL}v#C`^J7xvS?%l!|a^SQHIE} z$sab(P94(;9yTUP7G6DpND)SJ(+?^4hsjmL0!@(7t&yDLRskI_wG8>M7B7#TCZEZ^ zmsf3#NQB&`kFcwJt55-y$UqoIe3vWtJ{KofjEKuQM_{eH>LO!AP`Jw$eu9mMXOFFl zQbc5P&_Ly4o13wmOBQ}mLf0iV2L)7J!%Wdo*I^C7miu8{UXUg&+qL8_jfC}PZmusE zlmKafTAIA;<r*E@WQwjBhGnKVH?x=sAUKX7s4=s!aLy%*u3!lt0Sue{bZn>AmwJVE zk&(ZC#8ZTIFMp;!G&f2fcqT?V_r=m)x&FSv^r5UeG81uk6;D#}W))vhaothfs(z?! zhu6-9%jdO!f|>kKP9D`CqrMt7*ypzTBdm?G=jhkzyfnw$fcT~{S%{^|pBU4hev|FT zw!<NF;@F0m`=zld7=LhV25pqS`+tr4RLucDbh8Llk&`AS(`Rz+#2WIZaZdosv2h8A zuZ&}e?H-tc`27cXj+pUWbL)7{KR<pI;;s*NL_Fug2K1>M{#ZJooPY2*YW6+U265!W zy%G0+nER}JxDVn>4?lpO_dKF|Cdkc?EU$T9(T@D#Dds~sh`|@Rs65$~BHKDI$3EH~ z<2F8e2jWVP{i}GMrypC3nr;)45RaVj5aKfvh9d4Yu^#$7Fp+0iJMkXWT$huJSmty_ z{pUGcUpe<))I63us~B3FNrRDJF{ve54^J9H=kt3^o<Qid`|-PIk4%};7x9EC)e-lf zmW24tDFMVsrZh$zIkj8WJ&K?#ZY*<=aG&?cVN<*1=z2Z^1WYJnPIk}}#fN|hu+|)h zU0Zdq{Q=WX2tcpM@5xRE5y)?H%5WGmWXz`Qpzw!vQNzQrGbh~}8Xg(pgZacH0SIWS z!P8Xocq1ab5i)IBRbW^e^GVa{glm}c#=%wP+tVt<t74?bXJ)Z$#TOR}3AKom(~@eq zD44J%x;Zw4n+!~6YIL4n2l2hr0~*^%SaQaYiLiWnGMaWy_ai<ty$|9RGdO?yj0D8v zXQUurIU}jsUPVCDo(HKXn|+vMoInoGSO>W+npI8a&9vRz4>PB`I%3+VAStH3N9o9% zSshi6I`oqKWmZ?@+s|%|{G+ouzhm|g`aveo8F`&L(SV{LvP(m`W=?G=#;!T{kh+`6 z33VZuRc(=ctHT84&_vf(U4*R)*DaRDJe(~>A1vF*6g`ok)D@z9Kp)B&L9KEy78^O2 zDXwSJqs)(q>k76b`Z)H|q&v{AXvp?uAX^$xGgGudg7bL`T}s#85vRMr89+Lp0rjM~ zJkVXN)2v}4i%)^qOaGJ6a`xz`92C-nq;>;zYmpr=5@&Hgn_3wlO{H)WrY9_%d~UUY zY7L`B@vs!pCJ>?9aa)Oj9^e+yoDZB#fPnz)4I!b`9O|87^0iA2gti2-Co<eE0XAl0 zIEJ#_!#Jn*B!-4vP7|q=(Xaq5GAsm)k$@<>NRci60ay+I#g+ptmZ6y?<IR5<kKz7u zhWmNA2qEKPWI`0g%EC&34bH?xQ0b*5mjf*s&|Fvn7)UmZ?gmM|EaenI+XLi^5mcYA zG^rCV8Xz+yttqkE0Qr0$hnH-$D``Z~TSj<cv{<fi7nUi~k)V`@1U|?0NZ^5F=~5dc z5X3%6Ac#G6sVhv`G00kk(%MLCy%KaUt{s3`9Z0wz`GGdU?Lbe=&>!PaH7rxyh8*U) zSst5P_nu)WV8*+VpwmF~1%VZ4tveb}e_W+t@XfG;XoRF&Tlu!j$CbCi7DV0)2a*fP zyze8IBES=*Xe0Pa{pIL+39ebzczgN89olpks!g{|Nrl2;SF!#o%=>4GIRuD+eRFd> zmgww4YA){OY_eE}MUB(7nEWAK350Udl~<Lcm-UU-OU*u)i5Ts7^(%39i9Yf$SY>P? zr8QG`V9jK0Ql*goV7;#XAm{22aAQtc=W`gaAIPMb1`8~duXrvG>rx1m(w=fbv0}T> zN`SFqv+{+8E9EI?cn*d_m9H7ER2j;rkx|+MT?#={+EWfRz=0-T159Z$>j85F_igT( zm7#eJsFT-TN%F_XGkkh`^Hnmey)t^id>jIwU(oQnCs~YHk1BwDuxhy3J+)wj-XLgJ z{s<XCwg2L!LoVSHV$<y$BnYKo1`F`Y`a{lN_&Qz5@49G!a1RB1K77^rl`hK{mZW>P z>ND;e^5l}f)jnnXe07voyRPb?C9=m;ft*4m;9etf4o~N+2!GW2sU-VLN%lfX_M*y0 zqb~@6dlNYMVb+hTvXWbAT~fu^GJVuie=glH&>gK>&XouDdsT_GV=n7ArdwqnZr9b! z)*rfDrFa(WPhI2ozcNW?Eo}t4j#|o(BD0qEqXqK#(x$XfM&~_==bF`dV@n;&%082H zTh^>p+ww-$>rVTx&!0K&`BObzsMnnTbOudVjQMX5%hn=Z<9|N5W^)%1Oke7&bGaIY zV?|Q73!r=Xv*HLJ>RRp*KQtboVA~df!Xq-Q<!Zj-Q`plo@Jx*gX(QQ8lC>iLj;Cu2 zb#953Up#vqEs@_pdpj*zuqqvu8LPgdRp=k2XJq!X{mf)MY4HK){Ig!U@0puadh_5^ z^t^TT1$wp&<mWKrwo-tXD!@xM;0e#Qw_YfV*BUiCe7wf%#rzA;jpOsw-7nOqJURD; ze<A+%g-p~0))XK<yrwbY3NKEkx8<A{@25)()-J|f|M@pNAwIe`H>CGa;xIVo)+~A7 zOScByi!%Y$g&6XCjJiG3FFRbECr`h0Hz2gz)|wW{N4C|H&#hzeEL_)sHpz4AZlbmM z?O&cm^tycKl~^i}pTBai<*1c}LH8k337}EGS6czb{8#x2a>J|1B<1I?cA-!6gX<d+ zeJ*d@@HoAhe{jRIMBC&e`L~3Oi~KG>Q_bDNgWJtTa=@k*0Fd{__P|`8A>d)>kP$ak zR$l|MlH;{*;+x&&@=fXVj{JC2V|piFyq3e)%#&YV<Xv*4Fc;SUHCel0K5ft6P_UM~ zYeIEH-bjnw>=xJ($(?W31r8xPX3F7PZo8J2`?u705oM8+Ew;7<q9v6qeDZLaN=;7g zwrSgo^t>GKRzF%TUqif1M!vm@R?1i2&L60Zug~W-tV6tF*k*__KQSsj4HeVM79o|Y zGn*cCE;PzEd)(qYB2%u~R4oS=X97Npdh1Y};~g{Lflwgh(GCl8xU^t7;foR#;R?eq zE>|?v5klS0;HpGB8~XLkMu`tmp|b<-njlh2`1BqGUw;OX{B*|y*lT*eS0~C9N3f8X zh3_@&?mnfWX9i4w$Z~gtJ!hDO%FlCefedRETVJeLw<-7CrzylMJ1Bp8FEdKh6VNIc z*6*_U&gNp%Q8{{Nbn;@2QC&=;0m6?z^(;?fUtplW9pk)}C)e$444xg?Y11)T>-~7- zTfIL8FBfmWUtM2%^A-c-kH6oO4#-Bk{8(D=T{W)CKff!O<nq=B@YY$TyWwlacVAEN zI&i)WP~wyF+^)o^OS(~Y{8lFHzER|UDM#&&qjPfR?toaP3cho?*IYp!a5WkS4;--; z%5Qh4WM1TrGZ@Ajjk>A9ZZ5LG%P{K?)dCf=DG-k8kP3ldPsnZ`bVx7W{SAAfx&d+- zP;`{(q1-0inz2l<tnlgF!e#RZ)j^lfKIjxZhvD0Q)0j%8AcLn(oGa5l^f%bKipSeO z8rH9jHx9`yvaREsg$D-@ABX%1e|Xm@5Bfj)VRsOC=Z8aTsDoMd%N_jFFd#Ym<tezF z@<R^o*>c^UI@d3A7^sF70t?mCV8eX=u*4ZmTeybGg9rgixEfIGEOqaHEg<>Lo{rw1 z8A~7VO5Yn0ht9}b_XcuSuI8ETFUnbt>GSjM_6v_H{-h<${#z3u0h7%8iU(U|z6rqn z<cH5Gn0uKxyN;_ad0I*>S0eUB2$2(pv$*_qZ>PI{Q#t{b1U-F1?=1F@D-;lA+i~E$ zF+PBc@_@-FJ0Jw!lp58+xG9Q!HUtln;5LWGC(e_3AEh+-Re|$UJoKDJ2>hJKV0hLr z9v}KhVcuDJ;iEW#q~gB%a9%R@-3pO-VqaV}<;5z}PYH%MHwz&&iSy-_egCRHMadMn zc~bLH*z4gvE|m?78b$x8AA4VhP}@Ig^PDF~7R5uaW)(eH^#Bt_?FRN5-gU-W2W0gA zuHvL42kcMjs+#%k=$OYnI9B#qUuurn;EtW^ao2lD;b6HBJM1>{O%EbmIa{B~efx)F z87)7qM@!|PkDG}lXXWCLdp7!cF;f}vt!!7GmE)@f&-#()2^~&FmNi3KpZIg0WU^)B zdxW33Sr65M%6l503WQxdTMMl1cBX4P!!IxCtT~#yl992OQ!y&QN`b~1fG-u;o0qSC zQn%IqMch-*t@Wh@ZHDW2iN)4(CTp_c+bT?f>mO%TU#cpsA7#>kdKn9NDIilY+!j69 z65ui(3p>vX!&ESc;r2r~awe!2L<xMMnR}o?JH2-GU|GC&F8RUcWRV18a!rq7$vpOz z{N_L%k-Jh_2W!%C+2~+~c<QVida!9L$p``e7oRC)O1#7Qm0HP;GEyNm#M6OMygQ)} zhVMU^<X_0cm67q-5z0330h(M1`FX0W_UVn*0i|tNwM|$vm^%K`I<d2uNI;&HVYJ*4 z7thH2Pg`3T)d+p9WY|S#<X@jAdrxV!evpkmtI=#K4-fLKBaoSMtPsdsKPd4q72i-> z8i-&ZkA8MNO_PP6rP2}k{b!A8Z74?+y)OSPd`WevUF0&YXY3~}UGQ6KDc$mv=$8Fk zb?Da7Lxb@3WBbFgb+0gP0ax6LFJB-St}Rlhv0ORfa4!t_@Nnbna#<z)A`r$R0HLrR zmyw@$@qMEiFbl^MIZ4LIai2HBfYqO;(qy^+^91qtBzXZ<XJoA}Zl+v${}-#PE?dQv zu;U;(o4-^<*(y_xw4~{B$dUR$^2sAH;+xg-`6KnkAFJiZM_OUajXHWCt(A`+trJ&j zr;3Dy5LvuaEitW&@{OZ46H0G@d_`4e1N`A=Yx+($`m%M5Ye0&(Pa6UX7dq$5j==HV zFT35c$khR87^uCSiDl>DZbpj$?i3fh9RecJ;cNyrcoWF52<}p=!o83yFuH8*kb}O8 z4Scjxt-b*u<8*J>$5dxza<%AelS{u!Oh2G%S!g>5?255y|HpwBH4(STOJ8LtXk9T% z>55UTEAqi(EkVl-#~L6$daP#Ld}bburG80c^N)h^6{;R@K)Yo7<1y*Q3k}l?Wlts? z3k}1y|35Bt=J8<CS4z_P=>S(-NIXGd?P5a0yTD5UUIK8Fu<v+n;Bw)3omB0a@Dm)= z0+)i8Iuqbo0p1=;?llLz+FTDfWuLfR96KYIod{ISdqxqok~LYre`0QB?S{FON0*@| zTjP;()yaIZaJB4rYGSu{83)+5QtXT<VW+q3D!sV=`^Qz*{5px=l^worBMOhn+^<_F z96HXZgmxRhC)L1f2;b<1VgLH<>pD1nUirE~(xI!pU3f6M&d=r?l5yaS?0NbIdP?S< zj*EO=e}_l#EAwRG>6B`P+LQ2EFKL}*5q(kqb-G*dcO|GsS4;_ugR3mT)|Vsg8d^`w z;b-c*jxD><Pspdwbn5@H_KfSG=h<Uw39e@XamJhBJa2PziC2>ps1^&Nu$N#8vUjO5 z$Q;sxw=r6V4}2rAs{Pr9;?i^SzOyyOoK<qp*(7m!rQCS7BYh<&>}W1iznRYDd;Oa_ zQ2is{WK^2XYhbko^@1=emt)b;;@dsl-r)v*=q=GhZwceVOBl!JnsD5M{UhUgeaDO& z`&~PpdE<A}qIYW<+Q+`xdaZ|fvgf%h%>2Z;+TAbe=J&a|&~4Uh#PQqoo36AK=_c3N z;Cm&1#8pl5mvb%fB-8kOV%4P#!5427e+W9`xEy-EalBGWXrj0H+ZSBF;oRrNr!TLb zuPM;&qw~Gew`w6^9~~^lF-dz-uQJqt+NVQ1+-A2YURHa5UmLdyqrPw2=zZ?TUkiXo zyG!MZ6jpGQAZ{U72?O_iAMeuNEKPs2g#NA_Wb@zw{jnSUGyRJ$;M{$omaB^Y3lQ^* zE?5MCI)1SOeU+bdsR0-I{D6X#Kja|J`LUsF|6_ZVyQgKIrbq?g_^hi-AcSczIbI0; zMdsB|2Sr@dUZWcDa98`M`v7C^yXO(J^ltpQZrr9)<omc(zEEEJ`37<JsI-5HhJV@b zmo{Saak=o9{v~x0zur;IZ}n9&+f2T0RhI&D<b+=nD{NMC<NH<FWMfsi_16}WM-;1> z#Q(*HUrRpt+n>Pp{@=TY2J&XNG=KN+gNWt7;h(H|H~m>ZGUTvxvz+ngcQjj$yUdMC zFQ<mQ3^i7Ma=BsSd902Q6L@xP;rRm2+Q`cfu=b+p3F65HWQAeiQ)HtnwR)d*j|4me zclC8fU;g#;6|;#K9(ThAJom#d97c&x0%|I(SZ@mgCmU;~%)62*woR5zo`{1Rf9gtq zdP~}Wy^7Q2rN4@g@2p0$>fhb*5I*4V^b}?OA^5zp2Y@}lUE{!v9m&9@iD9|ziH_Xz zcZ*6(d2ffk!70kHzFtI>8JXu+0mCzkI#O?P7D;MNA1vBO-RMInj;J{~&W%KEgNK+z ze4kQ$(Q)=fAta35OtxayPG>z){idI3lcxv)t&3!YfZ)or+!?1k<)+~X4SK?+VnLIg zNI_l2mB~&&L4o*OrX@G<ieMnK`Emd&GOb)^fuLcP_wWRvGvL`1P6d+^Dym9*r(x}I z8k<zD-?<QO?uRw}=q9Y6c_===BW+*pG+Z0lupG=YG%JS-$NS#!c9bJz@4zdU>H(Gs z>nCTaN&ec;s-={=;IVa$a-XeloWmxyuC+#W3i8cE5Yhm-Dz^0YnzK$*52aLnoR=D8 zNpUSJ8b*4kl|Z`I1B{UF@laE+;BOD5C;rEY!BxWK{lX|%Pfw0HyFs+vrFe_iS~A&+ zFscUNuZ2-(`og&!1_3zY^bDs|I_f+UPC>LS52xhnUozd)6$`%jQBD)?L9Bhwk#LGj zIL(cL2tG<>@`s-=ITlV-v3QQz=TyN9Ooh+62Z|p#EhDI9BTZU#W;2EDzKEOc99;5v z8SG&$s<WADbXG=CwVGdWWt4%3D@a1JXlU(KgK7rwEao^LN6@X2Wok0K)SYsjTrU=p z>#X)tB9iyL)QoZq{`697lcqb(BdM-$vf=?W7GQ=&QbNm%N^<x_h}|qj;A18WxDhl8 zN7dbGR`?_&vf#c&TY5M|9K*LoQewun!*M|4h5W-%J##gPWc;;5A$mNswcF_%MQH;* z;@MePp;wkRNcb|ZRHbXB$l713(i`geiK+xRrDDEe>e5o}j4*A$|9BL|SN@7S1#x8x z?^ReIJHATPuhDU?#Kk)v`ZV)eu`uP}qT-ltVUvb2;GeJ#ICCq}l>R5U1)|OFGVX-A zWD_=X+&QUp$VuG}8vr@AJ}qvCd1U3cPU&`-0;o`_b{1%7RAmaLzNf~3LQE06t@Q(! z@%@d#uh_uHj}JghzKF%SW|{L=Wojz^ob3Einck&83SPJHH)yqg<RLZuhA$1bn+&Gd z2H;alSft5Ssb0=6+{rgo^>B4(v(oNl5zG5qnYthr1mO?;eVDL!JYUHBFz$CYa>i!V z;NciO;%yq2-I&mZ6-HaXs#)$+Oi`Hprj}z`KsF_sP7{>TilJ&T?{mHT#Q2vQv@8{u zChL0EkaG*eox5Vl>*U@-o(e8r2hKX9V<<Xy-8r5g+8Z=OI0rrUx^vF57)liA^-c^0 zJ1^%QArO}MG^hSB7?4D_3bH6d)g$b~MmIpwO&(nx2rrp2r8@Owm=9K`@$|IweRZlH ze?Xf=K5T_>u~^T-5|=yvSZo34pB+mXoUV$cbWT5ur9Pad#8E9yyTwskPG`i??VO&B z16Pq&uR)zTy{iT#RM!)+<*kTKrWmP$1vSV=+-pTV_2u-ZczTc1EeSN7)4Db3>A28n zZ!pYn@*?5KKz7l5v?h(<Zry899Ztv8!gr5Im(`*s4MR)fBcVGaRD}l%*fU*OmgKc+ zQ`cCnQoI*MMnbzw=9^fXVlqPLtT%(<{tDT_ivI9kknf6qPp=Z=?hY;)y1O>jX4Jl| zO_}kRl_W**cSHaYD47GyZ;fwJiV3#fWPi5=isL7oa3(w3aKpsu2|qR`45}5NdVU4f z{Wi((%MRcLB@h7<4hi>-u+V#GK=Uv!K*31WJAm&`xMg>MnlN^k1C-KCFOWBnO(sy& zzr{EdDw&N9hL?~P18)mbi}Vm>te^*tIG}iXGett|;*tIcY)K)?<D;vy&W<3o4NLew zNU@B9H<4CEX-Gkj-^co}DUojBCSQ{7nvq0ljf*E^=iu5YFgr+4(&8*kqBacV<0N{b z1b#(^e=oj%<EFT}nhHJZ(s(ZKsEe;tkY1@vdyAzo5?GmFc=4Rh$$FFpTac7Y^~B$k zonFb5z@tVaQzK6ElIakq52R@5Yf|VjF4wJ34LQBJJ{{q-X#?GVa0B{;%gq~VO&ZaV z+H-kbL%OG0Xw|VL&I_87Q>kfjQ*@c8QK>YBN1sTg-JHJMNY8S#5pCh}(#9IAy^S?i zJ<_Nd*XO3uZkptDZ$dY5@#!WM<aB!zJ^Y6zw1dl=n^Ie^M#@jlsthm)wI60sg43}X z%_&BzEWW3jX`>RKPPL0=Jvta(2J;)!X&?j3Pp8wI=4I%q0eq?7of1BfS9y#4ZiK7V z5~fVe)a%-uNssdAjONsm)6vao7pJXS(4FG1$<9+PDDygHwYAr2XR(FB-rIt1W3g=A zk_OWh=joPI;~y5Yx20b6uPrH~xJy|k*tlinN%yy+J2-9BS~KJK)_O@PZRn^qMfusT z^J@XMn6}g&Ea=x(vtVso-FZ$H#XCQ@#UEe;(#m$){C?F=H&$*>PZc9kmgw8sQ$`C- zw;=o&_4vw3v|4z`;|&Se)!e9`rMZFGPlp&TnBj5GU+wUZ0e}nUPH+lsn#hH$4q6Ka zbkKa5-$C=CQb#Rc9Xrx~rhm-!)Pbfsw_i`gp+oOp4_c!3{Pk1>s=w8V1|X@~nUX8$ zt2f^Yl9#@Tb_RE*sx-?P-x&_%Y-ep}!sDv5vomE>a36uKCml~0YDsgQc3r6CxQ0t9 zXJ8lV=>3Zi!-nr`IZ?(rJGxLkfH~KN`jmXagw4i=?_kY!Zs<yB&5G{``QC|HrN2AU z5(k3*k<a$cPJ}N}bds}HUcrX0gnyc#FG2|(xw`&zj&!5s?#EOgKc8~cVYPT)#C|k; zlLGZAyW^F!G3qzn*<;T*r69X^U~FNnS<VgJsjGYICivFPS=F5~5?mbP;IFw_aa@B9 zvo<<Ebf<rbnUBkP@4n|$?TNhv`H4F+osm5$wHRM~{G`CG?TIt?cIRAAdJ{x`p%?X| zC!D`}QEFuAML5lGpiZLbpfm9XiWlp@cJgkZI<&%h=LV`tMNU?4igtdyfvU7oe@COA zQ~857ewX#T7=AZ}mxs$WKQ;<0bWP}WUt+^-1*T5@{{UfASqRSD-Z)U@InVWmcd~u= z;y#q?-M(8*66Um>hyT2=z7Jf7?Yo`N`cN{hTYMuWR@}hc_5I`_uUv5|rl8}Ev|NbG zlbjQM@sAbqoPXbpQ(2z#x=zAxp-w3EyoL7CGfs_ulpnL2$4B7_nXG43mY)Nkb58c7 z6k6#--3kRbyUXctD}4j<G`x*AiCG^OoVbl16!ELLzfJf1j^cl7<iNoT@ovuEf^m0% zp5pZD&SL|puJcZRS|Cn+RB+3kT${I@Ycs{!ea@x<^u1WROP7}vIY;h7dD|{$&p?_+ zc?Eq3(Mb|Ji<}li=w08RyA&-d*uU*^!tbVj;>s>(z}<AC_<NVL;ciM3OHMf_@1|xo zUQi2x|LJn-iW+DO>jfumC@kyb-Oh-i_~v5PZs*)kn4;;s3ljbf3rjB-JTi>%t<Eb> z?QFUo$-~+70=-fY7(q=vV#;pkmQmC~Ox^9w9fi#n)9o5XJzQ{cPNUJV2Qzm&^T)u> zj2Vq*#2Knk!&y0+?y0<nv4R%hZ;kyGtu;=}80y9-3>u?Q;9-vfgQ&6eykI83Hjbup z+U5Z`w@9ZwfXxx<idi&+)0Ekm8)?ya*n@S>Z{sOIuRAp!q;I|JwdOtVL_S0}f#Itj zqUz4*#|R%-alRdrV-L|!VDQ0*sZpCx*hLL}GZ3kSJlQ4PiwhOa!}x{9Q!M0AQ!E32 zo@Bk|w10#a7f)Dl^bzuqhU>o-2QgNd*wE(*_J5e@&vGa7Snl67;@HDe@)sztm03>S zV-WD=&UcT|R@&sOnLw#5uRSM198MKE6DLx2&?YbuuJ)y(GSvy|l$3;AKJ{b;{;?U` zQRHlxNIe1XuZgJA@B&V^9DL?c0(4{!+BDE>+P=)8TGkw;yLJPd3)P=N%G}d6@R1!J zGE#q@s>n)cat%i*)3^B`H`=k5*6rJ{hr|E?RjXIZmY7Ug_a(x1!SQ_vOmv>}b}n_y zDk{xYZ-^5(9+!d=#!y19xtBCjVX$+|Yp25`YDG((2}qa*O%}oh#PU~4MbixnD7v8P zWc;(2+KYK6kj@8ZMCJMOZo?PJhgHJOUi1_k=RQzGjdLEILbd&SiqsPM${r|V6Vd{! zR0L*ZvGe8>O72ut(hEBa9tg|yg8cDlzBK|J?E|`V1S)~QVPAARJ6}wvn$Go8sXi+k z_!{TToJ!T8^v=quFa}=~IbTercC6hf$2sZK=(?D#Un~B2Y%wmV|6Iqkj~sORPlGvx z`p=(6EpIuZ*n|x))F;53aA+hSmGLb%<H+|}2}q-HpN{u0ZJp4B3RlY}Ye``PReL&B zXS)Gh5}d3VRKdA(I$7eYB4^ZeoLY)W!nZ=0t~5zdEG7brajsefEDb2orFijeXB00! z!5KY+D$+;JqcdPdVM;!lL7hTdj>q}355+`AVU}zg*K#AQbxzxvAQa5;gqbi!5PgV5 zf)hClmJV_em{lwnPOn)wc$Z_V#?Qv>;5z5o*|<^LQ?P3`;TmbV)8q+S<DI9meXZd5 z6O=~Ym2T0An@dX>)N6Aom(%9+aQa3%XC5_BdtkJ)XCBq{p4R*icaBe`s!r5=s@<#< zu^<xH5bK;C5vDG&IP!p2cg@#UdF^~{l|P@48`Q;4)B^0uS_#Tqad$fCTwXx6qCQ-t z1cwFFTI$r30H*15wdy!~B(<ve0ox#4YYCroqr{o}DmAO9Y?OM#((Tv4!aG(qdGwtM zPS_%<({$2)p4|@9mV>YMT>+b>8$7N_!N-=k--2##aQZKzxHwm64cvCIG^=|a$R{NJ z$wkzn))J*k(6Z25h40UQ3&YOMEyQPzw-%gQL^XuhJ7m;-_u-#JIe$8|ui&>Ov{^V` zKSj-*v=!tnxN{jb@D#kV5?)uqPtQ=i=~Ui8{hW`Ur;P<UFHq(99Q^+q!*{H5%?Qhw zU>L?HVTMrwX{bESEg$z-#u(JUOokCxs{JDJy-Jl$PucNRF+K_HKZjYyB&4?q!$>RD zKPMX%JYjgL%9|q$1LKUH6)dAY(g~O}u~c~k%7Iel`%tcrGKLwE2*APcBE*@NF&XK8 zv^PQ;TGa0)i_B50Cd?jHs)M+y+yv#AQtd5JE={GZtNPnlmB+Z_LkKxoG$ULyB1e<G z2q^OkLm2h}hP6wTUw5e#YJbx$hse4Ued{4yu4F07QDsX}A_^(G8Zk(jqVaBhJ*2?N z$jm{eGBTaqhMxg|$=Ea1GEAhEQP1VO-Eu0*yz<9TX26{Pe+fDNY49@eFAEbePh$~6 z<w{qTE212aa2*2wF#kDJMTkZS$wW2enT^#E9`{;CH^ay=9>p@MprUj~o{Xiu##Qw^ zNdiJm1pWz4R15jtpbYbr;V@-(G_s5}koum;$G8H>EDa&!M?Qf0@wBCXSYjC<%Rv%C z9fZ0F%vTG6+maDd5O|^n2nWI~qbt(g2)z1+2(Ttf+F!FQwJ=_FV}uZVR=)=^u0PWI zZbyVKK8h<wAb&l=<Iwjzk@8>$S{G#=z$9gs<-uO`Mp_>$W6c=>Rs9!IULlwBBAnXq zQ>{8pkZ+363?UsM10fTEWv)4bqK4D%eX5?*7R7c5d#hNA$jl`sWO^E}z6AgF$oB<d zZ$`=}Ft|{;7xFxPEuhmADbtV3{1aM#mMWDn&(`IfVs3UoMQKiTL^(tcMv&L<ijQ$4 zw1}C-H2Z%GqyFy!WL33{zwuMAqGfafBApSsAn*@MN9mt#ZlSwVWg|7u*$Xx3gDRG4 zHV{1!6i<*c0hypTAcTIvZbkH?P)!Cs7aU@B<e$*ETamvFfn}>X;4{pxF_1NbW#kS7 zmZhww7Q`Pv9yhSS@efbJVE`BK6KcOp7mGPL5asV6h&;hmpXPR`{3Ob(z6D8^@dm;c zgjva!u>j#2gnWeU$uPhBkvWO*6M~Tf>xK}Akc7|(p*2D$gqsisAdEtogfJK3IfM-e zTM>33>_s?)a1!An!es<+eaonU5JX5rXoJuLVF1GLnux|BOhTB0uo&TagqIME{?58E z5nHhD13DXn|L?f@IBgfwbRIiFo#W!js5<<<(WAzWOC9&{=%J}YMvWXd^r3O4;cPof zU1GbKMwQadL&uICHP$pt=g0|)>tI}WO<S62cp@r^Y=gqH$Q&~6p)ez&A|{D;d;a@@ zkwa1k4H-9l{Gf3|@mA$ONr?k0_^JydA~ZE5AxzKXF*GQj2366B^jTzhi-#LTQ4wL3 z1t$BhBctNoLyMb;s8Sfd<pdti;*-?5Q#s41A4011%G1mS<*lkd{_xMJ&Ho#-wp0BS zRR$gFoub@|n}jjw{`;bUf%Dc;>Rh8&`2>UMb~c=$7-!EZs+l7q!o)Ch#QhaxseBSr zM6Afh|2hrNGR5$_?`JxC%t2%CVPZy;QMoiR@qa$a>#HF`M1#Or96oaVs3C*K4Ieeq z1Yr|A!zzxrKN}y<S5WY}D1bf&7->ZLd}Npm1k|h)HkJ$#b`AWR<x$UF(X6j2p+@;6 SOCqC|`fsEKyT7LE{Qn2<I9bU6 delta 22073 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 diff --git a/plugins/hello/dist/plugin.wasm.sources b/plugins/hello/dist/plugin.wasm.sources new file mode 100644 index 000000000..72f8f773b --- /dev/null +++ b/plugins/hello/dist/plugin.wasm.sources @@ -0,0 +1 @@ +837c72fc3ab90de50efcd4fa3628d3174fe7f68382a822c6c2a161a330b689cf diff --git a/plugins/hello/plugin.toml b/plugins/hello/plugin.toml index 54e273038..6145272b0 100644 --- a/plugins/hello/plugin.toml +++ b/plugins/hello/plugin.toml @@ -17,7 +17,7 @@ activation = ["on-command:*", "on-view:greetings"] required = ["editor.read", "editor.write"] # Static declarations make the commands visible before the plugin activates; -# the plugin may also register more at runtime with `register_command`. +# a command must be declared here to exist. [[contributes.commands]] id = "insert-date" title = "Hello: Insert Date" diff --git a/plugins/hello/src/lib.rs b/plugins/hello/src/lib.rs index da009057b..26f754106 100644 --- a/plugins/hello/src/lib.rs +++ b/plugins/hello/src/lib.rs @@ -19,7 +19,7 @@ const GREETINGS: &[(&str, &[&str])] = &[ ]; impl Plugin for Hello { - fn activate(&mut self, ctx: &Context) -> Result<(), String> { + fn activate(&mut self, ctx: &Context) -> Result<(), Error> { host::log(LogLevel::Info, &format!("hello {} activated", ctx.plugin_version)); Ok(()) } @@ -28,7 +28,7 @@ impl Plugin for Hello { host::log(LogLevel::Info, "hello deactivated"); } - fn run_command(&mut self, id: &str, ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, String> { + fn run_command(&mut self, id: &str, ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, Error> { let editor = ctx.editor().ok_or("no file is open in the editor")?; match id { "insert-date" => { @@ -47,7 +47,7 @@ impl Plugin for Hello { host::notify(NotificationLevel::Info, &format!("Word count: {name}"), &body); Ok(Some(body)) } - other => Err(format!("unknown command: {other}")), + other => Err(Error::NotFound(format!("command {other}"))), } } } @@ -174,7 +174,7 @@ mod tests { let host = MockHost::install().with_capabilities(&["editor.read"]); host.open_editor("/project/a.txt", "text", "", &[(0, 0, 0, 0)]); let err = Hello::default().run_command("insert-date", &host.invocation(), None).unwrap_err(); - assert!(err.contains("editor.write"), "{err}"); + assert_eq!(err, Error::Denied("editor.write".into())); assert_eq!(host.editor_text(), ""); } } diff --git a/plugins/official/ai-completion/dist/plugin.wasm b/plugins/official/ai-completion/dist/plugin.wasm index ee5484557795be388c4406c49825feec70c63114..ee11a42e850fc3b06e5979c68cdcb859b988659b 100644 GIT binary patch delta 34248 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 F{tw>v%xM4s delta 33142 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 JU%cr0{{W7q6P^G7 diff --git a/plugins/official/ai-completion/dist/plugin.wasm.sources b/plugins/official/ai-completion/dist/plugin.wasm.sources new file mode 100644 index 000000000..43ce4cdf4 --- /dev/null +++ b/plugins/official/ai-completion/dist/plugin.wasm.sources @@ -0,0 +1 @@ +bf264a97dc918f24ac00f0e9e0d0373b68391a96921b44345b466c2a087c5283 diff --git a/plugins/official/ai-completion/src/lib.rs b/plugins/official/ai-completion/src/lib.rs index a908831f9..c07d60f6c 100644 --- a/plugins/official/ai-completion/src/lib.rs +++ b/plugins/official/ai-completion/src/lib.rs @@ -118,7 +118,7 @@ impl Providers for AiCompletion { } impl Plugin for AiCompletion { - fn run_command(&mut self, id: &str, _ctx: &Invocation, arg: Option<&str>) -> Result<Option<String>, String> { + fn run_command(&mut self, id: &str, _ctx: &Invocation, arg: Option<&str>) -> Result<Option<String>, Error> { let msg = match id { "use-local" => { storage::set("endpoint", LOCAL)?; @@ -145,7 +145,7 @@ impl Plugin for AiCompletion { storage::set("enabled", if enabled { "true" } else { "false" })?; (if enabled { "AI Completion is on." } else { "AI Completion is off." }).into() } - _ => return Err(format!("unknown command {id}")), + _ => return Err(Error::NotFound(format!("command {id}"))), }; host::notify(NotificationLevel::Info, "AI Completion", &msg); Ok(Some(msg)) diff --git a/plugins/official/checkstyle/dist/plugin.wasm b/plugins/official/checkstyle/dist/plugin.wasm index 13e6c1d643afcb21ae484ae2295dc5103d7d74c4..b7508504180eb7431eca93351e0cd453684f8492 100644 GIT binary patch delta 32921 zcmb__31Ae(^7mBF?q-wSBm)T$k`Q*600|@^ASB#mxKR-G;f+d!D1<A9;PuoEm!cvd zqc$QccmOIW2pSc9qN3t^s3?c1sNjtkAb6vGzv`adB#8d6FFM<Ib#--Bb#-;m@Sism zSG}9Kc6+ioT(|zM<oV=;&5-kbYD;HK>h0@`ted^&e9t^*Q?o8Di4qe92_j0Mph;#T zVno6u*~Aj*GGqyl)2ex*Cq+<-Q`CI;*}|9DEUih?rs>{9%NtBgNED6!NTe75Wf??< zVJ0Ssgyzl3q#%HL5)u;<grLMAHAk-r30`la*W*bPi6R(uwl@DG<D!eaCK*k9w}^I8 zvTJKM8-b`{@2+z`Y|$+Gz|7}Wa*G@8bi>7N7~r}`-9#!Uj=2fq#Ijksc^W4K|15TM zZd{@JX~vm*X6R}g+`^@9*nB*f?)+U>-FJhoRa?77;j}{|s($yV-`P8K&COrxaNB1( z{K*afaLX4T()qRD=<um;ocA*N(tFO`S>425lIuo^j?S{oWbr=9<=sR#)A)d7c_~Le zRHdb+v5(}8fg)GzC)shZ=;$#%qCDratdQ(`rtq7_$7oq1io_=*!=pslscjh$pQ<>j zeny=g8&yLkqN(^?)eJj_S|-yM&eW_zv5Dk${}8>S#uF4ATYm9`i>fQiCrz3<)^N<k z7cj*(1unePyv6e_&8406xOj*5&|2{}trmBP$Hb%JKjKyKZ}F)(Y`!Jl6Q2vm{82n7 zz7aQ>H=EyzdFCzVK{4N4VE!OBi#g_ObCLOzI4ZsqN5u2uu$XJ!U@kOoHNU)291zFE z&*B&HtN2(P68{o2%-_WCqSl;g{vmEOXPIA%>&@@QS3;Vb&F9S*%ooiq=1b-W=2zyI z=C|enbFoKyzB3nkZuQ*esr9Vzto5w%Jmz`SleG1Ygn8=^XU`;>x4tH)OoW#m-CS!_ zT7S%{tu>55$P-FvGqV<Pd#zjG><U%;Z~IZzn(vGZ-%PJK?}azwS4JLa^@*<K%vR0a zrkjtb+MhY+w7D|y%?ySXv^~}Vr>t#zaohLKyX^)!WnBY#+wbCfcB>HCv%gnOZa*=c z{cW2k-2$|X5SY2dNp9CB{b-|(ZI2zaK5=&BUi|;7Kf1-ascT!ORlBqkcQvqG<F3}V zbB8vL9&q}#A9dl05Y$B0REl`)`1n14KYkv$5!l*{nnc@RZa~g`&f)gmqZ<hGB}&9v zgtk#4R&z)_ja3{bm52x2@?~!MQa8PrLtlxwjq_1&A%|Tn@w1RBtzA4}hy&ozkf%R) zxY;NbWjbq=4u*#ybob=tMs*`QSQ!qOWuXK+uQD7IWvHEyCqfYlB@Ih9LKI3IkxYS5 zFq{wyhWta4p?m|VgMRZw?@01U)IqF!IK?)?0wD7$!4rn1=R5^*v!*hVnkS5i8A9Nx z4Et0E;Z%zo7KEE{kQZ+1_=-kQnsZrxv*L7x2H8~%Jw23W2WzUs6f$kIGHeEcO}g!? z37Lh4Y4^LVI+W(zSyWsastj2n;dbn?_ZXF-rXdA9#69F%^A#Ina2a929q6$-NAo)n zIjuXijZ(g0^#Attb&tHfW=Y+r#+=!w4N6YLkY-aQqa5TP%le2}fg#Q%;Dm4<MTG4? zC7D79h9rAK3eM3_=KP1@d~TecEP~zu58eo;YoaSkMAB@iC7Z0-3NcSK57Wx1G0=r5 z7|gWg#9}HDJJA<&%yv*WoXW(;<HN+M+&zR?ddLU*s9~jrd=n#NC!CUOa8;f#6*GeZ zlZ~Vq5|xF9VJ8kjK7p!cAc{#9HdRIh@&agPhJX%}+hhAH?L@R8YYB@v;}qoC&J+Hy zVh7VEGztk|0Adg(8GVRQnx4S=t^jH~s3}6JK+n2e4?Iuo0R|$<EmARP#&!^oJ i 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ˊUrWUJpcR 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 P_1KE@>b8v)e|Y{M+SzUJ delta 32706 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 Z#`y`_m=smdo>Mic{DOvpsp7cj{{Tv2eY^kw diff --git a/plugins/official/checkstyle/dist/plugin.wasm.sources b/plugins/official/checkstyle/dist/plugin.wasm.sources new file mode 100644 index 000000000..6192fb92e --- /dev/null +++ b/plugins/official/checkstyle/dist/plugin.wasm.sources @@ -0,0 +1 @@ +6095c7e30658b93d79270e6355d42941a4cfe13eeb8bada07acc7ae462194853 diff --git a/plugins/official/checkstyle/src/lib.rs b/plugins/official/checkstyle/src/lib.rs index b4ef62b72..74f17cc48 100644 --- a/plugins/official/checkstyle/src/lib.rs +++ b/plugins/official/checkstyle/src/lib.rs @@ -196,7 +196,7 @@ impl Providers for Checkstyle { } impl Plugin for Checkstyle { - fn run_command(&mut self, id: &str, ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, String> { + fn run_command(&mut self, id: &str, ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, Error> { match id { "check" => { let editor = ctx.editor().ok_or("no file is open")?; @@ -207,7 +207,7 @@ impl Plugin for Checkstyle { storage::set("config", if id == "use-sun" { SUN } else { GOOGLE })?; Ok(None) } - _ => Err(format!("unknown command {id}")), + _ => Err(Error::NotFound(format!("command {id}"))), } } diff --git a/plugins/official/csv-editor/dist/plugin.wasm b/plugins/official/csv-editor/dist/plugin.wasm index 4667b33e6def739b122e9319e7d011ed43d7e1a7..539a1dce2bc8803bf0e4ce71e50f111fd4c8e620 100644 GIT binary patch delta 21294 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 delta 21116 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 diff --git a/plugins/official/csv-editor/dist/plugin.wasm.sources b/plugins/official/csv-editor/dist/plugin.wasm.sources new file mode 100644 index 000000000..d1b11a175 --- /dev/null +++ b/plugins/official/csv-editor/dist/plugin.wasm.sources @@ -0,0 +1 @@ +b96941af697ce7a6c1c8ef1539d34aa7c23d2d41dd1b2ccffdfda5648f90cf56 diff --git a/plugins/official/csv-editor/src/lib.rs b/plugins/official/csv-editor/src/lib.rs index 046635fd2..38cad0ee8 100644 --- a/plugins/official/csv-editor/src/lib.rs +++ b/plugins/official/csv-editor/src/lib.rs @@ -183,8 +183,8 @@ impl CsvEditor { } impl Plugin for CsvEditor { - fn run_command(&mut self, id: &str, _ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, String> { - Err(format!("unknown command {id}")) + fn run_command(&mut self, id: &str, _ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, Error> { + Err(Error::NotFound(format!("command {id}"))) } } diff --git a/plugins/official/git-toolbox/dist/plugin.wasm b/plugins/official/git-toolbox/dist/plugin.wasm index e14440bf732dda0f574954b2cf5c4cd0671b5f7f..9479f183c540106fab7dc769999554ec26fa1fb4 100644 GIT binary patch delta 20688 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 delta 19214 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 BxS{|6 diff --git a/plugins/official/git-toolbox/dist/plugin.wasm.sources b/plugins/official/git-toolbox/dist/plugin.wasm.sources new file mode 100644 index 000000000..fd5094012 --- /dev/null +++ b/plugins/official/git-toolbox/dist/plugin.wasm.sources @@ -0,0 +1 @@ +ad545605b33c6f4e55a06a376333de3162739ed920b5d7d277942e50a4420083 diff --git a/plugins/official/git-toolbox/src/lib.rs b/plugins/official/git-toolbox/src/lib.rs index c5f36c805..5201eae95 100644 --- a/plugins/official/git-toolbox/src/lib.rs +++ b/plugins/official/git-toolbox/src/lib.rs @@ -130,7 +130,7 @@ fn fetch(projects: &[Project]) -> Result<usize, Error> { } impl Plugin for GitToolbox { - fn run_command(&mut self, id: &str, ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, String> { + fn run_command(&mut self, id: &str, ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, Error> { let project = current(ctx).ok_or("no project is open")?; match id { "fetch" => { @@ -142,12 +142,12 @@ impl Plugin for GitToolbox { show(&project)?; Ok(None) } - _ => Err(format!("unknown command {id}")), + _ => Err(Error::NotFound(format!("command {id}"))), } } fn handle_events(&mut self, events: &[Event], ctx: &Invocation) { - if events.iter().any(|e| *e == Event::Scheduled) { + if events.contains(&Event::Scheduled) { let projects = host::projects().unwrap_or_default(); if let Err(e) = fetch(&projects) && e != Error::Cancelled @@ -225,7 +225,7 @@ mod tests { GitToolbox.handle_events(&[Event::Scheduled], &host.invocation()); assert_eq!(host.notifications()[0].body, "no network"); let err = GitToolbox.run_command("fetch", &host.invocation(), None).unwrap_err(); - assert_eq!(err, "no network"); + assert_eq!(err, Error::Failed("no network".into())); host.fail_fetch(Some(Error::Cancelled)); GitToolbox.handle_events(&[Event::Scheduled], &host.invocation()); assert_eq!(host.notifications().len(), 1, "a cancelled fetch is not an error to report"); diff --git a/plugins/official/key-promoter/dist/plugin.wasm b/plugins/official/key-promoter/dist/plugin.wasm index 1ca08c5f42dc808fc2cccad375e3bd542a4633d8..daefc884d5dfdb76569c03d572061b0576f6b763 100644 GIT binary patch delta 17137 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 O3>?&=JaHm@>iB<Xn;TRB delta 17280 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 diff --git a/plugins/official/key-promoter/dist/plugin.wasm.sources b/plugins/official/key-promoter/dist/plugin.wasm.sources new file mode 100644 index 000000000..9965326dd --- /dev/null +++ b/plugins/official/key-promoter/dist/plugin.wasm.sources @@ -0,0 +1 @@ +345c39f818e4950914bf3fcc795fac9e30ff2d87ad3e66f24c7bc00800f92980 diff --git a/plugins/official/key-promoter/src/lib.rs b/plugins/official/key-promoter/src/lib.rs index 97ce5d863..4636e3dba 100644 --- a/plugins/official/key-promoter/src/lib.rs +++ b/plugins/official/key-promoter/src/lib.rs @@ -4,7 +4,7 @@ //! instance being reclaimed while you work. use openidea_plugin::host::{self, storage}; -use openidea_plugin::{ActionSource, Event, Invocation, NotificationLevel, Plugin, register_plugin}; +use openidea_plugin::{ActionSource, Error, Event, Invocation, NotificationLevel, Plugin, register_plugin}; /// Tips for one action are shown on these counts, then every `REPEAT_EVERY`. const FIRST_TIPS: [u32; 3] = [1, 3, 10]; @@ -22,7 +22,7 @@ fn get_u32(key: &str) -> u32 { } impl Plugin for KeyPromoter { - fn run_command(&mut self, id: &str, _ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, String> { + fn run_command(&mut self, id: &str, _ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, Error> { match id { "statistics" => { let mut rows: Vec<(u32, String)> = storage::keys(COUNT)? @@ -56,7 +56,7 @@ impl Plugin for KeyPromoter { storage::set(&format!("{SUPPRESSED}{action}"), "1")?; Ok(Some(format!("No more tips for {action}."))) } - _ => Err(format!("unknown command {id}")), + _ => Err(Error::NotFound(format!("command {id}"))), } } @@ -78,7 +78,7 @@ impl Plugin for KeyPromoter { return; // storage denied or full: nothing to count with } let _ = storage::set(&format!("{TITLE}{}", a.action), &a.title); - if FIRST_TIPS.contains(&n) || n % REPEAT_EVERY == 0 { + if FIRST_TIPS.contains(&n) || n.is_multiple_of(REPEAT_EVERY) { tip = Some((a.clone(), shortcut.clone(), n)); } } @@ -156,6 +156,9 @@ mod tests { let host = MockHost::install().with_capabilities(&["events.input"]); KeyPromoter.handle_events(&[action("x", ActionSource::Menu, Some("F2"))], &host.invocation()); assert!(host.notifications().is_empty()); - assert!(KeyPromoter.run_command("statistics", &host.invocation(), None).unwrap_err().contains("storage")); + assert_eq!( + KeyPromoter.run_command("statistics", &host.invocation(), None).unwrap_err(), + Error::Denied("storage".into()) + ); } } diff --git a/plugins/official/kubernetes/dist/plugin.wasm b/plugins/official/kubernetes/dist/plugin.wasm index acae4b47d939ae67687878c000c5f6228e4bdadb..8a55990b3ef97ff58bc922036f954d0de9938710 100644 GIT binary patch delta 35229 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 delta 36204 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 HrlkBY_=#mj diff --git a/plugins/official/kubernetes/dist/plugin.wasm.sources b/plugins/official/kubernetes/dist/plugin.wasm.sources new file mode 100644 index 000000000..1c2a824c5 --- /dev/null +++ b/plugins/official/kubernetes/dist/plugin.wasm.sources @@ -0,0 +1 @@ +3499cfcae4a0388587a11b38158f421b0cf5046eeda38ae23b0f7700728c92a8 diff --git a/plugins/official/kubernetes/src/lib.rs b/plugins/official/kubernetes/src/lib.rs index 9e41bf1b5..486d617e1 100644 --- a/plugins/official/kubernetes/src/lib.rs +++ b/plugins/official/kubernetes/src/lib.rs @@ -187,12 +187,12 @@ impl Kubernetes { } impl Plugin for Kubernetes { - fn run_command(&mut self, id: &str, _ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, String> { + fn run_command(&mut self, id: &str, _ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, Error> { match id { "refresh" => self.refresh(), "logs" => self.open_logs()?, "shell" => self.open_shell()?, - _ => return Err(format!("unknown command {id}")), + _ => return Err(Error::NotFound(format!("command {id}"))), } Ok(None) } diff --git a/plugins/official/mybatis/dist/plugin.wasm b/plugins/official/mybatis/dist/plugin.wasm index dc36a16478574ffd6b1c4df9aa281b5d98884eab..5ed3800aaa646c6e75d7e85dd6872d915651644c 100644 GIT binary patch delta 27571 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 C?-|qp delta 25994 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 EAK^1lTL1t6 diff --git a/plugins/official/mybatis/dist/plugin.wasm.sources b/plugins/official/mybatis/dist/plugin.wasm.sources new file mode 100644 index 000000000..0241ff751 --- /dev/null +++ b/plugins/official/mybatis/dist/plugin.wasm.sources @@ -0,0 +1 @@ +f7df58bb0ab2b92370b148de2614174ec1ff9b01c0078b5c20bd331f82298616 diff --git a/plugins/official/mybatis/src/lib.rs b/plugins/official/mybatis/src/lib.rs index eca8ba1ba..d10b135a5 100644 --- a/plugins/official/mybatis/src/lib.rs +++ b/plugins/official/mybatis/src/lib.rs @@ -5,7 +5,7 @@ use openidea_plugin::providers::{CompletionItem, CompletionKind, Document, LineMarker, Providers}; use openidea_plugin::text::{offset, position}; -use openidea_plugin::{Invocation, Location, Plugin, Position, Project, Range, host, register_plugin}; +use openidea_plugin::{Error, Invocation, Location, Plugin, Position, Project, Range, host, register_plugin}; use std::collections::BTreeMap; use std::time::{Duration, Instant}; @@ -292,8 +292,8 @@ impl Providers for Navigator { } impl Plugin for Navigator { - fn run_command(&mut self, id: &str, _ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, String> { - Err(format!("unknown command {id}")) + fn run_command(&mut self, id: &str, _ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, Error> { + Err(Error::NotFound(format!("command {id}"))) } } diff --git a/plugins/official/solidity/dist/plugin.wasm b/plugins/official/solidity/dist/plugin.wasm index e6d0c908d7707ecc300369c16f2fd67e43be7ed4..3cb17fd7ce387e314537ba999335fee7cb97f89d 100644 GIT binary patch delta 17813 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 delta 16844 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 HYH9sHGAMIv diff --git a/plugins/official/solidity/dist/plugin.wasm.sources b/plugins/official/solidity/dist/plugin.wasm.sources new file mode 100644 index 000000000..d7a74f4c1 --- /dev/null +++ b/plugins/official/solidity/dist/plugin.wasm.sources @@ -0,0 +1 @@ +3893bd8ad06d3e19c0112081c6643b3bf3c67f5a184098432a6fd338426cd682 diff --git a/plugins/official/solidity/src/lib.rs b/plugins/official/solidity/src/lib.rs index 5b7abcd47..def94beeb 100644 --- a/plugins/official/solidity/src/lib.rs +++ b/plugins/official/solidity/src/lib.rs @@ -4,15 +4,15 @@ //! `languages/solidity.toml`. use openidea_plugin::syntax::{Fold, FoldKind, Highlight, Symbol, SymbolKind, SyntaxProvider, Token, bracket_folds}; -use openidea_plugin::{Invocation, Plugin, register_plugin}; +use openidea_plugin::{Error, Invocation, Plugin, register_plugin}; use std::ops::Range; #[derive(Default)] pub struct Solidity; impl Plugin for Solidity { - fn run_command(&mut self, id: &str, _ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, String> { - Err(format!("unknown command {id}")) + fn run_command(&mut self, id: &str, _ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, Error> { + Err(Error::NotFound(format!("command {id}"))) } } @@ -442,7 +442,7 @@ fn outline(text: &str) -> Vec<Symbol> { } stmt_start = i + 1; } - (Kind::Ident, "contract" | "interface" | "library") if declares && word(i + 1) != "" => { + (Kind::Ident, "contract" | "interface" | "library") if declares && !word(i + 1).is_empty() => { if code.get(i + 1).is_some_and(|n| n.kind == Kind::Ident) { let kind = match w { "interface" => SymbolKind::Interface, diff --git a/plugins/official/string-tools/dist/plugin.wasm b/plugins/official/string-tools/dist/plugin.wasm index 1280d055ba32b6a313ab5383974f2237f5a4cf3c..1c33e3e439b141848c4fc7052470cc046a55640f 100644 GIT binary patch delta 22826 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 delta 22769 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 diff --git a/plugins/official/string-tools/dist/plugin.wasm.sources b/plugins/official/string-tools/dist/plugin.wasm.sources new file mode 100644 index 000000000..2dbf488f3 --- /dev/null +++ b/plugins/official/string-tools/dist/plugin.wasm.sources @@ -0,0 +1 @@ +a88c39d5782369329efa55b12330daa906e5c8d91252bd57c7343648840bac79 diff --git a/plugins/official/string-tools/src/lib.rs b/plugins/official/string-tools/src/lib.rs index c69ec6fc5..805b78ebf 100644 --- a/plugins/official/string-tools/src/lib.rs +++ b/plugins/official/string-tools/src/lib.rs @@ -9,10 +9,10 @@ use openidea_plugin::{Error, Invocation, Plugin, Range, TextEdit, TextEditor, re pub struct StringTools; impl Plugin for StringTools { - fn run_command(&mut self, id: &str, ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, String> { - let transform = transform(id).ok_or_else(|| format!("unknown command {id}"))?; + fn run_command(&mut self, id: &str, ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, Error> { + let transform = transform(id).ok_or_else(|| Error::NotFound(format!("command {id}")))?; let editor = ctx.editor().ok_or("String Tools works on the focused editor")?; - apply(&editor, transform, lines_command(id)).map_err(|e| e.to_string())?; + apply(&editor, transform, lines_command(id))?; Ok(None) } } @@ -270,7 +270,7 @@ fn base64_decode(s: &str) -> Option<Vec<u8>> { } let mut n = 0u32; for (i, b) in chunk.iter().enumerate() { - let v = B64.iter().position(|x| x == b).or_else(|| match b { + let v = B64.iter().position(|x| x == b).or(match b { b'-' => Some(62), b'_' => Some(63), _ => None, @@ -454,7 +454,7 @@ mod tests { let host = MockHost::install().with_capabilities(&["editor.read"]); host.open_editor("/p/a.txt", "text", "abc", &[(0, 0, 0, 3)]); let err = StringTools.run_command("upper-case", &host.invocation(), None).unwrap_err(); - assert!(err.contains("editor.write"), "{err}"); + assert_eq!(err, Error::Denied("editor.write".into())); assert_eq!(host.editor_text(), "abc"); } } diff --git a/sdk/rust/openidea-plugin/Cargo.lock b/sdk/rust/openidea-plugin/Cargo.lock index 5b8089e7c..9629fb20c 100644 --- a/sdk/rust/openidea-plugin/Cargo.lock +++ b/sdk/rust/openidea-plugin/Cargo.lock @@ -99,6 +99,7 @@ name = "openidea-plugin" version = "0.1.0" dependencies = [ "wit-bindgen", + "wit-parser", ] [[package]] diff --git a/sdk/rust/openidea-plugin/Cargo.toml b/sdk/rust/openidea-plugin/Cargo.toml index 7728a8600..3f36703f7 100644 --- a/sdk/rust/openidea-plugin/Cargo.toml +++ b/sdk/rust/openidea-plugin/Cargo.toml @@ -14,3 +14,7 @@ path = "src/lib.rs" [target.'cfg(target_arch = "wasm32")'.dependencies] wit-bindgen = "0.63" + +[dev-dependencies] +# Reads the WIT in tests/wit_coverage.rs (the version wit-bindgen already uses). +wit-parser = "0.262" diff --git a/sdk/rust/openidea-plugin/src/host.rs b/sdk/rust/openidea-plugin/src/host.rs index 12cf7a822..187c5f8bb 100644 --- a/sdk/rust/openidea-plugin/src/host.rs +++ b/sdk/rust/openidea-plugin/src/host.rs @@ -73,18 +73,6 @@ mod imp { wit::ui::remove_status_item(id) } - pub fn register_command(id: &str, title: &str, category: Option<&str>) -> Result<(), Error> { - wit::contributions::register_command(id, title, category).map_err(error) - } - - pub fn register_language(definition: &str) -> Result<(), Error> { - wit::contributions::register_language(definition).map_err(error) - } - - pub fn register_theme(definition: &str) -> Result<(), Error> { - wit::contributions::register_theme(definition).map_err(error) - } - pub fn storage_get(key: &str) -> Result<Option<String>, Error> { wit::storage::get(key).map_err(error) } @@ -533,22 +521,6 @@ pub fn remove_status_item(id: &str) { imp::remove_status_item(id) } -/// Registers or renames a command at runtime (action `plugin.<plugin-id>.<id>`). -/// Prefer declaring commands in `plugin.toml`: they show before the plugin runs. -pub fn register_command(id: &str, title: &str, category: Option<&str>) -> Result<(), Error> { - imp::register_command(id, title, category) -} - -/// Registers a language (`languages/<id>.toml` format) at runtime. -pub fn register_language(definition: &str) -> Result<(), Error> { - imp::register_language(definition) -} - -/// Registers a theme (theme JSON) at runtime. -pub fn register_theme(definition: &str) -> Result<(), Error> { - imp::register_theme(definition) -} - /// Delivers `kind` events to [`crate::Plugin::handle_events`] for the rest of /// the session (also after the instance is reclaimed: an event activates the /// plugin again). Needs `events` (`events.input` for `ActionPerformed`). diff --git a/sdk/rust/openidea-plugin/src/lib.rs b/sdk/rust/openidea-plugin/src/lib.rs index 5695cf5e7..f59d728c0 100644 --- a/sdk/rust/openidea-plugin/src/lib.rs +++ b/sdk/rust/openidea-plugin/src/lib.rs @@ -1,20 +1,20 @@ //! SDK for OpenIDEA plugins written in Rust (plugin API 1.0). //! //! ```ignore -//! use openidea_plugin::{Context, Invocation, Plugin, register_plugin}; +//! use openidea_plugin::{Error, Invocation, Plugin, register_plugin}; //! //! #[derive(Default)] //! struct Shout; //! //! impl Plugin for Shout { -//! fn run_command(&mut self, id: &str, ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, String> { +//! fn run_command(&mut self, id: &str, ctx: &Invocation, _arg: Option<&str>) -> Result<Option<String>, Error> { //! match id { //! "shout" => { //! let editor = ctx.editor().ok_or("no editor is focused")?; //! editor.transform_selections(|s| s.to_uppercase())?; //! Ok(None) //! } -//! _ => Err(format!("unknown command {id}")), +//! _ => Err(Error::NotFound(format!("command {id}"))), //! } //! } //! } @@ -120,9 +120,9 @@ pub const API_VERSION: &str = "1.0"; /// A plugin. One instance lives as long as the plugin's Wasm instance (until /// it is disabled, crashes or is reclaimed when idle). pub trait Plugin: 'static { - /// Called once when the instance is created. Register runtime commands - /// and subscriptions here; restore state from [`host::storage`]. - fn activate(&mut self, _ctx: &Context) -> Result<(), String> { + /// Called once when the instance is created: subscribe to events and + /// restore state from [`host::storage`]. An error fails the activation. + fn activate(&mut self, _ctx: &Context) -> Result<(), Error> { Ok(()) } @@ -130,7 +130,9 @@ pub trait Plugin: 'static { fn deactivate(&mut self) {} /// Runs one of the plugin's commands; `Ok(Some(text))` is shown to the user. - fn run_command(&mut self, id: &str, ctx: &Invocation, arg: Option<&str>) -> Result<Option<String>, String>; + /// A plain message (`Err("…".into())`, or `?` on a `String` error) is + /// [`Error::Failed`]; return [`Error::Cancelled`] when the user gave up. + fn run_command(&mut self, id: &str, ctx: &Invocation, arg: Option<&str>) -> Result<Option<String>, Error>; /// A batch of subscribed events (see [`host::subscribe`]), oldest first. fn handle_events(&mut self, _events: &[Event], _ctx: &Invocation) {} @@ -308,7 +310,7 @@ macro_rules! __plugin_exports { impl $crate::bindings::exports::openidea::plugin::lifecycle::Guest for $component { fn activate( ctx: $crate::bindings::openidea::plugin::types::Context, - ) -> ::core::result::Result<(), ::std::string::String> { + ) -> ::core::result::Result<(), $crate::bindings::openidea::plugin::types::Error> { $crate::__rt::activate::<$plugin>(ctx) } @@ -322,7 +324,10 @@ macro_rules! __plugin_exports { id: ::std::string::String, ctx: &$crate::bindings::openidea::plugin::invocation::Invocation, arg: ::core::option::Option<::std::string::String>, - ) -> ::core::result::Result<::core::option::Option<::std::string::String>, ::std::string::String> { + ) -> ::core::result::Result< + ::core::option::Option<::std::string::String>, + $crate::bindings::openidea::plugin::types::Error, + > { $crate::__rt::run_command::<$plugin>(id, ctx, arg) } } @@ -361,14 +366,14 @@ pub mod __rt { }) } - pub fn activate<T: Plugin + Default>(ctx: b::types::Context) -> Result<(), String> { + pub fn activate<T: Plugin + Default>(ctx: b::types::Context) -> Result<(), b::types::Error> { let ctx = Context { plugin_id: ctx.plugin_id, plugin_version: ctx.plugin_version, api_version: ctx.api_version, capabilities: ctx.capabilities, }; - with::<T, _>(|p| p.activate(&ctx)) + with::<T, _>(|p| p.activate(&ctx)).map_err(error_out) } pub fn deactivate<T: Plugin + Default>() { @@ -380,9 +385,9 @@ pub mod __rt { id: String, ctx: &b::invocation::Invocation, arg: Option<String>, - ) -> Result<Option<String>, String> { + ) -> Result<Option<String>, b::types::Error> { let ctx = Invocation::__new(ctx); - with::<T, _>(|p| p.run_command(&id, &ctx, arg.as_deref())) + with::<T, _>(|p| p.run_command(&id, &ctx, arg.as_deref())).map_err(error_out) } // ---- providers ---- @@ -413,24 +418,7 @@ pub mod __rt { .map(|i| w::CompletionItem { label: i.label, detail: i.detail, - kind: i.kind.map(|k| match k { - p::CompletionKind::Text => w::CompletionKind::Text, - p::CompletionKind::Method => w::CompletionKind::Method, - p::CompletionKind::Function => w::CompletionKind::Function, - p::CompletionKind::Constructor => w::CompletionKind::Constructor, - p::CompletionKind::Field => w::CompletionKind::Field, - p::CompletionKind::Variable => w::CompletionKind::Variable, - p::CompletionKind::Class => w::CompletionKind::Class, - p::CompletionKind::Interface => w::CompletionKind::Interface, - p::CompletionKind::Module => w::CompletionKind::Module, - p::CompletionKind::Property => w::CompletionKind::Property, - p::CompletionKind::Keyword => w::CompletionKind::Keyword, - p::CompletionKind::Snippet => w::CompletionKind::Snippet, - p::CompletionKind::Value => w::CompletionKind::Value, - p::CompletionKind::Constant => w::CompletionKind::Constant, - p::CompletionKind::File => w::CompletionKind::File, - p::CompletionKind::Reference => w::CompletionKind::Reference, - }), + kind: i.kind.map(|k| k.as_str().to_owned()), insert_text: i.insert_text, replace: i.replace.map(crate::host::__range_to), sort_text: i.sort_text, diff --git a/sdk/rust/openidea-plugin/src/mock.rs b/sdk/rust/openidea-plugin/src/mock.rs index e82a96cfa..33ce121dc 100644 --- a/sdk/rust/openidea-plugin/src/mock.rs +++ b/sdk/rust/openidea-plugin/src/mock.rs @@ -19,13 +19,6 @@ pub struct Notification { pub body: String, } -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Command { - pub id: String, - pub title: String, - pub category: Option<String>, -} - /// A task the plugin began. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct MockTask { @@ -64,9 +57,6 @@ struct State { files: BTreeMap<String, String>, editor: Option<Editor>, notifications: Vec<Notification>, - commands: Vec<Command>, - languages: Vec<String>, - themes: Vec<String>, logs: Vec<(LogLevel, String)>, status: BTreeMap<String, StatusItem>, storage: BTreeMap<String, String>, @@ -174,18 +164,6 @@ impl MockHost { self.state.borrow().notifications.clone() } - pub fn commands(&self) -> Vec<Command> { - self.state.borrow().commands.clone() - } - - pub fn languages(&self) -> Vec<String> { - self.state.borrow().languages.clone() - } - - pub fn themes(&self) -> Vec<String> { - self.state.borrow().themes.clone() - } - pub fn logs(&self) -> Vec<(LogLevel, String)> { self.state.borrow().logs.clone() } @@ -367,37 +345,6 @@ pub mod current { with(|s| s.status.remove(id)); } - pub fn register_command(id: &str, title: &str, category: Option<&str>) -> Result<(), Error> { - with(|s| { - let cmd = Command { id: id.into(), title: title.into(), category: category.map(str::to_owned) }; - match s.commands.iter_mut().find(|c| c.id == id) { - Some(existing) => *existing = cmd, - None => s.commands.push(cmd), - } - Ok(()) - }) - } - - pub fn register_language(definition: &str) -> Result<(), Error> { - with(|s| { - if !definition.contains("id") { - return Err(Error::Invalid("invalid language definition".into())); - } - s.languages.push(definition.into()); - Ok(()) - }) - } - - pub fn register_theme(definition: &str) -> Result<(), Error> { - with(|s| { - if !definition.trim_start().starts_with('{') { - return Err(Error::Invalid("invalid theme JSON".into())); - } - s.themes.push(definition.into()); - Ok(()) - }) - } - pub fn storage_get(key: &str) -> Result<Option<String>, Error> { with(|s| { require(s, "storage")?; @@ -714,10 +661,7 @@ mod tests { assert_eq!(project.read_file("src/a.txt").unwrap(), "hello"); assert!(matches!(project.read_file("../etc/passwd"), Err(Error::Invalid(_)))); host::notify(NotificationLevel::Info, "t", "b"); - host::register_command("c", "C", None).unwrap(); - host::register_command("c", "C2", Some("Cat")).unwrap(); assert_eq!(mock.notifications()[0].title, "t"); - assert_eq!(mock.commands(), [Command { id: "c".into(), title: "C2".into(), category: Some("Cat".into()) }]); host::storage::set("k", "v").unwrap(); assert_eq!(host::storage::get("k").unwrap().as_deref(), Some("v")); assert_eq!(host::storage::keys("").unwrap(), ["k"]); diff --git a/sdk/rust/openidea-plugin/src/providers.rs b/sdk/rust/openidea-plugin/src/providers.rs index 52a0d7446..c10f75cf9 100644 --- a/sdk/rust/openidea-plugin/src/providers.rs +++ b/sdk/rust/openidea-plugin/src/providers.rs @@ -23,7 +23,9 @@ pub struct Document { pub text: String, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// Icon and sort group of a completion item. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[non_exhaustive] pub enum CompletionKind { Text, Method, @@ -35,12 +37,54 @@ pub enum CompletionKind { Interface, Module, Property, + Unit, + Value, + Enum, Keyword, Snippet, - Value, - Constant, + Color, File, Reference, + Folder, + EnumMember, + Constant, + Struct, + Event, + Operator, + TypeParameter, +} + +impl CompletionKind { + /// The name the plugin API uses (`completion-item.kind`). + pub fn as_str(self) -> &'static str { + match self { + Self::Text => "text", + Self::Method => "method", + Self::Function => "function", + Self::Constructor => "constructor", + Self::Field => "field", + Self::Variable => "variable", + Self::Class => "class", + Self::Interface => "interface", + Self::Module => "module", + Self::Property => "property", + Self::Unit => "unit", + Self::Value => "value", + Self::Enum => "enum", + Self::Keyword => "keyword", + Self::Snippet => "snippet", + Self::Color => "color", + Self::File => "file", + Self::Reference => "reference", + Self::Folder => "folder", + Self::EnumMember => "enum-member", + Self::Constant => "constant", + Self::Struct => "struct", + Self::Event => "event", + Self::Operator => "operator", + Self::TypeParameter => "type-parameter", + } + } } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/sdk/rust/openidea-plugin/src/syntax.rs b/sdk/rust/openidea-plugin/src/syntax.rs index b7b8516d5..74e860a93 100644 --- a/sdk/rust/openidea-plugin/src/syntax.rs +++ b/sdk/rust/openidea-plugin/src/syntax.rs @@ -212,7 +212,7 @@ mod tests { fn bracket_folds_skip_strings() { let text = "a {\n \"{\"\n b {\n }\n}\n"; let s = text.find('"').unwrap(); - let folds = bracket_folds(text, b'{', b'}', &[s..s + 3]); + let folds = bracket_folds(text, b'{', b'}', std::slice::from_ref(&(s..s + 3))); assert_eq!( folds, [ diff --git a/sdk/rust/openidea-plugin/src/text.rs b/sdk/rust/openidea-plugin/src/text.rs index 2ba0f7afc..7f34e968e 100644 --- a/sdk/rust/openidea-plugin/src/text.rs +++ b/sdk/rust/openidea-plugin/src/text.rs @@ -30,22 +30,6 @@ pub fn position(text: &str, at: usize) -> Position { Position { line, character } } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn round_trips_utf16_columns() { - let text = "ab\n😀x\nlast"; - for at in [0, 1, 2, 3, 7, 8, 9, text.len()] { - assert_eq!(offset(text, position(text, at)), Some(at), "{at}"); - } - assert_eq!(position(text, 7), Position::new(1, 2)); - assert_eq!(offset(text, Position::new(0, 99)), Some(2), "clamped to the line"); - assert_eq!(offset(text, Position::new(5, 0)), None); - } -} - /// The text of a JSON string literal's contents (`\"`, `\\`, `\n`, `\uXXXX`). pub fn unescape_json(s: &str) -> String { let mut out = String::with_capacity(s.len()); @@ -80,3 +64,19 @@ pub fn unescape_json(s: &str) -> String { } out } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn round_trips_utf16_columns() { + let text = "ab\n😀x\nlast"; + for at in [0, 1, 2, 3, 7, 8, 9, text.len()] { + assert_eq!(offset(text, position(text, at)), Some(at), "{at}"); + } + assert_eq!(position(text, 7), Position::new(1, 2)); + assert_eq!(offset(text, Position::new(0, 99)), Some(2), "clamped to the line"); + assert_eq!(offset(text, Position::new(5, 0)), None); + } +} diff --git a/sdk/rust/openidea-plugin/src/types.rs b/sdk/rust/openidea-plugin/src/types.rs index 34237c474..5a74f67d3 100644 --- a/sdk/rust/openidea-plugin/src/types.rs +++ b/sdk/rust/openidea-plugin/src/types.rs @@ -35,6 +35,19 @@ impl From<Error> for String { } } +/// A plain message is [`Error::Failed`]. +impl From<String> for Error { + fn from(message: String) -> Self { + Self::Failed(message) + } +} + +impl From<&str> for Error { + fn from(message: &str) -> Self { + Self::Failed(message.to_owned()) + } +} + /// Log level of [`crate::host::log`]. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum LogLevel { diff --git a/sdk/rust/openidea-plugin/tests/api_frozen.rs b/sdk/rust/openidea-plugin/tests/api_frozen.rs new file mode 100644 index 000000000..e6723b428 --- /dev/null +++ b/sdk/rust/openidea-plugin/tests/api_frozen.rs @@ -0,0 +1,180 @@ +//! Released plugin APIs never change (plugins/README.md, "Evolving the plugin API"): +//! wasmtime links a component only if every function type matches exactly, so +//! a field added to a record or a case added to a variant breaks every +//! component built for the release. +//! +//! `tests/api/<version>/` is the WIT of each release, copied when it was +//! frozen. Every interface, type and function in it must still exist in +//! `wit/` with the same definition; additions are fine. When a version is +//! released, copy `wit/` to `tests/api/<that version>/`. Until the first +//! release there is no snapshot and the API may still change freely. + +use std::collections::BTreeMap; +use std::path::Path; +use wit_parser::{FunctionKind, Handle, Resolve, Type, TypeDefKind, TypeId, TypeOwner}; + +/// Everything a release promises, by key (`log/log`, `types/error`), rendered canonically. +fn surface(dir: &Path) -> BTreeMap<String, String> { + let mut resolve = Resolve::default(); + let (pkg, _) = resolve.push_dir(dir).unwrap_or_else(|e| panic!("{}: {e:?}", dir.display())); + let mut out = BTreeMap::new(); + for (name, &id) in &resolve.packages[pkg].interfaces { + let iface = &resolve.interfaces[id]; + for (ty_name, &ty) in &iface.types { + out.insert(format!("{name}/type {ty_name}"), definition(&resolve, ty)); + } + for f in iface.functions.values() { + let params: Vec<String> = f.params.iter().map(|p| format!("{}: {}", p.name, ty(&resolve, &p.ty))).collect(); + let result = f.result.as_ref().map(|r| format!(" -> {}", ty(&resolve, r))).unwrap_or_default(); + let kind = match f.kind { + FunctionKind::Freestanding => "", + FunctionKind::AsyncFreestanding => "async ", + FunctionKind::Method(_) => "method ", + FunctionKind::Static(_) => "static ", + FunctionKind::Constructor(_) => "constructor ", + _ => "other ", + }; + out.insert(format!("{name}/func {}", f.name), format!("{kind}({}){result}", params.join(", "))); + } + } + for (name, &id) in &resolve.packages[pkg].worlds { + let world = &resolve.worlds[id]; + let keys = |m: &wit_parser::IndexMap<wit_parser::WorldKey, wit_parser::WorldItem>| { + m.keys().map(|k| resolve.name_world_key(k)).collect::<Vec<_>>().join(", ") + }; + out.insert( + format!("world {name}"), + format!("imports [{}] exports [{}]", keys(&world.imports), keys(&world.exports)), + ); + } + out +} + +/// A named type's definition. +fn definition(r: &Resolve, id: TypeId) -> String { + let def = &r.types[id]; + match &def.kind { + TypeDefKind::Record(x) => { + let f: Vec<_> = x.fields.iter().map(|f| format!("{}: {}", f.name, ty(r, &f.ty))).collect(); + format!("record {{ {} }}", f.join(", ")) + } + TypeDefKind::Variant(x) => { + let c: Vec<_> = x + .cases + .iter() + .map(|c| c.ty.as_ref().map_or(c.name.clone(), |t| format!("{}({})", c.name, ty(r, t)))) + .collect(); + format!("variant {{ {} }}", c.join(", ")) + } + TypeDefKind::Enum(x) => { + format!("enum {{ {} }}", x.cases.iter().map(|c| c.name.as_str()).collect::<Vec<_>>().join(", ")) + } + TypeDefKind::Flags(x) => { + format!("flags {{ {} }}", x.flags.iter().map(|f| f.name.as_str()).collect::<Vec<_>>().join(", ")) + } + TypeDefKind::Resource => "resource".to_owned(), + // A `use`d type: the same type as the one it names. + TypeDefKind::Type(t) => format!("= {}", ty(r, t)), + _ => anonymous(r, id), + } +} + +/// A type in a position (parameter, field…): named types by name, others structurally. +fn ty(r: &Resolve, t: &Type) -> String { + match t { + Type::Id(id) => { + let def = &r.types[*id]; + match (&def.name, def.owner) { + (Some(name), TypeOwner::Interface(i)) => { + format!("{}.{name}", r.interfaces[i].name.as_deref().unwrap_or("?")) + } + (Some(name), _) => name.clone(), + (None, _) => anonymous(r, *id), + } + } + other => format!("{other:?}").to_lowercase(), + } +} + +fn anonymous(r: &Resolve, id: TypeId) -> String { + let opt = |t: &Option<Type>| t.as_ref().map_or("_".to_owned(), |t| ty(r, t)); + match &r.types[id].kind { + TypeDefKind::Option(t) => format!("option<{}>", ty(r, t)), + TypeDefKind::List(t) => format!("list<{}>", ty(r, t)), + TypeDefKind::Result(x) => format!("result<{}, {}>", opt(&x.ok), opt(&x.err)), + TypeDefKind::Tuple(x) => format!("tuple<{}>", x.types.iter().map(|t| ty(r, t)).collect::<Vec<_>>().join(", ")), + TypeDefKind::Handle(Handle::Own(t)) => format!("own<{}>", ty(r, &Type::Id(*t))), + TypeDefKind::Handle(Handle::Borrow(t)) => format!("borrow<{}>", ty(r, &Type::Id(*t))), + TypeDefKind::Type(t) => ty(r, t), + other => format!("{other:?}"), + } +} + +/// The frozen releases (`tests/api/<version>/`), none before the first release. +fn releases(root: &Path) -> Vec<std::path::PathBuf> { + match std::fs::read_dir(root.join("tests/api")) { + Ok(dir) => dir.map(|e| e.unwrap().path()).filter(|p| p.is_dir()).collect(), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Vec::new(), + Err(e) => panic!("tests/api: {e}"), + } +} + +#[test] +fn the_surface_covers_the_whole_api() { + // Keeps the check below meaningful while there is no release to compare. + let current = surface(&Path::new(env!("CARGO_MANIFEST_DIR")).join("wit")); + assert!(current.len() > 50, "only {} items: did wit/ load?", current.len()); + assert!(current.keys().any(|k| k.starts_with("world "))); + assert!(current.keys().any(|k| k.contains("/func "))); + assert!(current.keys().any(|k| k.contains("/type "))); +} + +#[test] +fn released_apis_are_unchanged() { + let root = Path::new(env!("CARGO_MANIFEST_DIR")); + let current = surface(&root.join("wit")); + for dir in releases(root) { + let release = surface(&dir); + let version = dir.file_name().unwrap().to_string_lossy().into_owned(); + let mut broken = Vec::new(); + for (key, was) in &release { + match current.get(key) { + None => broken.push(format!("{key}: removed (was {was})")), + // A world may gain imports and exports; everything else stays as it was. + Some(now) if key.starts_with("world ") => { + let had = |s: &str, part: &str| -> Vec<String> { + let start = s.find(part).unwrap() + part.len(); + let end = s[start..].find(']').unwrap() + start; + s[start..end].split(", ").filter(|x| !x.is_empty()).map(str::to_owned).collect() + }; + for part in ["imports [", "exports ["] { + let now = had(now, part); + for item in had(was, part) { + if !now.contains(&item) { + broken.push(format!("{key}: no longer has {item}")); + } + } + } + } + Some(now) if now != was => broken.push(format!("{key}:\n was {was}\n now {now}")), + Some(_) => {} + } + } + assert!(broken.is_empty(), "plugin API {version} changed incompatibly:\n {}", broken.join("\n ")); + assert!(release.len() > 50, "{version}: only {} items: did the snapshot load?", release.len()); + } +} + +#[test] +fn the_current_api_is_a_release_or_newer_than_all() { + // `wit/` is either a frozen release or the next one; never older. + let root = Path::new(env!("CARGO_MANIFEST_DIR")); + let src = std::fs::read_to_string(root.join("wit/plugin.wit")).unwrap(); + let current = src.lines().find_map(|l| l.trim().strip_prefix("package openidea:plugin@")).unwrap(); + let current: Vec<u64> = current.trim_end_matches(';').split('.').map(|p| p.parse().unwrap()).collect(); + for dir in releases(root) { + let name = dir.file_name().unwrap().to_string_lossy().into_owned(); + let release: Vec<u64> = name.split('.').map(|p| p.parse().unwrap()).collect(); + assert!(release <= current, "tests/api/{name} is newer than wit/ ({current:?})"); + } +} diff --git a/sdk/rust/openidea-plugin/tests/wit_coverage.rs b/sdk/rust/openidea-plugin/tests/wit_coverage.rs new file mode 100644 index 000000000..e3ce3db4b --- /dev/null +++ b/sdk/rust/openidea-plugin/tests/wit_coverage.rs @@ -0,0 +1,53 @@ +//! 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 ")); +} diff --git a/sdk/rust/openidea-plugin/wit/exports.wit b/sdk/rust/openidea-plugin/wit/exports.wit index f907ff472..065ef9a5f 100644 --- a/sdk/rust/openidea-plugin/wit/exports.wit +++ b/sdk/rust/openidea-plugin/wit/exports.wit @@ -1,20 +1,25 @@ package openidea:plugin@1.0.0; interface lifecycle { - use types.{context}; + use types.{context, error}; - /// Called once after the instance is created. Register runtime - /// contributions and subscriptions here; restore state from `storage`. - activate: func(ctx: context) -> result<_, string>; + /// Called once after the instance is created: subscribe to events and + /// restore state from `storage`. Contributions (commands, languages, + /// themes, …) are declared in plugin.toml, not registered here. + /// An error fails the activation (shown on the plugin's page). + activate: func(ctx: context) -> result<_, error>; /// Called before the instance is dropped (disable, uninstall, idle reclaim). deactivate: func(); } interface commands { + use types.{error}; use invocation.{invocation}; /// Runs one of the plugin's commands. Returns an optional message. - run: func(id: string, ctx: borrow<invocation>, arg: option<string>) -> result<option<string>, string>; + /// `cancelled` (the user gave up) and `denied` (a capability is missing) + /// are not counted as the plugin failing. + run: func(id: string, ctx: borrow<invocation>, arg: option<string>) -> result<option<string>, error>; } interface event-handler { diff --git a/sdk/rust/openidea-plugin/wit/host.wit b/sdk/rust/openidea-plugin/wit/host.wit index 750bfd342..b6325cb71 100644 --- a/sdk/rust/openidea-plugin/wit/host.wit +++ b/sdk/rust/openidea-plugin/wit/host.wit @@ -7,19 +7,6 @@ interface log { log: func(level: log-level, message: string); } -/// Contributions made at run time (declare static ones in plugin.toml). -interface contributions { - use types.{error}; - - /// Registers (or updates) a command `plugin.<plugin-id>.<id>`. - /// `id` must match `[a-z0-9][a-z0-9._-]*`. - register-command: func(id: string, title: string, category: option<string>) -> result<_, error>; - /// A language from a `languages/<id>.toml`-style definition. - register-language: func(definition: string) -> result<_, error>; - /// A theme from JSON: {"id","name","kind":"dark"|"light","tokens":{"--var":"value"}}. - register-theme: func(definition: string) -> result<_, error>; -} - interface ui { use types.{notification-level}; diff --git a/sdk/rust/openidea-plugin/wit/plugin.wit b/sdk/rust/openidea-plugin/wit/plugin.wit index 84b20a900..938ee0842 100644 --- a/sdk/rust/openidea-plugin/wit/plugin.wit +++ b/sdk/rust/openidea-plugin/wit/plugin.wit @@ -1,6 +1,6 @@ -/// OpenIDEA plugin API, version 1.0 (docs/plugin_plan.md 4.3). Frozen: 1.x -/// only adds (new interfaces, new optional exports); a 2.0 keeps 1.0 running -/// through an adapter in the host. +/// OpenIDEA plugin API, version 1.0. Once released, 1.x only adds (new +/// interfaces, new optional exports; plugins/README.md, "Evolving the plugin +/// API"); a 2.0 keeps 1.0 running through an adapter in the host. /// /// A plugin is a WebAssembly component targeting the `plugin` world (or /// `plugin-syntax`). It runs sandboxed: no filesystem, network, environment @@ -11,7 +11,6 @@ package openidea:plugin@1.0.0; world plugin { import log; - import contributions; import ui; import storage; import workspace; diff --git a/sdk/rust/openidea-plugin/wit/providers.wit b/sdk/rust/openidea-plugin/wit/providers.wit index af3f86eb4..cfca65609 100644 --- a/sdk/rust/openidea-plugin/wit/providers.wit +++ b/sdk/rust/openidea-plugin/wit/providers.wit @@ -15,29 +15,15 @@ interface providers { text: string, } - enum completion-kind { - text, - method, - function, - %constructor, - field, - variable, - class, - %interface, - module, - property, - keyword, - snippet, - value, - constant, - file, - reference, - } - record completion-item { label: string, detail: option<string>, - kind: option<completion-kind>, + /// Icon and sort group: text, method, function, constructor, field, + /// variable, class, interface, module, property, unit, value, enum, + /// keyword, snippet, color, file, reference, folder, enum-member, + /// constant, struct, event, operator or type-parameter. Unknown kinds + /// show no icon. + kind: option<string>, insert-text: string, /// Replaced on accept; `none`: the word at the caret. replace: option<range>, diff --git a/sdk/rust/openidea-plugin/wit/views.wit b/sdk/rust/openidea-plugin/wit/views.wit index 1dfbd1357..de1ae7801 100644 --- a/sdk/rust/openidea-plugin/wit/views.wit +++ b/sdk/rust/openidea-plugin/wit/views.wit @@ -1,6 +1,6 @@ package openidea:plugin@1.0.0; -/// Declarative views (docs/plugin_plan.md 4.2, L2): tool windows declared +/// Declarative views: tool windows declared /// under `[[contributes.views]]` and custom editors under /// `[[contributes.customEditors]]`. The plugin describes what to show; the /// IDE renders it with its own components (theme, keyboard, accessibility) diff --git a/src-tauri/src/remote_dev.rs b/src-tauri/src/remote_dev.rs index d9d47b8f6..7be979e35 100644 --- a/src-tauri/src/remote_dev.rs +++ b/src-tauri/src/remote_dev.rs @@ -162,7 +162,7 @@ impl Handler for ClientHandler { } Err(e) => tracing::warn!("remote event: {e}"), }, - // Plugin code of this window's project runs on the server (docs/plugin_plan.md 3). + // Plugin code of this window's project runs on the server. ide_host::plugins::remote_host::EVENT_NOTIFICATION => { match serde_json::from_value::<ide_host::plugins::remote_host::PluginEventMessage>(params) { Ok(m) if ide_host::plugins::remote_host::forwarded(&m.event) => { @@ -445,7 +445,7 @@ pub async fn remote_connect( /// Sends the desktop's plugin languages, with the programs granted to them, to /// the window's server: `Workspace` contributions take effect where the project -/// is (docs/plugin_plan.md 3). Servers without the command are left alone. +/// is. Servers without the command are left alone. pub async fn sync_plugin_languages(plugins: &ide_host::plugins::PluginHost, c: &Connection, window: &str) { use ide_host::plugins::remote::{SYNC_COMMAND, SyncReport}; if !c.commands.iter().any(|cmd| cmd == SYNC_COMMAND) { @@ -470,7 +470,7 @@ pub async fn sync_plugin_languages(plugins: &ide_host::plugins::PluginHost, c: & } /// Sends the desktop's live code plugins to the window's server, which runs -/// them next to the project (docs/plugin_plan.md 3): digests and grants first, +/// them next to the project: digests and grants first, /// then the files of the packages the server lacks, one per request. pub async fn sync_plugin_packages(plugins: &Arc<ide_host::plugins::PluginHost>, c: &Connection, window: &str) { use ide_host::plugins::remote_host::{PackageSyncReport, SYNC_PACKAGES}; diff --git a/src/features/plugins/consent.tsx b/src/features/plugins/consent.tsx index f978e05a0..017920b13 100644 --- a/src/features/plugins/consent.tsx +++ b/src/features/plugins/consent.tsx @@ -1,5 +1,5 @@ /** - * The install / permission dialog (docs/plugin_plan.md 4.2, 4.6): every + * The install / permission dialog: every * capability with its risk; optional ones can be refused; high-risk ones are * called out as running code on this machine. */ diff --git a/src/features/plugins/projection.ts b/src/features/plugins/projection.ts index b447a2f49..d2a286015 100644 --- a/src/features/plugins/projection.ts +++ b/src/features/plugins/projection.ts @@ -1,6 +1,6 @@ /** * Projection of plugin UI contributions onto the workbench's own `register*` - * functions (docs/plugin_plan.md 4.1). + * functions. * * The backend validated every declaration completely (Rust extension points); * a projection maps one onto an existing registration and returns its diff --git a/src/features/plugins/uiPoints.tsx b/src/features/plugins/uiPoints.tsx index a65172b32..e5e124537 100644 --- a/src/features/plugins/uiPoints.tsx +++ b/src/features/plugins/uiPoints.tsx @@ -1,5 +1,5 @@ /** - * Client side of the P7 UI points (docs/plugin_plan.md P7): declarations the + * Client side of the plugin UI points: declarations the * backend validated, projected onto the workbench's own registries. * * - `menus`: editor / project view / editor tab context menus, and Tools in the main menu; diff --git a/src/features/plugins/views/PluginView.tsx b/src/features/plugins/views/PluginView.tsx index 33406140e..2fac0b40a 100644 --- a/src/features/plugins/views/PluginView.tsx +++ b/src/features/plugins/views/PluginView.tsx @@ -1,5 +1,5 @@ /** - * Renders a plugin's declarative view (docs/plugin_plan.md 4.2, L2): the + * Renders a plugin's declarative view: the * plugin returns data (`View`), this draws it with the workbench's own * components and sends the user's actions back (`ViewEvent`). Used by plugin * tool windows and custom editors. A refresh comes from the plugin diff --git a/wit/exports.wit b/wit/exports.wit index f907ff472..065ef9a5f 100644 --- a/wit/exports.wit +++ b/wit/exports.wit @@ -1,20 +1,25 @@ package openidea:plugin@1.0.0; interface lifecycle { - use types.{context}; + use types.{context, error}; - /// Called once after the instance is created. Register runtime - /// contributions and subscriptions here; restore state from `storage`. - activate: func(ctx: context) -> result<_, string>; + /// Called once after the instance is created: subscribe to events and + /// restore state from `storage`. Contributions (commands, languages, + /// themes, …) are declared in plugin.toml, not registered here. + /// An error fails the activation (shown on the plugin's page). + activate: func(ctx: context) -> result<_, error>; /// Called before the instance is dropped (disable, uninstall, idle reclaim). deactivate: func(); } interface commands { + use types.{error}; use invocation.{invocation}; /// Runs one of the plugin's commands. Returns an optional message. - run: func(id: string, ctx: borrow<invocation>, arg: option<string>) -> result<option<string>, string>; + /// `cancelled` (the user gave up) and `denied` (a capability is missing) + /// are not counted as the plugin failing. + run: func(id: string, ctx: borrow<invocation>, arg: option<string>) -> result<option<string>, error>; } interface event-handler { diff --git a/wit/host.wit b/wit/host.wit index 750bfd342..b6325cb71 100644 --- a/wit/host.wit +++ b/wit/host.wit @@ -7,19 +7,6 @@ interface log { log: func(level: log-level, message: string); } -/// Contributions made at run time (declare static ones in plugin.toml). -interface contributions { - use types.{error}; - - /// Registers (or updates) a command `plugin.<plugin-id>.<id>`. - /// `id` must match `[a-z0-9][a-z0-9._-]*`. - register-command: func(id: string, title: string, category: option<string>) -> result<_, error>; - /// A language from a `languages/<id>.toml`-style definition. - register-language: func(definition: string) -> result<_, error>; - /// A theme from JSON: {"id","name","kind":"dark"|"light","tokens":{"--var":"value"}}. - register-theme: func(definition: string) -> result<_, error>; -} - interface ui { use types.{notification-level}; diff --git a/wit/plugin.wit b/wit/plugin.wit index 84b20a900..938ee0842 100644 --- a/wit/plugin.wit +++ b/wit/plugin.wit @@ -1,6 +1,6 @@ -/// OpenIDEA plugin API, version 1.0 (docs/plugin_plan.md 4.3). Frozen: 1.x -/// only adds (new interfaces, new optional exports); a 2.0 keeps 1.0 running -/// through an adapter in the host. +/// OpenIDEA plugin API, version 1.0. Once released, 1.x only adds (new +/// interfaces, new optional exports; plugins/README.md, "Evolving the plugin +/// API"); a 2.0 keeps 1.0 running through an adapter in the host. /// /// A plugin is a WebAssembly component targeting the `plugin` world (or /// `plugin-syntax`). It runs sandboxed: no filesystem, network, environment @@ -11,7 +11,6 @@ package openidea:plugin@1.0.0; world plugin { import log; - import contributions; import ui; import storage; import workspace; diff --git a/wit/providers.wit b/wit/providers.wit index af3f86eb4..cfca65609 100644 --- a/wit/providers.wit +++ b/wit/providers.wit @@ -15,29 +15,15 @@ interface providers { text: string, } - enum completion-kind { - text, - method, - function, - %constructor, - field, - variable, - class, - %interface, - module, - property, - keyword, - snippet, - value, - constant, - file, - reference, - } - record completion-item { label: string, detail: option<string>, - kind: option<completion-kind>, + /// Icon and sort group: text, method, function, constructor, field, + /// variable, class, interface, module, property, unit, value, enum, + /// keyword, snippet, color, file, reference, folder, enum-member, + /// constant, struct, event, operator or type-parameter. Unknown kinds + /// show no icon. + kind: option<string>, insert-text: string, /// Replaced on accept; `none`: the word at the caret. replace: option<range>, diff --git a/wit/views.wit b/wit/views.wit index 1dfbd1357..de1ae7801 100644 --- a/wit/views.wit +++ b/wit/views.wit @@ -1,6 +1,6 @@ package openidea:plugin@1.0.0; -/// Declarative views (docs/plugin_plan.md 4.2, L2): tool windows declared +/// Declarative views: tool windows declared /// under `[[contributes.views]]` and custom editors under /// `[[contributes.customEditors]]`. The plugin describes what to show; the /// IDE renders it with its own components (theme, keyboard, accessibility)