Skip to content

Commit 13bb065

Browse files
committed
Adds possible support for tabular tab completion results.
1 parent 9852270 commit 13bb065

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

cmd2/argparse_completer.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ def my_completer(text: str, line: str, begidx: int, endidx:int, extra_param: str
5959

6060
import argparse
6161
from colorama import Fore
62+
import os
6263
import sys
6364
from typing import List, Dict, Tuple, Callable, Union
6465

@@ -78,6 +79,15 @@ def my_completer(text: str, line: str, begidx: int, endidx:int, extra_param: str
7879
ACTION_SUPPRESS_HINT = 'suppress_hint'
7980

8081

82+
class CompletionItem(str):
83+
def __new__(cls, o, desc='', *args, **kwargs):
84+
return str.__new__(cls, o, *args, **kwargs)
85+
86+
# noinspection PyMissingConstructor,PyUnusedLocal
87+
def __init__(self, o, desc='', *args, **kwargs):
88+
self.description = desc
89+
90+
8191
class _RangeAction(object):
8292
def __init__(self, nargs: Union[int, str, Tuple[int, int], None]):
8393
self.nargs_min = None
@@ -413,6 +423,8 @@ def consume_positional_argument() -> None:
413423
completion_results = self._complete_for_arg(flag_action, text, line, begidx, endidx, consumed)
414424
if not completion_results:
415425
self._print_action_help(flag_action)
426+
elif len(completion_results) > 1:
427+
completion_results = self._format_completions(flag_action, completion_results)
416428

417429
# ok, we're not a flag, see if there's a positional argument to complete
418430
else:
@@ -422,9 +434,37 @@ def consume_positional_argument() -> None:
422434
completion_results = self._complete_for_arg(pos_action, text, line, begidx, endidx, consumed)
423435
if not completion_results:
424436
self._print_action_help(pos_action)
437+
elif len(completion_results) > 1:
438+
completion_results = self._format_completions(pos_action, completion_results)
425439

426440
return completion_results
427441

442+
def _format_completions(self, action, completions: List[Union[str, CompletionItem]]):
443+
if completions and len(completions) > 1 and \
444+
isinstance(completions[0], CompletionItem):
445+
446+
token_width = len(action.dest)
447+
completions_with_desc = []
448+
449+
for item in completions:
450+
if len(item) > token_width:
451+
token_width = len(item)
452+
453+
term_size = os.get_terminal_size()
454+
fill_width = int(term_size.columns * .6) - (token_width + 2)
455+
for item in completions:
456+
entry = '{: <{token_width}}{: <{fill_width}}'.format(item, item.description,
457+
token_width=token_width+2,
458+
fill_width=fill_width)
459+
completions_with_desc.append(entry)
460+
461+
header = '\n{: <{token_width}}{}'.format(action.dest, action.desc_header, token_width=token_width+2)
462+
print(header)
463+
464+
self._cmd2_app.display_matches = completions_with_desc
465+
466+
return completions
467+
428468
def complete_command_help(self, tokens: List[str], text: str, line: str, begidx: int, endidx: int) -> List[str]:
429469
"""Supports the completion of sub-commands for commands through the cmd2 help command."""
430470
for idx, token in enumerate(tokens):

examples/tab_autocompletion.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,18 @@ def instance_query_actors(self) -> List[str]:
109109
"""Simulating a function that queries and returns a completion values"""
110110
return actors
111111

112+
def instance_query_movie_ids(self) -> List[str]:
113+
"""Demonstrates showing tabular hinting of tab completion information"""
114+
completions_with_desc = []
115+
116+
for movie_id, movie_entry in self.MOVIE_DATABASE.items():
117+
completions_with_desc.append(argparse_completer.CompletionItem(movie_id, movie_entry['title']))
118+
119+
setattr(self.vid_delete_movie_id, 'desc_header', 'Title')
120+
setattr(self.movies_delete_movie_id, 'desc_header', 'Title')
121+
122+
return completions_with_desc
123+
112124
# This demonstrates a number of customizations of the AutoCompleter version of ArgumentParser
113125
# - The help output will separately group required vs optional flags
114126
# - The help output for arguments with multiple flags or with append=True is more concise
@@ -253,6 +265,8 @@ def _do_vid_media_shows(self, args) -> None:
253265
('path_complete', [False, False]))
254266

255267
vid_movies_delete_parser = vid_movies_commands_subparsers.add_parser('delete')
268+
vid_delete_movie_id = vid_movies_delete_parser.add_argument('movie_id', help='Movie ID')
269+
setattr(vid_delete_movie_id, argparse_completer.ACTION_ARG_CHOICES, instance_query_movie_ids)
256270

257271
vid_shows_parser = video_types_subparsers.add_parser('shows')
258272
vid_shows_parser.set_defaults(func=_do_vid_media_shows)
@@ -328,6 +342,8 @@ def _do_media_shows(self, args) -> None:
328342
movies_add_parser.add_argument('actor', help='Actors', nargs='*')
329343

330344
movies_delete_parser = movies_commands_subparsers.add_parser('delete')
345+
movies_delete_movie_id = movies_delete_parser.add_argument('movie_id', help='Movie ID')
346+
setattr(movies_delete_movie_id, argparse_completer.ACTION_ARG_CHOICES, 'instance_query_movie_ids')
331347

332348
movies_load_parser = movies_commands_subparsers.add_parser('load')
333349
movie_file_action = movies_load_parser.add_argument('movie_file', help='Movie database')
@@ -362,7 +378,7 @@ def complete_media(self, text, line, begidx, endidx):
362378
'director': TabCompleteExample.static_list_directors, # static list
363379
'movie_file': (self.path_complete, [False, False])
364380
}
365-
completer = argparse_completer.AutoCompleter(TabCompleteExample.media_parser, arg_choices=choices)
381+
completer = argparse_completer.AutoCompleter(TabCompleteExample.media_parser, arg_choices=choices, cmd2_app=self)
366382

367383
tokens, _ = self.tokens_for_completion(line, begidx, endidx)
368384
results = completer.complete_command(tokens, text, line, begidx, endidx)

0 commit comments

Comments
 (0)