toolchains: Add support for relative paths (#27777)

Closes #ISSUE

Release Notes:

- N/A
This commit is contained in:
Piotr Osiewicz 2025-03-31 19:48:09 +02:00 committed by GitHub
parent 627ae7af6f
commit edf712d45b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 178 additions and 93 deletions

View file

@ -89,6 +89,7 @@ impl DebugAdapter for PythonDebugAdapter {
.toolchain_store() .toolchain_store()
.active_toolchain( .active_toolchain(
delegate.worktree_id(), delegate.worktree_id(),
Arc::from("".as_ref()),
language::LanguageName::new(Self::LANGUAGE_NAME), language::LanguageName::new(Self::LANGUAGE_NAME),
cx, cx,
) )

View file

@ -4,7 +4,10 @@
//! which is a set of tools used to interact with the projects written in said language. //! which is a set of tools used to interact with the projects written in said language.
//! For example, a Python project can have an associated virtual environment; a Rust project can have a toolchain override. //! For example, a Python project can have an associated virtual environment; a Rust project can have a toolchain override.
use std::{path::PathBuf, sync::Arc}; use std::{
path::{Path, PathBuf},
sync::Arc,
};
use async_trait::async_trait; use async_trait::async_trait;
use collections::HashMap; use collections::HashMap;
@ -52,6 +55,7 @@ pub trait LanguageToolchainStore {
async fn active_toolchain( async fn active_toolchain(
self: Arc<Self>, self: Arc<Self>,
worktree_id: WorktreeId, worktree_id: WorktreeId,
relative_path: Arc<Path>,
language_name: LanguageName, language_name: LanguageName,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Option<Toolchain>; ) -> Option<Toolchain>;

View file

@ -293,7 +293,12 @@ impl LspAdapter for PythonLspAdapter {
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<Value> { ) -> Result<Value> {
let toolchain = toolchains let toolchain = toolchains
.active_toolchain(adapter.worktree_id(), LanguageName::new("Python"), cx) .active_toolchain(
adapter.worktree_id(),
Arc::from("".as_ref()),
LanguageName::new("Python"),
cx,
)
.await; .await;
cx.update(move |cx| { cx.update(move |cx| {
let mut user_settings = let mut user_settings =
@ -373,7 +378,7 @@ impl ContextProvider for PythonContextProvider {
cx.spawn(async move |cx| { cx.spawn(async move |cx| {
let active_toolchain = if let Some(worktree_id) = worktree_id { let active_toolchain = if let Some(worktree_id) = worktree_id {
toolchains toolchains
.active_toolchain(worktree_id, "Python".into(), cx) .active_toolchain(worktree_id, Arc::from("".as_ref()), "Python".into(), cx)
.await .await
.map_or_else( .map_or_else(
|| "python3".to_owned(), || "python3".to_owned(),
@ -900,6 +905,7 @@ impl LspAdapter for PyLspAdapter {
let venv = toolchains let venv = toolchains
.active_toolchain( .active_toolchain(
delegate.worktree_id(), delegate.worktree_id(),
Arc::from("".as_ref()),
LanguageName::new("Python"), LanguageName::new("Python"),
&mut cx.clone(), &mut cx.clone(),
) )
@ -1046,7 +1052,12 @@ impl LspAdapter for PyLspAdapter {
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<Value> { ) -> Result<Value> {
let toolchain = toolchains let toolchain = toolchains
.active_toolchain(adapter.worktree_id(), LanguageName::new("Python"), cx) .active_toolchain(
adapter.worktree_id(),
Arc::from("".as_ref()),
LanguageName::new("Python"),
cx,
)
.await; .await;
cx.update(move |cx| { cx.update(move |cx| {
let mut user_settings = let mut user_settings =

View file

@ -3065,7 +3065,7 @@ impl Project {
pub fn available_toolchains( pub fn available_toolchains(
&self, &self,
worktree_id: WorktreeId, path: ProjectPath,
language_name: LanguageName, language_name: LanguageName,
cx: &App, cx: &App,
) -> Task<Option<ToolchainList>> { ) -> Task<Option<ToolchainList>> {
@ -3074,7 +3074,7 @@ impl Project {
cx.update(|cx| { cx.update(|cx| {
toolchain_store toolchain_store
.read(cx) .read(cx)
.list_toolchains(worktree_id, language_name, cx) .list_toolchains(path, language_name, cx)
}) })
.ok()? .ok()?
.await .await
@ -3098,20 +3098,18 @@ impl Project {
pub fn activate_toolchain( pub fn activate_toolchain(
&self, &self,
worktree_id: WorktreeId, path: ProjectPath,
toolchain: Toolchain, toolchain: Toolchain,
cx: &mut App, cx: &mut App,
) -> Task<Option<()>> { ) -> Task<Option<()>> {
let Some(toolchain_store) = self.toolchain_store.clone() else { let Some(toolchain_store) = self.toolchain_store.clone() else {
return Task::ready(None); return Task::ready(None);
}; };
toolchain_store.update(cx, |this, cx| { toolchain_store.update(cx, |this, cx| this.activate_toolchain(path, toolchain, cx))
this.activate_toolchain(worktree_id, toolchain, cx)
})
} }
pub fn active_toolchain( pub fn active_toolchain(
&self, &self,
worktree_id: WorktreeId, path: ProjectPath,
language_name: LanguageName, language_name: LanguageName,
cx: &App, cx: &App,
) -> Task<Option<Toolchain>> { ) -> Task<Option<Toolchain>> {
@ -3120,7 +3118,7 @@ impl Project {
}; };
toolchain_store toolchain_store
.read(cx) .read(cx)
.active_toolchain(worktree_id, language_name, cx) .active_toolchain(path, language_name, cx)
} }
pub fn language_server_statuses<'a>( pub fn language_server_statuses<'a>(
&'a self, &'a self,

View file

@ -1,4 +1,4 @@
use crate::Project; use crate::{Project, ProjectPath};
use anyhow::{Context as _, Result}; use anyhow::{Context as _, Result};
use collections::HashMap; use collections::HashMap;
use gpui::{AnyWindowHandle, App, AppContext as _, Context, Entity, Task, WeakEntity}; use gpui::{AnyWindowHandle, App, AppContext as _, Context, Entity, Task, WeakEntity};
@ -407,14 +407,17 @@ impl Project {
cx: &Context<Project>, cx: &Context<Project>,
) -> Task<Option<PathBuf>> { ) -> Task<Option<PathBuf>> {
cx.spawn(async move |this, cx| { cx.spawn(async move |this, cx| {
if let Some((worktree, _)) = this if let Some((worktree, relative_path)) = this
.update(cx, |this, cx| this.find_worktree(&abs_path, cx)) .update(cx, |this, cx| this.find_worktree(&abs_path, cx))
.ok()? .ok()?
{ {
let toolchain = this let toolchain = this
.update(cx, |this, cx| { .update(cx, |this, cx| {
this.active_toolchain( this.active_toolchain(
worktree.read(cx).id(), ProjectPath {
worktree_id: worktree.read(cx).id(),
path: relative_path.into(),
},
LanguageName::new("Python"), LanguageName::new("Python"),
cx, cx,
) )

View file

@ -1,4 +1,8 @@
use std::{path::PathBuf, str::FromStr, sync::Arc}; use std::{
path::{Path, PathBuf},
str::FromStr,
sync::Arc,
};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
@ -15,7 +19,7 @@ use rpc::{
use settings::WorktreeId; use settings::WorktreeId;
use util::ResultExt as _; use util::ResultExt as _;
use crate::{worktree_store::WorktreeStore, ProjectEnvironment}; use crate::{worktree_store::WorktreeStore, ProjectEnvironment, ProjectPath};
pub struct ToolchainStore(ToolchainStoreInner); pub struct ToolchainStore(ToolchainStoreInner);
enum ToolchainStoreInner { enum ToolchainStoreInner {
@ -58,56 +62,46 @@ impl ToolchainStore {
} }
pub(crate) fn activate_toolchain( pub(crate) fn activate_toolchain(
&self, &self,
worktree_id: WorktreeId, path: ProjectPath,
toolchain: Toolchain, toolchain: Toolchain,
cx: &mut App, cx: &mut App,
) -> Task<Option<()>> { ) -> Task<Option<()>> {
match &self.0 { match &self.0 {
ToolchainStoreInner::Local(local, _) => local.update(cx, |this, cx| { ToolchainStoreInner::Local(local, _) => {
this.activate_toolchain(worktree_id, toolchain, cx) local.update(cx, |this, cx| this.activate_toolchain(path, toolchain, cx))
}), }
ToolchainStoreInner::Remote(remote) => { ToolchainStoreInner::Remote(remote) => {
remote remote.read(cx).activate_toolchain(path, toolchain, cx)
.read(cx)
.activate_toolchain(worktree_id, toolchain, cx)
} }
} }
} }
pub(crate) fn list_toolchains( pub(crate) fn list_toolchains(
&self, &self,
worktree_id: WorktreeId, path: ProjectPath,
language_name: LanguageName, language_name: LanguageName,
cx: &App, cx: &App,
) -> Task<Option<ToolchainList>> { ) -> Task<Option<ToolchainList>> {
match &self.0 { match &self.0 {
ToolchainStoreInner::Local(local, _) => { ToolchainStoreInner::Local(local, _) => {
local local.read(cx).list_toolchains(path, language_name, cx)
.read(cx)
.list_toolchains(worktree_id, language_name, cx)
} }
ToolchainStoreInner::Remote(remote) => { ToolchainStoreInner::Remote(remote) => {
remote remote.read(cx).list_toolchains(path, language_name, cx)
.read(cx)
.list_toolchains(worktree_id, language_name, cx)
} }
} }
} }
pub(crate) fn active_toolchain( pub(crate) fn active_toolchain(
&self, &self,
worktree_id: WorktreeId, path: ProjectPath,
language_name: LanguageName, language_name: LanguageName,
cx: &App, cx: &App,
) -> Task<Option<Toolchain>> { ) -> Task<Option<Toolchain>> {
match &self.0 { match &self.0 {
ToolchainStoreInner::Local(local, _) => { ToolchainStoreInner::Local(local, _) => {
local local.read(cx).active_toolchain(path, language_name, cx)
.read(cx)
.active_toolchain(worktree_id, language_name, cx)
} }
ToolchainStoreInner::Remote(remote) => { ToolchainStoreInner::Remote(remote) => {
remote remote.read(cx).active_toolchain(path, language_name, cx)
.read(cx)
.active_toolchain(worktree_id, language_name, cx)
} }
} }
} }
@ -130,7 +124,12 @@ impl ToolchainStore {
language_name, language_name,
}; };
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id); let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
Ok(this.activate_toolchain(worktree_id, toolchain, cx)) let path: Arc<Path> = if let Some(path) = envelope.payload.path {
Arc::from(path.as_ref())
} else {
Arc::from("".as_ref())
};
Ok(this.activate_toolchain(ProjectPath { worktree_id, path }, toolchain, cx))
})?? })??
.await; .await;
Ok(proto::Ack {}) Ok(proto::Ack {})
@ -144,7 +143,14 @@ impl ToolchainStore {
.update(&mut cx, |this, cx| { .update(&mut cx, |this, cx| {
let language_name = LanguageName::from_proto(envelope.payload.language_name); let language_name = LanguageName::from_proto(envelope.payload.language_name);
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id); let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
this.active_toolchain(worktree_id, language_name, cx) this.active_toolchain(
ProjectPath {
worktree_id,
path: Arc::from(envelope.payload.path.as_deref().unwrap_or("").as_ref()),
},
language_name,
cx,
)
})? })?
.await; .await;
@ -169,7 +175,8 @@ impl ToolchainStore {
.update(&mut cx, |this, cx| { .update(&mut cx, |this, cx| {
let language_name = LanguageName::from_proto(envelope.payload.language_name); let language_name = LanguageName::from_proto(envelope.payload.language_name);
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id); let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
this.list_toolchains(worktree_id, language_name, cx) let path = Arc::from(envelope.payload.path.as_deref().unwrap_or("").as_ref());
this.list_toolchains(ProjectPath { worktree_id, path }, language_name, cx)
})? })?
.await; .await;
let has_values = toolchains.is_some(); let has_values = toolchains.is_some();
@ -222,7 +229,7 @@ struct LocalToolchainStore {
languages: Arc<LanguageRegistry>, languages: Arc<LanguageRegistry>,
worktree_store: Entity<WorktreeStore>, worktree_store: Entity<WorktreeStore>,
project_environment: Entity<ProjectEnvironment>, project_environment: Entity<ProjectEnvironment>,
active_toolchains: BTreeMap<(WorktreeId, LanguageName), Toolchain>, active_toolchains: BTreeMap<(WorktreeId, LanguageName), BTreeMap<Arc<Path>, Toolchain>>,
} }
#[async_trait(?Send)] #[async_trait(?Send)]
@ -230,12 +237,13 @@ impl language::LanguageToolchainStore for LocalStore {
async fn active_toolchain( async fn active_toolchain(
self: Arc<Self>, self: Arc<Self>,
worktree_id: WorktreeId, worktree_id: WorktreeId,
path: Arc<Path>,
language_name: LanguageName, language_name: LanguageName,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Option<Toolchain> { ) -> Option<Toolchain> {
self.0 self.0
.update(cx, |this, cx| { .update(cx, |this, cx| {
this.active_toolchain(worktree_id, language_name, cx) this.active_toolchain(ProjectPath { worktree_id, path }, language_name, cx)
}) })
.ok()? .ok()?
.await .await
@ -247,12 +255,13 @@ impl language::LanguageToolchainStore for RemoteStore {
async fn active_toolchain( async fn active_toolchain(
self: Arc<Self>, self: Arc<Self>,
worktree_id: WorktreeId, worktree_id: WorktreeId,
path: Arc<Path>,
language_name: LanguageName, language_name: LanguageName,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Option<Toolchain> { ) -> Option<Toolchain> {
self.0 self.0
.update(cx, |this, cx| { .update(cx, |this, cx| {
this.active_toolchain(worktree_id, language_name, cx) this.active_toolchain(ProjectPath { worktree_id, path }, language_name, cx)
}) })
.ok()? .ok()?
.await .await
@ -265,6 +274,7 @@ impl language::LanguageToolchainStore for EmptyToolchainStore {
async fn active_toolchain( async fn active_toolchain(
self: Arc<Self>, self: Arc<Self>,
_: WorktreeId, _: WorktreeId,
_: Arc<Path>,
_: LanguageName, _: LanguageName,
_: &mut AsyncApp, _: &mut AsyncApp,
) -> Option<Toolchain> { ) -> Option<Toolchain> {
@ -284,16 +294,16 @@ impl EventEmitter<ToolchainStoreEvent> for LocalToolchainStore {}
impl LocalToolchainStore { impl LocalToolchainStore {
pub(crate) fn activate_toolchain( pub(crate) fn activate_toolchain(
&self, &self,
worktree_id: WorktreeId, path: ProjectPath,
toolchain: Toolchain, toolchain: Toolchain,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Task<Option<()>> { ) -> Task<Option<()>> {
cx.spawn(async move |this, cx| { cx.spawn(async move |this, cx| {
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
this.active_toolchains.insert( this.active_toolchains
(worktree_id, toolchain.language_name.clone()), .entry((path.worktree_id, toolchain.language_name.clone()))
toolchain.clone(), .or_default()
); .insert(path.path, toolchain.clone());
cx.emit(ToolchainStoreEvent::ToolchainActivated); cx.emit(ToolchainStoreEvent::ToolchainActivated);
}) })
.ok(); .ok();
@ -302,7 +312,7 @@ impl LocalToolchainStore {
} }
pub(crate) fn list_toolchains( pub(crate) fn list_toolchains(
&self, &self,
worktree_id: WorktreeId, path: ProjectPath,
language_name: LanguageName, language_name: LanguageName,
cx: &App, cx: &App,
) -> Task<Option<ToolchainList>> { ) -> Task<Option<ToolchainList>> {
@ -310,17 +320,22 @@ impl LocalToolchainStore {
let Some(root) = self let Some(root) = self
.worktree_store .worktree_store
.read(cx) .read(cx)
.worktree_for_id(worktree_id, cx) .worktree_for_id(path.worktree_id, cx)
.map(|worktree| worktree.read(cx).abs_path()) .map(|worktree| worktree.read(cx).abs_path())
else { else {
return Task::ready(None); return Task::ready(None);
}; };
let abs_path = root.join(path.path);
let environment = self.project_environment.clone(); let environment = self.project_environment.clone();
cx.spawn(async move |cx| { cx.spawn(async move |cx| {
let project_env = environment let project_env = environment
.update(cx, |environment, cx| { .update(cx, |environment, cx| {
environment.get_environment(Some(worktree_id), Some(root.clone()), cx) environment.get_environment(
Some(path.worktree_id),
Some(Arc::from(abs_path.as_path())),
cx,
)
}) })
.ok()? .ok()?
.await; .await;
@ -331,20 +346,26 @@ impl LocalToolchainStore {
.await .await
.ok()?; .ok()?;
let toolchains = language.toolchain_lister()?; let toolchains = language.toolchain_lister()?;
Some(toolchains.list(root.to_path_buf(), project_env).await) Some(toolchains.list(abs_path.to_path_buf(), project_env).await)
}) })
.await .await
}) })
} }
pub(crate) fn active_toolchain( pub(crate) fn active_toolchain(
&self, &self,
worktree_id: WorktreeId, path: ProjectPath,
language_name: LanguageName, language_name: LanguageName,
_: &App, _: &App,
) -> Task<Option<Toolchain>> { ) -> Task<Option<Toolchain>> {
let ancestors = path.path.ancestors();
Task::ready( Task::ready(
self.active_toolchains self.active_toolchains
.get(&(worktree_id, language_name)) .get(&(path.worktree_id, language_name))
.and_then(|paths| {
ancestors
.into_iter()
.find_map(|root_path| paths.get(root_path))
})
.cloned(), .cloned(),
) )
} }
@ -357,24 +378,25 @@ struct RemoteToolchainStore {
impl RemoteToolchainStore { impl RemoteToolchainStore {
pub(crate) fn activate_toolchain( pub(crate) fn activate_toolchain(
&self, &self,
worktree_id: WorktreeId, project_path: ProjectPath,
toolchain: Toolchain, toolchain: Toolchain,
cx: &App, cx: &App,
) -> Task<Option<()>> { ) -> Task<Option<()>> {
let project_id = self.project_id; let project_id = self.project_id;
let client = self.client.clone(); let client = self.client.clone();
cx.spawn(async move |_| { cx.background_spawn(async move {
let path = PathBuf::from(toolchain.path.to_string()); let path = PathBuf::from(toolchain.path.to_string());
let _ = client let _ = client
.request(proto::ActivateToolchain { .request(proto::ActivateToolchain {
project_id, project_id,
worktree_id: worktree_id.to_proto(), worktree_id: project_path.worktree_id.to_proto(),
language_name: toolchain.language_name.into(), language_name: toolchain.language_name.into(),
toolchain: Some(proto::Toolchain { toolchain: Some(proto::Toolchain {
name: toolchain.name.into(), name: toolchain.name.into(),
path: path.to_proto(), path: path.to_proto(),
raw_json: toolchain.as_json.to_string(), raw_json: toolchain.as_json.to_string(),
}), }),
path: Some(project_path.path.to_string_lossy().into_owned()),
}) })
.await .await
.log_err()?; .log_err()?;
@ -384,18 +406,19 @@ impl RemoteToolchainStore {
pub(crate) fn list_toolchains( pub(crate) fn list_toolchains(
&self, &self,
worktree_id: WorktreeId, path: ProjectPath,
language_name: LanguageName, language_name: LanguageName,
cx: &App, cx: &App,
) -> Task<Option<ToolchainList>> { ) -> Task<Option<ToolchainList>> {
let project_id = self.project_id; let project_id = self.project_id;
let client = self.client.clone(); let client = self.client.clone();
cx.spawn(async move |_| { cx.background_spawn(async move {
let response = client let response = client
.request(proto::ListToolchains { .request(proto::ListToolchains {
project_id, project_id,
worktree_id: worktree_id.to_proto(), worktree_id: path.worktree_id.to_proto(),
language_name: language_name.clone().into(), language_name: language_name.clone().into(),
path: Some(path.path.to_string_lossy().into_owned()),
}) })
.await .await
.log_err()?; .log_err()?;
@ -435,18 +458,19 @@ impl RemoteToolchainStore {
} }
pub(crate) fn active_toolchain( pub(crate) fn active_toolchain(
&self, &self,
worktree_id: WorktreeId, path: ProjectPath,
language_name: LanguageName, language_name: LanguageName,
cx: &App, cx: &App,
) -> Task<Option<Toolchain>> { ) -> Task<Option<Toolchain>> {
let project_id = self.project_id; let project_id = self.project_id;
let client = self.client.clone(); let client = self.client.clone();
cx.spawn(async move |_| { cx.background_spawn(async move {
let response = client let response = client
.request(proto::ActiveToolchain { .request(proto::ActiveToolchain {
project_id, project_id,
worktree_id: worktree_id.to_proto(), worktree_id: path.worktree_id.to_proto(),
language_name: language_name.clone().into(), language_name: language_name.clone().into(),
path: Some(path.path.to_string_lossy().into_owned()),
}) })
.await .await
.log_err()?; .log_err()?;

View file

@ -3269,6 +3269,7 @@ message ListToolchains {
uint64 project_id = 1; uint64 project_id = 1;
uint64 worktree_id = 2; uint64 worktree_id = 2;
string language_name = 3; string language_name = 3;
optional string path = 4;
} }
message Toolchain { message Toolchain {
@ -3293,12 +3294,14 @@ message ActivateToolchain {
uint64 worktree_id = 2; uint64 worktree_id = 2;
Toolchain toolchain = 3; Toolchain toolchain = 3;
string language_name = 4; string language_name = 4;
optional string path = 5;
} }
message ActiveToolchain { message ActiveToolchain {
uint64 project_id = 1; uint64 project_id = 1;
uint64 worktree_id = 2; uint64 worktree_id = 2;
string language_name = 3; string language_name = 3;
optional string path = 4;
} }
message ActiveToolchainResponse { message ActiveToolchainResponse {

View file

@ -1,5 +1,5 @@
mod native_kernel; mod native_kernel;
use std::{fmt::Debug, future::Future, path::PathBuf}; use std::{fmt::Debug, future::Future, path::PathBuf, sync::Arc};
use futures::{ use futures::{
channel::mpsc::{self, Receiver}, channel::mpsc::{self, Receiver},
@ -11,7 +11,7 @@ use language::LanguageName;
pub use native_kernel::*; pub use native_kernel::*;
mod remote_kernels; mod remote_kernels;
use project::{Project, WorktreeId}; use project::{Project, ProjectPath, WorktreeId};
pub use remote_kernels::*; pub use remote_kernels::*;
use anyhow::Result; use anyhow::Result;
@ -81,9 +81,14 @@ pub fn python_env_kernel_specifications(
cx: &mut App, cx: &mut App,
) -> impl Future<Output = Result<Vec<KernelSpecification>>> { ) -> impl Future<Output = Result<Vec<KernelSpecification>>> {
let python_language = LanguageName::new("Python"); let python_language = LanguageName::new("Python");
let toolchains = project let toolchains = project.read(cx).available_toolchains(
.read(cx) ProjectPath {
.available_toolchains(worktree_id, python_language, cx); worktree_id,
path: Arc::from("".as_ref()),
},
python_language,
cx,
);
let background_executor = cx.background_executor().clone(); let background_executor = cx.background_executor().clone();
async move { async move {

View file

@ -1,10 +1,12 @@
use std::sync::Arc;
use editor::Editor; use editor::Editor;
use gpui::{ use gpui::{
div, AsyncWindowContext, Context, Entity, IntoElement, ParentElement, Render, Subscription, div, AsyncWindowContext, Context, Entity, IntoElement, ParentElement, Render, Subscription,
Task, WeakEntity, Window, Task, WeakEntity, Window,
}; };
use language::{Buffer, BufferEvent, LanguageName, Toolchain}; use language::{Buffer, BufferEvent, LanguageName, Toolchain};
use project::{Project, WorktreeId}; use project::{Project, ProjectPath, WorktreeId};
use ui::{Button, ButtonCommon, Clickable, FluentBuilder, LabelSize, SharedString, Tooltip}; use ui::{Button, ButtonCommon, Clickable, FluentBuilder, LabelSize, SharedString, Tooltip};
use workspace::{item::ItemHandle, StatusItemView, Workspace}; use workspace::{item::ItemHandle, StatusItemView, Workspace};
@ -109,9 +111,14 @@ impl ActiveToolchain {
.flatten()?; .flatten()?;
let selected_toolchain = workspace let selected_toolchain = workspace
.update(cx, |this, cx| { .update(cx, |this, cx| {
this.project() this.project().read(cx).active_toolchain(
.read(cx) ProjectPath {
.active_toolchain(worktree_id, language_name.clone(), cx) worktree_id,
path: Arc::from("".as_ref()),
},
language_name.clone(),
cx,
)
}) })
.ok()? .ok()?
.await; .await;
@ -123,21 +130,33 @@ impl ActiveToolchain {
.ok()?; .ok()?;
let toolchains = cx let toolchains = cx
.update(|_, cx| { .update(|_, cx| {
project project.read(cx).available_toolchains(
.read(cx) ProjectPath {
.available_toolchains(worktree_id, language_name, cx) worktree_id,
path: Arc::from("".as_ref()),
},
language_name,
cx,
)
}) })
.ok()? .ok()?
.await?; .await?;
if let Some(toolchain) = toolchains.toolchains.first() { if let Some(toolchain) = toolchains.toolchains.first() {
// Since we don't have a selected toolchain, pick one for user here. // Since we don't have a selected toolchain, pick one for user here.
workspace::WORKSPACE_DB workspace::WORKSPACE_DB
.set_toolchain(workspace_id, worktree_id, toolchain.clone()) .set_toolchain(workspace_id, worktree_id, "".to_owned(), toolchain.clone())
.await .await
.ok()?; .ok()?;
project project
.update(cx, |this, cx| { .update(cx, |this, cx| {
this.activate_toolchain(worktree_id, toolchain.clone(), cx) this.activate_toolchain(
ProjectPath {
worktree_id,
path: Arc::from("".as_ref()),
},
toolchain.clone(),
cx,
)
}) })
.ok()? .ok()?
.await; .await;

View file

@ -9,7 +9,7 @@ use gpui::{
}; };
use language::{LanguageName, Toolchain, ToolchainList}; use language::{LanguageName, Toolchain, ToolchainList};
use picker::{Picker, PickerDelegate}; use picker::{Picker, PickerDelegate};
use project::{Project, WorktreeId}; use project::{Project, ProjectPath, WorktreeId};
use std::{path::Path, sync::Arc}; use std::{path::Path, sync::Arc};
use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing}; use ui::{prelude::*, HighlightedLabel, ListItem, ListItemSpacing};
use util::ResultExt; use util::ResultExt;
@ -169,7 +169,14 @@ impl ToolchainSelectorDelegate {
}); });
let available_toolchains = project let available_toolchains = project
.update(cx, |this, cx| { .update(cx, |this, cx| {
this.available_toolchains(worktree_id, language_name, cx) this.available_toolchains(
ProjectPath {
worktree_id,
path: Arc::from("".as_ref()),
},
language_name,
cx,
)
}) })
.ok()? .ok()?
.await?; .await?;
@ -241,13 +248,20 @@ impl PickerDelegate for ToolchainSelectorDelegate {
let worktree_id = self.worktree_id; let worktree_id = self.worktree_id;
cx.spawn_in(window, async move |_, cx| { cx.spawn_in(window, async move |_, cx| {
workspace::WORKSPACE_DB workspace::WORKSPACE_DB
.set_toolchain(workspace_id, worktree_id, toolchain.clone()) .set_toolchain(workspace_id, worktree_id, "".to_owned(), toolchain.clone())
.await .await
.log_err(); .log_err();
workspace workspace
.update(cx, |this, cx| { .update(cx, |this, cx| {
this.project().update(cx, |this, cx| { this.project().update(cx, |this, cx| {
this.activate_toolchain(worktree_id, toolchain, cx) this.activate_toolchain(
ProjectPath {
worktree_id,
path: Arc::from("".as_ref()),
},
toolchain,
cx,
)
}) })
}) })
.ok()? .ok()?

View file

@ -526,7 +526,8 @@ define_connection! {
), ),
sql!( sql!(
ALTER TABLE breakpoints DROP COLUMN kind ALTER TABLE breakpoints DROP COLUMN kind
) ),
sql!(ALTER TABLE toolchains ADD COLUMN relative_worktree_path TEXT DEFAULT "" NOT NULL)
]; ];
} }
@ -1331,23 +1332,23 @@ impl WorkspaceDb {
pub(crate) async fn toolchains( pub(crate) async fn toolchains(
&self, &self,
workspace_id: WorkspaceId, workspace_id: WorkspaceId,
) -> Result<Vec<(Toolchain, WorktreeId)>> { ) -> Result<Vec<(Toolchain, WorktreeId, Arc<Path>)>> {
self.write(move |this| { self.write(move |this| {
let mut select = this let mut select = this
.select_bound(sql!( .select_bound(sql!(
SELECT name, path, worktree_id, language_name, raw_json FROM toolchains WHERE workspace_id = ? SELECT name, path, worktree_id, relative_worktree_path, language_name, raw_json FROM toolchains WHERE workspace_id = ?
)) ))
.context("Preparing insertion")?; .context("Preparing insertion")?;
let toolchain: Vec<(String, String, u64, String, String)> = let toolchain: Vec<(String, String, u64, String, String, String)> =
select(workspace_id)?; select(workspace_id)?;
Ok(toolchain.into_iter().filter_map(|(name, path, worktree_id, language_name, raw_json)| Some((Toolchain { Ok(toolchain.into_iter().filter_map(|(name, path, worktree_id, relative_worktree_path, language_name, raw_json)| Some((Toolchain {
name: name.into(), name: name.into(),
path: path.into(), path: path.into(),
language_name: LanguageName::new(&language_name), language_name: LanguageName::new(&language_name),
as_json: serde_json::Value::from_str(&raw_json).ok()? as_json: serde_json::Value::from_str(&raw_json).ok()?
}, WorktreeId::from_proto(worktree_id)))).collect()) }, WorktreeId::from_proto(worktree_id), Arc::from(relative_worktree_path.as_ref())))).collect())
}) })
.await .await
} }
@ -1355,12 +1356,13 @@ impl WorkspaceDb {
&self, &self,
workspace_id: WorkspaceId, workspace_id: WorkspaceId,
worktree_id: WorktreeId, worktree_id: WorktreeId,
relative_worktree_path: String,
toolchain: Toolchain, toolchain: Toolchain,
) -> Result<()> { ) -> Result<()> {
self.write(move |conn| { self.write(move |conn| {
let mut insert = conn let mut insert = conn
.exec_bound(sql!( .exec_bound(sql!(
INSERT INTO toolchains(workspace_id, worktree_id, language_name, name, path) VALUES (?, ?, ?, ?, ?) INSERT INTO toolchains(workspace_id, worktree_id, relative_worktree_path, language_name, name, path) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT DO ON CONFLICT DO
UPDATE SET UPDATE SET
name = ?4, name = ?4,
@ -1372,6 +1374,7 @@ impl WorkspaceDb {
insert(( insert((
workspace_id, workspace_id,
worktree_id.to_usize(), worktree_id.to_usize(),
relative_worktree_path,
toolchain.language_name.as_ref(), toolchain.language_name.as_ref(),
toolchain.name.as_ref(), toolchain.name.as_ref(),
toolchain.path.as_ref(), toolchain.path.as_ref(),

View file

@ -1273,10 +1273,10 @@ impl Workspace {
}; };
let toolchains = DB.toolchains(workspace_id).await?; let toolchains = DB.toolchains(workspace_id).await?;
for (toolchain, worktree_id) in toolchains { for (toolchain, worktree_id, path) in toolchains {
project_handle project_handle
.update(cx, |this, cx| { .update(cx, |this, cx| {
this.activate_toolchain(worktree_id, toolchain, cx) this.activate_toolchain(ProjectPath { worktree_id, path }, toolchain, cx)
})? })?
.await; .await;
} }
@ -6319,10 +6319,10 @@ pub fn open_ssh_project(
})?; })?;
let toolchains = DB.toolchains(workspace_id).await?; let toolchains = DB.toolchains(workspace_id).await?;
for (toolchain, worktree_id) in toolchains { for (toolchain, worktree_id, path) in toolchains {
project project
.update(cx, |this, cx| { .update(cx, |this, cx| {
this.activate_toolchain(worktree_id, toolchain, cx) this.activate_toolchain(ProjectPath { worktree_id, path }, toolchain, cx)
})? })?
.await; .await;
} }