diff --git a/crates/language_extension/src/extension_lsp_adapter.rs b/crates/language_extension/src/extension_lsp_adapter.rs index 64fa12074b0344702888d87e6af8975049c3adc1..269aac1f2a065815077f9e2cce6b2215ac59bb92 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()), }) })