diff --git a/crates/extension/src/extension_manifest.rs b/crates/extension/src/extension_manifest.rs index c52a85c3acb45ea2551562bee7fcb5b5564b7dac..63d3da51c185766ef3e820ad6f8862a49733e385 100644 --- a/crates/extension/src/extension_manifest.rs +++ b/crates/extension/src/extension_manifest.rs @@ -5,6 +5,7 @@ use language::LanguageServerName; use serde::{Deserialize, Serialize}; use std::{ ffi::OsStr, + fmt, path::{Path, PathBuf}, sync::Arc, }; @@ -31,9 +32,16 @@ pub struct OldExtensionManifest { pub grammars: BTreeMap, PathBuf>, } +/// The schema version of the [`ExtensionManifest`]. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Serialize, Deserialize)] pub struct SchemaVersion(pub i32); +impl fmt::Display for SchemaVersion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + impl SchemaVersion { pub const ZERO: Self = Self(0); diff --git a/crates/extension/src/extension_store.rs b/crates/extension/src/extension_store.rs index 64f09e6f1060a95487af8d2841eb3a9e2078624f..2658f7f03b08df291116061a61b3591d278e1597 100644 --- a/crates/extension/src/extension_store.rs +++ b/crates/extension/src/extension_store.rs @@ -7,6 +7,7 @@ mod wasm_host; #[cfg(test)] mod extension_store_test; +use crate::extension_manifest::SchemaVersion; use crate::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host::wit}; use anyhow::{anyhow, bail, Context as _, Result}; use async_compression::futures::bufread::GzipDecoder; @@ -50,7 +51,7 @@ use util::{ paths::EXTENSIONS_DIR, ResultExt, }; -use wasm_host::{WasmExtension, WasmHost}; +use wasm_host::{wit::is_supported_wasm_api_version, WasmExtension, WasmHost}; pub use extension_manifest::{ ExtensionLibraryKind, ExtensionManifest, GrammarManifestEntry, OldExtensionManifest, @@ -60,7 +61,29 @@ pub use extension_settings::ExtensionSettings; const RELOAD_DEBOUNCE_DURATION: Duration = Duration::from_millis(200); const FS_WATCH_LATENCY: Duration = Duration::from_millis(100); -const CURRENT_SCHEMA_VERSION: i64 = 1; +/// The current extension [`SchemaVersion`] supported by Zed. +const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(1); + +/// Returns whether the given extension version is compatible with this version of Zed. +pub fn is_version_compatible(extension_version: &ExtensionMetadata) -> bool { + let schema_version = extension_version.manifest.schema_version.unwrap_or(0); + if CURRENT_SCHEMA_VERSION.0 < schema_version { + return false; + } + + if let Some(wasm_api_version) = extension_version + .manifest + .wasm_api_version + .as_ref() + .and_then(|wasm_api_version| SemanticVersion::from_str(wasm_api_version).ok()) + { + if !is_supported_wasm_api_version(wasm_api_version) { + return false; + } + } + + true +} pub struct ExtensionStore { builder: Arc, diff --git a/crates/extension/src/wasm_host/wit.rs b/crates/extension/src/wasm_host/wit.rs index 2c752a7a8f55d43cd929c735002cab901bfd1682..321aa40ad0792b245616aba5f3804037b179636a 100644 --- a/crates/extension/src/wasm_host/wit.rs +++ b/crates/extension/src/wasm_host/wit.rs @@ -28,6 +28,11 @@ fn wasi_view(state: &mut WasmState) -> &mut WasmState { state } +/// Returns whether the given Wasm API version is supported by the Wasm host. +pub fn is_supported_wasm_api_version(version: SemanticVersion) -> bool { + v0_0_1::VERSION <= version && version <= v0_0_4::VERSION +} + pub enum Extension { V004(v0_0_4::Extension), V001(v0_0_1::Extension), diff --git a/crates/extension/src/wasm_host/wit/v0_0_1.rs b/crates/extension/src/wasm_host/wit/v0_0_1.rs index a48569b4e24b097be3613d4b32f1e060340c93ac..212231a1d9c5aa2d70198c84d52905b00c258b24 100644 --- a/crates/extension/src/wasm_host/wit/v0_0_1.rs +++ b/crates/extension/src/wasm_host/wit/v0_0_1.rs @@ -4,8 +4,15 @@ use anyhow::Result; use async_trait::async_trait; use language::{LanguageServerBinaryStatus, LspAdapterDelegate}; use std::sync::{Arc, OnceLock}; +use util::SemanticVersion; use wasmtime::component::{Linker, Resource}; +pub const VERSION: SemanticVersion = SemanticVersion { + major: 0, + minor: 0, + patch: 1, +}; + wasmtime::component::bindgen!({ async: true, path: "../extension_api/wit/0.0.1", diff --git a/crates/extensions_ui/src/extension_version_selector.rs b/crates/extensions_ui/src/extension_version_selector.rs index 8eeac35cc247be1fdf65b365d4b33700b6d85cfd..a893b7bbb9c2e2424631c6271cead59ba33847b3 100644 --- a/crates/extensions_ui/src/extension_version_selector.rs +++ b/crates/extensions_ui/src/extension_version_selector.rs @@ -165,6 +165,10 @@ impl PickerDelegate for ExtensionVersionSelectorDelegate { let candidate_id = self.matches[self.selected_index].candidate_id; let extension_version = &self.extension_versions[candidate_id]; + if !extension::is_version_compatible(extension_version) { + return; + } + let extension_store = ExtensionStore::global(cx); extension_store.update(cx, |store, cx| { let extension_id = extension_version.id.clone(); @@ -196,21 +200,38 @@ impl PickerDelegate for ExtensionVersionSelectorDelegate { let version_match = &self.matches[ix]; let extension_version = &self.extension_versions[version_match.candidate_id]; + let is_version_compatible = extension::is_version_compatible(extension_version); + let disabled = !is_version_compatible; + Some( ListItem::new(ix) .inset(true) .spacing(ListItemSpacing::Sparse) .selected(selected) - .child(HighlightedLabel::new( - version_match.string.clone(), - version_match.positions.clone(), - )) - .end_slot(Label::new( - extension_version - .published_at - .format("%Y-%m-%d") - .to_string(), - )), + .disabled(disabled) + .child( + HighlightedLabel::new( + version_match.string.clone(), + version_match.positions.clone(), + ) + .when(disabled, |label| label.color(Color::Muted)), + ) + .end_slot( + h_flex() + .gap_2() + .when(!is_version_compatible, |this| { + this.child(Label::new("Incompatible").color(Color::Muted)) + }) + .child( + Label::new( + extension_version + .published_at + .format("%Y-%m-%d") + .to_string(), + ) + .when(disabled, |label| label.color(Color::Muted)), + ), + ), ) } } diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index 98db928234b7cce7e2748095c4883ed96eb7073e..1649230f5f28fbacd298cf6c771cb94a567bce34 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -578,10 +578,14 @@ impl ExtensionsPage { status: &ExtensionStatus, cx: &mut ViewContext, ) -> (Button, Option