From 7030465d044bb67cee4f5efcd8d1c583f4c05d2d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 14 Aug 2025 15:03:23 +0200 Subject: [PATCH] Replace a smol::block_on --- crates/languages/src/python.rs | 1 + crates/project/src/terminals.rs | 198 +++++++++++++++----------------- 2 files changed, 95 insertions(+), 104 deletions(-) diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index 708952275d..3f37ac3044 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -1008,6 +1008,7 @@ const BINARY_DIR: &str = if cfg!(target_os = "windows") { "bin" }; +// TODO lw: this depends on the shell? const ACTIVATE_PATH: &str = if cfg!(target_os = "windows") { "Scripts/activate.bat" } else { diff --git a/crates/project/src/terminals.rs b/crates/project/src/terminals.rs index 73a63b5b4c..09e9b01e38 100644 --- a/crates/project/src/terminals.rs +++ b/crates/project/src/terminals.rs @@ -1,7 +1,7 @@ use crate::{Project, ProjectPath}; use anyhow::Result; use collections::HashMap; -use gpui::{App, AppContext as _, Context, Entity, Task, WeakEntity}; +use gpui::{App, AppContext as _, AsyncApp, Context, Entity, Task, WeakEntity}; use itertools::Itertools; use language::LanguageName; use remote::ssh_session::SshArgs; @@ -122,12 +122,12 @@ impl Project { cx.spawn(async move |project, cx| { let python_venv_directory = if let Some(path) = path { - project - .update(cx, |this, cx| { - this.python_venv_directory(path, venv.clone(), cx) - })? - .await - .zip(Some(venv)) + match project.upgrade() { + Some(project) => Self::python_venv_directory(project, path, venv.clone(), cx) + .await? + .zip(Some(venv)), + None => None, + } } else { None }; @@ -166,27 +166,18 @@ impl Project { }; let activate_keyword = match venv_settings.activate_script { - terminal_settings::ActivateScript::Default => match std::env::consts::OS { - "windows" => ".", - _ => ".", - }, + terminal_settings::ActivateScript::Default => ".", terminal_settings::ActivateScript::Nushell => "overlay use", terminal_settings::ActivateScript::PowerShell => ".", terminal_settings::ActivateScript::Pyenv => "pyenv", _ => "source", }; - let line_ending = match std::env::consts::OS { - "windows" => "\r", - _ => "\n", - }; + let line_ending = if cfg!(windows) { '\r' } else { 'n' }; if venv_settings.venv_name.is_empty() { let path = python_venv_directory - .join(match std::env::consts::OS { - "windows" => "Scripts", - _ => "bin", - }) + .join(BINARY_DIR) .join(activate_script_name) .to_string_lossy() .to_string(); @@ -486,98 +477,91 @@ impl Project { }) } - fn python_venv_directory( - &self, + async fn python_venv_directory( + this: Entity, abs_path: Arc, venv_settings: VenvSettings, - cx: &Context, - ) -> Task> { - cx.spawn(async move |this, cx| { - if let Some((worktree, relative_path)) = this - .update(cx, |this, cx| this.find_worktree(&abs_path, cx)) - .ok()? - { - let toolchain = this - .update(cx, |this, cx| { - this.active_toolchain( - ProjectPath { - worktree_id: worktree.read(cx).id(), - path: relative_path.into(), - }, - LanguageName::new("Python"), - cx, - ) - }) - .ok()? - .await; + cx: &mut AsyncApp, + ) -> Result> { + let Some((worktree, relative_path)) = + this.update(cx, |this, cx| this.find_worktree(&abs_path, cx))? + else { + return Ok(None); + }; + let toolchain = this + .update(cx, |this, cx| { + this.active_toolchain( + ProjectPath { + worktree_id: worktree.read(cx).id(), + path: relative_path.into(), + }, + LanguageName::new("Python"), + cx, + ) + })? + .await; - if let Some(toolchain) = toolchain { - let toolchain_path = Path::new(toolchain.path.as_ref()); - return Some(toolchain_path.parent()?.parent()?.to_path_buf()); + if let Some(toolchain) = toolchain { + let toolchain_path = Path::new(toolchain.path.as_ref()); + return Ok(toolchain_path + .parent() + .and_then(|p| Some(p.parent()?.to_path_buf()))); + } + + let Some(venv_settings) = venv_settings.as_option() else { + return Ok(None); + }; + + let tool = this.update(cx, |this, cx| { + venv_settings + .directories + .iter() + .map(|name| abs_path.join(name)) + .find(|venv_path| { + let bin_path = venv_path.join(BINARY_DIR); + this.find_worktree(&bin_path, cx) + .and_then(|(worktree, relative_path)| { + worktree.read(cx).entry_for_path(&relative_path) + }) + .is_some_and(|entry| entry.is_dir()) + }) + })?; + + if let Some(toolchain_path) = tool { + return Ok(Some(toolchain_path)); + } + + let r = this.update(cx, move |_, cx| { + let fs = worktree.read(cx).as_local()?.fs().clone(); + let map: Vec<_> = venv_settings + .directories + .iter() + .map(|name| abs_path.join(name)) + .collect(); + Some(cx.spawn(async move |_, _| { + for venv_path in map { + let bin_path = venv_path.join(BINARY_DIR); + // One-time synchronous check is acceptable for terminal/task initialization + // we are within a spawned future anyways + let exists = fs + .metadata(&bin_path) + .await + .ok() + .flatten() + .map_or(false, |meta| meta.is_dir); + if exists { + return Some(venv_path); + } } - } - let venv_settings = venv_settings.as_option()?; - this.update(cx, move |this, cx| { - if let Some(path) = this.find_venv_in_worktree(&abs_path, &venv_settings, cx) { - return Some(path); - } - this.find_venv_on_filesystem(&abs_path, &venv_settings, cx) - }) - .ok() - .flatten() + None + })) + })?; + Ok(match r { + Some(task) => task.await, + None => None, }) } - fn find_venv_in_worktree( - &self, - abs_path: &Path, - venv_settings: &terminal_settings::VenvSettingsContent, - cx: &App, - ) -> Option { - let bin_dir_name = match std::env::consts::OS { - "windows" => "Scripts", - _ => "bin", - }; - venv_settings - .directories - .iter() - .map(|name| abs_path.join(name)) - .find(|venv_path| { - let bin_path = venv_path.join(bin_dir_name); - self.find_worktree(&bin_path, cx) - .and_then(|(worktree, relative_path)| { - worktree.read(cx).entry_for_path(&relative_path) - }) - .is_some_and(|entry| entry.is_dir()) - }) - } - - fn find_venv_on_filesystem( - &self, - abs_path: &Path, - venv_settings: &terminal_settings::VenvSettingsContent, - cx: &App, - ) -> Option { - let (worktree, _) = self.find_worktree(abs_path, cx)?; - let fs = worktree.read(cx).as_local()?.fs(); - let bin_dir_name = match std::env::consts::OS { - "windows" => "Scripts", - _ => "bin", - }; - venv_settings - .directories - .iter() - .map(|name| abs_path.join(name)) - .find(|venv_path| { - let bin_path = venv_path.join(bin_dir_name); - // One-time synchronous check is acceptable for terminal/task initialization - smol::block_on(fs.metadata(&bin_path)) - .ok() - .flatten() - .map_or(false, |meta| meta.is_dir) - }) - } - fn activate_script_kind(shell: Option<&str>) -> ActivateScript { let shell_env = std::env::var("SHELL").ok(); let shell_path = shell.or_else(|| shell_env.as_deref()); @@ -599,6 +583,12 @@ impl Project { } } +const BINARY_DIR: &str = if cfg!(target_os = "windows") { + "Scripts" +} else { + "bin" +}; + pub fn wrap_for_ssh( ssh_command: &SshCommand, command: Option<(&String, &Vec)>,