Add language toolchains (#19576)

This PR adds support for selecting toolchains for a given language (e.g.
Rust toolchains or Python virtual environments) with support for SSH
projects provided out of the box. For Python we piggy-back off of
[PET](https://github.com/microsoft/python-environment-tools), a library
maintained by Microsoft.
Closes #16421
Closes #7646

Release Notes:

- Added toolchain selector to the status bar (with initial support for
Python virtual environments)
This commit is contained in:
Piotr Osiewicz 2024-10-28 15:34:03 +01:00 committed by GitHub
parent 03bd95405b
commit cdddb4d360
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 2221 additions and 133 deletions

View file

@ -11,6 +11,7 @@ pub mod search;
mod task_inventory;
pub mod task_store;
pub mod terminals;
pub mod toolchain_store;
pub mod worktree_store;
#[cfg(test)]
@ -44,8 +45,8 @@ use itertools::Itertools;
use language::{
language_settings::InlayHintKind, proto::split_operations, Buffer, BufferEvent,
CachedLspAdapter, Capability, CodeLabel, DiagnosticEntry, Documentation, File as _, Language,
LanguageRegistry, LanguageServerName, PointUtf16, ToOffset, ToPointUtf16, Transaction,
Unclipped,
LanguageName, LanguageRegistry, LanguageServerName, PointUtf16, ToOffset, ToPointUtf16,
Toolchain, ToolchainList, Transaction, Unclipped,
};
use lsp::{
CompletionContext, CompletionItemKind, DocumentHighlightKind, LanguageServer, LanguageServerId,
@ -101,7 +102,7 @@ pub use lsp_store::{
LanguageServerStatus, LanguageServerToQuery, LspStore, LspStoreEvent,
SERVER_PROGRESS_THROTTLE_TIMEOUT,
};
pub use toolchain_store::ToolchainStore;
const MAX_PROJECT_SEARCH_HISTORY_SIZE: usize = 500;
const MAX_SEARCH_RESULT_FILES: usize = 5_000;
const MAX_SEARCH_RESULT_RANGES: usize = 10_000;
@ -158,6 +159,7 @@ pub struct Project {
snippets: Model<SnippetProvider>,
environment: Model<ProjectEnvironment>,
settings_observer: Model<SettingsObserver>,
toolchain_store: Option<Model<ToolchainStore>>,
}
#[derive(Default)]
@ -579,6 +581,7 @@ impl Project {
LspStore::init(&client);
SettingsObserver::init(&client);
TaskStore::init(Some(&client));
ToolchainStore::init(&client);
}
pub fn local(
@ -635,12 +638,15 @@ impl Project {
});
cx.subscribe(&settings_observer, Self::on_settings_observer_event)
.detach();
let toolchain_store = cx.new_model(|cx| {
ToolchainStore::local(languages.clone(), worktree_store.clone(), cx)
});
let lsp_store = cx.new_model(|cx| {
LspStore::new_local(
buffer_store.clone(),
worktree_store.clone(),
prettier_store.clone(),
toolchain_store.clone(),
environment.clone(),
languages.clone(),
client.http_client(),
@ -681,6 +687,8 @@ impl Project {
search_included_history: Self::new_search_history(),
search_excluded_history: Self::new_search_history(),
toolchain_store: Some(toolchain_store),
}
})
}
@ -737,10 +745,14 @@ impl Project {
.detach();
let environment = ProjectEnvironment::new(&worktree_store, None, cx);
let toolchain_store = Some(cx.new_model(|cx| {
ToolchainStore::remote(SSH_PROJECT_ID, ssh.read(cx).proto_client(), cx)
}));
let lsp_store = cx.new_model(|cx| {
LspStore::new_remote(
buffer_store.clone(),
worktree_store.clone(),
toolchain_store.clone(),
languages.clone(),
ssh_proto.clone(),
SSH_PROJECT_ID,
@ -798,6 +810,8 @@ impl Project {
search_included_history: Self::new_search_history(),
search_excluded_history: Self::new_search_history(),
toolchain_store,
};
let ssh = ssh.read(cx);
@ -818,6 +832,7 @@ impl Project {
LspStore::init(&ssh_proto);
SettingsObserver::init(&ssh_proto);
TaskStore::init(Some(&ssh_proto));
ToolchainStore::init(&ssh_proto);
this
})
@ -905,6 +920,7 @@ impl Project {
let mut lsp_store = LspStore::new_remote(
buffer_store.clone(),
worktree_store.clone(),
None,
languages.clone(),
client.clone().into(),
remote_id,
@ -993,6 +1009,7 @@ impl Project {
search_excluded_history: Self::new_search_history(),
environment: ProjectEnvironment::new(&worktree_store, None, cx),
remotely_created_models: Arc::new(Mutex::new(RemotelyCreatedModels::default())),
toolchain_store: None,
};
this.set_role(role, cx);
for worktree in worktrees {
@ -2346,6 +2363,46 @@ impl Project {
.map_err(|e| anyhow!(e))
}
pub fn available_toolchains(
&self,
worktree_id: WorktreeId,
language_name: LanguageName,
cx: &AppContext,
) -> Task<Option<ToolchainList>> {
if let Some(toolchain_store) = self.toolchain_store.as_ref() {
toolchain_store
.read(cx)
.list_toolchains(worktree_id, language_name, cx)
} else {
Task::ready(None)
}
}
pub fn activate_toolchain(
&self,
worktree_id: WorktreeId,
toolchain: Toolchain,
cx: &mut AppContext,
) -> Task<Option<()>> {
let Some(toolchain_store) = self.toolchain_store.clone() else {
return Task::ready(None);
};
toolchain_store.update(cx, |this, cx| {
this.activate_toolchain(worktree_id, toolchain, cx)
})
}
pub fn active_toolchain(
&self,
worktree_id: WorktreeId,
language_name: LanguageName,
cx: &AppContext,
) -> Task<Option<Toolchain>> {
let Some(toolchain_store) = self.toolchain_store.clone() else {
return Task::ready(None);
};
toolchain_store
.read(cx)
.active_toolchain(worktree_id, language_name, cx)
}
pub fn language_server_statuses<'a>(
&'a self,
cx: &'a AppContext,