@@ -25,6 +25,7 @@ use clap::builder::UnknownArgumentValueParser;
25
25
use home:: cargo_home_with_cwd;
26
26
use semver:: Version ;
27
27
use std:: collections:: HashMap ;
28
+ use std:: collections:: HashSet ;
28
29
use std:: ffi:: { OsStr , OsString } ;
29
30
use std:: path:: Path ;
30
31
use std:: path:: PathBuf ;
@@ -246,7 +247,11 @@ pub trait CommandExt: Sized {
246
247
"Space or comma separated list of features to activate" ,
247
248
)
248
249
. 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
+ } ) ) ,
250
255
)
251
256
. _arg (
252
257
flag ( "all-features" , "Activate all available features" )
@@ -1106,6 +1111,81 @@ pub fn get_registry_candidates() -> CargoResult<Vec<clap_complete::CompletionCan
1106
1111
}
1107
1112
}
1108
1113
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
+
1109
1189
fn get_example_candidates ( ) -> Vec < clap_complete:: CompletionCandidate > {
1110
1190
get_targets_from_metadata ( )
1111
1191
. unwrap_or_default ( )
0 commit comments