Text Markup & Formatting¶
Markup¶
rich-click supports 4 different values for text_markup
, which determines how text is rendered:
'ansi'
: (Default) Rendered as plain text with ANSI escape codes handled.'rich'
: Rendered using Rich's markup syntax.'markdown'
: Rendered with markdown.None
: Rendered as plain text, ANSI escape codes are not handled.
rich-click ≥1.8.0 deprecation
Prior to rich-click 1.8.0, markup was controlled by the booleans use_rich_markup
and use_markdown
.
These booleans have been silently deprecated (read: they will still be supported for the distant future),
and users are encouraged to use the text_markup
config option instead.
Rich markup¶
In order to be as widely compatible as possible with a simple import,
rich-click does not parse rich formatting markup (eg. [red]
) by default.
You need to opt-in to this behaviour.
Remember that you'll need to escape any regular square brackets using a back slash in your help texts,
for example: [dim]\[my-default: foo][\]
For more information, read the Rich docs on markup and styles.
help_config = {"text_markup": "rich"}
help_config = click.RichHelpConfiguration(text_markup="rich")
click.rich_click.TEXT_MARKUP = "rich"
See
examples/04_rich_markup.py
for an example.
Markdown¶
If you prefer, you can use Markdown text.
help_config = {"text_markup": "markdown"}
help_config = click.RichHelpConfiguration(text_markup="markdown")
click.rich_click.TEXT_MARKUP = "markdown"
See
examples/05_markdown.py
for an example.
Markup in text¶
The selected text_markup
is used to render all text in your CLI help that can be set in the high-level API:
- The command's
help
- Each subcommand's
help
- Each parameter's
help
- Each panel's
help
- Header text, footer text, and epilog text.
# /// script
# dependencies = ["rich-click>=1.9"]
# ///
import rich_click as click
@click.command(epilog="[blue]For more information, read the docs[/blue]")
@click.option("--foo", "-f", help="[green]foo[/]")
@click.option("--bar", "-b", help="[default on green]bar[/]")
@click.option_panel("Options",
help="[magenta not dim]These are [bold]all[/b] the options available.[/]")
@click.rich_config({"text_markup": "rich"})
def cli():
"""CLI for my [red]app[/red].
This is a demonstration of
[#FF6B6B bold]r[/][#FF8E53 bold]i[/][#FFB347 bold]c[/][#4ECDC4 bold]h[/]
markup in CLI help text.
"""
if __name__ == "__main__":
cli()
You can also set the styles of help
for objects in decorators using help_style=
.
The below code renders the same as the above code:
# /// script
# dependencies = ["rich-click>=1.9"]
# ///
import rich_click as click
@click.command(epilog="[blue]For more information, read the docs[/blue]")
@click.option("--foo", "-f", help="foo", help_style="green")
@click.option("--bar", "-b", help="bar", help_style="default on green")
@click.option_panel("Options",
help="These are [bold]all[/b] the options available.",
help_style="magenta not dim")
@click.rich_config({"text_markup": "rich"})
def cli():
"""CLI for my [red]app[/red].
This is a demonstration of
[#FF6B6B bold]r[/][#FF8E53 bold]i[/][#FFB347 bold]c[/][#4ECDC4 bold]h[/]
markup in CLI help text.
"""
if __name__ == "__main__":
cli()
Emojis¶
Emoji codes are rendered by default in 'rich'
and 'markdown'
text markup modes.
For other modes, including the default mode, emojis are not rendered.
You can explicitly enable and disable emojis with the config option text_emoji
:
# /// script
# dependencies = ["rich-click>=1.9"]
# ///
import rich_click as click
@click.command()
@click.option("--foo", "-f", help="Boo! :ghost: Oops, I mean... foo! :sweat_smile:")
@click.option("--bar", "-b", help="Bar - :bar_chart:")
@click.rich_config({"text_emojis": True})
def cli():
"""Demo of :heart_eyes_cat: emojis!
Note that this text is not rich markup: [b]See?[/b] :joy:
Emojis can be set independently of markup. :point_up: :nerd_face:
"""
if __name__ == "__main__":
cli()
You can view all available emojis with the following command:
uvx --from rich -- python3 -m rich.emoji
Newlines¶
Handling newlines on rich-click's end involves discretion because in a docstring, newlines can represent a softwrap; they are not necessarily genuine new lines.
For 'markdown'
mode, we send all newline handling directly to rich.markdown.Markdown()
For other modes, we implement the following rules:
- Double newlines collapse to single newlines by default.
- Single newlines are not preserved, unless...
- The line starts with
'- '
,'* '
,'> '
or' '
(4 spaces).
The following code snippet demonstrates this:
# /// script
# dependencies = ["rich-click>=1.9"]
# ///
import rich_click as click
@click.command("newline-control")
def cli():
"""Newline
control
Double newlines are always preserved.
But single newlines are usually not.
There are however a few situations where we preserve single newlines.
Indented lines with 4+ spaces are preserved:
```python
from foo import bar
\n\
bar.action()
```
Unordered lists are preserved:
- like
- this
also:
* newlines within unordered lists
are collapsed
down.
* pretty neat!
Last but not least, we preserve:
> block
> quotes
"""
if __name__ == "__main__":
cli()
Note that this differs from how base Click handles newlines.
The following is the same CLI help text but using import click
instead of import rich_click as click
:
Spacing¶
By default, rich-click renders double newlines as single newlines. This is a deliberate decision to make help text take up less vertical space.
However, you can override this behavior by setting the config option text_paragraph_linebreaks
to "\n\n"
.
# /// script
# dependencies = ["rich-click>=1.9"]
# ///
import rich_click as click
@click.command("newline-control")
@click.rich_config({"text_paragraph_linebreaks": "\n\n"})
def cli():
"""Newline
control
Double newlines are always preserved.
But single newlines are usually not.
There are however a few situations where we preserve single newlines.
Indented lines with 4+ spaces are preserved:
```python
from foo import bar
\n\
bar.action()
```
Unordered lists are preserved:
- like
- this
also:
* newlines within unordered lists
are collapsed
down.
* pretty neat!
Last but not least, we preserve:
> block
> quotes
"""
if __name__ == "__main__":
cli()
Info
This default behavior of collapsing newlines is not true when text_markup='markdown'
.
For markdown, we do not do any newline manipulation.