Skip to content

Commit f8b13b5

Browse files
committed
fix(lexer): Include underscores in lowercase metavar patterns
Lowercase metavars like `socket_name` were split at underscores because the regex `[a-z][-a-z0-9]*` didn't include `_`. Changed to `[a-z][-a-z0-9_]*` in all 6 pattern locations. Fixes: tmuxp load usage showing `<span>socket</span>_name` instead of `<span>socket_name</span>`.
1 parent 9642011 commit f8b13b5

2 files changed

Lines changed: 39 additions & 12 deletions

File tree

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

tests/docs/_ext/test_argparse_lexer.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,18 @@ class ShortOptionValueFixture(t.NamedTuple):
477477
option="-r",
478478
value="name",
479479
),
480+
ShortOptionValueFixture(
481+
test_id="underscore_metavar",
482+
input_text="-L socket_name",
483+
option="-L",
484+
value="socket_name",
485+
),
486+
ShortOptionValueFixture(
487+
test_id="multiple_underscores",
488+
input_text="-f tmux_config_file",
489+
option="-f",
490+
value="tmux_config_file",
491+
),
480492
]
481493

482494

@@ -796,3 +808,18 @@ def test_argparse_help_lexer_multiline() -> None:
796808
assert "options:" in token_values or any(
797809
"options:" in v for v in token_values if isinstance(v, str)
798810
)
811+
812+
813+
def test_tmuxp_load_usage_underscores() -> None:
814+
"""Test tmuxp load usage with underscore metavars.
815+
816+
Regression test for underscore handling in lowercase metavars.
817+
Previously, `socket_name` was tokenized as `socket` + `_name` (split).
818+
"""
819+
usage = "usage: tmuxp load [-L socket_name] [-S socket_path] [-f tmux_config_file]"
820+
tokens = get_usage_tokens(usage)
821+
822+
# All underscore metavars should be fully captured
823+
assert ("Token.Name.Variable", "socket_name") in tokens
824+
assert ("Token.Name.Variable", "socket_path") in tokens
825+
assert ("Token.Name.Variable", "tmux_config_file") in tokens

0 commit comments

Comments
 (0)