-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for automatic negation flags #815
Comments
Great proposal @kbknapp. Having a Perhaps this could even be the default, considering that v3 is still in beta? The user would then have to call |
Not all single flags are need overridable flags. From looking at the clis, this is a minority use case. Which is why this won't be a default behaviour. |
I'll subscribe to the issue for updates. In the meantime I've got a variant of @blyxxyz's snippet working for me. Thanks! |
While I don't think this is the way we should go, I thought I'd record how CLI11 solves this problem: runtime parsing of a declaration string. See https://cliutils.github.io/CLI11/book/chapters/flags.html |
I'd really like this to render as `--preview / --no-preview`, but I looked for a while in the Clap internals and issue tracker (clap-rs/clap#815) and I really can't figure out a way to do it -- this seems like the best we can do? It's also what they do in Orogene. Closes #7486.
Hi, is there any updates? Thanks! Especially, it would be great to have one in Parser derivation mode, instead of the builder mode. P.S. I found https://jwodder.github.io/kbits/posts/clap-bool-negate/ but the workaround is not super neat IMHO |
FWIW, in xh we've moved to a still cleaner implementation by enabling clap's let negations: Vec<_> = app
.get_arguments()
.filter(|a| !a.is_positional())
.map(|opt| {
let long = opt.get_long().expect("long option");
clap::Arg::new(format!("no-{}", long))
.long(format!("no-{}", long))
.hide(true)
.action(ArgAction::SetTrue)
// overrides_with is enough to make the flags take effect
// We never have to check their values, they'll simply
// unset previous occurrences of the original flag
.overrides_with(opt.get_id())
})
.collect();
app.args(negations)
.after_help("Each option can be reset with a --no-OPTION argument.") It no longer feels like a hacky workaround so I'm pretty happy with it. It's cool how flexible clap has become. |
For my specific use-case, it would be perfect for me if I could use |
In my experience you only use |
Thank you! That looks helpful |
If the #[derive(Parser)] could implement a boolean flag like Python click, @click.option('--color/--no-color', default=False)
@click.option('--enable-option/--disable-option', default=False)
@click.option('--with-something/--without-something', default=False) Imagine: /// Given that the default value is true, providing a short option should be considered as false. And vice versa.
#[arg(short = "C", long = "--color/--no-color", default_value = "true")]
color_flag: bool,
/// Explicitly providing a short option maps to true/false.
#[arg(short = "o/O", long = "--enable-option/--disable-option", default_value = "true")]
some_option_flag: bool, |
Would love to see this added! |
I would love this as well. Like others in this thread, I don't think we should try to do this automatically; I'd be happy to see an explicit way for the builder and declarative APIs to add a This option could work for a boolean, as well as for an #[arg(long, negation)]
pub auto: bool,
#[arg(long, default_value = "hello", negation)]
pub value: Option<String>, |
@joshtriplett interesting to also include this for options. We also would need to define the builder API and what the semantics for this would be (parser, help, etc). |
If #[arg(long, default_value_t = true, negation)]
pub auto: bool, In some way, |
I don't quite get this. Flags already require long/short. This is a modification to flag state. For someone to add
There are lots of ways of implementing this and they don't necessarily preclude this:
Overall though, this discussion has a fatal flaw: we are designing a feature around derive semantics and discussing what is or isn't possible. We need to focus on the builder semantics and then decide what automatic behavior we want to the derive semantics to have on top of that. There can be some back and forth on that (the derive influencing the builder design). Thats a big value-add of merging structopt into clap! We've run into many problems where the builder API made choices that work well in isolation but don't work well for builder and have worked to correct them. |
Maybe I'm misunderstanding what you imply. I didn't say that
This doesn't have the same user experience. The best workaround right now is to explictly define the additional
That's probably related to the misunderstanding above. What if @joshtriplett example was actually the following? #[arg(negation)]
pub auto: bool, What would it mean? What is the name of the added flag? And similarly if it was
I thought the builder and derive semantics were isomorphic since it seems derive attributes cover all builder functions. I never used the builder so maybe I'm missing some functionalities. That said, I agree that proposals need a clear semantics, and I hope the one I gave above is clear enough (for both builder and derive). |
More generally it might be desirable to support multiple argument names controlling the same value (with different actions) that are all automatically exclusive with eachother, with some shorthands to better support more common use cases (bools, options). Working out all of the semantics of this more general approach could make it much easier to define the semantics of the shorthand forms, as simply being equivalent to some usage of the general API. Admittedly the current API is very value-centric, making it a bit hard to imagine how this feature could be used, but being able to assign multiple flag-action pairings to the same value would be your starting point. |
Add a way to automatically generate flags that override (or negate) other flags. This can be done manually already, but doing so for an entire CLI can be tedious, painful, and error prone. Manually doing so will also pollute the
--help
output.This proposal offers a way to automatically have these negation flags generated on a case by case basis, or across all flags in the command. This proposal also offers a way to have these negation flags listed in the
--help
message or hidden.Design
A negation flag would simply take the long version of the regular flag and pre-pend
no
; for exmaple--follow
would get a--no-follow
. If a flag only specifies a short version, theno
would be prepended to the short such as-L
gets--no-L
.When parsing occurs, if a negation flag is found, and the negated argument was used, it functions exactly like a override that is already supported.
Functionally the following two examples are equivilant:
New proposal:
Concerns
There are two primary concerns with this approach.
Flags that already contian "no"
A flag which already starts with
no
such as--no-ignore
would end up getting a doubleno
in the form of--no-no-ignore
. This actually makes sense and is consistent, but looks strange at first glance. An alternative would be to check if a flag starts withno
and simply remove theno
, i.e.--no-ignore
becomes--ignore
but this has the downside of additional processing at runtime, becomes slightly more confusing, and has a higher chance of a conflict.Conflicting names
If a user has selected to auto-generate a negation flag, and the negating flag long conflicts with a flag already in use, a
panic!
will occur. Example,--ignore
and--no-ignore
is already defined elsewhere, and the user has selected to automaticlly generate negation flags, this will cause--ignore
to generate a--no-ignore
flag which already exists causing apanic!
. The fix is to either not use a sweeping setting that applies ot all flags indescriminantly, or to change/remove the already defined--no-ignore
flag.Progress
AppSettings::GenerateNegationFlags
which does the above, but automatically for all flags.AppSettings:GenerateHiddenNegationFlags
which hides all these negation flags.See the discussion in BurntSushi/ripgrep#196
Relates to #748
Edit: Removed
Arg::overridable(bool)
because this can already be done manually by making another flag.The text was updated successfully, but these errors were encountered: