diff options
author | Eric Lin <anselor@gmail.com> | 2018-10-06 17:47:12 +0000 |
---|---|---|
committer | Eric Lin <anselor@gmail.com> | 2018-10-06 17:47:12 +0000 |
commit | b0d44379e3cecc2171ded506c58b74ee6e596670 (patch) | |
tree | 0a4c756ae14c8fe567712018c2e6f5bcea9164b1 | |
parent | 96bcfe012b3b1659dbd0244ac6c4a43dfb5328d6 (diff) | |
download | cmd2-git-b0d44379e3cecc2171ded506c58b74ee6e596670.tar.gz |
Fixed a few edge cases:
- Once the argument list can match a positional and that positional is tagged
with nargs=argparse.REMAINDER it will consume all tokens including flag tokens.
AutoCompleter now correctly detects this case will no longer attempt to complete
flag tokens
- A single-character token that is a flag prefix doesn't count as a flag and is
parsed as a value. AutoCompleter now correctly detects this case.
-rwxr-xr-x | cmd2/argparse_completer.py | 23 | ||||
-rwxr-xr-x | examples/tab_autocompletion.py | 2 |
2 files changed, 19 insertions, 6 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 77942252..b8d6244b 100755 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -318,6 +318,7 @@ class AutoCompleter(object): flag_arg = AutoCompleter._ArgumentState() flag_action = None + # dict is used because object wrapper is necessary to allow inner functions to modify outer variables remainder = {'arg': None, 'action': None} matched_flags = [] @@ -334,7 +335,8 @@ class AutoCompleter(object): """Consuming token as a flag argument""" # we're consuming flag arguments # if this is not empty and is not another potential flag, count towards flag arguments - if token and token[0] not in self._parser.prefix_chars and flag_action is not None: + # if the token is a single character length, it doesn't matter that it matches a flag prefix + if token and (len(token) == 1 or token[0] not in self._parser.prefix_chars) and flag_action is not None: flag_arg.count += 1 # does this complete a option item for the flag @@ -377,7 +379,6 @@ class AutoCompleter(object): arg_state.max = float('inf') arg_state.variable = True if action.nargs == argparse.REMAINDER: - print("Setting remainder") remainder['action'] = action remainder['arg'] = arg_state elif action.nargs == '?': @@ -407,25 +408,35 @@ class AutoCompleter(object): is_last_token = idx >= len(tokens) - 1 # Only start at the start token index if idx >= self._token_start_index: + # If a remainder action is found, force all future tokens to go to that if remainder['arg'] is not None: - print("In Remainder mode") if remainder['action'] == pos_action: consume_positional_argument() continue elif remainder['action'] == flag_action: consume_flag_argument() continue - else: - print("!!") current_is_positional = False # Are we consuming flag arguments? if not flag_arg.needed: + # Special case when each of the following is true: + # - We're not in the middle of consuming flag arguments + # - The current positional argument count has hit the max count + # - The next positional argument is a REMAINDER argument + # Argparse will now treat all future tokens as arguments to the positional including tokens that look like flags + # so the completer should skip any flag related processing once this happens + skip_flag = False + if (pos_action is not None) and pos_arg.count >= pos_arg.max and next_pos_arg_index < len(self._positional_actions) and \ + self._positional_actions[next_pos_arg_index].nargs == argparse.REMAINDER: + skip_flag = True + + # At this point we're no longer consuming flag arguments. Is the current argument a potential flag? # If the argument is the start of a flag and this is the last token, we proceed forward to try # and match against our known flags. # If this argument is not the last token and the argument is exactly a flag prefix, then this # token should be consumed as an argument to a prior flag or positional argument. - if len(token) > 0 and token[0] in self._parser.prefix_chars and\ + if len(token) > 0 and token[0] in self._parser.prefix_chars and not skip_flag and\ (is_last_token or (not is_last_token and token not in self._parser.prefix_chars)): # reset some tracking values flag_arg.reset() diff --git a/examples/tab_autocompletion.py b/examples/tab_autocompletion.py index 6be7f0c8..7725be95 100755 --- a/examples/tab_autocompletion.py +++ b/examples/tab_autocompletion.py @@ -298,6 +298,8 @@ class TabCompleteExample(cmd2.Cmd): .format(movie['title'], movie['rating'], movie_id, ', '.join(movie['director']), '\n '.join(movie['actor']))) + elif args.command == 'add': + print('Adding Movie\n----------------\nTitle: {}\nRating: {}\nDirectors: {}\nActors: {}\n\n'.format(args.title, args.rating, ', '.join(args.director), ', '.join(args.actor))) def _do_media_shows(self, args) -> None: if not args.command: |