diff --git a/src/bin/cargo/commands/update.rs b/src/bin/cargo/commands/update.rs index 4c19bcb270d0..8c20ad923b52 100644 --- a/src/bin/cargo/commands/update.rs +++ b/src/bin/cargo/commands/update.rs @@ -13,7 +13,10 @@ pub fn cli() -> Command { .value_name("SPEC") .help_heading(heading::PACKAGE_SELECTION) .group("package-group") - .help("Package to update")]) + .help("Package to update") + .add(clap_complete::ArgValueCandidates::new( + get_package_candidates, + ))]) .arg( optional_multi_opt("package", "SPEC", "Package to update") .short('p') diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index 3069a3799715..fb90592d0c3d 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -1,9 +1,11 @@ -use crate::core::compiler::{BuildConfig, MessageFormat, TimingOutput}; -use crate::core::resolver::CliFeatures; -use crate::core::{shell, Edition, Target, TargetKind, Workspace}; +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::ops::lockfile::LOCKFILE_NAME; use crate::ops::registry::RegistryOrIndex; -use crate::ops::{CompileFilter, CompileOptions, NewOptions, Packages, VersionControl}; +use crate::ops::{self, CompileFilter, CompileOptions, NewOptions, Packages, VersionControl}; use crate::util::important_paths::find_root_manifest_for_wd; use crate::util::interning::InternedString; use crate::util::is_rustup; @@ -20,6 +22,8 @@ use cargo_util_schemas::manifest::RegistryName; use cargo_util_schemas::manifest::StringOrVec; use clap::builder::UnknownArgumentValueParser; use home::cargo_home_with_cwd; +use semver::Version; +use std::collections::HashMap; use std::ffi::{OsStr, OsString}; use std::path::Path; use std::path::PathBuf; @@ -1174,6 +1178,122 @@ fn get_target_triples_from_rustc() -> CargoResult Vec { + let package_map = HashMap::<&str, Vec>::new(); + + let package_map = + get_packages() + .unwrap_or_default() + .into_iter() + .fold(package_map, |mut map, package| { + map.entry(package.name().as_str()) + .or_insert_with(Vec::new) + .push(package); + map + }); + + package_map + .into_iter() + .flat_map(|(name, packages)| { + // For unique package name + if packages.len() == 1 { + return vec![ + clap_complete::CompletionCandidate::new(name.to_string()).help( + packages[0] + .manifest() + .metadata() + .description + .to_owned() + .map(From::from), + ), + ]; + } + + let version_map = HashMap::>::new(); + let version_map = packages.into_iter().fold(version_map, |mut map, package| { + map.entry(package.version().to_owned()) + .or_insert_with(Vec::new) + .push(package); + map + }); + + version_map + .into_iter() + .flat_map(|(version, packages)| { + // For package name with duplicates but unique version + if packages.len() == 1 { + return vec![clap_complete::CompletionCandidate::new(format!( + "{}@{}", + name, version + )) + .help( + packages[0] + .manifest() + .metadata() + .description + .to_owned() + .map(From::from), + )]; + } + + // For package name with duplicates and duplicate version + packages + .into_iter() + .map(|package| { + clap_complete::CompletionCandidate::new(format!( + "{}", + package.package_id().to_spec() + )) + .help( + package + .manifest() + .metadata() + .description + .to_owned() + .map(From::from), + ) + }) + .collect::>() + }) + .collect::>() + }) + .collect::>() +} + +fn get_packages() -> CargoResult> { + let cwd = std::env::current_dir()?; + let mut gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?); + gctx.configure(0, true, None, false, true, false, &None, &[], &[])?; + let ws = Workspace::new(&find_root_manifest_for_wd(&cwd)?, &gctx)?; + + let requested_kinds = CompileKind::from_requested_targets(ws.gctx(), &[])?; + let mut target_data = RustcTargetData::new(&ws, &requested_kinds)?; + // `cli_features.all_features` must be true in case that `specs` is empty. + let cli_features = CliFeatures::new_all(true); + let has_dev_units = HasDevUnits::Yes; + let force_all_targets = ForceAllTargets::No; + let dry_run = true; + + let ws_resolve = ops::resolve_ws_with_opts( + &ws, + &mut target_data, + &requested_kinds, + &cli_features, + &[], + has_dev_units, + force_all_targets, + dry_run, + )?; + + let packages = ws_resolve + .pkg_set + .packages() + .map(Clone::clone) + .collect::>(); + + Ok(packages) +} + #[track_caller] pub fn ignore_unknown(r: Result) -> T { match r {