aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_prefix.py
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-15 11:39:12 +0200
committeruser <user@clank>2026-08-15 11:39:12 +0200
commit56bbd25f246f95d15dca992fa79328b37d515afe (patch)
tree3eb984a1e51c9cc30ee9bf394e14adcce049ffec /tests/test_prefix.py
parentsl0ppty: modals get their own surface, and dim what is behind them (diff)
downloadslosh-56bbd25f246f95d15dca992fa79328b37d515afe.tar.gz
slosh-56bbd25f246f95d15dca992fa79328b37d515afe.tar.xz
slosh-56bbd25f246f95d15dca992fa79328b37d515afe.zip
sl0ppty: the prefix badge names the key you actually bound
The leader has been configurable since M6 -- `keys { prefix "ctrl+b" }` -- and one place had not been told. The badge in the status bar, the indicator whose entire job is to say "you are in prefix mode", was the literal string "C-a" whatever you had configured. That is worse than not having the setting. A prefix you can change and a UI that keeps naming the old one teaches you to distrust the thing telling you which mode you are in, and it is precisely the person who rebound it -- the one who wanted C-a back for start-of-line -- who gets lied to. It renders the binding now, through the same function the cheatsheet uses, so the two cannot disagree. Measured rather than assumed to be three columns wide, because `C-space` is seven and `M-\`` is three of a different width. tests/test_prefix.py holds the whole story in one place: four prefixes, each shown correctly in the badge; the rebound key running commands; the *old* key going through to the program untouched (checked with `cat -v`, so a control byte arriving is visible as ^A rather than an invisible character that would prove nothing); the double-press still sending the key itself; and the cheatsheet agreeing with the badge. Also said it out loud in the README and the config, since the way this came up was somebody reasonably assuming it was not possible.
Diffstat (limited to 'tests/test_prefix.py')
-rw-r--r--tests/test_prefix.py96
1 files changed, 96 insertions, 0 deletions
diff --git a/tests/test_prefix.py b/tests/test_prefix.py
new file mode 100644
index 0000000..ab69a3a
--- /dev/null
+++ b/tests/test_prefix.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+"""The leader key is whatever you say it is — everywhere.
+
+It has always been configurable; what had not been was the badge in the status
+bar, which was the string "C-a" whatever you had bound. A prefix you can
+change and a UI that keeps naming the old one is worse than no setting: it
+teaches you to distrust the thing that is telling you which mode you are in.
+
+So this is mostly one assertion made in several places: rebind it, and the
+badge, the cheatsheet, the swallowing and the pass-through all follow.
+"""
+import sys
+import tempfile
+
+from harness import Session, check, report
+
+# `cat -v`, so a control byte that reaches the program is *visible* as ^A
+# rather than arriving as an invisible control character and proving nothing.
+SH = ["/bin/sh", "-c", 'printf "\\033]2;p\\007"; stty raw -echo; cat -v']
+
+
+def cfg(prefix):
+ f = tempfile.NamedTemporaryFile("w", suffix=".kdl", delete=False)
+ f.write('keys {\n prefix "%s"\n}\n' % prefix)
+ f.close()
+ return f.name
+
+
+def strip(s):
+ return s.snapshot().line(1)
+
+
+def test_the_badge_names_the_key_you_bound():
+ for prefix, chord, shown in (("ctrl+a", r"\x01", "C-a"),
+ ("ctrl+b", r"\x02", "C-b"),
+ ("ctrl+space", r"\x00", "C-space"),
+ ("alt+x", r"\ex", "M-x")):
+ with Session(SH, cols=60, rows=8, config=cfg(prefix)) as s:
+ s.settle()
+ check(f"{prefix}: no badge until it is pressed",
+ shown not in strip(s), strip(s))
+ s.send(chord)
+ s.settle()
+ check(f"{prefix}: the badge says {shown}", shown in strip(s),
+ strip(s))
+
+
+def test_a_rebound_prefix_actually_works():
+ with Session(SH, cols=80, rows=14, config=cfg("ctrl+b")) as s:
+ s.settle()
+ check("one pane to start", len(s.panes()) == 1)
+ s.send(r"\x02\\") # C-b \ : split
+ s.settle()
+ check("the new prefix runs commands", len(s.panes()) == 2,
+ str(s.panes()))
+
+
+def test_the_old_prefix_goes_to_the_program_instead():
+ """The reason to move it: C-a is start-of-line to half the world."""
+ with Session(SH, cols=80, rows=14, config=cfg("ctrl+b")) as s:
+ s.settle()
+ s.send(r"\x01") # C-a, no longer ours
+ s.settle(40)
+ check("C-a is not swallowed as a prefix", "C-a" not in strip(s),
+ strip(s))
+ snap = s.until(lambda sn: "^A" in sn.screen())
+ check("it reached the program", "^A" in snap.screen(),
+ snap.screen()[:200])
+
+
+def test_pressing_the_prefix_twice_sends_it_on():
+ with Session(SH, cols=80, rows=14, config=cfg("ctrl+b")) as s:
+ s.settle()
+ s.send(r"\x02\x02")
+ s.settle(40)
+ check("the badge is gone again", "C-b" not in strip(s), strip(s))
+ snap = s.until(lambda sn: "^B" in sn.screen())
+ check("and the pane got the keystroke", "^B" in snap.screen(),
+ snap.screen()[:200])
+
+
+def test_the_cheatsheet_agrees_with_the_badge():
+ with Session(SH, cols=92, rows=28, config=cfg("ctrl+b")) as s:
+ s.settle()
+ s.send(r"\x02?")
+ s.settle()
+ screen = s.snapshot().screen()
+ check("the cheatsheet names the same key", "C-b then:" in screen,
+ screen)
+ check("and not the old one", "C-a then:" not in screen, screen)
+
+
+for name, fn in sorted(list(globals().items())):
+ if name.startswith("test_"):
+ fn()
+sys.exit(report())