From a906c5f6927d51f7f27ac72a5a400143841cae12 Mon Sep 17 00:00:00 2001 From: "zed-zippy[bot]" <234243425+zed-zippy[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 13:10:27 +0000 Subject: [PATCH] language_extension: Handle prefixed WASI windows paths in extension spawning (#44477) (cherry-pick to preview) (#44481) Cherry-pick of #44477 to preview ---- Closes https://github.com/zed-industries/zed/issues/12013 Release Notes: - Fixed some wasm language extensions failing to spawn on windows Co-authored-by: Lukas Wirth --- .../src/extension_lsp_adapter.rs | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/crates/language_extension/src/extension_lsp_adapter.rs b/crates/language_extension/src/extension_lsp_adapter.rs index 920bf07f5d1aa5702487369d93abceb0b37fd976..aead46d956dd6a53e293dc15bd4011451d9a7a30 100644 --- a/crates/language_extension/src/extension_lsp_adapter.rs +++ b/crates/language_extension/src/extension_lsp_adapter.rs @@ -1,5 +1,5 @@ use std::ops::Range; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Arc; use anyhow::{Context as _, Result}; @@ -174,7 +174,32 @@ impl DynLspInstaller for ExtensionLspAdapter { ) .await?; - let path = self.extension.path_from_extension(command.command.as_ref()); + // on windows, extensions might produce weird paths + // that start with a leading slash due to WASI + // requiring that for PWD and friends so account for + // that here and try to transform those paths back + // to windows paths + // + // if we don't do this, std will interpret the path as relative, + // which changes join behavior + let command_path: &Path = if cfg!(windows) + && let Some(command) = command.command.to_str() + { + let mut chars = command.chars(); + if chars.next().is_some_and(|c| c == '/') + && chars.next().is_some_and(|c| c.is_ascii_alphabetic()) + && chars.next().is_some_and(|c| c == ':') + && chars.next().is_some_and(|c| c == '\\' || c == '/') + { + // looks like a windows path with a leading slash, so strip it + command.strip_prefix('/').unwrap().as_ref() + } else { + command.as_ref() + } + } else { + command.command.as_ref() + }; + let path = self.extension.path_from_extension(command_path); // 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 @@ -193,7 +218,32 @@ impl DynLspInstaller for ExtensionLspAdapter { Ok(LanguageServerBinary { path, - arguments: command.args.into_iter().map(|arg| arg.into()).collect(), + arguments: command + .args + .into_iter() + .map(|arg| { + // on windows, extensions might produce weird paths + // that start with a leading slash due to WASI + // requiring that for PWD and friends so account for + // that here and try to transform those paths back + // to windows paths + if cfg!(windows) { + let mut chars = arg.chars(); + if chars.next().is_some_and(|c| c == '/') + && chars.next().is_some_and(|c| c.is_ascii_alphabetic()) + && chars.next().is_some_and(|c| c == ':') + && chars.next().is_some_and(|c| c == '\\' || c == '/') + { + // looks like a windows path with a leading slash, so strip it + arg.strip_prefix('/').unwrap().into() + } else { + arg.into() + } + } else { + arg.into() + } + }) + .collect(), env: Some(command.env.into_iter().collect()), }) })