Detailed changes
@@ -1723,6 +1723,10 @@ impl ProtoClient for Client {
fn is_via_collab(&self) -> bool {
true
}
+
+ fn has_wsl_interop(&self) -> bool {
+ false
+ }
}
/// prefix for the zed:// url scheme
@@ -453,7 +453,9 @@ impl AgentServerStore {
.clone()
.and_then(|settings| settings.custom_command()),
http_client: http_client.clone(),
- is_remote: downstream_client.is_some(),
+ no_browser: downstream_client
+ .as_ref()
+ .is_some_and(|(_, client)| !client.has_wsl_interop()),
}),
);
self.external_agents.insert(
@@ -1355,7 +1357,7 @@ struct LocalCodex {
project_environment: Entity<ProjectEnvironment>,
http_client: Arc<dyn HttpClient>,
custom_command: Option<AgentServerCommand>,
- is_remote: bool,
+ no_browser: bool,
}
impl ExternalAgentServer for LocalCodex {
@@ -1375,7 +1377,7 @@ impl ExternalAgentServer for LocalCodex {
.map(|root_dir| Path::new(root_dir))
.unwrap_or(paths::home_dir())
.into();
- let is_remote = self.is_remote;
+ let no_browser = self.no_browser;
cx.spawn(async move |cx| {
let mut env = project_environment
@@ -1388,7 +1390,7 @@ impl ExternalAgentServer for LocalCodex {
})?
.await
.unwrap_or_default();
- if is_remote {
+ if no_browser {
env.insert("NO_BROWSER".to_owned(), "1".to_owned());
}
@@ -43,7 +43,6 @@ urlencoding.workspace = true
util.workspace = true
which.workspace = true
-
[dev-dependencies]
gpui = { workspace = true, features = ["test-support"] }
fs = { workspace = true, features = ["test-support"] }
@@ -328,8 +328,15 @@ impl RemoteClient {
let (incoming_tx, incoming_rx) = mpsc::unbounded::<Envelope>();
let (connection_activity_tx, connection_activity_rx) = mpsc::channel::<()>(1);
- let client =
- cx.update(|cx| ChannelClient::new(incoming_rx, outgoing_tx, cx, "client"))?;
+ let client = cx.update(|cx| {
+ ChannelClient::new(
+ incoming_rx,
+ outgoing_tx,
+ cx,
+ "client",
+ remote_connection.has_wsl_interop(),
+ )
+ })?;
let path_style = remote_connection.path_style();
let this = cx.new(|_| Self {
@@ -420,8 +427,9 @@ impl RemoteClient {
outgoing_tx: mpsc::UnboundedSender<Envelope>,
cx: &App,
name: &'static str,
+ has_wsl_interop: bool,
) -> AnyProtoClient {
- ChannelClient::new(incoming_rx, outgoing_tx, cx, name).into()
+ ChannelClient::new(incoming_rx, outgoing_tx, cx, name, has_wsl_interop).into()
}
pub fn shutdown_processes<T: RequestMessage>(
@@ -921,8 +929,8 @@ impl RemoteClient {
});
let (outgoing_tx, _) = mpsc::unbounded::<Envelope>();
let (_, incoming_rx) = mpsc::unbounded::<Envelope>();
- let server_client =
- server_cx.update(|cx| ChannelClient::new(incoming_rx, outgoing_tx, cx, "fake-server"));
+ let server_client = server_cx
+ .update(|cx| ChannelClient::new(incoming_rx, outgoing_tx, cx, "fake-server", false));
let connection: Arc<dyn RemoteConnection> = Arc::new(fake::FakeRemoteConnection {
connection_options: opts.clone(),
server_cx: fake::SendableCx::new(server_cx),
@@ -1140,6 +1148,7 @@ pub trait RemoteConnection: Send + Sync {
fn path_style(&self) -> PathStyle;
fn shell(&self) -> String;
fn default_system_shell(&self) -> String;
+ fn has_wsl_interop(&self) -> bool;
#[cfg(any(test, feature = "test-support"))]
fn simulate_disconnect(&self, _: &AsyncApp) {}
@@ -1188,6 +1197,7 @@ struct ChannelClient {
name: &'static str,
task: Mutex<Task<Result<()>>>,
remote_started: Signal<()>,
+ has_wsl_interop: bool,
}
impl ChannelClient {
@@ -1196,6 +1206,7 @@ impl ChannelClient {
outgoing_tx: mpsc::UnboundedSender<Envelope>,
cx: &App,
name: &'static str,
+ has_wsl_interop: bool,
) -> Arc<Self> {
Arc::new_cyclic(|this| Self {
outgoing_tx: Mutex::new(outgoing_tx),
@@ -1211,6 +1222,7 @@ impl ChannelClient {
&cx.to_async(),
)),
remote_started: Signal::new(cx),
+ has_wsl_interop,
})
}
@@ -1489,6 +1501,10 @@ impl ProtoClient for ChannelClient {
fn is_via_collab(&self) -> bool {
false
}
+
+ fn has_wsl_interop(&self) -> bool {
+ self.has_wsl_interop
+ }
}
#[cfg(any(test, feature = "test-support"))]
@@ -1652,6 +1668,10 @@ mod fake {
fn default_system_shell(&self) -> String {
"sh".to_owned()
}
+
+ fn has_wsl_interop(&self) -> bool {
+ false
+ }
}
pub(super) struct Delegate;
@@ -131,11 +131,7 @@ async fn build_remote_server_from_source(
let build_remote_server =
std::env::var("ZED_BUILD_REMOTE_SERVER").unwrap_or("nocompress".into());
- if build_remote_server == "false"
- || build_remote_server == "no"
- || build_remote_server == "off"
- || build_remote_server == "0"
- {
+ if let "false" | "no" | "off" | "0" = &*build_remote_server {
return Ok(None);
}
@@ -394,6 +394,10 @@ impl RemoteConnection for SshRemoteConnection {
fn path_style(&self) -> PathStyle {
self.ssh_path_style
}
+
+ fn has_wsl_interop(&self) -> bool {
+ false
+ }
}
impl SshRemoteConnection {
@@ -47,6 +47,7 @@ pub(crate) struct WslRemoteConnection {
shell: String,
shell_kind: ShellKind,
default_system_shell: String,
+ has_wsl_interop: bool,
connection_options: WslConnectionOptions,
}
@@ -71,6 +72,7 @@ impl WslRemoteConnection {
shell: String::new(),
shell_kind: ShellKind::Posix,
default_system_shell: String::from("/bin/sh"),
+ has_wsl_interop: false,
};
delegate.set_status(Some("Detecting WSL environment"), cx);
this.shell = this
@@ -79,6 +81,15 @@ impl WslRemoteConnection {
.context("failed detecting shell")?;
log::info!("Remote shell discovered: {}", this.shell);
this.shell_kind = ShellKind::new(&this.shell, false);
+ this.has_wsl_interop = this.detect_has_wsl_interop().await.unwrap_or_default();
+ log::info!(
+ "Remote has wsl interop {}",
+ if this.has_wsl_interop {
+ "enabled"
+ } else {
+ "disabled"
+ }
+ );
this.platform = this
.detect_platform()
.await
@@ -115,6 +126,14 @@ impl WslRemoteConnection {
.unwrap_or_else(|| "/bin/sh".to_string()))
}
+ async fn detect_has_wsl_interop(&self) -> Result<bool> {
+ Ok(self
+ .run_wsl_command_with_output("cat", &["/proc/sys/fs/binfmt_misc/WSLInterop"])
+ .await
+ .inspect_err(|err| log::error!("Failed to detect wsl interop: {err}"))?
+ .contains("enabled"))
+ }
+
async fn windows_path_to_wsl_path(&self, source: &Path) -> Result<String> {
windows_path_to_wsl_path_impl(&self.connection_options, source).await
}
@@ -317,6 +336,7 @@ impl RemoteConnection for WslRemoteConnection {
proxy_args.push(format!("{}={}", env_var, value));
}
}
+
proxy_args.push(remote_binary_path.display(PathStyle::Posix).into_owned());
proxy_args.push("proxy".to_owned());
proxy_args.push("--identifier".to_owned());
@@ -489,6 +509,10 @@ impl RemoteConnection for WslRemoteConnection {
fn default_system_shell(&self) -> String {
self.default_system_shell.clone()
}
+
+ fn has_wsl_interop(&self) -> bool {
+ self.has_wsl_interop
+ }
}
/// `wslpath` is a executable available in WSL, it's a linux binary.
@@ -199,6 +199,7 @@ fn start_server(
listeners: ServerListeners,
log_rx: Receiver<Vec<u8>>,
cx: &mut App,
+ is_wsl_interop: bool,
) -> AnyProtoClient {
// This is the server idle timeout. If no connection comes in this timeout, the server will shut down.
const IDLE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10 * 60);
@@ -318,7 +319,7 @@ fn start_server(
})
.detach();
- RemoteClient::proto_client_from_channels(incoming_rx, outgoing_tx, cx, "server")
+ RemoteClient::proto_client_from_channels(incoming_rx, outgoing_tx, cx, "server", is_wsl_interop)
}
fn init_paths() -> anyhow::Result<()> {
@@ -407,8 +408,15 @@ pub fn execute_run(
HeadlessProject::init(cx);
+ let is_wsl_interop = if cfg!(target_os = "linux") {
+ // See: https://learn.microsoft.com/en-us/windows/wsl/filesystems#disable-interoperability
+ matches!(std::fs::read_to_string("/proc/sys/fs/binfmt_misc/WSLInterop"), Ok(s) if s.contains("enabled"))
+ } else {
+ false
+ };
+
log::info!("gpui app started, initializing server");
- let session = start_server(listeners, log_rx, cx);
+ let session = start_server(listeners, log_rx, cx, is_wsl_interop);
GitHostingProviderRegistry::set_global(git_hosting_provider_registry, cx);
git_hosting_providers::init(cx);
@@ -59,6 +59,7 @@ pub trait ProtoClient: Send + Sync {
fn message_handler_set(&self) -> &parking_lot::Mutex<ProtoMessageHandlerSet>;
fn is_via_collab(&self) -> bool;
+ fn has_wsl_interop(&self) -> bool;
}
#[derive(Default)]
@@ -510,6 +511,10 @@ impl AnyProtoClient {
},
);
}
+
+ pub fn has_wsl_interop(&self) -> bool {
+ self.0.client.has_wsl_interop()
+ }
}
fn to_any_envelope<T: EnvelopedMessage>(