davetron5000 / gli

Make awesome command-line applications the easy way
http://davetron5000.github.io/gli
Apache License 2.0
1.26k stars 102 forks source link

Allow for option(s) after the argument(s) #272

Closed dkinzer closed 5 years ago

dkinzer commented 6 years ago

According to https://github.com/davetron5000/gli/issues/18#issuecomment-1451598 it might be possible to allow for options after the argument. For example with git I can do the following:

git clone hello/world --branch=mybranch --bare ---quiet

The synopsis here is more like

git [options] command [arguments] [options]

It seems like I would not be able to reproduce that with Gli because Gli (as far as I can tell) forces the following synopsis:

git [options] command [options] [arguments]

Thus the example git command implemented with Gli would be have to be

git clone --branch=mybranch --bare --quiet hello/world

Which seems unnatural.

Is there a way to implement a command so that options can be passed after arguments and allow the synopsis to be as follows?

executable [options] command [arguments] [options]

Thanks.

davetron5000 commented 6 years ago

Currently, GLI very much wants all arguments at the end after all commands and options have been parsed. git clone hello/world --branch=mybranch --bare ---quiet would treat hello/word as a command (which it wouldn't find and generate an error).

It does make it unclear how GLI would know what is a command and what is an argument. Because it's possible to make nested commands, you could create a somwhat confusing situation, for example suppose that you had a command list that had two subcommands new and archived:

> todo list new # list only new tasks
> todo list archive # list only archived tasks
> todo list new -l # long-form listing
> todo list archive --fields=id,name # customize fields

Suppose you wanted todo list to list all tasks, which is currently possible to implement in GLI. Now suppose further you wanted it to take an argument, like the location of the task list:

> todo list ~/tasks

If you did todo list ~/tasks -l GLI would not know if you were giving list a subcommand (which list supports) or giving it an argument. This is a problem when you fat-finger a subcommand, e.g. todo list nwe.

So, to make all this work with the existing nested commands and all the features therein, you would need to make some decisions (and possibly internal options) on what to do with an "unknown" command, i.e. pass it as an arg, or something else.

dkinzer commented 6 years ago

@davetron5000 thank you for getting back to me. I appreciate the work that you've put into this project and your consideration.

It does make it unclear how GLI would know what is a command and what is an argument

That's an interesting point; just to be clear, you are saying that because GLI cannot distinguish a non command token from a command token then it assumes the non-command token is a command, and then throws and error when it's not (understandably)?

However, isn't it reasonable for the GLI parser to assume that any token that is not the same as one of it's sub commands (it has knowledge of all it's subcommands) must be an argument to the last previously defined command instead of attempting to treat it as a subcommand?

In the example you show above, todo list ~/tasks -l, it seems to me that if list is indeed a known subcommand for todo than it's perfectly reasonable for the parser to assume that list is a command and not an argument.

davetron5000 commented 6 years ago

In the example you show above, todo list ~/tasks -l, it seems to me that if list is indeed a known subcommand for todo than it's perfectly reasonable for the parser to assume that list is a command and not an argument.

Yeah, but if it worked this way, it would mean that typos for intended subcommands would be treated as arguments and not errors, and this could be surprising for users of the cli app.

But, I could imagine some sort of configuration option per app or even per command that could enable this behavior.

I think what's tricky is that the more flexibility that has been added that harder it is to add even more :)

dkinzer commented 6 years ago

Yeah, but if it worked this way, it would mean that typos for intended subcommands would be treated as arguments

Yes, I think that would be the case with how things are set up. Even with the current setup I believe there are scenarios where a typo in the subcommand name would be taken as an argument.

I believe however that this is an artifact of the way the parser works. Because the parser works from left to right. I think if the parser were to work from right to left this would catch all these types of errors regardless of whether the options come after or before the arguments.

Such a parser would probably not be useful for an autocompletion feature so maybe that's not a good option either.

I think what's tricky is that the more flexibility that has been added that harder it is to add even more :)

Yeah, I know what you mean. Thanks for considering it, though. If you are amenable I might give the reverse parser or the options idea a crack at some point.

As for my immediate problem, I've decided to go with all options instead of arguments. I was uncomfortable with the idea at first but then I noticed the gpg command uses only options. (no arguments)