====
Help
====

A help screen is standard for every CLI application.
Cyclopts by-default adds ``--help`` and ``-h`` flags to the application:

.. code-block:: console

   $ my-application --help
   Usage: my-application COMMAND

   My application short description.

   ╭─ Commands ─────────────────────────────────────────────────────────╮
   │ foo        Foo help string.                                        │
   │ bar        Bar help string.                                        │
   │ --help -h  Display this message and exit.                          │
   │ --version  Display application version.                            │
   ╰────────────────────────────────────────────────────────────────────╯

Cyclopts derives the components of the help string from a variety of sources.
The source resolution order is as follows (as applicable):

1. The ``help`` field in the :meth:`@app.command <cyclopts.App.command>` decorator.

   .. code-block:: python

      app = cyclopts.App()


      @app.command(help="This is the highest precedence help-string for 'bar'.")
      def bar():
          pass

   When registering an :class:`.App` object, supplying ``help`` via the :meth:`@app.command <cyclopts.App.command>` decorator is forbidden to reduce ambiguity and will raise a :exc:`ValueError`. See (2).

2. Via :attr:`.App.help`.

   .. code-block:: python

      app = cyclopts.App(help="This help string has highest precedence at the app-level.")

      sub_app = cyclopts.App(help="This is the help string for the 'foo' subcommand.")
      app.command(sub_app, name="foo")
      app.command(sub_app, name="foo", help="This is illegal and raises a ValueError.")


3. The ``__doc__`` docstring of the registered :meth:`@app.default <cyclopts.App.default>` command.
   Cyclopts parses the docstring to populate short-descriptions and long-descriptions
   at the command-level, as well as at the parameter-level.

   .. code-block:: python

      app = cyclopts.App()
      app.command(cyclopts.App(), name="foo")


      @app.default
      def bar(val1: str):
          """This is the primary application docstring.

          Parameters
          ----------
          val1: str
              This will be parsed for val1 help-string.
          """


      @app["foo"].default  # You can access sub-apps like a dictionary.
      def foo_handler():
          """This will be shown for the "foo" command."""

   .. note::
      Docstrings should always use the **Python variable name** from the function signature.

      .. code-block:: python

         @app.default
         def main(internal_name: Annotated[str, Parameter(name="external-name")]):
             """Command description.

             Parameters
             ----------
             internal_name:            # Use the Python variable name
                 Help text here.
             """

      This follows standard Python documentation conventions; the parameter will still appear as ``--external-name`` on the CLI.

4. This resolution order, but of the :ref:`Meta App`.

   .. code-block:: python

      app = cyclopts.App()


      @app.meta.default
      def bar():
          """This is the primary application docstring."""

-------------
Markup Format
-------------
While the standard markup language for docstrings in Python is reStructuredText (see `PEP-0287`_), Cyclopts defaults to Markdown for better readability and simplicity.
Cyclopts mostly respects `PEP-0257`_, but has some slight differences for developer ergonomics:

1. The "summary line" (AKA short-description) may actually be multiple lines. Cyclopts will unwrap the first block of text and interpret it as the short description. The first block of text ends at the first double-newline (i.e. a single blank line) is reached.

   .. code-block:: python

      def my_command():
          """
          This entire sentence
          is part of the short description and will
          have all the newlines removed.

          This is the beginning of the long description.
          """

2. If a docstring is provided with a long description, it **must** also have a short description.

By default, Cyclopts parses docstring descriptions as markdown and renders it appropriately.
To change the markup format, set the :attr:`.App.help_format` field accordingly. The different options are described below.

Subapps inherit their parent's :attr:`.App.help_format` unless explicitly overridden. I.e. you only need
to set :attr:`.App.help_format` in your main root application for all docstrings to be parsed appropriately.

^^^^^^^^^
PlainText
^^^^^^^^^
Do not perform any additional parsing, display supplied text as-is.

.. code-block:: python

   from cyclopts import App

   app = App(help_format="plaintext")

   @app.default
   def default():
       """My application summary.

       This is a pretty standard docstring; if there's a really long sentence
       I should probably wrap it because people don't like code that is more
       than 80 columns long.

       In this new paragraph, I would like to discuss the benefits of relaxing 80 cols to 120 cols.
       More text in this paragraph.

       Some new paragraph.
       """

   app()

.. code-block:: text

   Usage: default COMMAND

   My application summary.

   This is a pretty standard docstring; if there's a really long
   sentence
   I should probably wrap it because people don't like code that is
   more
   than 80 columns long.

   In this new paragraph, I would like to discuss the benefits of
   relaxing 80 cols to 120 cols.
   More text in this paragraph.

   Some new paragraph.

   ╭─ Commands ─────────────────────────────────────────────────────╮
   │ --help,-h  Display this message and exit.                      │
   │ --version  Display application version.                        │
   ╰────────────────────────────────────────────────────────────────╯

Most noteworthy, is **no additional text reflow is performed**; newlines are presented as-is.

^^^^
Rich
^^^^
Displays text as `Rich Markup`_.

.. note::

      Newlines are interpreted literally.

.. code-block:: python

   from cyclopts import App

   app = App(help_format="rich")

   @app.default
   def default():
      """Rich can display colors like [red]red[/red] easily.

      However, I cannot be bothered to figure out how to show that in documentation.
      """

   app()

.. raw:: html

Usage: default COMMAND

   Rich can display colors like red easily.

   ╭─ Commands ───────────────────────────────────────────────────────╮
   │ --help -h  Display this message and exit.                        │
   │ --version  Display application version.                          │
   ╰──────────────────────────────────────────────────────────────────╯

^^^^^^^^^^^^^^^^
ReStructuredText
^^^^^^^^^^^^^^^^
ReStructuredText can be enabled by setting `help_format` to "restructuredtext" or "rst".

.. code-block:: python

   app = App(help_format="restructuredtext")  # or "rst"

   @app.default
   def default():
       """My application summary.

       We can do RST things like have **bold text**.
       More words in this paragraph.

       This is a new paragraph with some bulletpoints below:

       * bullet point 1.
       * bullet point 2.
       """

   app()

Resulting help:

.. raw:: html

Usage: default COMMAND

   My application summary.

   We can do RST things like have bold text. More words in this
   paragraph.

   This is a new paragraph with some bulletpoints below:

   1. bullet point 1.
   2. bullet point 2.

   ╭─ Commands ──────────────────────────────────────────────────────────╮
   │ --help -h  Display this message and exit.                           │
   │ --version  Display application version.                             │
   ╰─────────────────────────────────────────────────────────────────────╯

Under most circumstances, plaintext (without any additional markup) looks prettier and reflows better when interpreted as restructuredtext (or markdown, for that matter).

^^^^^^^^^
Markdown
^^^^^^^^^
Markdown is the default parsing behavior of Cyclopts, so `help_format` won't need to be explicitly set. It's another popular markup language that Cyclopts can render.

.. code-block:: python

   app = App(help_format="markdown")  # or "md"
   # or don't supply help_format at all; markdown is default.


   @app.default
   def default():
       """My application summary.

       We can do markdown things like have **bold text**.
       [Hyperlinks work as well.](https://cyclopts.readthedocs.io)
       """

Resulting help:

.. raw:: html

Usage: default COMMAND

   My application summary.

   We can do markdown things like have bold text. Hyperlinks work as well (https://cyclopts.readthedocs.io).

   ╭─ Commands ──────────────────────────────────────────────────────────╮
   │ --help -h  Display this message and exit.                           │
   │ --version  Display application version.                             │
   ╰─────────────────────────────────────────────────────────────────────╯

----------
Help Flags
----------
The default ``--help`` flags can be changed to different name(s) via the ``help_flags`` parameter.

.. code-block:: python

   app = cyclopts.App(help_flags="--show-help")
   app = cyclopts.App(help_flags=["--send-help", "--send-help-plz", "-h"])

To disable the help-page entirely, set ``help_flags`` to an empty string or iterable.

.. code-block:: python

   app = cyclopts.App(help_flags="")
   app = cyclopts.App(help_flags=[])

--------------------------
Help Prologue and Epilogue
--------------------------
An epilogue is text displayed at the end of the help screen, after all command and parameter panels.
A prologue is text display at the beginning of the help screen, before the usage section.
This is commonly used for version information, support contact details, or additional notes.

The epilogue is set via the :attr:`.App.help_epilogue` attribute:

.. code-block:: python

   from cyclopts import App

   app = App(
       name="myapp",
       help="My application description.",
       help_epilogue="Support: support@example.com"
   )

   @app.default
   def main():
       """Main command."""
       pass

   app()

.. code-block:: console

   $ myapp --help
   Usage: myapp [ARGS]

   My application description.

   ╭─ Commands ────────────────────────────────────────────────────────────╮
   │ --help -h  Display this message and exit.                             │
   │ --version  Display application version.                               │
   ╰───────────────────────────────────────────────────────────────────────╯

   Support: support@example.com

The prologue is set via the :attr:`.App.help_prologue` attribute:

.. code-block:: python

   from cyclopts import App

   app = App(
       name="myapp",
       help="My application help.",
       help_prologue=f"myapp, v1.0.0 (http://example.myapp.com)"
   )
   app()

.. code-block:: console

   $ my-script --help
   myapp, v1.0.0 (http://example.myapp.com)

   Usage: myapp COMMAND

   My application help.

   ╭─ Commands ────────────────────────────────────────────────────────────╮
   │ --help -h  Display this message and exit.                             │
   │ --version  Display application version.                               │
   ╰───────────────────────────────────────────────────────────────────────╯

Like :attr:`.App.help_format`, epilogues and prologues inherit from parent to child apps.
This allows you to set a single epilogue or prologue that applies across your entire application:

.. code-block:: python

   parent = App(
       name="myapp",
       help_epilogue="Version 1.0.0 | support@example.com"
   )

   # Child inherits parent's epilogue
   child = App(name="process", help="Process data files.")
   parent.command(child)

   # Another child overrides with its own epilogue
   admin = App(
       name="admin",
       help="Admin commands.",
       help_epilogue="Admin Tools v2.0 | USE WITH CAUTION"
   )
   parent.command(admin)

   parent()

.. code-block:: console

   $ myapp process --help
   Usage: myapp process

   Process data files.

   Version 1.0.0 | support@example.com    # Inherited from parent

   $ myapp admin --help
   Usage: myapp admin

   Admin commands.

   Admin Tools v2.0 | USE WITH CAUTION    # Overridden by child

To disable the epilogue or prologue for a specific subcommand, set it to an empty string:

.. code-block:: python

   no_epilogue = App(name="internal", help_epilogue="")
   parent.command(no_epilogue)

.. code-block:: python

   no_prologue = App(name="internal", help_prologue="")
   parent.command(no_prologue)

--------------------
Help Customization
--------------------
For advanced customization of help screen appearance, including custom formatters,
styled panels, and dynamic column layouts, see :ref:`Help Customization`.


.. _PEP-0257: https://peps.python.org/pep-0257/
.. _PEP-0287: https://peps.python.org/pep-0287/
.. _Rich Markup: https://rich.readthedocs.io/en/stable/markup.html
