python: Fix flickering in the status bar (#36039)

Piotr Osiewicz and Lukas Wirth created

- **util: Have maybe! use async closures instead of async blocks**
- **python: Fix flickering of virtual environment indicator in status
bar**

Closes #30723

Release Notes:

- Python: Fixed flickering of the status bar virtual environment
indicator

---------

Co-authored-by: Lukas Wirth <lukas@zed.dev>

Change summary

crates/toolchain_selector/src/active_toolchain.rs | 105 ++++++++++------
crates/util/src/util.rs                           |   4 
2 files changed, 66 insertions(+), 43 deletions(-)

Detailed changes

crates/toolchain_selector/src/active_toolchain.rs 🔗

@@ -8,6 +8,7 @@ use gpui::{
 use language::{Buffer, BufferEvent, LanguageName, Toolchain};
 use project::{Project, ProjectPath, WorktreeId, toolchain_store::ToolchainStoreEvent};
 use ui::{Button, ButtonCommon, Clickable, FluentBuilder, LabelSize, SharedString, Tooltip};
+use util::maybe;
 use workspace::{StatusItemView, Workspace, item::ItemHandle};
 
 use crate::ToolchainSelector;
@@ -55,49 +56,61 @@ impl ActiveToolchain {
     }
     fn spawn_tracker_task(window: &mut Window, cx: &mut Context<Self>) -> Task<Option<()>> {
         cx.spawn_in(window, async move |this, cx| {
-            let active_file = this
-                .read_with(cx, |this, _| {
-                    this.active_buffer
-                        .as_ref()
-                        .map(|(_, buffer, _)| buffer.clone())
-                })
-                .ok()
-                .flatten()?;
-            let workspace = this.read_with(cx, |this, _| this.workspace.clone()).ok()?;
-            let language_name = active_file
-                .read_with(cx, |this, _| Some(this.language()?.name()))
-                .ok()
-                .flatten()?;
-            let term = workspace
-                .update(cx, |workspace, cx| {
-                    let languages = workspace.project().read(cx).languages();
-                    Project::toolchain_term(languages.clone(), language_name.clone())
-                })
-                .ok()?
-                .await?;
-            let _ = this.update(cx, |this, cx| {
-                this.term = term;
-                cx.notify();
-            });
-            let (worktree_id, path) = active_file
-                .update(cx, |this, cx| {
-                    this.file().and_then(|file| {
-                        Some((
-                            file.worktree_id(cx),
-                            Arc::<Path>::from(file.path().parent()?),
-                        ))
+            let did_set_toolchain = maybe!(async {
+                let active_file = this
+                    .read_with(cx, |this, _| {
+                        this.active_buffer
+                            .as_ref()
+                            .map(|(_, buffer, _)| buffer.clone())
+                    })
+                    .ok()
+                    .flatten()?;
+                let workspace = this.read_with(cx, |this, _| this.workspace.clone()).ok()?;
+                let language_name = active_file
+                    .read_with(cx, |this, _| Some(this.language()?.name()))
+                    .ok()
+                    .flatten()?;
+                let term = workspace
+                    .update(cx, |workspace, cx| {
+                        let languages = workspace.project().read(cx).languages();
+                        Project::toolchain_term(languages.clone(), language_name.clone())
                     })
+                    .ok()?
+                    .await?;
+                let _ = this.update(cx, |this, cx| {
+                    this.term = term;
+                    cx.notify();
+                });
+                let (worktree_id, path) = active_file
+                    .update(cx, |this, cx| {
+                        this.file().and_then(|file| {
+                            Some((
+                                file.worktree_id(cx),
+                                Arc::<Path>::from(file.path().parent()?),
+                            ))
+                        })
+                    })
+                    .ok()
+                    .flatten()?;
+                let toolchain =
+                    Self::active_toolchain(workspace, worktree_id, path, language_name, cx).await?;
+                this.update(cx, |this, cx| {
+                    this.active_toolchain = Some(toolchain);
+
+                    cx.notify();
                 })
                 .ok()
-                .flatten()?;
-            let toolchain =
-                Self::active_toolchain(workspace, worktree_id, path, language_name, cx).await?;
-            let _ = this.update(cx, |this, cx| {
-                this.active_toolchain = Some(toolchain);
-
-                cx.notify();
-            });
-            Some(())
+            })
+            .await
+            .is_some();
+            if !did_set_toolchain {
+                this.update(cx, |this, cx| {
+                    this.active_toolchain = None;
+                    cx.notify();
+                })
+                .ok();
+            }
+            did_set_toolchain.then_some(())
         })
     }
 
@@ -110,6 +123,17 @@ impl ActiveToolchain {
         let editor = editor.read(cx);
         if let Some((_, buffer, _)) = editor.active_excerpt(cx) {
             if let Some(worktree_id) = buffer.read(cx).file().map(|file| file.worktree_id(cx)) {
+                if self
+                    .active_buffer
+                    .as_ref()
+                    .is_some_and(|(old_worktree_id, old_buffer, _)| {
+                        (old_worktree_id, old_buffer.entity_id())
+                            == (&worktree_id, buffer.entity_id())
+                    })
+                {
+                    return;
+                }
+
                 let subscription = cx.subscribe_in(
                     &buffer,
                     window,
@@ -231,7 +255,6 @@ impl StatusItemView for ActiveToolchain {
         cx: &mut Context<Self>,
     ) {
         if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
-            self.active_toolchain.take();
             self.update_lister(editor, window, cx);
         }
         cx.notify();

crates/util/src/util.rs 🔗

@@ -887,10 +887,10 @@ macro_rules! maybe {
         (|| $block)()
     };
     (async $block:block) => {
-        (|| async $block)()
+        (async || $block)()
     };
     (async move $block:block) => {
-        (|| async move $block)()
+        (async move || $block)()
     };
 }