Skip to content

Commit 704096e

Browse files
committed
feat: Add support for completing cargo update <TAB>
1 parent d7bffc3 commit 704096e

File tree

2 files changed

+99
-5
lines changed

2 files changed

+99
-5
lines changed

src/bin/cargo/commands/update.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ pub fn cli() -> Command {
1313
.value_name("SPEC")
1414
.help_heading(heading::PACKAGE_SELECTION)
1515
.group("package-group")
16-
.help("Package to update")])
16+
.help("Package to update")
17+
.add(clap_complete::ArgValueCandidates::new(
18+
get_package_candidates,
19+
))])
1720
.arg(
1821
optional_multi_opt("package", "SPEC", "Package to update")
1922
.short('p')

src/cargo/util/command_prelude.rs

+95-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
use crate::core::compiler::{BuildConfig, MessageFormat, TimingOutput};
2-
use crate::core::resolver::CliFeatures;
3-
use crate::core::{shell, Edition, Target, TargetKind, Workspace};
1+
use crate::core::compiler::{
2+
BuildConfig, CompileKind, MessageFormat, RustcTargetData, TimingOutput,
3+
};
4+
use crate::core::resolver::{CliFeatures, ForceAllTargets, HasDevUnits};
5+
use crate::core::{shell, Edition, Package, Target, TargetKind, Workspace};
46
use crate::ops::lockfile::LOCKFILE_NAME;
57
use crate::ops::registry::RegistryOrIndex;
6-
use crate::ops::{CompileFilter, CompileOptions, NewOptions, Packages, VersionControl};
8+
use crate::ops::{self, CompileFilter, CompileOptions, NewOptions, Packages, VersionControl};
79
use crate::util::important_paths::find_root_manifest_for_wd;
810
use crate::util::interning::InternedString;
911
use crate::util::is_rustup;
@@ -20,6 +22,8 @@ use cargo_util_schemas::manifest::RegistryName;
2022
use cargo_util_schemas::manifest::StringOrVec;
2123
use clap::builder::UnknownArgumentValueParser;
2224
use home::cargo_home_with_cwd;
25+
use semver::Version;
26+
use std::collections::HashMap;
2327
use std::ffi::{OsStr, OsString};
2428
use std::path::Path;
2529
use std::path::PathBuf;
@@ -1174,6 +1178,93 @@ fn get_target_triples_from_rustc() -> CargoResult<Vec<clap_complete::CompletionC
11741178
.collect())
11751179
}
11761180

1181+
pub fn get_package_candidates() -> Vec<clap_complete::CompletionCandidate> {
1182+
let package_map = HashMap::<&str, Vec<Package>>::new();
1183+
1184+
let package_map =
1185+
get_packages()
1186+
.unwrap_or_default()
1187+
.into_iter()
1188+
.fold(package_map, |mut map, package| {
1189+
map.entry(package.name().as_str())
1190+
.or_insert_with(Vec::new)
1191+
.push(package);
1192+
map
1193+
});
1194+
1195+
package_map
1196+
.into_iter()
1197+
.flat_map(|(name, packages)| {
1198+
// For unique package name
1199+
if packages.len() == 1 {
1200+
return vec![clap_complete::CompletionCandidate::new(name.to_string())];
1201+
}
1202+
1203+
let version_map = HashMap::<Version, Vec<Package>>::new();
1204+
let version_map = packages.into_iter().fold(version_map, |mut map, package| {
1205+
map.entry(package.version().to_owned())
1206+
.or_insert_with(Vec::new)
1207+
.push(package);
1208+
map
1209+
});
1210+
1211+
version_map
1212+
.into_iter()
1213+
.flat_map(|(version, packages)| {
1214+
// For package name with duplicates but unique version
1215+
if packages.len() == 1 {
1216+
return vec![clap_complete::CompletionCandidate::new(format!(
1217+
"{}@{}",
1218+
name, version
1219+
))];
1220+
}
1221+
1222+
// For package name with duplicates and duplicate version
1223+
packages
1224+
.into_iter()
1225+
.map(|package| format!("{}", package.package_id().to_spec()))
1226+
.map(clap_complete::CompletionCandidate::new)
1227+
.collect::<Vec<_>>()
1228+
})
1229+
.collect::<Vec<_>>()
1230+
})
1231+
.collect::<Vec<_>>()
1232+
}
1233+
1234+
fn get_packages() -> CargoResult<Vec<Package>> {
1235+
let cwd = std::env::current_dir()?;
1236+
let mut gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?);
1237+
gctx.configure(0, true, None, false, true, false, &None, &[], &[])?;
1238+
let ws = Workspace::new(&find_root_manifest_for_wd(&cwd)?, &gctx)?;
1239+
1240+
let requested_kinds = CompileKind::from_requested_targets(ws.gctx(), &[])?;
1241+
let mut target_data = RustcTargetData::new(&ws, &requested_kinds)?;
1242+
// `cli_features.all_features` must be true in case that `specs` is empty.
1243+
let cli_features = CliFeatures::new_all(true);
1244+
let has_dev_units = HasDevUnits::Yes;
1245+
let force_all_targets = ForceAllTargets::No;
1246+
let dry_run = true;
1247+
1248+
let ws_resolve = ops::resolve_ws_with_opts(
1249+
&ws,
1250+
&mut target_data,
1251+
&requested_kinds,
1252+
&cli_features,
1253+
&[],
1254+
has_dev_units,
1255+
force_all_targets,
1256+
dry_run,
1257+
)?;
1258+
1259+
let packages = ws_resolve
1260+
.pkg_set
1261+
.packages()
1262+
.map(Clone::clone)
1263+
.collect::<Vec<_>>();
1264+
1265+
Ok(packages)
1266+
}
1267+
11771268
#[track_caller]
11781269
pub fn ignore_unknown<T: Default>(r: Result<T, clap::parser::MatchesError>) -> T {
11791270
match r {

0 commit comments

Comments
 (0)