Skip to content

Commit 913ca89

Browse files
authored
Builder: Use shutil.get_terminal_size() (#926)
See also: #667, #702, #737
2 parents f4b392c + cd5d114 commit 913ca89

File tree

9 files changed

+136
-91
lines changed

9 files changed

+136
-91
lines changed

Diff for: .tmuxp.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"focus": true,
1111
"layout": "main-horizontal",
1212
"options": {
13-
"main-pane-height": 35
13+
"main-pane-height": "67%"
1414
},
1515
"panes": [
1616
{
@@ -25,7 +25,7 @@
2525
"window_name": "docs",
2626
"layout": "main-horizontal",
2727
"options": {
28-
"main-pane-height": 35
28+
"main-pane-height": "67%"
2929
},
3030
"start_directory": "docs/",
3131
"panes": [
@@ -38,4 +38,4 @@
3838
]
3939
}
4040
]
41-
}
41+
}

Diff for: .tmuxp.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ windows:
77
focus: True
88
layout: main-horizontal
99
options:
10-
main-pane-height: 35
10+
main-pane-height: 67%
1111
panes:
1212
- focus: true
1313
- pane
@@ -16,7 +16,7 @@ windows:
1616
- window_name: docs
1717
layout: main-horizontal
1818
options:
19-
main-pane-height: 35
19+
main-pane-height: 67%
2020
start_directory: docs/
2121
panes:
2222
- focus: true

Diff for: CHANGES

+25
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,31 @@ $ pipx install --suffix=@next 'tmuxp' --pip-args '\--pre' --force
1919

2020
<!-- Maintainers, insert changes / features for the next release here -->
2121

22+
### Breaking change
23+
24+
#### Workspace builder now detects terminal size (#926)
25+
26+
Dimensions used by workspace builder now use {py:func}`shutil.get_terminal_size()`.
27+
28+
In conjunction with `main-pane-height: 67%`, for instance, this will render a
29+
proportional layout:
30+
31+
```yaml
32+
session_name: my session
33+
windows:
34+
- window_name: example with percentage
35+
focus: True
36+
layout: main-horizontal
37+
options:
38+
main-pane-height: 67%
39+
panes:
40+
- focus: true
41+
- pane
42+
```
43+
44+
To use old behavior, set `TMUXP_DETECT_TERMINAL_SIZE=0` in your terminal
45+
environment and file an issue on the tracker.
46+
2247
### Documentation
2348

2449
- Automatically linkify links that were previously only text.

Diff for: docs/configuration/examples.md

+24
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,30 @@ pane during creation.
554554

555555
## Main pane height
556556

557+
### Percentage
558+
559+
:::{versionadded} 1.46.0
560+
561+
Before this, tmuxp layouts would not detect the terminal's size.
562+
563+
:::
564+
565+
````{tab} YAML
566+
```{literalinclude} ../../examples/main-pane-height-percentage.yaml
567+
:language: yaml
568+
569+
```
570+
````
571+
572+
````{tab} JSON
573+
```{literalinclude} ../../examples/main-pane-height-percentage.json
574+
:language: json
575+
576+
```
577+
````
578+
579+
### Rows
580+
557581
````{tab} YAML
558582
```{literalinclude} ../../examples/main-pane-height.yaml
559583
:language: yaml

Diff for: examples/main-pane-height-percentage.json

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"windows": [
3+
{
4+
"panes": [
5+
{
6+
"shell_command": [
7+
"top"
8+
],
9+
"start_directory": "~"
10+
},
11+
{
12+
"shell_command": [
13+
"echo \"hey\""
14+
]
15+
},
16+
{
17+
"shell_command": [
18+
"echo \"moo\""
19+
]
20+
}
21+
],
22+
"layout": "main-horizontal",
23+
"options": {
24+
"main-pane-height": "67%"
25+
},
26+
"window_name": "editor"
27+
}
28+
],
29+
"session_name": "main pane height",
30+
"start_directory": "~"
31+
}

Diff for: examples/main-pane-height-percentage.yaml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
session_name: main-pane-height
2+
start_directory: "~"
3+
windows:
4+
- layout: main-horizontal
5+
options:
6+
main-pane-height: 67%
7+
panes:
8+
- shell_command:
9+
- top
10+
start_directory: "~"
11+
- shell_command:
12+
- echo "hey"
13+
- shell_command:
14+
- echo "moo"
15+
window_name: my window name

Diff for: src/tmuxp/cli/load.py

-77
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import sys
1010
import typing as t
1111

12-
from libtmux.common import has_gte_version
1312
from libtmux.server import Server
1413
from libtmux.session import Session
1514

@@ -48,65 +47,6 @@ class CLILoadNamespace(argparse.Namespace):
4847
log_file: t.Optional[str]
4948

5049

51-
def set_layout_hook(session: Session, hook_name: str) -> None:
52-
"""Set layout hooks to normalize layout.
53-
54-
References
55-
----------
56-
- tmuxp issue: https://github.com/tmux-python/tmuxp/issues/309
57-
- tmux issue: https://github.com/tmux/tmux/issues/1106
58-
59-
tmux 2.6+ requires that the window be viewed with the client before
60-
select-layout adjustments can take effect.
61-
62-
To handle this, this function creates temporary hook for this session to
63-
iterate through all windows and select the layout.
64-
65-
In order for layout changes to take effect, a client must at the very
66-
least be attached to the window (not just the session).
67-
68-
hook_name is provided to allow this to set to multiple scenarios, such
69-
as 'client-attached' (which the user attaches the session). You may
70-
also want 'after-switch-client' for cases where the user loads tmuxp
71-
sessions inside tmux since tmuxp offers to switch for them.
72-
73-
Also, the hooks are set immediately unbind after they're invoked via -u.
74-
75-
Parameters
76-
----------
77-
session : :class:`libtmux.session.Session`
78-
session to bind hook to
79-
hook_name : str
80-
hook name to bind to, e.g. 'client-attached'
81-
"""
82-
assert session.id is not None
83-
cmd: t.List[str] = ["set-hook", hook_name]
84-
hook_cmd = []
85-
active_window = session.active_window
86-
for window in session.windows:
87-
# unfortunately, select-layout won't work unless
88-
# we've literally selected the window at least once
89-
# with the client
90-
hook_cmd.append(f"selectw -t {window.id}")
91-
# edit: removed -t, or else it won't respect main-pane-w/h
92-
hook_cmd.append("selectl")
93-
hook_cmd.append("selectw -p")
94-
95-
# unset the hook immediately after executing
96-
hook_cmd.extend(
97-
(f"set-hook -u -t {session.id} {hook_name}", f"selectw -t {active_window.id}")
98-
)
99-
100-
# join the hook's commands with semicolons
101-
_hook_cmd = "{}".format("; ".join(hook_cmd))
102-
103-
# append the hook command
104-
cmd.append(_hook_cmd)
105-
106-
# create the hook
107-
session.cmd(*cmd, target=session.id)
108-
109-
11050
def load_plugins(session_config: t.Dict[str, t.Any]) -> t.List[t.Any]:
11151
"""Load and return plugins in workspace."""
11252
plugins = []
@@ -200,20 +140,10 @@ def _load_attached(builder: WorkspaceBuilder, detached: bool) -> None:
200140
# unset TMUX, save it, e.g. '/tmp/tmux-1000/default,30668,0'
201141
tmux_env = os.environ.pop("TMUX")
202142

203-
if has_gte_version("2.6"):
204-
set_layout_hook(builder.session, "client-session-changed")
205-
206143
builder.session.switch_client() # switch client to new session
207144

208145
os.environ["TMUX"] = tmux_env # set TMUX back again
209146
else:
210-
if has_gte_version("2.6"):
211-
# if attaching for first time
212-
set_layout_hook(builder.session, "client-attached")
213-
214-
# for cases where user switches client for first time
215-
set_layout_hook(builder.session, "client-session-changed")
216-
217147
if not detached:
218148
builder.session.attach_session()
219149

@@ -230,10 +160,6 @@ def _load_detached(builder: WorkspaceBuilder) -> None:
230160

231161
assert builder.session is not None
232162

233-
if has_gte_version("2.6"): # prepare for both cases
234-
set_layout_hook(builder.session, "client-attached")
235-
set_layout_hook(builder.session, "client-session-changed")
236-
237163
print("Session created in detached state.")
238164

239165

@@ -248,9 +174,6 @@ def _load_append_windows_to_current_session(builder: WorkspaceBuilder) -> None:
248174
current_attached_session = builder.find_current_attached_session()
249175
builder.build(current_attached_session, append=True)
250176
assert builder.session is not None
251-
if has_gte_version("2.6"): # prepare for both cases
252-
set_layout_hook(builder.session, "client-attached")
253-
set_layout_hook(builder.session, "client-session-changed")
254177

255178

256179
def _setup_plugins(builder: WorkspaceBuilder) -> Session:

Diff for: src/tmuxp/workspace/builder.py

+29-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""Create a tmux workspace from a workspace :py:obj:`dict`."""
22

33
import logging
4+
import os
5+
import shutil
46
import time
57
import typing as t
68

@@ -16,9 +18,22 @@
1618

1719
logger = logging.getLogger(__name__)
1820

19-
DEFAULT_WIDTH = "800"
20-
DEFAULT_HEIGHT = "600"
21-
DEFAULT_SIZE = f"{DEFAULT_WIDTH}x{DEFAULT_HEIGHT}"
21+
COLUMNS_FALLBACK = 80
22+
23+
24+
def get_default_columns() -> int:
25+
"""Return default session column size use when building new tmux sessions."""
26+
return int(
27+
os.getenv("TMUXP_DEFAULT_COLUMNS", os.getenv("COLUMNS", COLUMNS_FALLBACK))
28+
)
29+
30+
31+
ROWS_FALLBACK = int(os.getenv("TMUXP_DEFAULT_ROWS", os.getenv("ROWS", 24)))
32+
33+
34+
def get_default_rows() -> int:
35+
"""Return default session row size use when building new tmux sessions."""
36+
return int(os.getenv("TMUXP_DEFAULT_ROWS", os.getenv("ROWS", ROWS_FALLBACK)))
2237

2338

2439
class WorkspaceBuilder:
@@ -229,9 +244,17 @@ def build(self, session: t.Optional[Session] = None, append: bool = False) -> No
229244
new_session_kwargs["start_directory"] = self.session_config[
230245
"start_directory"
231246
]
232-
if has_gte_version("2.6"):
233-
new_session_kwargs["x"] = 800
234-
new_session_kwargs["y"] = 600
247+
248+
if (
249+
has_gte_version("2.6")
250+
and os.getenv("TMUXP_DETECT_TERMINAL_SIZE", "1") == "1"
251+
):
252+
terminal_size = shutil.get_terminal_size(
253+
fallback=(get_default_columns(), get_default_rows())
254+
)
255+
new_session_kwargs["x"] = terminal_size.columns
256+
new_session_kwargs["y"] = terminal_size.lines
257+
235258
session = self.server.new_session(
236259
session_name=self.session_config["session_name"],
237260
**new_session_kwargs,

Diff for: tests/workspace/test_builder.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,8 @@ def test_automatic_rename_option(
486486
) -> None:
487487
"""Test workspace builder with automatic renaming enabled."""
488488
monkeypatch.setenv("DISABLE_AUTO_TITLE", "true")
489+
monkeypatch.setenv("ROWS", "36")
490+
489491
workspace = ConfigReader._from_file(
490492
test_utils.get_workspace_file("workspace/builder/window_automatic_rename.yaml"),
491493
)
@@ -1506,7 +1508,12 @@ def test_issue_800_default_size_many_windows(
15061508
a lot of panes.
15071509
15081510
See also: https://github.com/tmux-python/tmuxp/issues/800
1511+
1512+
2024-04-07: This test isn't being used as of this date, as default-size is totally
1513+
unused in builder.py.
15091514
"""
1515+
monkeypatch.setenv("ROWS", "36")
1516+
15101517
yaml_workspace = test_utils.get_workspace_file(
15111518
"regressions/issue_800_default_size_many_windows.yaml",
15121519
)
@@ -1519,9 +1526,6 @@ def test_issue_800_default_size_many_windows(
15191526
for k, v in confoverrides.items():
15201527
workspace[k] = v
15211528

1522-
if TMUXP_DEFAULT_SIZE is not None:
1523-
monkeypatch.setenv("TMUXP_DEFAULT_SIZE", TMUXP_DEFAULT_SIZE)
1524-
15251529
builder = WorkspaceBuilder(session_config=workspace, server=server)
15261530

15271531
if raises:

0 commit comments

Comments
 (0)