@@ -122,38 +122,25 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens)
122
122
numbers isn't very helpful to a user without context. Returning a list of
123
123
CompletionItems instead of a regular string for completion results will signal
124
124
the 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::
126
126
127
127
Instead of this:
128
128
1 2 3
129
129
130
130
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
136
136
137
137
138
138
The left-most column is the actual value being tab completed and its header is
139
139
that 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.
157
144
158
145
To use CompletionItems, just return them from your choices_provider or
159
146
completer 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)
162
149
argparse so that when evaluating choices, input is compared to
163
150
CompletionItem.orig_value instead of the CompletionItem instance.
164
151
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
166
200
presses 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
169
203
exceeds 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.
171
205
172
206
173
207
**Patched argparse functions**
@@ -200,8 +234,8 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens)
200
234
- ``argparse.Action.get_choices_callable()`` - See `action_get_choices_callable` for more details.
201
235
- ``argparse.Action.set_choices_provider()`` - See `_action_set_choices_provider` for more details.
202
236
- ``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.
205
239
- ``argparse.Action.get_nargs_range()`` - See `_action_get_nargs_range` for more details.
206
240
- ``argparse.Action.set_nargs_range()`` - See `_action_set_nargs_range` for more details.
207
241
- ``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)
249
283
Group ,
250
284
RenderableType ,
251
285
)
286
+ from rich .protocol import is_renderable
252
287
from rich .table import Column , Table
253
288
from rich .text import Text
254
289
from rich_argparse import (
@@ -263,6 +298,7 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens)
263
298
constants ,
264
299
rich_utils ,
265
300
)
301
+ from .rich_utils import Cmd2Style
266
302
267
303
if TYPE_CHECKING : # pragma: no cover
268
304
from .argparse_completer import (
@@ -349,15 +385,17 @@ def __new__(cls, value: object, *_args: Any, **_kwargs: Any) -> 'CompletionItem'
349
385
"""Responsible for creating and returning a new instance, called before __init__ when an object is instantiated."""
350
386
return super ().__new__ (cls , value )
351
387
352
- def __init__ (self , value : object , description : str = '' , * args : Any ) -> None :
388
+ def __init__ (self , value : object , descriptive_data : Sequence [ Any ] , * args : Any ) -> None :
353
389
"""CompletionItem Initializer.
354
390
355
391
:param value: the value being tab completed
356
- :param description: description text to display
392
+ :param descriptive_data: descriptive data to display
357
393
:param args: args for str __init__
358
394
"""
359
395
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 ]
361
399
362
400
# Save the original value to support CompletionItems as argparse choices.
363
401
# 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:
483
521
ATTR_CHOICES_CALLABLE = 'choices_callable'
484
522
485
523
# Descriptive header that prints when using CompletionItems
486
- ATTR_DESCRIPTIVE_HEADER = 'descriptive_header '
524
+ ATTR_DESCRIPTIVE_HEADERS = 'descriptive_headers '
487
525
488
526
# A tuple specifying nargs as a range (min, max)
489
527
ATTR_NARGS_RANGE = 'nargs_range'
@@ -580,38 +618,38 @@ def _action_set_completer(
580
618
581
619
582
620
############################################################################################################
583
- # Patch argparse.Action with accessors for descriptive_header attribute
621
+ # Patch argparse.Action with accessors for descriptive_headers attribute
584
622
############################################################################################################
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.
587
625
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.
589
627
590
- To call: ``action.get_descriptive_header ()``
628
+ To call: ``action.get_descriptive_headers ()``
591
629
592
630
: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
594
632
"""
595
- return cast (str | None , getattr (self , ATTR_DESCRIPTIVE_HEADER , None ))
633
+ return cast (Sequence [ str | Column ] | None , getattr (self , ATTR_DESCRIPTIVE_HEADERS , None ))
596
634
597
635
598
- setattr (argparse .Action , 'get_descriptive_header ' , _action_get_descriptive_header )
636
+ setattr (argparse .Action , 'get_descriptive_headers ' , _action_get_descriptive_headers )
599
637
600
638
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.
603
641
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.
605
643
606
- To call: ``action.set_descriptive_header(descriptive_header )``
644
+ To call: ``action.set_descriptive_headers(descriptive_headers )``
607
645
608
646
:param self: argparse Action being updated
609
- :param descriptive_header : value being assigned
647
+ :param descriptive_headers : value being assigned
610
648
"""
611
- setattr (self , ATTR_DESCRIPTIVE_HEADER , descriptive_header )
649
+ setattr (self , ATTR_DESCRIPTIVE_HEADERS , descriptive_headers )
612
650
613
651
614
- setattr (argparse .Action , 'set_descriptive_header ' , _action_set_descriptive_header )
652
+ setattr (argparse .Action , 'set_descriptive_headers ' , _action_set_descriptive_headers )
615
653
616
654
617
655
############################################################################################################
@@ -762,7 +800,7 @@ def _add_argument_wrapper(
762
800
choices_provider : ChoicesProviderFunc | None = None ,
763
801
completer : CompleterFunc | None = None ,
764
802
suppress_tab_hint : bool = False ,
765
- descriptive_header : str | None = None ,
803
+ descriptive_headers : list [ Column | str ] | None = None ,
766
804
** kwargs : Any ,
767
805
) -> argparse .Action :
768
806
"""Wrap ActionsContainer.add_argument() which supports more settings used by cmd2.
@@ -782,8 +820,8 @@ def _add_argument_wrapper(
782
820
current argument's help text as a hint. Set this to True to suppress the hint. If this
783
821
argument's help text is set to argparse.SUPPRESS, then tab hints will not display
784
822
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.
787
825
788
826
# Args from original function
789
827
:param kwargs: keyword-arguments recognized by argparse._ActionsContainer.add_argument
@@ -874,7 +912,7 @@ def _add_argument_wrapper(
874
912
new_arg .set_completer (completer ) # type: ignore[attr-defined]
875
913
876
914
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]
878
916
879
917
for keyword , value in custom_attribs .items ():
880
918
attr_setter = getattr (new_arg , f'set_{ keyword } ' , None )
@@ -1445,7 +1483,7 @@ def error(self, message: str) -> NoReturn:
1445
1483
# Add error style to message
1446
1484
console = self ._get_formatter ().console
1447
1485
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 )
1449
1487
formatted_message = f"{ capture .get ()} "
1450
1488
1451
1489
self .exit (2 , f'{ formatted_message } \n ' )
0 commit comments