From 0ddfaaa9465ed316ff1383a9b51826b6f366401e Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Fri, 24 Apr 2026 18:34:13 -0400 Subject: [PATCH 1/2] Remove always_show_hint settable This setting worked well for some cases when cmd2 was using readline. However, due to subtle differences in how prompt-toolkit and readline work, setting this True provided a bad user experience with prompt-toolkit. --- CHANGELOG.md | 2 ++ cmd2/argparse_completer.py | 13 ------------- cmd2/cmd2.py | 5 ----- cmd2/pt_utils.py | 2 +- docs/features/builtin_commands.md | 1 - docs/features/initialization.md | 1 - docs/features/settings.md | 5 ----- tests/scripts/postcmds.txt | 2 +- tests/scripts/precmds.txt | 2 +- tests/test_cmd2.py | 8 ++++---- tests/test_completion.py | 2 +- tests/test_pt_utils.py | 24 ------------------------ 12 files changed, 10 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5e563151..e640542ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,8 @@ prompt is displayed. - Renamed `Cmd._command_parsers` to `Cmd.command_parsers`. - Removed `RichPrintKwargs` `TypedDict` in favor of using `Mapping[str, Any]`, allowing for greater flexibility in passing keyword arguments to `console.print()` calls. + - Removed `always_show_hint` settable as it provided a poor user experience with + `prompt-toolkit` - Enhancements - New `cmd2.Cmd` parameters - **auto_suggest**: (boolean) if `True`, provide fish shell style auto-suggestions. These diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 9f07162f6..cdf017038 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -491,13 +491,6 @@ def _handle_last_token( # If we have results, then return them if completions: - if not completions.hint: - # Add a hint even though there are results in case Cmd.always_show_hint is True. - completions = dataclasses.replace( - completions, - hint=_build_hint(self._parser, flag_arg_state.action), - ) - return completions # Otherwise, print a hint if the flag isn't finished or text isn't possibly the start of a flag @@ -519,12 +512,6 @@ def _handle_last_token( # If we have results, then return them if completions: - if not completions.hint: - # Add a hint even though there are results in case Cmd.always_show_hint is True. - completions = dataclasses.replace( - completions, - hint=_build_hint(self._parser, pos_arg_state.action), - ) return completions # Otherwise, print a hint if text isn't possibly the start of a flag diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index f2ab98944..5668aaee4 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -464,7 +464,6 @@ def __init__( self.interactive_pipe = False # Attributes which ARE dynamically settable via the set command at runtime - self.always_show_hint = False self.debug = False self.echo = False self.editor = self.DEFAULT_EDITOR @@ -1324,10 +1323,6 @@ def allow_style_type(value: str) -> ru.AllowStyle: choices_provider=get_allow_style_choices, ) ) - - self.add_settable( - Settable("always_show_hint", bool, "Display completion hint even when completion suggestions print", self) - ) self.add_settable(Settable("debug", bool, "Show full traceback on exception", self)) self.add_settable(Settable("echo", bool, "Echo command issued into output", self)) self.add_settable(Settable("editor", str, "Program used by 'edit'", self)) diff --git a/cmd2/pt_utils.py b/cmd2/pt_utils.py index 8ebdb9f3e..ef383a952 100644 --- a/cmd2/pt_utils.py +++ b/cmd2/pt_utils.py @@ -98,7 +98,7 @@ def get_completions(self, document: Document, _complete_event: object) -> Iterab print_formatted_text(pt_filter_style("\n" + capture.get())) # Print hint if present and settings say we should - if completions.hint and (self.cmd_app.always_show_hint or not completions): + if completions.hint and not completions: print_formatted_text(pt_filter_style(completions.hint)) if not completions: diff --git a/docs/features/builtin_commands.md b/docs/features/builtin_commands.md index ddd633de6..ee92ec201 100644 --- a/docs/features/builtin_commands.md +++ b/docs/features/builtin_commands.md @@ -80,7 +80,6 @@ application: Name Value Description ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── allow_style Terminal Allow ANSI text style sequences in output (valid values: Always, Never, Terminal) - always_show_hint False Display completion hint even when completion suggestions print debug False Show full traceback on exception echo False Echo command issued into output editor vim Program used by 'edit' diff --git a/docs/features/initialization.md b/docs/features/initialization.md index dad4226ce..c1ad6e2c0 100644 --- a/docs/features/initialization.md +++ b/docs/features/initialization.md @@ -33,7 +33,6 @@ The `cmd2.Cmd` class provides a large number of public instance attributes which Here are instance attributes of `cmd2.Cmd` which developers might wish to override: -- **always_show_hint**: if `True`, display tab completion hint even when completion suggestions print (Default: `False`) - **bottom_toolbar**: if `True`, then a bottom toolbar will be displayed (Default: `False`) - **broken_pipe_warning**: if non-empty, this string will be displayed if a broken pipe error occurs - **continuation_prompt**: used for multiline commands on 2nd+ line of input diff --git a/docs/features/settings.md b/docs/features/settings.md index 37d951639..84cc01616 100644 --- a/docs/features/settings.md +++ b/docs/features/settings.md @@ -37,11 +37,6 @@ This setting can be one of three values: stripped. - `Always` - ANSI escape sequences are always passed through to the output -### always_show_hint - -If `True`, display tab completion hint even when completion suggestions print. The default value of -this setting is `False`. - ### debug The default value of this setting is `False`, which causes the `cmd2.Cmd.pexcept` method to only diff --git a/tests/scripts/postcmds.txt b/tests/scripts/postcmds.txt index 7f93a5d46..d48172c0d 100644 --- a/tests/scripts/postcmds.txt +++ b/tests/scripts/postcmds.txt @@ -1 +1 @@ -set always_show_hint False +set allow_style Terminal diff --git a/tests/scripts/precmds.txt b/tests/scripts/precmds.txt index 241504ff4..7d036acfe 100644 --- a/tests/scripts/precmds.txt +++ b/tests/scripts/precmds.txt @@ -1 +1 @@ -set always_show_hint True +set allow_style Always diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index d8093eed1..d0a52c964 100644 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -488,11 +488,11 @@ def test_run_script_nested_run_scripts(base_app, request) -> None: expected = f""" {initial_run} _relative_run_script precmds.txt -set always_show_hint True +set allow_style Always help shortcuts _relative_run_script postcmds.txt -set always_show_hint False""" +set allow_style Terminal""" out, _err = run_cmd(base_app, "history -s") assert out == normalize(expected) @@ -505,11 +505,11 @@ def test_runcmds_plus_hooks(base_app, request) -> None: base_app.runcmds_plus_hooks(["run_script " + prefilepath, "help", "shortcuts", "run_script " + postfilepath]) expected = f""" run_script {prefilepath} -set always_show_hint True +set allow_style Always help shortcuts run_script {postfilepath} -set always_show_hint False""" +set allow_style Terminal""" out, _err = run_cmd(base_app, "history -s") assert out == normalize(expected) diff --git a/tests/test_completion.py b/tests/test_completion.py index a8b02d95f..b23cb8a3c 100644 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -1100,7 +1100,7 @@ def test_complete_set_value(cmd2_app) -> None: expected = ["SUCCESS"] completions = cmd2_app.complete(text, line, begidx, endidx) assert completions.to_strings() == Completions.from_values(expected).to_strings() - assert completions.hint.strip() == "Hint:\n value a test settable param" + assert completions.hint.strip() == "" def test_complete_set_value_invalid_settable(cmd2_app) -> None: diff --git a/tests/test_pt_utils.py b/tests/test_pt_utils.py index 146ab81c8..a8fcdbacc 100644 --- a/tests/test_pt_utils.py +++ b/tests/test_pt_utils.py @@ -34,7 +34,6 @@ def __init__(self) -> None: self.complete = Mock(return_value=cmd2.Completions()) self.stdout = io.StringIO() - self.always_show_hint = False self.statement_parser = Mock() self.statement_parser.terminators = [";"] self.statement_parser.shortcuts = [] @@ -335,29 +334,6 @@ def test_get_completions_no_matches(self, mock_cmd_app: MockCmd, monkeypatch) -> args, _ = mock_print.call_args assert cmd2_completions.hint in str(args[0]) - def test_get_completions_always_show_hints(self, mock_cmd_app: MockCmd, monkeypatch) -> None: - """Test that get_completions respects 'always_show_hint' and prints a hint even with no matches.""" - mock_print = Mock() - monkeypatch.setattr(pt_utils, "print_formatted_text", mock_print) - - completer = pt_utils.Cmd2Completer(cast(Any, mock_cmd_app)) - document = Document("test", cursor_position=4) - - # Enable hint printing when there are no matches. - mock_cmd_app.always_show_hint = True - - # Set up matches - cmd2_completions = cmd2.Completions(hint="Completion Hint") - mock_cmd_app.complete.return_value = cmd2_completions - - completions = list(completer.get_completions(document, None)) - assert not completions - - # Verify that only the completion hint printed - assert mock_print.call_count == 1 - args, _ = mock_print.call_args - assert cmd2_completions.hint in str(args[0]) - def test_get_completions_with_error(self, mock_cmd_app: MockCmd, monkeypatch) -> None: """Test get_completions with a completion error.""" mock_print = Mock() From 508898a50079569e7e42e0e7347e72c53db4b7e5 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Fri, 24 Apr 2026 18:46:02 -0400 Subject: [PATCH 2/2] Simplified a logic condition to prevent double checking Also: - Clarified a parameter description to document how to accept fish-shell style auto-suggestions --- cmd2/cmd2.py | 3 ++- cmd2/pt_utils.py | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 5668aaee4..e9cbb8f48 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -395,7 +395,8 @@ def __init__( instantiate and register all commands. If False, CommandSets must be manually installed with `register_command_set`. :param auto_suggest: If True, cmd2 will provide fish shell style auto-suggestions - based on history. If False, these will not be provided. + based on history. User can press right-arrow key to accept the + provided suggestion. :param bottom_toolbar: if ``True``, then a bottom toolbar will be displayed. :param command_sets: Provide CommandSet instances to load during cmd2 initialization. This allows CommandSets with custom constructor parameters to be diff --git a/cmd2/pt_utils.py b/cmd2/pt_utils.py index ef383a952..8b7da28da 100644 --- a/cmd2/pt_utils.py +++ b/cmd2/pt_utils.py @@ -97,11 +97,10 @@ def get_completions(self, document: Document, _complete_event: object) -> Iterab console.print(completions.table, end="", soft_wrap=False) print_formatted_text(pt_filter_style("\n" + capture.get())) - # Print hint if present and settings say we should - if completions.hint and not completions: - print_formatted_text(pt_filter_style(completions.hint)) - if not completions: + # # Print hint if present + if completions.hint: + print_formatted_text(pt_filter_style(completions.hint)) return # The length of the user's input minus any shortcut.