Skip to content

Commit 86a0e33

Browse files
committed
docs(topics): Add workspace setup guide with doctests
why: Multi-pane workspace creation essential for parallel task execution what: - Add window creation patterns with attach options - Add pane splitting (vertical/horizontal) examples - Add layout management (tiled, even-horizontal, main-vertical) - Add practical recipes for dev workspace and pane grids - All examples are runnable doctests
1 parent 22bfe4e commit 86a0e33

File tree

1 file changed

+353
-0
lines changed

1 file changed

+353
-0
lines changed

docs/topics/workspace_setup.md

Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
(workspace-setup)=
2+
3+
# Workspace Setup
4+
5+
libtmux makes it easy to create and configure multi-pane workspaces programmatically.
6+
This is useful for setting up development environments, running parallel tasks,
7+
and orchestrating terminal-based workflows.
8+
9+
Open two terminals:
10+
11+
Terminal one: start tmux in a separate terminal:
12+
13+
```console
14+
$ tmux
15+
```
16+
17+
Terminal two, `python` or `ptpython` if you have it:
18+
19+
```console
20+
$ python
21+
```
22+
23+
## Creating Windows
24+
25+
The {meth}`~libtmux.Session.new_window` method creates new windows within a session.
26+
27+
### Basic window creation
28+
29+
```python
30+
>>> new_window = session.new_window(window_name='workspace')
31+
>>> new_window # doctest: +ELLIPSIS
32+
Window(@... ...:workspace, Session($... ...))
33+
34+
>>> # Window is part of the session
35+
>>> new_window in session.windows
36+
True
37+
```
38+
39+
### Create without attaching
40+
41+
Use `attach=False` to create a window in the background:
42+
43+
```python
44+
>>> background_window = session.new_window(
45+
... window_name='background-task',
46+
... attach=False,
47+
... )
48+
>>> background_window # doctest: +ELLIPSIS
49+
Window(@... ...:background-task, Session($... ...))
50+
51+
>>> # Clean up
52+
>>> background_window.kill()
53+
```
54+
55+
### Create with specific shell
56+
57+
```python
58+
>>> shell_window = session.new_window(
59+
... window_name='shell-test',
60+
... attach=False,
61+
... window_shell='sh -c "echo Hello; exec sh"',
62+
... )
63+
>>> shell_window # doctest: +ELLIPSIS
64+
Window(@... ...:shell-test, Session($... ...))
65+
66+
>>> # Clean up
67+
>>> shell_window.kill()
68+
```
69+
70+
## Splitting Panes
71+
72+
The {meth}`~libtmux.Window.split` method divides windows into multiple panes.
73+
74+
### Vertical split (top/bottom)
75+
76+
```python
77+
>>> import time
78+
>>> from libtmux.constants import PaneDirection
79+
80+
>>> # Create a window with enough space
81+
>>> v_split_window = session.new_window(window_name='v-split-demo', attach=False)
82+
>>> v_split_window.resize(height=40, width=120) # doctest: +ELLIPSIS
83+
Window(@... ...)
84+
85+
>>> # Default split is vertical (creates pane below)
86+
>>> top_pane = v_split_window.active_pane
87+
>>> bottom_pane = v_split_window.split()
88+
>>> bottom_pane # doctest: +ELLIPSIS
89+
Pane(%... Window(@... ..., Session($... ...)))
90+
91+
>>> len(v_split_window.panes)
92+
2
93+
94+
>>> # Clean up
95+
>>> v_split_window.kill()
96+
```
97+
98+
### Horizontal split (left/right)
99+
100+
```python
101+
>>> from libtmux.constants import PaneDirection
102+
103+
>>> # Create a fresh window for this demo
104+
>>> h_split_window = session.new_window(window_name='h-split', attach=False)
105+
>>> h_split_window.resize(height=40, width=120) # doctest: +ELLIPSIS
106+
Window(@... ...)
107+
108+
>>> left_pane = h_split_window.active_pane
109+
>>> right_pane = left_pane.split(direction=PaneDirection.Right)
110+
>>> right_pane # doctest: +ELLIPSIS
111+
Pane(%... Window(@... ..., Session($... ...)))
112+
113+
>>> len(h_split_window.panes)
114+
2
115+
116+
>>> # Clean up
117+
>>> h_split_window.kill()
118+
```
119+
120+
### Split with specific size
121+
122+
```python
123+
>>> # Create a fresh window for size demo
124+
>>> size_window = session.new_window(window_name='size-demo', attach=False)
125+
>>> size_window.resize(height=40, width=120) # doctest: +ELLIPSIS
126+
Window(@... ...)
127+
128+
>>> main_pane = size_window.active_pane
129+
>>> # Create pane with specific percentage
130+
>>> small_pane = main_pane.split(size='20%')
131+
>>> small_pane # doctest: +ELLIPSIS
132+
Pane(%... Window(@... ..., Session($... ...)))
133+
134+
>>> # Clean up
135+
>>> size_window.kill()
136+
```
137+
138+
## Layout Management
139+
140+
The {meth}`~libtmux.Window.select_layout` method arranges panes using built-in layouts.
141+
142+
### Available layouts
143+
144+
tmux provides five built-in layouts:
145+
146+
| Layout | Description |
147+
|--------|-------------|
148+
| `even-horizontal` | Panes spread evenly left to right |
149+
| `even-vertical` | Panes spread evenly top to bottom |
150+
| `main-horizontal` | Large pane on top, others below |
151+
| `main-vertical` | Large pane on left, others on right |
152+
| `tiled` | Panes spread evenly in rows and columns |
153+
154+
### Applying layouts
155+
156+
```python
157+
>>> # Create window with multiple panes
158+
>>> layout_window = session.new_window(window_name='layout-demo', attach=False)
159+
>>> layout_window.resize(height=60, width=120) # doctest: +ELLIPSIS
160+
Window(@... ...)
161+
162+
>>> pane1 = layout_window.active_pane
163+
>>> pane2 = layout_window.split()
164+
>>> pane3 = layout_window.split()
165+
>>> pane4 = layout_window.split()
166+
167+
>>> # Apply tiled layout
168+
>>> layout_window.select_layout('tiled') # doctest: +ELLIPSIS
169+
Window(@... ...)
170+
171+
>>> # Apply even-horizontal layout
172+
>>> layout_window.select_layout('even-horizontal') # doctest: +ELLIPSIS
173+
Window(@... ...)
174+
175+
>>> # Apply main-vertical layout
176+
>>> layout_window.select_layout('main-vertical') # doctest: +ELLIPSIS
177+
Window(@... ...)
178+
179+
>>> # Clean up
180+
>>> layout_window.kill()
181+
```
182+
183+
## Renaming and Organizing
184+
185+
### Rename windows
186+
187+
```python
188+
>>> rename_window = session.new_window(window_name='old-name', attach=False)
189+
>>> rename_window.rename_window('new-name') # doctest: +ELLIPSIS
190+
Window(@... ...:new-name, Session($... ...))
191+
192+
>>> rename_window.window_name
193+
'new-name'
194+
195+
>>> # Clean up
196+
>>> rename_window.kill()
197+
```
198+
199+
### Access window properties
200+
201+
```python
202+
>>> demo_window = session.new_window(window_name='props-demo', attach=False)
203+
204+
>>> # Window index
205+
>>> demo_window.window_index # doctest: +ELLIPSIS
206+
'...'
207+
208+
>>> # Window ID
209+
>>> demo_window.window_id # doctest: +ELLIPSIS
210+
'@...'
211+
212+
>>> # Parent session
213+
>>> demo_window.session # doctest: +ELLIPSIS
214+
Session($... ...)
215+
216+
>>> # Clean up
217+
>>> demo_window.kill()
218+
```
219+
220+
## Practical Recipes
221+
222+
### Recipe: Create a development workspace
223+
224+
```python
225+
>>> import time
226+
>>> from libtmux.constants import PaneDirection
227+
228+
>>> def create_dev_workspace(session, name='dev'):
229+
... """Create a typical development workspace layout."""
230+
... window = session.new_window(window_name=name, attach=False)
231+
... window.resize(height=50, width=160)
232+
...
233+
... # Main editing pane (large, left side)
234+
... main_pane = window.active_pane
235+
...
236+
... # Terminal pane (bottom)
237+
... terminal_pane = main_pane.split(size='30%')
238+
...
239+
... # Logs pane (right side of terminal)
240+
... log_pane = terminal_pane.split(direction=PaneDirection.Right)
241+
...
242+
... return {
243+
... 'window': window,
244+
... 'main': main_pane,
245+
... 'terminal': terminal_pane,
246+
... 'logs': log_pane,
247+
... }
248+
249+
>>> workspace = create_dev_workspace(session, 'my-project')
250+
>>> len(workspace['window'].panes)
251+
3
252+
253+
>>> # Clean up
254+
>>> workspace['window'].kill()
255+
```
256+
257+
### Recipe: Create a grid of panes
258+
259+
```python
260+
>>> from libtmux.constants import PaneDirection
261+
262+
>>> def create_pane_grid(session, rows=2, cols=2, name='grid'):
263+
... """Create an NxM grid of panes."""
264+
... window = session.new_window(window_name=name, attach=False)
265+
... window.resize(height=50, width=160)
266+
...
267+
... panes = []
268+
... base_pane = window.active_pane
269+
... panes.append(base_pane)
270+
...
271+
... # Create first row of panes
272+
... current = base_pane
273+
... for _ in range(cols - 1):
274+
... new_pane = current.split(direction=PaneDirection.Right)
275+
... panes.append(new_pane)
276+
... current = new_pane
277+
...
278+
... # Create additional rows
279+
... for _ in range(rows - 1):
280+
... row_start = panes[-cols]
281+
... current = row_start
282+
... for col in range(cols):
283+
... new_pane = panes[-cols + col].split(direction=PaneDirection.Below)
284+
... panes.append(new_pane)
285+
...
286+
... # Apply tiled layout for even distribution
287+
... window.select_layout('tiled')
288+
... return window, panes
289+
290+
>>> grid_window, grid_panes = create_pane_grid(session, rows=2, cols=2, name='test-grid')
291+
>>> len(grid_panes) >= 4
292+
True
293+
294+
>>> # Clean up
295+
>>> grid_window.kill()
296+
```
297+
298+
### Recipe: Run commands in multiple panes
299+
300+
```python
301+
>>> import time
302+
303+
>>> def run_in_panes(panes, commands):
304+
... """Run different commands in each pane."""
305+
... for pane, cmd in zip(panes, commands):
306+
... pane.send_keys(cmd)
307+
308+
>>> multi_window = session.new_window(window_name='multi-cmd', attach=False)
309+
>>> multi_window.resize(height=40, width=120) # doctest: +ELLIPSIS
310+
Window(@... ...)
311+
312+
>>> pane_a = multi_window.active_pane
313+
>>> pane_b = multi_window.split()
314+
>>> pane_c = multi_window.split()
315+
316+
>>> run_in_panes(
317+
... [pane_a, pane_b, pane_c],
318+
... ['echo "Task A"', 'echo "Task B"', 'echo "Task C"'],
319+
... )
320+
321+
>>> # Give commands time to execute
322+
>>> time.sleep(0.2)
323+
324+
>>> # Verify all commands ran
325+
>>> 'Task A' in '\\n'.join(pane_a.capture_pane())
326+
True
327+
328+
>>> # Clean up
329+
>>> multi_window.kill()
330+
```
331+
332+
## Window Context Managers
333+
334+
Windows can be used as context managers for automatic cleanup:
335+
336+
```python
337+
>>> with session.new_window(window_name='temp-window') as temp_win:
338+
... pane = temp_win.active_pane
339+
... pane.send_keys('echo "temporary workspace"')
340+
... temp_win in session.windows
341+
True
342+
343+
>>> # Window is automatically killed after exiting context
344+
>>> temp_win not in session.windows
345+
True
346+
```
347+
348+
:::{seealso}
349+
- {ref}`pane-interaction` for working with pane content
350+
- {ref}`automation-patterns` for advanced orchestration
351+
- {class}`~libtmux.Window` for all window methods
352+
- {class}`~libtmux.Session` for session management
353+
:::

0 commit comments

Comments
 (0)