Skip to content

Commit

Permalink
Feature: theming support for list of subcommand aliases (#152)
Browse files Browse the repository at this point in the history
* Feature: style list of subcommand aliases

* Dark theme: set default styles for `alias` and `alias_secondary`
  • Loading branch information
janluke authored May 15, 2023
1 parent 14ae6d6 commit 97ad6d4
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 7 deletions.
25 changes: 21 additions & 4 deletions cloup/_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"""
import inspect
from typing import (
Any, Callable, Dict, Iterable, List, NamedTuple, Optional, Tuple, Type,
TypeVar, Union, cast, overload,
Any, Callable, Dict, Iterable, List, NamedTuple, Optional, Sequence, Tuple,
Type, TypeVar, Union, cast, overload,
)

import click
Expand All @@ -36,6 +36,7 @@
from ._sections import Section, SectionMixin
from ._util import click_version_ge_8_1, first_bool, reindent
from .constraints import ConstraintMixin
from .styling import DEFAULT_THEME
from .typing import AnyCallable

ClickCommand = TypeVar('ClickCommand', bound=click.Command)
Expand Down Expand Up @@ -227,10 +228,26 @@ def format_subcommand_name(
) -> str:
aliases = getattr(cmd, 'aliases', None)
if aliases and self.must_show_subcommand_aliases(ctx):
alias_list = ', '.join(aliases)
return f"{name} ({alias_list})"
assert isinstance(ctx, cloup.Context)
theme = cast(
cloup.HelpTheme, ctx.formatter_settings.get("theme", DEFAULT_THEME)
)
alias_list = self.format_subcommand_aliases(aliases, theme)
return f"{name} {alias_list}"
return name

@staticmethod
def format_subcommand_aliases(aliases: Sequence[str], theme: cloup.HelpTheme) -> str:
secondary_style = theme.alias_secondary
if secondary_style is None or secondary_style == theme.alias:
return theme.alias(f"({', '.join(aliases)})")
else:
return (
secondary_style("(")
+ secondary_style(", ").join(theme.alias(alias) for alias in aliases)
+ secondary_style(")")
)

# MyPy complains because "Signature of "group" incompatible with supertype".
# The supertype signature is (*args, **kwargs), which is compatible with
# this provided that you pass all arguments (expect "name") as keyword arg.
Expand Down
19 changes: 19 additions & 0 deletions cloup/styling.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import click

from cloup._util import FrozenSpace, click_version_tuple, delete_keys, identity
from cloup.typing import MISSING, Possibly

IStyle = Callable[[str], str]
"""A callable that takes a string and returns a styled version of it."""
Expand Down Expand Up @@ -39,6 +40,8 @@ class HelpTheme(NamedTuple):
Style of the second column of a definition list (help text).
:param epilog:
Style of the epilog.
:param alias:
Style of subcommand aliases in a definition lists.
"""

invoked_command: IStyle = identity
Expand All @@ -62,6 +65,13 @@ class HelpTheme(NamedTuple):
col2: IStyle = identity
"""Style of the second column of a definition list (help text)."""

alias: IStyle = identity
"""Style of subcommand aliases in a definition lists."""

alias_secondary: Optional[IStyle] = None
"""Style of separator and eventual parenthesis/brackets in subcommand alias lists.
If not provided, the ``alias`` style will be used."""

epilog: IStyle = identity
"""Style of the epilog."""

Expand All @@ -73,9 +83,13 @@ def with_(
section_help: Optional[IStyle] = None,
col1: Optional[IStyle] = None,
col2: Optional[IStyle] = None,
alias: Optional[IStyle] = None,
alias_secondary: Possibly[Optional[IStyle]] = MISSING,
epilog: Optional[IStyle] = None,
) -> 'HelpTheme':
kwargs = {key: val for key, val in locals().items() if val is not None}
if alias_secondary is MISSING:
del kwargs["alias_secondary"]
kwargs.pop('self')
if kwargs:
return self._replace(**kwargs)
Expand All @@ -89,6 +103,8 @@ def dark() -> "HelpTheme":
heading=Style(fg='bright_white', bold=True),
constraint=Style(fg='magenta'),
col1=Style(fg='bright_yellow'),
alias=Style(fg='yellow'),
alias_secondary=Style(fg='white'),
)

@staticmethod
Expand Down Expand Up @@ -184,3 +200,6 @@ class Color(FrozenSpace):
bright_magenta = "bright_magenta"
bright_cyan = "bright_cyan"
bright_white = "bright_white"


DEFAULT_THEME = HelpTheme()
2 changes: 2 additions & 0 deletions examples/manim/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
# col1=Style(...),
col2=Style(dim=True),
epilog=Style(fg=Color.bright_white, italic=True),
alias=Style(fg=Color.yellow),
alias_secondary=Style(fg=Color.white),
),
),
)
Expand Down
32 changes: 30 additions & 2 deletions tests/test_aliases.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from typing import Optional

import click
import pytest

import cloup
from cloup import Group
from cloup._util import first_bool, reindent
from cloup import Color, Group, HelpTheme, Style
from cloup._util import first_bool, identity, reindent
from cloup.styling import IStyle
from cloup.typing import MISSING


Expand Down Expand Up @@ -168,3 +171,28 @@ def test_cloup_subgroup_help(cli, runner):
--help Show this message and exit.
""")
assert res.output == expected


def test_alias_are_correctly_styled(runner):
red = Style(fg=Color.red)
green = Style(fg=Color.green)

def fmt(alias: IStyle = identity, alias_secondary: Optional[IStyle] = None):
theme = HelpTheme(alias=alias, alias_secondary=alias_secondary)
return Group.format_subcommand_aliases(["i", "add"], theme)

# No styles (default theme)
assert fmt() == "(i, add)"

# Only theme.alias
assert fmt(alias=green) == f"{green('(i, add)')}"

# Only theme.alias_secondary
assert fmt(alias_secondary=green) == (
green("(") + "i" + green(", ") + "add" + green(")")
)

# Both
assert fmt(alias=red, alias_secondary=green) == (
green("(") + red("i") + green(", ") + red("add") + green(")")
)
2 changes: 1 addition & 1 deletion tests/test_formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
import pytest

from cloup import HelpFormatter
from cloup.typing import Possibly
from cloup.formatting import HelpSection, unstyled_len
from cloup.formatting.sep import (
Hline, RowSepIf, RowSepPolicy, multiline_rows_are_at_least
)
from cloup.styling import HelpTheme, Style
from cloup.typing import Possibly
from tests.util import parametrize

LOREM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor."
Expand Down

0 comments on commit 97ad6d4

Please sign in to comment.