mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-28 05:21:56 +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",
|
"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]]
|
[[package]]
|
||||||
name = "extension_cli"
|
name = "extension_cli"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -4096,7 +4119,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"env_logger 0.11.5",
|
"env_logger 0.11.5",
|
||||||
"extension_host",
|
"extension",
|
||||||
"fs",
|
"fs",
|
||||||
"language",
|
"language",
|
||||||
"log",
|
"log",
|
||||||
@ -4124,6 +4147,7 @@ dependencies = [
|
|||||||
"collections",
|
"collections",
|
||||||
"ctor",
|
"ctor",
|
||||||
"env_logger 0.11.5",
|
"env_logger 0.11.5",
|
||||||
|
"extension",
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.30",
|
"futures 0.3.30",
|
||||||
"gpui",
|
"gpui",
|
||||||
@ -4151,11 +4175,8 @@ dependencies = [
|
|||||||
"ui",
|
"ui",
|
||||||
"url",
|
"url",
|
||||||
"util",
|
"util",
|
||||||
"wasm-encoder 0.215.0",
|
|
||||||
"wasmparser 0.215.0",
|
|
||||||
"wasmtime",
|
"wasmtime",
|
||||||
"wasmtime-wasi",
|
"wasmtime-wasi",
|
||||||
"wit-component",
|
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ members = [
|
|||||||
"crates/docs_preprocessor",
|
"crates/docs_preprocessor",
|
||||||
"crates/editor",
|
"crates/editor",
|
||||||
"crates/evals",
|
"crates/evals",
|
||||||
|
"crates/extension",
|
||||||
"crates/extension_api",
|
"crates/extension_api",
|
||||||
"crates/extension_cli",
|
"crates/extension_cli",
|
||||||
"crates/extension_host",
|
"crates/extension_host",
|
||||||
@ -201,6 +202,7 @@ copilot = { path = "crates/copilot" }
|
|||||||
db = { path = "crates/db" }
|
db = { path = "crates/db" }
|
||||||
diagnostics = { path = "crates/diagnostics" }
|
diagnostics = { path = "crates/diagnostics" }
|
||||||
editor = { path = "crates/editor" }
|
editor = { path = "crates/editor" }
|
||||||
|
extension = { path = "crates/extension" }
|
||||||
extension_host = { path = "crates/extension_host" }
|
extension_host = { path = "crates/extension_host" }
|
||||||
extensions_ui = { path = "crates/extensions_ui" }
|
extensions_ui = { path = "crates/extensions_ui" }
|
||||||
feature_flags = { path = "crates/feature_flags" }
|
feature_flags = { path = "crates/feature_flags" }
|
||||||
|
@ -13,7 +13,6 @@ path = "src/call.rs"
|
|||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
no-webrtc = ["live_kit_client/no-webrtc"]
|
|
||||||
test-support = [
|
test-support = [
|
||||||
"client/test-support",
|
"client/test-support",
|
||||||
"collections/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::{
|
||||||
use crate::ExtensionManifest;
|
parse_wasm_extension_version, ExtensionLibraryKind, ExtensionManifest, GrammarManifestEntry,
|
||||||
use crate::{extension_manifest::ExtensionLibraryKind, GrammarManifestEntry};
|
};
|
||||||
use anyhow::{anyhow, bail, Context as _, Result};
|
use anyhow::{anyhow, bail, Context as _, Result};
|
||||||
use async_compression::futures::bufread::GzipDecoder;
|
use async_compression::futures::bufread::GzipDecoder;
|
||||||
use async_tar::Archive;
|
use async_tar::Archive;
|
@ -16,7 +16,7 @@ path = "src/main.rs"
|
|||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
clap = { workspace = true, features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
extension_host = { workspace = true, features = ["no-webrtc"] }
|
extension.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
language.workspace = true
|
language.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
@ -9,7 +9,7 @@ use std::{
|
|||||||
use ::fs::{copy_recursive, CopyOptions, Fs, RealFs};
|
use ::fs::{copy_recursive, CopyOptions, Fs, RealFs};
|
||||||
use anyhow::{anyhow, bail, Context, Result};
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use extension_host::{
|
use extension::{
|
||||||
extension_builder::{CompileExtensionOptions, ExtensionBuilder},
|
extension_builder::{CompileExtensionOptions, ExtensionBuilder},
|
||||||
ExtensionManifest,
|
ExtensionManifest,
|
||||||
};
|
};
|
||||||
|
@ -12,9 +12,6 @@ workspace = true
|
|||||||
path = "src/extension_host.rs"
|
path = "src/extension_host.rs"
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[features]
|
|
||||||
no-webrtc = ["workspace/no-webrtc"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
assistant_slash_command.workspace = true
|
assistant_slash_command.workspace = true
|
||||||
@ -23,6 +20,7 @@ async-tar.workspace = true
|
|||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
|
extension.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
@ -48,11 +46,8 @@ toml.workspace = true
|
|||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
url.workspace = true
|
url.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
wasm-encoder.workspace = true
|
|
||||||
wasmparser.workspace = true
|
|
||||||
wasmtime-wasi.workspace = true
|
wasmtime-wasi.workspace = true
|
||||||
wasmtime.workspace = true
|
wasmtime.workspace = true
|
||||||
wit-component.workspace = true
|
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
pub mod extension_builder;
|
|
||||||
mod extension_indexed_docs_provider;
|
mod extension_indexed_docs_provider;
|
||||||
mod extension_lsp_adapter;
|
mod extension_lsp_adapter;
|
||||||
mod extension_manifest;
|
|
||||||
mod extension_settings;
|
mod extension_settings;
|
||||||
mod extension_slash_command;
|
mod extension_slash_command;
|
||||||
mod wasm_host;
|
mod wasm_host;
|
||||||
@ -10,7 +8,6 @@ mod wasm_host;
|
|||||||
mod extension_store_test;
|
mod extension_store_test;
|
||||||
|
|
||||||
use crate::extension_indexed_docs_provider::ExtensionIndexedDocsProvider;
|
use crate::extension_indexed_docs_provider::ExtensionIndexedDocsProvider;
|
||||||
use crate::extension_manifest::SchemaVersion;
|
|
||||||
use crate::extension_slash_command::ExtensionSlashCommand;
|
use crate::extension_slash_command::ExtensionSlashCommand;
|
||||||
use crate::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host::wit};
|
use crate::{extension_lsp_adapter::ExtensionLspAdapter, wasm_host::wit};
|
||||||
use anyhow::{anyhow, bail, Context as _, Result};
|
use anyhow::{anyhow, bail, Context as _, Result};
|
||||||
@ -19,7 +16,8 @@ use async_compression::futures::bufread::GzipDecoder;
|
|||||||
use async_tar::Archive;
|
use async_tar::Archive;
|
||||||
use client::{telemetry::Telemetry, Client, ExtensionMetadata, GetExtensionsResponse};
|
use client::{telemetry::Telemetry, Client, ExtensionMetadata, GetExtensionsResponse};
|
||||||
use collections::{btree_map, BTreeMap, HashSet};
|
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 fs::{Fs, RemoveOptions};
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::{
|
channel::{
|
||||||
@ -62,7 +60,7 @@ use wasm_host::{
|
|||||||
WasmExtension, WasmHost,
|
WasmExtension, WasmHost,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use extension_manifest::{
|
pub use extension::{
|
||||||
ExtensionLibraryKind, ExtensionManifest, GrammarManifestEntry, OldExtensionManifest,
|
ExtensionLibraryKind, ExtensionManifest, GrammarManifestEntry, OldExtensionManifest,
|
||||||
};
|
};
|
||||||
pub use extension_settings::ExtensionSettings;
|
pub use extension_settings::ExtensionSettings;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use crate::extension_manifest::SchemaVersion;
|
|
||||||
use crate::extension_settings::ExtensionSettings;
|
use crate::extension_settings::ExtensionSettings;
|
||||||
use crate::{
|
use crate::{
|
||||||
Event, ExtensionIndex, ExtensionIndexEntry, ExtensionIndexLanguageEntry,
|
Event, ExtensionIndex, ExtensionIndexEntry, ExtensionIndexLanguageEntry,
|
||||||
@ -8,6 +7,7 @@ use crate::{
|
|||||||
use assistant_slash_command::SlashCommandRegistry;
|
use assistant_slash_command::SlashCommandRegistry;
|
||||||
use async_compression::futures::bufread::GzipEncoder;
|
use async_compression::futures::bufread::GzipEncoder;
|
||||||
use collections::BTreeMap;
|
use collections::BTreeMap;
|
||||||
|
use extension::SchemaVersion;
|
||||||
use fs::{FakeFs, Fs, RealFs};
|
use fs::{FakeFs, Fs, RealFs};
|
||||||
use futures::{io::BufReader, AsyncReadExt, StreamExt};
|
use futures::{io::BufReader, AsyncReadExt, StreamExt};
|
||||||
use gpui::{Context, SemanticVersion, TestAppContext};
|
use gpui::{Context, SemanticVersion, TestAppContext};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
pub(crate) mod wit;
|
pub(crate) mod wit;
|
||||||
|
|
||||||
use crate::ExtensionManifest;
|
use crate::ExtensionManifest;
|
||||||
use anyhow::{anyhow, bail, Context as _, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use fs::{normalize_path, Fs};
|
use fs::{normalize_path, Fs};
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use futures::{
|
use futures::{
|
||||||
@ -112,7 +112,8 @@ impl WasmHost {
|
|||||||
) -> Task<Result<WasmExtension>> {
|
) -> Task<Result<WasmExtension>> {
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
executor.clone().spawn(async move {
|
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)
|
let component = Component::from_binary(&this.engine, &wasm_bytes)
|
||||||
.context("failed to compile wasm component")?;
|
.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 {
|
impl WasmExtension {
|
||||||
pub async fn call<T, Fn>(&self, f: Fn) -> T
|
pub async fn call<T, Fn>(&self, f: Fn) -> T
|
||||||
where
|
where
|
||||||
|
@ -13,7 +13,6 @@ path = "src/workspace.rs"
|
|||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
no-webrtc = ["call/no-webrtc"]
|
|
||||||
test-support = [
|
test-support = [
|
||||||
"call/test-support",
|
"call/test-support",
|
||||||
"client/test-support",
|
"client/test-support",
|
||||||
|
Loading…
Reference in New Issue
Block a user