Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ prompt is displayed.
full type hints and IDE autocompletion for `self._cmd` without needing to override and cast
the property.
- Added `traceback_kwargs` attribute to allow customization of Rich-based tracebacks.
- Added ability to customize `prompt-toolkit` completion menu colors by overriding
`Cmd2Style.COMPLETION_MENU_ITEM` and `Cmd2Style.COMPLETION_MENU_META` in the `cmd2` theme.

## 3.5.1 (April 24, 2026)

Expand Down
45 changes: 45 additions & 0 deletions cmd2/cmd2.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
from prompt_toolkit.output import DummyOutput, create_output
from prompt_toolkit.patch_stdout import patch_stdout
from prompt_toolkit.shortcuts import CompleteStyle, PromptSession, choice, set_title
from prompt_toolkit.styles import DynamicStyle
from prompt_toolkit.styles import Style as PtStyle
from rich.console import (
Group,
JustifyMethod,
Expand Down Expand Up @@ -714,6 +716,46 @@ def _should_continue_multiline(self) -> bool:
# No macro found or already processed. The statement is complete.
return False

def _get_pt_style(self) -> "PtStyle":
"""Return the prompt_toolkit style for the completion menu."""

def to_pt_style(rich_style: Style | None) -> str:
"""Convert a rich Style object to a prompt_toolkit style string."""
if not rich_style:
return ""
parts = ["noreverse"]
if rich_style.color:
c = rich_style.color.get_truecolor()
parts.append(f"fg:#{c.red:02x}{c.green:02x}{c.blue:02x}")
else:
parts.append("fg:default")

if rich_style.bgcolor:
c = rich_style.bgcolor.get_truecolor()
parts.append(f"bg:#{c.red:02x}{c.green:02x}{c.blue:02x}")
else:
parts.append("bg:default")

if rich_style.bold is not None:
parts.append("bold" if rich_style.bold else "nobold")
if rich_style.italic is not None:
parts.append("italic" if rich_style.italic else "noitalic")
if rich_style.underline is not None:
parts.append("underline" if rich_style.underline else "nounderline")
return " ".join(parts)

theme = ru.get_theme()
item_style = to_pt_style(theme.styles.get(Cmd2Style.COMPLETION_MENU_ITEM))
meta_style = to_pt_style(theme.styles.get(Cmd2Style.COMPLETION_MENU_META))

return PtStyle.from_dict(
{
"completion-menu.completion.current": item_style,
"completion-menu.meta.completion.current": meta_style,
"completion-menu.multi-column-meta": meta_style,
}
)

def _create_main_session(self, auto_suggest: bool, completekey: str) -> PromptSession[str]:
"""Create and return the main PromptSession for the application.

Expand Down Expand Up @@ -755,6 +797,7 @@ def _(event: Any) -> None: # pragma: no cover
"multiline": filters.Condition(self._should_continue_multiline),
"prompt_continuation": self.continuation_prompt,
"rprompt": self.get_rprompt,
"style": DynamicStyle(self._get_pt_style),
}

if self.stdin.isatty() and self.stdout.isatty():
Expand Down Expand Up @@ -3578,6 +3621,7 @@ def read_input(
key_bindings=self.main_session.key_bindings,
input=self.main_session.input,
output=self.main_session.output,
style=DynamicStyle(self._get_pt_style),
)

return self._read_raw_input(prompt, temp_session)
Expand All @@ -3596,6 +3640,7 @@ def read_secret(
temp_session: PromptSession[str] = PromptSession(
input=self.main_session.input,
output=self.main_session.output,
style=DynamicStyle(self._get_pt_style),
)

return self._read_raw_input(prompt, temp_session, is_password=True)
Expand Down
4 changes: 4 additions & 0 deletions cmd2/styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class Cmd2Style(StrEnum):
"""

COMMAND_LINE = "cmd2.example" # Command line examples in help text
COMPLETION_MENU_ITEM = "cmd2.completion_menu.item" # Selected completion item
COMPLETION_MENU_META = "cmd2.completion_menu.meta" # Selected completion help/meta text
ERROR = "cmd2.error" # Error text (used by perror())
HELP_HEADER = "cmd2.help.header" # Help table header text
HELP_LEADER = "cmd2.help.leader" # Text right before the help tables are listed
Expand All @@ -63,6 +65,8 @@ class Cmd2Style(StrEnum):
# Tightly coupled with the Cmd2Style enum.
DEFAULT_CMD2_STYLES: dict[str, StyleType] = {
Cmd2Style.COMMAND_LINE: Style(color=Color.CYAN, bold=True),
Cmd2Style.COMPLETION_MENU_ITEM: Style(color=Color.BLACK, bgcolor=Color.GREEN),
Cmd2Style.COMPLETION_MENU_META: Style(color=Color.BLACK, bgcolor=Color.LIGHT_GREEN),
Cmd2Style.ERROR: Style(color=Color.BRIGHT_RED),
Cmd2Style.HELP_HEADER: Style(color=Color.BRIGHT_GREEN),
Cmd2Style.HELP_LEADER: Style(color=Color.CYAN),
Expand Down
7 changes: 7 additions & 0 deletions docs/features/completion.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ demonstration of how this is used.
[read_input](https://github.com/python-cmd2/cmd2/blob/main/examples/read_input.py) example for a
demonstration.

## Custom Completion Menu Colors

`cmd2` provides the ability to customize the foreground and background colors of the completion menu
items and their associated help text. See
[Customizing Completion Menu Colors](./theme.md#customizing-completion-menu-colors) in the Theme
documentation for more details.

## For More Information

See [cmd2's argparse_utils API](../api/argparse_utils.md) for a more detailed discussion of argparse
Expand Down
14 changes: 14 additions & 0 deletions docs/features/theme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,19 @@
information. You can use this to brand your application and set an overall consistent look and feel
that is appealing to your user base.

## Customizing Completion Menu Colors

`cmd2` leverages `prompt-toolkit` for its tab completion menu. You can customize the colors of the
completion menu by overriding the following styles in your `cmd2` theme:

- `Cmd2Style.COMPLETION_MENU_ITEM`: The background and foreground color of the selected completion
item.
- `Cmd2Style.COMPLETION_MENU_META`: The background and foreground color of the selected completion
item's help/meta text.

By default, these are styled with black text on a green background to provide contrast.

## Example

See [rich_theme.py](https://github.com/python-cmd2/cmd2/blob/main/examples/rich_theme.py) for a
simple example of configuring a custom theme for your `cmd2` application.
10 changes: 10 additions & 0 deletions docs/upgrades.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ See the
example for a demonstration of how to implement a background thread that refreshes the toolbar
periodically.

### Custom Completion Menu Colors

`cmd2` now leverages `prompt-toolkit` for its tab completion menu and provides the ability to
customize its appearance using the `cmd2` theme.

- **Customization**: Override the `Cmd2Style.COMPLETION_MENU_ITEM` and
`Cmd2Style.COMPLETION_MENU_META` styles using `cmd2.rich_utils.set_theme()`. See
[Customizing Completion Menu Colors](features/theme.md#customizing-completion-menu-colors) for
more details.

### Deleted Modules

Removed `rl_utils.py` and `terminal_utils.py` since `prompt-toolkit` provides this functionality.
Expand Down
Loading