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:
Marshall Bowers 2024-11-01 13:20:30 -04:00 committed by GitHub
parent ea44c510a3
commit 770886880f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 122 additions and 68 deletions

29
Cargo.lock generated
View File

@ -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",
]

View File

@ -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" }

View File

@ -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",

View 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

View File

@ -0,0 +1 @@
../../LICENSE-GPL

View 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
}
}

View File

@ -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;

View File

@ -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

View File

@ -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,
};

View File

@ -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]

View File

@ -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;

View File

@ -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};

View File

@ -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

View File

@ -13,7 +13,6 @@ path = "src/workspace.rs"
doctest = false
[features]
no-webrtc = ["call/no-webrtc"]
test-support = [
"call/test-support",
"client/test-support",