Skip to content

Commit 992ec48

Browse files
committed
Pane(feat[break_pane]): add break_pane() wrapping tmux break-pane
why: break-pane is essential for layout management, allowing a pane to be moved into its own window programmatically. what: - Add break_pane() method with detach (-d) and window_name (-n) parameters - Use -P -F#{window_id} to capture new window ID from output - Return Window object via Window.from_window_id() - Use server.cmd with explicit -s flag to avoid auto-target conflicts - Add tests for basic break and named window
1 parent 663569f commit 992ec48

2 files changed

Lines changed: 79 additions & 0 deletions

File tree

src/libtmux/pane.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,55 @@ def enter(self) -> Pane:
11391139
self.cmd("send-keys", "Enter")
11401140
return self
11411141

1142+
def break_pane(
1143+
self,
1144+
*,
1145+
detach: bool = True,
1146+
window_name: str | None = None,
1147+
) -> Window:
1148+
"""Break this pane out into a new window via ``$ tmux break-pane``.
1149+
1150+
Parameters
1151+
----------
1152+
detach : bool, optional
1153+
Do not switch to the new window (``-d`` flag), default True.
1154+
window_name : str, optional
1155+
Name for the new window (``-n`` flag).
1156+
1157+
Returns
1158+
-------
1159+
:class:`Window`
1160+
The newly created window containing the pane.
1161+
1162+
Examples
1163+
--------
1164+
>>> pane_to_break = window.split(shell='sleep 1m')
1165+
>>> new_window = pane_to_break.break_pane(window_name='broken')
1166+
>>> new_window.window_name
1167+
'broken'
1168+
"""
1169+
tmux_args: tuple[str, ...] = ("-P", "-F#{window_id}")
1170+
1171+
if detach:
1172+
tmux_args += ("-d",)
1173+
1174+
if window_name is not None:
1175+
tmux_args += ("-n", window_name)
1176+
1177+
tmux_args += ("-s", str(self.pane_id))
1178+
1179+
# Use server.cmd to avoid auto-adding -t from self.cmd
1180+
proc = self.server.cmd("break-pane", *tmux_args)
1181+
1182+
if proc.stderr:
1183+
raise exc.LibTmuxException(proc.stderr)
1184+
1185+
window_id = proc.stdout[0].strip()
1186+
1187+
from libtmux.window import Window
1188+
1189+
return Window.from_window_id(server=self.server, window_id=window_id)
1190+
11421191
def swap(
11431192
self,
11441193
target: str | Pane,

tests/test_pane.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,36 @@ def test_split_percentage_size_mutual_exclusion(session: Session) -> None:
740740
pane.split(size=10, percentage=50)
741741

742742

743+
def test_break_pane_basic(session: Session) -> None:
744+
"""Test Pane.break_pane() creates a new window."""
745+
window = session.new_window(window_name="test_break")
746+
initial_window_count = len(session.windows)
747+
pane = window.active_pane
748+
assert pane is not None
749+
750+
new_pane = pane.split(shell="sleep 1m")
751+
assert len(window.panes) == 2
752+
753+
new_window = new_pane.break_pane()
754+
session.refresh()
755+
756+
assert len(session.windows) == initial_window_count + 1
757+
window.refresh()
758+
assert len(window.panes) == 1
759+
assert new_window.window_id is not None
760+
761+
762+
def test_break_pane_with_name(session: Session) -> None:
763+
"""Test Pane.break_pane() with window_name."""
764+
window = session.new_window(window_name="test_break_name")
765+
pane = window.active_pane
766+
assert pane is not None
767+
768+
new_pane = pane.split(shell="sleep 1m")
769+
new_window = new_pane.break_pane(window_name="my_broken")
770+
assert new_window.window_name == "my_broken"
771+
772+
743773
def test_swap_pane(session: Session) -> None:
744774
"""Test Pane.swap() swaps two panes."""
745775
window = session.new_window(window_name="test_swap_pane")

0 commit comments

Comments
 (0)