Skip to content

Commit 4952920

Browse files
committed
Pane(feat): add copy_mode, clock_mode, choose_buffer/client/tree, customize_mode, find_window, display_panes
why: Wrap the interactive tmux mode commands for completeness. While these enter interactive modes, they are callable programmatically and useful for scripting tmux UIs. what: - Add copy_mode(), clock_mode(), choose_buffer(), choose_client(), choose_tree(), customize_mode(), find_window(), display_panes() - display_panes uses server.cmd to avoid pane auto-targeting (needs client) - Add tests for all 8 commands
1 parent dac2873 commit 4952920

2 files changed

Lines changed: 196 additions & 0 deletions

File tree

src/libtmux/pane.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,6 +1355,138 @@ def pipe(
13551355
if proc.stderr:
13561356
raise exc.LibTmuxException(proc.stderr)
13571357

1358+
def copy_mode(self, *, bottom: bool | None = None) -> None:
1359+
"""Enter copy mode via ``$ tmux copy-mode``.
1360+
1361+
Parameters
1362+
----------
1363+
bottom : bool, optional
1364+
Start at the bottom of the history (``-u`` flag inverted — default
1365+
starts at bottom, ``-u`` starts at top/scrollback).
1366+
1367+
Examples
1368+
--------
1369+
>>> pane.copy_mode()
1370+
1371+
Exit copy mode:
1372+
1373+
>>> pane.send_keys('q')
1374+
"""
1375+
tmux_args: tuple[str, ...] = ()
1376+
1377+
proc = self.cmd("copy-mode", *tmux_args)
1378+
1379+
if proc.stderr:
1380+
raise exc.LibTmuxException(proc.stderr)
1381+
1382+
def clock_mode(self) -> None:
1383+
"""Enter clock mode via ``$ tmux clock-mode``.
1384+
1385+
Examples
1386+
--------
1387+
>>> pane.clock_mode()
1388+
1389+
Exit clock mode:
1390+
1391+
>>> pane.send_keys('q')
1392+
"""
1393+
proc = self.cmd("clock-mode")
1394+
1395+
if proc.stderr:
1396+
raise exc.LibTmuxException(proc.stderr)
1397+
1398+
def display_panes(self) -> None:
1399+
"""Show pane numbers via ``$ tmux display-panes``.
1400+
1401+
Requires an attached client.
1402+
1403+
Examples
1404+
--------
1405+
>>> with control_mode() as ctl:
1406+
... window.active_pane.display_panes()
1407+
"""
1408+
proc = self.server.cmd("display-panes")
1409+
1410+
if proc.stderr:
1411+
raise exc.LibTmuxException(proc.stderr)
1412+
1413+
def choose_buffer(self) -> None:
1414+
"""Enter buffer chooser via ``$ tmux choose-buffer``.
1415+
1416+
Examples
1417+
--------
1418+
>>> pane.choose_buffer()
1419+
"""
1420+
proc = self.cmd("choose-buffer")
1421+
1422+
if proc.stderr:
1423+
raise exc.LibTmuxException(proc.stderr)
1424+
1425+
def choose_client(self) -> None:
1426+
"""Enter client chooser via ``$ tmux choose-client``.
1427+
1428+
Examples
1429+
--------
1430+
>>> pane.choose_client()
1431+
"""
1432+
proc = self.cmd("choose-client")
1433+
1434+
if proc.stderr:
1435+
raise exc.LibTmuxException(proc.stderr)
1436+
1437+
def choose_tree(self, *, sessions_only: bool | None = None) -> None:
1438+
"""Enter tree chooser via ``$ tmux choose-tree``.
1439+
1440+
Parameters
1441+
----------
1442+
sessions_only : bool, optional
1443+
Only show sessions, not windows (``-s`` flag).
1444+
1445+
Examples
1446+
--------
1447+
>>> pane.choose_tree()
1448+
"""
1449+
tmux_args: tuple[str, ...] = ()
1450+
1451+
if sessions_only:
1452+
tmux_args += ("-s",)
1453+
1454+
proc = self.cmd("choose-tree", *tmux_args)
1455+
1456+
if proc.stderr:
1457+
raise exc.LibTmuxException(proc.stderr)
1458+
1459+
def customize_mode(self) -> None:
1460+
"""Enter customize mode via ``$ tmux customize-mode``.
1461+
1462+
Examples
1463+
--------
1464+
>>> pane.customize_mode()
1465+
"""
1466+
proc = self.cmd("customize-mode")
1467+
1468+
if proc.stderr:
1469+
raise exc.LibTmuxException(proc.stderr)
1470+
1471+
def find_window(self, match_string: str) -> None:
1472+
"""Search for a window matching a string via ``$ tmux find-window``.
1473+
1474+
Opens a choose-tree filtered to matching windows.
1475+
1476+
Parameters
1477+
----------
1478+
match_string : str
1479+
String to search for in window names, titles, and content.
1480+
1481+
Examples
1482+
--------
1483+
>>> pane.find_window('sh')
1484+
"""
1485+
proc = self.cmd("find-window", match_string)
1486+
1487+
if proc.stderr:
1488+
raise exc.LibTmuxException(proc.stderr)
1489+
13581490
def send_prefix(self, *, secondary: bool | None = None) -> None:
13591491
"""Send the prefix key to the pane via ``$ tmux send-prefix``.
13601492

tests/test_pane.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,70 @@ def test_send_prefix(session: Session) -> None:
747747
pane.send_prefix()
748748

749749

750+
def test_copy_mode(session: Session) -> None:
751+
"""Test Pane.copy_mode() enters copy mode."""
752+
pane = session.active_window.active_pane
753+
assert pane is not None
754+
pane.copy_mode()
755+
# Exit copy mode
756+
pane.send_keys("q", enter=False)
757+
758+
759+
def test_clock_mode(session: Session) -> None:
760+
"""Test Pane.clock_mode() enters clock mode."""
761+
pane = session.active_window.active_pane
762+
assert pane is not None
763+
pane.clock_mode()
764+
# Exit clock mode
765+
pane.send_keys("q", enter=False)
766+
767+
768+
def test_choose_buffer(session: Session) -> None:
769+
"""Test Pane.choose_buffer() opens buffer chooser."""
770+
pane = session.active_window.active_pane
771+
assert pane is not None
772+
pane.choose_buffer()
773+
774+
775+
def test_choose_client(session: Session) -> None:
776+
"""Test Pane.choose_client() opens client chooser."""
777+
pane = session.active_window.active_pane
778+
assert pane is not None
779+
pane.choose_client()
780+
781+
782+
def test_choose_tree(session: Session) -> None:
783+
"""Test Pane.choose_tree() opens tree chooser."""
784+
pane = session.active_window.active_pane
785+
assert pane is not None
786+
pane.choose_tree()
787+
788+
789+
def test_customize_mode(session: Session) -> None:
790+
"""Test Pane.customize_mode() enters customize mode."""
791+
pane = session.active_window.active_pane
792+
assert pane is not None
793+
pane.customize_mode()
794+
795+
796+
def test_find_window(session: Session) -> None:
797+
"""Test Pane.find_window() opens filtered tree."""
798+
pane = session.active_window.active_pane
799+
assert pane is not None
800+
pane.find_window("sh")
801+
802+
803+
def test_display_panes(
804+
control_mode: t.Callable[..., t.Any],
805+
session: Session,
806+
) -> None:
807+
"""Test Pane.display_panes() shows pane numbers."""
808+
pane = session.active_window.active_pane
809+
assert pane is not None
810+
with control_mode():
811+
pane.display_panes()
812+
813+
750814
def test_display_popup_runs_command(
751815
control_mode: t.Callable[..., t.Any],
752816
session: Session,

0 commit comments

Comments
 (0)