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