mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-26 06:01:41 +03:00
Disable incompatible extension versions in extension view (#9938)
This PR makes it so extension versions that are incompatible with what the current Zed instance supports are disabled in the UI, to prevent attempting to install them. Here's what it looks like in the extension version picker: <img width="589" alt="Screenshot 2024-03-28 at 4 21 15 PM" src="https://github.com/zed-industries/zed/assets/1486634/8ef11c72-c8f0-4de8-a73b-5c82e96f6bfe"> Release Notes: - N/A
This commit is contained in:
parent
95fd426eff
commit
0d7f5f49e6
@ -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<Arc<str>, 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);
|
||||
|
||||
|
@ -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<ExtensionBuilder>,
|
||||
|
@ -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),
|
||||
|
@ -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",
|
||||
|
@ -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)),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -578,10 +578,14 @@ impl ExtensionsPage {
|
||||
status: &ExtensionStatus,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> (Button, Option<Button>) {
|
||||
let is_compatible = extension::is_version_compatible(&extension);
|
||||
let disabled = !is_compatible;
|
||||
|
||||
match status.clone() {
|
||||
ExtensionStatus::NotInstalled => (
|
||||
Button::new(SharedString::from(extension.id.clone()), "Install").on_click(
|
||||
cx.listener({
|
||||
Button::new(SharedString::from(extension.id.clone()), "Install")
|
||||
.disabled(disabled)
|
||||
.on_click(cx.listener({
|
||||
let extension_id = extension.id.clone();
|
||||
let version = extension.manifest.version.clone();
|
||||
move |this, _, cx| {
|
||||
@ -591,8 +595,7 @@ impl ExtensionsPage {
|
||||
store.install_extension(extension_id.clone(), version.clone(), cx)
|
||||
});
|
||||
}
|
||||
}),
|
||||
),
|
||||
})),
|
||||
None,
|
||||
),
|
||||
ExtensionStatus::Installing => (
|
||||
@ -622,8 +625,9 @@ impl ExtensionsPage {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
Button::new(SharedString::from(extension.id.clone()), "Upgrade").on_click(
|
||||
cx.listener({
|
||||
Button::new(SharedString::from(extension.id.clone()), "Upgrade")
|
||||
.disabled(disabled)
|
||||
.on_click(cx.listener({
|
||||
let extension_id = extension.id.clone();
|
||||
let version = extension.manifest.version.clone();
|
||||
move |this, _, cx| {
|
||||
@ -640,8 +644,7 @@ impl ExtensionsPage {
|
||||
.detach_and_log_err(cx)
|
||||
});
|
||||
}
|
||||
}),
|
||||
),
|
||||
})),
|
||||
)
|
||||
},
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user