8000 feat(docs): Render argparse metadata as semantic definition list by tony · Pull Request #1011 · tmux-python/tmuxp · GitHub
[go: up one dir, main page]

Skip to content
8 changes: 7 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,17 @@ _Notes on the upcoming release will go here._

CLI documentation now supports direct linking to specific arguments:

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

#### Improved argument metadata display (#1011)

- **Cleaner layout**: Default, Type, Choices shown as key-value pairs instead of pipe-separated text
- **Required badge**: Subtle amber tag visible in both light and dark modes
- **Consistent sizing**: All CLI elements match code block font size

## tmuxp 1.64.0 (2026-01-24)

### Documentation
Expand Down
24 changes: 12 additions & 12 deletions docs/_ext/argparse_lexer.py
8000
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class ArgparseUsageLexer(RegexLexer):
# Whitespace
(r"\s+", Whitespace),
# Program name (first lowercase word after usage:)
(r"\b[a-z][-a-z0-9]*\b", Name.Label, "usage_body"),
(r"\b[a-z][-a-z0-9_]*\b", Name.Label, "usage_body"),
# Fallback to inline if something unexpected
include("inline"),
],
Expand All @@ -67,14 +67,14 @@ class ArgparseUsageLexer(RegexLexer):
(r"\.\.\.", Punctuation),
# Long options with = value (e.g., --log-level=VALUE)
(
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
bygroups(Name.Tag, Operator, Name.Variable), # type: ignore[no-untyped-call]
),
# Long options standalone
(r"--[a-zA-Z0-9][-a-zA-Z0-9]*", Name.Tag),
# Short options with space-separated value (e.g., -S socket-path)
(
r"(-[a-zA-Z0-9])(\s+)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
r"(-[a-zA-Z0-9])(\s+)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
bygroups(Name.Attribute, Whitespace, Name.Variable), # type: ignore[no-untyped-call]
),
# Short options standalone
Expand All @@ -94,7 +94,7 @@ class ArgparseUsageLexer(RegexLexer):
# UPPERCASE meta-variables (COMMAND, FILE, PATH)
(r"\b[A-Z][A-Z0-9_]*\b", Name.Variable),
# Subcommand/positional names (Name.Function for distinct styling)
(r"\b[a-z][-a-z0-9]*\b", Name.Function),
(r"\b[a-z][-a-z0-9_]*\b", Name.Function),
# Catch-all for any other text
(r"[^\s\[\]|(){},]+", Text),
],
Expand All @@ -105,14 +105,14 @@ class ArgparseUsageLexer(RegexLexer):
(r"\.\.\.", Punctuation),
# Long options with = value (e.g., --log-level=VALUE)
(
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
bygroups(Name.Tag, Operator, Name.Variable), # type: ignore[no-untyped-call]
),
# Long options standalone
(r"--[a-zA-Z0-9][-a-zA-Z0-9]*", Name.Tag),
# Short options with space-separated value (e.g., -S socket-path)
(
r"(-[a-zA-Z0-9])(\s+)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
r"(-[a-zA-Z0-9])(\s+)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
bygroups(Name.Attribute, Whitespace, Name.Variable), # type: ignore[no-untyped-call]
),
# Short options standalone
Expand All @@ -132,7 +132,7 @@ class ArgparseUsageLexer(RegexLexer):
# UPPERCASE meta-variables (COMMAND, FILE, PATH)
(r"\b[A-Z][A-Z0-9_]*\b", Name.Variable),
# Positional/command names (lowercase with dashes)
(r"\b[a-z][-a-z0-9]*\b", Name.Label),
(r"\b[a-z][-a-z0-9_]*\b", Name.Label),
# Catch-all for any other text
(r"[^\s\[\]|(){},]+", Text),
],
Expand Down Expand Up @@ -214,7 +214,7 @@ class ArgparseHelpLexer(RegexLexer):
# Whitespace
(r"\s+", Whitespace),
# Program name (first lowercase word after usage:)
(r"\b[a-z][-a-z0-9]*\b", Name.Label, "usage"),
(r"\b[a-z][-a-z0-9_]*\b", Name.Label, "usage"),
# Fallback to usage if something unexpected
include("usage_inline"),
],
Expand All @@ -234,14 +234,14 @@ class ArgparseHelpLexer(RegexLexer):
(r"\.\.\.", Punctuation),
# Long options with = value
(
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
bygroups(Name.Tag, Operator, Name.Variable), # type: ignore[no-untyped-call]
),
# Long options standalone
(r"--[a-zA-Z0-9][-a-zA-Z0-9]*", Name.Tag),
# Short options with value
(
r"(-[a-zA-Z0-9])(\s+)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
r"(-[a-zA-Z0-9])(\s+)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
bygroups(Name.Attribute, Whitespace, Name.Variable), # type: ignore[no-untyped-call]
),
# Short options standalone
Expand All @@ -259,7 +259,7 @@ class ArgparseHelpLexer(RegexLexer):
# UPPERCASE metavars
(r"\b[A-Z][A-Z0-9_]*\b", Name.Variable),
# Subcommand/positional names (Name.Function for distinct styling)
(r"\b[a-z][-a-z0-9]*\b", Name.Function),
(r"\b[a-z][-a-z0-9_]*\b", Name.Function),
# Other text
(r"[^\s\[\]|(){},\n]+", Text),
],
Expand All @@ -271,7 +271,7 @@ class ArgparseHelpLexer(RegexLexer):
),
# Long options with = value
(
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9]*)",
r"(--[a-zA-Z0-9][-a-zA-Z0-9]*)(=)([A-Z][A-Z0-9_]*|[a-z][-a-z0-9_]*)",
bygroups(Name.Tag, Operator, Name.Variable), # type: ignore[no-untyped-call]
),
# Long options with space-separated metavar
Expand Down
54 changes: 35 additions & 19 deletions docs/_ext/sphinx_argparse_neo/nodes.py
47CA
Original file line number Diff line number Diff line change
Expand Up @@ -518,30 +518,46 @@ def depart_argparse_argument_html(
node : argparse_argument
The argument node being departed.
"""
# Add metadata (default, choices, type)
metadata: list[str] = []

# Build metadata as definition list items
default = node.get("default_string")
if default is not None:
# Wrap default value in nv span for yellow/italic styling
metadata.append(f'Default: <span class="nv">{self.encode(default)}</span>')

choices = node.get("choices")
if choices:
choices_str = ", ".join(str(c) for c in choices)
metadata.append(f"Choices: {self.encode(choices_str)}")

type_name = node.get("type_name")
if type_name:
metadata.append(f"Type: {self.encode(type_name)}")

required = node.get("required", False)
if required:
metadata.append("Required")

if metadata:
meta_str = " | ".join(metadata)
self.body.append(f'<p class="argparse-argument-meta">{meta_str}</p>')
if default is not None or choices or type_name or required:
self.body.append('<dl class="argparse-argument-meta">\n')

if default is not None:
self.body.append('<div class="argparse-meta-item">')
self.body.append('<dt class="argparse-meta-key">Default</dt>')
self.body.append(
f'<dd class="argparse-meta-value">'
f'<span class="nv">{self.encode(default)}</span></dd>'
)
self.body.append("</div>\n")

if type_name:
self.body.append('<div class="argparse-meta-item">')
self.body.append('<dt class="argparse-meta-key">Type</dt>')
self.body.append(
f'<dd class="argparse-meta-value">'
f'<span class="nv">{self.encode(type_name)}</span></dd>'
)
self.body.append("</div>\n")

if choices:
choices_str = ", ".join(str(c) for c in choices)
self.body.append('<div class="argparse-meta-item">')
self.body.append('<dt class="argparse-meta-key">Choices</dt>')
self.body.append(
f'<dd class="argparse-meta-value">{self.encode(choices_str)}</dd>'
)
self.body.append("</div>\n")

if required:
self.body.append('<dt class="argparse-meta-tag">Required</dt>\n')

self.body.append("</dl>\n")

self.body.append("</dd>\n")
# Close wrapper div
Expand Down
82 changes: 75 additions & 7 deletions docs/_static/css/argparse-highlight.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@
========================================================================== */

/*
* Shared monospace font for all CLI inline roles
* Shared monospace font and code font-size for all CLI inline roles
*/
.cli-option,
.cli-metavar,
.cli-command,
.cli-default,
.cli-choice {
font-family: var(--font-stack--monospace);
font-size: var(--code-font-size);
}

/*
Expand Down Expand Up @@ -191,6 +192,7 @@
/* Usage block container - match Pygments monokai background and code block styling */
pre.argparse-usage {
background: var(--argparse-code-background);
font-size: var(--code-font-size);
padding: 0.625rem 0.875rem;
line-height: 1.5;
border-radius: 0.2rem;
Expand Down Expand Up @@ -289,6 +291,7 @@ pre.argparse-usage {
border-radius: 0.2rem;
padding: 0.485rem 0.875rem;
font-family: var(--font-stack--monospace);
font-size: var(--code-font-size);
width: fit-content;
position: relative;
}
Expand Down Expand Up @@ -330,15 +333,26 @@ pre.argparse-usage {

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

&:hover:not(:visited) {
color: #cfd0d0;
}
}

@media (prefers-color-scheme: light) {
body:not([data-theme="dark"]) .argparse-argument-name .headerlink {
color: #9ca0a5;

&:hover:not(:visited) {
color: #cfd0d0;
}
}
}

/*
* Highlight when targeted via URL fragment
* Uses Furo's highlight-on-target color for consistency.
Expand All @@ -348,18 +362,72 @@ body:not([data-theme="dark"]) .argparse-argument-name .headerlink {
}

/*
* Default value styling in metadata
* Styled like inline code with monokai background.
* Argument metadata definition list
*
* Renders metadata (Default, Type, Choices, Required) as a horizontal
* flexbox of key-value pairs and standalone tags.
*/
.argparse-argument-meta .nv {
.argparse-argument-meta {
margin: 0.5rem 0 0 0;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 0.5rem 1rem;
align-items: center;
}

.argparse-meta-item {
display: flex;
align-items: center;
gap: 0.25rem;
}

.argparse-meta-key {
color: var(--color-foreground-secondary, #6c757d);
font-size: var(--code-font-size);
}

.argparse-meta-key::after {
content: ":";
}

.argparse-meta-value .nv {
background: var(--argparse-code-background);
border-radius: 0.2rem;
padding: 0.1405rem 0.3rem;
padding: 0.1rem 0.3rem;
font-family: var(--font-stack--monospace);
font-size: var(--font-size--small);
font-size: var(--code-font-size);
color: #e5c07b;
}

/*
* Meta tag (e.g., "Required") - follows Furo's guilabel pattern
* Uses semi-transparent amber background with border for visibility
* without the harshness of solid fills. Amber conveys "needs attention".
*/
.argparse-meta-tag {
background-color: #fef3c780;
border: 1px solid #fcd34d80;
color: var(--color-foreground-primary);
font-size: var(--code-font-size);
padding: 0.1rem 0.4rem;
border-radius: 0.2rem;
font-weight: 500;
}

/* Dark mode: darker amber with adjusted border */
body[data-theme="dark"] .argparse-meta-tag {
background-color: #78350f60;
border-color: #b4530980;
}

@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) .argparse-meta-tag {
background-color: #78350f60;
border-color: #b4530980;
}
}

/*
* Help text description
* Adds spacing above for visual separation from argument name.
Expand Down
Loading
0