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 <max@zed.dev>
This commit is contained in:
parent
6e49a2460e
commit
8b586ef8e7
13 changed files with 97 additions and 46 deletions
47
Cargo.lock
generated
47
Cargo.lock
generated
|
@ -12670,28 +12670,21 @@ dependencies = [
|
||||||
name = "zed_astro"
|
name = "zed_astro"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zed_extension_api 0.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_csharp"
|
name = "zed_csharp"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zed_extension_api 0.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_erlang"
|
name = "zed_erlang"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
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_extension_api"
|
|
||||||
version = "0.0.4"
|
|
||||||
dependencies = [
|
|
||||||
"wit-bindgen",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -12703,67 +12696,83 @@ dependencies = [
|
||||||
"wit-bindgen",
|
"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]]
|
[[package]]
|
||||||
name = "zed_gleam"
|
name = "zed_gleam"
|
||||||
version = "0.0.2"
|
version = "0.0.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zed_extension_api 0.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_haskell"
|
name = "zed_haskell"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zed_extension_api 0.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_php"
|
name = "zed_php"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zed_extension_api 0.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_prisma"
|
name = "zed_prisma"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zed_extension_api 0.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_purescript"
|
name = "zed_purescript"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zed_extension_api 0.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_svelte"
|
name = "zed_svelte"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zed_extension_api 0.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_toml"
|
name = "zed_toml"
|
||||||
version = "0.0.2"
|
version = "0.0.2"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
name = "zed_uiua"
|
name = "zed_uiua"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zed_extension_api 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zed_extension_api 0.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed_zig"
|
name = "zed_zig"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
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]]
|
[[package]]
|
||||||
|
|
|
@ -57,8 +57,13 @@ impl LspAdapter for ExtensionLspAdapter {
|
||||||
.host
|
.host
|
||||||
.path_from_extension(&self.extension.manifest.id, command.command.as_ref());
|
.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
|
// TODO: This should now be done via the `zed::make_file_executable` function in
|
||||||
// now we just manually set the file permissions for extensions that we know need it.
|
// 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()) {
|
if ["toml", "zig"].contains(&self.extension.manifest.id.as_ref()) {
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
mod v0_0_1;
|
mod since_v0_0_1;
|
||||||
mod v0_0_4;
|
mod since_v0_0_4;
|
||||||
|
|
||||||
use super::{wasm_engine, WasmState};
|
use super::{wasm_engine, WasmState};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
@ -11,7 +11,7 @@ use wasmtime::{
|
||||||
Store,
|
Store,
|
||||||
};
|
};
|
||||||
|
|
||||||
use v0_0_4 as latest;
|
use since_v0_0_4 as latest;
|
||||||
|
|
||||||
pub use latest::{Command, LanguageServerConfig};
|
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.
|
/// Returns whether the given Wasm API version is supported by the Wasm host.
|
||||||
pub fn is_supported_wasm_api_version(version: SemanticVersion) -> bool {
|
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 {
|
pub enum Extension {
|
||||||
V004(v0_0_4::Extension),
|
V004(since_v0_0_4::Extension),
|
||||||
V001(v0_0_1::Extension),
|
V001(since_v0_0_1::Extension),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Extension {
|
impl Extension {
|
||||||
|
@ -44,17 +44,23 @@ impl Extension {
|
||||||
version: SemanticVersion,
|
version: SemanticVersion,
|
||||||
component: &Component,
|
component: &Component,
|
||||||
) -> Result<(Self, Instance)> {
|
) -> Result<(Self, Instance)> {
|
||||||
if version < latest::VERSION {
|
if version < latest::MIN_VERSION {
|
||||||
let (extension, instance) =
|
let (extension, instance) = since_v0_0_1::Extension::instantiate_async(
|
||||||
v0_0_1::Extension::instantiate_async(store, &component, v0_0_1::linker())
|
store,
|
||||||
.await
|
&component,
|
||||||
.context("failed to instantiate wasm extension")?;
|
since_v0_0_1::linker(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("failed to instantiate wasm extension")?;
|
||||||
Ok((Self::V001(extension), instance))
|
Ok((Self::V001(extension), instance))
|
||||||
} else {
|
} else {
|
||||||
let (extension, instance) =
|
let (extension, instance) = since_v0_0_4::Extension::instantiate_async(
|
||||||
v0_0_4::Extension::instantiate_async(store, &component, v0_0_4::linker())
|
store,
|
||||||
.await
|
&component,
|
||||||
.context("failed to instantiate wasm extension")?;
|
since_v0_0_4::linker(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("failed to instantiate wasm extension")?;
|
||||||
Ok((Self::V004(extension), instance))
|
Ok((Self::V004(extension), instance))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,11 @@ use semantic_version::SemanticVersion;
|
||||||
use std::sync::{Arc, OnceLock};
|
use std::sync::{Arc, OnceLock};
|
||||||
use wasmtime::component::{Linker, Resource};
|
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!({
|
wasmtime::component::bindgen!({
|
||||||
async: true,
|
async: true,
|
||||||
path: "../extension_api/wit/0.0.1",
|
path: "../extension_api/wit/since_v0.0.1",
|
||||||
with: {
|
with: {
|
||||||
"worktree": ExtensionWorktree,
|
"worktree": ExtensionWorktree,
|
||||||
},
|
},
|
|
@ -6,6 +6,7 @@ use async_trait::async_trait;
|
||||||
use futures::io::BufReader;
|
use futures::io::BufReader;
|
||||||
use language::{LanguageServerBinaryStatus, LspAdapterDelegate};
|
use language::{LanguageServerBinaryStatus, LspAdapterDelegate};
|
||||||
use semantic_version::SemanticVersion;
|
use semantic_version::SemanticVersion;
|
||||||
|
use std::path::Path;
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
@ -14,11 +15,12 @@ use std::{
|
||||||
use util::maybe;
|
use util::maybe;
|
||||||
use wasmtime::component::{Linker, Resource};
|
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!({
|
wasmtime::component::bindgen!({
|
||||||
async: true,
|
async: true,
|
||||||
path: "../extension_api/wit/0.0.4",
|
path: "../extension_api/wit/since_v0.0.4",
|
||||||
with: {
|
with: {
|
||||||
"worktree": ExtensionWorktree,
|
"worktree": ExtensionWorktree,
|
||||||
},
|
},
|
||||||
|
@ -274,6 +276,28 @@ impl ExtensionImports for WasmState {
|
||||||
.await;
|
.await;
|
||||||
convert_result(result)
|
convert_result(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn make_file_executable(&mut self, path: String) -> wasmtime::Result<Result<(), String>> {
|
||||||
|
#[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<T>(result: Result<T>) -> wasmtime::Result<Result<T, String>> {
|
fn convert_result<T>(result: Result<T>) -> wasmtime::Result<Result<T, String>> {
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "zed_extension_api"
|
name = "zed_extension_api"
|
||||||
version = "0.0.4"
|
version = "0.0.5"
|
||||||
description = "APIs for creating Zed extensions in Rust"
|
description = "APIs for creating Zed extensions in Rust"
|
||||||
repository = "https://github.com/zed-industries/zed"
|
repository = "https://github.com/zed-industries/zed"
|
||||||
documentation = "https://docs.rs/zed_extension_api"
|
documentation = "https://docs.rs/zed_extension_api"
|
||||||
|
|
|
@ -53,7 +53,7 @@ pub static ZED_API_VERSION: [u8; 6] = *include_bytes!(concat!(env!("OUT_DIR"), "
|
||||||
mod wit {
|
mod wit {
|
||||||
wit_bindgen::generate!({
|
wit_bindgen::generate!({
|
||||||
skip: ["init-extension"],
|
skip: ["init-extension"],
|
||||||
path: "./wit/0.0.4",
|
path: "./wit/since_v0.0.4",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,9 +62,12 @@ world extension {
|
||||||
/// Gets the latest release for the given GitHub repository.
|
/// Gets the latest release for the given GitHub repository.
|
||||||
import latest-github-release: func(repo: string, options: github-release-options) -> result<github-release, string>;
|
import latest-github-release: func(repo: string, options: github-release-options) -> result<github-release, string>;
|
||||||
|
|
||||||
/// 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.
|
/// 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.
|
/// 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);
|
import set-language-server-installation-status: func(language-server-name: string, status: language-server-installation-status);
|
|
@ -13,4 +13,4 @@ path = "src/toml.rs"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zed_extension_api = "0.0.4"
|
zed_extension_api = "0.0.5"
|
||||||
|
|
|
@ -72,6 +72,8 @@ impl TomlExtension {
|
||||||
)
|
)
|
||||||
.map_err(|err| format!("failed to download file: {err}"))?;
|
.map_err(|err| format!("failed to download file: {err}"))?;
|
||||||
|
|
||||||
|
zed::make_file_executable(&binary_path)?;
|
||||||
|
|
||||||
let entries = fs::read_dir(".")
|
let entries = fs::read_dir(".")
|
||||||
.map_err(|err| format!("failed to list working directory {err}"))?;
|
.map_err(|err| format!("failed to list working directory {err}"))?;
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
|
|
|
@ -13,4 +13,4 @@ path = "src/zig.rs"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zed_extension_api = "0.0.4"
|
zed_extension_api = "0.0.5"
|
||||||
|
|
|
@ -78,6 +78,8 @@ impl ZigExtension {
|
||||||
)
|
)
|
||||||
.map_err(|e| format!("failed to download file: {e}"))?;
|
.map_err(|e| format!("failed to download file: {e}"))?;
|
||||||
|
|
||||||
|
zed::make_file_executable(&binary_path)?;
|
||||||
|
|
||||||
let entries =
|
let entries =
|
||||||
fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
|
fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue