Skip to content

Commit

Permalink
feat: Add support for completing cargo update <TAB>
Browse files Browse the repository at this point in the history
  • Loading branch information
shannmu committed Sep 21, 2024
1 parent d7bffc3 commit 810acd6
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 5 deletions.
5 changes: 4 additions & 1 deletion src/bin/cargo/commands/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
128 changes: 124 additions & 4 deletions src/cargo/util/command_prelude.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -1174,6 +1178,122 @@ fn get_target_triples_from_rustc() -> CargoResult<Vec<clap_complete::CompletionC
.collect())
}

pub fn get_package_candidates() -> Vec<clap_complete::CompletionCandidate> {
let package_map = HashMap::<&str, Vec<Package>>::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::<Version, Vec<Package>>::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::<Vec<_>>()
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>()
}

fn get_packages() -> CargoResult<Vec<Package>> {
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::<Vec<_>>();

Ok(packages)
}

#[track_caller]
pub fn ignore_unknown<T: Default>(r: Result<T, clap::parser::MatchesError>) -> T {
match r {
Expand Down

0 comments on commit 810acd6

Please sign in to comment.