-
-
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
Flag subcommands #1974
Flag subcommands #1974
Conversation
Test all methods of `FlagSubCommand` and interactions with aliases
Using `pacman` as an example for `FlagSubCommand` because of clap-rs#1361
Adds support for Flag subcommands, for example: $ pacman -Ss $ pacman --sync -s
Removed a `debug!` line that referenced me.
After further thought, it makes sense instead of a I also realized that I missed validation specifically having Subcommand flags conflict with Arg flags, sorry! EDIT: converted to draft |
Instead of a `FlagSubCommand` struct the addition of two simple methods to `App`. `App::long_flag` and `App::short_flag` that cover all the bases of the many methods that were provided in `FlagSubCommand`. This API is far simpler to use and more akin to the present `Arg::long` and `Arg::short`.
Aliases exclusively for `App::short_flag` that allow visible and hidden short flag subcommands.
Tests that cover all methods for `short_flag_alias` corresponding methods
Flags weren't being set as visible in one instance. Flags were not being printed in help message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A good start, but yes, we should add the flags options to App
and not create a new FlagSubcommand
.
Improved printing instead of printing out the `Vec` using `{:?}`, `join(", ")` for improved printing. Improved documentation to b emore explicit and apply to removal of `FlagSubCommand` -> `App::short_flag` and `App::long_flag`
Conflicts with Flag subcommands and Args now panics when `debug_assertions` is enabled, rather than causing bizarre behavior.
I'm still probably missing something, but I think all that's remaining is changing usage to instead of always printing the subcommand name to print the flag in the cases of it being using. I'm probably glossing over YAML? Ideally, the usage would print all possible options...
Would ideally have
But that results in issues in the implementation, because we set the I think a not bad solution would be to show the flag instead of the name when it was used. |
Implementation of `fmt::Display` for `Flag` caused tests to fail when not ran with `debug_assertions` feature.
Tests that check conflicts are behind the `debug_assertions` feature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nick, thank you for pushing it forward! I haven't been able to review it thoroughly just yet - there aren't enough hours in the day - but I'll do my best to get it over with in a couple of days.
I agree with Pavan regarding App
. The main reasoning for having a separate FlagSubCommand
was to allow -Q
subcommands without having to allow normal, query
subcommands. It's gone out of window, so no reasons for separate facilities exist anymore.
Btw, what is [``]
in doc comments?
@CreepySkeleton Yeah, I ended up going this route because it was relatively easier. I'm completely open to either going back that route or towards an I personally like the freedom of choice for users of this route, but it makes the subcommands less explicit. Totally open to different ideas here 😄 |
Let's just go with current implementation. I don't have strong feelings on the matter, and given the myriads of opinions people can have about this, I wary if we start evaluating pros and cons here, it will spawn yet another mega thread of "I like this, I like that", which is something I would like to avoid. We can always add
What weird behavior were you referring to? I'm failing to imagine which conflicts it would case except for "
I'll need to think about it more in-depth. It doesn't looks good, I agree.
What does YAML has to do with any of this?
Don't you dare feel bad about this! This is something people call premature optimization. You see, it only makes sense to optimize critical paths (there are rare exceptions). Optimizing something that takes about 0.1% of runtime is kinda pointless and this case is one such. We set bin_name only once per parsing - very likely per program run! - and it's only ever used in generation of help/error messages which is already quite slow, not to mention that IO would induce much more of slowdown. The strictly speaking unneeded allocation simply doesn't matter because it's a rounding error. Clap is already heavily reliant on small temporary allocations, example, and that was an example from hot path! It's been done this way because it was easy for the programmer to write, easy for a reader to understand, and fast enough. Saving a single bin_name in a If we somehow come to conclusion that these small allocations do matter, we can simply make use of https://github.com/servo/rust-smallvec, thus gaining performance boost and saving some memory without having to addle our heads over this stuff. From ergonomics point of view, I don't see much difference between |
Yeah, you're right. I'll add that 😄
There's really no good way to re-visit the current argument, because of the recursive structure of the parser. It makes sense for it to be recursive, but it just makes edge cases like this a little ugly
Adding support for flag subcommands in the YAML implementation of Clap
Sweet, I'll work on that |
Wait, I still don't get why a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, got a bit (a lot) sidetracked. I'll be much more active here within the upcoming week or so 🙏.
OK, I reviewed the parsing algorithm. I don't like the way it's done, but I see no better way around. I guess we'll have to make do with what we have 😢.
Regarding the result, I really don't like that short options can appear between short subcommands, like
let matches = App::new("test")
.subcommand(
App::new("some")
.short_flag('S')
.long_flag("some")
.arg(Arg::from("-f, --flag 'some flag'"))
.arg(Arg::from("-p, --print 'print something'"))
.subcommand(
App::new("result")
.short_flag('R')
.long_flag("result")
.arg(Arg::from("-f, --flag 'some flag'"))
.arg(Arg::from("-p, --print 'print something'")),
),
)
.get_matches_from(vec!["myprog", "-SfpRfp"]); // should be -Sfp -Rfp instead
I just personally think this is very confusing and error prone, but that's not an out-and-out no. Do you think this kind of usage is of any use? I also see that prohibiting that would introduce more complexity to the parser which is an extra point to keeping it as is.
Overall, your works is great, especially considering that the parser's code is far from being a masterpiece. Keep it coming!
It isn't needed anymore, we've got past it. It's now an everyday normal app all the way down. |
Removed user facing panics and improved ergonomics to behave more like pacman behaves.
No longer abusing external_subcommand, but using subcmd_name in order to parse a subcommand, added a `keep_state` variable in order to handle the hacky solution of short command line arguments.
Previously `Flag::App` and `Flag::Arg` both owned their App/Arg which required many, many clones. This is unnecessary.
Moved from macro implementations to function implementations.
Displayed in the form of pacman {query, --query, -Q} [OPTIONS]
get_subcommands() now returns an impl Iterator rather than a slice, match_alias! removed
@CreepySkeleton, @pksunkara I think this is now ready for review, I probably need to rebase quite a bit, but other than that I think the features for Pacman style flag subcommands is covered. 👍 Completely open to feedback 🙂 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just minor stuff. Please create new commits addressing these rather than overwriting the existing commits. Thanks.
These names are more explicit about what short and long flag subcommands are.
Previously long_flag alias checks were against normal aliases instead of specifically designated long_flag aliases, this is more clear and explicit.
Just this left and then we can do the merge. |
Please let me review it first. I'll get to it tomorrow |
Tests to check for conflicts between flag subcommands long and short and their aliases and args both long and short and their aliases. Tests to handle self conflicts, where a flag subcommand short or long with have a corresponding alias with the same value.
When debug_assertions flag is active, properly handles conflicts between flag subcommands short and long their aliases and args and their aliases prevents self conflicts where the alias and the flag subcomand were the same.
Not necessary to borrow the to_string for comparison
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I finally reviewed this 🎉 Sorry for the delay, I must admit I forgot about this.
The only serious flaw I see is that flag subcommands aren't shown in --help
output (SUBCOMMANDS
section). I really think they should be.
@CreepySkeleton it does, I thought it would be good to add! It follows a similar structure to flags. Help
Usage
Is how usage is displayed, I'm open to changing the syntax here, it's just what |
Sorry, this is me poorly communicating in the threads in this PR. They are all on the same level, and therefore conflict with one another. pacman -S # valid pacman -QS # invalid I really like your suggested documentation though, it's clear and concise! I'll remove the bits that I caused confusion on. |
Improved documentation in flag subcommand example and in `App::short_flag` and `App::long_flag` method documentation.
Lint name changed clippy::block_in_if_condition_stmt -> clippy::block_in_if_conditions
Being clear and concise apparently goes toe-to-toe with being non-native English speaker :) Thank you for the hard work Nick, and may you code work flawlessly and your debugger always find bugs before they land to production! bors r+ |
Build succeeded: |
Closes #1361
FlagSubCommand
sI tried to catch all the issues, but with how large Clap is it's totally possible I missed one here or there.
Request review from @CreepySkeleton 😄
Feedback
Here are places where I think issues/inconsistencies are most likely to occur in the changes I've made. It's possible there are more, but these are places where further discussion may be necessary.
FlagSubCommands
supports using subcommands normally and with flagsPersonally, I think this gives a lot of freedom to users. This is a differing/controversial thing among
AUR Helpers
.src/build/app/flag_subcommand
FlagSubCommand
API, I thinknew
,new_short
,new_long
, andwith_name
are consistent with the current API and are descriptive, in most cases no one will use any of the others thannew
.src/parse/parser.rs
specifically in maintaining state between parsers inparse_subcommand
, andInput::previous
. This is all done to re-visit the current short argument list, but skip over the subcommand flag.Any feedback is greatly appreciated 😃