Detailed changes
@@ -27,6 +27,7 @@ fn build_grouped_entries(store: &ReplStore, worktree_id: WorktreeId) -> Vec<Kern
let mut python_envs = Vec::new();
let mut jupyter_kernels = Vec::new();
+ let mut wsl_kernels = Vec::new();
let mut remote_kernels = Vec::new();
for spec in store.kernel_specifications_for_worktree(worktree_id) {
@@ -59,14 +60,18 @@ fn build_grouped_entries(store: &ReplStore, worktree_id: WorktreeId) -> Vec<Kern
is_recommended,
});
}
- KernelSpecification::JupyterServer(_)
- | KernelSpecification::SshRemote(_)
- | KernelSpecification::WslRemote(_) => {
+ KernelSpecification::JupyterServer(_) | KernelSpecification::SshRemote(_) => {
remote_kernels.push(KernelPickerEntry::Kernel {
spec: spec.clone(),
is_recommended,
});
}
+ KernelSpecification::WslRemote(_) => {
+ wsl_kernels.push(KernelPickerEntry::Kernel {
+ spec: spec.clone(),
+ is_recommended,
+ });
+ }
}
}
@@ -105,6 +110,12 @@ fn build_grouped_entries(store: &ReplStore, worktree_id: WorktreeId) -> Vec<Kern
entries.extend(jupyter_kernels);
}
+ // WSL Kernels section
+ if !wsl_kernels.is_empty() {
+ entries.push(KernelPickerEntry::SectionHeader("WSL Kernels".into()));
+ entries.extend(wsl_kernels);
+ }
+
// Remote section
if !remote_kernels.is_empty() {
entries.push(KernelPickerEntry::SectionHeader("Remote Servers".into()));
@@ -325,10 +336,10 @@ impl PickerDelegate for KernelPickerDelegate {
let subtitle = match spec {
KernelSpecification::Jupyter(_) => None,
+ KernelSpecification::WslRemote(_) => Some(spec.path().to_string()),
KernelSpecification::PythonEnv(_)
| KernelSpecification::JupyterServer(_)
- | KernelSpecification::SshRemote(_)
- | KernelSpecification::WslRemote(_) => {
+ | KernelSpecification::SshRemote(_) => {
let env_kind = spec.environment_kind_label();
let path = spec.path();
match env_kind {
@@ -9,6 +9,7 @@ pub use native_kernel::*;
mod remote_kernels;
use project::{Project, ProjectPath, Toolchains, WorktreeId};
+use remote::RemoteConnectionOptions;
pub use remote_kernels::*;
mod ssh_kernel;
@@ -238,7 +239,7 @@ impl KernelSpecification {
Self::PythonEnv(spec) => spec.name.clone().into(),
Self::JupyterServer(spec) => spec.name.clone().into(),
Self::SshRemote(spec) => spec.name.clone().into(),
- Self::WslRemote(spec) => spec.name.clone().into(),
+ Self::WslRemote(spec) => spec.kernelspec.display_name.clone().into(),
}
}
@@ -262,7 +263,7 @@ impl KernelSpecification {
Self::PythonEnv(spec) => spec.path.to_string_lossy().into_owned(),
Self::JupyterServer(spec) => spec.url.to_string(),
Self::SshRemote(spec) => spec.path.to_string(),
- Self::WslRemote(_) => "WSL".to_string(),
+ Self::WslRemote(spec) => spec.distro.clone(),
})
}
@@ -348,7 +349,16 @@ pub fn python_env_kernel_specifications(
) -> impl Future<Output = Result<Vec<KernelSpecification>>> + use<> {
let python_language = LanguageName::new_static("Python");
let is_remote = project.read(cx).is_remote();
- log::info!("python_env_kernel_specifications: is_remote: {}", is_remote);
+ let wsl_distro = project
+ .read(cx)
+ .remote_connection_options(cx)
+ .and_then(|opts| {
+ if let RemoteConnectionOptions::Wsl(wsl) = opts {
+ Some(wsl.distro_name)
+ } else {
+ None
+ }
+ });
let toolchains = project.read(cx).available_toolchains(
ProjectPath {
@@ -383,6 +393,7 @@ pub fn python_env_kernel_specifications(
.flatten()
.chain(toolchains.toolchains)
.map(|toolchain| {
+ let wsl_distro = wsl_distro.clone();
background_executor.spawn(async move {
// For remote projects, we assume python is available assuming toolchain is reported.
// We can skip the `ipykernel` check or run it remotely.
@@ -390,10 +401,6 @@ pub fn python_env_kernel_specifications(
// `new_smol_command` runs locally. We need to run remotely if `is_remote`.
if is_remote {
- log::info!(
- "python_env_kernel_specifications: returning SshRemote for toolchain {}",
- toolchain.name
- );
let default_kernelspec = JupyterKernelspec {
argv: vec![
toolchain.path.to_string(),
@@ -409,6 +416,22 @@ pub fn python_env_kernel_specifications(
env: None,
};
+ if let Some(distro) = wsl_distro {
+ log::debug!(
+ "python_env_kernel_specifications: returning WslRemote for toolchain {}",
+ toolchain.name
+ );
+ return Some(KernelSpecification::WslRemote(WslKernelSpecification {
+ name: toolchain.name.to_string(),
+ kernelspec: default_kernelspec,
+ distro,
+ }));
+ }
+
+ log::debug!(
+ "python_env_kernel_specifications: returning SshRemote for toolchain {}",
+ toolchain.name
+ );
return Some(KernelSpecification::SshRemote(
SshRemoteKernelSpecification {
name: format!("Remote {}", toolchain.name),
@@ -274,7 +274,23 @@ impl WslRunningKernel {
cd_command, set_env_command, arg_string, arg_string, arg_string, arg_string
)
} else {
- quote_posix_shell_arguments(&kernel_args)?
+ let args_string = quote_posix_shell_arguments(&resolved_argv)?;
+
+ let cd_command = if let Some(wd) = wsl_working_directory.as_ref() {
+ let quoted_wd = shlex::try_quote(wd)
+ .map(|quoted| quoted.into_owned())?;
+ format!("cd {quoted_wd} && ")
+ } else {
+ String::new()
+ };
+
+ let env_prefix_inline = if !env_assignments.is_empty() {
+ format!("env {} ", env_assignments.join(" "))
+ } else {
+ String::new()
+ };
+
+ format!("{cd_command}exec {env_prefix_inline}{args_string}")
};
cmd.arg("bash")
@@ -578,8 +594,20 @@ pub async fn wsl_kernel_specifications(
})
})
.collect::<Vec<_>>();
+ } else if let Err(e) =
+ serde_json::from_str::<LocalKernelSpecsResponse>(&json_str)
+ {
+ log::error!(
+ "wsl_kernel_specifications parse error: {} \nJSON: {}",
+ e,
+ json_str
+ );
}
+ } else {
+ log::error!("wsl_kernel_specifications command failed");
}
+ } else if let Err(e) = output {
+ log::error!("wsl_kernel_specifications command execution failed: {}", e);
}
Vec::new()
@@ -8,6 +8,7 @@ use gpui::{App, Context, Entity, EntityId, Global, SharedString, Subscription, T
use jupyter_websocket_client::RemoteServer;
use language::{Language, LanguageName};
use project::{Fs, Project, ProjectPath, WorktreeId};
+use remote::RemoteConnectionOptions;
use settings::{Settings, SettingsStore};
use util::rel_path::RelPath;
@@ -144,6 +145,14 @@ impl ReplStore {
cx: &mut Context<Self>,
) -> Task<Result<()>> {
let is_remote = project.read(cx).is_remote();
+ // WSL does require access to global kernel specs, so we only exclude remote worktrees that aren't WSL.
+ // TODO: a better way to handle WSL vs SSH/remote projects,
+ let is_wsl_remote = project
+ .read(cx)
+ .remote_connection_options(cx)
+ .map_or(false, |opts| {
+ matches!(opts, RemoteConnectionOptions::Wsl(_))
+ });
let kernel_specifications = python_env_kernel_specifications(project, worktree_id, cx);
let active_toolchain = project.read(cx).active_toolchain(
ProjectPath {
@@ -168,7 +177,7 @@ impl ReplStore {
this.active_python_toolchain_for_worktree
.insert(worktree_id, path);
}
- if is_remote {
+ if is_remote && !is_wsl_remote {
this.remote_worktrees.insert(worktree_id);
} else {
this.remote_worktrees.remove(&worktree_id);