task: Poll Rust subcommands on background thread (#28553)

Closes #ISSUE

Release Notes:

- Improved app responsiveness when spawning Rust tasks.
This commit is contained in:
Piotr Osiewicz 2025-04-11 13:04:10 +02:00 committed by GitHub
parent bd4c9b45b6
commit cdcad708f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -3,7 +3,7 @@ use async_compression::futures::bufread::GzipDecoder;
use async_trait::async_trait; use async_trait::async_trait;
use collections::HashMap; use collections::HashMap;
use futures::{StreamExt, io::BufReader}; use futures::{StreamExt, io::BufReader};
use gpui::{App, AsyncApp, SharedString, Task}; use gpui::{App, AppContext, AsyncApp, SharedString, Task};
use http_client::github::AssetKind; use http_client::github::AssetKind;
use http_client::github::{GitHubLspBinaryVersion, latest_github_release}; use http_client::github::{GitHubLspBinaryVersion, latest_github_release};
pub use language::*; pub use language::*;
@ -548,46 +548,11 @@ impl ContextProvider for RustContextProvider {
.file() .file()
.and_then(|file| Some(file.as_local()?.abs_path(cx))); .and_then(|file| Some(file.as_local()?.abs_path(cx)));
let local_abs_path = local_abs_path.as_deref();
let mut variables = TaskVariables::default(); let mut variables = TaskVariables::default();
if let Some(target) = if let (Some(path), Some(stem)) = (&local_abs_path, task_variables.get(&VariableName::Stem))
local_abs_path.and_then(|path| target_info_from_abs_path(path, project_env.as_ref()))
{ {
variables.extend(TaskVariables::from_iter([ let fragment = test_fragment(&variables, &path, stem);
(RUST_PACKAGE_TASK_VARIABLE.clone(), target.package_name),
(RUST_BIN_NAME_TASK_VARIABLE.clone(), target.target_name),
(
RUST_BIN_KIND_TASK_VARIABLE.clone(),
target.target_kind.to_string(),
),
]));
if target.required_features.is_empty() {
variables.insert(RUST_BIN_REQUIRED_FEATURES_FLAG_TASK_VARIABLE, "".into());
variables.insert(RUST_BIN_REQUIRED_FEATURES_TASK_VARIABLE, "".into());
} else {
variables.insert(
RUST_BIN_REQUIRED_FEATURES_FLAG_TASK_VARIABLE.clone(),
"--features".to_string(),
);
variables.insert(
RUST_BIN_REQUIRED_FEATURES_TASK_VARIABLE.clone(),
target.required_features.join(","),
);
}
}
if let Some(package_name) = local_abs_path
.and_then(|local_abs_path| local_abs_path.parent())
.and_then(|path| human_readable_package_name(path, project_env.as_ref()))
{
variables.insert(RUST_PACKAGE_TASK_VARIABLE.clone(), package_name);
}
if let (Some(path), Some(stem)) = (local_abs_path, task_variables.get(&VariableName::Stem))
{
let fragment = test_fragment(&variables, path, stem);
variables.insert(RUST_TEST_FRAGMENT_TASK_VARIABLE, fragment); variables.insert(RUST_TEST_FRAGMENT_TASK_VARIABLE, fragment);
}; };
if let Some(test_name) = if let Some(test_name) =
@ -600,8 +565,44 @@ impl ContextProvider for RustContextProvider {
{ {
variables.insert(RUST_DOC_TEST_NAME_TASK_VARIABLE, doc_test_name.into()); variables.insert(RUST_DOC_TEST_NAME_TASK_VARIABLE, doc_test_name.into());
} }
cx.background_spawn(async move {
Task::ready(Ok(variables)) if let Some(path) = local_abs_path
.as_deref()
.and_then(|local_abs_path| local_abs_path.parent())
{
if let Some(package_name) =
human_readable_package_name(path, project_env.as_ref()).await
{
variables.insert(RUST_PACKAGE_TASK_VARIABLE.clone(), package_name);
}
}
if let Some(path) = local_abs_path.as_ref() {
if let Some(target) = target_info_from_abs_path(&path, project_env.as_ref()).await {
variables.extend(TaskVariables::from_iter([
(RUST_PACKAGE_TASK_VARIABLE.clone(), target.package_name),
(RUST_BIN_NAME_TASK_VARIABLE.clone(), target.target_name),
(
RUST_BIN_KIND_TASK_VARIABLE.clone(),
target.target_kind.to_string(),
),
]));
if target.required_features.is_empty() {
variables.insert(RUST_BIN_REQUIRED_FEATURES_FLAG_TASK_VARIABLE, "".into());
variables.insert(RUST_BIN_REQUIRED_FEATURES_TASK_VARIABLE, "".into());
} else {
variables.insert(
RUST_BIN_REQUIRED_FEATURES_FLAG_TASK_VARIABLE.clone(),
"--features".to_string(),
);
variables.insert(
RUST_BIN_REQUIRED_FEATURES_TASK_VARIABLE.clone(),
target.required_features.join(","),
);
}
}
}
Ok(variables)
})
} }
fn associated_tasks( fn associated_tasks(
@ -888,11 +889,11 @@ struct TargetInfo {
required_features: Vec<String>, required_features: Vec<String>,
} }
fn target_info_from_abs_path( async fn target_info_from_abs_path(
abs_path: &Path, abs_path: &Path,
project_env: Option<&HashMap<String, String>>, project_env: Option<&HashMap<String, String>>,
) -> Option<TargetInfo> { ) -> Option<TargetInfo> {
let mut command = util::command::new_std_command("cargo"); let mut command = util::command::new_smol_command("cargo");
if let Some(envs) = project_env { if let Some(envs) = project_env {
command.envs(envs); command.envs(envs);
} }
@ -903,6 +904,7 @@ fn target_info_from_abs_path(
.arg("--format-version") .arg("--format-version")
.arg("1") .arg("1")
.output() .output()
.await
.log_err()? .log_err()?
.stdout; .stdout;
@ -936,11 +938,11 @@ fn target_info_from_metadata(metadata: CargoMetadata, abs_path: &Path) -> Option
None None
} }
fn human_readable_package_name( async fn human_readable_package_name(
package_directory: &Path, package_directory: &Path,
project_env: Option<&HashMap<String, String>>, project_env: Option<&HashMap<String, String>>,
) -> Option<String> { ) -> Option<String> {
let mut command = util::command::new_std_command("cargo"); let mut command = util::command::new_smol_command("cargo");
if let Some(envs) = project_env { if let Some(envs) = project_env {
command.envs(envs); command.envs(envs);
} }
@ -949,6 +951,7 @@ fn human_readable_package_name(
.current_dir(package_directory) .current_dir(package_directory)
.arg("pkgid") .arg("pkgid")
.output() .output()
.await
.log_err()? .log_err()?
.stdout, .stdout,
) )
@ -1036,7 +1039,7 @@ mod tests {
use super::*; use super::*;
use crate::language; use crate::language;
use gpui::{AppContext as _, BorrowAppContext, Hsla, TestAppContext}; use gpui::{BorrowAppContext, Hsla, TestAppContext};
use language::language_settings::AllLanguageSettings; use language::language_settings::AllLanguageSettings;
use lsp::CompletionItemLabelDetails; use lsp::CompletionItemLabelDetails;
use settings::SettingsStore; use settings::SettingsStore;