-
Notifications
You must be signed in to change notification settings - Fork 56
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
Support for nesting commands #24
Comments
Something could be done to improve these use cases at least one level further. What I missed at the time is that often tools having commands also have:
synopses. However from a usability perspective I don't think it is a good thing to allow arbitrary nesting. Besides the bulk of the work here is not to define a grouping mechanism but rather how to handle and present the resulting automatic documentation in a sensible way (e.g. where do you put the documentation of the options for each subcommand, etc.). |
I think it's reasonable to assume that sub commands of the the same command would have some (if not all) options in common. In that case, each command could have its documentation page, printed when using |
I think having a sub-tree of commands would be useful, and w.r.t. documentation it would require, imho, two things:
|
One more thing I just noticed and that could be an issue by itself, but is closely related to this issue, is that when you use |
Le mardi, 7 avril 2015 à 18:40, Guillaume Bury a écrit :
Could you please open an issue with a repro case please. Daniel |
Done in issue#28 |
opam has an encoding of subcommands which is not too bad. See https://github.com/ocaml/opam/blob/master/src/client/opamArg.mli#L147-L173 |
This is how opam handles the documentation of subcommands. This seems like a sensible and scalable way to handle arbitrary nesting of subcommands. |
As noted in ocaml/dune#1482, dune would like to make use of nested subcommands. I would be interested in helping build out this functionality, if you would be open to contributions to realize this feature. Would you be open to some PRs in that direction, @dbuenzli? |
@shonfeder come up with a design at the API level first. |
Also so far I have personally not been too annoyed by the lack of it. So you might simply consider an encoding. See for example here. |
I'll do as you advise re: API design and report back as soon as I have something sensible. Thanks :) I'm currently using a pattern exactly along the lines you suggest, and I agree it works fine. Functionally speaking, however, we do miss
I'll be in touch soon. |
@shonfeder, have you made progress on this by any chance? We were talking about it in the context of dune |
@diml Unfortunately, I haven't had time to work on this at all. If someone else has the bandwidth and interest to tackle it, they should feel free to do so. If not, I'm happy to turn my attention here after I get ocaml/dune#2185 through (tho I can't guarantee how swift I'll be able to move). |
Ok. I haven't followed BTW, API-wise Core.Command could be a source of inspiration since it has supported arbitrary deep sub-tree of commands for a long time. It is also the second revision of the API, so I expect it to have reached a good level of maturity by now. |
As I already mentioned, I don't think arbitrary sub-tree of commands scale both from a documentation and end-user point of view. It should be sufficient to add support for one more layer. |
As an end-user of many command line interfaces that use From the usability perspective, the way they work is that you need to specify full command name (get to the leaf-level) before you can specify any options or flags. (in fact command groups can't have any parameters at all) From the documentation perspective, the way they work is that for every command group you've got a short doc and the list of subcommands with their one-sentence descriptions. The bulk of documentation (and all flag documentation) then lives in the leaves of this tree. A few things I think are great about unrestricted command group nesting:
|
Unless this has changed in
The properties you mention seems rather to be in favour of the person who is designing the command line than the person using it. I don't think it's a good idea for end users to go beyond
One has function abstraction for that.
I prefer if the person doing this takes time to reflect on and redesign the cli to make it fit in the above structure so that I don't have, as an end-user, to suffer absurd nesting levels. |
Pretty much everything in computers is hierarchically organised as a tree: the file systems, the modules of a library, etc... A program with several commands is simply a bunch of different functionalities organised under a single toplevel program name, so it makes sense to allow arbitrary tree of commands. Now, I agree that if you take any tree in any context you can find a way to refactor it to reduce the nesting, trim it and simplify it. However, having this extra power of being able to construct arbitrary trees allows you to explore the design space and converge towards better designs as your program evolves. Because at the end of the day, it's pretty rare that the right design just pops out. Instead you need to experiment to find out what works best. |
Cmdliner is interested about giving good UIs for humans not about mirroring how things are organized for computers. I don't think I ever saw a program using more than the levels I mentioned and I don't think it would be a good thing ux wise. Now I don't want to waste my time discussing this. As I said the first step is to come up with a good design at the API and documentation presentation level. I suspect it will be much easier if the person who does this first starts to think about a single additional level with documentation for the subcommands presented in the manpage of the command, basically mirroring what is done for the main manpage when multiple commands are used. After that whether this can/should be generalized can be discussed later. |
Yh, I guess the only examples I have in mind are Jane Street tools that are not public. Regarding the API, if we want to only support 2 levels I would suggest something like this: type 'a leaf_command = 'a t * info
type 'a command =
| Normal_command of 'a leaf_command
| Command_with_subcommabds of {
default : 'a leaf_command;
sub_commands : 'a leaf_command list;
}
val eval_choice_with_subcommands : ... ->
default:'a leaf_command -> commands:'a command list -> 'a result and the man page for a command with sub-command would include a Although, personally I would change the type of the |
An other way of saying this is that it it not allowed for commands to have a default subcommand. I would tend to be in favour of this as it reduces parsing ambiguities. However note that The current cmdliner when you have commands and a default command forces you to specify the command as the second argument. I always found that unpleasant because it doesn't respect the general model of cli parsing where you are supposed to be able to arbitrarily interleave option and position arguments. Now I don't know/remember why I didn't do this: on
That feels ok. There's also the question of where to put the subcommand's argument descriptions by default (when Thinking about a potential generalization, I think maybe you'd have a manpage for each level except for the last-to-one which collapses the last level in its own. |
Indeed. We are having a similar discussion for dune, and so far it seems that having shell completion would provide the same benefit without the troubles: ocaml/dune#2097
Isn't that ambiguous though? Since we don't know the grammar of the options until we know the command, it's difficult to say whether the first argument not starting with a dash is a command or the argument of a command line option. Additionally, I feel like it plays badly with adding new commands. A command line of the form
I also checked
That seems good. Here is a concrete proposal that I feel would produce good default man pages without too much trouble:
i.e. the options specific to one sub-command are listed in the sub-command description, with one more level of indentation. If there are not too many, that should be ok. |
Ah yes that was likely the reason :-) Assuming subcommands do not have too many specific options but mainly specific positional arguments, I think we can try the manpage layout your propose. |
This feature is now seriously needed for dune, so I gave it a try here. It seems to work fine in the happy case, but I'd like the following behaviour for dune: If one calls |
Not sure exactly what you are asking for but if you follow |
Thanks will take a look. To clarify, I'd like |
That sounds reasonable, as discussed above allowing doing something else on Alternatively we could also error, just mentioning a |
Okay, I've made some progress to the point where it should be good to experiment with in dune. There's still some work remaining on:
|
While using the existing support for nested commands in Dune, I came across the need to deprecate subcommands. Say, we have Providing support for deprecated commands doesn't seem difficult, but it seems worth discussing it here, so that it interacts well with the whole nested commands feature. EDIT: If there is a consensus that this is a good idea, I'd be happy to implement a prototype. |
So I eventually got to something. In a nutshell:
The drawback of 3. is that no option is allowed before a subcommand (like the current cmdliner does). This comes under the form of a new There's more information in the release notes and the documentation online was updated with the new terminology along with the tutorial and examples. I'm not ready to release yet and I want to test the API on a few projects of mine. If you have time to go through the changes or even test them do not hesitate to open issues about the new API. Thanks all for the discussion and especially to @rgrinberg for implementing #123 which significantly informed the current result. |
Commands can be specified using the
Term.eval_choice
function, however there doesn't seem to be any way to specify sub commands, to achieve results such asopam pin add
, whereadd
is a sub command ofpin
.More generally, it would be useful to be able to define arbitrary nesting of commands, possible through the use of a function with the same semantics as
Term.eval_choice
, but with the following signature:The text was updated successfully, but these errors were encountered: