diff options
-rw-r--r-- | cmd2/argparse_completer.py | 47 | ||||
-rw-r--r-- | cmd2/argparse_custom.py | 8 | ||||
-rw-r--r-- | tests/test_argparse_completer.py | 91 |
3 files changed, 120 insertions, 26 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 0c4cafde..1a8dd473 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -454,16 +454,28 @@ class AutoCompleter(object): # Here we're done parsing all of the prior arguments. We know what the next argument is. - completion_results = [] - # if we don't have a flag to populate with arguments and the last token starts with # a flag prefix then we'll complete the list of flag options - if not flag_arg.needed and len(tokens[-1]) > 0 and tokens[-1][0] in self._parser.prefix_chars and \ - not skip_remaining_flags: - return utils.basic_complete(text, line, begidx, endidx, - [flag for flag in self._flags if flag not in matched_flags]) + if not flag_arg.needed and len(tokens[-1]) > 0 and \ + tokens[-1][0] in self._parser.prefix_chars and not skip_remaining_flags: + + # Build a list of flags that can be tab completed + match_against = [] + + for flag in self._flags: + # Make sure this flag hasn't already been used + if flag not in matched_flags: + # Make sure this flag isn't considered hidden + action = self._flag_to_action[flag] + if action.help != argparse.SUPPRESS: + match_against.append(flag) + + return utils.basic_complete(text, line, begidx, endidx, match_against) + + completion_results = [] + # we're not at a positional argument, see if we're in a flag argument - elif not current_is_positional: + if not current_is_positional: if flag_action is not None: consumed = consumed_arg_values[flag_action.dest]\ if flag_action.dest in consumed_arg_values else [] @@ -614,35 +626,26 @@ class AutoCompleter(object): @staticmethod def _print_arg_hint(arg: argparse.Action) -> None: """Print argument hint to the terminal when tab completion results in no results""" - # is hinting disabled for this argument? + + # Check if hinting is disabled suppress_hint = getattr(arg, ATTR_SUPPRESS_TAB_HINT, False) - if suppress_hint: + if suppress_hint or arg.help == argparse.SUPPRESS: return # Check if this is a flag if arg.option_strings: flags = ', '.join(arg.option_strings) - param = '' - if arg.nargs is None or arg.nargs != 0: - param += ' ' + str(arg.dest).upper() - + param = ' ' + str(arg.dest).upper() prefix = '{}{}'.format(flags, param) # Otherwise this is a positional else: prefix = '{}'.format(str(arg.dest).upper()) - if not arg.help or arg.help == argparse.SUPPRESS: - help_text = '' - else: - help_text = arg.help - - # is there anything to print for this parameter? - if not prefix and not help_text: - return - prefix = ' {0: <{width}} '.format(prefix, width=20) pref_len = len(prefix) + + help_text = '' if arg.help is None else arg.help help_lines = help_text.splitlines() if len(help_lines) == 1: diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index 6361bdb9..b05ca6ed 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -59,7 +59,7 @@ def _add_argument_wrapper(self, *args, choices_method: Optional[Callable[[Any], Iterable[Any]]] = None, completer_function: Optional[Callable[[str, str, int, int], List[str]]] = None, completer_method: Optional[Callable[[Any, str, str, int, int], List[str]]] = None, - suppress_hint: bool = False, + suppress_tab_hint: bool = False, descriptive_header: Optional[str] = None, **kwargs) -> argparse.Action: """ @@ -77,8 +77,8 @@ def _add_argument_wrapper(self, *args, :param choices_method: cmd2-app method that provides choices for this argument :param completer_function: tab-completion function that provides choices for this argument :param completer_method: cmd2-app tab-completion method that provides choices for this argument - :param suppress_hint: when AutoCompleter has no choices to show during tab completion, it displays the current - argument's help text as a hint. Set this to True to suppress the hint. Defaults to False. + :param suppress_tab_hint: when AutoCompleter has no choices to show during tab completion, it displays the current + argument's help text as a hint. Set this to True to suppress the hint. Defaults to False. :param descriptive_header: if the provided choices are CompletionItems, then this header will display during tab completion. Defaults to None. @@ -152,7 +152,7 @@ def _add_argument_wrapper(self, *args, setattr(new_arg, ATTR_CHOICES_CALLABLE, ChoicesCallable(is_method=True, is_completer=True, to_call=completer_method)) - setattr(new_arg, ATTR_SUPPRESS_TAB_HINT, suppress_hint) + setattr(new_arg, ATTR_SUPPRESS_TAB_HINT, suppress_tab_hint) setattr(new_arg, ATTR_DESCRIPTIVE_COMPLETION_HEADER, descriptive_header) return new_arg diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index c73290e0..a3fa6a59 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -104,6 +104,21 @@ class AutoCompleteTester(cmd2.Cmd): def do_completer(self, args: argparse.Namespace) -> None: pass + ############################################################################################################ + # Begin code related to testing tab hints + ############################################################################################################ + hint_parser = Cmd2ArgParser() + hint_parser.add_argument('-f', '--flag', help='a flag arg') + hint_parser.add_argument('-s', '--suppressed_help', help=argparse.SUPPRESS) + hint_parser.add_argument('-t', '--suppressed_hint', help='a flag arg', suppress_tab_hint=True) + + hint_parser.add_argument('hint_pos', help='here is a hint\nwith new lines') + hint_parser.add_argument('no_help_pos') + + @with_argparser(hint_parser) + def do_hint(self, args: argparse.Namespace) -> None: + pass + @pytest.fixture def ac_app(): @@ -263,6 +278,82 @@ def test_completion_items_default_header(ac_app): assert DEFAULT_DESCRIPTIVE_HEADER in ac_app.completion_header +def test_autocomp_hint_flag(ac_app, capsys): + text = '' + line = 'hint --flag {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + out, err = capsys.readouterr() + + assert first_match is None + assert out == ''' +Hint: + -f, --flag FLAG a flag arg + +''' + + +def test_autocomp_hint_suppressed_help(ac_app, capsys): + text = '' + line = 'hint --suppressed_help {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + out, err = capsys.readouterr() + + assert first_match is None + assert not out + + +def test_autocomp_hint_suppressed_hint(ac_app, capsys): + text = '' + line = 'hint --suppressed_hint {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + out, err = capsys.readouterr() + + assert first_match is None + assert not out + + +def test_autocomp_hint_pos(ac_app, capsys): + text = '' + line = 'hint {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + out, err = capsys.readouterr() + + assert first_match is None + assert out == ''' +Hint: + HINT_POS here is a hint + with new lines + +''' + +def test_autocomp_hint_no_help(ac_app, capsys): + text = '' + line = 'hint foo {}'.format(text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + out, err = capsys.readouterr() + + assert first_match is None + assert not out == ''' +Hint: + NO_HELP_POS + +''' + # def test_autcomp_hint_in_narg_range(cmd2_app, capsys): # text = '' # line = 'suggest -d 2 {}'.format(text) |