Skip to content

Commit 90690c5

Browse files
committed
Auto merge of #14552 - shannmu:_cargo_update_completer, r=epage
feat: Add support for completing `cargo update <TAB>` ### What does this PR try to resolve? Tracking issue #14520 Add custom completer for `cargo update <TAB>`
2 parents 844457c + 373dcc2 commit 90690c5

File tree

2 files changed

+161
-5
lines changed

2 files changed

+161
-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_pkg_id_spec_candidates,
19+
))])
1720
.arg(
1821
optional_multi_opt("package", "SPEC", "Package to update")
1922
.short('p')

src/cargo/util/command_prelude.rs

+157-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,155 @@ fn get_target_triples_from_rustc() -> CargoResult<Vec<clap_complete::CompletionC
11741178
.collect())
11751179
}
11761180

1181+
pub fn get_pkg_id_spec_candidates() -> Vec<clap_complete::CompletionCandidate> {
1182+
let mut candidates = vec![];
1183+
1184+
let package_map = HashMap::<&str, Vec<Package>>::new();
1185+
let package_map =
1186+
get_packages()
1187+
.unwrap_or_default()
1188+
.into_iter()
1189+
.fold(package_map, |mut map, package| {
1190+
map.entry(package.name().as_str())
1191+
.or_insert_with(Vec::new)
1192+
.push(package);
1193+
map
1194+
});
1195+
1196+
let unique_name_candidates = package_map
1197+
.iter()
1198+
.filter(|(_name, packages)| packages.len() == 1)
1199+
.map(|(name, packages)| {
1200+
clap_complete::CompletionCandidate::new(name.to_string()).help(
1201+
packages[0]
1202+
.manifest()
1203+
.metadata()
1204+
.description
1205+
.to_owned()
1206+
.map(From::from),
1207+
)
1208+
})
1209+
.collect::<Vec<_>>();
1210+
1211+
let duplicate_name_pairs = package_map
1212+
.iter()
1213+
.filter(|(_name, packages)| packages.len() > 1)
1214+
.collect::<Vec<_>>();
1215+
1216+
let mut duplicate_name_candidates = vec![];
1217+
for (name, packages) in duplicate_name_pairs {
1218+
let mut version_count: HashMap<&Version, usize> = HashMap::new();
1219+
1220+
for package in packages {
1221+
*version_count.entry(package.version()).or_insert(0) += 1;
1222+
}
1223+
1224+
for package in packages {
1225+
if let Some(&count) = version_count.get(package.version()) {
1226+
if count == 1 {
1227+
duplicate_name_candidates.push(
1228+
clap_complete::CompletionCandidate::new(format!(
1229+
"{}@{}",
1230+
name,
1231+
package.version()
1232+
))
1233+
.help(
1234+
package
1235+
.manifest()
1236+
.metadata()
1237+
.description
1238+
.to_owned()
1239+
.map(From::from),
1240+
),
1241+
);
1242+
} else {
1243+
duplicate_name_candidates.push(
1244+
clap_complete::CompletionCandidate::new(format!(
1245+
"{}",
1246+
package.package_id().to_spec()
1247+
))
1248+
.help(
1249+
package
1250+
.manifest()
1251+
.metadata()
1252+
.description
1253+
.to_owned()
1254+
.map(From::from),
1255+
),
1256+
)
1257+
}
1258+
}
1259+
}
1260+
}
1261+
1262+
candidates.extend(unique_name_candidates);
1263+
candidates.extend(duplicate_name_candidates);
1264+
1265+
candidates
1266+
}
1267+
1268+
fn get_packages() -> CargoResult<Vec<Package>> {
1269+
let gctx = new_gctx_for_completions()?;
1270+
1271+
let ws = Workspace::new(&find_root_manifest_for_wd(gctx.cwd())?, &gctx)?;
1272+
1273+
let requested_kinds = CompileKind::from_requested_targets(ws.gctx(), &[])?;
1274+
let mut target_data = RustcTargetData::new(&ws, &requested_kinds)?;
1275+
// `cli_features.all_features` must be true in case that `specs` is empty.
1276+
let cli_features = CliFeatures::new_all(true);
1277+
let has_dev_units = HasDevUnits::Yes;
1278+
let force_all_targets = ForceAllTargets::No;
1279+
let dry_run = true;
1280+
1281+
let ws_resolve = ops::resolve_ws_with_opts(
1282+
&ws,
1283+
&mut target_data,
1284+
&requested_kinds,
1285+
&cli_features,
1286+
&[],
1287+
has_dev_units,
1288+
force_all_targets,
1289+
dry_run,
1290+
)?;
1291+
1292+
let packages = ws_resolve
1293+
.pkg_set
1294+
.packages()
1295+
.map(Clone::clone)
1296+
.collect::<Vec<_>>();
1297+
1298+
Ok(packages)
1299+
}
1300+
1301+
fn new_gctx_for_completions() -> CargoResult<GlobalContext> {
1302+
let cwd = std::env::current_dir()?;
1303+
let mut gctx = GlobalContext::new(shell::Shell::new(), cwd.clone(), cargo_home_with_cwd(&cwd)?);
1304+
1305+
let verbose = 0;
1306+
let quiet = true;
1307+
let color = None;
1308+
let frozen = false;
1309+
let locked = true;
1310+
let offline = false;
1311+
let target_dir = None;
1312+
let unstable_flags = &[];
1313+
let cli_config = &[];
1314+
1315+
gctx.configure(
1316+
verbose,
1317+
quiet,
1318+
color,
1319+
frozen,
1320+
locked,
1321+
offline,
1322+
&target_dir,
1323+
unstable_flags,
1324+
cli_config,
1325+
)?;
1326+
1327+
Ok(gctx)
1328+
}
1329+
11771330
#[track_caller]
11781331
pub fn ignore_unknown<T: Default>(r: Result<T, clap::parser::MatchesError>) -> T {
11791332
match r {

0 commit comments

Comments
 (0)