Skip to content

Commit 810acd6

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

File tree

2 files changed

+128
-5
lines changed

2 files changed

+128
-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

+124-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,122 @@ 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![
1201+
clap_complete::CompletionCandidate::new(name.to_string()).help(
1202+
packages[0]
1203+
.manifest()
1204+
.metadata()
1205+
.description
1206+
.to_owned()
1207+
.map(From::from),
1208+
),
1209+
];
1210+
}
1211+
1212+
let version_map = HashMap::<Version, Vec<Package>>::new();
1213+
let version_map = packages.into_iter().fold(version_map, |mut map, package| {
1214+
map.entry(package.version().to_owned())
1215+
.or_insert_with(Vec::new)
1216+
.push(package);
1217+
map
1218+
});
1219+
1220+
version_map
1221+
.into_iter()
1222+
.flat_map(|(version, packages)| {
1223+
// For package name with duplicates but unique version
1224+
if packages.len() == 1 {
1225+
return vec![clap_complete::CompletionCandidate::new(format!(
1226+
"{}@{}",
1227+
name, version
1228+
))
1229+
.help(
1230+
packages[0]
1231+
.manifest()
1232+
.metadata()
1233+
.description
1234+
.to_owned()
1235+
.map(From::from),
1236+
)];
1237+
}
1238+
1239+
// For package name with duplicates and duplicate version
1240+
packages
1241+
.into_iter()
1242+
.map(|package| {
1243+
clap_complete::CompletionCandidate::new(format!(
1244+
"{}",
1245+
package.package_id().to_spec()
1246+
))
1247+
.help(
1248+
package
1249+
.manifest()
1250+
.metadata()
1251+
.description
1252+
.to_owned()
1253+
.map(From::from),
1254+
)
1255+
})
1256+
.collect::<Vec<_>>()
1257+
})
1258+
.collect::<Vec<_>>()
1259+
})
1260+
.collect::<Vec<_>>()
1261+
}
1262+
1263+
fn get_packages() -> CargoResult<Vec<Package>> {
1264+
let cwd = std::env::current_dir()?;
1265+
let mut gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?);
1266+
gctx.configure(0, true, None, false, true, false, &None, &[], &[])?;
1267+
let ws = Workspace::new(&find_root_manifest_for_wd(&cwd)?, &gctx)?;
1268+
1269+
let requested_kinds = CompileKind::from_requested_targets(ws.gctx(), &[])?;
1270+
let mut target_data = RustcTargetData::new(&ws, &requested_kinds)?;
1271+
// `cli_features.all_features` must be true in case that `specs` is empty.
1272+
let cli_features = CliFeatures::new_all(true);
1273+
let has_dev_units = HasDevUnits::Yes;
1274+
let force_all_targets = ForceAllTargets::No;
1275+
let dry_run = true;
1276+
1277+
let ws_resolve = ops::resolve_ws_with_opts(
1278+
&ws,
1279+
&mut target_data,
1280+
&requested_kinds,
1281+
&cli_features,
1282+
&[],
1283+
has_dev_units,
1284+
force_all_targets,
1285+
dry_run,
1286+
)?;
1287+
1288+
let packages = ws_resolve
1289+
.pkg_set
1290+
.packages()
1291+
.map(Clone::clone)
1292+
.collect::<Vec<_>>();
1293+
1294+
Ok(packages)
1295+
}
1296+
11771297
#[track_caller]
11781298
pub fn ignore_unknown<T: Default>(r: Result<T, clap::parser::MatchesError>) -> T {
11791299
match r {

0 commit comments

Comments
 (0)