Skip to content

Commit 29ca1f2

Browse files
committed
feat(cmd2): print completion hints above prompt and allow optional bottom toolbar
Removed completion hints from the bottom toolbar and implemented printing them above the prompt with ANSI support. Added an optional 'include_bottom_toolbar' parameter to Cmd.__init__ which displays the application name (sys.argv[0]) in the bottom toolbar when enabled.
1 parent 8c7686e commit 29ca1f2

File tree

4 files changed

+24
-41
lines changed

4 files changed

+24
-41
lines changed

cmd2/cmd2.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ def __init__(
306306
auto_load_commands: bool = False,
307307
allow_clipboard: bool = True,
308308
suggest_similar_command: bool = False,
309+
include_bottom_toolbar: bool = False,
309310
intro: RenderableType = '',
310311
) -> None:
311312
"""Easy but powerful framework for writing line-oriented command interpreters, extends Python's cmd package.
@@ -354,10 +355,10 @@ def __init__(
354355
instantiate and register all commands. If False, CommandSets
355356
must be manually installed with `register_command_set`.
356357
:param allow_clipboard: If False, cmd2 will disable clipboard interactions
357-
:param suggest_similar_command: If ``True``, ``cmd2`` will attempt to suggest the most
358-
similar command when the user types a command that does
359-
not exist. Default: ``False``.
360-
"param intro: Intro banner to print when starting the application.
358+
:param suggest_similar_command: if ``True``, then when a command is not found,
359+
[cmd2.Cmd][] will look for similar commands and suggest them.
360+
:param include_bottom_toolbar: if ``True``, then a bottom toolbar will be displayed.
361+
:param intro: introduction to display at startup
361362
"""
362363
# Check if py or ipy need to be disabled in this instance
363364
if not include_py:
@@ -438,6 +439,7 @@ def __init__(
438439
# Initialize prompt-toolkit PromptSession
439440
self.history_adapter = Cmd2History(self)
440441
self.completer = Cmd2Completer(self)
442+
self.include_bottom_toolbar = include_bottom_toolbar
441443

442444
try:
443445
self.session: PromptSession[str] = PromptSession(
@@ -1625,10 +1627,8 @@ def _reset_completion_defaults(self) -> None:
16251627

16261628
def _bottom_toolbar(self) -> Any:
16271629
"""Get the bottom toolbar content."""
1628-
if self.formatted_completions:
1629-
return ANSI(self.formatted_completions.rstrip())
1630-
if self.completion_hint:
1631-
return ANSI(self.completion_hint.rstrip())
1630+
if self.include_bottom_toolbar:
1631+
return sys.argv[0]
16321632
return None
16331633

16341634
def tokens_for_completion(self, line: str, begidx: int, endidx: int) -> tuple[list[str], list[str]]:

cmd2/pt_utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
TYPE_CHECKING,
77
)
88

9+
from prompt_toolkit import print_formatted_text
910
from prompt_toolkit.completion import (
1011
Completer,
1112
Completion,
1213
)
1314
from prompt_toolkit.document import Document
15+
from prompt_toolkit.formatted_text import ANSI
1416
from prompt_toolkit.history import History
1517

1618
from . import (
@@ -64,6 +66,12 @@ def get_completions(self, document: Document, _complete_event: object) -> Iterab
6466
# We pass state=0 to trigger the completion calculation.
6567
self.cmd_app.complete(text, 0, line=line, begidx=begidx, endidx=endidx, custom_settings=self.custom_settings)
6668

69+
# Print formatted completions or hints above the prompt if present
70+
if self.cmd_app.formatted_completions:
71+
print_formatted_text(ANSI(self.cmd_app.formatted_completions.rstrip()))
72+
elif self.cmd_app.completion_hint:
73+
print_formatted_text(ANSI(self.cmd_app.completion_hint.rstrip()))
74+
6775
# Now we iterate over self.cmd_app.completion_matches and self.cmd_app.display_matches
6876
matches = self.cmd_app.completion_matches
6977
display_matches = self.cmd_app.display_matches

tests/test_cmd2.py

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3365,41 +3365,14 @@ def run_alert():
33653365
assert "another thread holds terminal_lock" in str(exceptions[0])
33663366

33673367

3368-
def test_bottom_toolbar(base_app):
3369-
from prompt_toolkit.formatted_text import ANSI
3370-
3371-
# Test when both are empty
3372-
base_app.formatted_completions = ''
3373-
base_app.completion_hint = ''
3368+
def test_bottom_toolbar(base_app, monkeypatch):
3369+
# Test default (disabled)
33743370
assert base_app._bottom_toolbar() is None
33753371

3376-
# Test when formatted_completions is set
3377-
text = 'completions'
3378-
base_app.formatted_completions = text + '\n'
3379-
base_app.completion_hint = ''
3380-
result = base_app._bottom_toolbar()
3381-
assert isinstance(result, ANSI)
3382-
3383-
# Test when completion_hint is set
3384-
hint = 'hint'
3385-
base_app.formatted_completions = ''
3386-
base_app.completion_hint = hint + ' '
3387-
result = base_app._bottom_toolbar()
3388-
assert isinstance(result, ANSI)
3389-
3390-
# Test prioritization and rstrip
3391-
with mock.patch('cmd2.cmd2.ANSI', wraps=ANSI) as mock_ansi:
3392-
# formatted_completions takes precedence
3393-
base_app.formatted_completions = 'formatted\n'
3394-
base_app.completion_hint = 'hint'
3395-
base_app._bottom_toolbar()
3396-
mock_ansi.assert_called_with('formatted')
3397-
3398-
# completion_hint used when formatted_completions is empty
3399-
base_app.formatted_completions = ''
3400-
base_app.completion_hint = 'hint '
3401-
base_app._bottom_toolbar()
3402-
mock_ansi.assert_called_with('hint')
3372+
# Test enabled
3373+
base_app.include_bottom_toolbar = True
3374+
monkeypatch.setattr(sys, 'argv', ['myapp.py'])
3375+
assert base_app._bottom_toolbar() == 'myapp.py'
34033376

34043377

34053378
def test_multiline_complete_statement_keyboard_interrupt(multiline_app, monkeypatch):

tests/test_pt_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ def __init__(self):
1818
self.completion_matches = []
1919
self.display_matches = []
2020
self.history = []
21+
self.formatted_completions = ''
22+
self.completion_hint = ''
2123

2224

2325
@pytest.fixture

0 commit comments

Comments
 (0)