tomaz / GBCli

Objective C foundation tool command line interface library
http://gentlebytes.com
MIT License
181 stars 19 forks source link

Support more argument forms #9

Open uranusjr opened 9 years ago

uranusjr commented 9 years ago

Some proposals about additional argument forms that can be supported:

Syntax to explicitly signal end of options

If a non-option argument needs to start with -, we need a way to tell the parser to explicitly treat it that way. To solve this, it is common to use a special option (usually - or --) to signal end of options. So for the following:

cmd --option1 --option2 -- --arg1 arg2

will be parsed as

Arguments:
    --arg1
    arg2

Options:
    option1
    option2

Interlacing arguments and options

Many applications accept a more permissive argument form, like this:

cmd arg1 --option=value arg2 arg3

while GBCli currently requires it to be written as

cmd --option=value arg1 arg2 arg3

instead.

I think this can be implemented quite easily. Currently the parser terminates parsing immediately when a non-option argument is met. If this is not the case, interlacing can happen.

Means to add auto-creation of negative variant

The negative variant of GBValueNone options is nice, but there are times when that is not really wanted. How about an extra method to let the user specify explicitly whether the negative variant should be created?

I’m not sure how this should be implemented though. Maybe something like

- (void)registerOption:(NSString *)longOption
              shortcut:(char)shortOption
           requirement:(GBValueRequirements)requirement
   addsNegativeVariant:(BOOL)addsNegativeVariant

Looking forward to hear how you feel about these improvements. I have tried to implement the interlacing feature. Although not thoroughly tested yet, I think it works. If you feel okay with the above I’ll start working on the other parts and submit a pull request afterwards.

tomaz commented 9 years ago

Explicit options ending and interlacing arguments and options sound useful.

As for negative options: the reason they are there are to allow option reset when using setting hierarchies. If for example certain option is set on by default (either in factory defaults or plist files), then there's no means to reset it to off in command line (or other higher priority setting in hierarchy). With that in mind, if they are truly undesirable in certain case, I'd keep negative variant by default and require explicit action to suppress it. As for API, instead of adding additional method, I'd add new GBValueRequirement flag for that. Note that any change on GBCommandLineParser also needs to be addressed on GBOptionsHelper (GBOptionFlags probably).

uranusjr commented 9 years ago

Explicit options ending and interlacing arguments and options sound useful.

Great! I’ll start working on those!

As for negative options: the reason they are there are to allow option reset when using setting hierarchies. If for example certain option is set on by default (either in factory defaults or plist files), then there's no means to reset it to off in command line (or other higher priority setting in hierarchy). With that in mind, if they are truly undesirable in certain case, I'd keep negative variant by default and require explicit action to suppress it. As for API, instead of adding additional method, I'd add new GBValueRequirement flag for that. Note that any change on GBCommandLineParser also needs to be addressed on GBOptionsHelper (GBOptionFlags probably).

My use case is that I want only the negative flag to couple with another flag. Let’s say I want to build a syntax highlighter that format code blocks with HTML highlighting. I want a option that specifies the theme:

highlighter --theme=bootstrap3 main.c

If the option is omitted, I want to default to a factory value, e.g. this

highlighter main.c

is semantically equal to

highlighter --theme=simple main.c

But the user can also explicitly disable styling like this:

highlighter --no-theme main.c

So there should be a required option theme. But the negative one is hard to configure—if I add a no-theme flag, a no-no-theme option would be registered automatically.

But now that I though about this, maybe the right way to do this is to make the requirement a bitmask (NS_OPTIONS) instead? Something like

typedef NS_OPTIONS(NSUInteger, GBValueFlags) {
    GBValueSwitch         = 0,   // The positive option should always exist; this is just for readability.
    GBValueNegativeSwitch = 1,   // The lowest bit is used to set whether a negative flag should exist.
    GBValueRequired       = 1 << 1,
    GBValueOptional       = 2 << 1,
    GBValueNone           = GBValueSwitch | GBValueNegativeSwitch
};

This way the above can be merged into a theme option with GBValueRequired | GBValueNegativeSwitch. The current flags’ behaviours are all kept intact; if you want an option without its negative variant you can use GBValueOption instead. Would this be better?