diff --git a/src/cargo/core/profiles.rs b/src/cargo/core/profiles.rs index cac149efaa7..86d64d668d8 100644 --- a/src/cargo/core/profiles.rs +++ b/src/cargo/core/profiles.rs @@ -391,6 +391,11 @@ impl Profiles { .get(name) .ok_or_else(|| anyhow::format_err!("profile `{}` is not defined", name)) } + + /// Returns an iterator over all profile names known to Cargo. + pub fn profile_names(&self) -> impl Iterator + '_ { + self.by_name.keys().copied() + } } /// An object used for handling the profile hierarchy. diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index 0b11ac4f0d1..f492fca2ab6 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -2,7 +2,7 @@ use crate::core::compiler::{ BuildConfig, CompileKind, MessageFormat, RustcTargetData, TimingOutput, }; use crate::core::resolver::{CliFeatures, ForceAllTargets, HasDevUnits}; -use crate::core::{shell, Edition, Package, Target, TargetKind, Workspace}; +use crate::core::{profiles::Profiles, shell, Edition, Package, Target, TargetKind, Workspace}; use crate::ops::lockfile::LOCKFILE_NAME; use crate::ops::registry::RegistryOrIndex; use crate::ops::{self, CompileFilter, CompileOptions, NewOptions, Packages, VersionControl}; @@ -274,7 +274,11 @@ pub trait CommandExt: Sized { self._arg( opt("profile", profile) .value_name("PROFILE-NAME") - .help_heading(heading::COMPILATION_OPTIONS), + .help_heading(heading::COMPILATION_OPTIONS) + .add(clap_complete::ArgValueCandidates::new(|| { + let candidates = get_profile_candidates(); + candidates + })), ) } @@ -1106,6 +1110,53 @@ pub fn get_registry_candidates() -> CargoResult Vec { + match get_workspace_profile_candidates() { + Ok(candidates) if !candidates.is_empty() => candidates, + // fallback to default profile candidates + _ => default_profile_candidates(), + } +} + +fn get_workspace_profile_candidates() -> CargoResult> { + let gctx = new_gctx_for_completions()?; + let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?; + let profiles = Profiles::new(&ws, InternedString::new("dev"))?; + + let mut candidates = Vec::new(); + for name in profiles.profile_names() { + let Ok(profile_instance) = Profiles::new(&ws, name) else { + continue; + }; + let base_profile = profile_instance.base_profile(); + + let mut description = String::from(if base_profile.opt_level.as_str() == "0" { + "unoptimized" + } else { + "optimized" + }); + + if base_profile.debuginfo.is_turned_on() { + description.push_str(" + debuginfo"); + } + + candidates + .push(clap_complete::CompletionCandidate::new(&name).help(Some(description.into()))); + } + + Ok(candidates) +} + +fn default_profile_candidates() -> Vec { + vec![ + clap_complete::CompletionCandidate::new("dev").help(Some("unoptimized + debuginfo".into())), + clap_complete::CompletionCandidate::new("release").help(Some("optimized".into())), + clap_complete::CompletionCandidate::new("test") + .help(Some("unoptimized + debuginfo".into())), + clap_complete::CompletionCandidate::new("bench").help(Some("optimized".into())), + ] +} + fn get_example_candidates() -> Vec { get_targets_from_metadata() .unwrap_or_default()