From 8b586ef8e7cc5aaf1917819c264be4c5efd60916 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Mon, 1 Apr 2024 15:28:24 -0400 Subject: [PATCH] Add new `make-file-executable` API for extensions (#10047) This PR adds a new function, `make-file-executable`, to the Zed extension API that can be used to mark a given file as executable (typically the language server binary). This is available in v0.0.5 of the `zed_extension_api` crate. We also reworked how we represent the various WIT versions on disk to make it a bit clearer what the version number entails. Release Notes: - N/A --------- Co-authored-by: Max --- Cargo.lock | 47 +++++++++++-------- crates/extension/src/extension_lsp_adapter.rs | 9 +++- crates/extension/src/wasm_host/wit.rs | 36 ++++++++------ .../wit/{v0_0_1.rs => since_v0_0_1.rs} | 4 +- .../wit/{v0_0_4.rs => since_v0_0_4.rs} | 28 ++++++++++- crates/extension_api/Cargo.toml | 2 +- crates/extension_api/src/extension_api.rs | 2 +- .../wit/{0.0.1 => since_v0.0.1}/extension.wit | 0 .../wit/{0.0.4 => since_v0.0.4}/extension.wit | 7 ++- extensions/toml/Cargo.toml | 2 +- extensions/toml/src/toml.rs | 2 + extensions/zig/Cargo.toml | 2 +- extensions/zig/src/zig.rs | 2 + 13 files changed, 97 insertions(+), 46 deletions(-) rename crates/extension/src/wasm_host/wit/{v0_0_1.rs => since_v0_0_1.rs} (98%) rename crates/extension/src/wasm_host/wit/{v0_0_4.rs => since_v0_0_4.rs} (90%) rename crates/extension_api/wit/{0.0.1 => since_v0.0.1}/extension.wit (100%) rename crates/extension_api/wit/{0.0.4 => since_v0.0.4}/extension.wit (90%) diff --git a/Cargo.lock b/Cargo.lock index 8a775d2f85..d55254bfbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12670,28 +12670,21 @@ dependencies = [ name = "zed_astro" version = "0.0.1" dependencies = [ - "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "zed_extension_api 0.0.4", ] [[package]] name = "zed_csharp" version = "0.0.1" dependencies = [ - "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "zed_extension_api 0.0.4", ] [[package]] name = "zed_erlang" version = "0.0.1" dependencies = [ - "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "zed_extension_api" -version = "0.0.4" -dependencies = [ - "wit-bindgen", + "zed_extension_api 0.0.4", ] [[package]] @@ -12703,67 +12696,83 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "zed_extension_api" +version = "0.0.5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "zed_extension_api" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5f4ae4e302a80591635ef9a236b35fde6fcc26cfd060e66fde4ba9f9fd394a1" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "zed_gleam" version = "0.0.2" dependencies = [ - "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "zed_extension_api 0.0.4", ] [[package]] name = "zed_haskell" version = "0.0.1" dependencies = [ - "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "zed_extension_api 0.0.4", ] [[package]] name = "zed_php" version = "0.0.1" dependencies = [ - "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "zed_extension_api 0.0.4", ] [[package]] name = "zed_prisma" version = "0.0.1" dependencies = [ - "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "zed_extension_api 0.0.4", ] [[package]] name = "zed_purescript" version = "0.0.1" dependencies = [ - "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "zed_extension_api 0.0.4", ] [[package]] name = "zed_svelte" version = "0.0.1" dependencies = [ - "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "zed_extension_api 0.0.4", ] [[package]] name = "zed_toml" version = "0.0.2" dependencies = [ - "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "zed_extension_api 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "zed_uiua" version = "0.0.1" dependencies = [ - "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "zed_extension_api 0.0.4", ] [[package]] name = "zed_zig" version = "0.0.1" dependencies = [ - "zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "zed_extension_api 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/crates/extension/src/extension_lsp_adapter.rs b/crates/extension/src/extension_lsp_adapter.rs index 86c3a26154..07ced55323 100644 --- a/crates/extension/src/extension_lsp_adapter.rs +++ b/crates/extension/src/extension_lsp_adapter.rs @@ -57,8 +57,13 @@ impl LspAdapter for ExtensionLspAdapter { .host .path_from_extension(&self.extension.manifest.id, command.command.as_ref()); - // TODO: Eventually we'll want to expose an extension API for doing this, but for - // now we just manually set the file permissions for extensions that we know need it. + // TODO: This should now be done via the `zed::make_file_executable` function in + // Zed extension API, but we're leaving these existing usages in place temporarily + // to avoid any compatibility issues between Zed and the extension versions. + // + // We can remove once the following extension versions no longer see any use: + // - toml@0.0.2 + // - zig@0.0.1 if ["toml", "zig"].contains(&self.extension.manifest.id.as_ref()) { #[cfg(not(windows))] { diff --git a/crates/extension/src/wasm_host/wit.rs b/crates/extension/src/wasm_host/wit.rs index 00d69acd1e..a4790deec1 100644 --- a/crates/extension/src/wasm_host/wit.rs +++ b/crates/extension/src/wasm_host/wit.rs @@ -1,5 +1,5 @@ -mod v0_0_1; -mod v0_0_4; +mod since_v0_0_1; +mod since_v0_0_4; use super::{wasm_engine, WasmState}; use anyhow::{Context, Result}; @@ -11,7 +11,7 @@ use wasmtime::{ Store, }; -use v0_0_4 as latest; +use since_v0_0_4 as latest; pub use latest::{Command, LanguageServerConfig}; @@ -30,12 +30,12 @@ fn wasi_view(state: &mut WasmState) -> &mut WasmState { /// 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 + since_v0_0_1::MIN_VERSION <= version && version <= latest::MAX_VERSION } pub enum Extension { - V004(v0_0_4::Extension), - V001(v0_0_1::Extension), + V004(since_v0_0_4::Extension), + V001(since_v0_0_1::Extension), } impl Extension { @@ -44,17 +44,23 @@ impl Extension { version: SemanticVersion, component: &Component, ) -> Result<(Self, Instance)> { - if version < latest::VERSION { - let (extension, instance) = - v0_0_1::Extension::instantiate_async(store, &component, v0_0_1::linker()) - .await - .context("failed to instantiate wasm extension")?; + if version < latest::MIN_VERSION { + let (extension, instance) = since_v0_0_1::Extension::instantiate_async( + store, + &component, + since_v0_0_1::linker(), + ) + .await + .context("failed to instantiate wasm extension")?; Ok((Self::V001(extension), instance)) } else { - let (extension, instance) = - v0_0_4::Extension::instantiate_async(store, &component, v0_0_4::linker()) - .await - .context("failed to instantiate wasm extension")?; + let (extension, instance) = since_v0_0_4::Extension::instantiate_async( + store, + &component, + since_v0_0_4::linker(), + ) + .await + .context("failed to instantiate wasm extension")?; Ok((Self::V004(extension), instance)) } } diff --git a/crates/extension/src/wasm_host/wit/v0_0_1.rs b/crates/extension/src/wasm_host/wit/since_v0_0_1.rs similarity index 98% rename from crates/extension/src/wasm_host/wit/v0_0_1.rs rename to crates/extension/src/wasm_host/wit/since_v0_0_1.rs index 29d9d2ee05..246b06028e 100644 --- a/crates/extension/src/wasm_host/wit/v0_0_1.rs +++ b/crates/extension/src/wasm_host/wit/since_v0_0_1.rs @@ -7,11 +7,11 @@ use semantic_version::SemanticVersion; use std::sync::{Arc, OnceLock}; use wasmtime::component::{Linker, Resource}; -pub const VERSION: SemanticVersion = SemanticVersion::new(0, 0, 1); +pub const MIN_VERSION: SemanticVersion = SemanticVersion::new(0, 0, 1); wasmtime::component::bindgen!({ async: true, - path: "../extension_api/wit/0.0.1", + path: "../extension_api/wit/since_v0.0.1", with: { "worktree": ExtensionWorktree, }, diff --git a/crates/extension/src/wasm_host/wit/v0_0_4.rs b/crates/extension/src/wasm_host/wit/since_v0_0_4.rs similarity index 90% rename from crates/extension/src/wasm_host/wit/v0_0_4.rs rename to crates/extension/src/wasm_host/wit/since_v0_0_4.rs index 24acb3c7c2..cd1b429bb2 100644 --- a/crates/extension/src/wasm_host/wit/v0_0_4.rs +++ b/crates/extension/src/wasm_host/wit/since_v0_0_4.rs @@ -6,6 +6,7 @@ use async_trait::async_trait; use futures::io::BufReader; use language::{LanguageServerBinaryStatus, LspAdapterDelegate}; use semantic_version::SemanticVersion; +use std::path::Path; use std::{ env, path::PathBuf, @@ -14,11 +15,12 @@ use std::{ use util::maybe; use wasmtime::component::{Linker, Resource}; -pub const VERSION: SemanticVersion = SemanticVersion::new(0, 0, 4); +pub const MIN_VERSION: SemanticVersion = SemanticVersion::new(0, 0, 4); +pub const MAX_VERSION: SemanticVersion = SemanticVersion::new(0, 0, 5); wasmtime::component::bindgen!({ async: true, - path: "../extension_api/wit/0.0.4", + path: "../extension_api/wit/since_v0.0.4", with: { "worktree": ExtensionWorktree, }, @@ -274,6 +276,28 @@ impl ExtensionImports for WasmState { .await; convert_result(result) } + + async fn make_file_executable(&mut self, path: String) -> wasmtime::Result> { + #[allow(unused)] + let path = self + .host + .writeable_path_from_extension(&self.manifest.id, Path::new(&path))?; + + #[cfg(unix)] + { + use std::fs::{self, Permissions}; + use std::os::unix::fs::PermissionsExt; + + return convert_result( + fs::set_permissions(&path, Permissions::from_mode(0o755)).map_err(|error| { + anyhow!("failed to set permissions for path {path:?}: {error}") + }), + ); + } + + #[cfg(not(unix))] + Ok(Ok(())) + } } fn convert_result(result: Result) -> wasmtime::Result> { diff --git a/crates/extension_api/Cargo.toml b/crates/extension_api/Cargo.toml index ad00f14283..c5d75259dc 100644 --- a/crates/extension_api/Cargo.toml +++ b/crates/extension_api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zed_extension_api" -version = "0.0.4" +version = "0.0.5" description = "APIs for creating Zed extensions in Rust" repository = "https://github.com/zed-industries/zed" documentation = "https://docs.rs/zed_extension_api" diff --git a/crates/extension_api/src/extension_api.rs b/crates/extension_api/src/extension_api.rs index d55eb0a1b7..435e83a77c 100644 --- a/crates/extension_api/src/extension_api.rs +++ b/crates/extension_api/src/extension_api.rs @@ -53,7 +53,7 @@ pub static ZED_API_VERSION: [u8; 6] = *include_bytes!(concat!(env!("OUT_DIR"), " mod wit { wit_bindgen::generate!({ skip: ["init-extension"], - path: "./wit/0.0.4", + path: "./wit/since_v0.0.4", }); } diff --git a/crates/extension_api/wit/0.0.1/extension.wit b/crates/extension_api/wit/since_v0.0.1/extension.wit similarity index 100% rename from crates/extension_api/wit/0.0.1/extension.wit rename to crates/extension_api/wit/since_v0.0.1/extension.wit diff --git a/crates/extension_api/wit/0.0.4/extension.wit b/crates/extension_api/wit/since_v0.0.4/extension.wit similarity index 90% rename from crates/extension_api/wit/0.0.4/extension.wit rename to crates/extension_api/wit/since_v0.0.4/extension.wit index 5d0c5fb174..735c921ee0 100644 --- a/crates/extension_api/wit/0.0.4/extension.wit +++ b/crates/extension_api/wit/since_v0.0.4/extension.wit @@ -62,9 +62,12 @@ world extension { /// Gets the latest release for the given GitHub repository. import latest-github-release: func(repo: string, options: github-release-options) -> result; - /// Downloads a file from the given url, and saves it to the given filename within the extension's + /// Downloads a file from the given url, and saves it to the given path within the extension's /// working directory. Extracts the file according to the given file type. - import download-file: func(url: string, output-filename: string, file-type: downloaded-file-type) -> result<_, string>; + import download-file: func(url: string, file-path: string, file-type: downloaded-file-type) -> result<_, string>; + + /// Makes the file at the given path executable. + import make-file-executable: func(filepath: string) -> result<_, string>; /// Updates the installation status for the given language server. import set-language-server-installation-status: func(language-server-name: string, status: language-server-installation-status); diff --git a/extensions/toml/Cargo.toml b/extensions/toml/Cargo.toml index ae412929dd..deb7143d86 100644 --- a/extensions/toml/Cargo.toml +++ b/extensions/toml/Cargo.toml @@ -13,4 +13,4 @@ path = "src/toml.rs" crate-type = ["cdylib"] [dependencies] -zed_extension_api = "0.0.4" +zed_extension_api = "0.0.5" diff --git a/extensions/toml/src/toml.rs b/extensions/toml/src/toml.rs index a4fb1c8d4d..e629e1a3e8 100644 --- a/extensions/toml/src/toml.rs +++ b/extensions/toml/src/toml.rs @@ -72,6 +72,8 @@ impl TomlExtension { ) .map_err(|err| format!("failed to download file: {err}"))?; + zed::make_file_executable(&binary_path)?; + let entries = fs::read_dir(".") .map_err(|err| format!("failed to list working directory {err}"))?; for entry in entries { diff --git a/extensions/zig/Cargo.toml b/extensions/zig/Cargo.toml index 852e776ef9..47a03d53e4 100644 --- a/extensions/zig/Cargo.toml +++ b/extensions/zig/Cargo.toml @@ -13,4 +13,4 @@ path = "src/zig.rs" crate-type = ["cdylib"] [dependencies] -zed_extension_api = "0.0.4" +zed_extension_api = "0.0.5" diff --git a/extensions/zig/src/zig.rs b/extensions/zig/src/zig.rs index 94568afad5..a6066e481a 100644 --- a/extensions/zig/src/zig.rs +++ b/extensions/zig/src/zig.rs @@ -78,6 +78,8 @@ impl ZigExtension { ) .map_err(|e| format!("failed to download file: {e}"))?; + zed::make_file_executable(&binary_path)?; + let entries = fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?; for entry in entries {