From 5c2c6d7e5ed83657f4b0aa21ed6616725473eba5 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Wed, 16 Apr 2025 19:05:57 +0200 Subject: [PATCH] toolchain: Respect currently focused file when querying toolchains (#28875) Closes #21743 https://github.com/user-attachments/assets/0230f233-58a4-494c-90af-28ce82f9fc1d Release Notes: - Virtual environment picker now looks up virtual environment based on parent directory of active file; this enables having multiple active virtual environments in a single worktree. --- crates/project/src/project.rs | 3 ++ crates/project/src/toolchain_store.rs | 3 +- crates/title_bar/src/title_bar.rs | 2 +- .../src/active_toolchain.rs | 53 +++++++++++++++---- .../src/toolchain_selector.rs | 32 ++++++++--- crates/workspace/src/persistence.rs | 5 +- 6 files changed, 77 insertions(+), 21 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index e1d4010576..34c4d001f4 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -3094,6 +3094,9 @@ impl Project { .map(|lister| lister.term()) } + pub fn toolchain_store(&self) -> Option> { + self.toolchain_store.clone() + } pub fn activate_toolchain( &self, path: ProjectPath, diff --git a/crates/project/src/toolchain_store.rs b/crates/project/src/toolchain_store.rs index 473fe7df82..77889bef9f 100644 --- a/crates/project/src/toolchain_store.rs +++ b/crates/project/src/toolchain_store.rs @@ -55,6 +55,7 @@ impl ToolchainStore { }); Self(ToolchainStoreInner::Local(entity, subscription)) } + pub(super) fn remote(project_id: u64, client: AnyProtoClient, cx: &mut App) -> Self { Self(ToolchainStoreInner::Remote( cx.new(|_| RemoteToolchainStore { client, project_id }), @@ -285,7 +286,7 @@ struct LocalStore(WeakEntity); struct RemoteStore(WeakEntity); #[derive(Clone)] -pub(crate) enum ToolchainStoreEvent { +pub enum ToolchainStoreEvent { ToolchainActivated, } diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index 225c3613ce..cd11eceea0 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -302,7 +302,7 @@ impl TitleBar { cx.notify() }), ); - subscriptions.push(cx.subscribe(&project, |_, _, _, cx| cx.notify())); + subscriptions.push(cx.subscribe(&project, |_, _, _: &project::Event, cx| cx.notify())); subscriptions.push(cx.observe(&active_call, |this, _, cx| this.active_call_changed(cx))); subscriptions.push(cx.observe_window_activation(window, Self::window_activation_changed)); subscriptions.push(cx.observe(&user_store, |_, _, cx| cx.notify())); diff --git a/crates/toolchain_selector/src/active_toolchain.rs b/crates/toolchain_selector/src/active_toolchain.rs index 04e2e8c19e..847bcc869f 100644 --- a/crates/toolchain_selector/src/active_toolchain.rs +++ b/crates/toolchain_selector/src/active_toolchain.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{path::Path, sync::Arc}; use editor::Editor; use gpui::{ @@ -6,7 +6,7 @@ use gpui::{ WeakEntity, Window, div, }; use language::{Buffer, BufferEvent, LanguageName, Toolchain}; -use project::{Project, ProjectPath, WorktreeId}; +use project::{Project, ProjectPath, WorktreeId, toolchain_store::ToolchainStoreEvent}; use ui::{Button, ButtonCommon, Clickable, FluentBuilder, LabelSize, SharedString, Tooltip}; use workspace::{StatusItemView, Workspace, item::ItemHandle}; @@ -22,6 +22,28 @@ pub struct ActiveToolchain { impl ActiveToolchain { pub fn new(workspace: &Workspace, window: &mut Window, cx: &mut Context) -> Self { + if let Some(store) = workspace.project().read(cx).toolchain_store() { + cx.subscribe_in( + &store, + window, + |this, _, _: &ToolchainStoreEvent, window, cx| { + let editor = this + .workspace + .update(cx, |workspace, cx| { + workspace + .active_item(cx) + .and_then(|item| item.downcast::()) + }) + .ok() + .flatten(); + if let Some(editor) = editor { + this.active_toolchain.take(); + this.update_lister(editor, window, cx); + } + }, + ) + .detach(); + } Self { active_toolchain: None, active_buffer: None, @@ -57,12 +79,19 @@ impl ActiveToolchain { this.term = term; cx.notify(); }); - let worktree_id = active_file - .update(cx, |this, cx| Some(this.file()?.worktree_id(cx))) + let (worktree_id, path) = active_file + .update(cx, |this, cx| { + this.file().and_then(|file| { + Some(( + file.worktree_id(cx), + Arc::::from(file.path().parent()?), + )) + }) + }) .ok() .flatten()?; let toolchain = - Self::active_toolchain(workspace, worktree_id, language_name, cx).await?; + Self::active_toolchain(workspace, worktree_id, path, language_name, cx).await?; let _ = this.update(cx, |this, cx| { this.active_toolchain = Some(toolchain); @@ -101,6 +130,7 @@ impl ActiveToolchain { fn active_toolchain( workspace: WeakEntity, worktree_id: WorktreeId, + relative_path: Arc, language_name: LanguageName, cx: &mut AsyncWindowContext, ) -> Task> { @@ -114,7 +144,7 @@ impl ActiveToolchain { this.project().read(cx).active_toolchain( ProjectPath { worktree_id, - path: Arc::from("".as_ref()), + path: relative_path.clone(), }, language_name.clone(), cx, @@ -133,7 +163,7 @@ impl ActiveToolchain { project.read(cx).available_toolchains( ProjectPath { worktree_id, - path: Arc::from("".as_ref()), + path: relative_path.clone(), }, language_name, cx, @@ -144,7 +174,12 @@ impl ActiveToolchain { if let Some(toolchain) = toolchains.toolchains.first() { // Since we don't have a selected toolchain, pick one for user here. workspace::WORKSPACE_DB - .set_toolchain(workspace_id, worktree_id, "".to_owned(), toolchain.clone()) + .set_toolchain( + workspace_id, + worktree_id, + relative_path.to_string_lossy().into_owned(), + toolchain.clone(), + ) .await .ok()?; project @@ -152,7 +187,7 @@ impl ActiveToolchain { this.activate_toolchain( ProjectPath { worktree_id, - path: Arc::from("".as_ref()), + path: relative_path, }, toolchain.clone(), cx, diff --git a/crates/toolchain_selector/src/toolchain_selector.rs b/crates/toolchain_selector/src/toolchain_selector.rs index 92620b68b8..67252dd6a4 100644 --- a/crates/toolchain_selector/src/toolchain_selector.rs +++ b/crates/toolchain_selector/src/toolchain_selector.rs @@ -50,6 +50,7 @@ impl ToolchainSelector { let language_name = buffer.read(cx).language()?.name(); let worktree_id = buffer.read(cx).file()?.worktree_id(cx); + let relative_path: Arc = Arc::from(buffer.read(cx).file()?.path().parent()?); let worktree_root_path = project .read(cx) .worktree_for_id(worktree_id, cx)? @@ -58,8 +59,9 @@ impl ToolchainSelector { let workspace_id = workspace.database_id()?; let weak = workspace.weak_handle(); cx.spawn_in(window, async move |workspace, cx| { + let as_str = relative_path.to_string_lossy().into_owned(); let active_toolchain = workspace::WORKSPACE_DB - .toolchain(workspace_id, worktree_id, language_name.clone()) + .toolchain(workspace_id, worktree_id, as_str, language_name.clone()) .await .ok() .flatten(); @@ -72,6 +74,7 @@ impl ToolchainSelector { active_toolchain, worktree_id, worktree_root_path, + relative_path, language_name, window, cx, @@ -91,6 +94,7 @@ impl ToolchainSelector { active_toolchain: Option, worktree_id: WorktreeId, worktree_root: Arc, + relative_path: Arc, language_name: LanguageName, window: &mut Window, cx: &mut Context, @@ -104,6 +108,7 @@ impl ToolchainSelector { worktree_id, worktree_root, project, + relative_path, language_name, window, cx, @@ -137,6 +142,7 @@ pub struct ToolchainSelectorDelegate { workspace: WeakEntity, worktree_id: WorktreeId, worktree_abs_path_root: Arc, + relative_path: Arc, placeholder_text: Arc, _fetch_candidates_task: Task>, } @@ -149,6 +155,7 @@ impl ToolchainSelectorDelegate { worktree_id: WorktreeId, worktree_abs_path_root: Arc, project: Entity, + relative_path: Arc, language_name: LanguageName, window: &mut Window, cx: &mut Context>, @@ -162,17 +169,26 @@ impl ToolchainSelectorDelegate { }) .ok()? .await?; - let placeholder_text = format!("Select a {}…", term.to_lowercase()).into(); + let relative_path = this + .update(cx, |this, _| this.delegate.relative_path.clone()) + .ok()?; + let placeholder_text = format!( + "Select a {} for `{}`…", + term.to_lowercase(), + relative_path.to_string_lossy() + ) + .into(); let _ = this.update_in(cx, move |this, window, cx| { this.delegate.placeholder_text = placeholder_text; this.refresh_placeholder(window, cx); }); + let available_toolchains = project .update(cx, |this, cx| { this.available_toolchains( ProjectPath { worktree_id, - path: Arc::from("".as_ref()), + path: relative_path.clone(), }, language_name, cx, @@ -211,6 +227,7 @@ impl ToolchainSelectorDelegate { worktree_id, worktree_abs_path_root, placeholder_text, + relative_path, _fetch_candidates_task, } } @@ -246,19 +263,18 @@ impl PickerDelegate for ToolchainSelectorDelegate { { let workspace = self.workspace.clone(); let worktree_id = self.worktree_id; + let path = self.relative_path.clone(); + let relative_path = self.relative_path.to_string_lossy().into_owned(); cx.spawn_in(window, async move |_, cx| { workspace::WORKSPACE_DB - .set_toolchain(workspace_id, worktree_id, "".to_owned(), toolchain.clone()) + .set_toolchain(workspace_id, worktree_id, relative_path, toolchain.clone()) .await .log_err(); workspace .update(cx, |this, cx| { this.project().update(cx, |this, cx| { this.activate_toolchain( - ProjectPath { - worktree_id, - path: Arc::from("".as_ref()), - }, + ProjectPath { worktree_id, path }, toolchain, cx, ) diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index 06a84773ce..4608a7302f 100644 --- a/crates/workspace/src/persistence.rs +++ b/crates/workspace/src/persistence.rs @@ -1334,17 +1334,18 @@ impl WorkspaceDb { &self, workspace_id: WorkspaceId, worktree_id: WorktreeId, + relative_path: String, language_name: LanguageName, ) -> Result> { self.write(move |this| { let mut select = this .select_bound(sql!( - SELECT name, path, raw_json FROM toolchains WHERE workspace_id = ? AND language_name = ? AND worktree_id = ? + SELECT name, path, raw_json FROM toolchains WHERE workspace_id = ? AND language_name = ? AND worktree_id = ? AND relative_path = ? )) .context("Preparing insertion")?; let toolchain: Vec<(String, String, String)> = - select((workspace_id, language_name.as_ref().to_string(), worktree_id.to_usize()))?; + select((workspace_id, language_name.as_ref().to_string(), worktree_id.to_usize(), relative_path))?; Ok(toolchain.into_iter().next().and_then(|(name, path, raw_json)| Some(Toolchain { name: name.into(),