rich-click icon indicating copy to clipboard operation
rich-click copied to clipboard

Show commands before options

Open mirpedrol opened this issue 1 year ago • 5 comments

Hello!

Is there a way to show the command groups before the options on the help output?

We are using rich-click on nf-core with the following help output (see screenshot) and we would like to see the commands at the top, as those are more important than the options we show. image

I would be happy to contribute if this can help. I would appreciate any pointers on how to achieve this. Thank you!

mirpedrol avatar May 30 '24 13:05 mirpedrol

Thanks for writing this up @mirpedrol 👍🏻 I think it should basically be a question of having a config option that allows these lines to be executed in the opposite order:

https://github.com/ewels/rich-click/blob/76625934a66275b9a8f9328ffa50ea8907b62a29/src/rich_click/rich_command.py#L294-L296

I might be oversimplifying it though. And it's worth thinking about what the best way to handle this config is, I know @dwreeves you've been working on refactoring a lot of the configuration lately.. Any thoughts on this?

ewels avatar May 30 '24 14:05 ewels

The way to do that right now without needing to wait on us would be something like this:

import rich_click as click

class CustomRichGroup(click.RichGroup):

    def format_options(self, ctx, formatter) -> None:
        from rich_click.rich_help_rendering import get_rich_options
        self.format_commands(ctx, formatter)
        get_rich_options(self, ctx, formatter)

@click.group(cls=CustomRichGroup)
def cli():
    ...

I tested this in my code and it works!

Also, I believe the above code only works for versions 1.8+; I believe in 1.7 we didn't separate out commands+options, and in prior versions subclassing was not well-supported.


We could also implement this as a config option. It will take me either until this or next weekend to get around to it, though, but it's a relatively simple change.

dwreeves avatar May 30 '24 15:05 dwreeves

Also, I should say, if you'd like to contribute to this, you certainly can, but we need to think through implementation.

My first thought is we could do something like COMMANDS_BEFORE_OPTIONS: bool = False as a config option. I don't see anything wrong with that.

The issue though is that, in 1.9 (target release is end of year), we intended on doing a complete revamp of the "panels" functionality (currently called "groups"). See #179 for more info on that. I don't know what the implementation will ultimately end up being for panels, but a possible implementation of that might be the act of combining all panels into a single interface and distinguishing them by type, e.g. panel_type: Literal["command", "option", "argument"].

And if we opt to have panels=? as an interface for all panels, then one way to sort the panels would be something like this:

@click.group
@click.argument("bar") 
@click.option("--foo")
@click.rich_config({"show_arguments": True}, panels=["Arguments", "Commands", "Options"])  # defines order
def cli():
    ...

^ That said, if we implement this, then my suggestion of subclassing breaks in 1.9! 😆 Since we'd need to re-combine the format_options + format_commands and break from how base click does things. 🙊 No good solutions, eh....


Anyway, to bridge the gap in the meanwhile (since this is targeted for 6 months out), would could add COMMANDS_BEFORE_OPTIONS: bool = False, and the way this could work in 1.9 to preserve backwards compat is that it sorts the panels in the instance where the panels are not being sorted manually.

dwreeves avatar May 30 '24 18:05 dwreeves

Anyway, to bridge the gap in the meanwhile (since this is targeted for 6 months out), would could add COMMANDS_BEFORE_OPTIONS: bool = False, and the way this could work in 1.9 to preserve backwards compat is that it sorts the panels in the instance where the panels are not being sorted manually.

This sounds like a sensible plan of action 👍🏻

Thanks @dwreeves !

ewels avatar May 31 '24 07:05 ewels

Hi @dwreeves, thanks for all the information! I have tried your suggestion of adding a custom CustomRichGroup class and it works great. I think we can pin the version of rich-click to 1.8.* until the new release is out. And I can also try to implement COMMANDS_BEFORE_OPTIONS: bool = False as a long term solution.

mirpedrol avatar May 31 '24 07:05 mirpedrol

Coming back to this a whole year later.

commands_before_options will be released as part of 1.9.0 and is in this PR: #252

import rich_click as click

@click.group
@click.rich_config({"commands_before_options": True})
def cli():
    """My CLI"""

Panels (formerly groups) will also have a much-improved API and ordering them will be much more sensible. For example, the below code is effectively the same as the above code in that it orders the commands panel to be above the options panel (panel rendering will follow the same order as the definitions of the panels in the decorators).

import rich_click as click

@click.group
@click.command_panel("Commands")
@click.option_panel("Options")
def cli():
    """My CLI"""

dwreeves avatar Aug 11 '25 15:08 dwreeves

Hi @dwreeves, thank you very much for this update! And sorry for not getting back to it. This will be very useful to simplify our code when the new version is out 👏

mirpedrol avatar Aug 12 '25 09:08 mirpedrol

Closing this because panel ordering is 100% feature-complete via #254. The 1.9.0dev0 prerelease is not fully done re: command/panel ordering, but 1.9.0dev1 fixes all the issues (when that's available).

dwreeves avatar Aug 16 '25 03:08 dwreeves