Skip to content

Commit eb91815

Browse files
authored
docs(cli) Improve arguments and options (#1011)
## Summary - Replace inline pipe-separated metadata (`Default: None | Type: str | Required`) with semantic `<dl>` definition list structure - Enable independent CSS styling of keys, values, and tags - Use Furo's guilabel pattern for "Required" tag with semi-transparent amber background - Support both light and dark mode with proper contrast - Standardize all argparse elements to use `var(--code-font-size)` for consistent sizing ## Changes **HTML structure (before):** ```html <p class="argparse-argument-meta">Default: <span class="nv">None</span> | Type: str | Required</p> ``` **HTML structure (after):** ```html <dl class="argparse-argument-meta"> <div class="argparse-meta-item"> <dt class="argparse-meta-key">Default</dt> <dd class="argparse-meta-value"><span class="nv">None</span></dd> </div> <div class="argparse-meta-item"> <dt class="argparse-meta-key">Type</dt> <dd class="argparse-meta-value"><span class="nv">str</span></dd> </div> <dt class="argparse-meta-tag">Required</dt> </dl> ``` **Font size standardization:** - CLI inline roles (`.cli-option`, `.cli-metavar`, etc.) - Usage blocks (`pre.argparse-usage`) - Argument names (`.argparse-argument-name`) - Metadata elements (`.argparse-meta-key`, `.argparse-meta-value`, `.argparse-meta-tag`) All now use `var(--code-font-size)` (Furo's 81.25%) for consistency with other code elements.
2 parents 3487e8b + eacd727 commit eb91815

6 files changed

Lines changed: 248 additions & 42 deletions

File tree

CHANGES

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,17 @@ _Notes on the upcoming release will go here._
4141

4242
CLI documentation now supports direct linking to specific arguments:
4343

44-
- **Linkable options**: Each `--option` and positional argument has a permanent URL anchor (e.g., `cli/load.html#-d`)
44+
- **Linkable options**: Each `--option` and positional argument has a permanent URL anchor (e.g., `cli/load.html#load-d`)
4545
- **Headerlinks**: Hover over any argument to reveal a ¶ link for easy sharing
4646
- **Visual styling**: Argument names displayed with syntax-highlighted backgrounds for better readability
4747
- **Default value badges**: Default values shown as styled inline code (e.g., `auto`)
4848

49+
#### Improved argument metadata display (#1011)
50+
51+
- **Cleaner layout**: Default, Type, Choices shown as key-value pairs instead of pipe-separated text
52+
- **Required badge**: Subtle amber tag visible in both light and dark modes
53+
- **Consistent sizing**: All CLI elements match code block font size
54+
4955
## tmuxp 1.64.0 (2026-01-24)
5056

5157
### Documentation

docs/_ext/argparse_lexer.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class ArgparseUsageLexer(RegexLexer):
5656
# Whitespace
5757
(r"\s+", Whitespace),
5858
# Program name (first lowercase word after usage:)
59-
(r"\b[a-z][-a-z0-9]*\b", Name.Label, "usage_body"),
59+
(r"\b[a-z][-a-z0-9_]*\b", Name.Label, "usage_body"),
6060
# Fallback to inline if something unexpected
6161
include("inline"),
6262
],
@@ -67,14 +67,14 @@ class ArgparseUsageLexer(RegexLexer):
6767
(r"\.\.\.", Punctuation),
6868
# Long options with = value (e.g., --log-level=VALUE)
6969
(
70-
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
70+
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
7171
bygroups(Name.Tag, Operator, Name.Variable), # type: ignore[no-untyped-call]
7272
),
7373
# Long options standalone
7474
(r"--[a-zA-Z0-9][-a-zA-Z0-9]*", Name.Tag),
7575
# Short options with space-separated value (e.g., -S socket-path)
7676
(
77-
r"(-[a-zA-Z0-9])(\s+)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
77+
r"(-[a-zA-Z0-9])(\s+)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
7878
bygroups(Name.Attribute, Whitespace, Name.Variable), # type: ignore[no-untyped-call]
7979
),
8080
# Short options standalone
@@ -94,7 +94,7 @@ class ArgparseUsageLexer(RegexLexer):
9494
# UPPERCASE meta-variables (COMMAND, FILE, PATH)
9595
(r"\b[A-Z][A-Z0-9_]*\b", Name.Variable),
9696
# Subcommand/positional names (Name.Function for distinct styling)
97-
(r"\b[a-z][-a-z0-9]*\b", Name.Function),
97+
(r"\b[a-z][-a-z0-9_]*\b", Name.Function),
9898
# Catch-all for any other text
9999
(r"[^\s\[\]|(){},]+", Text),
100100
],
@@ -105,14 +105,14 @@ class ArgparseUsageLexer(RegexLexer):
105105
(r"\.\.\.", Punctuation),
106106
# Long options with = value (e.g., --log-level=VALUE)
107107
(
108-
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
108+
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
109109
bygroups(Name.Tag, Operator, Name.Variable), # type: ignore[no-untyped-call]
110110
),
111111
# Long options standalone
112112
(r"--[a-zA-Z0-9][-a-zA-Z0-9]*", Name.Tag),
113113
# Short options with space-separated value (e.g., -S socket-path)
114114
(
115-
r"(-[a-zA-Z0-9])(\s+)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
115+
r"(-[a-zA-Z0-9])(\s+)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
116116
bygroups(Name.Attribute, Whitespace, Name.Variable), # type: ignore[no-untyped-call]
117117
),
118118
# Short options standalone
@@ -132,7 +132,7 @@ class ArgparseUsageLexer(RegexLexer):
132132
# UPPERCASE meta-variables (COMMAND, FILE, PATH)
133133
(r"\b[A-Z][A-Z0-9_]*\b", Name.Variable),
134134
# Positional/command names (lowercase with dashes)
135-
(r"\b[a-z][-a-z0-9]*\b", Name.Label),
135+
(r"\b[a-z][-a-z0-9_]*\b", Name.Label),
136136
# Catch-all for any other text
137137
(r"[^\s\[\]|(){},]+", Text),
138138
],
@@ -214,7 +214,7 @@ class ArgparseHelpLexer(RegexLexer):
214214
# Whitespace
215215
(r"\s+", Whitespace),
216216
# Program name (first lowercase word after usage:)
217-
(r"\b[a-z][-a-z0-9]*\b", Name.Label, "usage"),
217+
(r"\b[a-z][-a-z0-9_]*\b", Name.Label, "usage"),
218218
# Fallback to usage if something unexpected
219219
include("usage_inline"),
220220
],
@@ -234,14 +234,14 @@ class ArgparseHelpLexer(RegexLexer):
234234
(r"\.\.\.", Punctuation),
235235
# Long options with = value
236236
(
237-
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
237+
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
238238
bygroups(Name.Tag, Operator, Name.Variable), # type: ignore[no-untyped-call]
239239
),
240240
# Long options standalone
241241
(r"--[a-zA-Z0-9][-a-zA-Z0-9]*", Name.Tag),
242242
# Short options with value
243243
(
244-
r"(-[a-zA-Z0-9])(\s+)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
244+
r"(-[a-zA-Z0-9])(\s+)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
245245
bygroups(Name.Attribute, Whitespace, Name.Variable), # type: ignore[no-untyped-call]
246246
),
247247
# Short options standalone
@@ -259,7 +259,7 @@ class ArgparseHelpLexer(RegexLexer):
259259
# UPPERCASE metavars
260260
(r"\b[A-Z][A-Z0-9_]*\b", Name.Variable),
261261
# Subcommand/positional names (Name.Function for distinct styling)
262-
(r"\b[a-z][-a-z0-9]*\b", Name.Function),
262+
(r"\b[a-z][-a-z0-9_]*\b", Name.Function),
263263
# Other text
264264
(r"[^\s\[\]|(){},\n]+", Text),
265265
],
@@ -271,7 +271,7 @@ class ArgparseHelpLexer(RegexLexer):
271271
),
272272
# Long options with = value
273273
(
274-
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
274+
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
275275
bygroups(Name.Tag, Operator, Name.Variable), # type: ignore[no-untyped-call]
276276
),
277277
# Long options with space-separated metavar

docs/_ext/sphinx_argparse_neo/nodes.py

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -518,30 +518,46 @@ def depart_argparse_argument_html(
518518
node : argparse_argument
519519
The argument node being departed.
520520
"""
521-
# Add metadata (default, choices, type)
522-
metadata: list[str] = []
523-
521+
# Build metadata as definition list items
524522
default = node.get("default_string")
525-
if default is not None:
526-
# Wrap default value in nv span for yellow/italic styling
527-
metadata.append(f'Default: <span class="nv">{self.encode(default)}</span>')
528-
529523
choices = node.get("choices")
530-
if choices:
531-
choices_str = ", ".join(str(c) for c in choices)
532-
metadata.append(f"Choices: {self.encode(choices_str)}")
533-
534524
type_name = node.get("type_name")
535-
if type_name:
536-
metadata.append(f"Type: {self.encode(type_name)}")
537-
538525
required = node.get("required", False)
539-
if required:
540-
metadata.append("Required")
541526

542-
if metadata:
543-
meta_str = " | ".join(metadata)
544-
self.body.append(f'<p class="argparse-argument-meta">{meta_str}</p>')
527+
if default is not None or choices or type_name or required:
528+
self.body.append('<dl class="argparse-argument-meta">\n')
529+
530+
if default is not None:
531+
self.body.append('<div class="argparse-meta-item">')
532+
self.body.append('<dt class="argparse-meta-key">Default</dt>')
533+
self.body.append(
534+
f'<dd class="argparse-meta-value">'
535+
f'<span class="nv">{self.encode(default)}</span></dd>'
536+
)
537+
self.body.append("</div>\n")
538+
539+
if type_name:
540+
self.body.append('<div class="argparse-meta-item">')
541+
self.body.append('<dt class="argparse-meta-key">Type</dt>')
542+
self.body.append(
543+
f'<dd class="argparse-meta-value">'
544+
f'<span class="nv">{self.encode(type_name)}</span></dd>'
545+
)
546+
self.body.append("</div>\n")
547+
548+
if choices:
549+
choices_str = ", ".join(str(c) for c in choices)
550+
self.body.append('<div class="argparse-meta-item">')
551+
self.body.append('<dt class="argparse-meta-key">Choices</dt>')
552+
self.body.append(
553+
f'<dd class="argparse-meta-value">{self.encode(choices_str)}</dd>'
554+
)
555+
self.body.append("</div>\n")
556+
557+
if required:
558+
self.body.append('<dt class="argparse-meta-tag">Required</dt>\n')
559+
560+
self.body.append("</dl>\n")
545561

546562
self.body.append("</dd>\n")
547563
# Close wrapper div

docs/_static/css/argparse-highlight.css

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,15 @@
2222
========================================================================== */
2323

2424
/*
25-
* Shared monospace font for all CLI inline roles
25+
* Shared monospace font and code font-size for all CLI inline roles
2626
*/
2727
.cli-option,
2828
.cli-metavar,
2929
.cli-command,
3030
.cli-default,
3131
.cli-choice {
3232
font-family: var(--font-stack--monospace);
33+
font-size: var(--code-font-size);
3334
}
3435

3536
/*
@@ -191,6 +192,7 @@
191192
/* Usage block container - match Pygments monokai background and code block styling */
192193
pre.argparse-usage {
193194
background: var(--argparse-code-background);
195+
font-size: var(--code-font-size);
194196
padding: 0.625rem 0.875rem;
195197
line-height: 1.5;
196198
border-radius: 0.2rem;
@@ -289,6 +291,7 @@ pre.argparse-usage {
289291
border-radius: 0.2rem;
290292
padding: 0.485rem 0.875rem;
291293
font-family: var(--font-stack--monospace);
294+
font-size: var(--code-font-size);
292295
width: fit-content;
293296
position: relative;
294297
}
@@ -330,15 +333,26 @@ pre.argparse-usage {
330333

331334
/*
332335
* Light mode headerlink color overrides
336+
* Needed because code block has dark background regardless of theme
333337
*/
334-
body:not([data-theme="dark"]) .argparse-argument-name .headerlink {
338+
body[data-theme="light"] .argparse-argument-name .headerlink {
335339
color: #9ca0a5;
336340

337341
&:hover:not(:visited) {
338342
color: #cfd0d0;
339343
}
340344
}
341345

346+
@media (prefers-color-scheme: light) {
347+
body:not([data-theme="dark"]) .argparse-argument-name .headerlink {
348+
color: #9ca0a5;
349+
350+
&:hover:not(:visited) {
351+
color: #cfd0d0;
352+
}
353+
}
354+
}
355+
342356
/*
343357
* Highlight when targeted via URL fragment
344358
* Uses Furo's highlight-on-target color for consistency.
@@ -348,18 +362,72 @@ body:not([data-theme="dark"]) .argparse-argument-name .headerlink {
348362
}
349363

350364
/*
351-
* Default value styling in metadata
352-
* Styled like inline code with monokai background.
365+
* Argument metadata definition list
366+
*
367+
* Renders metadata (Default, Type, Choices, Required) as a horizontal
368+
* flexbox of key-value pairs and standalone tags.
353369
*/
354-
.argparse-argument-meta .nv {
370+
.argparse-argument-meta {
371+
margin: 0.5rem 0 0 0;
372+
padding: 0;
373+
display: flex;
374+
flex-wrap: wrap;
375+
gap: 0.5rem 1rem;
376+
align-items: center;
377+
}
378+
379+
.argparse-meta-item {
380+
display: flex;
381+
align-items: center;
382+
gap: 0.25rem;
383+
}
384+
385+
.argparse-meta-key {
386+
color: var(--color-foreground-secondary, #6c757d);
387+
font-size: var(--code-font-size);
388+
}
389+
390+
.argparse-meta-key::after {
391+
content: ":";
392+
}
393+
394+
.argparse-meta-value .nv {
355395
background: var(--argparse-code-background);
356396
border-radius: 0.2rem;
357-
padding: 0.1405rem 0.3rem;
397+
padding: 0.1rem 0.3rem;
358398
font-family: var(--font-stack--monospace);
359-
font-size: var(--font-size--small);
399+
font-size: var(--code-font-size);
360400
color: #e5c07b;
361401
}
362402

403+
/*
404+
* Meta tag (e.g., "Required") - follows Furo's guilabel pattern
405+
* Uses semi-transparent amber background with border for visibility
406+
* without the harshness of solid fills. Amber conveys "needs attention".
407+
*/
408+
.argparse-meta-tag {
409+
background-color: #fef3c780;
410+
border: 1px solid #fcd34d80;
411+
color: var(--color-foreground-primary);
412+
font-size: var(--code-font-size);
413+
padding: 0.1rem 0.4rem;
414+
border-radius: 0.2rem;
415+
font-weight: 500;
416+
}
417+
418+
/* Dark mode: darker amber with adjusted border */
419+
body[data-theme="dark"] .argparse-meta-tag {
420+
background-color: #78350f60;
421+
border-color: #b4530980;
422+
}
423+
424+
@media (prefers-color-scheme: dark) {
425+
body:not([data-theme="light"]) .argparse-meta-tag {
426+
background-color: #78350f60;
427+
border-color: #b4530980;
428+
}
429+
}
430+
363431
/*
364432
* Help text description
365433
* Adds spacing above for visual separation from argument name.

0 commit comments

Comments
 (0)