arcanis / clipanion

Type-safe CLI library / framework with no runtime dependencies
https://mael.dev/clipanion/
1.12k stars 66 forks source link

feat: allow a pretty name to be passed for positional arguments #33

Closed bgotink closed 4 years ago

bgotink commented 4 years ago

Usecase: I'm generating commands based on a JSON schema, and I want to prevent name conflicts between the defined arguments and actual properties on the command implementation. For options there's no problem because the name the property has on the prototype isn't used at all in the usage text, but for positionals this yields

$ pt g @scope/package:schematic [--project-name #0] [--skip-install] [--parent-ts-config #0] [prop_--name]

I've prefixed positonals with prop_-- to prevent any name clash. The options are prefixed with prop_ so --project-name maps to prop_projectName, but that isn't visible here.

With this change the usage statement could become

$ pt g @scope/package:schematic [--project-name #0] [--skip-install] [--parent-ts-config #0] [name]

which is a lot more userfriendly

arcanis commented 4 years ago

Sorry, I missed that one :< I think I'd prefer to keep the 1:1 mapping between the property names and what's displayed, for simplicity. Would using something simpler than prop_--name (for instance (name), although any character would work) be enough? That would print:

$ pt g @scope/package:schematic [--project-name #0] [--skip-install] [--parent-ts-config #0] [(name)]
cspotcode commented 4 years ago

Options/flags already have their CLI name and class property name specified separately; why the distinction between them and positionals? If the names are declared separately for options/flags, it seems simpler for the names to be (optionally) declared separately for everything: flags, options, and positionals.

What about something like this?

@Command.String('<name>')
f_name: string;

And Clipanion will see it as a positional named name, as far as help docs are concerned. We could drop the <> since they imply that it is required, and that's meant to be specified separately. It would still be unambiguous compared to an option, since the option begins with a hyphen.

Are cases where options do not begin with a hyphen? To support stuff like mycli arg=value?

bgotink commented 4 years ago

Would using something simpler than prop_--name (for instance (name), although any character would work) be enough?

That's a lot better actually, but it would still be confusing that the usage text for commands generated from JSON schema has positionals [(name)] while the usage text for regular commands yields [name].

I'd like to point out that my specific usecase is a generated command class, so using (name) as property name is feasible because I'll be doing this[propName] anyway. The same issue can exist with other commands as well if a positional name clashes with the name of any property on the command class. Creating commands like git log <path> is impossible.

.@cspotcode brings up a great point imo: for flags the property name doesn't even matter from the user's perspective. While I'm not advocating for breaking the API and making the name a required option when declaring a positional argument, I do think this possibility of aliasing the name of the positional has merit.

arcanis commented 4 years ago

Are cases where options do not begin with a hyphen? To support stuff like mycli arg=value?

No, we don't support this kind of thing - the -- prefix is hardwired when generating the state machine.

Thinking more about it that sounds reasonable, let's merge it 👍

bgotink commented 4 years ago

Great, thanks!