Skip to content

Commit 774a0ac

Browse files
committed
Server(feat): add bind_key, unbind_key, list_keys, list_commands
why: Key binding management and command introspection are useful for programmatic tmux configuration and tooling. what: - Add Server.bind_key() wrapping bind-key with key_table (-T), note (-N), repeat (-r) parameters - Add Server.unbind_key() wrapping unbind-key with key_table (-T) - Add Server.list_keys() wrapping list-keys with key_table (-T) filter - Add Server.list_commands() wrapping list-commands with optional filter - Add tests for bind/unbind cycle, list-keys, and list-commands
1 parent 90684ef commit 774a0ac

2 files changed

Lines changed: 185 additions & 0 deletions

File tree

src/libtmux/server.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,151 @@ def wait_for(
528528
if proc.stderr:
529529
raise exc.LibTmuxException(proc.stderr)
530530

531+
def bind_key(
532+
self,
533+
key: str,
534+
command: str,
535+
*,
536+
key_table: str | None = None,
537+
note: str | None = None,
538+
repeat: bool | None = None,
539+
) -> None:
540+
"""Bind a key to a command via ``$ tmux bind-key``.
541+
542+
Parameters
543+
----------
544+
key : str
545+
Key to bind (e.g. ``C-a``, ``F12``, ``M-x``).
546+
command : str
547+
Tmux command to run when key is pressed.
548+
key_table : str, optional
549+
Key table to bind in (``-T`` flag). Defaults to ``prefix``.
550+
note : str, optional
551+
Note for the binding (``-N`` flag).
552+
repeat : bool, optional
553+
Allow the key to repeat (``-r`` flag).
554+
555+
Examples
556+
--------
557+
>>> server.bind_key('F12', 'display-message test', key_table='root')
558+
>>> server.unbind_key('F12', key_table='root')
559+
"""
560+
tmux_args: tuple[str, ...] = ()
561+
562+
if repeat:
563+
tmux_args += ("-r",)
564+
565+
if note is not None:
566+
tmux_args += ("-N", note)
567+
568+
if key_table is not None:
569+
tmux_args += ("-T", key_table)
570+
571+
tmux_args += (key, command)
572+
573+
proc = self.cmd("bind-key", *tmux_args)
574+
575+
if proc.stderr:
576+
raise exc.LibTmuxException(proc.stderr)
577+
578+
def unbind_key(
579+
self,
580+
key: str,
581+
*,
582+
key_table: str | None = None,
583+
) -> None:
584+
"""Unbind a key via ``$ tmux unbind-key``.
585+
586+
Parameters
587+
----------
588+
key : str
589+
Key to unbind.
590+
key_table : str, optional
591+
Key table (``-T`` flag). Defaults to ``prefix``.
592+
593+
Examples
594+
--------
595+
>>> server.bind_key('F11', 'display-message test', key_table='root')
596+
>>> server.unbind_key('F11', key_table='root')
597+
"""
598+
tmux_args: tuple[str, ...] = ()
599+
600+
if key_table is not None:
601+
tmux_args += ("-T", key_table)
602+
603+
tmux_args += (key,)
604+
605+
proc = self.cmd("unbind-key", *tmux_args)
606+
607+
if proc.stderr:
608+
raise exc.LibTmuxException(proc.stderr)
609+
610+
def list_keys(
611+
self,
612+
*,
613+
key_table: str | None = None,
614+
) -> list[str]:
615+
"""List key bindings via ``$ tmux list-keys``.
616+
617+
Parameters
618+
----------
619+
key_table : str, optional
620+
Filter by key table (``-T`` flag).
621+
622+
Returns
623+
-------
624+
list[str]
625+
Key binding lines.
626+
627+
Examples
628+
--------
629+
>>> result = server.list_keys()
630+
>>> isinstance(result, list)
631+
True
632+
"""
633+
tmux_args: tuple[str, ...] = ()
634+
635+
if key_table is not None:
636+
tmux_args += ("-T", key_table)
637+
638+
proc = self.cmd("list-keys", *tmux_args)
639+
640+
if proc.stderr:
641+
raise exc.LibTmuxException(proc.stderr)
642+
643+
return proc.stdout
644+
645+
def list_commands(self, *, command_name: str | None = None) -> list[str]:
646+
"""List tmux commands via ``$ tmux list-commands``.
647+
648+
Parameters
649+
----------
650+
command_name : str, optional
651+
Filter to a specific command.
652+
653+
Returns
654+
-------
655+
list[str]
656+
Command listing lines.
657+
658+
Examples
659+
--------
660+
>>> result = server.list_commands(command_name='send-keys')
661+
>>> len(result) >= 1
662+
True
663+
"""
664+
tmux_args: tuple[str, ...] = ()
665+
666+
if command_name is not None:
667+
tmux_args += (command_name,)
668+
669+
proc = self.cmd("list-commands", *tmux_args)
670+
671+
if proc.stderr:
672+
raise exc.LibTmuxException(proc.stderr)
673+
674+
return proc.stdout
675+
531676
def show_messages(self) -> list[str]:
532677
"""Show server message log via ``$ tmux show-messages``.
533678

tests/test_server.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,46 @@ def test_tmux_bin_invalid_path_raise_if_dead() -> None:
461461
s.raise_if_dead()
462462

463463

464+
def test_bind_unbind_key(server: Server) -> None:
465+
"""Test Server.bind_key() and unbind_key() cycle."""
466+
server.new_session(session_name="bind_test")
467+
468+
server.bind_key("F12", "display-message bound", key_table="root")
469+
470+
# Verify binding exists
471+
keys = server.list_keys(key_table="root")
472+
assert any("F12" in line for line in keys)
473+
474+
# Unbind
475+
server.unbind_key("F12", key_table="root")
476+
477+
# Verify binding gone
478+
keys = server.list_keys(key_table="root")
479+
assert not any("F12" in line and "display-message" in line for line in keys)
480+
481+
482+
def test_list_keys(server: Server) -> None:
483+
"""Test Server.list_keys() returns key bindings."""
484+
server.new_session(session_name="listkeys_test")
485+
result = server.list_keys()
486+
assert isinstance(result, list)
487+
assert len(result) > 0 # default bindings exist
488+
489+
490+
def test_list_commands(server: Server) -> None:
491+
"""Test Server.list_commands() returns command listing."""
492+
server.new_session(session_name="listcmds_test")
493+
494+
# All commands
495+
result = server.list_commands()
496+
assert len(result) > 50 # tmux has many commands
497+
498+
# Filtered
499+
result = server.list_commands(command_name="send-keys")
500+
assert len(result) >= 1
501+
assert "send-keys" in result[0]
502+
503+
464504
def test_show_messages(server: Server) -> None:
465505
"""Test Server.show_messages() returns message log."""
466506
server.new_session(session_name="showmsg_test")

0 commit comments

Comments
 (0)