@@ -122,38 +122,25 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens)
122122numbers isn't very helpful to a user without context. Returning a list of
123123CompletionItems instead of a regular string for completion results will signal
124124the ArgparseCompleter to output the completion results in a table of completion
125- tokens with descriptions instead of just a table of tokens::
125+ tokens with descriptive data instead of just a table of tokens::
126126
127127 Instead of this:
128128 1 2 3
129129
130130 The user sees this:
131- ITEM_ID Item Name
132- ============================
133- 1 My item
134- 2 Another item
135- 3 Yet another item
131+ ITEM_ID Description
132+ ────────────────────────────
133+ 1 My item
134+ 2 Another item
135+ 3 Yet another item
136136
137137
138138The left-most column is the actual value being tab completed and its header is
139139that value's name. The right column header is defined using the
140- descriptive_header parameter of add_argument(). The right column values come
141- from the CompletionItem.description value.
142-
143- Example::
144-
145- token = 1
146- token_description = "My Item"
147- completion_item = CompletionItem(token, token_description)
148-
149- Since descriptive_header and CompletionItem.description are just strings, you
150- can format them in such a way to have multiple columns::
151-
152- ITEM_ID Item Name Checked Out Due Date
153- ==========================================================
154- 1 My item True 02/02/2022
155- 2 Another item False
156- 3 Yet another item False
140+ ``descriptive_headers`` parameter of add_argument(), which is a list of header
141+ names that defaults to ["Description"]. The right column values come from the
142+ ``CompletionItem.descriptive_data`` member, which is a list with the same number
143+ of items as columns defined in descriptive_headers.
157144
158145To use CompletionItems, just return them from your choices_provider or
159146completer functions. They can also be used as argparse choices. When a
@@ -162,12 +149,59 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens)
162149argparse so that when evaluating choices, input is compared to
163150CompletionItem.orig_value instead of the CompletionItem instance.
164151
165- To avoid printing a ton of information to the screen at once when a user
152+ Example::
153+
154+ Add an argument and define its descriptive_headers.
155+
156+ parser.add_argument(
157+ add_argument(
158+ "item_id",
159+ type=int,
160+ choices_provider=get_items,
161+ descriptive_headers=["Item Name", "Checked Out", "Due Date"],
162+ )
163+
164+ Implement the choices_provider to return CompletionItems.
165+
166+ def get_items(self) -> list[CompletionItems]:
167+ \" \" \" choices_provider which returns CompletionItems\" \" \"
168+
169+ # CompletionItem's second argument is descriptive_data.
170+ # Its item count should match that of descriptive_headers.
171+ return [
172+ CompletionItem(1, ["My item", True, "02/02/2022"]),
173+ CompletionItem(2, ["Another item", False, ""]),
174+ CompletionItem(3, ["Yet another item", False, ""]),
175+ ]
176+
177+ This is what the user will see during tab completion.
178+
179+ ITEM_ID Item Name Checked Out Due Date
180+ ───────────────────────────────────────────────────────
181+ 1 My item True 02/02/2022
182+ 2 Another item False
183+ 3 Yet another item False
184+
185+ ``descriptive_headers`` can be strings or ``Rich.table.Columns`` for more
186+ control over things like alignment.
187+
188+ - If a header is a string, it will render as a left-aligned column with its
189+ overflow behavior set to "fold". This means a long string will wrap within its
190+ cell, creating as many new lines as required to fit.
191+
192+ - If a header is a ``Column``, it defaults to "ellipsis" overflow behavior.
193+ This means a long string which exceeds the width of its column will be
194+ truncated with an ellipsis at the end. You can override this and other settings
195+ when you create the ``Column``.
196+
197+ ``descriptive_data`` items can include Rich objects, including styled text.
198+
199+ To avoid printing a excessive information to the screen at once when a user
166200presses tab, there is a maximum threshold for the number of CompletionItems
167- that will be shown. Its value is defined in cmd2.Cmd.max_completion_items. It
168- defaults to 50, but can be changed. If the number of completion suggestions
201+ that will be shown. Its value is defined in `` cmd2.Cmd.max_completion_items``.
202+ It defaults to 50, but can be changed. If the number of completion suggestions
169203exceeds this number, they will be displayed in the typical columnized format
170- and will not include the description value of the CompletionItems.
204+ and will not include the descriptive_data of the CompletionItems.
171205
172206
173207**Patched argparse functions**
@@ -200,8 +234,8 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens)
200234- ``argparse.Action.get_choices_callable()`` - See `action_get_choices_callable` for more details.
201235- ``argparse.Action.set_choices_provider()`` - See `_action_set_choices_provider` for more details.
202236- ``argparse.Action.set_completer()`` - See `_action_set_completer` for more details.
203- - ``argparse.Action.get_descriptive_header ()`` - See `_action_get_descriptive_header ` for more details.
204- - ``argparse.Action.set_descriptive_header ()`` - See `_action_set_descriptive_header ` for more details.
237+ - ``argparse.Action.get_descriptive_headers ()`` - See `_action_get_descriptive_headers ` for more details.
238+ - ``argparse.Action.set_descriptive_headers ()`` - See `_action_set_descriptive_headers ` for more details.
205239- ``argparse.Action.get_nargs_range()`` - See `_action_get_nargs_range` for more details.
206240- ``argparse.Action.set_nargs_range()`` - See `_action_set_nargs_range` for more details.
207241- ``argparse.Action.get_suppress_tab_hint()`` - See `_action_get_suppress_tab_hint` for more details.
@@ -249,6 +283,7 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens)
249283 Group ,
250284 RenderableType ,
251285)
286+ from rich .protocol import is_renderable
252287from rich .table import Column , Table
253288from rich .text import Text
254289from rich_argparse import (
@@ -263,6 +298,7 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens)
263298 constants ,
264299 rich_utils ,
265300)
301+ from .rich_utils import Cmd2Style
266302
267303if TYPE_CHECKING : # pragma: no cover
268304 from .argparse_completer import (
@@ -349,15 +385,17 @@ def __new__(cls, value: object, *_args: Any, **_kwargs: Any) -> 'CompletionItem'
349385 """Responsible for creating and returning a new instance, called before __init__ when an object is instantiated."""
350386 return super ().__new__ (cls , value )
351387
352- def __init__ (self , value : object , description : str = '' , * args : Any ) -> None :
388+ def __init__ (self , value : object , descriptive_data : Sequence [ Any ] , * args : Any ) -> None :
353389 """CompletionItem Initializer.
354390
355391 :param value: the value being tab completed
356- :param description: description text to display
392+ :param descriptive_data: descriptive data to display
357393 :param args: args for str __init__
358394 """
359395 super ().__init__ (* args )
360- self .description = description
396+
397+ # Make sure all objects are renderable by a Rich table.
398+ self .descriptive_data = [obj if is_renderable (obj ) else str (obj ) for obj in descriptive_data ]
361399
362400 # Save the original value to support CompletionItems as argparse choices.
363401 # cmd2 has patched argparse so input is compared to this value instead of the CompletionItem instance.
@@ -483,7 +521,7 @@ def choices_provider(self) -> ChoicesProviderFunc:
483521ATTR_CHOICES_CALLABLE = 'choices_callable'
484522
485523# Descriptive header that prints when using CompletionItems
486- ATTR_DESCRIPTIVE_HEADER = 'descriptive_header '
524+ ATTR_DESCRIPTIVE_HEADERS = 'descriptive_headers '
487525
488526# A tuple specifying nargs as a range (min, max)
489527ATTR_NARGS_RANGE = 'nargs_range'
@@ -580,38 +618,38 @@ def _action_set_completer(
580618
581619
582620############################################################################################################
583- # Patch argparse.Action with accessors for descriptive_header attribute
621+ # Patch argparse.Action with accessors for descriptive_headers attribute
584622############################################################################################################
585- def _action_get_descriptive_header (self : argparse .Action ) -> str | None :
586- """Get the descriptive_header attribute of an argparse Action.
623+ def _action_get_descriptive_headers (self : argparse .Action ) -> Sequence [ str | Column ] | None :
624+ """Get the descriptive_headers attribute of an argparse Action.
587625
588- This function is added by cmd2 as a method called ``get_descriptive_header ()`` to ``argparse.Action`` class.
626+ This function is added by cmd2 as a method called ``get_descriptive_headers ()`` to ``argparse.Action`` class.
589627
590- To call: ``action.get_descriptive_header ()``
628+ To call: ``action.get_descriptive_headers ()``
591629
592630 :param self: argparse Action being queried
593- :return: The value of descriptive_header or None if attribute does not exist
631+ :return: The value of descriptive_headers or None if attribute does not exist
594632 """
595- return cast (str | None , getattr (self , ATTR_DESCRIPTIVE_HEADER , None ))
633+ return cast (Sequence [ str | Column ] | None , getattr (self , ATTR_DESCRIPTIVE_HEADERS , None ))
596634
597635
598- setattr (argparse .Action , 'get_descriptive_header ' , _action_get_descriptive_header )
636+ setattr (argparse .Action , 'get_descriptive_headers ' , _action_get_descriptive_headers )
599637
600638
601- def _action_set_descriptive_header (self : argparse .Action , descriptive_header : str | None ) -> None :
602- """Set the descriptive_header attribute of an argparse Action.
639+ def _action_set_descriptive_headers (self : argparse .Action , descriptive_headers : Sequence [ str | Column ] | None ) -> None :
640+ """Set the descriptive_headers attribute of an argparse Action.
603641
604- This function is added by cmd2 as a method called ``set_descriptive_header ()`` to ``argparse.Action`` class.
642+ This function is added by cmd2 as a method called ``set_descriptive_headers ()`` to ``argparse.Action`` class.
605643
606- To call: ``action.set_descriptive_header(descriptive_header )``
644+ To call: ``action.set_descriptive_headers(descriptive_headers )``
607645
608646 :param self: argparse Action being updated
609- :param descriptive_header : value being assigned
647+ :param descriptive_headers : value being assigned
610648 """
611- setattr (self , ATTR_DESCRIPTIVE_HEADER , descriptive_header )
649+ setattr (self , ATTR_DESCRIPTIVE_HEADERS , descriptive_headers )
612650
613651
614- setattr (argparse .Action , 'set_descriptive_header ' , _action_set_descriptive_header )
652+ setattr (argparse .Action , 'set_descriptive_headers ' , _action_set_descriptive_headers )
615653
616654
617655############################################################################################################
@@ -762,7 +800,7 @@ def _add_argument_wrapper(
762800 choices_provider : ChoicesProviderFunc | None = None ,
763801 completer : CompleterFunc | None = None ,
764802 suppress_tab_hint : bool = False ,
765- descriptive_header : str | None = None ,
803+ descriptive_headers : list [ Column | str ] | None = None ,
766804 ** kwargs : Any ,
767805) -> argparse .Action :
768806 """Wrap ActionsContainer.add_argument() which supports more settings used by cmd2.
@@ -782,8 +820,8 @@ def _add_argument_wrapper(
782820 current argument's help text as a hint. Set this to True to suppress the hint. If this
783821 argument's help text is set to argparse.SUPPRESS, then tab hints will not display
784822 regardless of the value passed for suppress_tab_hint. Defaults to False.
785- :param descriptive_header : if the provided choices are CompletionItems, then this header will display
786- during tab completion . Defaults to None.
823+ :param descriptive_headers : if the provided choices are CompletionItems, then these are the headers
824+ of the descriptive data . Defaults to None.
787825
788826 # Args from original function
789827 :param kwargs: keyword-arguments recognized by argparse._ActionsContainer.add_argument
@@ -874,7 +912,7 @@ def _add_argument_wrapper(
874912 new_arg .set_completer (completer ) # type: ignore[attr-defined]
875913
876914 new_arg .set_suppress_tab_hint (suppress_tab_hint ) # type: ignore[attr-defined]
877- new_arg .set_descriptive_header ( descriptive_header ) # type: ignore[attr-defined]
915+ new_arg .set_descriptive_headers ( descriptive_headers ) # type: ignore[attr-defined]
878916
879917 for keyword , value in custom_attribs .items ():
880918 attr_setter = getattr (new_arg , f'set_{ keyword } ' , None )
@@ -1445,7 +1483,7 @@ def error(self, message: str) -> NoReturn:
14451483 # Add error style to message
14461484 console = self ._get_formatter ().console
14471485 with console .capture () as capture :
1448- console .print (formatted_message , style = "cmd2.error" , crop = False )
1486+ console .print (formatted_message , style = Cmd2Style . ERROR , crop = False )
14491487 formatted_message = f"{ capture .get ()} "
14501488
14511489 self .exit (2 , f'{ formatted_message } \n ' )
0 commit comments