Skip to content

Commit 6c8c6e4

Browse files
committed
options.py(fix[explode_complex]): Parse multi-feature terminal-overrides
why: Per tmux.1, terminal-overrides entries are colon-separated strings where the first part is a terminal pattern and remaining parts are individual features. The previous code used `split(":", maxsplit=1)` which collapsed all features after the pattern into a single key. what: - Split on all colons, not just the first - Iterate over each feature part individually - Add parametrized tests for multi-feature entries
1 parent bb084f2 commit 6c8c6e4

File tree

2 files changed

+80
-10
lines changed

2 files changed

+80
-10
lines changed

src/libtmux/options.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -519,18 +519,21 @@ def explode_complex(
519519

520520
for item in val.iter_values():
521521
try:
522-
term, features = item.split(":", maxsplit=1)
522+
# Split on all colons: first part is terminal pattern,
523+
# remaining parts are individual features/capabilities
524+
parts = item.split(":")
525+
term = parts[0]
526+
features = parts[1:]
527+
523528
if term not in new_overrides:
524529
new_overrides[term] = {}
525-
if features and "=" in features:
526-
k, v = features.split("=", 1)
527-
528-
if v.isdigit():
529-
v = int(v)
530530

531-
new_overrides[term][k] = v
532-
else:
533-
new_overrides[term][features] = None
531+
for feature in features:
532+
if feature and "=" in feature:
533+
k, v = feature.split("=", 1)
534+
new_overrides[term][k] = int(v) if v.isdigit() else v
535+
elif feature:
536+
new_overrides[term][feature] = None
534537
except Exception: # NOQA: PERF203
535538
logger.exception("Error parsing options")
536539
options[key] = new_overrides

tests/test_options.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from libtmux.common import has_gte_version
2020
from libtmux.constants import OptionScope
2121
from libtmux.exc import OptionError
22-
from libtmux.options import convert_values
22+
from libtmux.options import TerminalOverrides, convert_values
2323
from libtmux.pane import Pane
2424

2525
if t.TYPE_CHECKING:
@@ -301,6 +301,73 @@ def test_terminal_overrides(
301301
assert options_["terminal-overrides"]["xterm-256color"] == {"Tc": None}
302302

303303

304+
class TerminalOverridesMultiFeatureCase(t.NamedTuple):
305+
"""Test fixture for terminal-overrides with multiple features per terminal."""
306+
307+
test_id: str
308+
raw_entry: str
309+
expected_term: str
310+
expected_features: dict[str, str | int | None]
311+
312+
313+
@pytest.mark.parametrize(
314+
TerminalOverridesMultiFeatureCase._fields,
315+
[
316+
TerminalOverridesMultiFeatureCase(
317+
test_id="two_flags",
318+
raw_entry="xterm*:smcup@:rmcup@",
319+
expected_term="xterm*",
320+
expected_features={"smcup@": None, "rmcup@": None},
321+
),
322+
TerminalOverridesMultiFeatureCase(
323+
test_id="flag_and_keyval",
324+
raw_entry="xterm*:XT:Ms=clipboard",
325+
expected_term="xterm*",
326+
expected_features={"XT": None, "Ms": "clipboard"},
327+
),
328+
TerminalOverridesMultiFeatureCase(
329+
test_id="multiple_keyvals",
330+
raw_entry="screen*:Tc:RGB:setab=test",
331+
expected_term="screen*",
332+
expected_features={
333+
"Tc": None,
334+
"RGB": None,
335+
"setab": "test",
336+
},
337+
),
338+
TerminalOverridesMultiFeatureCase(
339+
test_id="integer_value",
340+
raw_entry="tmux*:colors=256:Tc",
341+
expected_term="tmux*",
342+
expected_features={"colors": 256, "Tc": None},
343+
),
344+
],
345+
ids=lambda x: x.test_id if isinstance(x, TerminalOverridesMultiFeatureCase) else x,
346+
)
347+
def test_terminal_overrides_multi_feature(
348+
server: Server,
349+
monkeypatch: pytest.MonkeyPatch,
350+
test_id: str,
351+
raw_entry: str,
352+
expected_term: str,
353+
expected_features: dict[str, str | int | None],
354+
) -> None:
355+
"""Test terminal-overrides parsing with multiple features per terminal.
356+
357+
Per tmux.1 (lines 4311-4326), terminal-overrides entries are colon-separated
358+
strings: terminal pattern followed by a LIST of features (not just one).
359+
For example, 'xterm*:XT:Ms=value' should parse as two features: XT and Ms.
360+
"""
361+
mock_options = [f"terminal-overrides[0] {raw_entry}"]
362+
monkeypatch.setattr(server, "cmd", fake_cmd(stdout=mock_options))
363+
options_ = server._show_options()
364+
365+
assert "terminal-overrides" in options_
366+
term_overrides = t.cast(TerminalOverrides, options_["terminal-overrides"])
367+
assert expected_term in term_overrides
368+
assert term_overrides[expected_term] == expected_features
369+
370+
304371
def test_command_alias(
305372
server: Server,
306373
monkeypatch: pytest.MonkeyPatch,

0 commit comments

Comments
 (0)