Skip to content

Commit 3138b5d

Browse files
committed
feat(completer): Added completion for --features flag
1 parent bc577dc commit 3138b5d

File tree

1 file changed

+81
-1
lines changed

1 file changed

+81
-1
lines changed

src/cargo/util/command_prelude.rs

+81-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use clap::builder::UnknownArgumentValueParser;
2525
use home::cargo_home_with_cwd;
2626
use semver::Version;
2727
use std::collections::HashMap;
28+
use std::collections::HashSet;
2829
use std::ffi::{OsStr, OsString};
2930
use std::path::Path;
3031
use std::path::PathBuf;
@@ -246,7 +247,11 @@ pub trait CommandExt: Sized {
246247
"Space or comma separated list of features to activate",
247248
)
248249
.short('F')
249-
.help_heading(heading::FEATURE_SELECTION),
250+
.help_heading(heading::FEATURE_SELECTION)
251+
.add(clap_complete::ArgValueCandidates::new(|| {
252+
let candidates = get_feature_candidates();
253+
candidates.unwrap_or_default()
254+
})),
250255
)
251256
._arg(
252257
flag("all-features", "Activate all available features")
@@ -1106,6 +1111,81 @@ pub fn get_registry_candidates() -> CargoResult<Vec<clap_complete::CompletionCan
11061111
}
11071112
}
11081113

1114+
fn get_feature_candidates() -> CargoResult<Vec<clap_complete::CompletionCandidate>> {
1115+
let gctx = new_gctx_for_completions()?;
1116+
1117+
let manifest_path = match find_root_manifest_for_wd(gctx.cwd()) {
1118+
Ok(path) => path,
1119+
Err(_) => return Ok(Vec::new()),
1120+
};
1121+
1122+
let ws = match Workspace::new(&manifest_path, &gctx) {
1123+
Ok(ws) => ws,
1124+
Err(_) => return Ok(Vec::new()),
1125+
};
1126+
1127+
let current_dir = std::env::current_dir()?;
1128+
1129+
let current_pkg = ws
1130+
.members()
1131+
.find(|pkg| current_dir.starts_with(pkg.root()))
1132+
.or_else(|| ws.members().next())
1133+
.cloned();
1134+
1135+
let mut features = HashSet::new();
1136+
1137+
if let Some(package) = current_pkg {
1138+
for feature_name in package.summary().features().keys() {
1139+
features.insert(feature_name.as_str().to_string());
1140+
}
1141+
1142+
for dep in package.dependencies() {
1143+
if dep.is_optional() {
1144+
features.insert(dep.name_in_toml().to_string());
1145+
}
1146+
}
1147+
1148+
let mut package_features = std::collections::HashMap::new();
1149+
1150+
for pkg in ws.members() {
1151+
let pkg_name = pkg.name().as_str().to_string();
1152+
let pkg_features: Vec<String> = pkg
1153+
.summary()
1154+
.features()
1155+
.keys()
1156+
.map(|k| k.as_str().to_string())
1157+
.collect();
1158+
1159+
package_features.insert(pkg_name, pkg_features);
1160+
}
1161+
1162+
for dep in package.dependencies() {
1163+
let dep_name = dep.package_name().as_str().to_string();
1164+
1165+
if let Some(dep_features) = package_features.get(&dep_name) {
1166+
for feat in dep_features {
1167+
features.insert(format!("{}/{}", dep_name, feat));
1168+
}
1169+
}
1170+
}
1171+
} else {
1172+
// If we couldn't determine the current package, collect features from all workspace members
1173+
for pkg in ws.members() {
1174+
for feature_name in pkg.summary().features().keys() {
1175+
features.insert(feature_name.as_str().to_string());
1176+
}
1177+
}
1178+
}
1179+
1180+
// always include the default feature
1181+
features.insert("default".to_string());
1182+
1183+
Ok(features
1184+
.into_iter()
1185+
.map(|name| clap_complete::CompletionCandidate::new(name))
1186+
.collect())
1187+
}
1188+
11091189
fn get_example_candidates() -> Vec<clap_complete::CompletionCandidate> {
11101190
get_targets_from_metadata()
11111191
.unwrap_or_default()

0 commit comments

Comments
 (0)