summaryrefslogtreecommitdiff
path: root/cmd2/argparse_completer.py
diff options
context:
space:
mode:
authorkotfu <kotfu@kotfu.net>2018-06-19 10:06:20 -0600
committerkotfu <kotfu@kotfu.net>2018-06-19 10:06:20 -0600
commit101395a437ef66846e207c039a12ee946128fab9 (patch)
tree4e024a7899396f4e111bda10f2cd8c053db12a5b /cmd2/argparse_completer.py
parentb0a0251c77e73a3f3c0a755f3fabb9fdf136ccfa (diff)
parentb5def934f4d368a7e1a1fe67a98b3cdcc14cd2d9 (diff)
downloadcmd2-git-101395a437ef66846e207c039a12ee946128fab9.tar.gz
Merge branch 'master' into plugin_functions
Diffstat (limited to 'cmd2/argparse_completer.py')
-rwxr-xr-xcmd2/argparse_completer.py81
1 files changed, 62 insertions, 19 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py
index d98a6eac..995aeb48 100755
--- a/cmd2/argparse_completer.py
+++ b/cmd2/argparse_completer.py
@@ -59,6 +59,7 @@ Released under MIT license, see LICENSE file
import argparse
from colorama import Fore
+import os
import sys
from typing import List, Dict, Tuple, Callable, Union
@@ -76,10 +77,20 @@ from .rl_utils import rl_force_redisplay
# define the completion choices for the argument. You may provide a Collection or a Function.
ACTION_ARG_CHOICES = 'arg_choices'
ACTION_SUPPRESS_HINT = 'suppress_hint'
+ACTION_DESCRIPTIVE_COMPLETION_HEADER = 'desc_header'
+
+
+class CompletionItem(str):
+ def __new__(cls, o, desc='', *args, **kwargs) -> str:
+ return str.__new__(cls, o, *args, **kwargs)
+
+ # noinspection PyMissingConstructor,PyUnusedLocal
+ def __init__(self, o, desc='', *args, **kwargs) -> None:
+ self.description = desc
class _RangeAction(object):
- def __init__(self, nargs: Union[int, str, Tuple[int, int], None]):
+ def __init__(self, nargs: Union[int, str, Tuple[int, int], None]) -> None:
self.nargs_min = None
self.nargs_max = None
@@ -118,7 +129,7 @@ class _StoreRangeAction(argparse._StoreAction, _RangeAction):
choices=None,
required=False,
help=None,
- metavar=None):
+ metavar=None) -> None:
_RangeAction.__init__(self, nargs)
@@ -147,7 +158,7 @@ class _AppendRangeAction(argparse._AppendAction, _RangeAction):
choices=None,
required=False,
help=None,
- metavar=None):
+ metavar=None) -> None:
_RangeAction.__init__(self, nargs)
@@ -164,7 +175,7 @@ class _AppendRangeAction(argparse._AppendAction, _RangeAction):
metavar=metavar)
-def register_custom_actions(parser: argparse.ArgumentParser):
+def register_custom_actions(parser: argparse.ArgumentParser) -> None:
"""Register custom argument action types"""
parser.register('action', None, _StoreRangeAction)
parser.register('action', 'store', _StoreRangeAction)
@@ -175,14 +186,14 @@ class AutoCompleter(object):
"""Automatically command line tab completion based on argparse parameters"""
class _ArgumentState(object):
- def __init__(self):
+ def __init__(self) -> None:
self.min = None
self.max = None
self.count = 0
self.needed = False
self.variable = False
- def reset(self):
+ def reset(self) -> None:
"""reset tracking values"""
self.min = None
self.max = None
@@ -196,7 +207,7 @@ class AutoCompleter(object):
arg_choices: Dict[str, Union[List, Tuple, Callable]] = None,
subcmd_args_lookup: dict = None,
tab_for_arg_help: bool = True,
- cmd2_app=None):
+ cmd2_app=None) -> None:
"""
Create an AutoCompleter
@@ -413,6 +424,8 @@ class AutoCompleter(object):
completion_results = self._complete_for_arg(flag_action, text, line, begidx, endidx, consumed)
if not completion_results:
self._print_action_help(flag_action)
+ elif len(completion_results) > 1:
+ completion_results = self._format_completions(flag_action, completion_results)
# ok, we're not a flag, see if there's a positional argument to complete
else:
@@ -422,9 +435,39 @@ class AutoCompleter(object):
completion_results = self._complete_for_arg(pos_action, text, line, begidx, endidx, consumed)
if not completion_results:
self._print_action_help(pos_action)
+ elif len(completion_results) > 1:
+ completion_results = self._format_completions(pos_action, completion_results)
return completion_results
+ def _format_completions(self, action, completions: List[Union[str, CompletionItem]]) -> List[str]:
+ if completions and len(completions) > 1 and isinstance(completions[0], CompletionItem):
+ token_width = len(action.dest)
+ completions_with_desc = []
+
+ for item in completions:
+ if len(item) > token_width:
+ token_width = len(item)
+
+ term_size = os.get_terminal_size()
+ fill_width = int(term_size.columns * .6) - (token_width + 2)
+ for item in completions:
+ entry = '{: <{token_width}}{: <{fill_width}}'.format(item, item.description,
+ token_width=token_width+2,
+ fill_width=fill_width)
+ completions_with_desc.append(entry)
+
+ try:
+ desc_header = action.desc_header
+ except AttributeError:
+ desc_header = 'Description'
+ header = '\n{: <{token_width}}{}'.format(action.dest.upper(), desc_header, token_width=token_width+2)
+
+ self._cmd2_app.completion_header = header
+ self._cmd2_app.display_matches = completions_with_desc
+
+ return completions
+
def complete_command_help(self, tokens: List[str], text: str, line: str, begidx: int, endidx: int) -> List[str]:
"""Supports the completion of sub-commands for commands through the cmd2 help command."""
for idx, token in enumerate(tokens):
@@ -627,7 +670,7 @@ class AutoCompleter(object):
class ACHelpFormatter(argparse.HelpFormatter):
"""Custom help formatter to configure ordering of help text"""
- def _format_usage(self, usage, actions, groups, prefix):
+ def _format_usage(self, usage, actions, groups, prefix) -> str:
if prefix is None:
prefix = _('Usage: ')
@@ -740,7 +783,7 @@ class ACHelpFormatter(argparse.HelpFormatter):
# prefix with 'usage:'
return '%s%s\n\n' % (prefix, usage)
- def _format_action_invocation(self, action):
+ def _format_action_invocation(self, action) -> str:
if not action.option_strings:
default = self._get_default_metavar_for_positional(action)
metavar, = self._metavar_formatter(action, default)(1)
@@ -765,7 +808,7 @@ class ACHelpFormatter(argparse.HelpFormatter):
return ', '.join(action.option_strings) + ' ' + args_string
# End cmd2 customization
- def _metavar_formatter(self, action, default_metavar):
+ def _metavar_formatter(self, action, default_metavar) -> Callable:
if action.metavar is not None:
result = action.metavar
elif action.choices is not None:
@@ -784,7 +827,7 @@ class ACHelpFormatter(argparse.HelpFormatter):
return (result, ) * tuple_size
return format
- def _format_args(self, action, default_metavar):
+ def _format_args(self, action, default_metavar) -> str:
get_metavar = self._metavar_formatter(action, default_metavar)
# Begin cmd2 customization (less verbose)
if isinstance(action, _RangeAction) and \
@@ -799,7 +842,7 @@ class ACHelpFormatter(argparse.HelpFormatter):
result = super()._format_args(action, default_metavar)
return result
- def _split_lines(self, text, width):
+ def _split_lines(self, text: str, width) -> List[str]:
return text.splitlines()
@@ -807,7 +850,7 @@ class ACHelpFormatter(argparse.HelpFormatter):
class ACArgumentParser(argparse.ArgumentParser):
"""Custom argparse class to override error method to change default help text."""
- def __init__(self, *args, **kwargs):
+ def __init__(self, *args, **kwargs) -> None:
if 'formatter_class' not in kwargs:
kwargs['formatter_class'] = ACHelpFormatter
@@ -817,7 +860,7 @@ class ACArgumentParser(argparse.ArgumentParser):
self._custom_error_message = ''
# Begin cmd2 customization
- def set_custom_message(self, custom_message=''):
+ def set_custom_message(self, custom_message: str='') -> None:
"""
Allows an error message override to the error() function, useful when forcing a
re-parse of arguments with newly required parameters
@@ -825,7 +868,7 @@ class ACArgumentParser(argparse.ArgumentParser):
self._custom_error_message = custom_message
# End cmd2 customization
- def error(self, message):
+ def error(self, message: str) -> None:
"""Custom error override. Allows application to control the error being displayed by argparse"""
if len(self._custom_error_message) > 0:
message = self._custom_error_message
@@ -846,7 +889,7 @@ class ACArgumentParser(argparse.ArgumentParser):
self.print_help()
sys.exit(1)
- def format_help(self):
+ def format_help(self) -> str:
"""Copy of format_help() from argparse.ArgumentParser with tweaks to separately display required parameters"""
formatter = self._get_formatter()
@@ -896,7 +939,7 @@ class ACArgumentParser(argparse.ArgumentParser):
# determine help from format above
return formatter.format_help()
- def _get_nargs_pattern(self, action):
+ def _get_nargs_pattern(self, action) -> str:
# Override _get_nargs_pattern behavior to use the nargs ranges provided by AutoCompleter
if isinstance(action, _RangeAction) and \
action.nargs_min is not None and action.nargs_max is not None:
@@ -909,7 +952,7 @@ class ACArgumentParser(argparse.ArgumentParser):
return nargs_pattern
return super(ACArgumentParser, self)._get_nargs_pattern(action)
- def _match_argument(self, action, arg_strings_pattern):
+ def _match_argument(self, action, arg_strings_pattern) -> int:
# match the pattern for this action to the arg strings
nargs_pattern = self._get_nargs_pattern(action)
match = _re.match(nargs_pattern, arg_strings_pattern)
@@ -925,7 +968,7 @@ class ACArgumentParser(argparse.ArgumentParser):
# This is the official python implementation with a 5 year old patch applied
# See the comment below describing the patch
- def _parse_known_args(self, arg_strings, namespace): # pragma: no cover
+ def _parse_known_args(self, arg_strings, namespace) -> Tuple[argparse.Namespace, List[str]]: # pragma: no cover
# replace arg strings that are file references
if self.fromfile_prefix_chars is not None:
arg_strings = self._read_args_from_files(arg_strings)