summaryrefslogtreecommitdiff
path: root/cmd2/argparse_completer.py
diff options
context:
space:
mode:
Diffstat (limited to 'cmd2/argparse_completer.py')
-rw-r--r--cmd2/argparse_completer.py44
1 files changed, 22 insertions, 22 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py
index a866b3ff..04dfa00e 100644
--- a/cmd2/argparse_completer.py
+++ b/cmd2/argparse_completer.py
@@ -10,6 +10,7 @@ import argparse
import inspect
import numbers
import shutil
+from collections import deque
from typing import Dict, List, Optional, Union
from . import cmd2
@@ -116,13 +117,14 @@ class AutoCompleter(object):
parent_tokens = dict()
self._parent_tokens = parent_tokens
- self._flags = [] # all flags in this command
- self._flag_to_action = {} # maps flags to the argparse action object
- self._positional_actions = [] # actions for positional arguments (by position index)
- self._subcommand_action = None # this will be set if self._parser has subcommands
+ self._flags = [] # all flags in this command
+ self._flag_to_action = {} # maps flags to the argparse action object
+ self._positional_actions = [] # actions for positional arguments (by position index)
+ self._mutually_exclusive_groups = [] # Each item is a list of actions
+ self._subcommand_action = None # this will be set if self._parser has subcommands
# Start digging through the argparse structures.
- # _actions is the top level container of parameter definitions
+ # _actions is the top level container of parameter definitions
for action in self._parser._actions:
# if the parameter is flag based, it will have option_strings
if action.option_strings:
@@ -138,14 +140,17 @@ class AutoCompleter(object):
if isinstance(action, argparse._SubParsersAction):
self._subcommand_action = action
+ # Keep track of what actions are in mutually exclusive groups
+ for group in self._parser._mutually_exclusive_groups:
+ self._mutually_exclusive_groups.append(group._group_actions)
+
def complete_command(self, tokens: List[str], text: str, line: str, begidx: int, endidx: int) -> List[str]:
"""Complete the command using the argparse metadata and provided argument dictionary"""
if not tokens:
return []
- # Count which positional argument index we're at now. Loop through all tokens on the command line so far
- # Skip any flags or flag parameter tokens
- next_pos_arg_index = 0
+ # Positionals args that are left to parse
+ remaining_positionals = deque(self._positional_actions)
# This gets set to True when flags will no longer be processed as argparse flags
# That can happen when -- is used or an argument with nargs=argparse.REMAINDER is used
@@ -229,7 +234,7 @@ class AutoCompleter(object):
# Therefore don't erase any tokens already consumed for this flag
consumed_arg_values.setdefault(action.dest, [])
else:
- # This flag is not resusable, so mark that we've seen it
+ # This flag is not reusable, so mark that we've seen it
matched_flags.extend(action.option_strings)
# It's possible we already have consumed values for this flag if it was used
@@ -255,12 +260,9 @@ class AutoCompleter(object):
else:
# If we aren't current tracking a positional, then get the next positional arg to handle this token
if pos_arg_state is None:
- pos_index = next_pos_arg_index
- next_pos_arg_index += 1
-
- # Make sure we are still have positional arguments to fill
- if pos_index < len(self._positional_actions):
- action = self._positional_actions[pos_index]
+ # Make sure we are still have positional arguments to parse
+ if remaining_positionals:
+ action = remaining_positionals.popleft()
# Are we at a subcommand? If so, forward to the matching completer
if action == self._subcommand_action:
@@ -295,10 +297,9 @@ class AutoCompleter(object):
elif pos_arg_state.count >= pos_arg_state.max:
pos_arg_state = None
- # Check if this a case in which we've finished all positionals before one that has nargs
- # set to argparse.REMAINDER. At this point argparse allows no more flags to be processed.
- if next_pos_arg_index < len(self._positional_actions) and \
- self._positional_actions[next_pos_arg_index].nargs == argparse.REMAINDER:
+ # Check if the next positional has nargs set to argparse.REMAINDER.
+ # At this point argparse allows no more flags to be processed.
+ if remaining_positionals and remaining_positionals[0].nargs == argparse.REMAINDER:
skip_remaining_flags = True
#############################################################################################
@@ -338,12 +339,11 @@ class AutoCompleter(object):
return []
# Otherwise check if we have a positional to complete
- elif pos_arg_state is not None or next_pos_arg_index < len(self._positional_actions):
+ elif pos_arg_state is not None or remaining_positionals:
# If we aren't current tracking a positional, then get the next positional arg to handle this token
if pos_arg_state is None:
- pos_index = next_pos_arg_index
- action = self._positional_actions[pos_index]
+ action = remaining_positionals.popleft()
pos_arg_state = AutoCompleter._ArgumentState(action)
try: