diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c053610801..0691cba81d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,9 +89,6 @@ jobs: - name: cargo clippy run: cargo xtask clippy - - name: Install WASI dependencies - run: script/setup-wasm - - name: Run tests uses: ./.github/actions/run_tests diff --git a/Cargo.lock b/Cargo.lock index 66d26f14c6..81cae2ae82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3011,15 +3011,6 @@ dependencies = [ "util", ] -[[package]] -name = "debugid" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" -dependencies = [ - "uuid", -] - [[package]] name = "deflate" version = "0.8.6" @@ -3159,16 +3150,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "directories-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" -dependencies = [ - "cfg-if 1.0.0", - "dirs-sys-next", -] - [[package]] name = "dirs" version = "3.0.2" @@ -3556,6 +3537,7 @@ dependencies = [ "theme", "toml 0.8.10", "util", + "wasm-encoder", "wasmparser", "wasmtime", "wasmtime-wasi", @@ -4165,28 +4147,6 @@ dependencies = [ "thread_local", ] -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "fxprof-processed-profile" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd" -dependencies = [ - "bitflags 2.4.2", - "debugid", - "fxhash", - "serde", - "serde_json", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -5060,26 +5020,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" -[[package]] -name = "ittapi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b996fe614c41395cdaedf3cf408a9534851090959d90d54a535f675550b64b1" -dependencies = [ - "anyhow", - "ittapi-sys", - "log", -] - -[[package]] -name = "ittapi-sys" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f5385394064fa2c886205dba02598013ce83d3e92d33dbdc0c52fe0e7bf4fc" -dependencies = [ - "cc", -] - [[package]] name = "jni" version = "0.19.0" @@ -5385,7 +5325,6 @@ dependencies = [ "tree-sitter-svelte", "tree-sitter-toml", "tree-sitter-typescript", - "tree-sitter-uiua", "tree-sitter-vue", "tree-sitter-yaml", "tree-sitter-zig", @@ -10816,15 +10755,6 @@ dependencies = [ "tree-sitter", ] -[[package]] -name = "tree-sitter-uiua" -version = "0.10.0" -source = "git+https://github.com/shnarazk/tree-sitter-uiua?rev=21dc2db39494585bf29a3f86d5add6e9d11a22ba#21dc2db39494585bf29a3f86d5add6e9d11a22ba" -dependencies = [ - "cc", - "tree-sitter", -] - [[package]] name = "tree-sitter-vue" version = "0.0.1" @@ -11440,15 +11370,6 @@ dependencies = [ "leb128", ] -[[package]] -name = "wasm-encoder" -version = "0.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" -dependencies = [ - "leb128", -] - [[package]] name = "wasm-metadata" version = "0.10.20" @@ -11461,7 +11382,7 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.41.2", + "wasm-encoder", "wasmparser", ] @@ -11492,41 +11413,33 @@ version = "18.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c843b8bc4dd4f3a76173ba93405c71111d570af0d90ea5f6299c705d0c2add2" dependencies = [ - "addr2line", "anyhow", "async-trait", "bincode", "bumpalo", "cfg-if 1.0.0", "encoding_rs", - "fxprof-processed-profile", "gimli", "indexmap 2.0.0", - "ittapi", "libc", "log", "object", "once_cell", "paste", - "rayon", "rustix 0.38.30", "serde", "serde_derive", "serde_json", "target-lexicon", - "wasm-encoder 0.41.2", "wasmparser", - "wasmtime-cache", "wasmtime-component-macro", "wasmtime-component-util", "wasmtime-cranelift", "wasmtime-environ", "wasmtime-fiber", - "wasmtime-jit-debug", "wasmtime-jit-icache-coherence", "wasmtime-runtime", "wasmtime-winch", - "wat", "windows-sys 0.52.0", ] @@ -11563,26 +11476,6 @@ dependencies = [ "quote", ] -[[package]] -name = "wasmtime-cache" -version = "18.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb4fc2bbf9c790a57875eba65588fa97acf57a7d784dc86d057e648d9a1ed91" -dependencies = [ - "anyhow", - "base64 0.21.4", - "bincode", - "directories-next", - "log", - "rustix 0.38.30", - "serde", - "serde_derive", - "sha2 0.10.7", - "toml 0.5.11", - "windows-sys 0.52.0", - "zstd", -] - [[package]] name = "wasmtime-component-macro" version = "18.0.2" @@ -11664,7 +11557,7 @@ dependencies = [ "serde_derive", "target-lexicon", "thiserror", - "wasm-encoder 0.41.2", + "wasm-encoder", "wasmparser", "wasmprinter", "wasmtime-component-util", @@ -11686,18 +11579,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "wasmtime-jit-debug" -version = "18.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833dae95bc7a4f9177bf93f9497419763535b74e37eb8c37be53937d3281e287" -dependencies = [ - "object", - "once_cell", - "rustix 0.38.30", - "wasmtime-versioned-export-macros", -] - [[package]] name = "wasmtime-jit-icache-coherence" version = "18.0.2" @@ -11729,11 +11610,10 @@ dependencies = [ "psm", "rustix 0.38.30", "sptr", - "wasm-encoder 0.41.2", + "wasm-encoder", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-fiber", - "wasmtime-jit-debug", "wasmtime-versioned-export-macros", "wasmtime-wmemcheck", "windows-sys 0.52.0", @@ -11840,28 +11720,6 @@ dependencies = [ "leb128", ] -[[package]] -name = "wast" -version = "201.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ef6e1ef34d7da3e2b374fd2b1a9c0227aff6cad596e1b24df9b58d0f6222faa" -dependencies = [ - "bumpalo", - "leb128", - "memchr", - "unicode-width", - "wasm-encoder 0.201.0", -] - -[[package]] -name = "wat" -version = "1.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453d5b37a45b98dee4f4cb68015fc73634d7883bbef1c65e6e9c78d454cf3f32" -dependencies = [ - "wast 201.0.0", -] - [[package]] name = "wayland-backend" version = "0.3.3" @@ -12515,7 +12373,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.41.2", + "wasm-encoder", "wasm-metadata", "wasmparser", "wit-parser 0.13.2", @@ -12534,7 +12392,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.41.2", + "wasm-encoder", "wasm-metadata", "wasmparser", "wit-parser 0.14.0", @@ -12584,7 +12442,7 @@ dependencies = [ "anyhow", "log", "thiserror", - "wast 35.0.2", + "wast", ] [[package]] @@ -13003,6 +12861,13 @@ dependencies = [ "zed_extension_api", ] +[[package]] +name = "zed_uiua" +version = "0.0.1" +dependencies = [ + "zed_extension_api", +] + [[package]] name = "zeno" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index b6434ec832..f70b05ecbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,10 @@ members = [ "crates/workspace", "crates/zed", "crates/zed_actions", + "extensions/gleam", + "extensions/uiua", + "tooling/xtask", ] default-members = ["crates/zed"] @@ -308,7 +311,6 @@ tree-sitter-scheme = { git = "https://github.com/6cdh/tree-sitter-scheme", rev = tree-sitter-svelte = { git = "https://github.com/Himujjal/tree-sitter-svelte", rev = "bd60db7d3d06f89b6ec3b287c9a6e9190b5564bd" } tree-sitter-toml = { git = "https://github.com/tree-sitter/tree-sitter-toml", rev = "342d9be207c2dba869b9967124c679b5e6fd0ebe" } tree-sitter-typescript = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "5d20856f34315b068c41edaee2ac8a100081d259" } -tree-sitter-uiua = { git = "https://github.com/shnarazk/tree-sitter-uiua", rev = "21dc2db39494585bf29a3f86d5add6e9d11a22ba" } tree-sitter-vue = { git = "https://github.com/zed-industries/tree-sitter-vue", rev = "6608d9d60c386f19d80af7d8132322fa11199c42" } tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml", rev = "f545a41f57502e1b5ddf2a6668896c1b0620f930" } tree-sitter-zig = { git = "https://github.com/maxxnino/tree-sitter-zig", rev = "0d08703e4c3f426ec61695d7617415fff97029bd" } @@ -317,7 +319,8 @@ unicase = "2.6" url = "2.2" uuid = { version = "1.1.2", features = ["v4"] } wasmparser = "0.121" -wasmtime = "18.0" +wasm-encoder = "0.41" +wasmtime = { version = "18.0", default-features = false, features = ["async", "demangle", "runtime", "cranelift", "component-model"] } wasmtime-wasi = "18.0" which = "6.0.0" wit-component = "0.20" diff --git a/crates/extension/Cargo.toml b/crates/extension/Cargo.toml index ceb1a0af3e..ce5ea85480 100644 --- a/crates/extension/Cargo.toml +++ b/crates/extension/Cargo.toml @@ -37,7 +37,8 @@ settings.workspace = true theme.workspace = true toml.workspace = true util.workspace = true -wasmtime = { workspace = true, features = ["async"] } +wasm-encoder.workspace = true +wasmtime.workspace = true wasmtime-wasi.workspace = true wasmparser.workspace = true wit-component.workspace = true diff --git a/crates/extension/src/build_extension.rs b/crates/extension/src/build_extension.rs index b7ff4512a7..8d19544571 100644 --- a/crates/extension/src/build_extension.rs +++ b/crates/extension/src/build_extension.rs @@ -6,13 +6,16 @@ use async_tar::Archive; use futures::io::BufReader; use futures::AsyncReadExt; use serde::Deserialize; +use std::mem; use std::{ env, fs, path::{Path, PathBuf}, process::{Command, Stdio}, sync::Arc, }; -use util::http::{AsyncBody, HttpClient}; +use util::http::{self, AsyncBody, HttpClient}; +use wasm_encoder::{ComponentSectionId, Encode as _, RawSection, Section as _}; +use wasmparser::Parser; use wit_component::ComponentEncoder; /// Currently, we compile with Rust's `wasm32-wasi` target, which works with WASI `preview1`. @@ -59,8 +62,11 @@ struct CargoTomlPackage { } impl ExtensionBuilder { - pub fn new(cache_dir: PathBuf, http: Arc) -> Self { - Self { cache_dir, http } + pub fn new(cache_dir: PathBuf) -> Self { + Self { + cache_dir, + http: http::client(), + } } pub async fn compile_extension( @@ -138,6 +144,10 @@ impl ExtensionBuilder { .encode() .context("failed to encode wasm component")?; + let component_bytes = self + .strip_custom_sections(&component_bytes) + .context("failed to strip debug sections from wasm component")?; + fs::write(extension_dir.join("extension.wasm"), &component_bytes) .context("failed to write extension.wasm")?; @@ -310,7 +320,7 @@ impl ExtensionBuilder { async fn install_wasi_preview1_adapter_if_needed(&self) -> Result> { let cache_path = self.cache_dir.join("wasi_snapshot_preview1.reactor.wasm"); if let Ok(content) = fs::read(&cache_path) { - if wasmparser::Parser::is_core_wasm(&content) { + if Parser::is_core_wasm(&content) { return Ok(content); } } @@ -333,7 +343,7 @@ impl ExtensionBuilder { fs::write(&cache_path, &content) .with_context(|| format!("failed to save file {}", cache_path.display()))?; - if !wasmparser::Parser::is_core_wasm(&content) { + if !Parser::is_core_wasm(&content) { bail!("downloaded wasi adapter is invalid"); } Ok(content) @@ -379,4 +389,68 @@ impl ExtensionBuilder { Ok(clang_path) } + + // This was adapted from: + // https://github.com/bytecodealliance/wasm-tools/1791a8f139722e9f8679a2bd3d8e423e55132b22/src/bin/wasm-tools/strip.rs + fn strip_custom_sections(&self, input: &Vec) -> Result> { + use wasmparser::Payload::*; + + let strip_custom_section = |name: &str| name.starts_with(".debug"); + + let mut output = Vec::new(); + let mut stack = Vec::new(); + + for payload in Parser::new(0).parse_all(input) { + let payload = payload?; + + // Track nesting depth, so that we don't mess with inner producer sections: + match payload { + Version { encoding, .. } => { + output.extend_from_slice(match encoding { + wasmparser::Encoding::Component => &wasm_encoder::Component::HEADER, + wasmparser::Encoding::Module => &wasm_encoder::Module::HEADER, + }); + } + ModuleSection { .. } | ComponentSection { .. } => { + stack.push(mem::take(&mut output)); + continue; + } + End { .. } => { + let mut parent = match stack.pop() { + Some(c) => c, + None => break, + }; + if output.starts_with(&wasm_encoder::Component::HEADER) { + parent.push(ComponentSectionId::Component as u8); + output.encode(&mut parent); + } else { + parent.push(ComponentSectionId::CoreModule as u8); + output.encode(&mut parent); + } + output = parent; + } + _ => {} + } + + match &payload { + CustomSection(c) => { + if strip_custom_section(c.name()) { + continue; + } + } + + _ => {} + } + + if let Some((id, range)) = payload.as_section() { + RawSection { + id, + data: &input[range], + } + .append_to(&mut output); + } + } + + Ok(output) + } } diff --git a/crates/extension/src/extension_store.rs b/crates/extension/src/extension_store.rs index 00f0dff8d8..33cee64fae 100644 --- a/crates/extension/src/extension_store.rs +++ b/crates/extension/src/extension_store.rs @@ -193,7 +193,7 @@ impl ExtensionStore { extension_index: Default::default(), installed_dir, index_path, - builder: Arc::new(ExtensionBuilder::new(build_dir, http_client.clone())), + builder: Arc::new(ExtensionBuilder::new(build_dir)), outstanding_operations: Default::default(), modified_extensions: Default::default(), reload_complete_senders: Vec::new(), @@ -545,7 +545,7 @@ impl ExtensionStore { builder .compile_extension( &extension_source_path, - CompileExtensionOptions { release: true }, + CompileExtensionOptions { release: false }, ) .await } diff --git a/crates/extension/src/extension_store_test.rs b/crates/extension/src/extension_store_test.rs index 09aee3f878..4443cf022c 100644 --- a/crates/extension/src/extension_store_test.rs +++ b/crates/extension/src/extension_store_test.rs @@ -7,10 +7,7 @@ use collections::BTreeMap; use fs::{FakeFs, Fs, RealFs}; use futures::{io::BufReader, AsyncReadExt, StreamExt}; use gpui::{Context, TestAppContext}; -use language::{ - Language, LanguageConfig, LanguageMatcher, LanguageRegistry, LanguageServerBinaryStatus, - LanguageServerName, -}; +use language::{LanguageMatcher, LanguageRegistry, LanguageServerBinaryStatus, LanguageServerName}; use node_runtime::FakeNodeRuntime; use parking_lot::Mutex; use project::Project; @@ -573,19 +570,6 @@ async fn test_extension_store_with_gleam_extension(cx: &mut TestAppContext) { }) .await .unwrap(); - project.update(cx, |project, cx| { - project.set_language_for_buffer( - &buffer, - Arc::new(Language::new( - LanguageConfig { - name: "Gleam".into(), - ..Default::default() - }, - None, - )), - cx, - ) - }); let fake_server = fake_servers.next().await.unwrap(); let expected_server_path = extensions_dir.join("work/gleam/gleam-v1.2.3/gleam"); diff --git a/crates/extension/src/wasm_host.rs b/crates/extension/src/wasm_host.rs index 84f2cf698a..3d8e0b53e0 100644 --- a/crates/extension/src/wasm_host.rs +++ b/crates/extension/src/wasm_host.rs @@ -3,7 +3,7 @@ use anyhow::{anyhow, bail, Context as _, Result}; use async_compression::futures::bufread::GzipDecoder; use async_tar::Archive; use async_trait::async_trait; -use fs::Fs; +use fs::{normalize_path, Fs}; use futures::{ channel::{ mpsc::{self, UnboundedSender}, @@ -72,8 +72,6 @@ type ExtensionCall = Box< static WASM_ENGINE: OnceLock = OnceLock::new(); -const EXTENSION_WORK_DIR_PATH: &str = "/zed/work"; - impl WasmHost { pub fn new( fs: Arc, @@ -181,11 +179,12 @@ impl WasmHost { .await .context("failed to create extension work dir")?; - let work_dir_preopen = Dir::open_ambient_dir(extension_work_dir, ambient_authority()) + let work_dir_preopen = Dir::open_ambient_dir(&extension_work_dir, ambient_authority()) .context("failed to preopen extension work directory")?; let current_dir_preopen = work_dir_preopen .try_clone() .context("failed to preopen extension current directory")?; + let extension_work_dir = extension_work_dir.to_string_lossy(); let perms = wasi::FilePerms::all(); let dir_perms = wasi::DirPerms::all(); @@ -193,26 +192,24 @@ impl WasmHost { Ok(wasi::WasiCtxBuilder::new() .inherit_stdio() .preopened_dir(current_dir_preopen, dir_perms, perms, ".") - .preopened_dir(work_dir_preopen, dir_perms, perms, EXTENSION_WORK_DIR_PATH) - .env("PWD", EXTENSION_WORK_DIR_PATH) - .env("RUST_BACKTRACE", "1") + .preopened_dir(work_dir_preopen, dir_perms, perms, &extension_work_dir) + .env("PWD", &extension_work_dir) + .env("RUST_BACKTRACE", "full") .build()) } pub fn path_from_extension(&self, id: &Arc, path: &Path) -> PathBuf { - self.writeable_path_from_extension(id, path) - .unwrap_or_else(|| path.to_path_buf()) + let extension_work_dir = self.work_dir.join(id.as_ref()); + normalize_path(&extension_work_dir.join(path)) } - pub fn writeable_path_from_extension(&self, id: &Arc, path: &Path) -> Option { - let path = path.strip_prefix(EXTENSION_WORK_DIR_PATH).unwrap_or(path); - if path.is_relative() { - let mut result = self.work_dir.clone(); - result.push(id.as_ref()); - result.extend(path); - Some(result) + pub fn writeable_path_from_extension(&self, id: &Arc, path: &Path) -> Result { + let extension_work_dir = self.work_dir.join(id.as_ref()); + let path = normalize_path(&extension_work_dir.join(path)); + if path.starts_with(&extension_work_dir) { + Ok(path) } else { - None + Err(anyhow!("cannot write to path {}", path.display())) } } } @@ -252,13 +249,6 @@ impl WasmExtension { } } -impl WasmState { - pub fn writeable_path_from_extension(&self, path: &Path) -> Option { - self.host - .writeable_path_from_extension(&self.manifest.id, path) - } -} - #[async_trait] impl wit::HostWorktree for WasmState { async fn read_text_file( @@ -273,6 +263,26 @@ impl wit::HostWorktree for WasmState { .map_err(|error| error.to_string())) } + async fn shell_env( + &mut self, + delegate: Resource>, + ) -> wasmtime::Result { + let delegate = self.table.get(&delegate)?; + Ok(delegate.shell_env().await.into_iter().collect()) + } + + async fn which( + &mut self, + delegate: Resource>, + binary_name: String, + ) -> wasmtime::Result> { + let delegate = self.table.get(&delegate)?; + Ok(delegate + .which(binary_name.as_ref()) + .await + .map(|path| path.to_string_lossy().to_string())) + } + fn drop(&mut self, _worktree: Resource) -> Result<()> { // we only ever hand out borrows of worktrees Ok(()) @@ -395,8 +405,8 @@ impl wit::ExtensionImports for WasmState { this.host.fs.create_dir(&extension_work_dir).await?; let destination_path = this - .writeable_path_from_extension(&path) - .ok_or_else(|| anyhow!("cannot write to path {:?}", path))?; + .host + .writeable_path_from_extension(&this.manifest.id, &path)?; let mut response = this .host diff --git a/crates/extension_api/wit/extension.wit b/crates/extension_api/wit/extension.wit index 1b00874698..2afa599bc2 100644 --- a/crates/extension_api/wit/extension.wit +++ b/crates/extension_api/wit/extension.wit @@ -61,14 +61,18 @@ world extension { /// 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); + type env-vars = list>; + record command { command: string, args: list, - env: list>, + env: env-vars, } resource worktree { read-text-file: func(path: string) -> result; + which: func(binary-name: string) -> option; + shell-env: func() -> env-vars; } record language-server-config { diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index ee137abe43..2360aecd78 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -40,7 +40,7 @@ use smol::future::FutureExt as _; use std::{ any::Any, cell::RefCell, - ffi::OsString, + ffi::OsStr, fmt::Debug, hash::Hash, mem, @@ -277,7 +277,8 @@ pub trait LspAdapterDelegate: Send + Sync { fn http_client(&self) -> Arc; fn update_status(&self, language: LanguageServerName, status: LanguageServerBinaryStatus); - async fn which_command(&self, command: OsString) -> Option<(PathBuf, HashMap)>; + async fn which(&self, command: &OsStr) -> Option; + async fn shell_env(&self) -> HashMap; async fn read_text_file(&self, path: PathBuf) -> Result; } diff --git a/crates/languages/Cargo.toml b/crates/languages/Cargo.toml index facb1460c2..35c4d3f01a 100644 --- a/crates/languages/Cargo.toml +++ b/crates/languages/Cargo.toml @@ -76,7 +76,6 @@ tree-sitter-scheme.workspace = true tree-sitter-svelte.workspace = true tree-sitter-toml.workspace = true tree-sitter-typescript.workspace = true -tree-sitter-uiua.workspace = true tree-sitter-vue.workspace = true tree-sitter-yaml.workspace = true tree-sitter-zig.workspace = true diff --git a/crates/languages/src/go.rs b/crates/languages/src/go.rs index dc0abf1747..4710facc4b 100644 --- a/crates/languages/src/go.rs +++ b/crates/languages/src/go.rs @@ -58,7 +58,8 @@ impl super::LspAdapter for GoLspAdapter { &self, delegate: &dyn LspAdapterDelegate, ) -> Option { - let (path, env) = delegate.which_command(OsString::from("gopls")).await?; + let env = delegate.shell_env().await; + let path = delegate.which("gopls".as_ref()).await?; Some(LanguageServerBinary { path, arguments: server_binary_arguments(), diff --git a/crates/languages/src/lib.rs b/crates/languages/src/lib.rs index 5cd46edaea..07c298e057 100644 --- a/crates/languages/src/lib.rs +++ b/crates/languages/src/lib.rs @@ -39,7 +39,6 @@ mod tailwind; mod terraform; mod toml; mod typescript; -mod uiua; mod vue; mod yaml; mod zig; @@ -114,7 +113,6 @@ pub fn init( ("toml", tree_sitter_toml::language()), ("tsx", tree_sitter_typescript::language_tsx()), ("typescript", tree_sitter_typescript::language_typescript()), - ("uiua", tree_sitter_uiua::language()), ("vue", tree_sitter_vue::language()), ("yaml", tree_sitter_yaml::language()), ("zig", tree_sitter_zig::language()), @@ -344,7 +342,6 @@ pub fn init( "vue", vec![Arc::new(vue::VueLspAdapter::new(node_runtime.clone()))] ); - language!("uiua", vec![Arc::new(uiua::UiuaLanguageServer {})]); language!("proto"); language!("terraform", vec![Arc::new(terraform::TerraformLspAdapter)]); language!( diff --git a/crates/languages/src/zig.rs b/crates/languages/src/zig.rs index ab0e08983e..a8ba262905 100644 --- a/crates/languages/src/zig.rs +++ b/crates/languages/src/zig.rs @@ -7,7 +7,6 @@ use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; use lsp::LanguageServerBinary; use smol::fs; use std::env::consts::{ARCH, OS}; -use std::ffi::OsString; use std::{any::Any, path::PathBuf}; use util::async_maybe; use util::github::latest_github_release; @@ -45,7 +44,8 @@ impl LspAdapter for ZlsAdapter { &self, delegate: &dyn LspAdapterDelegate, ) -> Option { - let (path, env) = delegate.which_command(OsString::from("zls")).await?; + let env = delegate.shell_env().await; + let path = delegate.which("zls".as_ref()).await?; Some(LanguageServerBinary { path, arguments: vec![], diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 7f3dd153b3..37c6c96dcb 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -72,7 +72,7 @@ use std::{ cmp::{self, Ordering}, convert::TryInto, env, - ffi::OsString, + ffi::OsStr, hash::Hash, mem, num::NonZeroU32, @@ -9390,6 +9390,7 @@ struct ProjectLspAdapterDelegate { fs: Arc, http_client: Arc, language_registry: Arc, + shell_env: Mutex>>, } impl ProjectLspAdapterDelegate { @@ -9400,8 +9401,21 @@ impl ProjectLspAdapterDelegate { fs: project.fs.clone(), http_client: project.client.http_client(), language_registry: project.languages.clone(), + shell_env: Default::default(), }) } + + async fn load_shell_env(&self) { + let worktree_abs_path = self.worktree.abs_path(); + let shell_env = load_shell_environment(&worktree_abs_path) + .await + .with_context(|| { + format!("failed to determine load login shell environment in {worktree_abs_path:?}") + }) + .log_err() + .unwrap_or_default(); + *self.shell_env.lock() = Some(shell_env); + } } #[async_trait] @@ -9416,32 +9430,20 @@ impl LspAdapterDelegate for ProjectLspAdapterDelegate { self.http_client.clone() } - async fn which_command(&self, command: OsString) -> Option<(PathBuf, HashMap)> { + async fn shell_env(&self) -> HashMap { + self.load_shell_env().await; + self.shell_env.lock().as_ref().cloned().unwrap_or_default() + } + + async fn which(&self, command: &OsStr) -> Option { let worktree_abs_path = self.worktree.abs_path(); - - let shell_env = load_shell_environment(&worktree_abs_path) - .await - .with_context(|| { - format!("failed to determine load login shell environment in {worktree_abs_path:?}") - }) - .log_err(); - - if let Some(shell_env) = shell_env { - let shell_path = shell_env.get("PATH"); - match which::which_in(&command, shell_path, &worktree_abs_path) { - Ok(command_path) => Some((command_path, shell_env)), - Err(error) => { - log::warn!( - "failed to determine path for command {:?} in shell PATH {:?}: {error}", - command.to_string_lossy(), - shell_path.map(String::as_str).unwrap_or("") - ); - None - } - } - } else { - None - } + self.load_shell_env().await; + let shell_path = self + .shell_env + .lock() + .as_ref() + .and_then(|shell_env| shell_env.get("PATH").cloned()); + which::which_in(command, shell_path.as_ref(), &worktree_abs_path).ok() } fn update_status( diff --git a/extensions/gleam/.gitignore b/extensions/.gitignore similarity index 100% rename from extensions/gleam/.gitignore rename to extensions/.gitignore diff --git a/extensions/uiua/Cargo.toml b/extensions/uiua/Cargo.toml new file mode 100644 index 0000000000..107b842d58 --- /dev/null +++ b/extensions/uiua/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "zed_uiua" +version = "0.0.1" +edition = "2021" +publish = false +license = "Apache-2.0" + +[lints] +workspace = true + +[dependencies] +zed_extension_api = { path = "../../crates/extension_api" } + +[lib] +path = "src/uiua.rs" +crate-type = ["cdylib"] diff --git a/extensions/uiua/extension.toml b/extensions/uiua/extension.toml new file mode 100644 index 0000000000..e8448c425c --- /dev/null +++ b/extensions/uiua/extension.toml @@ -0,0 +1,13 @@ +id = "uiua" +name = "Uiua" +description = "Uiua support for Zed" +version = "0.0.1" +authors = ["Max Brunsfeld "] + +[language_servers.uiua] +name = "Uiua LSP" +language = "Uiua" + +[grammars.uiua] +repository = "https://github.com/shnarazk/tree-sitter-uiua" +commit = "21dc2db39494585bf29a3f86d5add6e9d11a22ba" diff --git a/crates/languages/src/uiua/config.toml b/extensions/uiua/languages/uiua/config.toml similarity index 100% rename from crates/languages/src/uiua/config.toml rename to extensions/uiua/languages/uiua/config.toml diff --git a/crates/languages/src/uiua/highlights.scm b/extensions/uiua/languages/uiua/highlights.scm similarity index 100% rename from crates/languages/src/uiua/highlights.scm rename to extensions/uiua/languages/uiua/highlights.scm diff --git a/crates/languages/src/uiua/indents.scm b/extensions/uiua/languages/uiua/indents.scm similarity index 100% rename from crates/languages/src/uiua/indents.scm rename to extensions/uiua/languages/uiua/indents.scm diff --git a/extensions/uiua/src/uiua.rs b/extensions/uiua/src/uiua.rs new file mode 100644 index 0000000000..f9e80767cb --- /dev/null +++ b/extensions/uiua/src/uiua.rs @@ -0,0 +1,27 @@ +use zed_extension_api::{self as zed, Result}; + +struct UiuaExtension; + +impl zed::Extension for UiuaExtension { + fn new() -> Self { + Self + } + + fn language_server_command( + &mut self, + _config: zed::LanguageServerConfig, + worktree: &zed::Worktree, + ) -> Result { + let path = worktree + .which("uiua") + .ok_or_else(|| "uiua is not installed".to_string())?; + + Ok(zed::Command { + command: path, + args: vec!["lsp".to_string()], + env: Default::default(), + }) + } +} + +zed::register_extension!(UiuaExtension); diff --git a/script/setup-wasm b/script/setup-wasm deleted file mode 100755 index a3674ba833..0000000000 --- a/script/setup-wasm +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -set -eu - -WASI_ADAPTER_URL="https://github.com/bytecodealliance/wasmtime/releases/download/v18.0.2/wasi_snapshot_preview1.reactor.wasm" -WASI_SDK_URL="https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-21/wasi-sdk-21.0-macos.tar.gz" - -echo "Downloading WASI adapter: $WASI_ADAPTER_URL" -curl -L $WASI_ADAPTER_URL -o target/wasi_snapshot_preview1.reactor.wasm - -echo "Downloading WASI SDK: $WASI_SDK_URL" -mkdir -p target/wasi-sdk.archive -curl -L $WASI_SDK_URL | tar -xz - -C target/wasi-sdk.archive -rm -rf target/wasi-sdk/ -mv -f target/wasi-sdk.archive/wasi-sdk-21.0/ target/wasi-sdk