.. py:currentmodule:: cloup

Help formatting and theming
===========================

Formatting settings
-------------------

In Cloup we can distinguish two groups of formatting settings:

1. parameters of commands/mixins classes

   - ``align_option_groups`` -- see :ref:`here <aligned-vs-nonaligned-group>`;
   - ``align_sections`` -- similar to ``align_option_groups`` but for subcommand sections;
   - ``show_constraints`` -- whether to include the "Constraints" section
     (see :ref:`here <show-constraints>`);

   since v0.8, these parameters are also available as ``Context`` parameters.

2. parameters of the :class:`HelpFormatter`, including ``theme``
   (click on :class:`HelpFormatter` to see the full list);
   these parameters can be passed to both ``Context`` and commands using the
   parameter ``formatter_settings``, which is a dictionary.

In both cases, commands settings override context settings.
In the case of ``formatter_settings``, the final settings used by a command are
obtained by merging the dictionaries like following:
``{**ctx.formatter_settings, **command.formatter_settings}``.

Context settings propagate to subcommands, while command settings don't.

An example
~~~~~~~~~~

.. tip::
    In Cloup, you can use the static methods :meth:`Context.settings` and
    :meth:`HelpFormatter.settings` to create dictionaries without leaving your
    IDE (to check the docs).

.. code-block:: python

    from cloup import Context, command, group, HelpFormatter, HelpTheme

    CONTEXT_SETTINGS = Context.settings(
        align_option_groups=False,
        align_sections=True,
        show_constraints=True,
        formatter_settings=HelpFormatter.settings(
            max_width=100,
            max_col1_width=25,
            min_col2_width=30,
            indent_increment=3,
            col_spacing=3,
            row_sep='\n',
            theme=HelpTheme.light()
        )
    )


    @group(context_settings=CONTEXT_SETTINGS)
    # ...
    def main(...):
        ...


    # This command overrides some of CONTEXT_SETTINGS values
    @main.command(
        align_option_groups=True,
        formatter_settings=HelpFormatter.settings(
            max_col1_width=30,
        )
    )
    # ...
    def cmd(...):
        ...


Theming
-------
A :class:`HelpTheme` is a collection of styles for several elements of the help page.
A "style" is just a function (or a callable) that takes a string and returns a
styled version of it. This means you can use your favorite styling/color library
(like ``rich``, ``colorful`` etc) with it.

Given that Click has some built-in basic styling functionality provided by the
function :func:`click.style`, Cloup provides the :class:`~cloup.Style` class, which
wraps ``click.style`` to facilitate its use with ``HelpTheme``.

.. tip::
    Cloup also provides an *enum-like* class :class:`Color` containing all
    colors supported by Click.

The following picture links ``HelpTheme`` arguments to the corresponding visual
elements of the help page (only ``epilog`` is missing):

.. image:: ../_static/theme-elems.png
    :alt: Elements

The above image was obtained with the following theme::

    HelpTheme(
        invoked_command=Style(fg='bright_yellow'),
        heading=Style(fg='bright_white', bold=True),
        constraint=Style(fg='magenta'),
        col1=Style(fg='bright_yellow'),
    )

For an always up-to-date list of all possible arguments these classes take,
refer to the API reference:

.. autosummary::
    HelpTheme
    Style


How to set a theme
~~~~~~~~~~~~~~~~~~

You must provide a ``theme`` as part of the ``formatter_settings`` dictionary,
as shown in `the example above <#an-example>`_.


Available themes (and how to override parts of them)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Cloup provides two reasonable themes:

.. autosummary::
    HelpTheme.dark
    HelpTheme.light

You probably want to select a theme based on the terminal background color in use.
Nonetheless, Cloup doesn't currently provide a way to get it (any suggestions are
welcome).

If you want, you can use the default themes as a base and change only some of
the styles using :meth:`HelpTheme.with_`, e.g.:

.. code-block:: python

    theme = HelpTheme.dark().with_(
        col1=Style(fg=Color.bright_green),
        epilog=Style(fg=Color.bright_white, italic=True)
    )


The linear layout for definition lists
--------------------------------------
When the terminal width is "too small" for a standard 2-column definition lists,
Cloup ``HelpFormatter`` switches to a "linear layout", where

- the option description is always printed below the option name, with an indentation
  increment of at least 3 spaces
- all definitions are separated by an empty line.

The following tabs compare the ``--help`` of the manim example ("aligned" and
"non-aligned" refer to the ``align_option_groups`` argument):

.. tabbed:: Linear layout

    .. code-block:: none

        Usage: manim render [OPTIONS]
                            SCRIPT_PATH
                            [SCENE_NAMES]...

          Render some or all scenes defined in a Python
          script.

        Global options:
          -c, --config_file TEXT
             Specify the configuration file to use for
             render settings.

          --custom_folders
             Use the folders defined in the
             [custom_folders] section of the config
             file to define the output folder
             structure.

          --disable_caching
             Disable the use of the cache (still
             generates cache files).

          --flush_cache
             Remove cached partial movie files.

          --tex_template TEXT
             Specify a custom TeX template file.

          -v, --verbosity [DEBUG|INFO|WARNING|ERROR|CRITICAL]
             Verbosity of CLI output. Changes ffmpeg
             log level unless 5+.

        [...]

.. tabbed:: Standard layout (aligned)

    .. code-block:: none

        Usage: manim render [OPTIONS]
                            SCRIPT_PATH
                            [SCENE_NAMES]...

          Render some or all scenes defined in a Python
          script.

        Global options:
          -c, --config_file TEXT      Specify the
                                      configuration
                                      file to use for
                                      render settings.
          --custom_folders            Use the folders
                                      defined in the
                                      [custom_folders]
                                      section of the
                                      config file to
                                      define the output
                                      folder structure.
          --disable_caching           Disable the use
                                      of the cache
                                      (still generates
                                      cache files).
          --flush_cache               Remove cached
                                      partial movie
                                      files.
          --tex_template TEXT         Specify a custom
                                      TeX template
                                      file.
          -v, --verbosity [DEBUG|INFO|WARNING|ERROR|CRITICAL]
                                      Verbosity of CLI
                                      output. Changes
                                      ffmpeg log level
                                      unless 5+.

        [...]


.. tabbed:: Standard layout (non-aligned)

    .. code-block:: none

        Usage: manim render [OPTIONS]
                            SCRIPT_PATH
                            [SCENE_NAMES]...

          Render some or all scenes defined in a Python
          script.

        Global options:
          -c, --config_file TEXT  Specify the
                                  configuration file to
                                  use for render
                                  settings.
          --custom_folders        Use the folders
                                  defined in the
                                  [custom_folders]
                                  section of the config
                                  file to define the
                                  output folder
                                  structure.
          --disable_caching       Disable the use of
                                  the cache (still
                                  generates cache
                                  files).
          --flush_cache           Remove cached partial
                                  movie files.
          --tex_template TEXT     Specify a custom TeX
                                  template file.
          -v, --verbosity [DEBUG|INFO|WARNING|ERROR|CRITICAL]
                                  Verbosity of CLI
                                  output. Changes
                                  ffmpeg log level
                                  unless 5+.
          --notify_outdated_version / --silent
                                  Display warnings for
                                  outdated
                                  installation.

        [...]


The linear layout is controlled by the ``min_col2_width`` argument of ``HelpFormatter``.
The linear layout is used when the available width for the 2nd column is below
``min_col2_width``, which defaults to 35.

You can disable the linear layout settings ``min_col2_width=0``.

You make the linear layout your default layout by settings ``min_col2_width`` to
a large number, possibly ``math.inf``.


Minor differences with Click
----------------------------

- The width of the 1st column of a definition list is computed excluding the
  rows that exceeds ``col1_max_width``; this results in a better use of space in
  many cases, especially with ``align_option_groups=False``.

- The default ``short_help``'s of commands actually use all the available
  terminal width (in Click, they don't; see "Related issue" of
  `this Click issue <https://github.com/pallets/click/issues/1849>`_)

- The command epilog is not indented (this is just my subjective preference).
