From 9e5a0711bac2968c50b2bb4990003dfb5484f40d Mon Sep 17 00:00:00 2001 From: shannmu Date: Wed, 10 Jul 2024 22:46:17 +0800 Subject: [PATCH] feat(clap_complete): Support hiding long flags and their long aliases --- clap_builder/src/builder/arg.rs | 15 +++++ clap_complete/src/dynamic/completer.rs | 71 ++++++++++++++++++++---- clap_complete/tests/testsuite/dynamic.rs | 22 ++++---- 3 files changed, 88 insertions(+), 20 deletions(-) diff --git a/clap_builder/src/builder/arg.rs b/clap_builder/src/builder/arg.rs index d0929e515d39..3f97f2bb099f 100644 --- a/clap_builder/src/builder/arg.rs +++ b/clap_builder/src/builder/arg.rs @@ -3952,6 +3952,21 @@ impl Arg { Some(longs) } + /// Get hidden aliases for this argument, if any + #[inline] + pub fn get_hidden_aliases(&self) -> Option> { + if self.aliases.is_empty() { + None + } else { + Some( + self.aliases + .iter() + .filter_map(|(s, v)| if !*v { Some(s.as_str()) } else { None }) + .collect(), + ) + } + } + /// Get the names of possible values for this argument. Only useful for user /// facing applications, such as building help messages or man files pub fn get_possible_values(&self) -> Vec { diff --git a/clap_complete/src/dynamic/completer.rs b/clap_complete/src/dynamic/completer.rs index f2911ce153a3..e7ae49e774e5 100644 --- a/clap_complete/src/dynamic/completer.rs +++ b/clap_complete/src/dynamic/completer.rs @@ -118,6 +118,7 @@ fn complete_arg( os.to_string_lossy() )) .help(help) + .visible(true) }), ); } @@ -126,11 +127,18 @@ fn complete_arg( comp.get_content() .starts_with(format!("--{}", flag).as_str()) })); + + completions.extend(hidden_longs_aliases(cmd).into_iter().filter(|comp| { + comp.get_content() + .starts_with(format!("--{}", flag).as_str()) + })) } } } else if arg.is_escape() || arg.is_stdio() || arg.is_empty() { // HACK: Assuming knowledge of is_escape / is_stdio completions.extend(longs_and_visible_aliases(cmd)); + + completions.extend(hidden_longs_aliases(cmd)); } if arg.is_empty() || arg.is_stdio() || arg.is_short() { @@ -151,6 +159,7 @@ fn complete_arg( comp.get_content().to_string_lossy() )) .help(comp.get_help().cloned()) + .visible(true) }), ); } @@ -163,7 +172,7 @@ fn complete_arg( completions.extend( complete_arg_value(arg.to_value(), positional, current_dir) .into_iter() - .map(|(os, help)| CompletionCandidate::new(os).help(help)), + .map(|(os, help)| CompletionCandidate::new(os).help(help).visible(true)), ); } @@ -171,6 +180,10 @@ fn complete_arg( completions.extend(complete_subcommand(value, cmd)); } + if completions.iter().any(|a| a.is_visible()) { + completions.retain(|a| a.is_visible()) + } + Ok(completions) } @@ -297,7 +310,7 @@ fn complete_subcommand(value: &str, cmd: &clap::Command) -> Vec Vec { debug!("longs: name={}", p.get_name()); @@ -306,7 +319,27 @@ fn longs_and_visible_aliases(p: &clap::Command) -> Vec { .filter_map(|a| { a.get_long_and_visible_aliases().map(|longs| { longs.into_iter().map(|s| { - CompletionCandidate::new(format!("--{}", s)).help(a.get_help().cloned()) + CompletionCandidate::new(format!("--{}", s.to_string())) + .help(a.get_help().cloned()) + .visible(!a.is_hide_set()) + }) + }) + }) + .flatten() + .collect() +} + +/// Gets all the long hidden aliases and flags of a [`clap::Command`]. +fn hidden_longs_aliases(p: &clap::Command) -> Vec { + debug!("longs: name={}", p.get_name()); + + p.get_arguments() + .filter_map(|a| { + a.get_hidden_aliases().map(|longs| { + longs.into_iter().map(|s| { + CompletionCandidate::new(format!("--{}", s.to_string())) + .help(a.get_help().cloned()) + .visible(false) }) }) }) @@ -322,9 +355,11 @@ fn shorts_and_visible_aliases(p: &clap::Command) -> Vec { p.get_arguments() .filter_map(|a| { a.get_short_and_visible_aliases().map(|shorts| { - shorts - .into_iter() - .map(|s| CompletionCandidate::new(s.to_string()).help(a.get_help().cloned())) + shorts.into_iter().map(|s| { + CompletionCandidate::new(s.to_string()) + .help(a.get_help().cloned()) + .visible(!a.is_hide_set()) + }) }) }) .flatten() @@ -351,14 +386,16 @@ fn subcommands(p: &clap::Command) -> Vec { debug!("subcommands: Has subcommands...{:?}", p.has_subcommands()); p.get_subcommands() .flat_map(|sc| { - sc.get_name_and_visible_aliases() - .into_iter() - .map(|s| CompletionCandidate::new(s.to_string()).help(sc.get_about().cloned())) + sc.get_name_and_visible_aliases().into_iter().map(|s| { + CompletionCandidate::new(s.to_string()) + .help(sc.get_about().cloned()) + .visible(true) + }) }) .collect() } -/// A completion candidate defination +/// A completion candidate definition /// /// This makes it easier to add more fields to completion candidate, /// rather than using `(OsString, Option)` or `(String, Option)` to represent a completion candidate @@ -369,6 +406,9 @@ pub struct CompletionCandidate { /// Help message with a completion candidate help: Option, + + /// Whether the completion candidate is visible + visible: bool, } impl CompletionCandidate { @@ -387,6 +427,12 @@ impl CompletionCandidate { self } + /// Set the visibility of the completion candidate + pub fn visible(mut self, visible: bool) -> Self { + self.visible = visible; + self + } + /// Get the content of the completion candidate pub fn get_content(&self) -> &OsStr { &self.content @@ -396,4 +442,9 @@ impl CompletionCandidate { pub fn get_help(&self) -> Option<&StyledStr> { self.help.as_ref() } + + /// Get the visibility of the completion candidate + pub fn is_visible(&self) -> bool { + self.visible + } } diff --git a/clap_complete/tests/testsuite/dynamic.rs b/clap_complete/tests/testsuite/dynamic.rs index 3eb4803ccaf6..a024fa1e1a46 100644 --- a/clap_complete/tests/testsuite/dynamic.rs +++ b/clap_complete/tests/testsuite/dynamic.rs @@ -42,10 +42,7 @@ fn suggest_hidden_long_flags() { assert_data_eq!( complete!(cmd, "--hello-world"), - snapbox::str![ - "--hello-world-visible ---hello-world-hidden" - ] + snapbox::str!["--hello-world-visible"] ); assert_data_eq!( @@ -110,9 +107,7 @@ fn suggest_hidden_long_flag_aliases() { complete!(cmd, "--hello"), snapbox::str![ "--hello-world ---hello-world-visible ---hello-moon ---hello-moon-visible" +--hello-world-visible" ] ); @@ -120,13 +115,20 @@ fn suggest_hidden_long_flag_aliases() { complete!(cmd, "--hello-m"), snapbox::str![ "--hello-moon ---hello-moon-visible" +--hello-moon-visible +--hello-moon-hidden" ] ); - assert_data_eq!(complete!(cmd, "--hello-moon-h"), snapbox::str![""]); + assert_data_eq!( + complete!(cmd, "--hello-moon-h"), + snapbox::str!["--hello-moon-hidden"] + ); - assert_data_eq!(complete!(cmd, "--hello-world-h"), snapbox::str![""]); + assert_data_eq!( + complete!(cmd, "--hello-world-h"), + snapbox::str!["--hello-world-hidden"] + ); } #[test]