mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-27 12:22:49 +03:00
Add new extension
crate (#20089)
This PR adds a new `extension` crate, containing some contents extracted from the `extension_host`. Right now it contains just the `ExtensionManifest` and `ExtensionBuilder`, although we may move more of the extension interface into here. The introduction of the `extension` crate allows us to depend on it in the `extension_cli`, thereby eliminating the need for the `no-webrtc` feature on a number of crates. Release Notes: - N/A
This commit is contained in:
parent
ea44c510a3
commit
770886880f
29
Cargo.lock
generated
29
Cargo.lock
generated
@ -4089,6 +4089,29 @@ dependencies = [
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "extension"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compression",
|
||||
"async-tar",
|
||||
"collections",
|
||||
"fs",
|
||||
"futures 0.3.30",
|
||||
"http_client",
|
||||
"language",
|
||||
"log",
|
||||
"lsp",
|
||||
"semantic_version",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml 0.8.19",
|
||||
"wasm-encoder 0.215.0",
|
||||
"wasmparser 0.215.0",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "extension_cli"
|
||||
version = "0.1.0"
|
||||
@ -4096,7 +4119,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"env_logger 0.11.5",
|
||||
"extension_host",
|
||||
"extension",
|
||||
"fs",
|
||||
"language",
|
||||
"log",
|
||||
@ -4124,6 +4147,7 @@ dependencies = [
|
||||
"collections",
|
||||
"ctor",
|
||||
"env_logger 0.11.5",
|
||||
"extension",
|
||||
"fs",
|
||||
"futures 0.3.30",
|
||||
"gpui",
|
||||
@ -4151,11 +4175,8 @@ dependencies = [
|
||||
"ui",
|
||||
"url",
|
||||
"util",
|
||||
"wasm-encoder 0.215.0",
|
||||
"wasmparser 0.215.0",
|
||||
"wasmtime",
|
||||
"wasmtime-wasi",
|
||||
"wit-component",
|
||||
"workspace",
|
||||
]
|
||||
|
||||
|
@ -27,6 +27,7 @@ members = [
|
||||
"crates/docs_preprocessor",
|
||||
"crates/editor",
|
||||
"crates/evals",
|
||||
"crates/extension",
|
||||
"crates/extension_api",
|
||||
"crates/extension_cli",
|
||||
"crates/extension_host",
|
||||
@ -201,6 +202,7 @@ copilot = { path = "crates/copilot" }
|
||||
db = { path = "crates/db" }
|
||||
diagnostics = { path = "crates/diagnostics" }
|
||||
editor = { path = "crates/editor" }
|
||||
extension = { path = "crates/extension" }
|
||||
extension_host = { path = "crates/extension_host" }
|
||||
extensions_ui = { path = "crates/extensions_ui" }
|
||||
feature_flags = { path = "crates/feature_flags" }
|
||||
|
@ -13,7 +13,6 @@ path = "src/call.rs"
|
||||
doctest = false
|
||||
|
||||
[features]
|
||||
no-webrtc = ["live_kit_client/no-webrtc"]
|
||||
test-support = [
|
||||
"client/test-support",
|
||||
"collections/test-support",
|
||||
|
31
crates/extension/Cargo.toml
Normal file
31
crates/extension/Cargo.toml
Normal file
@ -0,0 +1,31 @@
|
||||
[package]
|
||||
name = "extension"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/extension.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
async-compression.workspace = true
|
||||
async-tar.workspace = true
|
||||
collections.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
http_client.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
semantic_version.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
toml.workspace = true
|
||||
wasm-encoder.workspace = true
|
||||
wasmparser.workspace = true
|
||||
wit-component.workspace = true
|
1
crates/extension/LICENSE-GPL
Symbolic link
1
crates/extension/LICENSE-GPL
Symbolic link
@ -0,0 +1 @@
|
||||
../../LICENSE-GPL
|
50
crates/extension/src/extension.rs
Normal file
50
crates/extension/src/extension.rs
Normal file
@ -0,0 +1,50 @@
|
||||
pub mod extension_builder;
|
||||
mod extension_manifest;
|
||||
|
||||
use anyhow::{anyhow, bail, Context as _, Result};
|
||||
use semantic_version::SemanticVersion;
|
||||
|
||||
pub use crate::extension_manifest::*;
|
||||
|
||||
pub fn parse_wasm_extension_version(
|
||||
extension_id: &str,
|
||||
wasm_bytes: &[u8],
|
||||
) -> Result<SemanticVersion> {
|
||||
let mut version = None;
|
||||
|
||||
for part in wasmparser::Parser::new(0).parse_all(wasm_bytes) {
|
||||
if let wasmparser::Payload::CustomSection(s) =
|
||||
part.context("error parsing wasm extension")?
|
||||
{
|
||||
if s.name() == "zed:api-version" {
|
||||
version = parse_wasm_extension_version_custom_section(s.data());
|
||||
if version.is_none() {
|
||||
bail!(
|
||||
"extension {} has invalid zed:api-version section: {:?}",
|
||||
extension_id,
|
||||
s.data()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The reason we wait until we're done parsing all of the Wasm bytes to return the version
|
||||
// is to work around a panic that can happen inside of Wasmtime when the bytes are invalid.
|
||||
//
|
||||
// By parsing the entirety of the Wasm bytes before we return, we're able to detect this problem
|
||||
// earlier as an `Err` rather than as a panic.
|
||||
version.ok_or_else(|| anyhow!("extension {} has no zed:api-version section", extension_id))
|
||||
}
|
||||
|
||||
fn parse_wasm_extension_version_custom_section(data: &[u8]) -> Option<SemanticVersion> {
|
||||
if data.len() == 6 {
|
||||
Some(SemanticVersion::new(
|
||||
u16::from_be_bytes([data[0], data[1]]) as _,
|
||||
u16::from_be_bytes([data[2], data[3]]) as _,
|
||||
u16::from_be_bytes([data[4], data[5]]) as _,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use crate::wasm_host::parse_wasm_extension_version;
|
||||
use crate::ExtensionManifest;
|
||||
use crate::{extension_manifest::ExtensionLibraryKind, GrammarManifestEntry};
|
||||
use crate::{
|
||||
parse_wasm_extension_version, ExtensionLibraryKind, ExtensionManifest, GrammarManifestEntry,
|
||||
};
|
||||
use anyhow::{anyhow, bail, Context as _, Result};
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
@ -16,7 +16,7 @@ path = "src/main.rs"
|
||||
anyhow.workspace = true
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
env_logger.workspace = true
|
||||
extension_host = { workspace = true, features = ["no-webrtc"] }
|
||||
extension.workspace = true
|
||||
fs.workspace = true
|
||||
language.workspace = true
|
||||
log.workspace = true
|
||||
|
@ -9,7 +9,7 @@ use std::{
|
||||
use ::fs::{copy_recursive, CopyOptions, Fs, RealFs};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use clap::Parser;
|
||||
use extension_host::{
|
||||
use extension::{
|
||||
extension_builder::{CompileExtensionOptions, ExtensionBuilder},
|
||||
ExtensionManifest,
|
||||
};
|
||||
|
@ -12,9 +12,6 @@ workspace = true
|
||||
path = "src/extension_host.rs"
|
||||
doctest = false
|
||||
|
||||
[features]
|
||||
no-webrtc = ["workspace/no-webrtc"]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
assistant_slash_command.workspace = true
|
||||
@ -23,6 +20,7 @@ async-tar.workspace = true
|
||||
async-trait.workspace = true
|
||||
client.workspace = true
|
||||
collections.workspace = true
|
||||
extension.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
@ -48,11 +46,8 @@ toml.workspace = true
|
||||
ui.workspace = true
|
||||
url.workspace = true
|
||||
util.workspace = true
|
||||
wasm-encoder.workspace = true
|
||||
wasmparser.workspace = true
|
||||
wasmtime-wasi.workspace = true
|
||||
wasmtime.workspace = true
|
||||
wit-component.workspace = true
|
||||
workspace.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -1,7 +1,5 @@
|
||||
pub mod extension_builder;
|
||||
mod extension_indexed_docs_provider;
|
||||
mod extension_lsp_adapter;
|
||||
mod extension_manifest;
|
||||
mod extension_settings;
|
||||
mod extension_slash_command;
|
||||
mod wasm_host;
|
||||
@ -10,7 +8,6 @@ mod wasm_host;
|
||||
mod extension_store_test;
|
||||
|
||||
use crate::extension_indexed_docs_provider::ExtensionIndexedDocsProvider;
|
||||
use crate::extension_manifest::SchemaVersion;
|
||||
use crate::extension_slash_command::ExtensionSlashCommand;
|
||||
use crate::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host::wit};
|
||||
use anyhow::{anyhow, bail, Context as _, Result};
|
||||
@ -19,7 +16,8 @@ use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
||||
use client::{telemetry::Telemetry, Client, ExtensionMetadata, GetExtensionsResponse};
|
||||
use collections::{btree_map, BTreeMap, HashSet};
|
||||
use extension_builder::{CompileExtensionOptions, ExtensionBuilder};
|
||||
use extension::extension_builder::{CompileExtensionOptions, ExtensionBuilder};
|
||||
use extension::SchemaVersion;
|
||||
use fs::{Fs, RemoveOptions};
|
||||
use futures::{
|
||||
channel::{
|
||||
@ -62,7 +60,7 @@ use wasm_host::{
|
||||
WasmExtension, WasmHost,
|
||||
};
|
||||
|
||||
pub use extension_manifest::{
|
||||
pub use extension::{
|
||||
ExtensionLibraryKind, ExtensionManifest, GrammarManifestEntry, OldExtensionManifest,
|
||||
};
|
||||
pub use extension_settings::ExtensionSettings;
|
||||
|
@ -1,4 +1,3 @@
|
||||
use crate::extension_manifest::SchemaVersion;
|
||||
use crate::extension_settings::ExtensionSettings;
|
||||
use crate::{
|
||||
Event, ExtensionIndex, ExtensionIndexEntry, ExtensionIndexLanguageEntry,
|
||||
@ -8,6 +7,7 @@ use crate::{
|
||||
use assistant_slash_command::SlashCommandRegistry;
|
||||
use async_compression::futures::bufread::GzipEncoder;
|
||||
use collections::BTreeMap;
|
||||
use extension::SchemaVersion;
|
||||
use fs::{FakeFs, Fs, RealFs};
|
||||
use futures::{io::BufReader, AsyncReadExt, StreamExt};
|
||||
use gpui::{Context, SemanticVersion, TestAppContext};
|
||||
|
@ -1,7 +1,7 @@
|
||||
pub(crate) mod wit;
|
||||
|
||||
use crate::ExtensionManifest;
|
||||
use anyhow::{anyhow, bail, Context as _, Result};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use fs::{normalize_path, Fs};
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::{
|
||||
@ -112,7 +112,8 @@ impl WasmHost {
|
||||
) -> Task<Result<WasmExtension>> {
|
||||
let this = self.clone();
|
||||
executor.clone().spawn(async move {
|
||||
let zed_api_version = parse_wasm_extension_version(&manifest.id, &wasm_bytes)?;
|
||||
let zed_api_version =
|
||||
extension::parse_wasm_extension_version(&manifest.id, &wasm_bytes)?;
|
||||
|
||||
let component = Component::from_binary(&this.engine, &wasm_bytes)
|
||||
.context("failed to compile wasm component")?;
|
||||
@ -197,49 +198,6 @@ impl WasmHost {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_wasm_extension_version(
|
||||
extension_id: &str,
|
||||
wasm_bytes: &[u8],
|
||||
) -> Result<SemanticVersion> {
|
||||
let mut version = None;
|
||||
|
||||
for part in wasmparser::Parser::new(0).parse_all(wasm_bytes) {
|
||||
if let wasmparser::Payload::CustomSection(s) =
|
||||
part.context("error parsing wasm extension")?
|
||||
{
|
||||
if s.name() == "zed:api-version" {
|
||||
version = parse_wasm_extension_version_custom_section(s.data());
|
||||
if version.is_none() {
|
||||
bail!(
|
||||
"extension {} has invalid zed:api-version section: {:?}",
|
||||
extension_id,
|
||||
s.data()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The reason we wait until we're done parsing all of the Wasm bytes to return the version
|
||||
// is to work around a panic that can happen inside of Wasmtime when the bytes are invalid.
|
||||
//
|
||||
// By parsing the entirety of the Wasm bytes before we return, we're able to detect this problem
|
||||
// earlier as an `Err` rather than as a panic.
|
||||
version.ok_or_else(|| anyhow!("extension {} has no zed:api-version section", extension_id))
|
||||
}
|
||||
|
||||
fn parse_wasm_extension_version_custom_section(data: &[u8]) -> Option<SemanticVersion> {
|
||||
if data.len() == 6 {
|
||||
Some(SemanticVersion::new(
|
||||
u16::from_be_bytes([data[0], data[1]]) as _,
|
||||
u16::from_be_bytes([data[2], data[3]]) as _,
|
||||
u16::from_be_bytes([data[4], data[5]]) as _,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl WasmExtension {
|
||||
pub async fn call<T, Fn>(&self, f: Fn) -> T
|
||||
where
|
||||
|
@ -13,7 +13,6 @@ path = "src/workspace.rs"
|
||||
doctest = false
|
||||
|
||||
[features]
|
||||
no-webrtc = ["call/no-webrtc"]
|
||||
test-support = [
|
||||
"call/test-support",
|
||||
"client/test-support",
|
||||
|
Loading…
Reference in New Issue
Block a user