@@ -61,11 +61,32 @@ def _text_factory():
6161class HelpEntry :
6262 """Container for help table entry data."""
6363
64- names : tuple [str , ...] = ()
65- """Long option names (e.g., "--verbose", "--help ")."""
64+ positive_names : tuple [str , ...] = ()
65+ """Positive long option names (e.g., "--verbose", "--dry-run ")."""
6666
67- shorts : tuple [str , ...] = ()
68- """Short option names (e.g., "-v", "-h")."""
67+ positive_shorts : tuple [str , ...] = ()
68+ """Positive short option names (e.g., "-v", "-n")."""
69+
70+ negative_names : tuple [str , ...] = ()
71+ """Negative long option names (e.g., "--no-verbose", "--no-dry-run")."""
72+
73+ negative_shorts : tuple [str , ...] = ()
74+ """Negative short option names (e.g., "-N"). Rarely used."""
75+
76+ @property
77+ def names (self ) -> tuple [str , ...]:
78+ """All long option names (positive + negative). For backward compatibility."""
79+ return self .positive_names + self .negative_names
80+
81+ @property
82+ def shorts (self ) -> tuple [str , ...]:
83+ """All short option names (positive + negative). For backward compatibility."""
84+ return self .positive_shorts + self .negative_shorts
85+
86+ @property
87+ def all_options (self ) -> tuple [str , ...]:
88+ """All options in display order: positive longs, positive shorts, negative longs, negative shorts."""
89+ return self .positive_names + self .positive_shorts + self .negative_names + self .negative_shorts
6990
7091 description : Any = None
7192 """Help text description for this entry.
@@ -373,12 +394,12 @@ def help_append(text, style):
373394 if arg_name != options [0 ]:
374395 options = [arg_name , * options ]
375396
376- short_options , long_options = [], []
377- for option in options :
378- if _is_short (option ):
379- short_options . append ( option )
380- else :
381- long_options . append ( option )
397+ # Split options into positive/negative and long/short categories.
398+ negatives = set ( argument . negatives )
399+ positive_names = [ o for o in options if o not in negatives and not _is_short (o )]
400+ positive_shorts = [ o for o in options if o not in negatives and _is_short ( o )]
401+ negative_names = [ o for o in options if o in negatives and not _is_short ( o )]
402+ negative_shorts = [ o for o in options if o in negatives and _is_short ( o )]
382403
383404 help_description = InlineText .from_format (argument .parameter .help , format = format )
384405
@@ -428,9 +449,11 @@ def help_append(text, style):
428449
429450 # populate row
430451 entry = HelpEntry (
431- names = tuple (long_options ),
452+ positive_names = tuple (positive_names ),
453+ positive_shorts = tuple (positive_shorts ),
454+ negative_names = tuple (negative_names ),
455+ negative_shorts = tuple (negative_shorts ),
432456 description = help_description ,
433- shorts = tuple (short_options ),
434457 required = argument .required ,
435458 type = resolve_annotated (argument .field_info .annotation ),
436459 choices = choices ,
@@ -470,13 +493,14 @@ def format_command_entries(apps_with_names: Iterable, format: str) -> list[HelpE
470493 app = registered_command .app
471494 if not app .show :
472495 continue
496+ # Commands don't have negative variants, so all names are "positive"
473497 short_names , long_names = [], []
474498 for name in names :
475499 short_names .append (name ) if _is_short (name ) else long_names .append (name )
476500
477501 entry = HelpEntry (
478- names = tuple (long_names ),
479- shorts = tuple (short_names ),
502+ positive_names = tuple (long_names ),
503+ positive_shorts = tuple (short_names ),
480504 description = InlineText .from_format (docstring_parse (app .help , format ).short_description , format = format ),
481505 sort_key = resolve_callables (app .sort_key , app ),
482506 )
0 commit comments