language_extension: Handle prefixed WASI windows paths in extension spawning (#44477)

Closes https://github.com/zed-industries/zed/issues/12013

Release Notes:

- Fixed some wasm language extensions failing to spawn on windows
This commit is contained in:
Lukas Wirth 2025-12-09 13:22:57 +01:00 committed by GitHub
parent 660234fed2
commit b79d92d1c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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()),
})
})